Renderer プロセス設計
Renderer プロセスはすべての UI レンダリングとユーザー操作を処理し、React 18 + TypeScript で構築されています。エントリファイルは packages/renderer/src/app/App.tsx です。
Context Provider 階層
App コンポーネントは厳密な Provider のネスト順序を定義します。外側の Provider は内側の Provider とコンポーネントからアクセスできます。
graph TB
LP[LocaleProvider — i18n internationalization]
TP[ThemeProvider — theme/wallpaper]
CDP[ConfirmDialogProvider — global confirm dialog]
AP[AuthProvider — auth state]
SDP[SettingsDataProvider — settings data]
EP[ElfiProvider — Elfi assistant]
WSP[WebSearchProvider — search]
ChatDP[ChatDataProvider — chat data cache]
CBP[ChatBackendProvider — WebSocket backend]
UCP[UnifiedChatProvider — unified chat API]
HPP[HtmlPreviewProvider — HTML preview]
DPP[DevPreviewProvider — dev preview]
PR[ProtectedRoute — route guard]
HR[HashRouter — frontend routing]
CTP[ChatTabsProvider — multi-tab management]
AR[AppRoutes — route definition]
AC[AppContent — main content]
LP --> TP --> CDP --> AP --> SDP --> EP --> WSP --> ChatDP --> CBP --> UCP --> HPP --> DPP --> PR --> HR --> CTP --> AR --> AC
// Actual nesting structure in App.tsx
function App() {
return (
<LocaleProvider>
<ThemeProvider>
<ConfirmDialogProvider>
<AuthProvider>
<SettingsDataProvider>
<ElfiProvider>
<WebSearchProvider>
<ChatDataProvider>
<ChatBackendProvider>
<UnifiedChatProvider>
<HtmlPreviewProvider>
<DevPreviewProvider>
<ProtectedRoute>
<HashRouter>
<ChatTabsProvider>
<AppRoutes>
<AppContent />
</AppRoutes>
</ChatTabsProvider>
</HashRouter>
</ProtectedRoute>
</DevPreviewProvider>
</HtmlPreviewProvider>
</UnifiedChatProvider>
</ChatBackendProvider>
</ChatDataProvider>
</WebSearchProvider>
</ElfiProvider>
</SettingsDataProvider>
</AuthProvider>
</ConfirmDialogProvider>
</ThemeProvider>
</LocaleProvider>
);
}
Zustand Store
高頻度で更新されるチャット状態には Zustand を使用し、Context による不要な再レンダリングを回避します。
chatStore
subscribeWithSelector ミドルウェアを使用して正確な購読を行う、チャット状態の中核ストレージです。
// Simplified chatStore state interface
interface ChatState {
// Sessions
sessions: Session[];
sessionsLoading: boolean;
sessionsError: Error | null;
// Assistants
assistants: ChatAssistant[];
assistantsLoading: boolean;
// Providers
providers: LLMProvider[];
providersLoading: boolean;
// Message cache (indexed by sessionId)
messageCache: Map<string, Message[]>;
// Streaming state
streamingState: StreamingState;
// Branch info
branchInfo: Map<string, BranchInfo>;
// Actions
fetchSessions(): Promise<void>;
fetchAssistants(): Promise<void>;
fetchProviders(): Promise<void>;
loadMessages(sessionId: string): Promise<void>;
setStreamingState(state: Partial<StreamingState>): void;
switchBranch(messageId: string, index: number): void;
}
// Use selector for precise subscription, avoid irrelevant re-renders
const sessions = useChatStore(state => state.sessions);
const isStreaming = useChatStore(state => state.streamingState.isStreaming);
| Store | ファイル | 責務 |
|---|---|---|
chatStore | shared/state/chatStore.ts | セッション、メッセージ、ストリーミング状態、ブランチ |
settingsStore | shared/state/settingsStore.ts | 設定状態 |
useChatStoreSync | shared/state/useChatStoreSync.ts | ChatDataContext → chatStore の同期 |
React Context の詳細
23 個以上の Context があり、それぞれが独立した責務を持ちます。
| Context | ファイル | 責務 | 中核状態/メソッド |
|---|---|---|---|
LocaleContext | shared/state/LocaleContext.tsx | i18n (en/zh/ja) | useTranslation(), setLocale() |
themeStore | shared/state/themeStore.ts (host app/ThemeHost.tsx) | テーマモード、壁紙、カスタム CSS | mode, resolvedMode, userTheme, customCss |
ConfirmDialogHost | shared/state/ConfirmDialogHost.tsx | グローバル確認ダイアログ | confirm(options) |
authStore | shared/state/authStore.ts | 認証状態 | user, isAuthenticated, login(), logout() |
SettingsDataHost | app/SettingsDataHost.tsx | 設定の読み書き | settings, updateSetting() |
ElfiContext | features/elfi/state/ElfiContext.tsx | Elfi スマートアシスタント | isOpen, messages, sendMessage() |
webSearchStore | shared/state/webSearchStore.ts (host app/WebSearchHost.tsx) | Web 検索 | search(), results |
chatStore | shared/state/chatStore.ts | チャットデータキャッシュ + 統合チャット API (旧 ChatDataContext / UnifiedChatContext はここへ移行) | sessions, messages, sendMessage(), regenerate(), editMessage() |
htmlPreviewStore | shared/state/htmlPreviewStore.ts (host app/HtmlPreviewHost.tsx) | HTML コンテンツプレビュー | showPreview(), previewContent |
devPreviewStore | shared/state/devPreviewStore.ts (host app/DevPreviewHost.tsx) | Dev モードプレビュー | isDevMode, devData |
ChatTabsContext | features/chat/state/ChatTabsContext.tsx | 複数タブのチャット管理 | tabs, activeTabId, createTab(), closeTab() |
AgentContext | features/agents/state/AgentContext.tsx | Agent 状態 | agents, selectedAgent |
subagentStore | shared/state/subagentStore.ts (host shared/state/SubagentHost.tsx) | Subagent 状態 | subagents, status |
MessageSelectionContext | features/chat/state/MessageSelectionContext.tsx | メッセージの複数選択 | selectedIds, toggleSelect() |
sessionOrganizerStore | shared/state/sessionOrganizerStore.ts (host shared/state/sessionOrganizerStore/SessionOrganizerHost.tsx) | セッションの整理/分類 | folders, moveSession() |
worksStore | shared/state/worksStore.ts | Works 管理 | works, createWork() |
musicWorksStore | shared/state/musicWorksStore.ts | 音楽作品 | tracks, playTrack() |
musicTemplateStore | shared/state/musicTemplateStore.ts | 音楽テンプレート | templates |
VideoWorksContext | features/media/state/VideoWorksContext.tsx | 動画作品 | videos |
videoTemplateStore | shared/state/videoTemplateStore.ts | 動画テンプレート | templates |
TemplateContext | features/marketplace/state/TemplateContext.tsx | 画像テンプレート | templates |
useWorldInfoHighlight | shared/hooks/characters/useWorldInfoHighlight.ts | WI キーワードハイライト | highlightedKeywords |
カスタム Hooks
機能ドメインごとに整理された 60 個以上のカスタム Hooks があります。
Chat 関連
| Hook | ファイル | 目的 |
|---|---|---|
useAppState | shared/hooks/useAppState.ts | ビューフラグ、アクティブタブ、ユーザー設定を一元管理 |
useRouteSync | shared/hooks/useRouteSync.ts | URL パスをビュー状態に同期 |
useSessionProtection | features/chat/hooks/useSessionProtection.ts | アクティブセッションの保護 (更新によるクリアを防止) |
useDraft | features/chat/hooks/useDraft.ts | 入力ドラフトの保存/復元 |
useAutoUpdate | shared/hooks/useAutoUpdate.ts | アプリの自動更新 |
useAudioRecorder | features/media/hooks/useAudioRecorder.ts | 音声録音 |
Feature Hook ディレクトリ
| ディレクトリ | 目的 | 代表的な Hooks |
|---|---|---|
features/chat/hooks/ | チャット操作 | useMessageStream, useBranchNavigation |
features/marketplace/hooks/channel/ | Channel プラグイン | useChannelPlugins |
shared/hooks/characters/character/ | Character カード | useCharacterCards |
shared/hooks/characters/worldinfo/ | World info | useWorldInfo |
shared/hooks/characters/ | WI ハイライト / タグ | useWorldInfoHighlight, useTagsData |
features/characters/hooks/sprites/ | 表情 sprites | useSprites |
features/settings/components/tabs/tools-tab/hooks/ | MCP サーバー | useMcpManagement |
features/kb/hooks/ | Notes システム | useNotesData |
features/todo/hooks/ | Todos | useTodoData |
features/tasks/hooks/ | タスク管理 | useTasksData |
features/cron/hooks/ | 予約タスク | useCronJobs |
features/marketplace/hooks/skills/ | Skill 管理 | useSkillHub |
コンポーネントアーキテクチャ
レイアウト構造
graph TB
Root["div.fixed.inset-0.flex.bg-background"]
WS[WorkspaceShell]
WR[WorkspaceRail — left navigation rail]
TB[TitleBar — tab bar + window control]
MC[Main Content — routed content area]
Root --> WS
WS --> WR
WS --> TB
WS --> MC
MC --> ChatPage
MC --> AgentPage
MC --> SettingsPage
MC --> RoleplayPage
MC --> NotesPage
MC --> CoworkPage
- WorkspaceRail: 左側の固定ナビゲーションレール。Home、Agents、Settings のエントリポイントを含みます
- WorkspaceShell: メインレイアウトコンテナ。Rail + タブバー + コンテンツ領域を管理します
- TitleBar: 複数タブバー + ウィンドウ制御ボタン (最大化/最小化/閉じる)
- Main Content: ルートごとにページコンポーネントを動的にレンダリングします
Workspace 拡張 (Workspace Registry)
Chat/Agent ページ間の「workspace panel」(エディタ / git diff / HTML プレビュー / canvas / design studio の領域で、ファイルツリーの右側) は WorkspaceDefinition registry によって拡張されます。ホストシェルである WorkspaceArea.tsx はレイアウト + registry dispatch のみを担当し、activeView === 'X' のようなビジネス分岐は持ちません。
graph TB
WA["WorkspaceArea<br/>(layout + dispatch)"]
Reg["registry.ts<br/>WORKSPACE_REGISTRY[]"]
Slot["WorkspaceToolbarProvider<br/>(slot context)"]
Aff["host-affordances<br/>(cross-view coordination)"]
Defs["definitions/"]
WA -->|findActiveWorkspace| Reg
WA -->|wraps| Slot
WA -->|consumes| Aff
Reg -->|imports| Defs
Defs --> EW[EditorWorkspace]
Defs --> GW[GitWorkspace]
Defs --> HW[HtmlPreviewWorkspace]
Defs --> AW[A2UIWorkspace]
Defs --> DW[DevPreviewWorkspace]
Defs --> DS[DesignStudioWorkspace]
Defs --> CW[CanvasWorkspace]
各 WorkspaceDefinition は自身で次を管理します。
- 独自の
view識別子 +isAvailable(ctx)predicate - 独自のビューモード状態 (split/code/preview などの内部
useState。ホストには漏れません) useWorkspaceToolbar(config)で toolbar config を slot に公開します。ホストの<WorkspaceToolbar>はuseWorkspaceToolbarConfig()で読み取ります- 任意の
onClose(ctx, cb)hook (閉じるボタンでトリガーされ、React tree の外で実行されるため、close-fn チャネルはWorkspaceCallbacksを使用します)
新しい workspace type の追加 = 2 ファイルを変更: registry.ts 配列にエントリを追加し、definitions/<Name>Workspace.tsx を作成します。WorkspaceArea.tsx の変更は不要です。詳細な手順は packages/renderer/CLAUDE.md の「🧩 Add New Workspace Type」セクション、contract spec は openspec/specs/workspace-registry/spec.md にあります。
ビュー間の連携 (例: エディタ内の「Preview」ボタンで htmlPreview view に移動する、HTML プレビューの最大化で workspace pane 全体を隠す) は host-affordances.ts に集約されています。ここだけが activeView === 'X' の判定を許可される場所です。
| ファイル | 責務 |
|---|---|
features/chat/components/content/WorkspaceArea.tsx | ホストシェル (レイアウト + slot reader + registry dispatch、ビジネス分岐なし) |
features/chat/components/content/workspaces/types.ts | WorkspaceDefinition / WorkspaceHostContext / WorkspaceCallbacks contract |
features/chat/components/content/workspaces/registry.ts | WORKSPACE_REGISTRY の単一登録ポイント + findActiveWorkspace query |
features/chat/components/content/workspaces/host-affordances.ts | ビュー間連携 (cross-view toggle + maximize hide) |
features/chat/components/content/workspaces/workspace-toolbar-slot{,-internals,-hooks}.{tsx,ts} | Toolbar slot trio (Provider / contexts / hooks) |
features/chat/components/content/workspaces/definitions/<Name>Workspace.tsx | 7 つの workspace 定義 (editor / git / htmlPreview / a2ui / devPreview / designStudio / canvas) |
features/chat/components/content/workspaces/inline-file-viewers.tsx | InlineMarkdownViewer + InlineBinaryViewer (image/audio/video/pdf + 「プレビュー不可」fallback) |
features/chat/components/content/workspaces/inline-file-viewers-types.ts | getBinaryFileType / isMarkdownFile / isNonTextFile file type detection |
features/chat/components/content/unified-page/hooks/useWorkspaceState.ts | Workspace state hook (.html → openHtmlPreview() routing + handleFileSelect での binary file routing) |
ルーティングシステム
HashRouter を使用します (Electron は BrowserRouter をサポートしていません)。useRouteSync Hook で URL をビュー状態に同期します。
// Sync route to view state
function useRouteSync(pathname: string, state: AppState) {
useEffect(() => {
if (pathname.startsWith('/chat')) {
state.setIsChatView(true);
state.setIsHomeView(false);
} else if (pathname.startsWith('/agents')) {
state.setIsAgentView(true);
} else if (pathname.startsWith('/settings')) {
state.setIsSettingsView(true);
}
}, [pathname]);
}
i18n 国際化
useTranslation() Hook を使用して 3 言語をサポートします。
| 言語 | ディレクトリ | ファイル |
|---|---|---|
| 英語 | locales/en/ | 15+ JSON files |
| 中国語 (簡体字) | locales/zh/ | 15+ JSON files |
| 日本語 | locales/ja/ | 15+ JSON files |
翻訳ファイルは機能ドメインごとに分割されています: common.json, chat.json, settings.json, characters.json, elfi.json など。
AppState Hook
useAppState はグローバル UI 状態を一元管理します。
// Simplified AppState interface
interface AppState {
// View flags
isHomeView: boolean;
isChatView: boolean;
isAgentView: boolean;
isSettingsView: boolean;
// Active session
selectedSession: string | null;
activeSessions: Set<string>;
processingSessions: Set<string>;
// Tabs
activeTab: string;
settingsInitialTab: string;
// User preferences
autoExpandTools: boolean;
showRawParameters: boolean;
showThinking: boolean;
autoScrollToBottom: boolean;
sendByCtrlEnter: boolean;
// Modals
showVersionModal: boolean;
}
ビルド設定
| Config | Value |
|---|---|
| Build tool | Vite 7.0 |
| Styling | Tailwind CSS 3.4 + CSS variables (semantic tokens) |
| Code splitting | Route-level lazy loading (lazy/ directory, 6 files + 1 barrel by purpose) |
| Tree shaking | Named imports |
| Main bundle limit | 500 KB |
関連ファイル
| ファイル | 説明 |
|---|---|
packages/renderer/src/app/App.tsx | App エントリ、Provider 階層とレイアウト |
packages/renderer/src/shared/state/chatStore.ts | Zustand チャット状態 Store |
packages/renderer/src/shared/state/settingsStore.ts | Zustand 設定状態 Store |
packages/renderer/src/shared/state/chatStore.ts | 統合チャット API (旧 UnifiedChatContext は chatStore に移行済み) |
packages/renderer/src/shared/state/themeStore.ts | Theme Store (旧 ThemeContext) |
packages/renderer/src/shared/hooks/useAppState.ts | UI 状態の一元管理 |
packages/renderer/src/shared/hooks/useRouteSync.ts | ルート同期 |
packages/renderer/src/app/layout/WorkspaceShell.tsx | メインレイアウトコンテナ |
packages/renderer/src/app/layout/WorkspaceRail.tsx | 左側ナビゲーションレール |
packages/renderer/src/app/title-bar/ | タブバーコンポーネント |
packages/renderer/src/app/lazy/ | Lazy load component directory (pages.tsx / workspaces.tsx / inline.tsx / shell.tsx / skeletons.tsx / with-suspense.tsx, barrel in lazy/index.ts) |
packages/renderer/src/features/chat/components/content/WorkspaceArea.tsx | 中央 workspace ホストシェル (レイアウト + registry dispatch、ビジネス分岐なし) |
packages/renderer/src/features/chat/components/content/workspaces/ | Workspace registry (WorkspaceDefinition contract + 7 definition files + toolbar slot + host-affordances) |
packages/renderer/src/app/railItems.ts | ナビゲーションレール設定 |
packages/renderer/src/app/mainContentRouter.tsx | メインコンテンツルーター解析 |
packages/renderer/CLAUDE.md | フロントエンド UI 開発標準 |