refactor(frontend): カスタムディレクティブの型付け など

(cherry picked from commit 49c9215b61a70e020c5b28a68756bfdc0df3cac7)
This commit is contained in:
taiyme 2024-11-01 21:07:35 +09:00 committed by kakkokari-gtyih
parent a61a2c5f87
commit 1c34a03b7a
19 changed files with 262 additions and 138 deletions

View file

@ -64,13 +64,13 @@ initialize({
initLocalStorage(); initLocalStorage();
queueMicrotask(() => { queueMicrotask(() => {
Promise.all([ Promise.all([
import('../src/components'), import('../src/directives/index.js'),
import('../src/directives'), import('../src/components/index.js'),
import('../src/widgets'), import('../src/widgets/index.js'),
import('../src/scripts/theme'), import('../src/scripts/theme.js'),
import('../src/store'), import('../src/store.js'),
import('../src/os'), import('../src/os.js'),
]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { defaultStore }, os]) => { ]).then(([{ default: directives }, { default: components }, { default: widgets }, { applyTheme }, { defaultStore }, os]) => {
setup((app) => { setup((app) => {
moduleInitialized = true; moduleInitialized = true;
if (app[appInitialized]) { if (app[appInitialized]) {
@ -78,8 +78,8 @@ queueMicrotask(() => {
} }
app[appInitialized] = true; app[appInitialized] = true;
loadTheme(applyTheme); loadTheme(applyTheme);
components(app);
directives(app); directives(app);
components(app);
widgets(app); widgets(app);
misskeyOS = os; misskeyOS = os;
if (isChromatic()) { if (isChromatic()) {

View file

@ -6,9 +6,9 @@
import { computed, watch, version as vueVersion, App } from 'vue'; import { computed, watch, version as vueVersion, App } from 'vue';
import { compareVersions } from 'compare-versions'; import { compareVersions } from 'compare-versions';
import { version, lang, updateLocale, locale } from '@@/js/config.js'; import { version, lang, updateLocale, locale } from '@@/js/config.js';
import widgets from '@/widgets/index.js';
import directives from '@/directives/index.js'; import directives from '@/directives/index.js';
import components from '@/components/index.js'; import components from '@/components/index.js';
import widgets from '@/widgets/index.js';
import { applyTheme } from '@/scripts/theme.js'; import { applyTheme } from '@/scripts/theme.js';
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js'; import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
import { updateI18n, i18n } from '@/i18n.js'; import { updateI18n, i18n } from '@/i18n.js';
@ -239,9 +239,9 @@ export async function common(createVue: () => App<Element>) {
app.config.performance = true; app.config.performance = true;
} }
widgets(app);
directives(app); directives(app);
components(app); components(app);
widgets(app);
// https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114239210 // https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114239210
// なぜか2回実行されることがあるため、mountするdivを1つに制限する // なぜか2回実行されることがあるため、mountするdivを1つに制限する

View file

@ -54,7 +54,7 @@ import { defineAsyncComponent, ref } from 'vue';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { widgets as widgetDefs } from '@/widgets/index.js'; import { widgetDefs } from '@/widgets/index.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { isLink } from '@@/js/is-link.js'; import { isLink } from '@@/js/is-link.js';

View file

@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { App } from 'vue'; import type { App } from 'vue';
import Mfm from './global/MkMfm.js'; import Mfm from './global/MkMfm.js';
import MkA from './global/MkA.vue'; import MkA from './global/MkA.vue';
@ -34,28 +34,28 @@ export default function(app: App) {
} }
export const components = { export const components = {
I18n: I18n, I18n,
RouterView: RouterView, RouterView,
Mfm: Mfm, Mfm,
MkA: MkA, MkA,
MkAcct: MkAcct, MkAcct,
MkAvatar: MkAvatar, MkAd,
MkEmoji: MkEmoji, MkAvatar,
MkCondensedLine: MkCondensedLine, MkCondensedLine,
MkCustomEmoji: MkCustomEmoji, MkCustomEmoji,
MkUserName: MkUserName, MkEllipsis,
MkEllipsis: MkEllipsis, MkEmoji,
MkTime: MkTime, MkError,
MkUrl: MkUrl, MkFooterSpacer,
MkLoading: MkLoading, MkLazy,
MkError: MkError, MkLoading,
MkAd: MkAd, MkPageHeader,
MkPageHeader: MkPageHeader, MkSpacer,
MkSpacer: MkSpacer, MkStickyContainer,
MkFooterSpacer: MkFooterSpacer, MkTime,
MkStickyContainer: MkStickyContainer, MkUrl,
MkLazy: MkLazy, MkUserName,
}; } as const;
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
export interface GlobalComponents { export interface GlobalComponents {
@ -64,21 +64,21 @@ declare module '@vue/runtime-core' {
Mfm: typeof Mfm; Mfm: typeof Mfm;
MkA: typeof MkA; MkA: typeof MkA;
MkAcct: typeof MkAcct; MkAcct: typeof MkAcct;
MkAd: typeof MkAd;
MkAvatar: typeof MkAvatar; MkAvatar: typeof MkAvatar;
MkEmoji: typeof MkEmoji;
MkCondensedLine: typeof MkCondensedLine; MkCondensedLine: typeof MkCondensedLine;
MkCustomEmoji: typeof MkCustomEmoji; MkCustomEmoji: typeof MkCustomEmoji;
MkUserName: typeof MkUserName;
MkEllipsis: typeof MkEllipsis; MkEllipsis: typeof MkEllipsis;
MkTime: typeof MkTime; MkEmoji: typeof MkEmoji;
MkUrl: typeof MkUrl;
MkLoading: typeof MkLoading;
MkError: typeof MkError; MkError: typeof MkError;
MkAd: typeof MkAd; MkFooterSpacer: typeof MkFooterSpacer;
MkLazy: typeof MkLazy;
MkLoading: typeof MkLoading;
MkPageHeader: typeof MkPageHeader; MkPageHeader: typeof MkPageHeader;
MkSpacer: typeof MkSpacer; MkSpacer: typeof MkSpacer;
MkFooterSpacer: typeof MkFooterSpacer;
MkStickyContainer: typeof MkStickyContainer; MkStickyContainer: typeof MkStickyContainer;
MkLazy: typeof MkLazy; MkTime: typeof MkTime;
MkUrl: typeof MkUrl;
MkUserName: typeof MkUserName;
} }
} }

View file

@ -4,10 +4,17 @@
*/ */
import type { ObjectDirective } from 'vue'; import type { ObjectDirective } from 'vue';
import { getBgColor } from '@/scripts/get-bg-color.js';
export const vAdaptiveBg: ObjectDirective<HTMLElement, null | undefined> = { type VAdaptiveBg = ObjectDirective<HTMLElement, null | undefined>;
mounted(src) {
export const vAdaptiveBg = {
async mounted(src) {
const [
{ getBgColor },
] = await Promise.all([
import('@/scripts/get-bg-color.js'),
]);
const parentBg = getBgColor(src.parentElement) ?? 'transparent'; const parentBg = getBgColor(src.parentElement) ?? 'transparent';
const myBg = window.getComputedStyle(src).backgroundColor; const myBg = window.getComputedStyle(src).backgroundColor;
@ -18,4 +25,4 @@ export const vAdaptiveBg: ObjectDirective<HTMLElement, null | undefined> = {
src.style.backgroundColor = myBg; src.style.backgroundColor = myBg;
} }
}, },
}; } satisfies VAdaptiveBg as VAdaptiveBg;

View file

@ -4,10 +4,17 @@
*/ */
import type { ObjectDirective } from 'vue'; import type { ObjectDirective } from 'vue';
import { getBgColor } from '@/scripts/get-bg-color.js';
export const vAdaptiveBorder: ObjectDirective<HTMLElement, null | undefined> = { type VAdaptiveBorder = ObjectDirective<HTMLElement, null | undefined>;
mounted(src) {
export const vAdaptiveBorder = {
async mounted(src) {
const [
{ getBgColor },
] = await Promise.all([
import('@/scripts/get-bg-color.js'),
]);
const parentBg = getBgColor(src.parentElement) ?? 'transparent'; const parentBg = getBgColor(src.parentElement) ?? 'transparent';
const myBg = window.getComputedStyle(src).backgroundColor; const myBg = window.getComputedStyle(src).backgroundColor;
@ -18,4 +25,4 @@ export const vAdaptiveBorder: ObjectDirective<HTMLElement, null | undefined> = {
src.style.borderColor = myBg; src.style.borderColor = myBg;
} }
}, },
}; } satisfies VAdaptiveBorder as VAdaptiveBorder;

View file

@ -5,8 +5,10 @@
import type { ObjectDirective } from 'vue'; import type { ObjectDirective } from 'vue';
export const vAnim: ObjectDirective<HTMLElement, number | null | undefined> = { type VAnim = ObjectDirective<HTMLElement, number | null | undefined>;
beforeMount(src) {
export const vAnim = {
async beforeMount(src) {
src.style.opacity = '0'; src.style.opacity = '0';
src.style.transform = 'scale(0.9)'; src.style.transform = 'scale(0.9)';
// ページネーションと相性が悪いので // ページネーションと相性が悪いので
@ -16,10 +18,10 @@ export const vAnim: ObjectDirective<HTMLElement, number | null | undefined> = {
src.classList.add('_zoom'); src.classList.add('_zoom');
}, },
mounted(src) { async mounted(src) {
window.setTimeout(() => { window.setTimeout(() => {
src.style.opacity = '1'; src.style.opacity = '1';
src.style.transform = 'none'; src.style.transform = 'none';
}, 1); }, 1);
}, },
}; } satisfies VAnim as VAnim;

View file

@ -5,8 +5,10 @@
import type { ObjectDirective } from 'vue'; import type { ObjectDirective } from 'vue';
export const vAppear: ObjectDirective<HTMLElement, (() => unknown) | null | undefined> = { type VAppear = ObjectDirective<HTMLElement, (() => unknown) | null | undefined>;
mounted(src, binding) {
export const vAppear = {
async mounted(src, binding) {
const fn = binding.value; const fn = binding.value;
if (fn == null) return; if (fn == null) return;
@ -21,7 +23,7 @@ export const vAppear: ObjectDirective<HTMLElement, (() => unknown) | null | unde
src._observer_ = observer; src._observer_ = observer;
}, },
unmounted(src) { async unmounted(src) {
src._observer_?.disconnect(); src._observer_?.disconnect();
}, },
}; } satisfies VAppear as VAppear;

View file

@ -4,10 +4,17 @@
*/ */
import type { ObjectDirective } from 'vue'; import type { ObjectDirective } from 'vue';
import { defaultStore } from '@/store.js';
export const vClickAnime: ObjectDirective<HTMLElement, null | undefined> = { type VClickAnime = ObjectDirective<HTMLElement, null | undefined>;
mounted(src) {
export const vClickAnime = {
async mounted(src) {
const [
{ defaultStore },
] = await Promise.all([
import('@/store.js'),
]);
if (!defaultStore.state.animation) return; if (!defaultStore.state.animation) return;
const target = src.children[0]; const target = src.children[0];
@ -37,4 +44,4 @@ export const vClickAnime: ObjectDirective<HTMLElement, null | undefined> = {
target.classList.add('_anime_bounce_standBy'); target.classList.add('_anime_bounce_standBy');
}); });
}, },
}; } satisfies VClickAnime as VClickAnime;

View file

@ -11,8 +11,10 @@ const mountings = new Map<HTMLElement, {
fn: (w: number, h: number) => void; fn: (w: number, h: number) => void;
}>(); }>();
export const vGetSize: ObjectDirective<HTMLElement, ((w: number, h: number) => unknown) | null | undefined> = { type VGetSize = ObjectDirective<HTMLElement, ((w: number, h: number) => unknown) | null | undefined>;
mounted(src, binding) {
export const vGetSize = {
async mounted(src, binding) {
const resize = new ResizeObserver(() => { const resize = new ResizeObserver(() => {
calc(src); calc(src);
}); });
@ -22,15 +24,15 @@ export const vGetSize: ObjectDirective<HTMLElement, ((w: number, h: number) => u
calc(src); calc(src);
}, },
unmounted(src, binding) { async unmounted(src, binding) {
if (binding.value != null) binding.value(0, 0); binding.value(0, 0);
const info = mountings.get(src); const info = mountings.get(src);
if (!info) return; if (!info) return;
info.resize.disconnect(); info.resize.disconnect();
if (info.intersection) info.intersection.disconnect(); if (info.intersection) info.intersection.disconnect();
mountings.delete(src); mountings.delete(src);
}, },
}; } satisfies VGetSize as VGetSize;
function calc(src: HTMLElement) { function calc(src: HTMLElement) {
const info = mountings.get(src); const info = mountings.get(src);

View file

@ -5,10 +5,17 @@
import type { ObjectDirective } from 'vue'; import type { ObjectDirective } from 'vue';
import type { Keymap } from '@/scripts/hotkey.js'; import type { Keymap } from '@/scripts/hotkey.js';
import { makeHotkey } from '@/scripts/hotkey.js';
export const vHotkey: ObjectDirective<HTMLElement, Keymap | null | undefined, 'global'> = { type VHotkey = ObjectDirective<HTMLElement, Keymap | null | undefined, 'global'>;
mounted(src, binding) {
export const vHotkey = {
async mounted(src, binding) {
const [
{ makeHotkey },
] = await Promise.all([
import('@/scripts/hotkey.js'),
]);
src._hotkey_global = binding.modifiers.global === true; src._hotkey_global = binding.modifiers.global === true;
src._keyHandler = makeHotkey(binding.value); src._keyHandler = makeHotkey(binding.value);
@ -20,11 +27,11 @@ export const vHotkey: ObjectDirective<HTMLElement, Keymap | null | undefined, 'g
} }
}, },
unmounted(src) { async unmounted(src) {
if (src._hotkey_global) { if (src._hotkey_global) {
document.removeEventListener('keydown', src._keyHandler); document.removeEventListener('keydown', src._keyHandler);
} else { } else {
src.removeEventListener('keydown', src._keyHandler); src.removeEventListener('keydown', src._keyHandler);
} }
}, },
}; } satisfies VHotkey as VHotkey;

View file

@ -4,10 +4,17 @@
*/ */
import type { ObjectDirective } from 'vue'; import type { ObjectDirective } from 'vue';
import { getBgColor } from '@/scripts/get-bg-color.js';
export const vPanel: ObjectDirective<HTMLElement, null | undefined> = { type VPanel = ObjectDirective<HTMLElement, null | undefined>;
mounted(src) {
export const vPanel = {
async mounted(src) {
const [
{ getBgColor },
] = await Promise.all([
import('@/scripts/get-bg-color.js'),
]);
const parentBg = getBgColor(src.parentElement) ?? 'transparent'; const parentBg = getBgColor(src.parentElement) ?? 'transparent';
const myBg = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel'); const myBg = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel');
@ -18,4 +25,4 @@ export const vPanel: ObjectDirective<HTMLElement, null | undefined> = {
src.style.backgroundColor = 'var(--MI_THEME-panel)'; src.style.backgroundColor = 'var(--MI_THEME-panel)';
} }
}, },
}; } satisfies VPanel as VPanel;

View file

@ -4,11 +4,18 @@
*/ */
import type { ObjectDirective } from 'vue'; import type { ObjectDirective } from 'vue';
import { popup } from '@/os.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue'; import MkRippleEffect from '@/components/MkRippleEffect.vue';
export const vRipple: ObjectDirective<HTMLElement, boolean | null | undefined> = { type VRipple = ObjectDirective<HTMLElement, boolean | null | undefined>;
mounted(src, binding) {
export const vRipple = {
async mounted(src, binding) {
const [
{ popup },
] = await Promise.all([
import('@/os.js'),
]);
// 明示的に false であればバインドしない // 明示的に false であればバインドしない
if (binding.value === false) return; if (binding.value === false) return;
@ -23,4 +30,4 @@ export const vRipple: ObjectDirective<HTMLElement, boolean | null | undefined> =
}); });
}); });
}, },
}; } satisfies VRipple as VRipple;

