From 2cbe1d1210a5745787f37069ecb59b8f6c03c224 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Mon, 9 Sep 2024 20:57:36 +0900 Subject: feat(frontend): ノート・ユーザータイムライン埋め込み (#13929) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix * navhookをbootに移動 * サーバーサイドのbootも分けるように * 埋め込みページかどうかの判定は最初の一回だけに * tooltipは出せるように * fix design * 埋め込み独自のtooltipを削除 * ロジックの分岐が多かったMkNoteDetailedを分離 * fix indent * プレビュー用iframeにフォーカスが当たるのを修正 * popupの制御を出す側で行うように * パラメータが逆になっていたのを修正 * Update MkEmbedCodeGenDialog.vue * fix * eliminate misskey-js lint warns * fix * add appropriate attributes to embed html * enhance: サーバーサイドのembed系をさらに分離 * enhance: embed routerを分離(route定義をboot時に変更できるようにする改修を含む) * type * lint * fix indent * server-side styleを完全に分離 * Revert "refactor: 画面サイズのしきい値をconstにまとめる" This reverts commit 05ca36f400889456981e89489ae0ae242fa09b67. * fix * revert all changes in base.pug * embedドメインをまとめた * embedドメインをまとめた * prevent calling contextmenu in embed page by stopping at the caller * fix import * fix import * improve directory structure * fix import * register timeline ui as a container * wa- * rename * wa- * Update EmMediaList.vue * Update EmMediaList.vue * Update EmMediaList.vue * Update EmMediaImage.vue * Update EmNote.vue * revert mkmedialist changes * 戻し漏れ * wip * tweak embed media ui * revert original media components * Update boot.embed.js * rename * wip * Update MkNote.vue * wip * Update MkSubNoteContent.vue * Update EmNote.vue * Update packages/frontend/src/router/definition.ts * Revert "Update packages/frontend/src/router/definition.ts" This reverts commit 937ae44521cdb0f250796943b20142b65f8ed944. * refactor EmMediaImage * fix import * remove unused imports * Update router.ts * wip * Update boot.ts * wip * wip * wip * wip * Update EmNote.vue * Update EmNote.vue * Create EmA.vue * Create EmAvatar.vue * Update EmAvatar.vue * wip * wip * wip * Create EmImgWithBlurhash.vue * Update EmImgWithBlurhash.vue * Create EmPagination.vue * wip * Update boot.ts * wip * wip * wi@p * wip * wip * wiop * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Update boot.ts * wip * Update MkMisskeyFlavoredMarkdown.ts * wip * wip * wip * wip * wip * Update post-message.ts * wip * Update EmNoteDetailed.vue * Update EmNoteDetailed.vue * Create instance.ts * Update EmNoteDetailed.vue * wip * Update EmNoteDetailed.vue * wip * wip * wip * Update pnpm-lock.yaml * wip * wip * wp * wip * Update ClientServerService.ts * wip * Update boot.ts * Update vite.config.local-dev.ts * Update vite.config.ts * Create index.html * wa- * wip * Update boot.ts * wip * wip * wip * wip * wip * wip * wip * wip * wip * Create EmLink.vue * Create EmMention.vue * Update EmMfm.ts * wip * wip * wip * wip * Update vite.config.ts * Update boot.ts * Update EmA.vue * うぃp * wip * wip * Create EmError.vue * wip * Update MkEmbedCodeGenDialog.vue * Update EmNote.vue * wip * wip * Update user-timeline.vue * Update check-spdx-license-id.yml * wip * wip * style(frontend-shared): lint fixes on build.js * fix(frontend-shared): include `*.{js,json}` files in js-built * wip * use alias * refactor * refactor * Update scroll.ts * refactor * refactor * refactor * wip * wip * wip * wip * Update roles.vue * Update branding.vue * wip * wip * wip * Update page.vue * wip * fix import * add missing css variables * 絵文字をtwemojiに変更 クライアントデフォルトにあわせるため * force empoll readonly * fix compiler error * fix broken imports * tweak button style * run api extractor * fix storybook theme preloads * fix storybook instance imports * Update preview.ts * Update preview.ts * Update preview.ts * Revert "Update preview.ts" This reverts commit 12bab1c6fbd3baf753515df760ff19d027b85155. * Revert "Update preview.ts" This reverts commit 5c0ce01dbdf2194ffe94aba950f747a9968f29c4. * Revert "Update preview.ts" This reverts commit f4863524d7e5ca0f25470808849c24a72bea000a. * Revert "fix storybook instance imports" This reverts commit ed8eabb246edf731d31adffbe3c77c539e53ae9e. * Revert "wip" This reverts commit d3c1926519878155193a1654f49141e515d49683. * Revert "Update page.vue" This reverts commit 27c7900b0c1ae296b56075e8a9c22585d9cd744b. * Revert "Update branding.vue" This reverts commit c08ccb65ba66774c3e2b3dcfc6153004b5c0aa16. * Revert "Update roles.vue" This reverts commit 1488b670660cb1803d17d8f5c78f2d79e59fa52d. * Revert "wip" This reverts commit aab1c769814b08c257cad3025422a0eea3bfba4f. * refactor: use common media proxy * fix imports * fix * fix: MediaProxyの初期化を保証する(storybook対策?) * enhance(frontend-embed): improve embedParams provide * fix(backend): MK_DEV_PREFER=backendのときにembed viteが読み込めないのを修正 * fix * embed-pageを共通化 * fix import * fix import * fix import * const.jsを共通化 (たぶんrevertしすぎた) * fix type error * fix duplicated import * fix lint * fix * コメントとして残す * sharedとembedをlint対象にする * lint * attempt to fix eslint (frontend-shared) * lint fixes --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com> --- packages/frontend/src/boot/common.ts | 5 +- packages/frontend/src/boot/main-boot.ts | 13 + .../frontend/src/components/MkAutocomplete.vue | 6 +- packages/frontend/src/components/MkClickerGame.vue | 2 +- packages/frontend/src/components/MkCode.vue | 13 +- .../src/components/MkEmbedCodeGenDialog.vue | 412 +++++ .../src/components/MkEmojiPicker.section.vue | 2 +- packages/frontend/src/components/MkEmojiPicker.vue | 4 +- .../frontend/src/components/MkImgWithBlurhash.vue | 2 +- packages/frontend/src/components/MkInput.vue | 2 +- packages/frontend/src/components/MkMediaList.vue | 2 +- packages/frontend/src/components/MkMiniChart.vue | 2 +- packages/frontend/src/components/MkNote.vue | 2 +- .../src/components/MkNotificationSelectWindow.vue | 2 +- .../frontend/src/components/MkNotifications.vue | 2 +- packages/frontend/src/components/MkPageWindow.vue | 2 +- packages/frontend/src/components/MkPagination.vue | 4 +- packages/frontend/src/components/MkPoll.vue | 10 +- .../frontend/src/components/MkPullToRefresh.vue | 2 +- .../src/components/MkReactionsViewer.details.vue | 2 +- .../src/components/MkReactionsViewer.reaction.vue | 2 +- packages/frontend/src/components/MkSelect.vue | 2 +- packages/frontend/src/components/MkSignin.vue | 2 +- .../frontend/src/components/global/MkAvatar.vue | 2 +- .../frontend/src/components/global/MkEmoji.vue | 4 +- .../components/global/MkMisskeyFlavoredMarkdown.ts | 16 +- .../src/components/global/MkPageHeader.vue | 2 +- .../src/components/global/MkStickyContainer.vue | 2 +- packages/frontend/src/components/global/MkUrl.vue | 9 +- packages/frontend/src/const.ts | 137 -- packages/frontend/src/custom-emojis.ts | 22 +- packages/frontend/src/directives/follow-append.ts | 2 +- packages/frontend/src/emojilist.json | 1805 -------------------- packages/frontend/src/i18n.ts | 4 +- packages/frontend/src/instance.ts | 2 +- packages/frontend/src/local-storage.ts | 24 +- packages/frontend/src/nirax.ts | 9 +- packages/frontend/src/pages/admin/_header_.vue | 2 +- .../src/pages/admin/overview.instances.vue | 2 +- .../frontend/src/pages/admin/overview.users.vue | 2 +- packages/frontend/src/pages/admin/roles.editor.vue | 2 +- packages/frontend/src/pages/admin/roles.vue | 2 +- packages/frontend/src/pages/antenna-timeline.vue | 2 +- packages/frontend/src/pages/clip.vue | 39 +- packages/frontend/src/pages/drive.file.info.vue | 4 +- .../frontend/src/pages/drop-and-fusion.game.vue | 2 +- packages/frontend/src/pages/notifications.vue | 2 +- packages/frontend/src/pages/reversi/game.board.vue | 2 +- packages/frontend/src/pages/reversi/game.vue | 2 +- packages/frontend/src/pages/reversi/index.vue | 2 +- .../frontend/src/pages/settings/notifications.vue | 2 +- packages/frontend/src/pages/tag.vue | 15 +- packages/frontend/src/pages/theme-editor.vue | 4 +- packages/frontend/src/pages/timeline.vue | 4 +- packages/frontend/src/pages/user-list-timeline.vue | 2 +- packages/frontend/src/pages/user/home.vue | 2 +- packages/frontend/src/pages/welcome.timeline.vue | 2 +- packages/frontend/src/router/definition.ts | 39 +- packages/frontend/src/router/main.ts | 29 +- packages/frontend/src/scripts/aiscript/api.ts | 4 +- .../src/scripts/check-reaction-permissions.ts | 2 +- packages/frontend/src/scripts/code-highlighter.ts | 4 +- packages/frontend/src/scripts/emoji-base.ts | 25 - packages/frontend/src/scripts/emojilist.ts | 73 - .../src/scripts/extract-avg-color-from-blurhash.ts | 14 - packages/frontend/src/scripts/focus.ts | 2 +- packages/frontend/src/scripts/get-embed-code.ts | 87 + packages/frontend/src/scripts/get-note-menu.ts | 22 +- packages/frontend/src/scripts/get-user-menu.ts | 13 +- packages/frontend/src/scripts/i18n.ts | 245 --- packages/frontend/src/scripts/idb-proxy.ts | 11 +- packages/frontend/src/scripts/is-link.ts | 12 + packages/frontend/src/scripts/media-proxy.ts | 51 +- .../frontend/src/scripts/mfm-function-picker.ts | 2 +- packages/frontend/src/scripts/nyaize.ts | 27 - packages/frontend/src/scripts/popout.ts | 2 +- packages/frontend/src/scripts/post-message.ts | 2 +- packages/frontend/src/scripts/safe-parse.ts | 11 - packages/frontend/src/scripts/safe-uri-decode.ts | 12 - packages/frontend/src/scripts/scroll.ts | 144 -- packages/frontend/src/scripts/stream-mock.ts | 81 + packages/frontend/src/scripts/theme.ts | 4 +- packages/frontend/src/scripts/url.ts | 28 - .../src/scripts/use-document-visibility.ts | 24 - packages/frontend/src/scripts/use-interval.ts | 46 - packages/frontend/src/store.ts | 10 +- packages/frontend/src/stream.ts | 9 +- packages/frontend/src/themes/_dark.json5 | 93 - packages/frontend/src/themes/_light.json5 | 93 - packages/frontend/src/themes/d-astro.json5 | 69 - packages/frontend/src/themes/d-botanical.json5 | 26 - packages/frontend/src/themes/d-cherry.json5 | 21 - packages/frontend/src/themes/d-dark.json5 | 26 - packages/frontend/src/themes/d-future.json5 | 27 - packages/frontend/src/themes/d-green-lime.json5 | 24 - packages/frontend/src/themes/d-green-orange.json5 | 24 - packages/frontend/src/themes/d-ice.json5 | 14 - packages/frontend/src/themes/d-persimmon.json5 | 26 - packages/frontend/src/themes/d-u0.json5 | 83 - packages/frontend/src/themes/l-apricot.json5 | 23 - packages/frontend/src/themes/l-botanical.json5 | 30 - packages/frontend/src/themes/l-cherry.json5 | 22 - packages/frontend/src/themes/l-coffee.json5 | 22 - packages/frontend/src/themes/l-light.json5 | 21 - packages/frontend/src/themes/l-rainy.json5 | 22 - packages/frontend/src/themes/l-sushi.json5 | 19 - packages/frontend/src/themes/l-u0.json5 | 82 - packages/frontend/src/themes/l-vivid.json5 | 72 - .../src/ui/_common_/statusbar-federation.vue | 2 +- .../frontend/src/ui/_common_/statusbar-rss.vue | 2 +- .../src/ui/_common_/statusbar-user-list.vue | 2 +- packages/frontend/src/ui/deck/main-column.vue | 2 +- packages/frontend/src/ui/universal.vue | 2 +- .../src/widgets/WidgetBirthdayFollowings.vue | 2 +- packages/frontend/src/widgets/WidgetCalendar.vue | 2 +- packages/frontend/src/widgets/WidgetFederation.vue | 2 +- .../frontend/src/widgets/WidgetInstanceCloud.vue | 2 +- .../frontend/src/widgets/WidgetOnlineUsers.vue | 2 +- packages/frontend/src/widgets/WidgetRss.vue | 2 +- packages/frontend/src/widgets/WidgetRssTicker.vue | 2 +- packages/frontend/src/widgets/WidgetSlideshow.vue | 2 +- packages/frontend/src/widgets/WidgetTrends.vue | 2 +- packages/frontend/src/widgets/WidgetUserList.vue | 2 +- 123 files changed, 890 insertions(+), 3647 deletions(-) create mode 100644 packages/frontend/src/components/MkEmbedCodeGenDialog.vue delete mode 100644 packages/frontend/src/const.ts delete mode 100644 packages/frontend/src/emojilist.json delete mode 100644 packages/frontend/src/scripts/emoji-base.ts delete mode 100644 packages/frontend/src/scripts/emojilist.ts delete mode 100644 packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts create mode 100644 packages/frontend/src/scripts/get-embed-code.ts delete mode 100644 packages/frontend/src/scripts/i18n.ts create mode 100644 packages/frontend/src/scripts/is-link.ts delete mode 100644 packages/frontend/src/scripts/nyaize.ts delete mode 100644 packages/frontend/src/scripts/safe-parse.ts delete mode 100644 packages/frontend/src/scripts/safe-uri-decode.ts delete mode 100644 packages/frontend/src/scripts/scroll.ts create mode 100644 packages/frontend/src/scripts/stream-mock.ts delete mode 100644 packages/frontend/src/scripts/url.ts delete mode 100644 packages/frontend/src/scripts/use-document-visibility.ts delete mode 100644 packages/frontend/src/scripts/use-interval.ts delete mode 100644 packages/frontend/src/themes/_dark.json5 delete mode 100644 packages/frontend/src/themes/_light.json5 delete mode 100644 packages/frontend/src/themes/d-astro.json5 delete mode 100644 packages/frontend/src/themes/d-botanical.json5 delete mode 100644 packages/frontend/src/themes/d-cherry.json5 delete mode 100644 packages/frontend/src/themes/d-dark.json5 delete mode 100644 packages/frontend/src/themes/d-future.json5 delete mode 100644 packages/frontend/src/themes/d-green-lime.json5 delete mode 100644 packages/frontend/src/themes/d-green-orange.json5 delete mode 100644 packages/frontend/src/themes/d-ice.json5 delete mode 100644 packages/frontend/src/themes/d-persimmon.json5 delete mode 100644 packages/frontend/src/themes/d-u0.json5 delete mode 100644 packages/frontend/src/themes/l-apricot.json5 delete mode 100644 packages/frontend/src/themes/l-botanical.json5 delete mode 100644 packages/frontend/src/themes/l-cherry.json5 delete mode 100644 packages/frontend/src/themes/l-coffee.json5 delete mode 100644 packages/frontend/src/themes/l-light.json5 delete mode 100644 packages/frontend/src/themes/l-rainy.json5 delete mode 100644 packages/frontend/src/themes/l-sushi.json5 delete mode 100644 packages/frontend/src/themes/l-u0.json5 delete mode 100644 packages/frontend/src/themes/l-vivid.json5 (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index d86ae18ffe..19d30f64ce 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -22,7 +22,8 @@ import { getAccountFromId } from '@/scripts/get-account-from-id.js'; import { deckStore } from '@/ui/deck/deck-store.js'; import { miLocalStorage } from '@/local-storage.js'; import { fetchCustomEmojis } from '@/custom-emojis.js'; -import { setupRouter } from '@/router/definition.js'; +import { setupRouter } from '@/router/main.js'; +import { createMainRouter } from '@/router/definition.js'; export async function common(createVue: () => App) { console.info(`Misskey v${version}`); @@ -239,7 +240,7 @@ export async function common(createVue: () => App) { const app = createVue(); - setupRouter(app); + setupRouter(app, createMainRouter); if (_DEV_) { app.config.performance = true; diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 3e7c4f26f8..b31281dcf2 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -22,6 +22,7 @@ import { deckStore } from '@/ui/deck/deck-store.js'; import { emojiPicker } from '@/scripts/emoji-picker.js'; import { mainRouter } from '@/router/main.js'; import { type Keymap, makeHotkey } from '@/scripts/hotkey.js'; +import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js'; export async function mainBoot() { const { isClientUpdated } = await common(() => createApp( @@ -62,6 +63,18 @@ export async function mainBoot() { } }); + stream.on('emojiAdded', emojiData => { + addCustomEmoji(emojiData.emoji); + }); + + stream.on('emojiUpdated', emojiData => { + updateCustomEmojis(emojiData.emojis); + }); + + stream.on('emojiDeleted', emojiData => { + 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 diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 932c4ecb2e..f547991369 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -46,17 +46,17 @@ SPDX-License-Identifier: AGPL-3.0-only + + diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index c13164c296..fca7aa2f4e 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only `, + ]; + return iframeCode.join('\n'); +} + +/** + * 埋め込みコードを生成してコピーする(カスタマイズ機能つき) + * + * カスタマイズ機能がいらない場合(事前にパラメータを指定する場合)は getEmbedCode を直接使ってください + */ +export function genEmbedCode(entity: EmbeddableEntity, id: string, params?: EmbedParams) { + const _params = { ...params }; + + if (embedRouteWithScrollbar.includes(entity) && _params.maxHeight == null) { + _params.maxHeight = 700; + } + + // PCじゃない場合はコードカスタマイズ画面を出さずにそのままコピー + if (window.innerWidth < MOBILE_THRESHOLD) { + copyToClipboard(getEmbedCode(`/embed/${entity}/${id}`, _params)); + os.success(); + } else { + const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkEmbedCodeGenDialog.vue')), { + entity, + id, + params: _params, + }, { + closed: () => dispose(), + }); + } +} diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index b5d7350a41..e0ccea813d 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -21,6 +21,7 @@ import { MenuItem } from '@/types/menu.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { isSupportShare } from '@/scripts/navigator.js'; import { getAppearNote } from '@/scripts/get-appear-note.js'; +import { genEmbedCode } from '@/scripts/get-embed-code.js'; export async function getNoteClipMenu(props: { note: Misskey.entities.Note; @@ -156,6 +157,19 @@ export function getCopyNoteLinkMenu(note: Misskey.entities.Note, text: string): }; } +function getNoteEmbedCodeMenu(note: Misskey.entities.Note, text: string): MenuItem | undefined { + if (note.url != null || note.uri != null) return undefined; + if (['specified', 'followers'].includes(note.visibility)) return undefined; + + return { + icon: 'ti ti-code', + text, + action: (): void => { + genEmbedCode('notes', note.id); + }, + }; +} + export function getNoteMenu(props: { note: Misskey.entities.Note; translation: Ref; @@ -310,7 +324,7 @@ export function getNoteMenu(props: { action: () => { window.open(appearNote.url ?? appearNote.uri, '_blank', 'noopener'); }, - } : undefined, + } : getNoteEmbedCodeMenu(appearNote, i18n.ts.genEmbedCode), ...(isSupportShare() ? [{ icon: 'ti ti-share', text: i18n.ts.share, @@ -443,14 +457,14 @@ export function getNoteMenu(props: { icon: 'ti ti-copy', text: i18n.ts.copyContent, action: copyContent, - }, getCopyNoteLinkMenu(appearNote, i18n.ts.copyLink) - , (appearNote.url || appearNote.uri) ? { + }, getCopyNoteLinkMenu(appearNote, i18n.ts.copyLink), + (appearNote.url || appearNote.uri) ? { icon: 'ti ti-external-link', text: i18n.ts.showOnRemote, action: () => { window.open(appearNote.url ?? appearNote.uri, '_blank', 'noopener'); }, - } : undefined] + } : getNoteEmbedCodeMenu(appearNote, i18n.ts.genEmbedCode)] .filter(x => x !== undefined); } diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 33f16a68aa..035abc7bd0 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -17,6 +17,7 @@ import { notesSearchAvailable, canSearchNonLocalNotes } from '@/scripts/check-pe import { IRouter } from '@/nirax.js'; import { antennasCache, rolesCache, userListsCache } from '@/cache.js'; import { mainRouter } from '@/router/main.js'; +import { genEmbedCode } from '@/scripts/get-embed-code.js'; import { MenuItem } from '@/types/menu.js'; export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter = mainRouter) { @@ -179,7 +180,17 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter if (user.url == null) return; window.open(user.url, '_blank', 'noopener'); }, - }] : []), { + }] : [{ + icon: 'ti ti-code', + text: i18n.ts.genEmbedCode, + type: 'parent' as const, + children: [{ + text: i18n.ts.noteOfThisUser, + action: () => { + genEmbedCode('user-timeline', user.id); + }, + }], // TODO: ユーザーカードの埋め込みなど + }]), { icon: 'ti ti-share', text: i18n.ts.copyProfileUrl, action: () => { diff --git a/packages/frontend/src/scripts/i18n.ts b/packages/frontend/src/scripts/i18n.ts deleted file mode 100644 index b258a2a678..0000000000 --- a/packages/frontend/src/scripts/i18n.ts +++ /dev/null @@ -1,245 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ -import type { ILocale, ParameterizedString } from '../../../../locales/index.js'; - -type FlattenKeys = keyof { - [K in keyof T as T[K] extends ILocale - ? FlattenKeys extends infer C extends string - ? `${K & string}.${C}` - : never - : T[K] extends TPrediction - ? K - : never]: T[K]; -}; - -type ParametersOf> = TKey extends `${infer K}.${infer C}` - // @ts-expect-error -- C は明らかに FlattenKeys になるが、型システムはここでは TKey がドット区切りであることのコンテキストを持たないので、型システムに合法にて示すことはできない。 - ? ParametersOf - : TKey extends keyof T - ? T[TKey] extends ParameterizedString - ? P - : never - : never; - -type Tsx = { - readonly [K in keyof T as T[K] extends string ? never : K]: T[K] extends ParameterizedString - ? (arg: { readonly [_ in P]: string | number }) => string - // @ts-expect-error -- 証明省略 - : Tsx; -}; - -export class I18n { - private tsxCache?: Tsx; - - constructor(public locale: T) { - //#region BIND - this.t = this.t.bind(this); - //#endregion - } - - public get ts(): T { - if (_DEV_) { - class Handler implements ProxyHandler { - get(target: TTarget, p: string | symbol): unknown { - const value = target[p as keyof TTarget]; - - if (typeof value === 'object') { - return new Proxy(value, new Handler()); - } - - if (typeof value === 'string') { - const parameters = Array.from(value.matchAll(/\{(\w+)\}/g), ([, parameter]) => parameter); - - if (parameters.length) { - console.error(`Missing locale parameters: ${parameters.join(', ')} at ${String(p)}`); - } - - return value; - } - - console.error(`Unexpected locale key: ${String(p)}`); - - return p; - } - } - - return new Proxy(this.locale, new Handler()); - } - - return this.locale; - } - - public get tsx(): Tsx { - if (_DEV_) { - if (this.tsxCache) { - return this.tsxCache; - } - - class Handler implements ProxyHandler { - get(target: TTarget, p: string | symbol): unknown { - const value = target[p as keyof TTarget]; - - if (typeof value === 'object') { - return new Proxy(value, new Handler()); - } - - if (typeof value === 'string') { - const quasis: string[] = []; - const expressions: string[] = []; - let cursor = 0; - - while (~cursor) { - const start = value.indexOf('{', cursor); - - if (!~start) { - quasis.push(value.slice(cursor)); - break; - } - - quasis.push(value.slice(cursor, start)); - - const end = value.indexOf('}', start); - - expressions.push(value.slice(start + 1, end)); - - cursor = end + 1; - } - - if (!expressions.length) { - console.error(`Unexpected locale key: ${String(p)}`); - - return () => value; - } - - return (arg) => { - let str = quasis[0]; - - for (let i = 0; i < expressions.length; i++) { - if (!Object.hasOwn(arg, expressions[i])) { - console.error(`Missing locale parameters: ${expressions[i]} at ${String(p)}`); - } - - str += arg[expressions[i]] + quasis[i + 1]; - } - - return str; - }; - } - - console.error(`Unexpected locale key: ${String(p)}`); - - return p; - } - } - - return this.tsxCache = new Proxy(this.locale, new Handler()) as unknown as Tsx; - } - - if (this.tsxCache) { - return this.tsxCache; - } - - function build(target: ILocale): Tsx { - const result = {} as Tsx; - - for (const k in target) { - if (!Object.hasOwn(target, k)) { - continue; - } - - const value = target[k as keyof typeof target]; - - if (typeof value === 'object') { - result[k] = build(value as ILocale); - } else if (typeof value === 'string') { - const quasis: string[] = []; - const expressions: string[] = []; - let cursor = 0; - - while (~cursor) { - const start = value.indexOf('{', cursor); - - if (!~start) { - quasis.push(value.slice(cursor)); - break; - } - - quasis.push(value.slice(cursor, start)); - - const end = value.indexOf('}', start); - - expressions.push(value.slice(start + 1, end)); - - cursor = end + 1; - } - - if (!expressions.length) { - continue; - } - - result[k] = (arg) => { - let str = quasis[0]; - - for (let i = 0; i < expressions.length; i++) { - str += arg[expressions[i]] + quasis[i + 1]; - } - - return str; - }; - } - } - return result; - } - - return this.tsxCache = build(this.locale); - } - - /** - * @deprecated なるべくこのメソッド使うよりも ts 直接参照の方が vue のキャッシュ効いてパフォーマンスが良いかも - */ - public t>(key: TKey): string; - /** - * @deprecated なるべくこのメソッド使うよりも tsx 直接参照の方が vue のキャッシュ効いてパフォーマンスが良いかも - */ - public t>(key: TKey, args: { readonly [_ in ParametersOf]: string | number }): string; - public t(key: string, args?: { readonly [_: string]: string | number }) { - let str: string | ParameterizedString | ILocale = this.locale; - - for (const k of key.split('.')) { - str = str[k]; - - if (_DEV_) { - if (typeof str === 'undefined') { - console.error(`Unexpected locale key: ${key}`); - return key; - } - } - } - - if (args) { - if (_DEV_) { - const missing = Array.from((str as string).matchAll(/\{(\w+)\}/g), ([, parameter]) => parameter).filter(parameter => !Object.hasOwn(args, parameter)); - - if (missing.length) { - console.error(`Missing locale parameters: ${missing.join(', ')} at ${key}`); - } - } - - for (const [k, v] of Object.entries(args)) { - const search = `{${k}}`; - - if (_DEV_) { - if (!(str as string).includes(search)) { - console.error(`Unexpected locale parameter: ${k} at ${key}`); - } - } - - str = (str as string).replace(search, v.toString()); - } - } - - return str; - } -} diff --git a/packages/frontend/src/scripts/idb-proxy.ts b/packages/frontend/src/scripts/idb-proxy.ts index 6b511f2a5f..20f51660c7 100644 --- a/packages/frontend/src/scripts/idb-proxy.ts +++ b/packages/frontend/src/scripts/idb-proxy.ts @@ -10,10 +10,11 @@ import { set as iset, del as idel, } from 'idb-keyval'; +import { miLocalStorage } from '@/local-storage.js'; -const fallbackName = (key: string) => `idbfallback::${key}`; +const PREFIX = 'idbfallback::'; -let idbAvailable = typeof window !== 'undefined' ? !!(window.indexedDB && window.indexedDB.open) : true; +let idbAvailable = typeof window !== 'undefined' ? !!(window.indexedDB && typeof window.indexedDB.open === 'function') : true; // iframe.contentWindow.indexedDB.deleteDatabase() がchromeのバグで使用できないため、indexedDBを無効化している。 // バグが治って再度有効化するのであれば、cypressのコマンド内のコメントアウトを外すこと @@ -38,15 +39,15 @@ if (idbAvailable) { export async function get(key: string) { if (idbAvailable) return iget(key); - return JSON.parse(window.localStorage.getItem(fallbackName(key))); + return miLocalStorage.getItemAsJson(`${PREFIX}${key}`); } export async function set(key: string, val: any) { if (idbAvailable) return iset(key, val); - return window.localStorage.setItem(fallbackName(key), JSON.stringify(val)); + return miLocalStorage.setItemAsJson(`${PREFIX}${key}`, val); } export async function del(key: string) { if (idbAvailable) return idel(key); - return window.localStorage.removeItem(fallbackName(key)); + return miLocalStorage.removeItem(`${PREFIX}${key}`); } diff --git a/packages/frontend/src/scripts/is-link.ts b/packages/frontend/src/scripts/is-link.ts new file mode 100644 index 0000000000..946f86400e --- /dev/null +++ b/packages/frontend/src/scripts/is-link.ts @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export function isLink(el: HTMLElement) { + if (el.tagName === 'A') return true; + if (el.parentElement) { + return isLink(el.parentElement); + } + return false; +} diff --git a/packages/frontend/src/scripts/media-proxy.ts b/packages/frontend/src/scripts/media-proxy.ts index 099a22163a..68a5a1dcf8 100644 --- a/packages/frontend/src/scripts/media-proxy.ts +++ b/packages/frontend/src/scripts/media-proxy.ts @@ -3,51 +3,32 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { query } from '@/scripts/url.js'; +import { MediaProxy } from '@@/js/media-proxy.js'; import { url } from '@/config.js'; import { instance } from '@/instance.js'; -export function getProxiedImageUrl(imageUrl: string, type?: 'preview' | 'emoji' | 'avatar', mustOrigin = false, noFallback = false): string { - const localProxy = `${url}/proxy`; +let _mediaProxy: MediaProxy | null = null; - if (imageUrl.startsWith(instance.mediaProxy + '/') || imageUrl.startsWith('/proxy/') || imageUrl.startsWith(localProxy + '/')) { - // もう既にproxyっぽそうだったらurlを取り出す - imageUrl = (new URL(imageUrl)).searchParams.get('url') ?? imageUrl; +export function getProxiedImageUrl(...args: Parameters): string { + if (_mediaProxy == null) { + _mediaProxy = new MediaProxy(instance, url); } - return `${mustOrigin ? localProxy : instance.mediaProxy}/${ - type === 'preview' ? 'preview.webp' - : 'image.webp' - }?${query({ - url: imageUrl, - ...(!noFallback ? { 'fallback': '1' } : {}), - ...(type ? { [type]: '1' } : {}), - ...(mustOrigin ? { origin: '1' } : {}), - })}`; + return _mediaProxy.getProxiedImageUrl(...args); } -export function getProxiedImageUrlNullable(imageUrl: string | null | undefined, type?: 'preview'): string | null { - if (imageUrl == null) return null; - return getProxiedImageUrl(imageUrl, type); -} - -export function getStaticImageUrl(baseUrl: string): string { - const u = baseUrl.startsWith('http') ? new URL(baseUrl) : new URL(baseUrl, url); - - if (u.href.startsWith(`${url}/emoji/`)) { - // もう既にemojiっぽそうだったらsearchParams付けるだけ - u.searchParams.set('static', '1'); - return u.href; +export function getProxiedImageUrlNullable(...args: Parameters): string | null { + if (_mediaProxy == null) { + _mediaProxy = new MediaProxy(instance, url); } - if (u.href.startsWith(instance.mediaProxy + '/')) { - // もう既にproxyっぽそうだったらsearchParams付けるだけ - u.searchParams.set('static', '1'); - return u.href; + return _mediaProxy.getProxiedImageUrlNullable(...args); +} + +export function getStaticImageUrl(...args: Parameters): string { + if (_mediaProxy == null) { + _mediaProxy = new MediaProxy(instance, url); } - return `${instance.mediaProxy}/static.webp?${query({ - url: u.href, - static: '1', - })}`; + return _mediaProxy.getStaticImageUrl(...args); } diff --git a/packages/frontend/src/scripts/mfm-function-picker.ts b/packages/frontend/src/scripts/mfm-function-picker.ts index 9938e534c1..bf59fe98a0 100644 --- a/packages/frontend/src/scripts/mfm-function-picker.ts +++ b/packages/frontend/src/scripts/mfm-function-picker.ts @@ -6,7 +6,7 @@ import { Ref, nextTick } from 'vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; -import { MFM_TAGS } from '@/const.js'; +import { MFM_TAGS } from '@@/js/const.js'; import type { MenuItem } from '@/types/menu.js'; /** diff --git a/packages/frontend/src/scripts/nyaize.ts b/packages/frontend/src/scripts/nyaize.ts deleted file mode 100644 index abc8ada461..0000000000 --- a/packages/frontend/src/scripts/nyaize.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -const enRegex1 = /(?<=n)a/gi; -const enRegex2 = /(?<=morn)ing/gi; -const enRegex3 = /(?<=every)one/gi; -const koRegex1 = /[나-낳]/g; -const koRegex2 = /(다$)|(다(?=\.))|(다(?= ))|(다(?=!))|(다(?=\?))/gm; -const koRegex3 = /(야(?=\?))|(야$)|(야(?= ))/gm; - -export function nyaize(text: string): string { - return text - // ja-JP - .replaceAll('な', 'にゃ').replaceAll('ナ', 'ニャ').replaceAll('ナ', 'ニャ') - // en-US - .replace(enRegex1, x => x === 'A' ? 'YA' : 'ya') - .replace(enRegex2, x => x === 'ING' ? 'YAN' : 'yan') - .replace(enRegex3, x => x === 'ONE' ? 'NYAN' : 'nyan') - // ko-KR - .replace(koRegex1, match => String.fromCharCode( - match.charCodeAt(0)! + '냐'.charCodeAt(0) - '나'.charCodeAt(0), - )) - .replace(koRegex2, '다냥') - .replace(koRegex3, '냥'); -} diff --git a/packages/frontend/src/scripts/popout.ts b/packages/frontend/src/scripts/popout.ts index 1caa2dfc21..ed49611b4f 100644 --- a/packages/frontend/src/scripts/popout.ts +++ b/packages/frontend/src/scripts/popout.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { appendQuery } from './url.js'; +import { appendQuery } from '@@/js/url.js'; import * as config from '@/config.js'; export function popout(path: string, w?: HTMLElement) { diff --git a/packages/frontend/src/scripts/post-message.ts b/packages/frontend/src/scripts/post-message.ts index 31a9ac1ad9..11b6f52ddd 100644 --- a/packages/frontend/src/scripts/post-message.ts +++ b/packages/frontend/src/scripts/post-message.ts @@ -18,7 +18,7 @@ export type MiPostMessageEvent = { * 親フレームにイベントを送信 */ export function postMessageToParentWindow(type: PostMessageEventType, payload?: any): void { - window.postMessage({ + window.parent.postMessage({ type, payload, }, '*'); diff --git a/packages/frontend/src/scripts/safe-parse.ts b/packages/frontend/src/scripts/safe-parse.ts deleted file mode 100644 index 6bfcef6c36..0000000000 --- a/packages/frontend/src/scripts/safe-parse.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -export function safeParseFloat(str: unknown): number | null { - if (typeof str !== 'string' || str === '') return null; - const num = parseFloat(str); - if (isNaN(num)) return null; - return num; -} diff --git a/packages/frontend/src/scripts/safe-uri-decode.ts b/packages/frontend/src/scripts/safe-uri-decode.ts deleted file mode 100644 index 0edf4e9eba..0000000000 --- a/packages/frontend/src/scripts/safe-uri-decode.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -export function safeURIDecode(str: string): string { - try { - return decodeURIComponent(str); - } catch { - return str; - } -} diff --git a/packages/frontend/src/scripts/scroll.ts b/packages/frontend/src/scripts/scroll.ts deleted file mode 100644 index f0274034b5..0000000000 --- a/packages/frontend/src/scripts/scroll.ts +++ /dev/null @@ -1,144 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -type ScrollBehavior = 'auto' | 'smooth' | 'instant'; - -export function getScrollContainer(el: HTMLElement | null): HTMLElement | null { - if (el == null || el.tagName === 'HTML') return null; - const overflow = window.getComputedStyle(el).getPropertyValue('overflow-y'); - if (overflow === 'scroll' || overflow === 'auto') { - return el; - } else { - return getScrollContainer(el.parentElement); - } -} - -export function getStickyTop(el: HTMLElement, container: HTMLElement | null = null, top = 0) { - if (!el.parentElement) return top; - const data = el.dataset.stickyContainerHeaderHeight; - const newTop = data ? Number(data) + top : top; - if (el === container) return newTop; - return getStickyTop(el.parentElement, container, newTop); -} - -export function getStickyBottom(el: HTMLElement, container: HTMLElement | null = null, bottom = 0) { - if (!el.parentElement) return bottom; - const data = el.dataset.stickyContainerFooterHeight; - const newBottom = data ? Number(data) + bottom : bottom; - if (el === container) return newBottom; - return getStickyBottom(el.parentElement, container, newBottom); -} - -export function getScrollPosition(el: HTMLElement | null): number { - const container = getScrollContainer(el); - return container == null ? window.scrollY : container.scrollTop; -} - -export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) { - // とりあえず評価してみる - if (el.isConnected && isTopVisible(el)) { - cb(); - if (once) return null; - } - - const container = getScrollContainer(el) ?? window; - - const onScroll = ev => { - if (!document.body.contains(el)) return; - if (isTopVisible(el, tolerance)) { - cb(); - if (once) removeListener(); - } - }; - - function removeListener() { container.removeEventListener('scroll', onScroll); } - - container.addEventListener('scroll', onScroll, { passive: true }); - return removeListener; -} - -export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) { - const container = getScrollContainer(el); - - // とりあえず評価してみる - if (el.isConnected && isBottomVisible(el, tolerance, container)) { - cb(); - if (once) return null; - } - - const containerOrWindow = container ?? window; - const onScroll = ev => { - if (!document.body.contains(el)) return; - if (isBottomVisible(el, 1, container)) { - cb(); - if (once) removeListener(); - } - }; - - function removeListener() { - containerOrWindow.removeEventListener('scroll', onScroll); - } - - containerOrWindow.addEventListener('scroll', onScroll, { passive: true }); - return removeListener; -} - -export function scroll(el: HTMLElement, options: ScrollToOptions | undefined) { - const container = getScrollContainer(el); - if (container == null) { - window.scroll(options); - } else { - container.scroll(options); - } -} - -/** - * Scroll to Top - * @param el Scroll container element - * @param options Scroll options - */ -export function scrollToTop(el: HTMLElement, options: { behavior?: ScrollBehavior; } = {}) { - scroll(el, { top: 0, ...options }); -} - -/** - * Scroll to Bottom - * @param el Content element - * @param options Scroll options - * @param container Scroll container element - */ -export function scrollToBottom( - el: HTMLElement, - options: ScrollToOptions = {}, - container = getScrollContainer(el), -) { - if (container) { - container.scroll({ top: el.scrollHeight - container.clientHeight + getStickyTop(el, container) || 0, ...options }); - } else { - window.scroll({ - top: (el.scrollHeight - window.innerHeight + getStickyTop(el, container) + (window.innerWidth <= 500 ? 96 : 0)) || 0, - ...options, - }); - } -} - -export function isTopVisible(el: HTMLElement, tolerance = 1): boolean { - const scrollTop = getScrollPosition(el); - return scrollTop <= tolerance; -} - -export function isBottomVisible(el: HTMLElement, tolerance = 1, container = getScrollContainer(el)) { - if (container) return el.scrollHeight <= container.clientHeight + Math.abs(container.scrollTop) + tolerance; - return el.scrollHeight <= window.innerHeight + window.scrollY + tolerance; -} - -// https://ja.javascript.info/size-and-scroll-window#ref-932 -export function getBodyScrollHeight() { - return Math.max( - document.body.scrollHeight, document.documentElement.scrollHeight, - document.body.offsetHeight, document.documentElement.offsetHeight, - document.body.clientHeight, document.documentElement.clientHeight, - ); -} diff --git a/packages/frontend/src/scripts/stream-mock.ts b/packages/frontend/src/scripts/stream-mock.ts new file mode 100644 index 0000000000..cb0e607fcb --- /dev/null +++ b/packages/frontend/src/scripts/stream-mock.ts @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { EventEmitter } from 'eventemitter3'; +import * as Misskey from 'misskey-js'; +import type { Channels, StreamEvents, IStream, IChannelConnection } from 'misskey-js'; + +type AnyOf> = T[keyof T]; +type OmitFirst = T extends [any, ...infer R] ? R : never; + +/** + * Websocket無効化時に使うStreamのモック(なにもしない) + */ +export class StreamMock extends EventEmitter implements IStream { + public readonly state = 'initializing'; + + constructor(...args: ConstructorParameters) { + super(); + // do nothing + } + + public useChannel(channel: C, params?: Channels[C]['params'], name?: string): ChannelConnectionMock { + return new ChannelConnectionMock(this, channel, name); + } + + public removeSharedConnection(connection: any): void { + // do nothing + } + + public removeSharedConnectionPool(pool: any): void { + // do nothing + } + + public disconnectToChannel(): void { + // do nothing + } + + public send(typeOrPayload: string): void + public send(typeOrPayload: string, payload: any): void + public send(typeOrPayload: Record | any[]): void + public send(typeOrPayload: string | Record | any[], payload?: any): void { + // do nothing + } + + public ping(): void { + // do nothing + } + + public heartbeat(): void { + // do nothing + } + + public close(): void { + // do nothing + } +} + +class ChannelConnectionMock = any> extends EventEmitter implements IChannelConnection { + public id = ''; + public name?: string; // for debug + public inCount = 0; // for debug + public outCount = 0; // for debug + public channel: string; + + constructor(stream: IStream, ...args: OmitFirst>>) { + super(); + + this.channel = args[0]; + this.name = args[1]; + } + + public send(type: T, body: Channel['receives'][T]): void { + // do nothing + } + + public dispose(): void { + // do nothing + } +} diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts index c7f8b3d596..9b9f1f030c 100644 --- a/packages/frontend/src/scripts/theme.ts +++ b/packages/frontend/src/scripts/theme.ts @@ -5,11 +5,11 @@ import { ref } from 'vue'; import tinycolor from 'tinycolor2'; +import lightTheme from '@@/themes/_light.json5'; +import darkTheme from '@@/themes/_dark.json5'; import { deepClone } from './clone.js'; import type { BundledTheme } from 'shiki/themes'; import { globalEvents } from '@/events.js'; -import lightTheme from '@/themes/_light.json5'; -import darkTheme from '@/themes/_dark.json5'; import { miLocalStorage } from '@/local-storage.js'; export type Theme = { diff --git a/packages/frontend/src/scripts/url.ts b/packages/frontend/src/scripts/url.ts deleted file mode 100644 index 5a8265af9e..0000000000 --- a/packages/frontend/src/scripts/url.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -/* objを検査して - * 1. 配列に何も入っていない時はクエリを付けない - * 2. プロパティがundefinedの時はクエリを付けない - * (new URLSearchParams(obj)ではそこまで丁寧なことをしてくれない) - */ -export function query(obj: Record): string { - const params = Object.entries(obj) - .filter(([, v]) => Array.isArray(v) ? v.length : v !== undefined) - .reduce((a, [k, v]) => (a[k] = v, a), {} as Record); - - return Object.entries(params) - .map((p) => `${p[0]}=${encodeURIComponent(p[1])}`) - .join('&'); -} - -export function appendQuery(url: string, query: string): string { - return `${url}${/\?/.test(url) ? url.endsWith('?') ? '' : '&' : '?'}${query}`; -} - -export function extractDomain(url: string) { - const match = url.match(/^(?:https?:)?(?:\/\/)?(?:[^@\n]+@)?([^:\/\n]+)/im); - return match ? match[1] : null; -} diff --git a/packages/frontend/src/scripts/use-document-visibility.ts b/packages/frontend/src/scripts/use-document-visibility.ts deleted file mode 100644 index a8f4d5e03a..0000000000 --- a/packages/frontend/src/scripts/use-document-visibility.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { onMounted, onUnmounted, ref, Ref } from 'vue'; - -export function useDocumentVisibility(): Ref { - const visibility = ref(document.visibilityState); - - const onChange = (): void => { - visibility.value = document.visibilityState; - }; - - onMounted(() => { - document.addEventListener('visibilitychange', onChange); - }); - - onUnmounted(() => { - document.removeEventListener('visibilitychange', onChange); - }); - - return visibility; -} diff --git a/packages/frontend/src/scripts/use-interval.ts b/packages/frontend/src/scripts/use-interval.ts deleted file mode 100644 index b50e78c3cc..0000000000 --- a/packages/frontend/src/scripts/use-interval.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue'; - -export function useInterval(fn: () => void, interval: number, options: { - immediate: boolean; - afterMounted: boolean; -}): (() => void) | undefined { - if (Number.isNaN(interval)) return; - - let intervalId: number | null = null; - - if (options.afterMounted) { - onMounted(() => { - if (options.immediate) fn(); - intervalId = window.setInterval(fn, interval); - }); - } else { - if (options.immediate) fn(); - intervalId = window.setInterval(fn, interval); - } - - const clear = () => { - if (intervalId) window.clearInterval(intervalId); - intervalId = null; - }; - - onActivated(() => { - if (intervalId) return; - if (options.immediate) fn(); - intervalId = window.setInterval(fn, interval); - }); - - onDeactivated(() => { - clear(); - }); - - onUnmounted(() => { - clear(); - }); - - return clear; -} diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 437314074a..0bf499bb4d 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -458,10 +458,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, - contextMenu: { + contextMenu: { where: 'device', default: 'app' as 'app' | 'appWithShift' | 'native', - }, + }, sound_masterVolume: { where: 'device', @@ -520,8 +520,8 @@ interface Watcher { /** * 常にメモリにロードしておく必要がないような設定情報を保管するストレージ(非リアクティブ) */ -import lightTheme from '@/themes/l-light.json5'; -import darkTheme from '@/themes/d-green-lime.json5'; +import lightTheme from '@@/themes/l-light.json5'; +import darkTheme from '@@/themes/d-green-lime.json5'; export class ColdDeviceStorage { public static default = { @@ -558,7 +558,7 @@ export class ColdDeviceStorage { public static set(key: T, value: typeof ColdDeviceStorage.default[T]): void { // 呼び出し側のバグ等で undefined が来ることがある // undefined を文字列として miLocalStorage に入れると参照する際の JSON.parse でコケて不具合の元になるため無視 - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (value === undefined) { console.error(`attempt to store undefined value for key '${key}'`); return; diff --git a/packages/frontend/src/stream.ts b/packages/frontend/src/stream.ts index 0d5bd78b09..9d7edce890 100644 --- a/packages/frontend/src/stream.ts +++ b/packages/frontend/src/stream.ts @@ -7,17 +7,20 @@ import * as Misskey from 'misskey-js'; import { markRaw } from 'vue'; import { $i } from '@/account.js'; import { wsOrigin } from '@/config.js'; +// TODO: No WebsocketモードでStreamMockが使えそう +//import { StreamMock } from '@/scripts/stream-mock.js'; // heart beat interval in ms const HEART_BEAT_INTERVAL = 1000 * 60; -let stream: Misskey.Stream | null = null; -let timeoutHeartBeat: ReturnType | null = null; +let stream: Misskey.IStream | null = null; +let timeoutHeartBeat: number | null = null; let lastHeartbeatCall = 0; -export function useStream(): Misskey.Stream { +export function useStream(): Misskey.IStream { if (stream) return stream; + // TODO: No Websocketモードもここで判定 stream = markRaw(new Misskey.Stream(wsOrigin, $i ? { token: $i.token, } : null)); diff --git a/packages/frontend/src/themes/_dark.json5 b/packages/frontend/src/themes/_dark.json5 deleted file mode 100644 index 17fb98e4ee..0000000000 --- a/packages/frontend/src/themes/_dark.json5 +++ /dev/null @@ -1,93 +0,0 @@ -// ダークテーマのベーステーマ -// このテーマが直接使われることは無い -{ - id: 'dark', - - name: 'Dark', - author: 'syuilo', - desc: 'Default dark theme', - kind: 'dark', - - props: { - accent: '#86b300', - accentDarken: ':darken<10<@accent', - accentLighten: ':lighten<10<@accent', - accentedBg: ':alpha<0.15<@accent', - focus: ':alpha<0.3<@accent', - bg: '#000', - acrylicBg: ':alpha<0.5<@bg', - fg: '#dadada', - fgTransparentWeak: ':alpha<0.75<@fg', - fgTransparent: ':alpha<0.5<@fg', - fgHighlighted: ':lighten<3<@fg', - fgOnAccent: '#fff', - fgOnWhite: '#333', - divider: 'rgba(255, 255, 255, 0.1)', - indicator: '@accent', - panel: ':lighten<3<@bg', - panelHighlight: ':lighten<3<@panel', - panelHeaderBg: ':lighten<3<@panel', - panelHeaderFg: '@fg', - panelHeaderDivider: 'rgba(0, 0, 0, 0)', - panelBorder: '" solid 1px var(--divider)', - acrylicPanel: ':alpha<0.5<@panel', - windowHeader: ':alpha<0.85<@panel', - popup: ':lighten<3<@panel', - shadow: 'rgba(0, 0, 0, 0.3)', - header: ':alpha<0.7<@panel', - navBg: '@panel', - navFg: '@fg', - navHoverFg: ':lighten<17<@fg', - navActive: '@accent', - navIndicator: '@indicator', - link: '#44a4c1', - hashtag: '#ff9156', - mention: '@accent', - mentionMe: '@mention', - renote: '#229e82', - modalBg: 'rgba(0, 0, 0, 0.5)', - scrollbarHandle: 'rgba(255, 255, 255, 0.2)', - scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)', - dateLabelFg: '@fg', - infoBg: '#253142', - infoFg: '#fff', - infoWarnBg: '#42321c', - infoWarnFg: '#ffbd3e', - switchBg: 'rgba(255, 255, 255, 0.15)', - buttonBg: 'rgba(255, 255, 255, 0.05)', - buttonHoverBg: 'rgba(255, 255, 255, 0.1)', - buttonGradateA: '@accent', - buttonGradateB: ':hue<20<@accent', - switchOffBg: 'rgba(255, 255, 255, 0.1)', - switchOffFg: ':alpha<0.8<@fg', - switchOnBg: '@accentedBg', - switchOnFg: '@accent', - inputBorder: 'rgba(255, 255, 255, 0.1)', - inputBorderHover: 'rgba(255, 255, 255, 0.2)', - listItemHoverBg: 'rgba(255, 255, 255, 0.03)', - driveFolderBg: ':alpha<0.3<@accent', - wallpaperOverlay: 'rgba(0, 0, 0, 0.5)', - badge: '#31b1ce', - messageBg: '@bg', - success: '#86b300', - error: '#ec4137', - warn: '#ecb637', - codeString: '#ffb675', - codeNumber: '#cfff9e', - codeBoolean: '#c59eff', - deckBg: '#000', - htmlThemeColor: '@bg', - X3: 'rgba(255, 255, 255, 0.05)', - X4: 'rgba(255, 255, 255, 0.1)', - X5: 'rgba(255, 255, 255, 0.05)', - X6: 'rgba(255, 255, 255, 0.15)', - X7: 'rgba(255, 255, 255, 0.05)', - X11: 'rgba(0, 0, 0, 0.3)', - X12: 'rgba(255, 255, 255, 0.1)', - X13: 'rgba(255, 255, 255, 0.15)', - }, - - codeHighlighter: { - base: 'one-dark-pro', - }, -} diff --git a/packages/frontend/src/themes/_light.json5 b/packages/frontend/src/themes/_light.json5 deleted file mode 100644 index ca6c059e16..0000000000 --- a/packages/frontend/src/themes/_light.json5 +++ /dev/null @@ -1,93 +0,0 @@ -// ライトテーマのベーステーマ -// このテーマが直接使われることは無い -{ - id: 'light', - - name: 'Light', - author: 'syuilo', - desc: 'Default light theme', - kind: 'light', - - props: { - accent: '#86b300', - accentDarken: ':darken<10<@accent', - accentLighten: ':lighten<10<@accent', - accentedBg: ':alpha<0.15<@accent', - focus: ':alpha<0.3<@accent', - bg: '#fff', - acrylicBg: ':alpha<0.5<@bg', - fg: '#5f5f5f', - fgTransparentWeak: ':alpha<0.75<@fg', - fgTransparent: ':alpha<0.5<@fg', - fgHighlighted: ':darken<3<@fg', - fgOnAccent: '#fff', - fgOnWhite: '#333', - divider: 'rgba(0, 0, 0, 0.1)', - indicator: '@accent', - panel: ':lighten<3<@bg', - panelHighlight: ':darken<3<@panel', - panelHeaderBg: ':lighten<3<@panel', - panelHeaderFg: '@fg', - panelHeaderDivider: 'rgba(0, 0, 0, 0)', - panelBorder: '" solid 1px var(--divider)', - acrylicPanel: ':alpha<0.5<@panel', - windowHeader: ':alpha<0.85<@panel', - popup: ':lighten<3<@panel', - shadow: 'rgba(0, 0, 0, 0.1)', - header: ':alpha<0.7<@panel', - navBg: '@panel', - navFg: '@fg', - navHoverFg: ':darken<17<@fg', - navActive: '@accent', - navIndicator: '@indicator', - link: '#44a4c1', - hashtag: '#ff9156', - mention: '@accent', - mentionMe: '@mention', - renote: '#229e82', - modalBg: 'rgba(0, 0, 0, 0.3)', - scrollbarHandle: 'rgba(0, 0, 0, 0.2)', - scrollbarHandleHover: 'rgba(0, 0, 0, 0.4)', - dateLabelFg: '@fg', - infoBg: '#e5f5ff', - infoFg: '#72818a', - infoWarnBg: '#fff0db', - infoWarnFg: '#8f6e31', - switchBg: 'rgba(0, 0, 0, 0.15)', - buttonBg: 'rgba(0, 0, 0, 0.05)', - buttonHoverBg: 'rgba(0, 0, 0, 0.1)', - buttonGradateA: '@accent', - buttonGradateB: ':hue<20<@accent', - switchOffBg: 'rgba(0, 0, 0, 0.1)', - switchOffFg: '@panel', - switchOnBg: '@accent', - switchOnFg: '@fgOnAccent', - inputBorder: 'rgba(0, 0, 0, 0.1)', - inputBorderHover: 'rgba(0, 0, 0, 0.2)', - listItemHoverBg: 'rgba(0, 0, 0, 0.03)', - driveFolderBg: ':alpha<0.3<@accent', - wallpaperOverlay: 'rgba(255, 255, 255, 0.5)', - badge: '#31b1ce', - messageBg: '@bg', - success: '#86b300', - error: '#ec4137', - warn: '#ecb637', - codeString: '#b98710', - codeNumber: '#0fbbbb', - codeBoolean: '#62b70c', - deckBg: ':darken<3<@bg', - htmlThemeColor: '@bg', - X3: 'rgba(0, 0, 0, 0.05)', - X4: 'rgba(0, 0, 0, 0.1)', - X5: 'rgba(0, 0, 0, 0.05)', - X6: 'rgba(0, 0, 0, 0.25)', - X7: 'rgba(0, 0, 0, 0.05)', - X11: 'rgba(0, 0, 0, 0.1)', - X12: 'rgba(0, 0, 0, 0.1)', - X13: 'rgba(0, 0, 0, 0.15)', - }, - - codeHighlighter: { - base: 'catppuccin-latte', - }, -} diff --git a/packages/frontend/src/themes/d-astro.json5 b/packages/frontend/src/themes/d-astro.json5 deleted file mode 100644 index 1cbb4e519d..0000000000 --- a/packages/frontend/src/themes/d-astro.json5 +++ /dev/null @@ -1,69 +0,0 @@ -{ - id: '080a01c5-377d-4fbb-88cc-6bb5d04977ea', - base: 'dark', - name: 'Mi Astro Dark', - author: 'syuilo', - props: { - bg: '#232125', - fg: '#efdab9', - link: '#78b0a0', - warn: '#ecb637', - badge: '#31b1ce', - error: '#ec4137', - focus: ':alpha<0.3<@accent', - navBg: '@panel', - navFg: '@fg', - panel: '#2a272b', - accent: '#81c08b', - header: ':alpha<0.7<@bg', - infoBg: '#253142', - infoFg: '#fff', - renote: '#659CC8', - shadow: 'rgba(0, 0, 0, 0.3)', - divider: 'rgba(255, 255, 255, 0.1)', - hashtag: '#ff9156', - mention: '#ffd152', - modalBg: 'rgba(0, 0, 0, 0.5)', - success: '#86b300', - buttonBg: 'rgba(255, 255, 255, 0.05)', - acrylicBg: ':alpha<0.5<@bg', - indicator: '@accent', - mentionMe: '#fb5d38', - messageBg: '@bg', - navActive: '@accent', - infoWarnBg: '#42321c', - infoWarnFg: '#ffbd3e', - navHoverFg: ':lighten<17<@fg', - dateLabelFg: '@fg', - inputBorder: 'rgba(255, 255, 255, 0.1)', - inputBorderHover: 'rgba(255, 255, 255, 0.2)', - panelBorder: '" solid 1px var(--divider)', - accentDarken: ':darken<10<@accent', - acrylicPanel: ':alpha<0.5<@panel', - navIndicator: '@accent', - accentLighten: ':lighten<10<@accent', - buttonHoverBg: 'rgba(255, 255, 255, 0.1)', - buttonGradateA: '@accent', - buttonGradateB: ':hue<-20<@accent', - driveFolderBg: ':alpha<0.3<@accent', - fgHighlighted: ':lighten<3<@fg', - panelHeaderBg: ':lighten<3<@panel', - panelHeaderFg: '@fg', - htmlThemeColor: '@bg', - fgOnWhite: '@accent', - panelHighlight: ':lighten<3<@panel', - listItemHoverBg: 'rgba(255, 255, 255, 0.03)', - scrollbarHandle: 'rgba(255, 255, 255, 0.2)', - wallpaperOverlay: 'rgba(0, 0, 0, 0.5)', - panelHeaderDivider: 'rgba(0, 0, 0, 0)', - scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)', - X3: 'rgba(255, 255, 255, 0.05)', - X4: 'rgba(255, 255, 255, 0.1)', - X5: 'rgba(255, 255, 255, 0.05)', - X6: 'rgba(255, 255, 255, 0.15)', - X7: 'rgba(255, 255, 255, 0.05)', - X11: 'rgba(0, 0, 0, 0.3)', - X12: 'rgba(255, 255, 255, 0.1)', - X13: 'rgba(255, 255, 255, 0.15)', - }, -} diff --git a/packages/frontend/src/themes/d-botanical.json5 b/packages/frontend/src/themes/d-botanical.json5 deleted file mode 100644 index 62208d2378..0000000000 --- a/packages/frontend/src/themes/d-botanical.json5 +++ /dev/null @@ -1,26 +0,0 @@ -{ - id: '504debaf-4912-6a4c-5059-1db08a76b737', - - name: 'Mi Botanical Dark', - author: 'syuilo', - - base: 'dark', - - props: { - accent: 'rgb(148, 179, 0)', - bg: 'rgb(37, 38, 36)', - fg: 'rgb(216, 212, 199)', - fgHighlighted: '#fff', - fgOnWhite: '@accent', - divider: 'rgba(255, 255, 255, 0.14)', - panel: 'rgb(47, 47, 44)', - panelHeaderDivider: 'rgba(0, 0, 0, 0)', - header: ':alpha<0.7<@panel', - navBg: '#363636', - renote: '@accent', - mention: 'rgb(212, 153, 76)', - mentionMe: 'rgb(212, 210, 76)', - hashtag: '#5bcbb0', - link: '@accent', - }, -} diff --git a/packages/frontend/src/themes/d-cherry.json5 b/packages/frontend/src/themes/d-cherry.json5 deleted file mode 100644 index f9638124c2..0000000000 --- a/packages/frontend/src/themes/d-cherry.json5 +++ /dev/null @@ -1,21 +0,0 @@ -{ - id: '679b3b87-a4e9-4789-8696-b56c15cc33b0', - - name: 'Mi Cherry Dark', - author: 'syuilo', - - base: 'dark', - - props: { - accent: 'rgb(255, 89, 117)', - bg: 'rgb(28, 28, 37)', - fg: 'rgb(236, 239, 244)', - fgOnWhite: '@accent', - panel: 'rgb(35, 35, 47)', - renote: '@accent', - link: '@accent', - mention: '@accent', - hashtag: '@accent', - divider: 'rgb(63, 63, 80)', - }, -} diff --git a/packages/frontend/src/themes/d-dark.json5 b/packages/frontend/src/themes/d-dark.json5 deleted file mode 100644 index ae4f7d53f5..0000000000 --- a/packages/frontend/src/themes/d-dark.json5 +++ /dev/null @@ -1,26 +0,0 @@ -{ - id: '8050783a-7f63-445a-b270-36d0f6ba1677', - - name: 'Mi Dark', - author: 'syuilo', - desc: 'Default light theme', - - base: 'dark', - - props: { - bg: '#232323', - fg: 'rgb(199, 209, 216)', - fgHighlighted: '#fff', - fgOnWhite: '@accent', - divider: 'rgba(255, 255, 255, 0.14)', - panel: '#2d2d2d', - panelHeaderDivider: 'rgba(0, 0, 0, 0)', - header: ':alpha<0.7<@panel', - navBg: '#363636', - renote: '@accent', - mention: '#da6d35', - mentionMe: '#d44c4c', - hashtag: '#4cb8d4', - link: '@accent', - }, -} diff --git a/packages/frontend/src/themes/d-future.json5 b/packages/frontend/src/themes/d-future.json5 deleted file mode 100644 index f2c1f3eb86..0000000000 --- a/packages/frontend/src/themes/d-future.json5 +++ /dev/null @@ -1,27 +0,0 @@ -{ - id: '32a637ef-b47a-4775-bb7b-bacbb823f865', - - name: 'Mi Future Dark', - author: 'syuilo', - - base: 'dark', - - props: { - accent: '#63e2b7', - bg: '#101014', - fg: '#D5D5D6', - fgHighlighted: '#fff', - fgOnAccent: '#000', - fgOnWhite: '@accent', - divider: 'rgba(255, 255, 255, 0.1)', - panel: '#18181c', - panelHeaderDivider: 'rgba(0, 0, 0, 0)', - renote: '@accent', - mention: '#f2c97d', - mentionMe: '@accent', - hashtag: '#70c0e8', - link: '#e88080', - buttonGradateA: '@accent', - buttonGradateB: ':saturate<30<:hue<30<@accent', - }, -} diff --git a/packages/frontend/src/themes/d-green-lime.json5 b/packages/frontend/src/themes/d-green-lime.json5 deleted file mode 100644 index ca4e688fdb..0000000000 --- a/packages/frontend/src/themes/d-green-lime.json5 +++ /dev/null @@ -1,24 +0,0 @@ -{ - id: '02816013-8107-440f-877e-865083ffe194', - - name: 'Mi Green+Lime Dark', - author: 'syuilo', - - base: 'dark', - - props: { - accent: '#b4e900', - bg: '#0C1210', - fg: '#dee7e4', - fgHighlighted: '#fff', - fgOnAccent: '#192320', - fgOnWhite: '@accent', - divider: '#e7fffb24', - panel: '#192320', - panelHeaderDivider: 'rgba(0, 0, 0, 0)', - popup: '#293330', - renote: '@accent', - mentionMe: '#ffaa00', - link: '#24d7ce', - }, -} diff --git a/packages/frontend/src/themes/d-green-orange.json5 b/packages/frontend/src/themes/d-green-orange.json5 deleted file mode 100644 index c2539816e2..0000000000 --- a/packages/frontend/src/themes/d-green-orange.json5 +++ /dev/null @@ -1,24 +0,0 @@ -{ - id: 'dc489603-27b5-424a-9b25-1ff6aec9824a', - - name: 'Mi Green+Orange Dark', - author: 'syuilo', - - base: 'dark', - - props: { - accent: '#e97f00', - bg: '#0C1210', - fg: '#dee7e4', - fgHighlighted: '#fff', - fgOnAccent: '#192320', - fgOnWhite: '@accent', - divider: '#e7fffb24', - panel: '#192320', - panelHeaderDivider: 'rgba(0, 0, 0, 0)', - popup: '#293330', - renote: '@accent', - mentionMe: '#b4e900', - link: '#24d7ce', - }, -} diff --git a/packages/frontend/src/themes/d-ice.json5 b/packages/frontend/src/themes/d-ice.json5 deleted file mode 100644 index b4abc0cacb..0000000000 --- a/packages/frontend/src/themes/d-ice.json5 +++ /dev/null @@ -1,14 +0,0 @@ -{ - id: '66e7e5a9-cd43-42cd-837d-12f47841fa34', - - name: 'Mi Ice Dark', - author: 'syuilo', - - base: 'dark', - - props: { - accent: '#47BFE8', - fgOnWhite: '@accent', - bg: '#212526', - }, -} diff --git a/packages/frontend/src/themes/d-persimmon.json5 b/packages/frontend/src/themes/d-persimmon.json5 deleted file mode 100644 index 0ab6523dd7..0000000000 --- a/packages/frontend/src/themes/d-persimmon.json5 +++ /dev/null @@ -1,26 +0,0 @@ -{ - id: 'c503d768-7c70-4db2-a4e6-08264304bc8d', - - name: 'Mi Persimmon Dark', - author: 'syuilo', - - base: 'dark', - - props: { - accent: 'rgb(206, 102, 65)', - bg: 'rgb(31, 33, 31)', - fg: '#cdd8c7', - fgHighlighted: '#fff', - fgOnWhite: '@accent', - divider: 'rgba(255, 255, 255, 0.14)', - panel: 'rgb(41, 43, 41)', - infoFg: '@fg', - infoBg: '#333c3b', - navBg: '#141714', - renote: '@accent', - mention: '@accent', - mentionMe: '#de6161', - hashtag: '#68bad0', - link: '#a1c758', - }, -} diff --git a/packages/frontend/src/themes/d-u0.json5 b/packages/frontend/src/themes/d-u0.json5 deleted file mode 100644 index c8a31bb1a7..0000000000 --- a/packages/frontend/src/themes/d-u0.json5 +++ /dev/null @@ -1,83 +0,0 @@ -{ - id: '7a5bc13b-df8f-4d44-8e94-4452f0c634bb', - base: 'dark', - name: 'Mi U0 Dark', - props: { - X3: 'rgba(255, 255, 255, 0.05)', - X4: 'rgba(255, 255, 255, 0.1)', - X5: 'rgba(255, 255, 255, 0.05)', - X6: 'rgba(255, 255, 255, 0.15)', - X7: 'rgba(255, 255, 255, 0.05)', - bg: '#172426', - fg: '#dadada', - X10: ':alpha<0.4<@accent', - X11: 'rgba(0, 0, 0, 0.3)', - X12: 'rgba(255, 255, 255, 0.1)', - X13: 'rgba(255, 255, 255, 0.15)', - X14: ':alpha<0.5<@navBg', - X15: ':alpha<0<@panel', - X16: ':alpha<0.7<@panel', - X17: ':alpha<0.8<@bg', - link: '@accent', - warn: '#ecb637', - badge: '#31b1ce', - error: '#ec4137', - focus: ':alpha<0.3<@accent', - navBg: '@panel', - navFg: '@fg', - panel: ':lighten<3<@bg', - popup: ':lighten<3<@panel', - accent: '#00a497', - header: ':alpha<0.7<@panel', - infoBg: '#253142', - infoFg: '#fff', - renote: '@accent', - shadow: 'rgba(0, 0, 0, 0.3)', - divider: 'rgba(255, 255, 255, 0.1)', - hashtag: '#e6b422', - mention: '@accent', - modalBg: 'rgba(0, 0, 0, 0.5)', - success: '#86b300', - buttonBg: 'rgba(255, 255, 255, 0.05)', - switchBg: 'rgba(255, 255, 255, 0.15)', - acrylicBg: ':alpha<0.5<@bg', - indicator: '@accent', - mentionMe: '@mention', - messageBg: '@bg', - navActive: '@accent', - accentedBg: ':alpha<0.15<@accent', - codeNumber: '#cfff9e', - codeString: '#ffb675', - fgOnAccent: '#fff', - fgOnWhite: '@accent', - infoWarnBg: '#42321c', - infoWarnFg: '#ffbd3e', - navHoverFg: ':lighten<17<@fg', - codeBoolean: '#c59eff', - dateLabelFg: '@fg', - inputBorder: 'rgba(255, 255, 255, 0.1)', - panelBorder: '" solid 1px var(--divider)', - accentDarken: ':darken<10<@accent', - acrylicPanel: ':alpha<0.5<@panel', - navIndicator: '@indicator', - accentLighten: ':lighten<10<@accent', - buttonHoverBg: 'rgba(255, 255, 255, 0.1)', - driveFolderBg: ':alpha<0.3<@accent', - fgHighlighted: ':lighten<3<@fg', - fgTransparent: ':alpha<0.5<@fg', - panelHeaderBg: ':lighten<3<@panel', - panelHeaderFg: '@fg', - buttonGradateA: '@accent', - buttonGradateB: ':hue<20<@accent', - htmlThemeColor: '@bg', - panelHighlight: ':lighten<3<@panel', - listItemHoverBg: 'rgba(255, 255, 255, 0.03)', - scrollbarHandle: 'rgba(255, 255, 255, 0.2)', - inputBorderHover: 'rgba(255, 255, 255, 0.2)', - wallpaperOverlay: 'rgba(0, 0, 0, 0.5)', - fgTransparentWeak: ':alpha<0.75<@fg', - panelHeaderDivider: 'rgba(0, 0, 0, 0)', - scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)', - deckBg: '#142022', - }, -} diff --git a/packages/frontend/src/themes/l-apricot.json5 b/packages/frontend/src/themes/l-apricot.json5 deleted file mode 100644 index fe1f9f8927..0000000000 --- a/packages/frontend/src/themes/l-apricot.json5 +++ /dev/null @@ -1,23 +0,0 @@ -{ - id: '0ff48d43-aab3-46e7-ab12-8492110d2e2b', - - name: 'Mi Apricot Light', - author: 'syuilo', - - base: 'light', - - props: { - accent: 'rgb(234, 154, 82)', - bg: '#e6e5e2', - fg: 'rgb(149, 143, 139)', - fgOnWhite: '@accent', - panel: '#EEECE8', - renote: '@accent', - link: '@accent', - mention: '@accent', - hashtag: '@accent', - inputBorder: 'rgba(0, 0, 0, 0.1)', - inputBorderHover: 'rgba(0, 0, 0, 0.2)', - infoBg: 'rgb(226, 235, 241)', - }, -} diff --git a/packages/frontend/src/themes/l-botanical.json5 b/packages/frontend/src/themes/l-botanical.json5 deleted file mode 100644 index 17e9ca246f..0000000000 --- a/packages/frontend/src/themes/l-botanical.json5 +++ /dev/null @@ -1,30 +0,0 @@ -{ - id: '1100673c-f902-4ccd-93aa-7cb88be56178', - - name: 'Mi Botanical Light', - author: 'ThinaticSystem', - - base: 'light', - - props: { - accent: '#77b58c', - bg: 'e2deda', - fg: '#3d3d3d', - fgHighlighted: '#6bc9a0', - fgOnWhite: '@accent', - divider: '#cfcfcf', - panel: '@X14', - panelHeaderBg: '@panel', - panelHeaderDivider: '@divider', - header: ':alpha<0.7<@panel', - navBg: '@X14', - renote: '#229e92', - mention: '#da6d35', - mentionMe: '#d44c4c', - hashtag: '#4cb8d4', - link: '@accent', - buttonGradateB: ':hue<-70<@accent', - success: '#86b300', - X14: '#ebe7e5' - }, -} diff --git a/packages/frontend/src/themes/l-cherry.json5 b/packages/frontend/src/themes/l-cherry.json5 deleted file mode 100644 index 1189a28fe6..0000000000 --- a/packages/frontend/src/themes/l-cherry.json5 +++ /dev/null @@ -1,22 +0,0 @@ -{ - id: 'ac168876-f737-4074-a3fc-a370c732ef48', - - name: 'Mi Cherry Light', - author: 'syuilo', - - base: 'light', - - props: { - accent: 'rgb(219, 96, 114)', - bg: 'rgb(254, 248, 249)', - fg: 'rgb(152, 13, 26)', - fgOnWhite: '@accent', - panel: 'rgb(255, 255, 255)', - renote: '@accent', - link: 'rgb(156, 187, 5)', - mention: '@accent', - hashtag: '@accent', - divider: 'rgba(134, 51, 51, 0.1)', - inputBorderHover: 'rgb(238, 221, 222)', - }, -} diff --git a/packages/frontend/src/themes/l-coffee.json5 b/packages/frontend/src/themes/l-coffee.json5 deleted file mode 100644 index b64cc73583..0000000000 --- a/packages/frontend/src/themes/l-coffee.json5 +++ /dev/null @@ -1,22 +0,0 @@ -{ - id: '6ed80faa-74f0-42c2-98e4-a64d9e138eab', - - name: 'Mi Coffee Light', - author: 'syuilo', - - base: 'light', - - props: { - accent: '#9f8989', - bg: '#f5f3f3', - fg: '#7f6666', - fgOnWhite: '@accent', - panel: '#fff', - divider: 'rgba(87, 68, 68, 0.1)', - renote: 'rgb(160, 172, 125)', - link: 'rgb(137, 151, 159)', - mention: '@accent', - mentionMe: 'rgb(170, 149, 98)', - hashtag: '@accent', - }, -} diff --git a/packages/frontend/src/themes/l-light.json5 b/packages/frontend/src/themes/l-light.json5 deleted file mode 100644 index 63c2e6d278..0000000000 --- a/packages/frontend/src/themes/l-light.json5 +++ /dev/null @@ -1,21 +0,0 @@ -{ - id: '4eea646f-7afa-4645-83e9-83af0333cd37', - - name: 'Mi Light', - author: 'syuilo', - desc: 'Default light theme', - - base: 'light', - - props: { - bg: '#f9f9f9', - fg: '#676767', - fgOnWhite: '@accent', - divider: '#e8e8e8', - header: ':alpha<0.7<@panel', - navBg: '#fff', - panel: '#fff', - panelHeaderDivider: '@divider', - mentionMe: 'rgb(0, 179, 70)', - }, -} diff --git a/packages/frontend/src/themes/l-rainy.json5 b/packages/frontend/src/themes/l-rainy.json5 deleted file mode 100644 index e7d1d5af00..0000000000 --- a/packages/frontend/src/themes/l-rainy.json5 +++ /dev/null @@ -1,22 +0,0 @@ -{ - id: 'a58a0abb-ff8c-476a-8dec-0ad7837e7e96', - - name: 'Mi Rainy Light', - author: 'syuilo', - - base: 'light', - - props: { - accent: '#5db0da', - bg: 'rgb(246 248 249)', - fg: '#636b71', - fgOnWhite: '@accent', - panel: '#fff', - divider: 'rgb(230 233 234)', - panelHeaderDivider: '@divider', - renote: '@accent', - link: '@accent', - mention: '@accent', - hashtag: '@accent', - }, -} diff --git a/packages/frontend/src/themes/l-sushi.json5 b/packages/frontend/src/themes/l-sushi.json5 deleted file mode 100644 index e787d63734..0000000000 --- a/packages/frontend/src/themes/l-sushi.json5 +++ /dev/null @@ -1,19 +0,0 @@ -{ - id: '213273e5-7d20-d5f0-6e36-1b6a4f67115c', - - name: 'Mi Sushi Light', - author: 'syuilo', - - base: 'light', - - props: { - accent: '#e36749', - bg: '#f0eee9', - fg: '#5f5f5f', - fgOnWhite: '@accent', - renote: '@accent', - link: '@accent', - mention: '@accent', - hashtag: '#229e82', - }, -} diff --git a/packages/frontend/src/themes/l-u0.json5 b/packages/frontend/src/themes/l-u0.json5 deleted file mode 100644 index 0b952b003a..0000000000 --- a/packages/frontend/src/themes/l-u0.json5 +++ /dev/null @@ -1,82 +0,0 @@ -{ - id: 'e2c940b5-6e9a-4c03-b738-261c720c426d', - base: 'light', - name: 'Mi U0 Light', - props: { - X3: 'rgba(255, 255, 255, 0.05)', - X4: 'rgba(255, 255, 255, 0.1)', - X5: 'rgba(255, 255, 255, 0.05)', - X6: 'rgba(255, 255, 255, 0.15)', - X7: 'rgba(255, 255, 255, 0.05)', - bg: '#e7e7eb', - fg: '#5f5f5f', - X10: ':alpha<0.4<@accent', - X11: 'rgba(0, 0, 0, 0.3)', - X12: 'rgba(255, 255, 255, 0.1)', - X13: 'rgba(255, 255, 255, 0.15)', - X14: ':alpha<0.5<@navBg', - X15: ':alpha<0<@panel', - X16: ':alpha<0.7<@panel', - X17: ':alpha<0.8<@bg', - link: '@accent', - warn: '#ecb637', - badge: '#31b1ce', - error: '#ec4137', - focus: ':alpha<0.3<@accent', - navBg: '@panel', - navFg: '@fg', - panel: ':lighten<3<@bg', - popup: ':lighten<3<@panel', - accent: '#478384', - header: ':alpha<0.7<@panel', - infoBg: '#253142', - infoFg: '#fff', - renote: '@accent', - shadow: 'rgba(0, 0, 0, 0.3)', - divider: '#4646461a', - hashtag: '#1f3134', - mention: '@accent', - modalBg: 'rgba(0, 0, 0, 0.5)', - success: '#86b300', - buttonBg: '#0000000d', - switchBg: 'rgba(255, 255, 255, 0.15)', - acrylicBg: ':alpha<0.5<@bg', - indicator: '@accent', - mentionMe: '@mention', - messageBg: '@bg', - navActive: '@accent', - accentedBg: ':alpha<0.15<@accent', - codeNumber: '#cfff9e', - codeString: '#ffb675', - fgOnAccent: '#fff', - fgOnWhite: '@accent', - infoWarnBg: '#42321c', - infoWarnFg: '#ffbd3e', - navHoverFg: ':lighten<17<@fg', - codeBoolean: '#c59eff', - dateLabelFg: '@fg', - inputBorder: 'rgba(255, 255, 255, 0.1)', - panelBorder: '" solid 1px var(--divider)', - accentDarken: ':darken<10<@accent', - acrylicPanel: ':alpha<0.5<@panel', - navIndicator: '@indicator', - accentLighten: ':lighten<10<@accent', - buttonHoverBg: '#0000001a', - driveFolderBg: ':alpha<0.3<@accent', - fgHighlighted: ':lighten<3<@fg', - fgTransparent: ':alpha<0.5<@fg', - panelHeaderBg: ':lighten<3<@panel', - panelHeaderFg: '@fg', - buttonGradateA: '@accent', - buttonGradateB: ':hue<20<@accent', - htmlThemeColor: '@bg', - panelHighlight: ':lighten<3<@panel', - listItemHoverBg: 'rgba(255, 255, 255, 0.03)', - scrollbarHandle: '#74747433', - inputBorderHover: 'rgba(255, 255, 255, 0.2)', - wallpaperOverlay: 'rgba(0, 0, 0, 0.5)', - fgTransparentWeak: ':alpha<0.75<@fg', - panelHeaderDivider: 'rgba(0, 0, 0, 0)', - scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)', - }, -} diff --git a/packages/frontend/src/themes/l-vivid.json5 b/packages/frontend/src/themes/l-vivid.json5 deleted file mode 100644 index 3da2ca28fb..0000000000 --- a/packages/frontend/src/themes/l-vivid.json5 +++ /dev/null @@ -1,72 +0,0 @@ -{ - id: '6128c2a9-5c54-43fe-a47d-17942356470b', - - name: 'Mi Vivid Light', - author: 'syuilo', - - base: 'light', - - props: { - bg: '#fafafa', - fg: '#444', - link: '#ff9400', - warn: '#ecb637', - badge: '#31b1ce', - error: '#ec4137', - focus: ':alpha<0.3<@accent', - navBg: '@panel', - navFg: '@fg', - panel: '#fff', - accent: '#008cff', - header: ':alpha<0.7<@panel', - infoBg: '#e5f5ff', - infoFg: '#72818a', - renote: '@accent', - shadow: 'rgba(0, 0, 0, 0.1)', - divider: 'rgba(0, 0, 0, 0.08)', - hashtag: '#92d400', - mention: '@accent', - modalBg: 'rgba(0, 0, 0, 0.3)', - success: '#86b300', - buttonBg: 'rgba(0, 0, 0, 0.05)', - acrylicBg: ':alpha<0.5<@bg', - indicator: '@accent', - mentionMe: '@mention', - messageBg: '@bg', - navActive: '@accent', - infoWarnBg: '#fff0db', - infoWarnFg: '#8f6e31', - navHoverFg: ':darken<17<@fg', - dateLabelFg: '@fg', - inputBorder: 'rgba(0, 0, 0, 0.1)', - inputBorderHover: 'rgba(0, 0, 0, 0.2)', - panelBorder: '" solid 1px var(--divider)', - accentDarken: ':darken<10<@accent', - acrylicPanel: ':alpha<0.5<@panel', - navIndicator: '@accent', - accentLighten: ':lighten<10<@accent', - buttonHoverBg: 'rgba(0, 0, 0, 0.1)', - driveFolderBg: ':alpha<0.3<@accent', - fgHighlighted: ':darken<3<@fg', - fgTransparent: ':alpha<0.5<@fg', - fgOnWhite: '@accent', - panelHeaderBg: ':lighten<3<@panel', - panelHeaderFg: '@fg', - htmlThemeColor: '@bg', - panelHighlight: ':darken<3<@panel', - listItemHoverBg: 'rgba(0, 0, 0, 0.03)', - scrollbarHandle: 'rgba(0, 0, 0, 0.2)', - wallpaperOverlay: 'rgba(255, 255, 255, 0.5)', - fgTransparentWeak: ':alpha<0.75<@fg', - panelHeaderDivider: '@divider', - scrollbarHandleHover: 'rgba(0, 0, 0, 0.4)', - X3: 'rgba(0, 0, 0, 0.05)', - X4: 'rgba(0, 0, 0, 0.1)', - X5: 'rgba(0, 0, 0, 0.05)', - X6: 'rgba(0, 0, 0, 0.25)', - X7: 'rgba(0, 0, 0, 0.05)', - X11: 'rgba(0, 0, 0, 0.1)', - X12: 'rgba(0, 0, 0, 0.1)', - X13: 'rgba(0, 0, 0, 0.15)', - }, -} diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue index 8dad666623..e234bb3a33 100644 --- a/packages/frontend/src/ui/_common_/statusbar-federation.vue +++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue @@ -35,7 +35,7 @@ import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MarqueeText from '@/components/MkMarquee.vue'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { useInterval } from '@/scripts/use-interval.js'; +import { useInterval } from '@@/js/use-interval.js'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; const props = defineProps<{ diff --git a/packages/frontend/src/ui/_common_/statusbar-rss.vue b/packages/frontend/src/ui/_common_/statusbar-rss.vue index 6e1d06eec1..550fc39b00 100644 --- a/packages/frontend/src/ui/_common_/statusbar-rss.vue +++ b/packages/frontend/src/ui/_common_/statusbar-rss.vue @@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MarqueeText from '@/components/MkMarquee.vue'; -import { useInterval } from '@/scripts/use-interval.js'; +import { useInterval } from '@@/js/use-interval.js'; import { shuffle } from '@/scripts/shuffle.js'; const props = defineProps<{ diff --git a/packages/frontend/src/ui/_common_/statusbar-user-list.vue b/packages/frontend/src/ui/_common_/statusbar-user-list.vue index 67f8b109c4..078b595dca 100644 --- a/packages/frontend/src/ui/_common_/statusbar-user-list.vue +++ b/packages/frontend/src/ui/_common_/statusbar-user-list.vue @@ -35,7 +35,7 @@ import { ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MarqueeText from '@/components/MkMarquee.vue'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { useInterval } from '@/scripts/use-interval.js'; +import { useInterval } from '@@/js/use-interval.js'; import { getNoteSummary } from '@/scripts/get-note-summary.js'; import { notePage } from '@/filters/note.js'; diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue index 79c9671917..e7ecf7fd20 100644 --- a/packages/frontend/src/ui/deck/main-column.vue +++ b/packages/frontend/src/ui/deck/main-column.vue @@ -26,7 +26,7 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { useScrollPositionManager } from '@/nirax.js'; -import { getScrollContainer } from '@/scripts/scroll.js'; +import { getScrollContainer } from '@@/js/scroll.js'; import { mainRouter } from '@/router/main.js'; defineProps<{ diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 073acbd4db..00a6811fc9 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -108,7 +108,7 @@ import { $i } from '@/account.js'; import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { deviceKind } from '@/scripts/device-kind.js'; import { miLocalStorage } from '@/local-storage.js'; -import { CURRENT_STICKY_BOTTOM } from '@/const.js'; +import { CURRENT_STICKY_BOTTOM } from '@@/js/const.js'; import { useScrollPositionManager } from '@/nirax.js'; import { mainRouter } from '@/router/main.js'; diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue index 49fd103d37..bcfaaf00ab 100644 --- a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue +++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue @@ -25,11 +25,11 @@ SPDX-License-Identifier: AGPL-3.0-only