diff --git a/apps/stage-tamagotchi/src/main/tray/index.ts b/apps/stage-tamagotchi/src/main/tray/index.ts index 7cedf022a..f31fa5438 100644 --- a/apps/stage-tamagotchi/src/main/tray/index.ts +++ b/apps/stage-tamagotchi/src/main/tray/index.ts @@ -5,6 +5,7 @@ import type { I18n } from '../libs/i18n' import type { ServerChannel } from '../services/airi/channel-server' import type { setupBeatSync } from '../windows/beat-sync' import type { setupCaptionWindowManager } from '../windows/caption' +import type { SettingsWindowManager } from '../windows/settings' import type { WidgetsWindowManager } from '../windows/widgets' import { env } from 'node:process' @@ -79,7 +80,7 @@ function isPositionMatch(window: BrowserWindow, targetX: number, targetY: number export function setupTray(params: { mainWindow: BrowserWindow - settingsWindow: () => Promise + settingsWindow: SettingsWindowManager captionWindow: ReturnType widgetsWindow: WidgetsWindowManager beatSyncBgWindow: Awaited> @@ -172,7 +173,7 @@ export function setupTray(params: { ], }, { type: 'separator' }, - { label: params.i18n.t('tamagotchi.electron.tray.menu.labels.label.settings'), click: () => params.settingsWindow().then(window => toggleWindowShow(window)) }, + { label: params.i18n.t('tamagotchi.electron.tray.menu.labels.label.settings'), click: () => void params.settingsWindow.openWindow('/settings') }, { label: params.i18n.t('tamagotchi.electron.tray.menu.labels.label.about'), click: () => params.aboutWindow().then(window => toggleWindowShow(window)) }, { type: 'separator' }, { label: params.i18n.t('tamagotchi.electron.tray.menu.labels.label.open_inlay'), click: () => setupInlayWindow({ i18n: params.i18n, serverChannel: params.serverChannel }) }, diff --git a/apps/stage-tamagotchi/src/main/windows/dashboard/index.ts b/apps/stage-tamagotchi/src/main/windows/dashboard/index.ts index b5cdf64f6..083071c2d 100644 --- a/apps/stage-tamagotchi/src/main/windows/dashboard/index.ts +++ b/apps/stage-tamagotchi/src/main/windows/dashboard/index.ts @@ -4,6 +4,7 @@ import type { InferOutput } from 'valibot' import type { I18n } from '../../libs/i18n' import type { ServerChannel } from '../../services/airi/channel-server' import type { NoticeWindowManager } from '../notice' +import type { SettingsWindowManager } from '../settings' import { dirname, join, resolve } from 'node:path' import { env } from 'node:process' @@ -41,7 +42,7 @@ const appConfigSchema = object({ type AppConfig = InferOutput export async function setupDashboardWindow(params: { - settingsWindow: () => Promise + settingsWindow: SettingsWindowManager chatWindow: () => Promise noticeWindow: NoticeWindowManager onWindowCreated?: (window: BrowserWindow) => void diff --git a/apps/stage-tamagotchi/src/main/windows/dashboard/rpc/index.electron.ts b/apps/stage-tamagotchi/src/main/windows/dashboard/rpc/index.electron.ts index e1d7d3b31..44628227e 100644 --- a/apps/stage-tamagotchi/src/main/windows/dashboard/rpc/index.electron.ts +++ b/apps/stage-tamagotchi/src/main/windows/dashboard/rpc/index.electron.ts @@ -3,6 +3,7 @@ import type { BrowserWindow } from 'electron' import type { I18n } from '../../../libs/i18n' import type { ServerChannel } from '../../../services/airi/channel-server' import type { NoticeWindowManager } from '../../notice' +import type { SettingsWindowManager } from '../../settings' import { defineInvokeHandler } from '@moeru/eventa' import { createContext } from '@moeru/eventa/adapters/electron/main' @@ -14,7 +15,7 @@ import { setupBaseWindowElectronInvokes } from '../../shared/window' export async function setupDashboardWindowElectronInvokes(params: { window: BrowserWindow - settingsWindow: () => Promise + settingsWindow: SettingsWindowManager chatWindow: () => Promise noticeWindow: NoticeWindowManager i18n: I18n @@ -30,7 +31,7 @@ export async function setupDashboardWindowElectronInvokes(params: { await setupBaseWindowElectronInvokes({ context, window: params.window, serverChannel: params.serverChannel, i18n: params.i18n }) defineInvokeHandler(context, electronOpenMainDevtools, () => params.window.webContents.openDevTools({ mode: 'detach' })) - defineInvokeHandler(context, electronOpenSettings, async () => toggleWindowShow(await params.settingsWindow())) + defineInvokeHandler(context, electronOpenSettings, payload => params.settingsWindow.openWindow(payload?.route)) defineInvokeHandler(context, electronOpenChat, async () => toggleWindowShow(await params.chatWindow())) defineInvokeHandler(context, noticeWindowEventa.openWindow, payload => params.noticeWindow.open(payload)) } diff --git a/apps/stage-tamagotchi/src/main/windows/main/index.ts b/apps/stage-tamagotchi/src/main/windows/main/index.ts index f0e222e24..aae622ff1 100644 --- a/apps/stage-tamagotchi/src/main/windows/main/index.ts +++ b/apps/stage-tamagotchi/src/main/windows/main/index.ts @@ -7,6 +7,7 @@ import type { McpStdioManager } from '../../services/airi/mcp-servers' import type { AutoUpdater } from '../../services/electron/auto-updater' import type { NoticeWindowManager } from '../notice' import type { OnboardingWindowManager } from '../onboarding' +import type { SettingsWindowManager } from '../settings' import type { WidgetsWindowManager } from '../widgets' import { dirname, join, resolve } from 'node:path' @@ -46,7 +47,7 @@ const appConfigSchema = object({ type AppConfig = InferOutput export async function setupMainWindow(params: { - settingsWindow: () => Promise + settingsWindow: SettingsWindowManager chatWindow: () => Promise widgetsManager: WidgetsWindowManager noticeWindow: NoticeWindowManager diff --git a/apps/stage-tamagotchi/src/main/windows/main/rpc/index.electron.ts b/apps/stage-tamagotchi/src/main/windows/main/rpc/index.electron.ts index bc970b523..c28dd6b55 100644 --- a/apps/stage-tamagotchi/src/main/windows/main/rpc/index.electron.ts +++ b/apps/stage-tamagotchi/src/main/windows/main/rpc/index.electron.ts @@ -6,6 +6,7 @@ import type { McpStdioManager } from '../../../services/airi/mcp-servers' import type { AutoUpdater } from '../../../services/electron/auto-updater' import type { NoticeWindowManager } from '../../notice' import type { OnboardingWindowManager } from '../../onboarding' +import type { SettingsWindowManager } from '../../settings' import type { WidgetsWindowManager } from '../../widgets' import { defineInvokeHandler } from '@moeru/eventa' @@ -22,7 +23,7 @@ import { setupBaseWindowElectronInvokes } from '../../shared/window' export async function setupMainWindowElectronInvokes(params: { window: BrowserWindow - settingsWindow: () => Promise + settingsWindow: SettingsWindowManager chatWindow: () => Promise widgetsManager: WidgetsWindowManager noticeWindow: NoticeWindowManager @@ -46,7 +47,7 @@ export async function setupMainWindowElectronInvokes(params: { createOnboardingService({ context, onboardingWindowManager: params.onboardingWindowManager }) defineInvokeHandler(context, electronOpenMainDevtools, () => params.window.webContents.openDevTools({ mode: 'detach' })) - defineInvokeHandler(context, electronOpenSettings, async () => toggleWindowShow(await params.settingsWindow())) + defineInvokeHandler(context, electronOpenSettings, payload => params.settingsWindow.openWindow(payload?.route)) defineInvokeHandler(context, electronOpenChat, async () => toggleWindowShow(await params.chatWindow())) defineInvokeHandler(context, noticeWindowEventa.openWindow, payload => params.noticeWindow.open(payload)) } diff --git a/apps/stage-tamagotchi/src/main/windows/settings/index.ts b/apps/stage-tamagotchi/src/main/windows/settings/index.ts index 429869f47..3ec4fa52d 100644 --- a/apps/stage-tamagotchi/src/main/windows/settings/index.ts +++ b/apps/stage-tamagotchi/src/main/windows/settings/index.ts @@ -12,10 +12,17 @@ import { BrowserWindow, shell } from 'electron' import icon from '../../../../resources/icon.png?asset' +import { electronSettingsNavigate } from '../../../shared/eventa' import { baseUrl, getElectronMainDirname, load, withHashRoute } from '../../libs/electron/location' import { createReusableWindow } from '../../libs/electron/window-manager' +import { toggleWindowShow } from '../shared' import { setupSettingsWindowInvokes } from './rpc/index.electron' +export interface SettingsWindowManager { + getWindow: () => Promise + openWindow: (route?: string) => Promise +} + export function setupSettingsWindowReusableFunc(params: { widgetsManager: WidgetsWindowManager autoUpdater: AutoUpdater @@ -24,8 +31,13 @@ export function setupSettingsWindowReusableFunc(params: { serverChannel: ServerChannel mcpStdioManager: McpStdioManager i18n: I18n -}) { - return createReusableWindow(async () => { +}): SettingsWindowManager { + const rendererBase = baseUrl(resolve(getElectronMainDirname(), '..', 'renderer')) + const defaultRoute = '/settings' + let currentRoute = defaultRoute + let settingsContext: Awaited> | undefined + + const reusable = createReusableWindow(async () => { const window = new BrowserWindow({ title: 'Settings', width: 600.0, @@ -48,8 +60,8 @@ export function setupSettingsWindowReusableFunc(params: { return { action: 'deny' } }) - await load(window, withHashRoute(baseUrl(resolve(getElectronMainDirname(), '..', 'renderer')), '/settings')) - await setupSettingsWindowInvokes({ + await load(window, withHashRoute(rendererBase, currentRoute)) + settingsContext = await setupSettingsWindowInvokes({ settingsWindow: window, widgetsManager: params.widgetsManager, autoUpdater: params.autoUpdater, @@ -59,8 +71,32 @@ export function setupSettingsWindowReusableFunc(params: { i18n: params.i18n, }) + window.on('closed', () => { + if (settingsContext) + settingsContext = undefined + }) + initScreenCaptureForWindow(window) return window - }).getWindow + }) + + async function openWindow(route?: string) { + if (route) { + currentRoute = route + } + + const window = await reusable.getWindow() + + if (route && settingsContext) { + settingsContext.emit(electronSettingsNavigate, { route }) + } + + toggleWindowShow(window) + } + + return { + getWindow: reusable.getWindow, + openWindow, + } } diff --git a/apps/stage-tamagotchi/src/main/windows/settings/rpc/index.electron.ts b/apps/stage-tamagotchi/src/main/windows/settings/rpc/index.electron.ts index ce29005aa..b56e4f422 100644 --- a/apps/stage-tamagotchi/src/main/windows/settings/rpc/index.electron.ts +++ b/apps/stage-tamagotchi/src/main/windows/settings/rpc/index.electron.ts @@ -43,4 +43,6 @@ export async function setupSettingsWindowInvokes(params: { defineInvokeHandler(context, electronOpenDevtoolsWindow, async (payload) => { await params.devtoolsMarkdownStressWindow.openWindow(payload?.route) }) + + return context } diff --git a/apps/stage-tamagotchi/src/renderer/App.vue b/apps/stage-tamagotchi/src/renderer/App.vue index a9edcb025..58103880d 100644 --- a/apps/stage-tamagotchi/src/renderer/App.vue +++ b/apps/stage-tamagotchi/src/renderer/App.vue @@ -28,7 +28,6 @@ import { electronGetServerChannelConfig, electronMcpCallTool, electronMcpListTools, - electronOpenSettings, electronPluginInspect, electronPluginList, electronPluginLoad, @@ -36,6 +35,7 @@ import { electronPluginSetEnabled, electronPluginUnload, electronPluginUpdateCapability, + electronSettingsNavigate, electronStartTrackMousePosition, i18nSetLocale, pluginProtocolListProviders, @@ -105,6 +105,17 @@ watch(dark, () => updateThemeColor(), { immediate: true }) watch(route, () => updateThemeColor(), { immediate: true }) onMounted(() => updateThemeColor()) +context.value.on(electronSettingsNavigate, (event) => { + const targetRoute = event?.body?.route + if (!targetRoute || route.fullPath === targetRoute) { + return + } + + void router.push(targetRoute).catch((error) => { + console.warn('Failed to navigate settings window:', error) + }) +}) + onMounted(async () => { analyticsStore.initialize() cardStore.initialize() @@ -133,9 +144,6 @@ onMounted(async () => { }, }) } - - // Listen for open-settings IPC message from main process - defineInvokeHandler(context.value, electronOpenSettings, () => router.push('/settings')) }) watch(themeColorsHue, () => { diff --git a/apps/stage-tamagotchi/src/renderer/components/stage-islands/controls-island/index.vue b/apps/stage-tamagotchi/src/renderer/components/stage-islands/controls-island/index.vue index 3224f5501..3005d1250 100644 --- a/apps/stage-tamagotchi/src/renderer/components/stage-islands/controls-island/index.vue +++ b/apps/stage-tamagotchi/src/renderer/components/stage-islands/controls-island/index.vue @@ -120,7 +120,7 @@ function refreshWindow() {
- +