View file

@ -6,16 +6,23 @@
// TODO: useTooltip関数使うようにしたい // TODO: useTooltip関数使うようにしたい
// ただディレクティブ内でonUnmountedなどのcomposition api使えるのか不明 // ただディレクティブ内でonUnmountedなどのcomposition api使えるのか不明
import { defineAsyncComponent, ref } from 'vue'; import { type ObjectDirective, defineAsyncComponent, ref } from 'vue';
import type { ObjectDirective } from 'vue';
import { isTouchUsing } from '@/scripts/touch.js';
import { popup, alert } from '@/os.js';
const start = isTouchUsing ? 'touchstart' : 'mouseenter'; type VTooltip = ObjectDirective<HTMLElement, string | null | undefined, 'noDelay' | 'mfm' | 'top' | 'right' | 'bottom' | 'left', 'dialog'>;
const end = isTouchUsing ? 'touchend' : 'mouseleave';
export const vTooltip = {
async mounted(src, binding) {
const [
{ alert, popup },
{ isTouchUsing },
] = await Promise.all([
import('@/os.js'),
import('@/scripts/touch.js'),
]);
const start = isTouchUsing ? 'touchstart' : 'mouseenter';
const end = isTouchUsing ? 'touchend' : 'mouseleave';
export const vTooltip: ObjectDirective<HTMLElement, string | null | undefined, 'noDelay' | 'mfm' | 'top' | 'right' | 'bottom' | 'left', 'dialog'> = {
mounted(src, binding) {
const delay = binding.modifiers.noDelay ? 0 : 100; const delay = binding.modifiers.noDelay ? 0 : 100;
const self = (src as any)._tooltipDirective_ = {} as any; const self = (src as any)._tooltipDirective_ = {} as any;
@ -97,13 +104,13 @@ export const vTooltip: ObjectDirective<HTMLElement, string | null | undefined, '
}); });
}, },
updated(src, binding) { async updated(src, binding) {
const self = (src as any)._tooltipDirective_; const self = (src as any)._tooltipDirective_;
self.text = binding.value as string; self.text = binding.value as string;
}, },
unmounted(src) { async unmounted(src) {
const self = (src as any)._tooltipDirective_; const self = (src as any)._tooltipDirective_;
window.clearInterval(self.checkTimer); window.clearInterval(self.checkTimer);
}, },
}; } satisfies VTooltip as VTooltip;

