diff options
Diffstat (limited to 'packages/frontend/src/boot')
| -rw-r--r-- | packages/frontend/src/boot/common.ts | 184 | ||||
| -rw-r--r-- | packages/frontend/src/boot/main-boot.ts | 118 | ||||
| -rw-r--r-- | packages/frontend/src/boot/sub-boot.ts | 2 |
3 files changed, 162 insertions, 142 deletions
diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index 46ec4533ec..62cc74ea09 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -3,27 +3,31 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { computed, watch, version as vueVersion, App } from 'vue'; +import { computed, watch, version as vueVersion } from 'vue'; import { compareVersions } from 'compare-versions'; import { version, lang, langsVersion, updateLocale, locale } from '@@/js/config.js'; +import defaultLightTheme from '@@/themes/l-light.json5'; +import defaultDarkTheme from '@@/themes/d-green-lime.json5'; +import type { App } from 'vue'; import widgets from '@/widgets/index.js'; import directives from '@/directives/index.js'; import components from '@/components/index.js'; -import { applyTheme } from '@/scripts/theme.js'; -import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js'; +import { applyTheme } from '@/theme.js'; +import { isDeviceDarkmode } from '@/utility/is-device-darkmode.js'; import { updateI18n, i18n } from '@/i18n.js'; -import { $i, refreshAccount, login } from '@/account.js'; -import { defaultStore, ColdDeviceStorage } from '@/store.js'; +import { refreshCurrentAccount, login } from '@/accounts.js'; +import { store } from '@/store.js'; import { fetchInstance, instance } from '@/instance.js'; -import { deviceKind, updateDeviceKind } from '@/scripts/device-kind.js'; -import { reloadChannel } from '@/scripts/unison-reload.js'; -import { getUrlWithoutLoginId } from '@/scripts/login-id.js'; -import { getAccountFromId } from '@/scripts/get-account-from-id.js'; +import { deviceKind, updateDeviceKind } from '@/utility/device-kind.js'; +import { reloadChannel } from '@/utility/unison-reload.js'; +import { getUrlWithoutLoginId } from '@/utility/login-id.js'; +import { getAccountFromId } from '@/utility/get-account-from-id.js'; import { deckStore } from '@/ui/deck/deck-store.js'; +import { analytics, initAnalytics } from '@/analytics.js'; import { miLocalStorage } from '@/local-storage.js'; import { fetchCustomEmojis } from '@/custom-emojis.js'; -import { setupRouter } from '@/router/main.js'; -import { createMainRouter } from '@/router/definition.js'; +import { prefer } from '@/preferences.js'; +import { $i } from '@/i.js'; export async function common(createVue: () => App<Element>) { console.info(`Sharkey v${version}`); @@ -33,11 +37,6 @@ export async function common(createVue: () => App<Element>) { console.info(`vue ${vueVersion}`); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (window as any).$i = $i; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (window as any).$store = defaultStore; - window.addEventListener('error', event => { console.error(event); /* @@ -97,32 +96,32 @@ export async function common(createVue: () => App<Element>) { //#endregion // タッチデバイスでCSSの:hoverを機能させる - document.addEventListener('touchend', () => {}, { passive: true }); + window.document.addEventListener('touchend', () => {}, { passive: true }); // URLに#pswpを含む場合は取り除く - if (location.hash === '#pswp') { - history.replaceState(null, '', location.href.replace('#pswp', '')); + if (window.location.hash === '#pswp') { + window.history.replaceState(null, '', window.location.href.replace('#pswp', '')); } // 一斉リロード reloadChannel.addEventListener('message', path => { - if (path !== null) location.href = path; - else location.reload(); + if (path !== null) window.location.href = path; + else window.location.reload(); }); // If mobile, insert the viewport meta tag if (['smartphone', 'tablet'].includes(deviceKind)) { - const viewport = document.getElementsByName('viewport').item(0); + const viewport = window.document.getElementsByName('viewport').item(0); viewport.setAttribute('content', `${viewport.getAttribute('content')}, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover`); } //#region Set lang attr - const html = document.documentElement; + const html = window.document.documentElement; html.setAttribute('lang', lang); //#endregion - await defaultStore.ready; + await store.ready; await deckStore.ready; const fetchInstanceMetaPromise = fetchInstance(); @@ -132,11 +131,11 @@ export async function common(createVue: () => App<Element>) { }); //#region loginId - const params = new URLSearchParams(location.search); + const params = new URLSearchParams(window.location.search); const loginId = params.get('loginId'); if (loginId) { - const target = getUrlWithoutLoginId(location.href); + const target = getUrlWithoutLoginId(window.location.href); if (!$i || $i.id !== loginId) { const account = await getAccountFromId(loginId); @@ -145,71 +144,78 @@ export async function common(createVue: () => App<Element>) { } } - history.replaceState({ misskey: 'loginId' }, '', target); + window.history.replaceState({ misskey: 'loginId' }, '', target); } //#endregion // NOTE: この処理は必ずクライアント更新チェック処理より後に来ること(テーマ再構築のため) - watch(defaultStore.reactiveState.darkMode, (darkMode) => { - applyTheme(darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme')); + watch(store.r.darkMode, (darkMode) => { + applyTheme(darkMode + ? (prefer.s.darkTheme ?? defaultDarkTheme) + : (prefer.s.lightTheme ?? defaultLightTheme), + ); }, { immediate: miLocalStorage.getItem('theme') == null }); - document.documentElement.dataset.colorScheme = defaultStore.state.darkMode ? 'dark' : 'light'; + window.document.documentElement.dataset.colorScheme = store.s.darkMode ? 'dark' : 'light'; - const darkTheme = computed(ColdDeviceStorage.makeGetterSetter('darkTheme')); - const lightTheme = computed(ColdDeviceStorage.makeGetterSetter('lightTheme')); + const darkTheme = prefer.model('darkTheme'); + const lightTheme = prefer.model('lightTheme'); watch(darkTheme, (theme) => { - if (defaultStore.state.darkMode) { - applyTheme(theme); + if (store.s.darkMode) { + applyTheme(theme ?? defaultDarkTheme); } }); watch(lightTheme, (theme) => { - if (!defaultStore.state.darkMode) { - applyTheme(theme); + if (!store.s.darkMode) { + applyTheme(theme ?? defaultLightTheme); } }); //#region Sync dark mode - if (ColdDeviceStorage.get('syncDeviceDarkMode')) { - defaultStore.set('darkMode', isDeviceDarkmode()); + if (prefer.s.syncDeviceDarkMode) { + store.set('darkMode', isDeviceDarkmode()); } window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (mql) => { - if (ColdDeviceStorage.get('syncDeviceDarkMode')) { - defaultStore.set('darkMode', mql.matches); + if (prefer.s.syncDeviceDarkMode) { + store.set('darkMode', mql.matches); } }); //#endregion + if (prefer.s.darkTheme && store.s.darkMode) { + if (miLocalStorage.getItem('themeId') !== prefer.s.darkTheme.id) applyTheme(prefer.s.darkTheme); + } else if (prefer.s.lightTheme && !store.s.darkMode) { + if (miLocalStorage.getItem('themeId') !== prefer.s.lightTheme.id) applyTheme(prefer.s.lightTheme); + } + fetchInstanceMetaPromise.then(() => { - if (defaultStore.state.themeInitial) { - if (instance.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON.parse(instance.defaultLightTheme)); - if (instance.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON.parse(instance.defaultDarkTheme)); - defaultStore.set('themeInitial', false); - } + // TODO: instance.defaultLightTheme/instance.defaultDarkThemeが不正な形式だった場合のケア + if (prefer.s.lightTheme == null && instance.defaultLightTheme != null) prefer.commit('lightTheme', JSON.parse(instance.defaultLightTheme)); + if (prefer.s.darkTheme == null && instance.defaultDarkTheme != null) prefer.commit('darkTheme', JSON.parse(instance.defaultDarkTheme)); }); - watch(defaultStore.reactiveState.overridedDeviceKind, (kind) => { + watch(prefer.r.overridedDeviceKind, (kind) => { updateDeviceKind(kind); }, { immediate: true }); - watch(defaultStore.reactiveState.useBlurEffectForModal, v => { - document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none'); + watch(prefer.r.useBlurEffectForModal, v => { + window.document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none'); }, { immediate: true }); - watch(defaultStore.reactiveState.useBlurEffect, v => { + watch(prefer.r.useBlurEffect, v => { if (v) { - document.documentElement.style.removeProperty('--MI-blur'); + window.document.documentElement.style.removeProperty('--MI-blur'); } else { - document.documentElement.style.setProperty('--MI-blur', 'none'); + window.document.documentElement.style.setProperty('--MI-blur', 'none'); } }, { immediate: true }); // Keep screen on - const onVisibilityChange = () => document.addEventListener('visibilitychange', () => { - if (document.visibilityState === 'visible') { + const onVisibilityChange = () => window.document.addEventListener('visibilitychange', () => { + if (window.document.visibilityState === 'visible') { try { navigator.wakeLock.request('screen'); } catch (err) { @@ -217,13 +223,13 @@ export async function common(createVue: () => App<Element>) { } } }); - if (defaultStore.state.keepScreenOn && 'wakeLock' in navigator) { + if (prefer.s.keepScreenOn && 'wakeLock' in navigator) { navigator.wakeLock.request('screen') .then(onVisibilityChange) .catch(() => { // On WebKit-based browsers, user activation is required to send wake lock request // https://webkit.org/blog/13862/the-user-activation-api/ - document.addEventListener( + window.document.addEventListener( 'click', () => navigator.wakeLock.request('screen').then(onVisibilityChange), { once: true }, @@ -231,13 +237,17 @@ export async function common(createVue: () => App<Element>) { }); } + if (prefer.s.makeEveryTextElementsSelectable) { + window.document.documentElement.classList.add('forceSelectableAll'); + } + //#region Fetch user if ($i && $i.token) { if (_DEV_) { console.log('account cache found. refreshing...'); } - refreshAccount(); + refreshCurrentAccount(); } //#endregion @@ -245,9 +255,20 @@ export async function common(createVue: () => App<Element>) { await fetchCustomEmojis(); } catch (err) { /* empty */ } - const app = createVue(); + // analytics + fetchInstanceMetaPromise.then(async () => { + await initAnalytics(instance); - setupRouter(app, createMainRouter); + if ($i) { + analytics.identify($i.id); + } + + analytics.page({ + path: window.location.pathname, + }); + }); + + const app = createVue(); if (_DEV_) { app.config.performance = true; @@ -262,16 +283,16 @@ export async function common(createVue: () => App<Element>) { const rootEl = ((): HTMLElement => { const MISSKEY_MOUNT_DIV_ID = 'sharkey_app'; - const currentRoot = document.getElementById(MISSKEY_MOUNT_DIV_ID); + const currentRoot = window.document.getElementById(MISSKEY_MOUNT_DIV_ID); if (currentRoot) { console.warn('multiple import detected'); return currentRoot; } - const root = document.createElement('div'); + const root = window.document.createElement('div'); root.id = MISSKEY_MOUNT_DIV_ID; - document.body.appendChild(root); + window.document.body.appendChild(root); return root; })(); @@ -284,34 +305,37 @@ export async function common(createVue: () => App<Element>) { removeSplash(); //#region Self-XSS 対策メッセージ - console.log( - `%c${i18n.ts._selfXssPrevention.warning}`, - 'color: #f00; background-color: #ff0; font-size: 36px; padding: 4px;', - ); - console.log( - `%c${i18n.ts._selfXssPrevention.title}`, - 'color: #f00; font-weight: 900; font-family: "Hiragino Sans W9", "Hiragino Kaku Gothic ProN", sans-serif; font-size: 24px;', - ); - console.log( - `%c${i18n.ts._selfXssPrevention.description1}`, - 'font-size: 16px; font-weight: 700;', - ); - console.log( - `%c${i18n.ts._selfXssPrevention.description2}`, - 'font-size: 16px;', - 'font-size: 20px; font-weight: 700; color: #f00;', - ); - console.log(i18n.tsx._selfXssPrevention.description3({ link: 'https://misskey-hub.net/docs/for-users/resources/self-xss/' })); + if (!_DEV_) { + console.log( + `%c${i18n.ts._selfXssPrevention.warning}`, + 'color: #f00; background-color: #ff0; font-size: 36px; padding: 4px;', + ); + console.log( + `%c${i18n.ts._selfXssPrevention.title}`, + 'color: #f00; font-weight: 900; font-family: "Hiragino Sans W9", "Hiragino Kaku Gothic ProN", sans-serif; font-size: 24px;', + ); + console.log( + `%c${i18n.ts._selfXssPrevention.description1}`, + 'font-size: 16px; font-weight: 700;', + ); + console.log( + `%c${i18n.ts._selfXssPrevention.description2}`, + 'font-size: 16px;', + 'font-size: 20px; font-weight: 700; color: #f00;', + ); + console.log(i18n.tsx._selfXssPrevention.description3({ link: 'https://misskey-hub.net/docs/for-users/resources/self-xss/' })); + } //#endregion return { isClientUpdated, + lastVersion, app, }; } function removeSplash() { - const splash = document.getElementById('splash'); + const splash = window.document.getElementById('splash'); if (splash) { splash.style.opacity = '0'; splash.style.pointerEvents = 'none'; diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 6c544feb2a..38471cd86a 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -5,36 +5,43 @@ import { createApp, defineAsyncComponent, markRaw } from 'vue'; import { ui } from '@@/js/config.js'; +import * as Misskey from 'misskey-js'; +import { compareVersions } from 'compare-versions'; import { common } from './common.js'; -import type * as Misskey from 'misskey-js'; import type { Component } from 'vue'; +import type { Keymap } from '@/utility/hotkey.js'; import { i18n } from '@/i18n.js'; -import { alert, confirm, popup, post, toast } from '@/os.js'; +import { alert, confirm, popup, post } from '@/os.js'; import { useStream } from '@/stream.js'; -import * as sound from '@/scripts/sound.js'; -import { $i, signout, updateAccountPartial } from '@/account.js'; +import * as sound from '@/utility/sound.js'; +import { $i } from '@/i.js'; import { instance } from '@/instance.js'; -import { ColdDeviceStorage, defaultStore } from '@/store.js'; -import { reactionPicker } from '@/scripts/reaction-picker.js'; +import { store } from '@/store.js'; +import { reactionPicker } from '@/utility/reaction-picker.js'; import { miLocalStorage } from '@/local-storage.js'; -import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js'; -import { initializeSw } from '@/scripts/initialize-sw.js'; +import { claimAchievement, claimedAchievements } from '@/utility/achievements.js'; +import { initializeSw } from '@/utility/initialize-sw.js'; import { deckStore } from '@/ui/deck/deck-store.js'; -import { emojiPicker } from '@/scripts/emoji-picker.js'; -import { mainRouter } from '@/router/main.js'; -import { setFavIconDot } from '@/scripts/favicon-dot.js'; -import { type Keymap, makeHotkey } from '@/scripts/hotkey.js'; +import { emojiPicker } from '@/utility/emoji-picker.js'; +import { mainRouter } from '@/router.js'; +import { setFavIconDot } from '@/utility/favicon-dot.js'; +import { type Keymap, makeHotkey } from '@/utility/hotkey.js'; import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js'; +import { prefer } from '@/preferences.js'; +import { launchPlugins } from '@/plugin.js'; +import { updateCurrentAccountPartial } from '@/accounts.js'; +import { signout } from '@/signout.js'; +import { migrateOldSettings } from '@/pref-migrate.js'; export async function mainBoot() { - const { isClientUpdated } = await common(() => { + const { isClientUpdated, lastVersion } = await common(() => { let uiStyle = ui; const searchParams = new URLSearchParams(window.location.search); if (!$i) uiStyle = 'visitor'; if (searchParams.has('zen')) uiStyle = 'zen'; - if (uiStyle === 'deck' && deckStore.state.useSimpleUiForNonRootPages && location.pathname !== '/') uiStyle = 'zen'; + if (uiStyle === 'deck' && prefer.s['deck.useSimpleUiForNonRootPages'] && window.location.pathname !== '/') uiStyle = 'zen'; if (searchParams.has('ui')) uiStyle = searchParams.get('ui'); @@ -67,13 +74,23 @@ export async function mainBoot() { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUpdated.vue')), {}, { closed: () => dispose(), }); + + // prefereces migration + // TODO: そのうち消す + if (lastVersion && (compareVersions('2025.3.2-alpha.0', lastVersion) === 1)) { + console.log('Preferences migration'); + + migrateOldSettings(); + } } const stream = useStream(); let reloadDialogShowing = false; stream.on('_disconnected_', async () => { - if (defaultStore.state.serverDisconnectedBehavior === 'dialog') { + if (prefer.s.serverDisconnectedBehavior === 'reload') { + window.location.reload(); + } else if (prefer.s.serverDisconnectedBehavior === 'dialog') { if (reloadDialogShowing) return; reloadDialogShowing = true; const { canceled } = await confirm({ @@ -83,7 +100,7 @@ export async function mainBoot() { }); reloadDialogShowing = false; if (!canceled) { - location.reload(); + window.location.reload(); } } }); @@ -100,30 +117,24 @@ export async function mainBoot() { removeCustomEmojis(emojiData.emojis); }); - for (const plugin of ColdDeviceStorage.get('plugins').filter(p => p.active)) { - import('@/plugin.js').then(async ({ install }) => { - // Workaround for https://bugs.webkit.org/show_bug.cgi?id=242740 - await new Promise(r => setTimeout(r, 0)); - install(plugin); - }); - } + launchPlugins(); try { - if (defaultStore.state.enableSeasonalScreenEffect) { + if (prefer.s.enableSeasonalScreenEffect) { const month = new Date().getMonth() + 1; - if (defaultStore.state.hemisphere === 'S') { + if (prefer.s.hemisphere === 'S') { // ▼南半球 if (month === 7 || month === 8) { - const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; + const SnowfallEffect = (await import('@/utility/snowfall-effect.js')).SnowfallEffect; new SnowfallEffect({}).render(); } } else { // ▼北半球 if (month === 12 || month === 1) { - const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; + const SnowfallEffect = (await import('@/utility/snowfall-effect.js')).SnowfallEffect; new SnowfallEffect({}).render(); } else if (month === 3 || month === 4) { - const SakuraEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; + const SakuraEffect = (await import('@/utility/snowfall-effect.js')).SnowfallEffect; new SakuraEffect({ sakura: true, }).render(); @@ -136,8 +147,8 @@ export async function mainBoot() { } if ($i) { - defaultStore.loaded.then(() => { - if (defaultStore.state.accountSetupWizard !== -1) { + store.loaded.then(async () => { + if (store.s.accountSetupWizard !== -1) { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUserSetupDialog.vue')), {}, { closed: () => dispose(), }); @@ -152,7 +163,7 @@ export async function mainBoot() { }); } - function onAnnouncementCreated (ev: { announcement: Misskey.entities.Announcement }) { + function onAnnouncementCreated(ev: { announcement: Misskey.entities.Announcement }) { const announcement = ev.announcement; if (announcement.display === 'dialog') { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkAnnouncementDialog.vue')), { @@ -260,7 +271,7 @@ export async function mainBoot() { let lastVisibilityChangedAt = Date.now(); function claimPlainLucky() { - if (document.visibilityState !== 'visible') { + if (window.document.visibilityState !== 'visible') { if (justPlainLuckyTimer != null) window.clearTimeout(justPlainLuckyTimer); return; } @@ -275,7 +286,7 @@ export async function mainBoot() { window.addEventListener('visibilitychange', () => { const now = Date.now(); - if (document.visibilityState === 'visible') { + if (window.document.visibilityState === 'visible') { // タブを高速で切り替えたら取得処理が何度も走るのを防ぐ if ((now - lastVisibilityChangedAt) < 1000 * 10) { justPlainLuckyTimer = window.setTimeout(claimPlainLucky, 1000 * 10); @@ -320,7 +331,7 @@ export async function mainBoot() { const latestDonationInfoShownAt = miLocalStorage.getItem('latestDonationInfoShownAt'); const neverShowDonationInfo = miLocalStorage.getItem('neverShowDonationInfo'); - if (neverShowDonationInfo !== 'true' && (createdAt.getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !location.pathname.startsWith('/miauth')) { + if (neverShowDonationInfo !== 'true' && (createdAt.getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !window.location.pathname.startsWith('/miauth')) { if (latestDonationInfoShownAt == null || (new Date(latestDonationInfoShownAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 30)))) { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, { closed: () => dispose(), @@ -354,13 +365,13 @@ export async function mainBoot() { // 自分の情報が更新されたとき main.on('meUpdated', i => { - updateAccountPartial(i); + updateCurrentAccountPartial(i); }); main.on('readAllNotifications', () => { setFavIconDot(false); - updateAccountPartial({ + updateCurrentAccountPartial({ hasUnreadNotification: false, unreadNotificationsCount: 0, }); @@ -370,39 +381,24 @@ export async function mainBoot() { attemptShowNotificationDot(); const unreadNotificationsCount = ($i?.unreadNotificationsCount ?? 0) + 1; - updateAccountPartial({ + updateCurrentAccountPartial({ hasUnreadNotification: true, unreadNotificationsCount, }); }); - main.on('unreadMention', () => { - updateAccountPartial({ hasUnreadMentions: true }); - }); - - main.on('readAllUnreadMentions', () => { - updateAccountPartial({ hasUnreadMentions: false }); - }); - - main.on('unreadSpecifiedNote', () => { - updateAccountPartial({ hasUnreadSpecifiedNotes: true }); - }); - - main.on('readAllUnreadSpecifiedNotes', () => { - updateAccountPartial({ hasUnreadSpecifiedNotes: false }); - }); - - main.on('readAllAntennas', () => { - updateAccountPartial({ hasUnreadAntenna: false }); - }); - main.on('unreadAntenna', () => { - updateAccountPartial({ hasUnreadAntenna: true }); + updateCurrentAccountPartial({ hasUnreadAntenna: true }); sound.playMisskeySfx('antenna'); }); + main.on('newChatMessage', () => { + updateCurrentAccountPartial({ hasUnreadChatMessages: true }); + sound.playMisskeySfx('chat'); + }); + main.on('readAllAnnouncements', () => { - updateAccountPartial({ hasUnreadAnnouncement: false }); + updateCurrentAccountPartial({ hasUnreadAnnouncement: false }); }); // 個人宛てお知らせが発行されたとき @@ -422,13 +418,13 @@ export async function mainBoot() { post(); }, 'd': () => { - defaultStore.set('darkMode', !defaultStore.state.darkMode); + store.set('darkMode', !store.s.darkMode); }, 's': () => { mainRouter.push('/search'); }, } as const satisfies Keymap; - document.addEventListener('keydown', makeHotkey(keymap), { passive: false }); + window.document.addEventListener('keydown', makeHotkey(keymap), { passive: false }); initializeSw(); } diff --git a/packages/frontend/src/boot/sub-boot.ts b/packages/frontend/src/boot/sub-boot.ts index 35c84d5568..e24c324dfb 100644 --- a/packages/frontend/src/boot/sub-boot.ts +++ b/packages/frontend/src/boot/sub-boot.ts @@ -5,7 +5,7 @@ import { createApp, defineAsyncComponent } from 'vue'; import { common } from './common.js'; -import { emojiPicker } from '@/scripts/emoji-picker.js'; +import { emojiPicker } from '@/utility/emoji-picker.js'; export async function subBoot() { const { isClientUpdated } = await common(() => createApp( |