diff options
| -rw-r--r-- | locales/index.d.ts | 4 | ||||
| -rw-r--r-- | locales/ja-JP.yml | 1 | ||||
| -rw-r--r-- | packages/frontend/src/pages/settings/theme.vue | 35 |
3 files changed, 33 insertions, 7 deletions
diff --git a/locales/index.d.ts b/locales/index.d.ts index 16003570a2..492688773f 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -8367,6 +8367,10 @@ export interface Locale extends ILocale { */ "code": string; /** + * テーマコードをコピー + */ + "copyThemeCode": string; + /** * 説明 */ "description": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 6ef92e0f2e..3aa8e44ce7 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2193,6 +2193,7 @@ _theme: install: "テーマのインストール" manage: "テーマの管理" code: "テーマコード" + copyThemeCode: "テーマコードをコピー" description: "説明" installed: "{name}をインストールしました" installedThemes: "インストールされたテーマ" diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index f3a6458109..accb1ccc55 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="instanceLightTheme.id" /> - <label :for="`themeRadio_${instanceLightTheme.id}`" :class="$style.themeItemRoot" class="_button"> + <label :for="`themeRadio_${instanceLightTheme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(instanceLightTheme, $event)"> <MkThemePreview :theme="instanceLightTheme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ instanceLightTheme.name }}</div> </label> @@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="theme.id" /> - <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button"> + <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> <MkThemePreview :theme="theme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ theme.name }}</div> </label> @@ -96,7 +96,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="theme.id" /> - <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button"> + <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> <MkThemePreview :theme="theme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ theme.name }}</div> </label> @@ -127,7 +127,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="instanceDarkTheme.id" /> - <label :for="`themeRadio_${instanceDarkTheme.id}`" :class="$style.themeItemRoot" class="_button"> + <label :for="`themeRadio_${instanceDarkTheme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(instanceDarkTheme, $event)"> <MkThemePreview :theme="instanceDarkTheme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ instanceDarkTheme.name }}</div> </label> @@ -147,7 +147,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="theme.id" /> - <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button"> + <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> <MkThemePreview :theme="theme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ theme.name }}</div> </label> @@ -167,7 +167,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="theme.id" /> - <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button"> + <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> <MkThemePreview :theme="theme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ theme.name }}</div> </label> @@ -210,7 +210,7 @@ import FormSection from '@/components/form/section.vue'; import FormLink from '@/components/form/link.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkThemePreview from '@/components/MkThemePreview.vue'; -import { getBuiltinThemesRef, getThemesRef } from '@/theme.js'; +import { getBuiltinThemesRef, getThemesRef, removeTheme } from '@/theme.js'; import { isDeviceDarkmode } from '@/utility/is-device-darkmode.js'; import { store } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -218,6 +218,7 @@ import { instance } from '@/instance.js'; import { uniqueBy } from '@/utility/array.js'; import { definePage } from '@/page.js'; import { prefer } from '@/preferences.js'; +import { copyToClipboard } from '@/utility/copy-to-clipboard.js'; const installedThemes = getThemesRef(); const builtinThemes = getBuiltinThemesRef(); @@ -295,6 +296,26 @@ function changeThemesSyncEnabled(value: boolean) { } } +function onThemeContextmenu(theme: Theme, ev: MouseEvent) { + os.contextMenu([{ + type: 'label', + text: theme.name, + }, { + icon: 'ti ti-clipboard', + text: i18n.ts._theme.copyThemeCode, + action: () => { + copyToClipboard(JSON5.stringify(theme, null, '\t')); + }, + }, { + icon: 'ti ti-trash', + text: i18n.ts.delete, + danger: true, + action: () => { + removeTheme(theme); + }, + }], ev); +} + const headerActions = computed(() => []); const headerTabs = computed(() => []); |