generated from Labyricorn/labyricorn-project-template
CI / frontend-quality (push) Canceled after 0s
- All 'voicebox'/'Voicebox'/'VOICEBOX' strings replaced with 'talkbox'/'TalkBox'/'TALKBOX' - Port changed from 17493 to 17494 (avoids conflict with upstream VoiceBox) - MCP tool namespace: voicebox.* -> talkbox.* - App bundle ID: sh.voicebox.app -> com.talkbox.app - Binary names: voicebox-server -> talkbox-server, voicebox-mcp -> talkbox-mcp - Docker user/group: voicebox -> talkbox - Database: voicebox.db -> talkbox.db - Env vars: VOICEBOX_* -> TALKBOX_* - Asset files renamed: voicebox-logo.* -> talkbox-logo.*, etc. - External binaries in tauri.conf.json updated to talkbox-server/talkbox-mcp
110 lines
3.1 KiB
TypeScript
110 lines
3.1 KiB
TypeScript
import { create } from 'zustand';
|
|
import { persist } from 'zustand/middleware';
|
|
|
|
export type Theme = 'light' | 'dark' | 'system';
|
|
|
|
function resolveTheme(theme: Theme): 'light' | 'dark' {
|
|
if (theme !== 'system') return theme;
|
|
if (typeof window === 'undefined') return 'dark';
|
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
}
|
|
|
|
function applyTheme(theme: Theme) {
|
|
if (typeof document === 'undefined') return;
|
|
document.documentElement.classList.toggle('dark', resolveTheme(theme) === 'dark');
|
|
}
|
|
|
|
// Draft state for the create voice profile form
|
|
export interface ProfileFormDraft {
|
|
name: string;
|
|
description: string;
|
|
language: string;
|
|
personality: string;
|
|
referenceText: string;
|
|
sampleMode: 'upload' | 'record' | 'system';
|
|
// Note: File objects can't be persisted, so we store metadata
|
|
sampleFileName?: string;
|
|
sampleFileType?: string;
|
|
sampleFileData?: string; // Base64 encoded
|
|
}
|
|
|
|
interface UIStore {
|
|
// Sidebar
|
|
sidebarOpen: boolean;
|
|
setSidebarOpen: (open: boolean) => void;
|
|
|
|
// Modals
|
|
profileDialogOpen: boolean;
|
|
setProfileDialogOpen: (open: boolean) => void;
|
|
editingProfileId: string | null;
|
|
setEditingProfileId: (id: string | null) => void;
|
|
|
|
generationDialogOpen: boolean;
|
|
setGenerationDialogOpen: (open: boolean) => void;
|
|
|
|
// Selected profile for generation
|
|
selectedProfileId: string | null;
|
|
setSelectedProfileId: (id: string | null) => void;
|
|
|
|
// Currently selected engine (synced from generation form)
|
|
selectedEngine: string;
|
|
setSelectedEngine: (engine: string) => void;
|
|
|
|
// Selected voice in Voices tab inspector
|
|
selectedVoiceId: string | null;
|
|
setSelectedVoiceId: (id: string | null) => void;
|
|
|
|
// Profile form draft (for persisting create voice modal state)
|
|
profileFormDraft: ProfileFormDraft | null;
|
|
setProfileFormDraft: (draft: ProfileFormDraft | null) => void;
|
|
|
|
// Theme
|
|
theme: Theme;
|
|
setTheme: (theme: Theme) => void;
|
|
}
|
|
|
|
export const useUIStore = create<UIStore>()(
|
|
persist(
|
|
(set) => ({
|
|
sidebarOpen: true,
|
|
setSidebarOpen: (open) => set({ sidebarOpen: open }),
|
|
|
|
profileDialogOpen: false,
|
|
setProfileDialogOpen: (open) => set({ profileDialogOpen: open }),
|
|
editingProfileId: null,
|
|
setEditingProfileId: (id) => set({ editingProfileId: id }),
|
|
|
|
generationDialogOpen: false,
|
|
setGenerationDialogOpen: (open) => set({ generationDialogOpen: open }),
|
|
|
|
selectedProfileId: null,
|
|
setSelectedProfileId: (id) => set({ selectedProfileId: id }),
|
|
|
|
selectedEngine: 'qwen',
|
|
setSelectedEngine: (engine) => set({ selectedEngine: engine }),
|
|
|
|
selectedVoiceId: null,
|
|
setSelectedVoiceId: (id) => set({ selectedVoiceId: id }),
|
|
|
|
profileFormDraft: null,
|
|
setProfileFormDraft: (draft) => set({ profileFormDraft: draft }),
|
|
|
|
theme: 'system',
|
|
setTheme: (theme) => {
|
|
set({ theme });
|
|
applyTheme(theme);
|
|
},
|
|
}),
|
|
{
|
|
name: 'talkbox-ui',
|
|
partialize: (state) => ({
|
|
selectedProfileId: state.selectedProfileId,
|
|
theme: state.theme,
|
|
}),
|
|
onRehydrateStorage: () => (state) => {
|
|
if (state) applyTheme(state.theme);
|
|
},
|
|
},
|
|
),
|
|
);
|