From 21ed2e3002a71c32f01a99011c1c44b3154d4024 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Thu, 9 Oct 2025 09:28:27 +0900 Subject: fix(frontend): バナー画像のサイズがおかしい問題を修正 (#16627) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): バナー画像のサイズがおかしい問題を修正 * Update Changelog --- packages/frontend/src/pages/user/home.vue | 1 + 1 file changed, 1 insertion(+) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 7094aca7c0..78730b1811 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -382,6 +382,7 @@ onDeactivated(disposeBannerParallaxResizeObserver); left: 0; width: 100%; height: 300%; + background-size: 100% auto; background-color: #4c5e6d; background-repeat: repeat-y; background-position: center; -- cgit v1.2.3-freya From 37526de32310bdbf8f4e862fb5a4e4533b4db0ab Mon Sep 17 00:00:00 2001 From: おさむのひと <46447427+samunohito@users.noreply.github.com> Date: Thu, 9 Oct 2025 09:29:09 +0900 Subject: fix: aliasesの区切り文字が一致していないのを修正 (#16622) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: aliasesの区切り文字が一致していないのを修正 * fix CHANGELOG.md --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- CHANGELOG.md | 2 +- packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 15be6a5e00..fd204653e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,12 @@ - ### Client +- Fix: カスタム絵文字画面(beta)のaliasesで使用される区切り文字が一致していないのを修正 #15614 - Fix: バナー画像の幅が表示領域と一致していない問題を修正 ### Server - - ## 2025.10.0 ### NOTE diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue index a380bd133e..cbe863f184 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue @@ -503,7 +503,7 @@ function refreshGridItems() { name: it.name, host: it.host ?? '', category: it.category ?? '', - aliases: it.aliases.join(','), + aliases: it.aliases.join(' '), license: it.license ?? '', isSensitive: it.isSensitive, localOnly: it.localOnly, -- cgit v1.2.3-freya From 41aa0c8efebf2e443ba7372762948b5e56cfd990 Mon Sep 17 00:00:00 2001 From: 果物リン Date: Thu, 9 Oct 2025 09:29:47 +0900 Subject: [カスタム絵文字beta]MacのCmdキー対応とCtrl/Cmd+Arrowキー対応 (#16621) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [カスタム絵文字beta]MacのCmdキー対応とCtrl/Cmd+Arrowキー対応 * Update packages/frontend/src/components/grid/MkGrid.vue Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> --------- Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> --- packages/frontend/src/components/grid/MkGrid.vue | 262 +++++++++++------------ packages/frontend/src/utility/hotkey.ts | 4 +- packages/frontend/src/utility/key-event.ts | 153 ------------- 3 files changed, 124 insertions(+), 295 deletions(-) delete mode 100644 packages/frontend/src/utility/key-event.ts (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/components/grid/MkGrid.vue b/packages/frontend/src/components/grid/MkGrid.vue index a175485a7e..96d9e35773 100644 --- a/packages/frontend/src/components/grid/MkGrid.vue +++ b/packages/frontend/src/components/grid/MkGrid.vue @@ -71,7 +71,7 @@ import { import * as os from '@/os.js'; import { createColumn } from '@/components/grid/column.js'; import { createRow, defaultGridRowSetting, resetRow } from '@/components/grid/row.js'; -import { handleKeyEvent } from '@/utility/key-event.js'; +import { makeHotkey } from '@/utility/hotkey.js'; type RowHolder = { row: GridRow, @@ -289,161 +289,143 @@ function onKeyDown(ev: KeyboardEvent) { const max = availableBounds.value; const bounds = rangedBounds.value; - handleKeyEvent(ev, [ - { - code: 'Delete', handler: () => { - if (rangedRows.value.length > 0) { - if (rowSetting.events.delete) { - rowSetting.events.delete(rangedRows.value); - } - } else { - const context = createContext(); - removeDataFromGrid(context, (cell) => { - emitCellValue(cell, undefined); - }); + makeHotkey({ + 'delete': () => { + if (rangedRows.value.length > 0) { + if (rowSetting.events.delete) { + rowSetting.events.delete(rangedRows.value); } - }, - }, - { - code: 'KeyC', modifiers: ['Control'], handler: () => { - const context = createContext(); - copyGridDataToClipboard(data.value, context); - }, - }, - { - code: 'KeyV', modifiers: ['Control'], handler: async () => { - const _cells = cells.value; + } else { const context = createContext(); - await pasteToGridFromClipboard(context, (row, col, parsedValue) => { - emitCellValue(_cells[row.index].cells[col.index], parsedValue); + removeDataFromGrid(context, (cell) => { + emitCellValue(cell, undefined); }); - }, + } }, - { - code: 'ArrowRight', modifiers: ['Control', 'Shift'], handler: () => { - updateSelectionRange({ - leftTop: { col: selectedCellAddress.col, row: bounds.leftTop.row }, - rightBottom: { col: max.rightBottom.col, row: bounds.rightBottom.row }, - }); - }, + 'ctrl+c|meta+c': () => { + const context = createContext(); + copyGridDataToClipboard(data.value, context); }, - { - code: 'ArrowLeft', modifiers: ['Control', 'Shift'], handler: () => { - updateSelectionRange({ - leftTop: { col: max.leftTop.col, row: bounds.leftTop.row }, - rightBottom: { col: selectedCellAddress.col, row: bounds.rightBottom.row }, - }); - }, + 'ctrl+v|meta+v': async () => { + const _cells = cells.value; + const context = createContext(); + await pasteToGridFromClipboard(context, (row, col, parsedValue) => { + emitCellValue(_cells[row.index].cells[col.index], parsedValue); + }); }, - { - code: 'ArrowUp', modifiers: ['Control', 'Shift'], handler: () => { - updateSelectionRange({ - leftTop: { col: bounds.leftTop.col, row: max.leftTop.row }, - rightBottom: { col: bounds.rightBottom.col, row: selectedCellAddress.row }, - }); - }, + 'ctrl+shift+right|meta+shift+right': () => { + updateSelectionRange({ + leftTop: { col: selectedCellAddress.col, row: bounds.leftTop.row }, + rightBottom: { col: max.rightBottom.col, row: bounds.rightBottom.row }, + }); }, - { - code: 'ArrowDown', modifiers: ['Control', 'Shift'], handler: () => { - updateSelectionRange({ - leftTop: { col: bounds.leftTop.col, row: selectedCellAddress.row }, - rightBottom: { col: bounds.rightBottom.col, row: max.rightBottom.row }, - }); - }, + 'ctrl+shift+left|meta+shift+left': () => { + updateSelectionRange({ + leftTop: { col: max.leftTop.col, row: bounds.leftTop.row }, + rightBottom: { col: selectedCellAddress.col, row: bounds.rightBottom.row }, + }); }, - { - code: 'ArrowRight', modifiers: ['Shift'], handler: () => { - updateSelectionRange({ - leftTop: { - col: bounds.leftTop.col < selectedCellAddress.col - ? bounds.leftTop.col + 1 - : selectedCellAddress.col, - row: bounds.leftTop.row, - }, - rightBottom: { - col: (bounds.rightBottom.col > selectedCellAddress.col || bounds.leftTop.col === selectedCellAddress.col) - ? bounds.rightBottom.col + 1 - : selectedCellAddress.col, - row: bounds.rightBottom.row, - }, - }); - }, + 'ctrl+shift+up|meta+shift+up': () => { + updateSelectionRange({ + leftTop: { col: bounds.leftTop.col, row: max.leftTop.row }, + rightBottom: { col: bounds.rightBottom.col, row: selectedCellAddress.row }, + }); }, - { - code: 'ArrowLeft', modifiers: ['Shift'], handler: () => { - updateSelectionRange({ - leftTop: { - col: (bounds.leftTop.col < selectedCellAddress.col || bounds.rightBottom.col === selectedCellAddress.col) - ? bounds.leftTop.col - 1 - : selectedCellAddress.col, - row: bounds.leftTop.row, - }, - rightBottom: { - col: bounds.rightBottom.col > selectedCellAddress.col - ? bounds.rightBottom.col - 1 - : selectedCellAddress.col, - row: bounds.rightBottom.row, - }, - }); - }, + 'ctrl+shift+down|meta+shift+down': () => { + updateSelectionRange({ + leftTop: { col: bounds.leftTop.col, row: selectedCellAddress.row }, + rightBottom: { col: bounds.rightBottom.col, row: max.rightBottom.row }, + }); }, - { - code: 'ArrowUp', modifiers: ['Shift'], handler: () => { - updateSelectionRange({ - leftTop: { - col: bounds.leftTop.col, - row: (bounds.leftTop.row < selectedCellAddress.row || bounds.rightBottom.row === selectedCellAddress.row) - ? bounds.leftTop.row - 1 - : selectedCellAddress.row, - }, - rightBottom: { - col: bounds.rightBottom.col, - row: bounds.rightBottom.row > selectedCellAddress.row - ? bounds.rightBottom.row - 1 - : selectedCellAddress.row, - }, - }); - }, + 'ctrl+right|meta+right': () => { + selectionCell({ col: max.rightBottom.col, row: selectedCellAddress.row }); }, - { - code: 'ArrowDown', modifiers: ['Shift'], handler: () => { - updateSelectionRange({ - leftTop: { - col: bounds.leftTop.col, - row: bounds.leftTop.row < selectedCellAddress.row - ? bounds.leftTop.row + 1 - : selectedCellAddress.row, - }, - rightBottom: { - col: bounds.rightBottom.col, - row: (bounds.rightBottom.row > selectedCellAddress.row || bounds.leftTop.row === selectedCellAddress.row) - ? bounds.rightBottom.row + 1 - : selectedCellAddress.row, - }, - }); - }, + 'ctrl+left|meta+left': () => { + selectionCell({ col: max.leftTop.col, row: selectedCellAddress.row }); + }, + 'ctrl+up|meta+up': () => { + selectionCell({ col: selectedCellAddress.col, row: max.leftTop.row }); + }, + 'ctrl+down|meta+down': () => { + selectionCell({ col: selectedCellAddress.col, row: max.rightBottom.row }); + }, + 'shift+right': () => { + updateSelectionRange({ + leftTop: { + col: bounds.leftTop.col < selectedCellAddress.col + ? bounds.leftTop.col + 1 + : selectedCellAddress.col, + row: bounds.leftTop.row, + }, + rightBottom: { + col: (bounds.rightBottom.col > selectedCellAddress.col || bounds.leftTop.col === selectedCellAddress.col) + ? bounds.rightBottom.col + 1 + : selectedCellAddress.col, + row: bounds.rightBottom.row, + }, + }); + }, + 'shift+left': () => { + updateSelectionRange({ + leftTop: { + col: (bounds.leftTop.col < selectedCellAddress.col || bounds.rightBottom.col === selectedCellAddress.col) + ? bounds.leftTop.col - 1 + : selectedCellAddress.col, + row: bounds.leftTop.row, + }, + rightBottom: { + col: bounds.rightBottom.col > selectedCellAddress.col + ? bounds.rightBottom.col - 1 + : selectedCellAddress.col, + row: bounds.rightBottom.row, + }, + }); + }, + 'shift+up': () => { + updateSelectionRange({ + leftTop: { + col: bounds.leftTop.col, + row: (bounds.leftTop.row < selectedCellAddress.row || bounds.rightBottom.row === selectedCellAddress.row) + ? bounds.leftTop.row - 1 + : selectedCellAddress.row, + }, + rightBottom: { + col: bounds.rightBottom.col, + row: bounds.rightBottom.row > selectedCellAddress.row + ? bounds.rightBottom.row - 1 + : selectedCellAddress.row, + }, + }); + }, + 'shift+down': () => { + updateSelectionRange({ + leftTop: { + col: bounds.leftTop.col, + row: bounds.leftTop.row < selectedCellAddress.row + ? bounds.leftTop.row + 1 + : selectedCellAddress.row, + }, + rightBottom: { + col: bounds.rightBottom.col, + row: (bounds.rightBottom.row > selectedCellAddress.row || bounds.leftTop.row === selectedCellAddress.row) + ? bounds.rightBottom.row + 1 + : selectedCellAddress.row, + }, + }); }, - { - code: 'ArrowDown', handler: () => { - selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row + 1 }); - }, + 'down': () => { + selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row + 1 }); }, - { - code: 'ArrowUp', handler: () => { - selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row - 1 }); - }, + 'up': () => { + selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row - 1 }); }, - { - code: 'ArrowRight', handler: () => { - selectionCell({ col: selectedCellAddress.col + 1, row: selectedCellAddress.row }); - }, + 'right': () => { + selectionCell({ col: selectedCellAddress.col + 1, row: selectedCellAddress.row }); }, - { - code: 'ArrowLeft', handler: () => { - selectionCell({ col: selectedCellAddress.col - 1, row: selectedCellAddress.row }); - }, + 'left': () => { + selectionCell({ col: selectedCellAddress.col - 1, row: selectedCellAddress.row }); }, - ]); + }, [])(ev); break; } diff --git a/packages/frontend/src/utility/hotkey.ts b/packages/frontend/src/utility/hotkey.ts index d728cdfcb0..9c1e66a22e 100644 --- a/packages/frontend/src/utility/hotkey.ts +++ b/packages/frontend/src/utility/hotkey.ts @@ -50,12 +50,12 @@ let latestHotkey: Pattern & { callback: CallbackFunction } | null = null; //#endregion //#region impl -export const makeHotkey = (keymap: Keymap) => { +export const makeHotkey = (keymap: Keymap, ignoreElements = IGNORE_ELEMENTS) => { const actions = parseKeymap(keymap); return (ev: KeyboardEvent) => { if ('pswp' in window && window.pswp != null) return; if (window.document.activeElement != null) { - if (IGNORE_ELEMENTS.includes(window.document.activeElement.tagName.toLowerCase())) return; + if (ignoreElements.includes(window.document.activeElement.tagName.toLowerCase())) return; if (getHTMLElementOrNull(window.document.activeElement)?.isContentEditable) return; } for (const action of actions) { diff --git a/packages/frontend/src/utility/key-event.ts b/packages/frontend/src/utility/key-event.ts deleted file mode 100644 index 020a6c2174..0000000000 --- a/packages/frontend/src/utility/key-event.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -/** - * {@link KeyboardEvent.code} の値を表す文字列。不足分は適宜追加する - * @see https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values - */ -export type KeyCode = ( - | 'Backspace' - | 'Tab' - | 'Enter' - | 'Shift' - | 'Control' - | 'Alt' - | 'Pause' - | 'CapsLock' - | 'Escape' - | 'Space' - | 'PageUp' - | 'PageDown' - | 'End' - | 'Home' - | 'ArrowLeft' - | 'ArrowUp' - | 'ArrowRight' - | 'ArrowDown' - | 'Insert' - | 'Delete' - | 'Digit0' - | 'Digit1' - | 'Digit2' - | 'Digit3' - | 'Digit4' - | 'Digit5' - | 'Digit6' - | 'Digit7' - | 'Digit8' - | 'Digit9' - | 'KeyA' - | 'KeyB' - | 'KeyC' - | 'KeyD' - | 'KeyE' - | 'KeyF' - | 'KeyG' - | 'KeyH' - | 'KeyI' - | 'KeyJ' - | 'KeyK' - | 'KeyL' - | 'KeyM' - | 'KeyN' - | 'KeyO' - | 'KeyP' - | 'KeyQ' - | 'KeyR' - | 'KeyS' - | 'KeyT' - | 'KeyU' - | 'KeyV' - | 'KeyW' - | 'KeyX' - | 'KeyY' - | 'KeyZ' - | 'MetaLeft' - | 'MetaRight' - | 'ContextMenu' - | 'F1' - | 'F2' - | 'F3' - | 'F4' - | 'F5' - | 'F6' - | 'F7' - | 'F8' - | 'F9' - | 'F10' - | 'F11' - | 'F12' - | 'NumLock' - | 'ScrollLock' - | 'Semicolon' - | 'Equal' - | 'Comma' - | 'Minus' - | 'Period' - | 'Slash' - | 'Backquote' - | 'BracketLeft' - | 'Backslash' - | 'BracketRight' - | 'Quote' - | 'Meta' - | 'AltGraph' -); - -/** - * 修飾キーを表す文字列。不足分は適宜追加する。 - */ -export type KeyModifier = ( - | 'Shift' - | 'Control' - | 'Alt' - | 'Meta' -); - -/** - * 押下されたキー以外の状態を表す文字列。不足分は適宜追加する。 - */ -export type KeyState = ( - | 'composing' - | 'repeat' -); - -export type KeyEventHandler = { - modifiers?: KeyModifier[]; - states?: KeyState[]; - code: KeyCode | 'any'; - handler: (event: KeyboardEvent) => void; -}; - -export function handleKeyEvent(event: KeyboardEvent, handlers: KeyEventHandler[]) { - function checkModifier(ev: KeyboardEvent, modifiers? : KeyModifier[]) { - if (modifiers) { - return modifiers.every(modifier => ev.getModifierState(modifier)); - } - return true; - } - - function checkState(ev: KeyboardEvent, states?: KeyState[]) { - if (states) { - return states.every(state => ev.getModifierState(state)); - } - return true; - } - - let hit = false; - for (const handler of handlers.filter(it => it.code === event.code)) { - if (checkModifier(event, handler.modifiers) && checkState(event, handler.states)) { - handler.handler(event); - hit = true; - break; - } - } - - if (!hit) { - for (const handler of handlers.filter(it => it.code === 'any')) { - handler.handler(event); - } - } -} -- cgit v1.2.3-freya From edf7beff23f71b742cd0a9c81cb5a2509543ec6d Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Thu, 9 Oct 2025 11:30:34 +0900 Subject: fix(frontend/aiscript): Mk:toastを同期関数に変更 (#16480) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/aiscript/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/aiscript/api.ts b/packages/frontend/src/aiscript/api.ts index 0549ab76a0..dc84925375 100644 --- a/packages/frontend/src/aiscript/api.ts +++ b/packages/frontend/src/aiscript/api.ts @@ -66,7 +66,7 @@ export function createAiScriptEnv(opts: { storageKey: string, token?: string }) }); return confirm.canceled ? values.FALSE : values.TRUE; }), - 'Mk:toast': values.FN_NATIVE(async ([text]) => { + 'Mk:toast': values.FN_NATIVE(([text]) => { utils.assertString(text); os.toast(text.value); return values.NULL; -- cgit v1.2.3-freya From f8c6273acc87cae945e9c37ef0a542a95ee57b3b Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Fri, 10 Oct 2025 09:33:35 +0900 Subject: fix(frontend): scroll-driven animation非対応環境でバナー画像が上下中央に表示されない問題を修正 (#16632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): scroll-driven animation非対応環境でバナー画像が上下中央に表示されない問題を修正 * Update Changelog --- CHANGELOG.md | 1 + packages/frontend/src/pages/user/home.vue | 27 +++++++++++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 73709c56c9..30018a9d23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Client - Fix: カスタム絵文字画面(beta)のaliasesで使用される区切り文字が一致していないのを修正 #15614 - Fix: バナー画像の幅が表示領域と一致していない問題を修正 +- Fix: 一部のブラウザでバナー画像が上下中央に表示されない問題を修正 ### Server - diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 78730b1811..3002eddda8 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -372,9 +372,6 @@ onDeactivated(disposeBannerParallaxResizeObserver); overflow: clip; background-size: cover; background-position: center; - view-timeline-name: --bannerParallax; - view-timeline-inset: var(--bannerParallaxInset, auto); - view-timeline-axis: block; > .banner { position: absolute; @@ -387,9 +384,7 @@ onDeactivated(disposeBannerParallaxResizeObserver); background-repeat: repeat-y; background-position: center; will-change: transform; - animation: bannerParallaxKeyframes linear both; - animation-timeline: --bannerParallax; - animation-range: cover; + transform: translateY(-50%); } > .fade { @@ -746,6 +741,26 @@ onDeactivated(disposeBannerParallaxResizeObserver); } } +@supports (view-timeline-name: --name) { + .ftskorzw { + > .main { + > .profile > .main { + > .banner-container { + view-timeline-name: --bannerParallax; + view-timeline-inset: var(--bannerParallaxInset, auto); + view-timeline-axis: block; + + > .banner { + animation: bannerParallaxKeyframes linear both; + animation-timeline: --bannerParallax; + animation-range: cover; + } + } + } + } + } +} + @keyframes bannerParallaxKeyframes { from { transform: translateY(-50%); -- cgit v1.2.3-freya From 3df81931eca517904fae42c9cc4588f6fc0b9eaf Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:14:03 +0900 Subject: fix(frontend): ナビゲーションバーの設定で削除した項目をその場で再追加できない問題を修正 (#16652) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): ナビゲーションバーの設定で削除した項目をその場で再追加できない問題を修正 * Update Changelog --- CHANGELOG.md | 1 + packages/frontend/src/pages/settings/navbar.vue | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 30018a9d23..ab3bdbb58c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Fix: カスタム絵文字画面(beta)のaliasesで使用される区切り文字が一致していないのを修正 #15614 - Fix: バナー画像の幅が表示領域と一致していない問題を修正 - Fix: 一部のブラウザでバナー画像が上下中央に表示されない問題を修正 +- Fix: ナビゲーションバーの設定で削除した項目をその場で再追加できない問題を修正 ### Server - diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index c8cbc0977f..d25708dcb4 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -67,7 +67,6 @@ import { store } from '@/store.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { prefer } from '@/preferences.js'; -import { PREF_DEF } from '@/preferences/def.js'; import { getInitialPrefValue } from '@/preferences/manager.js'; import { genId } from '@/utility/id.js'; @@ -77,12 +76,13 @@ const items = ref(prefer.s.menu.map(x => ({ id: genId(), type: x, }))); +const itemTypeValues = computed(() => items.value.map(x => x.type)); const menuDisplay = computed(store.makeGetterSetter('menuDisplay')); const showNavbarSubButtons = prefer.model('showNavbarSubButtons'); async function addItem() { - const menu = Object.keys(navbarItemDef).filter(k => !prefer.s.menu.includes(k)); + const menu = Object.keys(navbarItemDef).filter(k => !itemTypeValues.value.includes(k)); const { canceled, result: item } = await os.select({ title: i18n.ts.addItem, items: [...menu.map(k => ({ @@ -102,8 +102,9 @@ function removeItem(index: number) { items.value.splice(index, 1); } -async function save() { - prefer.commit('menu', items.value.map(x => x.type)); +function save() { + prefer.commit('menu', itemTypeValues.value); + os.success(); } function reset() { -- cgit v1.2.3-freya From 42008d1377757a3c76ee9fd49a17c0cea5e7a850 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:59:26 +0900 Subject: fix(frontend): デッキのメインカラムのヘッダをクリックしてもページ上部/下部にスクロールしない問題を修正 (#16653) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): デッキのメインカラムのヘッダをクリックしても上部にスクロールしない問題を修正 * fix * Update Changelog * fix lint --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- CHANGELOG.md | 1 + packages/frontend/src/ui/deck/column.vue | 8 +++++++- packages/frontend/src/ui/deck/main-column.vue | 24 +++++++++++++++++++++--- 3 files changed, 29 insertions(+), 4 deletions(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index ab3bdbb58c..96b9554c6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - 依存関係の更新 ### Client +- Enhance: デッキのメインカラムのヘッダをクリックしてページ上部/下部にスクロールできるように - Fix: カスタム絵文字画面(beta)のaliasesで使用される区切り文字が一致していないのを修正 #15614 - Fix: バナー画像の幅が表示領域と一致していない問題を修正 - Fix: 一部のブラウザでバナー画像が上下中央に表示されない問題を修正 diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue index 11937fda24..312ca51c83 100644 --- a/packages/frontend/src/ui/deck/column.vue +++ b/packages/frontend/src/ui/deck/column.vue @@ -62,15 +62,18 @@ const props = withDefaults(defineProps<{ column: Column; isStacked?: boolean; naked?: boolean; + handleScrollToTop?: boolean; menu?: MenuItem[]; refresher?: () => Promise; }>(), { isStacked: false, naked: false, + handleScrollToTop: true, }); const emit = defineEmits<{ (ev: 'headerWheel', ctx: WheelEvent): void; + (ev: 'headerClick', ctx: MouseEvent): void; }>(); const body = useTemplateRef('body'); @@ -252,7 +255,10 @@ function onContextmenu(ev: MouseEvent) { os.contextMenu(getMenu(), ev); } -function goTop() { +function goTop(ev: MouseEvent) { + emit('headerClick', ev); + if (!props.handleScrollToTop) return; + if (body.value) { body.value.scrollTo({ top: 0, diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue index 78454d2e49..1388cbdc18 100644 --- a/packages/frontend/src/ui/deck/main-column.vue +++ b/packages/frontend/src/ui/deck/main-column.vue @@ -4,7 +4,13 @@ SPDX-License-Identifier: AGPL-3.0-only --> -- cgit v1.2.3-freya From a405575cd61242122d3ebb0f3e2cf0f944436b7c Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 16 Oct 2025 09:44:58 +0900 Subject: chore(frontend): add tips --- locales/index.d.ts | 4 ++++ locales/ja-JP.yml | 1 + packages/frontend/src/pages/settings/preferences.vue | 1 + 3 files changed, 6 insertions(+) (limited to 'packages/frontend/src') diff --git a/locales/index.d.ts b/locales/index.d.ts index 6f36cba861..b54763a8a2 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -3118,6 +3118,10 @@ export interface Locale extends ILocale { * アニメーション画像を再生しない */ "disableShowingAnimatedImages": string; + /** + * この設定に関わらずアニメーション画像が再生されないときは、ブラウザ・OSのアクセシビリティ設定や省電力設定等が干渉している場合があります。 + */ + "disableShowingAnimatedImages_caption": string; /** * メディアがセンシティブであることを分かりやすく表示 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ec27da085f..3c9f0a5853 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -775,6 +775,7 @@ lockedAccountInfo: "フォローを承認制にしても、ノートの公開範 alwaysMarkSensitive: "デフォルトでメディアをセンシティブ設定にする" loadRawImages: "添付画像のサムネイルをオリジナル画質にする" disableShowingAnimatedImages: "アニメーション画像を再生しない" +disableShowingAnimatedImages_caption: "この設定に関わらずアニメーション画像が再生されないときは、ブラウザ・OSのアクセシビリティ設定や省電力設定等が干渉している場合があります。" highlightSensitiveMedia: "メディアがセンシティブであることを分かりやすく表示" verificationEmailSent: "確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。" notSet: "未設定" diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index c622647b4f..5e3f148710 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -475,6 +475,7 @@ SPDX-License-Identifier: AGPL-3.0-only + -- cgit v1.2.3-freya From 8cfd147555312b98854ba39966f9240ea25a83e4 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Thu, 16 Oct 2025 11:17:47 +0900 Subject: fix(frontend): ロールポリシーによりダイレクトメッセージが無効化されている際のデッキのダイレクトメッセージカラムの挙動を改善 (#16656) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): ロールポリシーによりダイレクトメッセージが無効化されている際のデッキのダイレクトメッセージカラムの挙動を改善 * Update Changelog --- CHANGELOG.md | 1 + packages/frontend/src/ui/deck.vue | 2 +- packages/frontend/src/ui/deck/chat-column.vue | 11 ++++++++--- 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 96b9554c6a..26acc75c7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Fix: バナー画像の幅が表示領域と一致していない問題を修正 - Fix: 一部のブラウザでバナー画像が上下中央に表示されない問題を修正 - Fix: ナビゲーションバーの設定で削除した項目をその場で再追加できない問題を修正 +- Fix: ロールポリシーによりダイレクトメッセージが無効化されている際のデッキのダイレクトメッセージカラムの挙動を改善 ### Server - diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index e2ee4b658e..ff8e91663a 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -167,7 +167,7 @@ const columnsEl = useTemplateRef('columnsEl'); const addColumn = async (ev) => { const { canceled, result: column } = await os.select({ title: i18n.ts._deck.addColumn, - items: columnTypes.map(column => ({ + items: columnTypes.filter(column => column !== 'chat' || $i == null || $i.policies.chatAvailability !== 'unavailable').map(column => ({ value: column, label: i18n.ts._deck._columns[column], })), }); diff --git a/packages/frontend/src/ui/deck/chat-column.vue b/packages/frontend/src/ui/deck/chat-column.vue index 791af2e44c..0015447e22 100644 --- a/packages/frontend/src/ui/deck/chat-column.vue +++ b/packages/frontend/src/ui/deck/chat-column.vue @@ -7,21 +7,26 @@ SPDX-License-Identifier: AGPL-3.0-only -
- +
+ {{ i18n.ts._chat.chatIsReadOnlyForThisAccountOrServer }} + {{ i18n.ts._chat.chatNotAvailableForThisAccountOrServer }} +
-- cgit v1.2.3-freya From a132a1d3e14b624fbb793ecf9db0e796fb5305bf Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Thu, 16 Oct 2025 18:01:25 +0900 Subject: fix(frontend): 外部アプリ設定のアプリアイコンに変な余白が入っているのを修正 (#16660) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/settings/apps.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue index 54e214241b..10901f737b 100644 --- a/packages/frontend/src/pages/settings/apps.vue +++ b/packages/frontend/src/pages/settings/apps.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -86,6 +86,7 @@ definePage(() => ({ -- cgit v1.2.3-freya From 29892d2a01d902166b762dbfe2d2487169a8d048 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:45:37 +0900 Subject: enhance: リモートユーザーのロールバッジを表示するかどうかをサーバー管理者が設定できるように (#16661) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance: リモートユーザーのロールバッジを表示するかどうかをサーバー管理者が設定できるように * Update Changelog * build misskey-js with types --- CHANGELOG.md | 2 ++ locales/index.d.ts | 4 ++++ locales/ja-JP.yml | 1 + .../migration/1760607435831-RoleBadgesRemoteUsers.js | 16 ++++++++++++++++ .../backend/src/core/entities/UserEntityService.ts | 4 ++-- packages/backend/src/models/Meta.ts | 5 +++++ .../backend/src/server/api/endpoints/admin/meta.ts | 5 +++++ .../src/server/api/endpoints/admin/update-meta.ts | 5 +++++ packages/frontend/src/pages/admin/performance.vue | 18 ++++++++++++++++++ packages/misskey-js/src/autogen/types.ts | 2 ++ 10 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 packages/backend/migration/1760607435831-RoleBadgesRemoteUsers.js (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 26acc75c7e..7d20ef5c6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## 2025.10.1 ### General +- Enhance: リモートユーザーのロールバッジを表示できるように(オプトイン) + パフォーマンス上の問題からデフォルトで無効化されています。「コントロールパネル > パフォーマンス」から有効化できます。 - 依存関係の更新 ### Client diff --git a/locales/index.d.ts b/locales/index.d.ts index b54763a8a2..7391e197c4 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -4706,6 +4706,10 @@ export interface Locale extends ILocale { * ユーザーごとのIdenticon生成を有効にする */ "enableIdenticonGeneration": string; + /** + * リモートユーザーのロールバッジを表示する + */ + "showRoleBadgesOfRemoteUsers": string; /** * オフにするとパフォーマンスが向上します。 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 3c9f0a5853..9ee3224441 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1172,6 +1172,7 @@ installed: "インストール済み" branding: "ブランディング" enableServerMachineStats: "サーバーのマシン情報を公開する" enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする" +showRoleBadgesOfRemoteUsers: "リモートユーザーのロールバッジを表示する" turnOffToImprovePerformance: "オフにするとパフォーマンスが向上します。" createInviteCode: "招待コードを作成" createWithOptions: "オプションを指定して作成" diff --git a/packages/backend/migration/1760607435831-RoleBadgesRemoteUsers.js b/packages/backend/migration/1760607435831-RoleBadgesRemoteUsers.js new file mode 100644 index 0000000000..483d35a91b --- /dev/null +++ b/packages/backend/migration/1760607435831-RoleBadgesRemoteUsers.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class RoleBadgesRemoteUsers1760607435831 { + name = 'RoleBadgesRemoteUsers1760607435831' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "showRoleBadgesOfRemoteUsers" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "showRoleBadgesOfRemoteUsers"`); + } +} diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 47021359e1..ac5b855096 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -512,8 +512,8 @@ export class UserEntityService implements OnModuleInit { } : undefined) : undefined, emojis: this.customEmojiService.populateEmojis(user.emojis, user.host), onlineStatus: this.getOnlineStatus(user), - // パフォーマンス上の理由でローカルユーザーのみ - badgeRoles: user.host == null ? this.roleService.getUserBadgeRoles(user.id).then((rs) => rs + // パフォーマンス上の理由で、明示的に設定しない場合はローカルユーザーのみ取得 + badgeRoles: (this.meta.showRoleBadgesOfRemoteUsers || user.host == null) ? this.roleService.getUserBadgeRoles(user.id).then((rs) => rs .filter((r) => r.isPublic || iAmModerator) .sort((a, b) => b.displayOrder - a.displayOrder) .map((r) => ({ diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index f8021a7a84..205c9eeb89 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -717,6 +717,11 @@ export class MiMeta { }) public remoteNotesCleaningExpiryDaysForEachNotes: number; + @Column('boolean', { + default: false, + }) + public showRoleBadgesOfRemoteUsers: boolean; + @Column('jsonb', { default: { }, }) diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 21099c0a8c..2c7f793584 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -593,6 +593,10 @@ export const meta = { type: 'number', optional: false, nullable: false, }, + showRoleBadgesOfRemoteUsers: { + type: 'boolean', + optional: false, nullable: false, + }, }, }, } as const; @@ -748,6 +752,7 @@ export default class extends Endpoint { // eslint- enableRemoteNotesCleaning: instance.enableRemoteNotesCleaning, remoteNotesCleaningExpiryDaysForEachNotes: instance.remoteNotesCleaningExpiryDaysForEachNotes, remoteNotesCleaningMaxProcessingDurationInMinutes: instance.remoteNotesCleaningMaxProcessingDurationInMinutes, + showRoleBadgesOfRemoteUsers: instance.showRoleBadgesOfRemoteUsers, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index a1a2a99d6e..b3c2cecc67 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -209,6 +209,7 @@ export const paramDef = { enableRemoteNotesCleaning: { type: 'boolean' }, remoteNotesCleaningExpiryDaysForEachNotes: { type: 'number' }, remoteNotesCleaningMaxProcessingDurationInMinutes: { type: 'number' }, + showRoleBadgesOfRemoteUsers: { type: 'boolean' }, }, required: [], } as const; @@ -743,6 +744,10 @@ export default class extends Endpoint { // eslint- set.remoteNotesCleaningMaxProcessingDurationInMinutes = ps.remoteNotesCleaningMaxProcessingDurationInMinutes; } + if (ps.showRoleBadgesOfRemoteUsers !== undefined) { + set.showRoleBadgesOfRemoteUsers = ps.showRoleBadgesOfRemoteUsers; + } + const before = await this.metaService.fetch(true); await this.metaService.update(set); diff --git a/packages/frontend/src/pages/admin/performance.vue b/packages/frontend/src/pages/admin/performance.vue index e3021778e7..c5f3c2d4f0 100644 --- a/packages/frontend/src/pages/admin/performance.vue +++ b/packages/frontend/src/pages/admin/performance.vue @@ -53,6 +53,15 @@ SPDX-License-Identifier: AGPL-3.0-only
+ +
+ + + + +
+
+ @@ -188,6 +197,7 @@ const enableIdenticonGeneration = ref(meta.enableIdenticonGeneration); const enableChartsForRemoteUser = ref(meta.enableChartsForRemoteUser); const enableStatsForFederatedInstances = ref(meta.enableStatsForFederatedInstances); const enableChartsForFederatedInstances = ref(meta.enableChartsForFederatedInstances); +const showRoleBadgesOfRemoteUsers = ref(meta.showRoleBadgesOfRemoteUsers); function onChange_enableServerMachineStats(value: boolean) { os.apiWithDialog('admin/update-meta', { @@ -229,6 +239,14 @@ function onChange_enableChartsForFederatedInstances(value: boolean) { }); } +function onChange_showRoleBadgesOfRemoteUsers(value: boolean) { + os.apiWithDialog('admin/update-meta', { + showRoleBadgesOfRemoteUsers: value, + }).then(() => { + fetchInstance(true); + }); +} + const fttForm = useForm({ enableFanoutTimeline: meta.enableFanoutTimeline, enableFanoutTimelineDbFallback: meta.enableFanoutTimelineDbFallback, diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 7edd43bf9b..3e95651071 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -9460,6 +9460,7 @@ export interface operations { enableRemoteNotesCleaning: boolean; remoteNotesCleaningExpiryDaysForEachNotes: number; remoteNotesCleaningMaxProcessingDurationInMinutes: number; + showRoleBadgesOfRemoteUsers: boolean; }; }; }; @@ -12780,6 +12781,7 @@ export interface operations { enableRemoteNotesCleaning?: boolean; remoteNotesCleaningExpiryDaysForEachNotes?: number; remoteNotesCleaningMaxProcessingDurationInMinutes?: number; + showRoleBadgesOfRemoteUsers?: boolean; }; }; }; -- cgit v1.2.3-freya From d98bf012b59b343559dd679f0b4ae370ebd75079 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sun, 19 Oct 2025 11:36:00 +0900 Subject: refactor(frontend): カスタムディレクティブの型付け (#16659) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(frontend): カスタムディレクティブの型付け * fix --- packages/frontend/src/directives/adaptive-bg.ts | 6 +- .../frontend/src/directives/adaptive-border.ts | 8 +-- packages/frontend/src/directives/anim.ts | 8 +-- packages/frontend/src/directives/appear.ts | 14 +++-- packages/frontend/src/directives/click-anime.ts | 6 +- packages/frontend/src/directives/follow-append.ts | 12 ++-- packages/frontend/src/directives/get-size.ts | 10 ++-- packages/frontend/src/directives/hotkey.ts | 11 +++- packages/frontend/src/directives/index.ts | 66 ++++++++++++++-------- packages/frontend/src/directives/panel.ts | 6 +- packages/frontend/src/directives/ripple.ts | 7 ++- packages/frontend/src/directives/tooltip.ts | 48 +++++++++++----- packages/frontend/src/directives/user-preview.ts | 52 +++++++++-------- 13 files changed, 159 insertions(+), 95 deletions(-) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/directives/adaptive-bg.ts b/packages/frontend/src/directives/adaptive-bg.ts index a68cd1b18b..25e9ae1c9e 100644 --- a/packages/frontend/src/directives/adaptive-bg.ts +++ b/packages/frontend/src/directives/adaptive-bg.ts @@ -6,8 +6,8 @@ import type { Directive } from 'vue'; import { getBgColor } from '@/utility/get-bg-color.js'; -export default { - mounted(src, binding, vn) { +export const adaptiveBgDirective = { + mounted(src) { const parentBg = getBgColor(src.parentElement) ?? 'transparent'; const myBg = window.getComputedStyle(src).backgroundColor; @@ -18,4 +18,4 @@ export default { src.style.backgroundColor = myBg; } }, -} as Directive; +} as Directive; diff --git a/packages/frontend/src/directives/adaptive-border.ts b/packages/frontend/src/directives/adaptive-border.ts index 8072a1ffd9..749861fd94 100644 --- a/packages/frontend/src/directives/adaptive-border.ts +++ b/packages/frontend/src/directives/adaptive-border.ts @@ -9,8 +9,8 @@ import { globalEvents } from '@/events.js'; const handlerMap = new WeakMap(); -export default { - mounted(src, binding, vn) { +export const adaptiveBorderDirective = { + mounted(src) { function calc() { const parentBg = getBgColor(src.parentElement) ?? 'transparent'; @@ -30,7 +30,7 @@ export default { globalEvents.on('themeChanged', calc); }, - unmounted(src, binding, vn) { + unmounted(src) { globalEvents.off('themeChanged', handlerMap.get(src)); }, -} as Directive; +} as Directive; diff --git a/packages/frontend/src/directives/anim.ts b/packages/frontend/src/directives/anim.ts index ad0cb5ed81..a165fa11e0 100644 --- a/packages/frontend/src/directives/anim.ts +++ b/packages/frontend/src/directives/anim.ts @@ -5,8 +5,8 @@ import type { Directive } from 'vue'; -export default { - beforeMount(src, binding, vn) { +export const animDirective = { + beforeMount(src) { src.style.opacity = '0'; src.style.transform = 'scale(0.9)'; // ページネーションと相性が悪いので @@ -14,10 +14,10 @@ export default { src.classList.add('_zoom'); }, - mounted(src, binding, vn) { + mounted(src) { window.setTimeout(() => { src.style.opacity = '1'; src.style.transform = 'none'; }, 1); }, -} as Directive; +} as Directive; diff --git a/packages/frontend/src/directives/appear.ts b/packages/frontend/src/directives/appear.ts index f5fec108dc..f714871420 100644 --- a/packages/frontend/src/directives/appear.ts +++ b/packages/frontend/src/directives/appear.ts @@ -6,12 +6,16 @@ import { throttle } from 'throttle-debounce'; import type { Directive } from 'vue'; -export default { - mounted(src, binding, vn) { +interface HTMLElementWithObserver extends HTMLElement { + _observer_?: IntersectionObserver; +} + +export const appearDirective = { + mounted(src, binding) { const fn = binding.value; if (fn == null) return; - const check = throttle(1000, (entries) => { + const check = throttle(1000, (entries) => { if (entries.some(entry => entry.isIntersecting)) { fn(); } @@ -24,7 +28,7 @@ export default { src._observer_ = observer; }, - unmounted(src, binding, vn) { + unmounted(src) { if (src._observer_) src._observer_.disconnect(); }, -} as Directive; +} as Directive void>; diff --git a/packages/frontend/src/directives/click-anime.ts b/packages/frontend/src/directives/click-anime.ts index c34f351fb3..7891e8092c 100644 --- a/packages/frontend/src/directives/click-anime.ts +++ b/packages/frontend/src/directives/click-anime.ts @@ -6,8 +6,8 @@ import type { Directive } from 'vue'; import { prefer } from '@/preferences.js'; -export default { - mounted(el: HTMLElement, binding, vn) { +export const clickAnimeDirective = { + mounted(el) { if (!prefer.s.animation) return; const target = el.children[0]; @@ -37,4 +37,4 @@ export default { target.classList.add('_anime_bounce_standBy'); }); }, -} as Directive; +} as Directive; diff --git a/packages/frontend/src/directives/follow-append.ts b/packages/frontend/src/directives/follow-append.ts index f3eaac10e3..303dcb842a 100644 --- a/packages/frontend/src/directives/follow-append.ts +++ b/packages/frontend/src/directives/follow-append.ts @@ -6,8 +6,12 @@ import type { Directive } from 'vue'; import { getScrollContainer, getScrollPosition } from '@@/js/scroll.js'; -export default { - mounted(src, binding, vn) { +interface HTMLElementWithRO extends HTMLElement { + _ro_?: ResizeObserver; +} + +export const followAppendDirective = { + mounted(src, binding) { if (binding.value === false) return; let isBottom = true; @@ -34,7 +38,7 @@ export default { src._ro_ = ro; }, - unmounted(src, binding, vn) { + unmounted(src) { if (src._ro_) src._ro_.unobserve(src); }, -} as Directive; +} as Directive; diff --git a/packages/frontend/src/directives/get-size.ts b/packages/frontend/src/directives/get-size.ts index 488f201a0d..42660987dd 100644 --- a/packages/frontend/src/directives/get-size.ts +++ b/packages/frontend/src/directives/get-size.ts @@ -37,8 +37,10 @@ function calc(src: Element) { info.fn(width, height); } -export default { - mounted(src, binding, vn) { +type SizeCallback = (w: number, h: number) => void; + +export const getSizeDirective = { + mounted(src, binding) { const resize = new ResizeObserver((entries, observer) => { calc(src); }); @@ -48,7 +50,7 @@ export default { calc(src); }, - unmounted(src, binding, vn) { + unmounted(src, binding) { binding.value(0, 0); const info = mountings.get(src); if (!info) return; @@ -56,4 +58,4 @@ export default { if (info.intersection) info.intersection.disconnect(); mountings.delete(src); }, -} as Directive void>; +} as Directive; diff --git a/packages/frontend/src/directives/hotkey.ts b/packages/frontend/src/directives/hotkey.ts index 63637ab2ba..d8fdfe647a 100644 --- a/packages/frontend/src/directives/hotkey.ts +++ b/packages/frontend/src/directives/hotkey.ts @@ -5,8 +5,14 @@ import type { Directive } from 'vue'; import { makeHotkey } from '@/utility/hotkey.js'; +import type { Keymap } from '@/utility/hotkey.js'; -export default { +interface HTMLElementWithHotkey extends HTMLElement { + _hotkey_global?: boolean; + _keyHandler?: (ev: KeyboardEvent) => void; +} + +export const hotkeyDirective = { mounted(el, binding) { el._hotkey_global = binding.modifiers.global === true; @@ -20,10 +26,11 @@ export default { }, unmounted(el) { + if (el._keyHandler == null) return; if (el._hotkey_global) { window.document.removeEventListener('keydown', el._keyHandler); } else { el.removeEventListener('keydown', el._keyHandler); } }, -} as Directive; +} as Directive; diff --git a/packages/frontend/src/directives/index.ts b/packages/frontend/src/directives/index.ts index 9555045afe..07b756b95d 100644 --- a/packages/frontend/src/directives/index.ts +++ b/packages/frontend/src/directives/index.ts @@ -3,19 +3,19 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import type { App } from 'vue'; +import type { App, Directive } from 'vue'; -import userPreview from './user-preview.js'; -import getSize from './get-size.js'; -import ripple from './ripple.js'; -import tooltip from './tooltip.js'; -import hotkey from './hotkey.js'; -import appear from './appear.js'; -import anim from './anim.js'; -import clickAnime from './click-anime.js'; -import panel from './panel.js'; -import adaptiveBorder from './adaptive-border.js'; -import adaptiveBg from './adaptive-bg.js'; +import { userPreviewDirective } from './user-preview.js'; +import { getSizeDirective } from './get-size.js'; +import { rippleDirective } from './ripple.js'; +import { tooltipDirective } from './tooltip.js'; +import { hotkeyDirective } from './hotkey.js'; +import { appearDirective } from './appear.js'; +import { animDirective } from './anim.js'; +import { clickAnimeDirective } from './click-anime.js'; +import { panelDirective } from './panel.js'; +import { adaptiveBorderDirective } from './adaptive-border.js'; +import { adaptiveBgDirective } from './adaptive-bg.js'; export default function(app: App) { for (const [key, value] of Object.entries(directives)) { @@ -24,16 +24,32 @@ export default function(app: App) { } export const directives = { - 'userPreview': userPreview, - 'user-preview': userPreview, - 'get-size': getSize, - 'ripple': ripple, - 'tooltip': tooltip, - 'hotkey': hotkey, - 'appear': appear, - 'anim': anim, - 'click-anime': clickAnime, - 'panel': panel, - 'adaptive-border': adaptiveBorder, - 'adaptive-bg': adaptiveBg, -}; + 'userPreview': userPreviewDirective, + 'user-preview': userPreviewDirective, + 'get-size': getSizeDirective, + 'ripple': rippleDirective, + 'tooltip': tooltipDirective, + 'hotkey': hotkeyDirective, + 'appear': appearDirective, + 'anim': animDirective, + 'click-anime': clickAnimeDirective, + 'panel': panelDirective, + 'adaptive-border': adaptiveBorderDirective, + 'adaptive-bg': adaptiveBgDirective, +} as Record; + +declare module 'vue' { + export interface ComponentCustomProperties { + vUserPreview: typeof userPreviewDirective; + vGetSize: typeof getSizeDirective; + vRipple: typeof rippleDirective; + vTooltip: typeof tooltipDirective; + vHotkey: typeof hotkeyDirective; + vAppear: typeof appearDirective; + vAnim: typeof animDirective; + vClickAnime: typeof clickAnimeDirective; + vPanel: typeof panelDirective; + vAdaptiveBorder: typeof adaptiveBorderDirective; + vAdaptiveBg: typeof adaptiveBgDirective; + } +} diff --git a/packages/frontend/src/directives/panel.ts b/packages/frontend/src/directives/panel.ts index 0af19e6ca3..7913baefe4 100644 --- a/packages/frontend/src/directives/panel.ts +++ b/packages/frontend/src/directives/panel.ts @@ -6,8 +6,8 @@ import type { Directive } from 'vue'; import { getBgColor } from '@/utility/get-bg-color.js'; -export default { - mounted(src, binding, vn) { +export const panelDirective = { + mounted(src) { const parentBg = getBgColor(src.parentElement) ?? 'transparent'; const myBg = getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel'); @@ -18,4 +18,4 @@ export default { src.style.backgroundColor = 'var(--MI_THEME-panel)'; } }, -} as Directive; +} as Directive; diff --git a/packages/frontend/src/directives/ripple.ts b/packages/frontend/src/directives/ripple.ts index 614cd37011..bacf49ab72 100644 --- a/packages/frontend/src/directives/ripple.ts +++ b/packages/frontend/src/directives/ripple.ts @@ -3,12 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import type { Directive } from 'vue'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { prefer } from '@/preferences.js'; import { popup } from '@/os.js'; -export default { - mounted(el, binding, vn) { +export const rippleDirective = { + mounted(el, binding) { // 明示的に false であればバインドしない if (binding.value === false) return; if (!prefer.s.animation) return; @@ -24,4 +25,4 @@ export default { }); }); }, -}; +} as Directive; diff --git a/packages/frontend/src/directives/tooltip.ts b/packages/frontend/src/directives/tooltip.ts index 62aecbc87c..9cfa8d657d 100644 --- a/packages/frontend/src/directives/tooltip.ts +++ b/packages/frontend/src/directives/tooltip.ts @@ -14,13 +14,30 @@ import { popup, alert } from '@/os.js'; const start = isTouchUsing ? 'touchstart' : 'mouseenter'; const end = isTouchUsing ? 'touchend' : 'mouseleave'; -export default { - mounted(el: HTMLElement, binding, vn) { +type TooltipDirectiveState = { + text: string; + _close: null | (() => void); + showTimer: number | null; + hideTimer: number | null; + checkTimer: number | null; + show: () => void; + close: () => void; +}; + +interface TooltipDirectiveElement extends HTMLElement { + _tooltipDirective_?: TooltipDirectiveState; +} + +type TooltipDirectiveModifiers = 'left' | 'right' | 'top' | 'bottom' | 'mfm' | 'noDelay'; +type TooltipDirectiveArg = 'dialog'; + +export const tooltipDirective = { + mounted(el, binding) { const delay = binding.modifiers.noDelay ? 0 : 100; - const self = (el as any)._tooltipDirective_ = {} as any; + const self = el._tooltipDirective_ = {} as TooltipDirectiveState; - self.text = binding.value as string; + self.text = binding.value; self._close = null; self.showTimer = null; self.hideTimer = null; @@ -28,7 +45,7 @@ export default { self.close = () => { if (self._close) { - window.clearInterval(self.checkTimer); + if (self.checkTimer) window.clearInterval(self.checkTimer); self._close(); self._close = null; } @@ -72,8 +89,8 @@ export default { }); el.addEventListener(start, (ev) => { - window.clearTimeout(self.showTimer); - window.clearTimeout(self.hideTimer); + if (self.showTimer) window.clearTimeout(self.showTimer); + if (self.hideTimer) window.clearTimeout(self.hideTimer); if (delay === 0) { self.show(); } else { @@ -82,8 +99,8 @@ export default { }, { passive: true }); el.addEventListener(end, () => { - window.clearTimeout(self.showTimer); - window.clearTimeout(self.hideTimer); + if (self.showTimer) window.clearTimeout(self.showTimer); + if (self.hideTimer) window.clearTimeout(self.hideTimer); if (delay === 0) { self.close(); } else { @@ -92,18 +109,23 @@ export default { }, { passive: true }); el.addEventListener('click', () => { - window.clearTimeout(self.showTimer); + if (self.showTimer) window.clearTimeout(self.showTimer); self.close(); }); }, updated(el, binding) { const self = el._tooltipDirective_; + if (self == null) return; self.text = binding.value as string; }, - unmounted(el, binding, vn) { + unmounted(el) { const self = el._tooltipDirective_; - window.clearInterval(self.checkTimer); + if (self == null) return; + if (self.showTimer) window.clearTimeout(self.showTimer); + if (self.hideTimer) window.clearTimeout(self.hideTimer); + if (self.checkTimer) window.clearTimeout(self.checkTimer); + self.close(); }, -} as Directive; +} as Directive; diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts index b11ef8f088..76e345a108 100644 --- a/packages/frontend/src/directives/user-preview.ts +++ b/packages/frontend/src/directives/user-preview.ts @@ -5,18 +5,19 @@ import { defineAsyncComponent, ref } from 'vue'; import type { Directive } from 'vue'; +import * as Misskey from 'misskey-js'; import { popup } from '@/os.js'; import { isTouchUsing } from '@/utility/touch.js'; export class UserPreview { - private el; - private user; - private showTimer; - private hideTimer; - private checkTimer; - private promise; - - constructor(el, user) { + private el: HTMLElement; + private user: string | Misskey.entities.UserDetailed; + private showTimer: number | null = null; + private hideTimer: number | null = null; + private checkTimer: number | null = null; + private promise: null | { cancel: () => void } = null; + + constructor(el: HTMLElement, user: string | Misskey.entities.UserDetailed) { this.el = el; this.user = user; @@ -43,10 +44,10 @@ export class UserPreview { source: this.el, }, { mouseover: () => { - window.clearTimeout(this.hideTimer); + if (this.hideTimer) window.clearTimeout(this.hideTimer); }, mouseleave: () => { - window.clearTimeout(this.showTimer); + if (this.showTimer) window.clearTimeout(this.showTimer); this.hideTimer = window.setTimeout(this.close, 500); }, closed: () => dispose(), @@ -60,8 +61,8 @@ export class UserPreview { this.checkTimer = window.setInterval(() => { if (!window.document.body.contains(this.el)) { - window.clearTimeout(this.showTimer); - window.clearTimeout(this.hideTimer); + if (this.showTimer) window.clearTimeout(this.showTimer); + if (this.hideTimer) window.clearTimeout(this.hideTimer); this.close(); } }, 1000); @@ -69,26 +70,26 @@ export class UserPreview { private close() { if (this.promise) { - window.clearInterval(this.checkTimer); + if (this.checkTimer) window.clearInterval(this.checkTimer); this.promise.cancel(); this.promise = null; } } private onMouseover() { - window.clearTimeout(this.showTimer); - window.clearTimeout(this.hideTimer); + if (this.showTimer) window.clearTimeout(this.showTimer); + if (this.hideTimer) window.clearTimeout(this.hideTimer); this.showTimer = window.setTimeout(this.show, 500); } private onMouseleave() { - window.clearTimeout(this.showTimer); - window.clearTimeout(this.hideTimer); + if (this.showTimer) window.clearTimeout(this.showTimer); + if (this.hideTimer) window.clearTimeout(this.hideTimer); this.hideTimer = window.setTimeout(this.close, 500); } private onClick() { - window.clearTimeout(this.showTimer); + if (this.showTimer) window.clearTimeout(this.showTimer); this.close(); } @@ -105,8 +106,14 @@ export class UserPreview { } } -export default { - mounted(el: HTMLElement, binding, vn) { +interface UserPreviewDirectiveElement extends HTMLElement { + _userPreviewDirective_?: { + preview: UserPreview; + }; +} + +export const userPreviewDirective = { + mounted(el, binding) { if (binding.value == null) return; if (isTouchUsing) return; @@ -117,10 +124,11 @@ export default { self.preview = new UserPreview(el, binding.value); }, - unmounted(el, binding, vn) { + unmounted(el, binding) { if (binding.value == null) return; const self = el._userPreviewDirective_; + if (self == null) return; self.preview.detach(); }, -} as Directive; +} as Directive; -- cgit v1.2.3-freya From acd35ef96ca461604219272048f8d40ff3e0c482 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Mon, 20 Oct 2025 14:48:14 +0900 Subject: add note --- packages/frontend/src/components/MkUploaderDialog.vue | 1 + 1 file changed, 1 insertion(+) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/components/MkUploaderDialog.vue b/packages/frontend/src/components/MkUploaderDialog.vue index ce098d71e4..8849fa447d 100644 --- a/packages/frontend/src/components/MkUploaderDialog.vue +++ b/packages/frontend/src/components/MkUploaderDialog.vue @@ -33,6 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only + -- cgit v1.2.3-freya From 8714945ec9deb88e1af6164b9290c9cf7e633aab Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:05:23 +0900 Subject: fix(frontend): ウォーターマーク配置のエフェクトが壊れている問題を修正 (#16662) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): ウォーターマーク配置のエフェクトが壊れている問題を修正 * enhance: add settings for noBoundingBoxExpansion * Update Changelog * fix * perf: ウォーターマークのrepeatをWRAP属性で制御するように * fix: ウォーターマークをrepeatした際に回転や拡大縮小の中心が「位置」設定を考慮しないのを修正 * fix: ウォーターマークをrepeatした際にマージンが各ウォーターマークごとのマージンとなっていない問題を修正 * fix: リピートモード時の拡大縮小の原点が、アライメントの設定にかかわらず左上になる問題を修正 * enhance: preserveBoundingRect の翻訳文字を変更 * fix: remove description * fix * fix: 回転の向きが逆になっているのを修正 * fix: マージンは元画像の大きさに対する割合で算出するように * Update watermarkPlacement.ts --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- CHANGELOG.md | 4 + locales/index.d.ts | 4 + locales/ja-JP.yml | 1 + .../components/MkWatermarkEditorDialog.Layer.vue | 24 +- .../src/components/MkWatermarkEditorDialog.vue | 2 + .../image-effector/fxs/watermarkPlacement.ts | 256 ++++++++++++++------- packages/frontend/src/utility/watermark.ts | 4 + 7 files changed, 208 insertions(+), 87 deletions(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e024a3043..e74b950467 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ - Fix: 一部のブラウザでバナー画像が上下中央に表示されない問題を修正 - Fix: ナビゲーションバーの設定で削除した項目をその場で再追加できない問題を修正 - Fix: ロールポリシーによりダイレクトメッセージが無効化されている際のデッキのダイレクトメッセージカラムの挙動を改善 +- Fix: ウォーターマークの各種挙動修正 + - ウォーターマークを回転させると歪む問題を修正 + - ウォーターマークを敷き詰めると上下左右反転した画像/文字が表示される問題を修正 + - ウォーターマークを回転させた際に画面からはみ出た部分を考慮できるように ### Server - diff --git a/locales/index.d.ts b/locales/index.d.ts index d79db121db..96d6c890a8 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -12345,6 +12345,10 @@ export interface Locale extends ILocale { * 敷き詰める */ "repeat": string; + /** + * 回転時はみ出ないように調整する + */ + "preserveBoundingRect": string; /** * 不透明度 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5d1c37740c..8e935b5d9e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -3305,6 +3305,7 @@ _watermarkEditor: title: "ウォーターマークの編集" cover: "全体に被せる" repeat: "敷き詰める" + preserveBoundingRect: "回転時はみ出ないように調整する" opacity: "不透明度" scale: "サイズ" text: "テキスト" diff --git a/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue b/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue index 288293db3f..b34181e5cc 100644 --- a/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue +++ b/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue @@ -65,6 +65,10 @@ SPDX-License-Identifier: AGPL-3.0-only + + + +