diff --git a/.gitignore b/.gitignore index 4d5bd1ce08..b270d5cb3a 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,10 @@ temp tsdoc-metadata.json misskey-assets +# Vite temporary files +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + # blender backups *.blend1 *.blend2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 093f6a2564..7535e60209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,12 @@ - Enhance: サイズ制限を超過するファイルをアップロードしようとした際にエラーを出すように - Enhance: アイコンデコレーション管理画面にプレビューを追加 - Enhance: コントロールパネル内のファイル一覧でセンシティブなファイルを区別しやすく +- Enhance: ScratchpadにUIインスペクターを追加 - Fix: サーバーメトリクスが2つ以上あるとリロード直後の表示がおかしくなる問題を修正 - Fix: 月の違う同じ日はセパレータが表示されないのを修正 +- Fix: 縦横比が極端なカスタム絵文字を表示する際にレイアウトが崩れる箇所があるのを修正 + (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/725) +- Fix: 設定変更時のリロード確認ダイアログが複数個表示されることがある問題を修正 - Fix: 一部の環境でノート・ユーザー検索の入力を確定しても検索が始まらない問題を修正 ### Server diff --git a/locales/index.d.ts b/locales/index.d.ts index fecc570395..b06e0f245b 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -3121,7 +3121,7 @@ export interface Locale extends ILocale { */ "narrow": string; /** - * 設定はページリロード後に反映されます。今すぐリロードしますか? + * 設定はページリロード後に反映されます。 */ "reloadToApplySetting": string; /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index a1210bad29..292569cc5a 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -592,6 +592,8 @@ ascendingOrder: "昇順" descendingOrder: "降順" scratchpad: "スクラッチパッド" scratchpadDescription: "スクラッチパッドは、AiScriptの実験環境を提供します。Misskeyと対話するコードの記述、実行、結果の確認ができます。" +uiInspector: "UIインスペクター" +uiInspectorDescription: "メモリ上に存在しているUIコンポーネントのインスタンスの一覧を見ることができます。UIコンポーネントはUi:C:系関数により生成されます。" output: "出力" script: "スクリプト" disablePagesScript: "Pagesのスクリプトを無効にする" @@ -776,7 +778,7 @@ left: "左" center: "中央" wide: "広い" narrow: "狭い" -reloadToApplySetting: "設定はページリロード後に反映されます。今すぐリロードしますか?" +reloadToApplySetting: "設定はページリロード後に反映されます。" needReloadToApply: "反映には再起動が必要です。" showTitlebar: "タイトルバーを表示する" clearCache: "キャッシュをクリア" diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 793d8974b3..e827ffa68c 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -123,11 +123,14 @@ export class AntennaService implements OnApplicationShutdown { if (antenna.src === 'home') { // TODO } else if (antenna.src === 'list') { - const listUsers = (await this.userListMembershipsRepository.findBy({ - userListId: antenna.userListId!, - })).map(x => x.userId); - - if (!listUsers.includes(note.userId)) return false; + if (antenna.userListId == null) return false; + const exists = await this.userListMembershipsRepository.exists({ + where: { + userListId: antenna.userListId, + userId: note.userId, + }, + }); + if (!exists) return false; } else if (antenna.src === 'users') { const accts = antenna.users.map(x => { const { username, host } = Acct.parse(x); diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 5ba175fc35..3bad8da06f 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -611,6 +611,7 @@ defineExpose({ width: auto; height: auto; min-width: 0; + padding: 0; &:disabled { cursor: not-allowed; @@ -717,7 +718,7 @@ defineExpose({ > .item { position: relative; - padding: 0; + padding: 0 3px; width: var(--eachSize); height: var(--eachSize); contain: strict; diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index ee65743574..738cba2134 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only :withTooltip="true" :reaction="notification.reaction.replace(/^:(\w+):$/, ':$1@.:')" :noStyle="true" - style="width: 100%; height: 100%;" + style="width: 100%; height: 100% !important; object-fit: contain;" /> @@ -122,7 +122,7 @@ SPDX-License-Identifier: AGPL-3.0-only :withTooltip="true" :reaction="reaction.reaction.replace(/^:(\w+):$/, ':$1@.:')" :noStyle="true" - style="width: 100%; height: 100%;" + style="width: 100%; height: 100% !important; object-fit: contain;" /> diff --git a/packages/frontend/src/components/MkReactionTooltip.vue b/packages/frontend/src/components/MkReactionTooltip.vue index 15409a216a..77ca841ad0 100644 --- a/packages/frontend/src/components/MkReactionTooltip.vue +++ b/packages/frontend/src/components/MkReactionTooltip.vue @@ -36,6 +36,7 @@ const emit = defineEmits<{ .icon { display: block; width: 60px; + max-height: 60px; font-size: 60px; // unicodeな絵文字についてはwidthが効かないため margin: 0 auto; object-fit: contain; diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue index 3dd02b261c..8038ec7429 100644 --- a/packages/frontend/src/components/MkReactionsViewer.details.vue +++ b/packages/frontend/src/components/MkReactionsViewer.details.vue @@ -63,6 +63,7 @@ function getReactionName(reaction: string): string { .reactionIcon { display: block; width: 60px; + max-height: 60px; font-size: 60px; // unicodeな絵文字についてはwidthが効かないため object-fit: contain; margin: 0 auto; diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts b/packages/frontend/src/components/global/MkMfm.stories.impl.ts similarity index 78% rename from packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts rename to packages/frontend/src/components/global/MkMfm.stories.impl.ts index 730351f795..1daf7a29cb 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts +++ b/packages/frontend/src/components/global/MkMfm.stories.impl.ts @@ -2,16 +2,15 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ - -/* eslint-disable @typescript-eslint/explicit-function-return-type */ + import { StoryObj } from '@storybook/vue3'; import { expect, within } from '@storybook/test'; -import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js'; +import MkMfm from './MkMfm.js'; export const Default = { render(args) { return { components: { - MkMisskeyFlavoredMarkdown, + MkMfm, }, setup() { return { @@ -25,7 +24,7 @@ export const Default = { }; }, }, - template: '', + template: '', }; }, async play({ canvasElement, args }) { @@ -54,25 +53,25 @@ export const Default = { parameters: { layout: 'centered', }, -} satisfies StoryObj; +} satisfies StoryObj; export const Plain = { ...Default, args: { ...Default.args, plain: true, }, -} satisfies StoryObj; +} satisfies StoryObj; export const Nowrap = { ...Default, args: { ...Default.args, nowrap: true, }, -} satisfies StoryObj; +} satisfies StoryObj; export const IsNotNote = { ...Default, args: { ...Default.args, isNote: false, }, -} satisfies StoryObj; +} satisfies StoryObj; diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMfm.ts similarity index 100% rename from packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts rename to packages/frontend/src/components/global/MkMfm.ts diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index 44d8d59941..b36625ed1b 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -5,7 +5,7 @@ import { App } from 'vue'; -import Mfm from './global/MkMisskeyFlavoredMarkdown.js'; +import Mfm from './global/MkMfm.js'; import MkA from './global/MkA.vue'; import MkAcct from './global/MkAcct.vue'; import MkAvatar from './global/MkAvatar.vue'; diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index eea3f68130..4747aa5205 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -344,6 +344,7 @@ definePageMetadata(() => ({ > .img { width: 42px; height: 42px; + object-fit: contain; } > .body { @@ -390,6 +391,7 @@ definePageMetadata(() => ({ > .img { width: 32px; height: 32px; + object-fit: contain; } > .body { diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue index 9aaa8ff9c6..897ff6acdf 100644 --- a/packages/frontend/src/pages/scratchpad.vue +++ b/packages/frontend/src/pages/scratchpad.vue @@ -30,6 +30,24 @@ SPDX-License-Identifier: AGPL-3.0-only + + +
+
+
{{ c.value.type }}
+
{{ c.value.id }}
+ +
+ +
+
+
{{ i18n.ts.uiInspectorDescription }}
+
+
+
{{ i18n.ts.scratchpadDescription }}
@@ -43,6 +61,7 @@ import { onDeactivated, onUnmounted, Ref, ref, watch, computed } from 'vue'; import { Interpreter, Parser, utils } from '@syuilo/aiscript'; import MkContainer from '@/components/MkContainer.vue'; import MkButton from '@/components/MkButton.vue'; +import MkTextarea from '@/components/MkTextarea.vue'; import MkCodeEditor from '@/components/MkCodeEditor.vue'; import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js'; import * as os from '@/os.js'; @@ -61,6 +80,7 @@ const logs = ref([]); const root = ref(); const components = ref[]>([]); const uiKey = ref(0); +const uiInspectorOpenedComponents = ref(new Map); const saved = miLocalStorage.getItem('scratchpad'); if (saved) { @@ -71,6 +91,14 @@ watch(code, () => { miLocalStorage.setItem('scratchpad', code.value); }); +function stringifyUiProps(uiProps) { + return JSON.stringify( + { ...uiProps, type: undefined, id: undefined }, + (k, v) => typeof v === 'function' ? '' : v, + 2 + ); +} + async function run() { if (aiscript) aiscript.abort(); root.value = undefined; @@ -192,4 +220,35 @@ definePageMetadata(() => ({ } } } + +.uiInspector { + display: grid; + gap: 8px; + padding: 16px; +} + +.uiInspectorType { + display: inline-block; + border: hidden; + border-radius: 10px; + background-color: var(--panelHighlight); + padding: 2px 8px; + font-size: 12px; +} + +.uiInspectorId { + display: inline-block; + padding-left: 8px; +} + +.uiInspectorDescription { + display: block; + font-size: 12px; + padding-top: 16px; +} + +.uiInspectorPropsToggle { + background: none; + border: none; +} diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 15af5617cc..69238b0436 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -258,7 +258,7 @@ import { langs } from '@@/js/config.js'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { unisonReload } from '@/scripts/unison-reload.js'; +import { reloadAsk } from '@/scripts/reload-ask.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { miLocalStorage } from '@/local-storage.js'; @@ -270,16 +270,6 @@ const fontSize = ref(miLocalStorage.getItem('fontSize')); const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null); const dataSaver = ref(defaultStore.state.dataSaver); -async function reloadAsk() { - const { canceled } = await os.confirm({ - type: 'info', - text: i18n.ts.reloadToApplySetting, - }); - if (canceled) return; - - unisonReload(); -} - const hemisphere = computed(defaultStore.makeGetterSetter('hemisphere')); const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind')); const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior')); @@ -369,7 +359,7 @@ watch([ confirmWhenRevealingSensitiveMedia, contextMenu, ], async () => { - await reloadAsk(); + await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); }); const emojiIndexLangs = ['en-US', 'ja-JP', 'ja-JP_hira'] as const; diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index 7f8460e316..a0e6cad9c8 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -54,7 +54,7 @@ import MkContainer from '@/components/MkContainer.vue'; import * as os from '@/os.js'; import { navbarItemDef } from '@/navbar.js'; import { defaultStore } from '@/store.js'; -import { unisonReload } from '@/scripts/unison-reload.js'; +import { reloadAsk } from '@/scripts/reload-ask.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -67,16 +67,6 @@ const items = ref(defaultStore.state.menu.map(x => ({ const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay')); -async function reloadAsk() { - const { canceled } = await os.confirm({ - type: 'info', - text: i18n.ts.reloadToApplySetting, - }); - if (canceled) return; - - unisonReload(); -} - async function addItem() { const menu = Object.keys(navbarItemDef).filter(k => !defaultStore.state.menu.includes(k)); const { canceled, result: item } = await os.select({ @@ -100,7 +90,7 @@ function removeItem(index: number) { async function save() { defaultStore.set('menu', items.value.map(x => x.type)); - await reloadAsk(); + await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); } function reset() { @@ -111,7 +101,7 @@ function reset() { } watch(menuDisplay, async () => { - await reloadAsk(); + await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); }); const headerActions = computed(() => []); diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue index a1cb2ea1c4..0f7609c83e 100644 --- a/packages/frontend/src/pages/settings/other.vue +++ b/packages/frontend/src/pages/settings/other.vue @@ -98,7 +98,7 @@ import { defaultStore } from '@/store.js'; import { signout, signinRequired } from '@/account.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { unisonReload } from '@/scripts/unison-reload.js'; +import { reloadAsk } from '@/scripts/reload-ask.js'; import FormSection from '@/components/form/section.vue'; const $i = signinRequired(); @@ -132,16 +132,6 @@ async function deleteAccount() { await signout(); } -async function reloadAsk() { - const { canceled } = await os.confirm({ - type: 'info', - text: i18n.ts.reloadToApplySetting, - }); - if (canceled) return; - - unisonReload(); -} - async function updateRepliesAll(withReplies: boolean) { const { canceled } = await os.confirm({ type: 'warning', @@ -155,7 +145,7 @@ async function updateRepliesAll(withReplies: boolean) { watch([ enableCondensedLineForAcct, ], async () => { - await reloadAsk(); + await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); }); const headerActions = computed(() => []); diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index 7d192bcbea..ce8ec68692 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -88,19 +88,9 @@ import { uniqueBy } from '@/scripts/array.js'; import { fetchThemes, getThemes } from '@/theme-store.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { miLocalStorage } from '@/local-storage.js'; -import { unisonReload } from '@/scripts/unison-reload.js'; +import { reloadAsk } from '@/scripts/reload-ask.js'; import * as os from '@/os.js'; -async function reloadAsk() { - const { canceled } = await os.confirm({ - type: 'info', - text: i18n.ts.reloadToApplySetting, - }); - if (canceled) return; - - unisonReload(); -} - const installedThemes = ref(getThemes()); const builtinThemes = getBuiltinThemesRef(); @@ -148,13 +138,13 @@ watch(syncDeviceDarkMode, () => { } }); -watch(wallpaper, () => { +watch(wallpaper, async () => { if (wallpaper.value == null) { miLocalStorage.removeItem('wallpaper'); } else { miLocalStorage.setItem('wallpaper', wallpaper.value); } - reloadAsk(); + await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); }); onActivated(() => { diff --git a/packages/frontend/src/scripts/reload-ask.ts b/packages/frontend/src/scripts/reload-ask.ts new file mode 100644 index 0000000000..733d91b85a --- /dev/null +++ b/packages/frontend/src/scripts/reload-ask.ts @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { i18n } from '@/i18n.js'; +import * as os from '@/os.js'; +import { unisonReload } from '@/scripts/unison-reload.js'; + +let isReloadConfirming = false; + +export async function reloadAsk(opts: { + unison?: boolean; + reason?: string; +}) { + if (isReloadConfirming) { + return; + } + + isReloadConfirming = true; + + const { canceled } = await os.confirm(opts.reason == null ? { + type: 'info', + text: i18n.ts.reloadConfirm, + } : { + type: 'info', + title: i18n.ts.reloadConfirm, + text: opts.reason, + }).finally(() => { + isReloadConfirming = false; + }); + + if (canceled) return; + + if (opts.unison) { + unisonReload(); + } else { + location.reload(); + } +}