enhance(frontend): ウィジェットの設定項目の多言語対応 (#17032)

* enhance(frontend): ウィジェットの設定項目の多言語対応

* Update Changelog

* refactor: move options locale key to root for optimizing artifacts for locale inlining

* fix

* fix

* ✌️

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
かっこかり 2025-12-30 15:59:18 +09:00 committed by GitHub
parent 4285303c81
commit 97d485bdd2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 262 additions and 18 deletions

View File

@ -6,6 +6,7 @@
### Client ### Client
- Enhance: ドライブのファイル一覧で自動でもっと見るを利用可能に - Enhance: ドライブのファイル一覧で自動でもっと見るを利用可能に
- Enhance: ウィジェットの表示設定をプレビューを見ながら行えるように - Enhance: ウィジェットの表示設定をプレビューを見ながら行えるように
- Enhance: ウィジェットの設定項目のラベルの多言語対応
- Fix: ドライブクリーナーでファイルを削除しても画面に反映されない問題を修正 #16061 - Fix: ドライブクリーナーでファイルを削除しても画面に反映されない問題を修正 #16061
### Server ### Server

View File

@ -2603,6 +2603,43 @@ _widgets:
birthdayFollowings: "今日誕生日のユーザー" birthdayFollowings: "今日誕生日のユーザー"
chat: "ダイレクトメッセージ" chat: "ダイレクトメッセージ"
_widgetOptions:
showHeader: "ヘッダーを表示"
transparent: "背景を透明にする"
height: "高さ"
_button:
colored: "色付き"
_clock:
size: "サイズ"
thickness: "針の太さ"
thicknessThin: "細い"
thicknessMedium: "普通"
thicknessThick: "太い"
graduations: "文字盤の目盛り"
graduationDots: "ドット"
graduationArabic: "アラビア数字"
fadeGraduations: "目盛りをフェード"
sAnimation: "秒針のアニメーション"
sAnimationElastic: "リアル"
sAnimationEaseOut: "滑らか"
twentyFour: "24時間表示"
labelTime: "時刻"
labelTz: "タイムゾーン"
labelTimeAndTz: "時刻とタイムゾーン"
timezone: "タイムゾーン"
showMs: "ミリ秒を表示"
showLabel: "ラベルを表示"
_jobQueue:
sound: "音を鳴らす"
_rss:
url: "RSSフィードのURL"
refreshIntervalSec: "更新間隔(秒)"
maxEntries: "最大表示件数"
_rssTicker:
shuffle: "表示順をシャッフル"
duration: "ティッカーのスクロール速度(秒)"
reverse: "逆方向にスクロール"
_cw: _cw:
hide: "隠す" hide: "隠す"
show: "もっと見る" show: "もっと見る"

View File

@ -38,10 +38,12 @@ const name = 'activity';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: false, default: false,
}, },
view: { view: {

View File

@ -12,6 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, useTemplateRef } from 'vue'; import { onMounted, onUnmounted, useTemplateRef } from 'vue';
import { useWidgetPropsManager } from './widget.js'; import { useWidgetPropsManager } from './widget.js';
import { i18n } from '@/i18n.js';
import type { WidgetComponentProps, WidgetComponentEmits, WidgetComponentExpose } from './widget.js'; import type { WidgetComponentProps, WidgetComponentEmits, WidgetComponentExpose } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js'; import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
@ -20,6 +21,7 @@ const name = 'ai';
const widgetPropsDef = { const widgetPropsDef = {
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: false, default: false,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -37,10 +37,12 @@ const name = 'aiscript';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
script: { script: {
type: 'string', type: 'string',
label: i18n.ts.script,
multiline: true, multiline: true,
default: '(1 + 1)', default: '(1 + 1)',
hidden: true, hidden: true,

View File

@ -24,6 +24,7 @@ import type { AsUiComponent, AsUiRoot } from '@/aiscript/ui.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js'; import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
import { $i } from '@/i.js'; import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import MkAsUi from '@/components/MkAsUi.vue'; import MkAsUi from '@/components/MkAsUi.vue';
import MkContainer from '@/components/MkContainer.vue'; import MkContainer from '@/components/MkContainer.vue';
import { registerAsUiLib } from '@/aiscript/ui.js'; import { registerAsUiLib } from '@/aiscript/ui.js';
@ -33,11 +34,13 @@ const name = 'aiscriptApp';
const widgetPropsDef = { const widgetPropsDef = {
script: { script: {
type: 'string', type: 'string',
label: i18n.ts.script,
multiline: true, multiline: true,
default: '', default: '',
}, },
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -33,11 +33,12 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { $i } from '@/i.js'; import { $i } from '@/i.js';
const name = i18n.ts._widgets.birthdayFollowings; const name = 'birthdayFollowings';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -20,20 +20,24 @@ import * as os from '@/os.js';
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js'; import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
import { $i } from '@/i.js'; import { $i } from '@/i.js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
const name = 'button'; const name = 'button';
const widgetPropsDef = { const widgetPropsDef = {
label: { label: {
type: 'string', type: 'string',
label: i18n.ts.label,
default: 'BUTTON', default: 'BUTTON',
}, },
colored: { colored: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions._button.colored,
default: true, default: true,
}, },
script: { script: {
type: 'string', type: 'string',
label: i18n.ts.script,
multiline: true, multiline: true,
default: 'Mk:dialog("hello" "world")', default: 'Mk:dialog("hello" "world")',
}, },

View File

@ -50,6 +50,7 @@ const name = 'calendar';
const widgetPropsDef = { const widgetPropsDef = {
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: false, default: false,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -29,6 +29,7 @@ const name = 'chat';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { useWidgetPropsManager } from './widget.js'; import { useWidgetPropsManager } from './widget.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js'; import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
import { i18n } from '@/i18n.js';
import MkContainer from '@/components/MkContainer.vue'; import MkContainer from '@/components/MkContainer.vue';
import MkClickerGame from '@/components/MkClickerGame.vue'; import MkClickerGame from '@/components/MkClickerGame.vue';
@ -23,6 +24,7 @@ const name = 'clicker';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: false, default: false,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -44,10 +44,12 @@ const name = 'clock';
const widgetPropsDef = { const widgetPropsDef = {
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: false, default: false,
}, },
size: { size: {
type: 'radio', type: 'radio',
label: i18n.ts._widgetOptions._clock.size,
default: 'medium', default: 'medium',
options: [{ options: [{
value: 'small' as const, value: 'small' as const,
@ -62,79 +64,89 @@ const widgetPropsDef = {
}, },
thickness: { thickness: {
type: 'radio', type: 'radio',
label: i18n.ts._widgetOptions._clock.thickness,
default: 0.2, default: 0.2,
options: [{ options: [{
value: 0.1 as const, value: 0.1 as const,
label: 'thin', label: i18n.ts._widgetOptions._clock.thicknessThin,
}, { }, {
value: 0.2 as const, value: 0.2 as const,
label: 'medium', label: i18n.ts._widgetOptions._clock.thicknessMedium,
}, { }, {
value: 0.3 as const, value: 0.3 as const,
label: 'thick', label: i18n.ts._widgetOptions._clock.thicknessThick,
}], }],
}, },
graduations: { graduations: {
type: 'radio', type: 'radio',
label: i18n.ts._widgetOptions._clock.graduations,
default: 'numbers', default: 'numbers',
options: [{ options: [{
value: 'none' as const, value: 'none' as const,
label: 'None', label: i18n.ts.none,
}, { }, {
value: 'dots' as const, value: 'dots' as const,
label: 'Dots', label: i18n.ts._widgetOptions._clock.graduationDots,
}, { }, {
value: 'numbers' as const, value: 'numbers' as const,
label: 'Numbers', label: i18n.ts._widgetOptions._clock.graduationArabic,
}], }, /*, {
value: 'roman' as const,
label: i18n.ts._widgetOptions._clock.graduationRoman,
}*/],
}, },
fadeGraduations: { fadeGraduations: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions._clock.fadeGraduations,
default: true, default: true,
}, },
sAnimation: { sAnimation: {
type: 'radio', type: 'radio',
label: i18n.ts._widgetOptions._clock.sAnimation,
default: 'elastic', default: 'elastic',
options: [{ options: [{
value: 'none' as const, value: 'none' as const,
label: 'None', label: i18n.ts.none,
}, { }, {
value: 'elastic' as const, value: 'elastic' as const,
label: 'Elastic', label: i18n.ts._widgetOptions._clock.sAnimationElastic,
}, { }, {
value: 'easeOut' as const, value: 'easeOut' as const,
label: 'Ease out', label: i18n.ts._widgetOptions._clock.sAnimationEaseOut,
}], }],
}, },
twentyFour: { twentyFour: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions._clock.twentyFour,
default: false, default: false,
}, },
label: { label: {
type: 'radio', type: 'radio',
label: i18n.ts.label,
default: 'none', default: 'none',
options: [{ options: [{
value: 'none' as const, value: 'none' as const,
label: 'None', label: i18n.ts.none,
}, { }, {
value: 'time' as const, value: 'time' as const,
label: 'Time', label: i18n.ts._widgetOptions._clock.labelTime,
}, { }, {
value: 'tz' as const, value: 'tz' as const,
label: 'TZ', label: i18n.ts._widgetOptions._clock.labelTz,
}, { }, {
value: 'timeAndTz' as const, value: 'timeAndTz' as const,
label: 'Time + TZ', label: i18n.ts._widgetOptions._clock.labelTimeAndTz,
}], }],
}, },
timezone: { timezone: {
type: 'enum', type: 'enum',
label: i18n.ts._widgetOptions._clock.timezone,
default: null, default: null,
enum: [...timezones.map((tz) => ({ enum: [...timezones.map((tz) => ({
label: tz.name, label: tz.name,
value: tz.name.toLowerCase(), value: tz.name.toLowerCase(),
})), { })), {
label: '(auto)', label: i18n.ts.auto,
value: null, value: null,
}], }],
}, },

View File

@ -19,6 +19,7 @@ import { useWidgetPropsManager } from './widget.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js'; import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
import { timezones } from '@/utility/timezones.js'; import { timezones } from '@/utility/timezones.js';
import { i18n } from '@/i18n.js';
import MkDigitalClock from '@/components/MkDigitalClock.vue'; import MkDigitalClock from '@/components/MkDigitalClock.vue';
const name = 'digitalClock'; const name = 'digitalClock';
@ -26,29 +27,34 @@ const name = 'digitalClock';
const widgetPropsDef = { const widgetPropsDef = {
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: false, default: false,
}, },
fontSize: { fontSize: {
type: 'number', type: 'number',
label: i18n.ts.fontSize,
default: 1.5, default: 1.5,
step: 0.1, step: 0.1,
}, },
showMs: { showMs: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions._clock.showMs,
default: true, default: true,
}, },
showLabel: { showLabel: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions._clock.showLabel,
default: true, default: true,
}, },
timezone: { timezone: {
type: 'enum', type: 'enum',
label: i18n.ts._widgetOptions._clock.timezone,
default: null, default: null,
enum: [...timezones.map((tz) => ({ enum: [...timezones.map((tz) => ({
label: tz.name, label: tz.name,
value: tz.name.toLowerCase(), value: tz.name.toLowerCase(),
})), { })), {
label: '(auto)', label: i18n.ts.auto,
value: null, value: null,
}], }],
}, },

View File

@ -43,6 +43,7 @@ const name = 'federation';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -29,12 +29,14 @@ import MkTagCloud from '@/components/MkTagCloud.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js'; import { misskeyApi } from '@/utility/misskey-api.js';
import { getProxiedImageUrlNullable } from '@/utility/media-proxy.js'; import { getProxiedImageUrlNullable } from '@/utility/media-proxy.js';
import { i18n } from '@/i18n.js';
const name = 'instanceCloud'; const name = 'instanceCloud';
const widgetPropsDef = { const widgetPropsDef = {
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: false, default: false,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -61,16 +61,19 @@ import * as sound from '@/utility/sound.js';
import { deepClone } from '@/utility/clone.js'; import { deepClone } from '@/utility/clone.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { genId } from '@/utility/id.js'; import { genId } from '@/utility/id.js';
import { i18n } from '@/i18n.js';
const name = 'jobQueue'; const name = 'jobQueue';
const widgetPropsDef = { const widgetPropsDef = {
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: false, default: false,
}, },
sound: { sound: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions._jobQueue.sound,
default: false, default: false,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -29,10 +29,12 @@ const name = 'memo';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
height: { height: {
type: 'number', type: 'number',
label: i18n.ts.height,
default: 100, default: 100,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -31,10 +31,12 @@ const name = 'notifications';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
height: { height: {
type: 'number', type: 'number',
label: i18n.ts.height,
default: 300, default: 300,
}, },
excludeTypes: { excludeTypes: {

View File

@ -28,6 +28,7 @@ const name = 'onlineUsers';
const widgetPropsDef = { const widgetPropsDef = {
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: true, default: true,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -39,10 +39,12 @@ const name = 'photos';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: false, default: false,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -25,6 +25,7 @@ import * as Misskey from 'misskey-js';
import { url as base } from '@@/js/config.js'; import { url as base } from '@@/js/config.js';
import { useInterval } from '@@/js/use-interval.js'; import { useInterval } from '@@/js/use-interval.js';
import { useWidgetPropsManager } from './widget.js'; import { useWidgetPropsManager } from './widget.js';
import { i18n } from '@/i18n.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js'; import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
import MkContainer from '@/components/MkContainer.vue'; import MkContainer from '@/components/MkContainer.vue';
@ -34,19 +35,23 @@ const name = 'rss';
const widgetPropsDef = { const widgetPropsDef = {
url: { url: {
type: 'string', type: 'string',
label: i18n.ts._widgetOptions._rss.url,
default: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews', default: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews',
manualSave: true, manualSave: true,
}, },
refreshIntervalSec: { refreshIntervalSec: {
type: 'number', type: 'number',
label: i18n.ts._widgetOptions._rss.refreshIntervalSec,
default: 60, default: 60,
}, },
maxEntries: { maxEntries: {
type: 'number', type: 'number',
label: i18n.ts._widgetOptions._rss.maxEntries,
default: 15, default: 15,
}, },
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -35,6 +35,7 @@ import MkMarqueeText from '@/components/MkMarqueeText.vue';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js'; import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
import MkContainer from '@/components/MkContainer.vue'; import MkContainer from '@/components/MkContainer.vue';
import { shuffle } from '@/utility/shuffle.js'; import { shuffle } from '@/utility/shuffle.js';
import { i18n } from '@/i18n.js';
import { url as base } from '@@/js/config.js'; import { url as base } from '@@/js/config.js';
import { useInterval } from '@@/js/use-interval.js'; import { useInterval } from '@@/js/use-interval.js';
@ -43,23 +44,28 @@ const name = 'rssTicker';
const widgetPropsDef = { const widgetPropsDef = {
url: { url: {
type: 'string', type: 'string',
label: i18n.ts._widgetOptions._rss.url,
default: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews', default: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews',
manualSave: true, manualSave: true,
}, },
shuffle: { shuffle: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions._rssTicker.shuffle,
default: true, default: true,
}, },
refreshIntervalSec: { refreshIntervalSec: {
type: 'number', type: 'number',
label: i18n.ts._widgetOptions._rss.refreshIntervalSec,
default: 60, default: 60,
}, },
maxEntries: { maxEntries: {
type: 'number', type: 'number',
label: i18n.ts._widgetOptions._rss.maxEntries,
default: 15, default: 15,
}, },
duration: { duration: {
type: 'range', type: 'range',
label: i18n.ts._widgetOptions._rssTicker.duration,
default: 70, default: 70,
step: 1, step: 1,
min: 5, min: 5,
@ -67,14 +73,17 @@ const widgetPropsDef = {
}, },
reverse: { reverse: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions._rssTicker.reverse,
default: false, default: false,
}, },
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: false, default: false,
}, },
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: false, default: false,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -33,6 +33,7 @@ const name = 'slideshow';
const widgetPropsDef = { const widgetPropsDef = {
height: { height: {
type: 'number', type: 'number',
label: i18n.ts._widgetOptions.height,
default: 300, default: 300,
}, },
folderId: { folderId: {

View File

@ -41,6 +41,7 @@ const name = 'hashtags';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -18,6 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { onUnmounted, ref, watch } from 'vue'; import { onUnmounted, ref, watch } from 'vue';
import { useWidgetPropsManager } from './widget.js'; import { useWidgetPropsManager } from './widget.js';
import { i18n } from '@/i18n.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js'; import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
@ -26,19 +27,23 @@ const name = 'unixClock';
const widgetPropsDef = { const widgetPropsDef = {
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: false, default: false,
}, },
fontSize: { fontSize: {
type: 'number', type: 'number',
label: i18n.ts.fontSize,
default: 1.5, default: 1.5,
step: 0.1, step: 0.1,
}, },
showMs: { showMs: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions._clock.showMs,
default: true, default: true,
}, },
showLabel: { showLabel: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions._clock.showLabel,
default: true, default: true,
}, },
} satisfies FormWithDefault; } satisfies FormWithDefault;

View File

@ -41,6 +41,7 @@ const name = 'userList';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
listId: { listId: {

View File

@ -40,10 +40,12 @@ const name = 'serverMetric';
const widgetPropsDef = { const widgetPropsDef = {
showHeader: { showHeader: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.showHeader,
default: true, default: true,
}, },
transparent: { transparent: {
type: 'boolean', type: 'boolean',
label: i18n.ts._widgetOptions.transparent,
default: false, default: false,
}, },
view: { view: {

View File

@ -10,6 +10,7 @@ import type { Reactive } from 'vue';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js'; import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { deepClone } from '@/utility/clone.js'; import { deepClone } from '@/utility/clone.js';
import { i18n } from '@/i18n';
export type Widget<P extends Record<string, unknown>> = { export type Widget<P extends Record<string, unknown>> = {
id: string; id: string;
@ -75,7 +76,7 @@ export const useWidgetPropsManager = <F extends FormWithDefault>(
canceled: true; canceled: true;
}>((resolve) => { }>((resolve) => {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkWidgetSettingsDialog.vue')), { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkWidgetSettingsDialog.vue')), {
widgetName: name, widgetName: i18n.ts._widgets[name] ?? name,
form: form, form: form,
currentSettings: widgetProps, currentSettings: widgetProps,
}, { }, {

View File

@ -9893,6 +9893,138 @@ export interface Locale extends ILocale {
*/ */
"chat": string; "chat": string;
}; };
"_widgetOptions": {
/**
*
*/
"showHeader": string;
/**
*
*/
"transparent": string;
/**
*
*/
"height": string;
"_button": {
/**
*
*/
"colored": string;
};
"_clock": {
/**
*
*/
"size": string;
/**
*
*/
"thickness": string;
/**
*
*/
"thicknessThin": string;
/**
*
*/
"thicknessMedium": string;
/**
*
*/
"thicknessThick": string;
/**
*
*/
"graduations": string;
/**
*
*/
"graduationDots": string;
/**
*
*/
"graduationArabic": string;
/**
*
*/
"fadeGraduations": string;
/**
*
*/
"sAnimation": string;
/**
*
*/
"sAnimationElastic": string;
/**
*
*/
"sAnimationEaseOut": string;
/**
* 24
*/
"twentyFour": string;
/**
*
*/
"labelTime": string;
/**
*
*/
"labelTz": string;
/**
*
*/
"labelTimeAndTz": string;
/**
*
*/
"timezone": string;
/**
*
*/
"showMs": string;
/**
*
*/
"showLabel": string;
};
"_jobQueue": {
/**
*
*/
"sound": string;
};
"_rss": {
/**
* RSSフィードのURL
*/
"url": string;
/**
* ()
*/
"refreshIntervalSec": string;
/**
*
*/
"maxEntries": string;
};
"_rssTicker": {
/**
*
*/
"shuffle": string;
/**
* ()
*/
"duration": string;
/**
*
*/
"reverse": string;
};
};
"_cw": { "_cw": {
/** /**
* *