View file

@ -3,12 +3,13 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { defineAsyncComponent, ref } from 'vue'; import { type ObjectDirective, defineAsyncComponent, ref } from 'vue';
import type { ObjectDirective } from 'vue';
import { popup } from '@/os.js'; import { popup } from '@/os.js';
export const vUserPreview: ObjectDirective<HTMLElement, string | null | undefined> = { type VUserPreview = ObjectDirective<HTMLElement, string | null | undefined>;
mounted(src, binding) {
export const vUserPreview = {
async mounted(src, binding) {
if (binding.value == null) return; if (binding.value == null) return;
// TODO: 新たにプロパティを作るのをやめMapを使う // TODO: 新たにプロパティを作るのをやめMapを使う
@ -18,21 +19,21 @@ export const vUserPreview: ObjectDirective<HTMLElement, string | null | undefine
self.preview = new UserPreview(src, binding.value); self.preview = new UserPreview(src, binding.value);
}, },
unmounted(src, binding) { async unmounted(src, binding) {
if (binding.value == null) return; if (binding.value == null) return;
const self = src._userPreviewDirective_; const self = src._userPreviewDirective_;
self.preview.detach(); self.preview.detach();
}, },
}; } satisfies VUserPreview as VUserPreview;
class UserPreview { class UserPreview {
private el: HTMLElement; private el;
private user: string; private user;
private showTimer: number | null = null; private showTimer;
private hideTimer: number | null = null; private hideTimer;
private checkTimer: number | null = null; private checkTimer;
private promise: { cancel: () => void } | null = null; private promise;
constructor(el, user) { constructor(el, user) {
this.el = el; this.el = el;

View file

@ -3,40 +3,75 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { App, defineAsyncComponent } from 'vue'; import { type App, defineAsyncComponent } from 'vue';
const WidgetProfile = defineAsyncComponent(() => import('@/widgets/WidgetProfile.vue'));
const WidgetInstanceInfo = defineAsyncComponent(() => import('@/widgets/WidgetInstanceInfo.vue'));
const WidgetMemo = defineAsyncComponent(() => import('@/widgets/WidgetMemo.vue'));
const WidgetNotifications = defineAsyncComponent(() => import('@/widgets/WidgetNotifications.vue'));
const WidgetTimeline = defineAsyncComponent(() => import('@/widgets/WidgetTimeline.vue'));
const WidgetCalendar = defineAsyncComponent(() => import('@/widgets/WidgetCalendar.vue'));
const WidgetRss = defineAsyncComponent(() => import('@/widgets/WidgetRss.vue'));
const WidgetRssTicker = defineAsyncComponent(() => import('@/widgets/WidgetRssTicker.vue'));
const WidgetTrends = defineAsyncComponent(() => import('@/widgets/WidgetTrends.vue'));
const WidgetClock = defineAsyncComponent(() => import('@/widgets/WidgetClock.vue'));
const WidgetActivity = defineAsyncComponent(() => import('@/widgets/WidgetActivity.vue'));
const WidgetPhotos = defineAsyncComponent(() => import('@/widgets/WidgetPhotos.vue'));
const WidgetDigitalClock = defineAsyncComponent(() => import('@/widgets/WidgetDigitalClock.vue'));
const WidgetUnixClock = defineAsyncComponent(() => import('@/widgets/WidgetUnixClock.vue'));
const WidgetFederation = defineAsyncComponent(() => import('@/widgets/WidgetFederation.vue'));
const WidgetInstanceCloud = defineAsyncComponent(() => import('@/widgets/WidgetInstanceCloud.vue'));
const WidgetPostForm = defineAsyncComponent(() => import('@/widgets/WidgetPostForm.vue'));
const WidgetSlideshow = defineAsyncComponent(() => import('@/widgets/WidgetSlideshow.vue'));
const WidgetServerMetric = defineAsyncComponent(() => import('@/widgets/server-metric/index.vue'));
const WidgetOnlineUsers = defineAsyncComponent(() => import('@/widgets/WidgetOnlineUsers.vue'));
const WidgetJobQueue = defineAsyncComponent(() => import('@/widgets/WidgetJobQueue.vue'));
const WidgetButton = defineAsyncComponent(() => import('@/widgets/WidgetButton.vue'));
const WidgetAiscript = defineAsyncComponent(() => import('@/widgets/WidgetAiscript.vue'));
const WidgetAiscriptApp = defineAsyncComponent(() => import('@/widgets/WidgetAiscriptApp.vue'));
const WidgetAichan = defineAsyncComponent(() => import('@/widgets/WidgetAichan.vue'));
const WidgetUserList = defineAsyncComponent(() => import('@/widgets/WidgetUserList.vue'));
const WidgetClicker = defineAsyncComponent(() => import('@/widgets/WidgetClicker.vue'));
const WidgetBirthdayFollowings = defineAsyncComponent(() => import('@/widgets/WidgetBirthdayFollowings.vue'));
export default function(app: App) { export default function(app: App) {
app.component('WidgetProfile', defineAsyncComponent(() => import('./WidgetProfile.vue'))); for (const [key, value] of Object.entries(widgets)) {
app.component('WidgetInstanceInfo', defineAsyncComponent(() => import('./WidgetInstanceInfo.vue'))); app.component(key, value);
app.component('WidgetMemo', defineAsyncComponent(() => import('./WidgetMemo.vue'))); }
app.component('WidgetNotifications', defineAsyncComponent(() => import('./WidgetNotifications.vue')));
app.component('WidgetTimeline', defineAsyncComponent(() => import('./WidgetTimeline.vue')));
app.component('WidgetCalendar', defineAsyncComponent(() => import('./WidgetCalendar.vue')));
app.component('WidgetRss', defineAsyncComponent(() => import('./WidgetRss.vue')));
app.component('WidgetRssTicker', defineAsyncComponent(() => import('./WidgetRssTicker.vue')));
app.component('WidgetTrends', defineAsyncComponent(() => import('./WidgetTrends.vue')));
app.component('WidgetClock', defineAsyncComponent(() => import('./WidgetClock.vue')));
app.component('WidgetActivity', defineAsyncComponent(() => import('./WidgetActivity.vue')));
app.component('WidgetPhotos', defineAsyncComponent(() => import('./WidgetPhotos.vue')));
app.component('WidgetDigitalClock', defineAsyncComponent(() => import('./WidgetDigitalClock.vue')));
app.component('WidgetUnixClock', defineAsyncComponent(() => import('./WidgetUnixClock.vue')));
app.component('WidgetFederation', defineAsyncComponent(() => import('./WidgetFederation.vue')));
app.component('WidgetPostForm', defineAsyncComponent(() => import('./WidgetPostForm.vue')));
app.component('WidgetSlideshow', defineAsyncComponent(() => import('./WidgetSlideshow.vue')));
app.component('WidgetServerMetric', defineAsyncComponent(() => import('./server-metric/index.vue')));
app.component('WidgetOnlineUsers', defineAsyncComponent(() => import('./WidgetOnlineUsers.vue')));
app.component('WidgetJobQueue', defineAsyncComponent(() => import('./WidgetJobQueue.vue')));
app.component('WidgetInstanceCloud', defineAsyncComponent(() => import('./WidgetInstanceCloud.vue')));
app.component('WidgetButton', defineAsyncComponent(() => import('./WidgetButton.vue')));
app.component('WidgetAiscript', defineAsyncComponent(() => import('./WidgetAiscript.vue')));
app.component('WidgetAiscriptApp', defineAsyncComponent(() => import('./WidgetAiscriptApp.vue')));
app.component('WidgetAichan', defineAsyncComponent(() => import('./WidgetAichan.vue')));
app.component('WidgetUserList', defineAsyncComponent(() => import('./WidgetUserList.vue')));
app.component('WidgetClicker', defineAsyncComponent(() => import('./WidgetClicker.vue')));
app.component('WidgetBirthdayFollowings', defineAsyncComponent(() => import('./WidgetBirthdayFollowings.vue')));
} }
export const widgets = [ const widgets = {
WidgetProfile,
WidgetInstanceInfo,
WidgetMemo,
WidgetNotifications,
WidgetTimeline,
WidgetCalendar,
WidgetRss,
WidgetRssTicker,
WidgetTrends,
WidgetClock,
WidgetActivity,
WidgetPhotos,
WidgetDigitalClock,
WidgetUnixClock,
WidgetFederation,
WidgetInstanceCloud,
WidgetPostForm,
WidgetSlideshow,
WidgetServerMetric,
WidgetOnlineUsers,
WidgetJobQueue,
WidgetButton,
WidgetAiscript,
WidgetAiscriptApp,
WidgetAichan,
WidgetUserList,
WidgetClicker,
WidgetBirthdayFollowings,
} as const;
export const widgetDefs = [
'profile', 'profile',
'instanceInfo', 'instanceInfo',
'memo', 'memo',
@ -65,4 +100,37 @@ export const widgets = [
'userList', 'userList',
'clicker', 'clicker',
'birthdayFollowings', 'birthdayFollowings',
]; ] as const;
declare module '@vue/runtime-core' {
export interface GlobalComponents {
WidgetProfile: typeof WidgetProfile;
WidgetInstanceInfo: typeof WidgetInstanceInfo;
WidgetMemo: typeof WidgetMemo;
WidgetNotifications: typeof WidgetNotifications;
WidgetTimeline: typeof WidgetTimeline;
WidgetCalendar: typeof WidgetCalendar;
WidgetRss: typeof WidgetRss;
WidgetRssTicker: typeof WidgetRssTicker;
WidgetTrends: typeof WidgetTrends;
WidgetClock: typeof WidgetClock;
WidgetActivity: typeof WidgetActivity;
WidgetPhotos: typeof WidgetPhotos;
WidgetDigitalClock: typeof WidgetDigitalClock;
WidgetUnixClock: typeof WidgetUnixClock;
WidgetFederation: typeof WidgetFederation;
WidgetInstanceCloud: typeof WidgetInstanceCloud;
WidgetPostForm: typeof WidgetPostForm;
WidgetSlideshow: typeof WidgetSlideshow;
WidgetServerMetric: typeof WidgetServerMetric;
WidgetOnlineUsers: typeof WidgetOnlineUsers;
WidgetJobQueue: typeof WidgetJobQueue;
WidgetButton: typeof WidgetButton;
WidgetAiscript: typeof WidgetAiscript;
WidgetAiscriptApp: typeof WidgetAiscriptApp;
WidgetAichan: typeof WidgetAichan;
WidgetUserList: typeof WidgetUserList;
WidgetClicker: typeof WidgetClicker;
WidgetBirthdayFollowings: typeof WidgetBirthdayFollowings;
}
}

View file

@ -6,9 +6,9 @@
import { describe, test, assert, afterEach } from 'vitest'; import { describe, test, assert, afterEach } from 'vitest';
import { render, cleanup, type RenderResult } from '@testing-library/vue'; import { render, cleanup, type RenderResult } from '@testing-library/vue';
import { defaultStoreState } from './init.js'; import { defaultStoreState } from './init.js';
import { getEmojiName } from '@@/js/emojilist.js'; import { getEmojiName } from '../../frontend-shared/js/emojilist.js';
import { components } from '@/components/index.js';
import { directives } from '@/directives/index.js'; import { directives } from '@/directives/index.js';
import { components } from '@/components/index.js';
import MkEmoji from '@/components/global/MkEmoji.vue'; import MkEmoji from '@/components/global/MkEmoji.vue';
describe('Emoji', () => { describe('Emoji', () => {

View file

@ -7,8 +7,8 @@ import { describe, test, assert, afterEach } from 'vitest';
import { render, cleanup, type RenderResult } from '@testing-library/vue'; import { render, cleanup, type RenderResult } from '@testing-library/vue';
import './init'; import './init';
import type * as Misskey from 'misskey-js'; import type * as Misskey from 'misskey-js';
import { components } from '@/components/index.js';
import { directives } from '@/directives/index.js'; import { directives } from '@/directives/index.js';
import { components } from '@/components/index.js';
import MkMediaImage from '@/components/MkMediaImage.vue'; import MkMediaImage from '@/components/MkMediaImage.vue';
describe('MkMediaImage', () => { describe('MkMediaImage', () => {

View file

@ -7,8 +7,8 @@ import { describe, test, assert, afterEach } from 'vitest';
import { render, cleanup, type RenderResult } from '@testing-library/vue'; import { render, cleanup, type RenderResult } from '@testing-library/vue';
import './init'; import './init';
import type { summaly } from '@misskey-dev/summaly'; import type { summaly } from '@misskey-dev/summaly';
import { components } from '@/components/index.js';
import { directives } from '@/directives/index.js'; import { directives } from '@/directives/index.js';
import { components } from '@/components/index.js';
import MkUrlPreview from '@/components/MkUrlPreview.vue'; import MkUrlPreview from '@/components/MkUrlPreview.vue';
type SummalyResult = Awaited<ReturnType<typeof summaly>>; type SummalyResult = Awaited<ReturnType<typeof summaly>>;