From 17debf3e96670e5bf9e37c48f73825a3be991d83 Mon Sep 17 00:00:00 2001 From: Neko Ayaka Date: Sun, 18 Jan 2026 04:54:57 +0800 Subject: [PATCH] refactor(stage-*): de-centralize for route header --- .../src/renderer/layouts/settings.vue | 165 +++++------------ .../src/renderer/pages/devtools/index.vue | 1 + .../pages/devtools/lag-visualizer.vue | 2 + .../pages/devtools/screen-capture.vue | 2 + .../devtools/use-electron-all-displays.vue | 2 + .../devtools/use-electron-relative-mouse.vue | 2 + .../pages/devtools/use-magic-keys.vue | 2 + .../pages/devtools/use-window-mouse.vue | 2 + .../pages/devtools/widgets-calling.vue | 2 + .../pages/settings/connection/index.vue | 6 + .../src/renderer/pages/settings/index.vue | 63 ++----- .../pages/settings/system/developer.vue | 2 + .../renderer/pages/settings/system/index.vue | 6 + .../pages/devtools/performance-playground.vue | 2 + .../pages/devtools/performance-visualizer.vue | 2 + .../src/pages/settings/characters/index.vue | 1 + .../src/pages/settings/system/developer.vue | 2 + .../src/pages/settings/system/index.vue | 6 + packages/stage-layouts/src/layouts/plain.vue | 4 + .../stage-layouts/src/layouts/settings.vue | 170 +++++------------- .../src/pages/devtools/beat-sync.vue | 2 + .../src/pages/devtools/context-flow/index.vue | 2 + .../stage-pages/src/pages/devtools/image.vue | 2 + .../src/pages/devtools/markdown-stress.vue | 2 + ...ders-transcription-realtime-aliyun-nls.vue | 2 + .../src/pages/devtools/vibrant.vue | 2 + .../pages/devtools/websocket-inspector.vue | 2 + .../src/pages/settings/airi-card/index.vue | 6 + .../src/pages/settings/connection/index.vue | 6 + .../src/pages/settings/data/index.vue | 6 + .../stage-pages/src/pages/settings/index.vue | 69 ++----- .../src/pages/settings/memory/index.vue | 6 + .../src/pages/settings/models/index.vue | 6 + .../src/pages/settings/modules/beat-sync.vue | 2 + .../pages/settings/modules/consciousness.vue | 2 + .../settings/modules/gaming-factorio.vue | 2 + .../settings/modules/gaming-minecraft.vue | 2 + .../src/pages/settings/modules/hearing.vue | 2 + .../src/pages/settings/modules/index.vue | 6 + .../settings/modules/memory-long-term.vue | 2 + .../settings/modules/memory-short-term.vue | 2 + .../settings/modules/messaging-discord.vue | 2 + .../src/pages/settings/modules/speech.vue | 2 + .../src/pages/settings/modules/vision.vue | 2 + .../src/pages/settings/modules/x.vue | 2 + .../src/pages/settings/providers/index.vue | 6 + .../src/pages/settings/scene/index.vue | 6 + .../pages/settings/system/color-scheme.vue | 2 + .../src/pages/settings/system/general.vue | 2 + 49 files changed, 242 insertions(+), 359 deletions(-) diff --git a/apps/stage-tamagotchi/src/renderer/layouts/settings.vue b/apps/stage-tamagotchi/src/renderer/layouts/settings.vue index b32b06bfd..ffc6b20e2 100644 --- a/apps/stage-tamagotchi/src/renderer/layouts/settings.vue +++ b/apps/stage-tamagotchi/src/renderer/layouts/settings.vue @@ -2,7 +2,6 @@ import { isStageTamagotchi } from '@proj-airi/stage-shared' import { PageHeader } from '@proj-airi/stage-ui/components' import { useProvidersStore } from '@proj-airi/stage-ui/stores/providers' -import { storeToRefs } from 'pinia' import { computed, ref } from 'vue' import { useI18n } from 'vue-i18n' import { RouterView, useRoute } from 'vue-router' @@ -14,140 +13,56 @@ import { useRestoreScroll } from '../composables/use-restore-scroll' const route = useRoute() const { t } = useI18n() const providersStore = useProvidersStore() -const { allProvidersMetadata } = storeToRefs(providersStore) const scrollContainer = ref() useRestoreScroll(scrollContainer) -const routeHeaderMetadataMap = computed(() => { - const map: Record = { - '/settings/airi-card': { - subtitle: t('settings.title'), - title: t('settings.pages.card.title'), - }, - '/settings/system': { - subtitle: t('settings.title'), - title: t('settings.pages.system.title'), - }, - '/settings/system/general': { - subtitle: t('settings.title'), - title: t('settings.pages.system.general.title'), - }, - '/settings/system/color-scheme': { - subtitle: t('settings.title'), - title: t('settings.pages.system.color-scheme.title'), - }, - '/settings/system/window-shortcuts': { - subtitle: t('settings.title'), - title: t('tamagotchi.settings.pages.system.window-shortcuts.title'), - }, - '/settings/system/developer': { - subtitle: t('settings.title'), - title: t('settings.pages.system.developer.title'), - }, - '/settings/memory': { - subtitle: t('settings.title'), - title: t('settings.pages.memory.title'), - }, - '/settings/models': { - subtitle: t('settings.title'), - title: t('settings.pages.models.title'), - }, - '/settings/modules': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.title'), - }, - '/settings/modules/consciousness': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.consciousness.title'), - }, - '/settings/modules/speech': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.speech.title'), - }, - '/settings/modules/hearing': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.hearing.title'), - }, - '/settings/modules/memory-short-term': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.memory-short-term.title'), - }, - '/settings/modules/memory-long-term': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.memory-long-term.title'), - }, - '/settings/modules/messaging-discord': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.messaging-discord.title'), - }, - '/settings/modules/x': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.x.title'), - }, - '/settings/modules/gaming-minecraft': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.gaming-minecraft.title'), - }, - '/settings/modules/gaming-factorio': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.gaming-factorio.title'), - }, - '/settings/modules/beat-sync': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.beat_sync.title'), - }, - '/settings/providers': { - subtitle: t('settings.title'), - title: t('settings.pages.providers.title'), - }, - '/settings/data': { - subtitle: t('settings.title'), - title: t('settings.pages.data.title'), - }, - '/settings/connection': { - subtitle: t('settings.title'), - title: t('settings.pages.connection.title'), - }, - '/settings/scene': { - subtitle: t('settings.title'), - title: t('settings.pages.scene.title'), - }, - '/settings': { - title: t('settings.title'), - }, +const routeMeta = computed(() => route.meta as { + titleKey?: string + subtitleKey?: string + title?: string + subtitle?: string +}) - // Tamagotchi specific - '/settings/modules/mcp': { - subtitle: t('settings.title'), - title: t('settings.pages.modules.mcp-server.title'), - }, - '/devtools/widgets-calling': { - subtitle: t('tamagotchi.settings.devtools.title'), - title: t('tamagotchi.settings.devtools.pages.widgets-calling.title'), - }, - '/devtools/markdown-stress': { - subtitle: t('tamagotchi.settings.devtools.title'), - title: t('tamagotchi.settings.devtools.pages.markdown-stress.title'), - }, - '/devtools/context-flow': { - subtitle: t('tamagotchi.settings.devtools.title'), - title: t('tamagotchi.settings.devtools.pages.context-flow.title'), - }, +const providerTitle = computed(() => { + if (!route.path.startsWith('/settings/providers/')) + return undefined + + const segments = route.path.split('/').filter(Boolean) + const providerId = segments[3] + + if (!providerId) + return undefined + + try { + const metadata = providersStore.getProviderMetadata(providerId) + return t(metadata.nameKey) } - - for (const metadata of allProvidersMetadata.value) { - map[`/settings/providers/${metadata.category}/${metadata.id}`] = { - subtitle: t('settings.title'), - title: t(metadata.nameKey), - } + catch { + return undefined } - - return map }) // const activeSettingsTutorial = ref('default') const routeHeaderMetadata = computed(() => { - return routeHeaderMetadataMap.value[route.path] || routeHeaderMetadataMap.value[`${route.path}/`] + const { titleKey, subtitleKey, title, subtitle } = routeMeta.value + const resolvedTitle = titleKey ? t(titleKey) : title + const resolvedSubtitle = subtitleKey ? t(subtitleKey) : subtitle + + if (resolvedTitle || resolvedSubtitle) { + return { + title: resolvedTitle, + subtitle: resolvedSubtitle, + } + } + + if (providerTitle.value) { + return { + title: providerTitle.value, + subtitle: t('settings.title'), + } + } + + return undefined }) diff --git a/apps/stage-tamagotchi/src/renderer/pages/devtools/index.vue b/apps/stage-tamagotchi/src/renderer/pages/devtools/index.vue index a6e6be962..0b749bbf7 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/devtools/index.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/devtools/index.vue @@ -9,4 +9,5 @@ import { RouterView } from 'vue-router' meta: layout: settings + titleKey: tamagotchi.settings.devtools.title diff --git a/apps/stage-tamagotchi/src/renderer/pages/devtools/lag-visualizer.vue b/apps/stage-tamagotchi/src/renderer/pages/devtools/lag-visualizer.vue index 284507e54..c8d4abb63 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/devtools/lag-visualizer.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/devtools/lag-visualizer.vue @@ -21,4 +21,6 @@ const { t } = useI18n() meta: layout: settings + titleKey: tamagotchi.settings.devtools.pages.lag-visualizer.title + subtitleKey: tamagotchi.settings.devtools.title diff --git a/apps/stage-tamagotchi/src/renderer/pages/devtools/screen-capture.vue b/apps/stage-tamagotchi/src/renderer/pages/devtools/screen-capture.vue index 5c04a978e..a94c490f1 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/devtools/screen-capture.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/devtools/screen-capture.vue @@ -387,4 +387,6 @@ onBeforeUnmount(() => { meta: layout: settings + title: Screen Capture + subtitleKey: tamagotchi.settings.devtools.title diff --git a/apps/stage-tamagotchi/src/renderer/pages/devtools/use-electron-all-displays.vue b/apps/stage-tamagotchi/src/renderer/pages/devtools/use-electron-all-displays.vue index ff281dda2..5726ab6f2 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/devtools/use-electron-all-displays.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/devtools/use-electron-all-displays.vue @@ -142,4 +142,6 @@ const containerDimensions = computed(() => { meta: layout: settings + title: useElectronAllDisplays + subtitleKey: tamagotchi.settings.devtools.title diff --git a/apps/stage-tamagotchi/src/renderer/pages/devtools/use-electron-relative-mouse.vue b/apps/stage-tamagotchi/src/renderer/pages/devtools/use-electron-relative-mouse.vue index 79ca9036a..9e33a8aee 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/devtools/use-electron-relative-mouse.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/devtools/use-electron-relative-mouse.vue @@ -77,4 +77,6 @@ const windowBounds = useElectronWindowBounds() meta: layout: settings + title: useElectronRelativeMouse + subtitleKey: tamagotchi.settings.devtools.title diff --git a/apps/stage-tamagotchi/src/renderer/pages/devtools/use-magic-keys.vue b/apps/stage-tamagotchi/src/renderer/pages/devtools/use-magic-keys.vue index af6679933..e8834acd0 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/devtools/use-magic-keys.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/devtools/use-magic-keys.vue @@ -139,4 +139,6 @@ const Key = defineComponent({ meta: layout: settings + title: useMagicKeys + subtitleKey: tamagotchi.settings.devtools.title diff --git a/apps/stage-tamagotchi/src/renderer/pages/devtools/use-window-mouse.vue b/apps/stage-tamagotchi/src/renderer/pages/devtools/use-window-mouse.vue index 7649077ff..d6871ad00 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/devtools/use-window-mouse.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/devtools/use-window-mouse.vue @@ -17,4 +17,6 @@ const { x, y } = useElectronMouse() meta: layout: settings + title: useWindowMouse + subtitleKey: tamagotchi.settings.devtools.title diff --git a/apps/stage-tamagotchi/src/renderer/pages/devtools/widgets-calling.vue b/apps/stage-tamagotchi/src/renderer/pages/devtools/widgets-calling.vue index 0f6c725af..3ee079bc7 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/devtools/widgets-calling.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/devtools/widgets-calling.vue @@ -379,4 +379,6 @@ function applyMapPreset() { meta: layout: settings + titleKey: tamagotchi.settings.devtools.pages.widgets-calling.title + subtitleKey: tamagotchi.settings.devtools.title diff --git a/apps/stage-tamagotchi/src/renderer/pages/settings/connection/index.vue b/apps/stage-tamagotchi/src/renderer/pages/settings/connection/index.vue index 5ac66f827..b9e492ab8 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/settings/connection/index.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/settings/connection/index.vue @@ -11,6 +11,12 @@ import ConnectionSettings from '@proj-airi/stage-pages/pages/settings/connection meta: layout: settings + titleKey: settings.pages.connection.title + subtitleKey: settings.title + descriptionKey: settings.pages.connection.description + icon: i-solar:wi-fi-router-bold-duotone + settingsEntry: true + order: 8 stageTransition: name: slide diff --git a/apps/stage-tamagotchi/src/renderer/pages/settings/index.vue b/apps/stage-tamagotchi/src/renderer/pages/settings/index.vue index e2ee630c4..19ce449eb 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/settings/index.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/settings/index.vue @@ -23,56 +23,18 @@ const removeBeforeEach = router.beforeEach(async (_, __, next) => { next() }) -const settings = computed(() => [ - { - title: t('settings.pages.card.title'), - description: t('settings.pages.card.description'), - icon: 'i-solar:emoji-funny-square-bold-duotone', - to: '/settings/airi-card', - }, - { - title: t('settings.pages.modules.title'), - description: t('settings.pages.modules.description'), - icon: 'i-solar:layers-bold-duotone', - to: '/settings/modules', - }, - { - title: t('settings.pages.scene.title'), - description: t('settings.pages.scene.description'), - icon: 'i-solar:armchair-2-bold-duotone', - to: '/settings/scene', - }, - { - title: t('settings.pages.models.title'), - description: t('settings.pages.models.description'), - icon: 'i-solar:people-nearby-bold-duotone', - to: '/settings/models', - }, - { - title: t('settings.pages.memory.title'), - description: t('settings.pages.memory.description'), - icon: 'i-solar:leaf-bold-duotone', - to: '/settings/memory', - }, - { - title: t('settings.pages.providers.title'), - description: t('settings.pages.providers.description'), - icon: 'i-solar:box-minimalistic-bold-duotone', - to: '/settings/providers', - }, - { - title: t('settings.pages.connection.title'), - description: t('settings.pages.connection.description'), - icon: 'i-solar:wi-fi-router-bold-duotone', - to: '/settings/connection', - }, - { - title: t('settings.pages.system.title'), - description: t('settings.pages.system.description'), - icon: 'i-solar:filters-bold-duotone', - to: '/settings/system', - }, -]) +const settings = computed(() => { + return router + .getRoutes() + .filter(route => route.meta?.settingsEntry) + .sort((a, b) => (Number(a.meta?.order ?? 0) - Number(b.meta?.order ?? 0))) + .map(route => ({ + title: route.meta?.titleKey ? t(route.meta.titleKey as string) : (route.meta?.title as string | undefined), + description: route.meta?.descriptionKey ? t(route.meta.descriptionKey as string) : (route.meta?.description as string | undefined) || '', + icon: route.meta?.icon as string | undefined, + to: route.path, + })) +})