@zq-silk/yui 0.2.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +603 -133
- package/README.md +806 -31
- package/dist/agent/agent.js +2 -1
- package/dist/agent/argumentPolicy.js +3 -1
- package/dist/agent/launchEnvironment.js +106 -0
- package/dist/agent/managedRuntimeEnvironment.js +34 -0
- package/dist/brief/taskBrief.js +11 -1
- package/dist/cli/agentConfigurationPicker.js +287 -0
- package/dist/cli/commandCatalog.js +488 -60
- package/dist/cli/completion.js +146 -22
- package/dist/cli/helpRenderer.js +3 -1
- package/dist/cli/interactionCandidates.js +53 -15
- package/dist/cli/interactionPolicy.js +267 -30
- package/dist/cli/interactiveSelection.js +6 -2
- package/dist/cli/invocationRouter.js +5 -1
- package/dist/cli/operatorWizard.js +87 -0
- package/dist/cli/roleOptionCatalog.js +1 -0
- package/dist/cli/roleWizard.js +185 -21
- package/dist/cli/updateCommand.js +62 -19
- package/dist/cli/updateOrchestrator.js +539 -0
- package/dist/cli/updatePorts.js +1119 -0
- package/dist/cli/upgradeCommand.js +112 -0
- package/dist/cli.js +1420 -86
- package/dist/commands/agentCommands.js +146 -3
- package/dist/commands/configCommands.js +126 -0
- package/dist/commands/controllerCommands.js +365 -0
- package/dist/commands/globalRoleCommands.js +168 -126
- package/dist/commands/jobCommands.js +18 -8
- package/dist/commands/operatorCommands.js +159 -9
- package/dist/commands/profileCommands.js +203 -0
- package/dist/commands/projectCommands.js +650 -0
- package/dist/commands/roleConfiguration.js +85 -24
- package/dist/commands/roleRuntimeGuard.js +12 -0
- package/dist/commands/roleSkillValidation.js +47 -0
- package/dist/commands/taskActor.js +127 -0
- package/dist/commands/taskCommands.js +4201 -313
- package/dist/commands/taskCompletionGate.js +131 -0
- package/dist/commands/taskContextCommand.js +244 -30
- package/dist/commands/taskInputCommands.js +177 -59
- package/dist/commands/taskIntegrationCommands.js +303 -0
- package/dist/commands/taskOverviewCommand.js +363 -0
- package/dist/commands/taskRoleRuntimeStatus.js +125 -19
- package/dist/commands/textInput.js +15 -0
- package/dist/completion/completionInstaller.js +26 -22
- package/dist/config/yuiConfig.js +4 -3
- package/dist/context/dispatchContext.js +90 -38
- package/dist/context/roleSessionContext.js +119 -0
- package/dist/controller/claudeLifecycleHook.js +203 -0
- package/dist/controller/clientRuntime.js +408 -56
- package/dist/controller/codexLifecycleHook.js +108 -0
- package/dist/controller/controller.js +1089 -32
- package/dist/controller/domainIdentity.js +505 -0
- package/dist/controller/ephemeralResourceReaper.js +131 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
- package/dist/controller/providerHookRunFence.js +127 -0
- package/dist/controller/resourceCleanupLinux.js +286 -0
- package/dist/controller/resourceInventory.js +531 -0
- package/dist/controller/resourceInventoryLinux.js +610 -0
- package/dist/controller/runtime.js +629 -10
- package/dist/controller/runtimeEventInbox.js +564 -0
- package/dist/controller/runtimeEventProcessor.js +248 -0
- package/dist/controller/runtimeLaunchCoordinator.js +477 -0
- package/dist/controller/sessionNotify.js +121 -78
- package/dist/coordination/deadlineScheduler.js +15 -0
- package/dist/coordination/mailboxScheduler.js +108 -0
- package/dist/coordination/workMailbox.js +329 -0
- package/dist/coordination/workMailboxQueue.js +86 -0
- package/dist/core/controllerClient.js +19 -5
- package/dist/core/controllerEndpoint.js +37 -0
- package/dist/core/controllerServer.js +218 -10
- package/dist/core/protocol.js +6 -2
- package/dist/decision/decision.js +2 -1
- package/dist/doctor/doctor.js +681 -32
- package/dist/domain/validation.js +53 -0
- package/dist/errors/cliError.js +5 -3
- package/dist/event/taskEvent.js +7 -3
- package/dist/execution/codexThreadNaming.js +160 -0
- package/dist/execution/executionGroup.js +579 -0
- package/dist/executor/agentAdapter.js +255 -40
- package/dist/executor/agentConfigurationCatalog.js +326 -0
- package/dist/executor/agentConfigurationProbe.js +506 -0
- package/dist/executor/agentExecutor.js +625 -10
- package/dist/executor/codexConfigConflict.js +290 -0
- package/dist/executor/effectiveLaunch.js +340 -0
- package/dist/executor/executorRegistry.js +238 -36
- package/dist/executor/fileRoleLaunchPlanner.js +550 -40
- package/dist/executor/turnCompletion.js +126 -0
- package/dist/input/inputRequest.js +30 -9
- package/dist/integration/changeSet.js +36 -0
- package/dist/integration/checkResult.js +24 -0
- package/dist/integration/gitIntegrationService.js +695 -0
- package/dist/integration/integrationAttempt.js +142 -0
- package/dist/interaction/operatorPresentation.js +96 -0
- package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
- package/dist/lifecycle/exactRunTerminalization.js +572 -0
- package/dist/lifecycle/providerLifecycleMapping.js +190 -0
- package/dist/lifecycle/taskRoleSessionReset.js +124 -0
- package/dist/message/message.js +23 -7
- package/dist/milestone/milestone.js +2 -1
- package/dist/operator/operatorSessionHistory.js +124 -0
- package/dist/output/agentConfigurationPresentation.js +43 -0
- package/dist/output/rolePresentation.js +34 -10
- package/dist/output/terminal.js +8 -0
- package/dist/output/timePresentation.js +55 -0
- package/dist/profile/agentProfile.js +128 -0
- package/dist/repository/gitWorkspace.js +578 -24
- package/dist/repository/project.js +213 -0
- package/dist/repository/taskWorkspaceCoordinator.js +392 -0
- package/dist/repository/taskWorkspacePreparer.js +1688 -191
- package/dist/review/reviewConfig.js +11 -0
- package/dist/review/reviewRound.js +399 -0
- package/dist/review/taskFinalReviewContract.js +90 -0
- package/dist/role/role.js +124 -23
- package/dist/run/agentRun.js +155 -12
- package/dist/run/runIdentity.js +82 -0
- package/dist/runtime/exactControlPlane.js +472 -0
- package/dist/runtime/index.js +8 -0
- package/dist/runtime/lifecycleReservation.js +38 -0
- package/dist/runtime/ports.js +11 -0
- package/dist/runtime/preallocatedNativeSession.js +13 -0
- package/dist/runtime/promptEnvelope.js +30 -0
- package/dist/runtime/runtimeBinding.js +31 -0
- package/dist/runtime/runtimeOwner.js +14 -0
- package/dist/runtime/sessionLaunchRequest.js +62 -0
- package/dist/runtime/sessionTitle.js +54 -0
- package/dist/runtime/taskRuntimeIsolation.js +643 -0
- package/dist/runtime/tmuxAdapters.js +315 -0
- package/dist/runtime/turnCompletion.js +3 -0
- package/dist/runtime/validation.js +23 -0
- package/dist/scheduler/activeRoleRunDelivery.js +342 -32
- package/dist/scheduler/activeTaskProgress.js +63 -0
- package/dist/scheduler/leaderFailure.js +2 -1
- package/dist/scheduler/leaderWakeupProcessor.js +307 -66
- package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
- package/dist/scheduler/operatorNotification.js +44 -2
- package/dist/scheduler/ports.js +28 -1
- package/dist/scheduler/roleRunLiveness.js +131 -25
- package/dist/scheduler/roleRunStall.js +951 -0
- package/dist/scheduler/taskExecutionProjection.js +544 -0
- package/dist/scheduler/wakeupQueue.js +3 -0
- package/dist/setup/setupCommand.js +302 -52
- package/dist/storage/compatibleTaskStore.js +102 -0
- package/dist/storage/migration/baseline.js +78 -0
- package/dist/storage/migration/classifier.js +51 -0
- package/dist/storage/migration/compatibleCodec.js +53 -0
- package/dist/storage/migration/engine.js +147 -0
- package/dist/storage/migration/index.js +33 -0
- package/dist/storage/migration/planner.js +154 -0
- package/dist/storage/migration/productionRegistry.js +486 -0
- package/dist/storage/migration/registry.js +169 -0
- package/dist/storage/migration/report.js +54 -0
- package/dist/storage/migration/types.js +31 -0
- package/dist/storage/storageSchema.js +147 -123
- package/dist/storage/storageVersions.js +11 -0
- package/dist/storage/taskStore.js +1793 -197
- package/dist/storage/upgrade/homeClassification.js +156 -0
- package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
- package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
- package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
- package/dist/storage/upgrade/recordVersionScan.js +176 -0
- package/dist/storage/upgrade/recordVersions.js +159 -0
- package/dist/storage/upgrade/switchProgress.js +80 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
- package/dist/storage/upgrade/upgradeReceipt.js +161 -0
- package/dist/storage/upgradeCoordination.js +186 -0
- package/dist/storage/upgradeFence.js +366 -0
- package/dist/task/task.js +132 -26
- package/dist/task/taskRecordReference.js +66 -0
- package/dist/tmux/commandExecutor.js +75 -2
- package/dist/tmux/tmuxManager.js +747 -49
- package/dist/version.js +23 -0
- package/dist/web/assets/assetManifest.js +62 -0
- package/dist/web/assets/client/app.js +631 -0
- package/dist/web/assets/client/components.js +605 -0
- package/dist/web/assets/client/dom.js +14 -0
- package/dist/web/assets/client/format.js +28 -0
- package/dist/web/assets/client/i18n.js +494 -0
- package/dist/web/assets/client/markdown.js +114 -0
- package/dist/web/assets/client/theme.js +32 -0
- package/dist/web/assets/client/view.js +458 -0
- package/dist/web/assets/fontData.js +12 -0
- package/dist/web/assets/fonts.js +12 -0
- package/dist/web/assets/shell.js +114 -0
- package/dist/web/assets/styles/cards.js +135 -0
- package/dist/web/assets/styles/layout.js +47 -0
- package/dist/web/assets/styles/markdown.js +29 -0
- package/dist/web/assets/styles/responsive.js +39 -0
- package/dist/web/assets/styles/tokens.js +101 -0
- package/dist/web/assets/styles/widgets.js +147 -0
- package/dist/web/tmuxWebTerminal.js +158 -0
- package/dist/web/webServer.js +463 -0
- package/dist/web/webSnapshot.js +148 -0
- package/dist/workItem/workItem.js +642 -23
- package/dist/workspace/gitChangeSetCapture.js +86 -0
- package/dist/workspace/workItemChangeSetManager.js +445 -0
- package/dist/worktree/managedWorkspace.js +202 -0
- package/docs/task-local-identity.md +62 -0
- package/i18n/README.zh-CN.md +406 -31
- package/package.json +10 -2
- package/skills/yui-leader/SKILL.md +601 -39
- package/skills/yui-operator/SKILL.md +255 -34
- package/skills/yui-reviewer/SKILL.md +57 -0
- package/skills/yui-worker/SKILL.md +214 -17
- package/dist/commands/repositoryCommands.js +0 -86
- package/dist/operator/operatorContext.js +0 -66
- package/dist/repository/repository.js +0 -55
- package/dist/scheduler/archivedTaskRuntime.js +0 -12
- package/dist/worktree/roleWorkspace.js +0 -62
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
export const I18N_SCRIPT = `
|
|
2
|
+
const messages = {
|
|
3
|
+
"en": {
|
|
4
|
+
"a11y.skip": "Skip to the main panel",
|
|
5
|
+
"actions.answer": "Answer",
|
|
6
|
+
"actions.back": "Back to task list",
|
|
7
|
+
"actions.openRole": "Open session",
|
|
8
|
+
"theme.controlRoom": "Control room",
|
|
9
|
+
"theme.paper": "Paper ledger",
|
|
10
|
+
"page.eyebrow": "SYSTEM OVERVIEW",
|
|
11
|
+
"page.title": "Control room",
|
|
12
|
+
"page.lede": "A quiet view into every task, role, and unresolved decision.",
|
|
13
|
+
"overview.title": "Everything at a glance",
|
|
14
|
+
"overview.lede": "Tracking {total} tasks — {active} active, {inputs} awaiting input, {completed} completed.",
|
|
15
|
+
"overview.distribution": "Status distribution",
|
|
16
|
+
"overview.attention": "Needs attention",
|
|
17
|
+
"overview.attentionEmpty": "No open inputs or stalled Runs.",
|
|
18
|
+
"overview.activeNow": "Active now",
|
|
19
|
+
"overview.activeEmpty": "No tasks are currently active.",
|
|
20
|
+
"sync.label": "LAST SYNC",
|
|
21
|
+
"actions.openRun": "Open run",
|
|
22
|
+
"actions.operator": "Operator session",
|
|
23
|
+
"actions.refresh": "Refresh",
|
|
24
|
+
"actions.showLess": "Show less",
|
|
25
|
+
"actions.showMore": "Show more",
|
|
26
|
+
"actions.showRemaining": "Show {count} more",
|
|
27
|
+
"app.title": "Yui Control Room",
|
|
28
|
+
"author.leader": "Leader",
|
|
29
|
+
"author.operator": "Operator",
|
|
30
|
+
"author.system": "System",
|
|
31
|
+
"author.user": "User",
|
|
32
|
+
"board.eyebrow": "TASK INDEX",
|
|
33
|
+
"board.title": "Current work",
|
|
34
|
+
"brand.connection": "Local loopback",
|
|
35
|
+
"brand.subtitle": "local control plane",
|
|
36
|
+
"breadcrumb.taskList": "All work",
|
|
37
|
+
"controls.language": "Language",
|
|
38
|
+
"controls.theme": "Theme",
|
|
39
|
+
"decision.active": "Active",
|
|
40
|
+
"decision.superseded": "Superseded",
|
|
41
|
+
"delivery.delivered": "Delivered",
|
|
42
|
+
"delivery.pushed": "Pushed · awaiting provider acceptance",
|
|
43
|
+
"delivery.pending": "Awaiting delivery",
|
|
44
|
+
"detail.acceptance": "Acceptance criteria",
|
|
45
|
+
"detail.agent": "Agent",
|
|
46
|
+
"detail.agents": "Agents",
|
|
47
|
+
"detail.archived": "Task archived",
|
|
48
|
+
"detail.archivedBy": "Archived by",
|
|
49
|
+
"detail.assignee": "Assignee",
|
|
50
|
+
"detail.attention": "Needs your input",
|
|
51
|
+
"detail.baseRef": "Base ref",
|
|
52
|
+
"detail.checks": "Checks",
|
|
53
|
+
"detail.completedBy": "Completed by",
|
|
54
|
+
"detail.conclusion": "Task conclusion",
|
|
55
|
+
"detail.constraints": "Constraints",
|
|
56
|
+
"detail.decisions": "Decisions",
|
|
57
|
+
"detail.dependsOn": "Depends on",
|
|
58
|
+
"detail.desired": "Desired launch",
|
|
59
|
+
"detail.desiredAgent": "Desired Agent",
|
|
60
|
+
"detail.due": "Due",
|
|
61
|
+
"detail.effective": "Effective launch",
|
|
62
|
+
"detail.effectiveAgent": "Effective Agent",
|
|
63
|
+
"detail.evidence": "Diagnostic evidence",
|
|
64
|
+
"detail.execution": "Runs",
|
|
65
|
+
"detail.expectedOutput": "Expected output",
|
|
66
|
+
"detail.focus": "Current focus",
|
|
67
|
+
"detail.runtimeHealth": "Runtime health",
|
|
68
|
+
"detail.lastProgress": "Last semantic progress",
|
|
69
|
+
"input.freeText": "Type your answer",
|
|
70
|
+
"input.answered": "Input answered.",
|
|
71
|
+
"input.new": "A new input needs your attention.",
|
|
72
|
+
"errors.answer": "Unable to answer this input.",
|
|
73
|
+
"terminal.connecting": "Connecting",
|
|
74
|
+
"terminal.writable": "Interactive",
|
|
75
|
+
"terminal.readOnly": "Read only",
|
|
76
|
+
"terminal.closed": "Detached",
|
|
77
|
+
"terminal.close": "Close terminal",
|
|
78
|
+
"terminal.error": "Unable to open the native session.",
|
|
79
|
+
"terminal.historyLimited": "This existing Role has a {current}-line tmux history. Exit and re-enter it to use {target} lines.",
|
|
80
|
+
"detail.historyEmpty": "No milestones or decisions yet.",
|
|
81
|
+
"detail.instruction": "Instruction",
|
|
82
|
+
"detail.leaderSummary": "Leader summary",
|
|
83
|
+
"detail.messages": "Messages",
|
|
84
|
+
"detail.milestones": "Milestones",
|
|
85
|
+
"detail.objective": "Objective",
|
|
86
|
+
"detail.openInput": "Open input",
|
|
87
|
+
"detail.outcome": "Outcome",
|
|
88
|
+
"detail.permission": "Permission",
|
|
89
|
+
"detail.profileIntent": "Profile intent",
|
|
90
|
+
"detail.project": "Project",
|
|
91
|
+
"detail.replacement": "Replacement Task",
|
|
92
|
+
"detail.responsibilities": "Responsibilities",
|
|
93
|
+
"detail.retired": "Task retired",
|
|
94
|
+
"detail.retiredBy": "Retired by",
|
|
95
|
+
"detail.reviewBase": "Review base",
|
|
96
|
+
"detail.reviews": "Reviews",
|
|
97
|
+
"detail.roles": "Roles",
|
|
98
|
+
"detail.skills": "Skills",
|
|
99
|
+
"detail.technicalApproach": "Technical approach",
|
|
100
|
+
"detail.updated": "Updated",
|
|
101
|
+
"detail.workItem": "Work item",
|
|
102
|
+
"detail.workItems": "Work items",
|
|
103
|
+
"detail.workspace": "Workspace",
|
|
104
|
+
"detail.writeProjects": "Writable Projects",
|
|
105
|
+
"disposition.abandoned": "Abandoned",
|
|
106
|
+
"disposition.integrated": "Integrated",
|
|
107
|
+
"empty.brief": "No task brief has been recorded.",
|
|
108
|
+
"empty.none": "No items",
|
|
109
|
+
"empty.runs": "No runs yet.",
|
|
110
|
+
"empty.tasks": "No tasks match this view.",
|
|
111
|
+
"errors.answer": "Unable to answer this input.",
|
|
112
|
+
"errors.dashboard": "Unable to read the dashboard.",
|
|
113
|
+
"errors.detail": "Unable to read task detail.",
|
|
114
|
+
"filters.label": "Filter by status",
|
|
115
|
+
"group.active": "Active",
|
|
116
|
+
"group.archived": "Archived",
|
|
117
|
+
"group.attention": "Needs input",
|
|
118
|
+
"group.draft": "Draft",
|
|
119
|
+
"group.finished": "Completed",
|
|
120
|
+
"input.answered": "Input answered.",
|
|
121
|
+
"input.blocking": "Blocking",
|
|
122
|
+
"input.freeText": "Type your answer",
|
|
123
|
+
"input.new": "A new input needs your attention.",
|
|
124
|
+
"input.recommended": "Recommended",
|
|
125
|
+
"input.required": "Required",
|
|
126
|
+
"input.timeoutAt": "Auto-resolves",
|
|
127
|
+
"launch.current": "Current desired launch",
|
|
128
|
+
"launch.drift": "Desired change pending next launch",
|
|
129
|
+
"loading.dashboard": "Reading local state…",
|
|
130
|
+
"loading.detail": "Reading task detail…",
|
|
131
|
+
"messageKind.operator": "Operator",
|
|
132
|
+
"messageKind.role-result": "Role result",
|
|
133
|
+
"messageKind.system": "System",
|
|
134
|
+
"messageKind.user": "User",
|
|
135
|
+
"metrics.active": "In progress",
|
|
136
|
+
"metrics.completed": "Completed",
|
|
137
|
+
"metrics.inputs": "Waiting on you",
|
|
138
|
+
"metrics.label": "Task summary",
|
|
139
|
+
"metrics.total": "Total tasks",
|
|
140
|
+
"milestone.recorded": "Recorded",
|
|
141
|
+
"history.milestone": "Milestone",
|
|
142
|
+
"history.decision": "Decision",
|
|
143
|
+
"mode.new": "New session",
|
|
144
|
+
"mode.resume": "Resumed session",
|
|
145
|
+
"overview.activeEmpty": "No tasks are currently active.",
|
|
146
|
+
"overview.activeNow": "Active now",
|
|
147
|
+
"overview.recent": "Recently updated",
|
|
148
|
+
"overview.attention": "Needs attention",
|
|
149
|
+
"overview.attentionEmpty": "No open inputs. Nothing is waiting on you.",
|
|
150
|
+
"overview.distribution": "Status distribution",
|
|
151
|
+
"overview.inbox": "Needs your input",
|
|
152
|
+
"overview.inboxEmpty": "Nothing is waiting on you.",
|
|
153
|
+
"overview.lede": "Tracking {total} tasks — {active} active, {inputs} awaiting input, {completed} completed.",
|
|
154
|
+
"overview.title": "Everything at a glance",
|
|
155
|
+
"page.eyebrow": "SYSTEM OVERVIEW",
|
|
156
|
+
"page.lede": "A quiet view into every task, role, and unresolved decision.",
|
|
157
|
+
"page.title": "Overview",
|
|
158
|
+
"priority.high": "High priority",
|
|
159
|
+
"priority.low": "Low priority",
|
|
160
|
+
"priority.medium": "Medium priority",
|
|
161
|
+
"priority.urgent": "Urgent",
|
|
162
|
+
"review.completed": "Completed",
|
|
163
|
+
"review.failed": "Failed",
|
|
164
|
+
"review.pending": "Pending",
|
|
165
|
+
"review.running": "Running",
|
|
166
|
+
"role.detached": "Detached",
|
|
167
|
+
"role.exited": "Exited",
|
|
168
|
+
"role.failed": "Failed",
|
|
169
|
+
"role.idle": "Idle",
|
|
170
|
+
"role.running": "Running",
|
|
171
|
+
"run.active": "Active",
|
|
172
|
+
"run.failed": "Failed",
|
|
173
|
+
"run.yielded": "Yielded",
|
|
174
|
+
"search.label": "Search tasks",
|
|
175
|
+
"search.placeholder": "Filter by title, ID, tag, or project…",
|
|
176
|
+
"stats.inputs": "inputs",
|
|
177
|
+
"stats.running": "running",
|
|
178
|
+
"status.active": "Active",
|
|
179
|
+
"status.all": "All",
|
|
180
|
+
"status.archived": "Archived",
|
|
181
|
+
"status.completed": "Completed",
|
|
182
|
+
"status.draft": "Draft",
|
|
183
|
+
"status.retired": "Retired",
|
|
184
|
+
"sync.label": "Last sync",
|
|
185
|
+
"tabs.exec": "Runs",
|
|
186
|
+
"tabs.focus": "Focus",
|
|
187
|
+
"tabs.history": "History",
|
|
188
|
+
"tabs.label": "Task sections",
|
|
189
|
+
"tabs.messages": "Messages",
|
|
190
|
+
"tabs.reviews": "Reviews",
|
|
191
|
+
"tabs.roles": "Roles",
|
|
192
|
+
"tabs.summary": "Summary",
|
|
193
|
+
"tabs.work": "Work items",
|
|
194
|
+
"terminal.attachNote": "This panel stays attached until you close it.",
|
|
195
|
+
"terminal.attachTitle": "Open the full Operator in a terminal",
|
|
196
|
+
"terminal.close": "Close terminal",
|
|
197
|
+
"terminal.closed": "Detached",
|
|
198
|
+
"terminal.connecting": "Connecting",
|
|
199
|
+
"terminal.error": "Unable to open the native session.",
|
|
200
|
+
"terminal.historyLimited": "This existing Role has a {current}-line tmux history. Exit and re-enter it to use {target} lines.",
|
|
201
|
+
"terminal.readOnly": "Read only",
|
|
202
|
+
"terminal.writable": "Interactive",
|
|
203
|
+
"theme.atlas": "Atlas",
|
|
204
|
+
"theme.controlRoom": "Control room",
|
|
205
|
+
"theme.paper": "Paper ledger",
|
|
206
|
+
"time.justNow": "just now",
|
|
207
|
+
"work.awaiting_acceptance": "Awaiting acceptance",
|
|
208
|
+
"work.cancelled": "Cancelled",
|
|
209
|
+
"work.completed": "Completed",
|
|
210
|
+
"work.failed": "Failed",
|
|
211
|
+
"work.pending": "Pending",
|
|
212
|
+
"work.retired": "Retired",
|
|
213
|
+
"work.running": "Running",
|
|
214
|
+
"work.superseded": "Superseded",
|
|
215
|
+
"stats.running": "running",
|
|
216
|
+
"stats.inputs": "inputs",
|
|
217
|
+
"stats.stalledRuns": "stalled Runs"
|
|
218
|
+
},
|
|
219
|
+
"zh-CN": {
|
|
220
|
+
"a11y.skip": "跳到主面板",
|
|
221
|
+
"actions.answer": "提交回答",
|
|
222
|
+
"actions.back": "返回任务列表",
|
|
223
|
+
"actions.openRole": "打开会话",
|
|
224
|
+
"theme.controlRoom": "控制室",
|
|
225
|
+
"theme.paper": "纸本台账",
|
|
226
|
+
"page.eyebrow": "系统概览",
|
|
227
|
+
"page.title": "控制室",
|
|
228
|
+
"page.lede": "安静地查看每个任务、角色与尚未解决的决策。",
|
|
229
|
+
"overview.title": "全局概览",
|
|
230
|
+
"overview.lede": "正在跟踪 {total} 个任务 —— {active} 个进行中,{inputs} 个待处理输入,{completed} 个已完成。",
|
|
231
|
+
"overview.distribution": "状态分布",
|
|
232
|
+
"overview.attention": "需要关注",
|
|
233
|
+
"overview.attentionEmpty": "没有待处理输入或停滞运行,当前无需你介入。",
|
|
234
|
+
"overview.activeNow": "进行中",
|
|
235
|
+
"overview.activeEmpty": "当前没有进行中的任务。",
|
|
236
|
+
"sync.label": "最近同步",
|
|
237
|
+
"actions.openRun": "打开会话",
|
|
238
|
+
"actions.operator": "Operator 会话",
|
|
239
|
+
"actions.refresh": "刷新",
|
|
240
|
+
"actions.showLess": "收起",
|
|
241
|
+
"actions.showMore": "展开更多",
|
|
242
|
+
"actions.showRemaining": "再显示 {count} 条",
|
|
243
|
+
"app.title": "Yui 控制室",
|
|
244
|
+
"author.leader": "负责人",
|
|
245
|
+
"author.operator": "Operator",
|
|
246
|
+
"author.system": "系统",
|
|
247
|
+
"author.user": "用户",
|
|
248
|
+
"board.eyebrow": "任务索引",
|
|
249
|
+
"board.title": "当前工作",
|
|
250
|
+
"brand.connection": "本机回环",
|
|
251
|
+
"brand.subtitle": "本地控制平面",
|
|
252
|
+
"breadcrumb.taskList": "所有工作",
|
|
253
|
+
"controls.language": "语言",
|
|
254
|
+
"controls.theme": "主题",
|
|
255
|
+
"decision.active": "有效",
|
|
256
|
+
"decision.superseded": "已替代",
|
|
257
|
+
"delivery.delivered": "已送达",
|
|
258
|
+
"delivery.pushed": "已推送 · 等待提供方确认",
|
|
259
|
+
"delivery.pending": "等待送达",
|
|
260
|
+
"detail.acceptance": "验收标准",
|
|
261
|
+
"detail.agent": "Agent",
|
|
262
|
+
"detail.agents": "智能体",
|
|
263
|
+
"detail.archived": "任务已归档",
|
|
264
|
+
"detail.archivedBy": "归档人",
|
|
265
|
+
"detail.assignee": "负责人",
|
|
266
|
+
"detail.attention": "需要你的输入",
|
|
267
|
+
"detail.baseRef": "基线引用",
|
|
268
|
+
"detail.checks": "检查",
|
|
269
|
+
"detail.completedBy": "完成人",
|
|
270
|
+
"detail.conclusion": "任务结论",
|
|
271
|
+
"detail.constraints": "约束",
|
|
272
|
+
"detail.decisions": "决策",
|
|
273
|
+
"detail.dependsOn": "依赖",
|
|
274
|
+
"detail.desired": "期望启动配置",
|
|
275
|
+
"detail.desiredAgent": "期望 Agent",
|
|
276
|
+
"detail.due": "截止时间",
|
|
277
|
+
"detail.effective": "实际启动配置",
|
|
278
|
+
"detail.effectiveAgent": "实际 Agent",
|
|
279
|
+
"detail.evidence": "诊断证据",
|
|
280
|
+
"detail.execution": "运行",
|
|
281
|
+
"detail.expectedOutput": "期望产出",
|
|
282
|
+
"detail.focus": "当前重点",
|
|
283
|
+
"detail.runtimeHealth": "运行健康",
|
|
284
|
+
"detail.lastProgress": "最近语义进展",
|
|
285
|
+
"input.freeText": "输入回答",
|
|
286
|
+
"input.answered": "输入已回答。",
|
|
287
|
+
"input.new": "有新的输入请求需要处理。",
|
|
288
|
+
"errors.answer": "无法回答该输入请求。",
|
|
289
|
+
"terminal.connecting": "正在连接",
|
|
290
|
+
"terminal.writable": "可交互",
|
|
291
|
+
"terminal.readOnly": "只读",
|
|
292
|
+
"terminal.closed": "已分离",
|
|
293
|
+
"terminal.close": "关闭终端",
|
|
294
|
+
"terminal.error": "无法打开原生会话。",
|
|
295
|
+
"terminal.historyLimited": "这个已有 Role 的 tmux 历史只有 {current} 行;退出并重新进入后可使用 {target} 行。",
|
|
296
|
+
"detail.historyEmpty": "尚无里程碑或决策记录。",
|
|
297
|
+
"detail.instruction": "执行指令",
|
|
298
|
+
"detail.leaderSummary": "负责人总结",
|
|
299
|
+
"detail.messages": "消息",
|
|
300
|
+
"detail.milestones": "里程碑",
|
|
301
|
+
"detail.objective": "目标",
|
|
302
|
+
"detail.openInput": "待处理输入",
|
|
303
|
+
"detail.outcome": "执行结果",
|
|
304
|
+
"detail.permission": "权限",
|
|
305
|
+
"detail.profileIntent": "Profile 行为意图",
|
|
306
|
+
"detail.project": "项目",
|
|
307
|
+
"detail.replacement": "替代任务",
|
|
308
|
+
"detail.responsibilities": "职责",
|
|
309
|
+
"detail.retired": "任务已退役",
|
|
310
|
+
"detail.retiredBy": "退役人",
|
|
311
|
+
"detail.reviewBase": "评审基线",
|
|
312
|
+
"detail.reviews": "评审记录",
|
|
313
|
+
"detail.roles": "角色",
|
|
314
|
+
"detail.skills": "技能",
|
|
315
|
+
"detail.technicalApproach": "技术方案",
|
|
316
|
+
"detail.updated": "更新时间",
|
|
317
|
+
"detail.workItem": "工作项",
|
|
318
|
+
"detail.workItems": "工作项",
|
|
319
|
+
"detail.workspace": "工作区",
|
|
320
|
+
"detail.writeProjects": "可写项目",
|
|
321
|
+
"disposition.abandoned": "已废弃",
|
|
322
|
+
"disposition.integrated": "已集成",
|
|
323
|
+
"empty.brief": "尚未记录任务简报。",
|
|
324
|
+
"empty.none": "暂无条目",
|
|
325
|
+
"empty.runs": "暂无运行记录。",
|
|
326
|
+
"empty.tasks": "当前视图没有匹配的任务。",
|
|
327
|
+
"errors.answer": "无法回答该输入请求。",
|
|
328
|
+
"errors.dashboard": "无法读取控制面板。",
|
|
329
|
+
"errors.detail": "无法读取任务详情。",
|
|
330
|
+
"filters.label": "按状态筛选",
|
|
331
|
+
"group.active": "进行中",
|
|
332
|
+
"group.archived": "已归档",
|
|
333
|
+
"group.attention": "需输入",
|
|
334
|
+
"group.draft": "草稿",
|
|
335
|
+
"group.finished": "已完成",
|
|
336
|
+
"input.answered": "输入已回答。",
|
|
337
|
+
"input.blocking": "阻塞",
|
|
338
|
+
"input.freeText": "输入回答",
|
|
339
|
+
"input.new": "有新的输入请求需要处理。",
|
|
340
|
+
"input.recommended": "建议",
|
|
341
|
+
"input.required": "必填",
|
|
342
|
+
"input.timeoutAt": "自动处理",
|
|
343
|
+
"launch.current": "与当前期望配置一致",
|
|
344
|
+
"launch.drift": "期望变更将在下次启动生效",
|
|
345
|
+
"loading.dashboard": "正在读取本地状态…",
|
|
346
|
+
"loading.detail": "正在读取任务详情…",
|
|
347
|
+
"messageKind.operator": "Operator",
|
|
348
|
+
"messageKind.role-result": "角色结果",
|
|
349
|
+
"messageKind.system": "系统",
|
|
350
|
+
"messageKind.user": "用户",
|
|
351
|
+
"metrics.active": "进行中",
|
|
352
|
+
"metrics.completed": "已完成",
|
|
353
|
+
"metrics.inputs": "等待你回答",
|
|
354
|
+
"metrics.label": "任务摘要",
|
|
355
|
+
"metrics.total": "任务总数",
|
|
356
|
+
"milestone.recorded": "已记录",
|
|
357
|
+
"history.milestone": "里程碑",
|
|
358
|
+
"history.decision": "决策",
|
|
359
|
+
"mode.new": "新会话",
|
|
360
|
+
"mode.resume": "恢复会话",
|
|
361
|
+
"overview.activeEmpty": "当前没有进行中的任务。",
|
|
362
|
+
"overview.activeNow": "进行中",
|
|
363
|
+
"overview.recent": "最近更新",
|
|
364
|
+
"overview.attention": "需要关注",
|
|
365
|
+
"overview.attentionEmpty": "没有待处理输入,当前无需你介入。",
|
|
366
|
+
"overview.distribution": "状态分布",
|
|
367
|
+
"overview.inbox": "需要你的输入",
|
|
368
|
+
"overview.inboxEmpty": "目前没有等待你回答的问题。",
|
|
369
|
+
"overview.lede": "正在跟踪 {total} 个任务 —— {active} 个进行中,{inputs} 个待处理输入,{completed} 个已完成。",
|
|
370
|
+
"overview.title": "全局概览",
|
|
371
|
+
"page.eyebrow": "系统概览",
|
|
372
|
+
"page.lede": "安静地查看每个任务、角色与尚未解决的决策。",
|
|
373
|
+
"page.title": "概览",
|
|
374
|
+
"priority.high": "高优先级",
|
|
375
|
+
"priority.low": "低优先级",
|
|
376
|
+
"priority.medium": "中优先级",
|
|
377
|
+
"priority.urgent": "紧急",
|
|
378
|
+
"review.completed": "已完成",
|
|
379
|
+
"review.failed": "失败",
|
|
380
|
+
"review.pending": "待准备",
|
|
381
|
+
"review.running": "评审中",
|
|
382
|
+
"role.detached": "已分离",
|
|
383
|
+
"role.exited": "已退出",
|
|
384
|
+
"role.failed": "失败",
|
|
385
|
+
"role.idle": "空闲",
|
|
386
|
+
"role.running": "运行中",
|
|
387
|
+
"run.active": "执行中",
|
|
388
|
+
"run.failed": "失败",
|
|
389
|
+
"run.yielded": "已交付",
|
|
390
|
+
"search.label": "搜索任务",
|
|
391
|
+
"search.placeholder": "按标题、ID、标签或项目筛选…",
|
|
392
|
+
"stats.inputs": "项输入",
|
|
393
|
+
"stats.running": "运行中",
|
|
394
|
+
"status.active": "进行中",
|
|
395
|
+
"status.all": "全部",
|
|
396
|
+
"status.archived": "已归档",
|
|
397
|
+
"status.completed": "已完成",
|
|
398
|
+
"status.draft": "草稿",
|
|
399
|
+
"status.retired": "已退役",
|
|
400
|
+
"sync.label": "最近同步",
|
|
401
|
+
"tabs.exec": "运行",
|
|
402
|
+
"tabs.focus": "焦点",
|
|
403
|
+
"tabs.history": "历史",
|
|
404
|
+
"tabs.label": "任务分区",
|
|
405
|
+
"tabs.messages": "消息",
|
|
406
|
+
"tabs.reviews": "评审",
|
|
407
|
+
"tabs.roles": "角色",
|
|
408
|
+
"tabs.summary": "摘要",
|
|
409
|
+
"tabs.work": "工作项",
|
|
410
|
+
"terminal.attachNote": "此面板将保持附加,直到你手动关闭。",
|
|
411
|
+
"terminal.attachTitle": "在终端中打开完整的 Operator",
|
|
412
|
+
"terminal.close": "关闭终端",
|
|
413
|
+
"terminal.closed": "已分离",
|
|
414
|
+
"terminal.connecting": "正在连接",
|
|
415
|
+
"terminal.error": "无法打开原生会话。",
|
|
416
|
+
"terminal.historyLimited": "这个已有 Role 的 tmux 历史只有 {current} 行;退出并重新进入后可使用 {target} 行。",
|
|
417
|
+
"terminal.readOnly": "只读",
|
|
418
|
+
"terminal.writable": "可交互",
|
|
419
|
+
"theme.atlas": "Atlas 深空",
|
|
420
|
+
"theme.controlRoom": "控制室",
|
|
421
|
+
"theme.paper": "纸本台账",
|
|
422
|
+
"time.justNow": "刚刚",
|
|
423
|
+
"work.awaiting_acceptance": "待验收",
|
|
424
|
+
"work.cancelled": "已取消",
|
|
425
|
+
"work.completed": "已完成",
|
|
426
|
+
"work.failed": "失败",
|
|
427
|
+
"work.pending": "待处理",
|
|
428
|
+
"work.retired": "已退役",
|
|
429
|
+
"work.running": "进行中",
|
|
430
|
+
"work.superseded": "已替代",
|
|
431
|
+
"stats.running": "运行中",
|
|
432
|
+
"stats.inputs": "项输入",
|
|
433
|
+
"stats.stalledRuns": "停滞运行"
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
export const SUPPORTED_LOCALES = Object.freeze(Object.keys(messages));
|
|
438
|
+
|
|
439
|
+
function preferredLocale() {
|
|
440
|
+
const saved = localStorage.getItem("yui.locale");
|
|
441
|
+
if (SUPPORTED_LOCALES.includes(saved)) return saved;
|
|
442
|
+
const languages = navigator.languages || [navigator.language];
|
|
443
|
+
return languages.some(function (language) { return String(language).toLowerCase().startsWith("zh"); })
|
|
444
|
+
? "zh-CN"
|
|
445
|
+
: "en";
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export function createI18n(select) {
|
|
449
|
+
let locale = preferredLocale();
|
|
450
|
+
const subscribers = new Set();
|
|
451
|
+
|
|
452
|
+
function t(key) {
|
|
453
|
+
return messages[locale][key] || messages.en[key] || key;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function apply() {
|
|
457
|
+
document.documentElement.lang = locale;
|
|
458
|
+
document.title = t("app.title");
|
|
459
|
+
document.querySelectorAll("[data-i18n]").forEach(function (element) {
|
|
460
|
+
element.textContent = t(element.dataset.i18n);
|
|
461
|
+
});
|
|
462
|
+
document.querySelectorAll("[data-i18n-placeholder]").forEach(function (element) {
|
|
463
|
+
element.setAttribute("placeholder", t(element.dataset.i18nPlaceholder));
|
|
464
|
+
});
|
|
465
|
+
document.querySelectorAll("[data-i18n-aria-label]").forEach(function (element) {
|
|
466
|
+
element.setAttribute("aria-label", t(element.dataset.i18nAriaLabel));
|
|
467
|
+
});
|
|
468
|
+
if (select) select.value = locale;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function setLocale(nextLocale) {
|
|
472
|
+
if (!SUPPORTED_LOCALES.includes(nextLocale) || nextLocale === locale) return;
|
|
473
|
+
locale = nextLocale;
|
|
474
|
+
localStorage.setItem("yui.locale", locale);
|
|
475
|
+
apply();
|
|
476
|
+
subscribers.forEach(function (subscriber) { subscriber(locale); });
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (select) {
|
|
480
|
+
select.addEventListener("change", function () { setLocale(select.value); });
|
|
481
|
+
}
|
|
482
|
+
apply();
|
|
483
|
+
return {
|
|
484
|
+
t: t,
|
|
485
|
+
apply: apply,
|
|
486
|
+
getLocale: function () { return locale; },
|
|
487
|
+
setLocale: setLocale,
|
|
488
|
+
subscribe: function (subscriber) {
|
|
489
|
+
subscribers.add(subscriber);
|
|
490
|
+
return function () { subscribers.delete(subscriber); };
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
`;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export const MARKDOWN_SCRIPT = `
|
|
2
|
+
// Minimal, safe Markdown renderer.
|
|
3
|
+
//
|
|
4
|
+
// Agent-authored prose (focus, outcomes, reviews, messages) arrives as plain
|
|
5
|
+
// text with informal Markdown. Rendering structure (headings, lists, code)
|
|
6
|
+
// turns walls of text into scannable blocks.
|
|
7
|
+
//
|
|
8
|
+
// Safety rule: every byte of user text is HTML-escaped BEFORE any tag is
|
|
9
|
+
// introduced; the transforms below only ever add our own elements. Links are
|
|
10
|
+
// limited to http(s) URLs taken from already-escaped text, so no attribute
|
|
11
|
+
// breakout is possible.
|
|
12
|
+
export function escapeHtml(text) {
|
|
13
|
+
return String(text)
|
|
14
|
+
.replace(/&/g, "&")
|
|
15
|
+
.replace(/</g, "<")
|
|
16
|
+
.replace(/>/g, ">")
|
|
17
|
+
.replace(/"/g, """);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Inline spans: code, bold, auto-links. Expects HTML-escaped input.
|
|
21
|
+
export function inlineMarkdown(escaped) {
|
|
22
|
+
const codes = [];
|
|
23
|
+
let out = escaped.replace(/\`([^\`\\n]+)\`/g, function (_match, code) {
|
|
24
|
+
codes.push(code);
|
|
25
|
+
return "\\u0000" + (codes.length - 1) + "\\u0000";
|
|
26
|
+
});
|
|
27
|
+
out = out.replace(/\\*\\*([^*\\n]+)\\*\\*/g, "<strong>$1</strong>");
|
|
28
|
+
out = out.replace(/(https?:\\/\\/[^\\s<]+)/g, function (url) {
|
|
29
|
+
return '<a href="' + url + '" target="_blank" rel="noreferrer">' + url + "</a>";
|
|
30
|
+
});
|
|
31
|
+
out = out.replace(/\\u0000(\\d+)\\u0000/g, function (_match, index) {
|
|
32
|
+
return "<code>" + codes[Number(index)] + "</code>";
|
|
33
|
+
});
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Block structure: fenced code, ATX headings, unordered / ordered lists,
|
|
38
|
+
// paragraphs with soft line breaks.
|
|
39
|
+
export function renderMarkdown(text) {
|
|
40
|
+
const lines = String(text).replace(/\\u0000/g, "").replace(/\\r\\n?/g, "\\n").split("\\n");
|
|
41
|
+
const html = [];
|
|
42
|
+
let paragraph = [];
|
|
43
|
+
let list = null;
|
|
44
|
+
let code = null;
|
|
45
|
+
function flushParagraph() {
|
|
46
|
+
if (!paragraph.length) return;
|
|
47
|
+
html.push("<p>" + paragraph.map(inlineMarkdown).join("<br>") + "</p>");
|
|
48
|
+
paragraph = [];
|
|
49
|
+
}
|
|
50
|
+
function flushList() {
|
|
51
|
+
if (!list) return;
|
|
52
|
+
html.push("<" + list.type + ">" + list.items.map(function (item) {
|
|
53
|
+
return "<li>" + inlineMarkdown(item) + "</li>";
|
|
54
|
+
}).join("") + "</" + list.type + ">");
|
|
55
|
+
list = null;
|
|
56
|
+
}
|
|
57
|
+
lines.forEach(function (line) {
|
|
58
|
+
if (code !== null) {
|
|
59
|
+
if (/^\\s*\`\`\`/.test(line)) {
|
|
60
|
+
html.push("<pre><code>" + code.join("\\n") + "</code></pre>");
|
|
61
|
+
code = null;
|
|
62
|
+
} else {
|
|
63
|
+
code.push(line);
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (/^\\s*\`\`\`/.test(line)) {
|
|
68
|
+
flushParagraph();
|
|
69
|
+
flushList();
|
|
70
|
+
code = [];
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const heading = line.match(/^\\s{0,3}(#{1,4})\\s+(.*)$/);
|
|
74
|
+
if (heading) {
|
|
75
|
+
flushParagraph();
|
|
76
|
+
flushList();
|
|
77
|
+
const level = Math.min(heading[1].length + 3, 6);
|
|
78
|
+
html.push("<h" + level + ">" + inlineMarkdown(escapeHtml(heading[2].trim())) + "</h" + level + ">");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const unordered = line.match(/^\\s*[-*•]\\s+(.*)$/);
|
|
82
|
+
if (unordered) {
|
|
83
|
+
flushParagraph();
|
|
84
|
+
if (!list || list.type !== "ul") {
|
|
85
|
+
flushList();
|
|
86
|
+
list = { type: "ul", items: [] };
|
|
87
|
+
}
|
|
88
|
+
list.items.push(escapeHtml(unordered[1]));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const ordered = line.match(/^\\s*\\d+[.)]\\s+(.*)$/);
|
|
92
|
+
if (ordered) {
|
|
93
|
+
flushParagraph();
|
|
94
|
+
if (!list || list.type !== "ol") {
|
|
95
|
+
flushList();
|
|
96
|
+
list = { type: "ol", items: [] };
|
|
97
|
+
}
|
|
98
|
+
list.items.push(escapeHtml(ordered[1]));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (/^\\s*$/.test(line)) {
|
|
102
|
+
flushParagraph();
|
|
103
|
+
flushList();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
flushList();
|
|
107
|
+
paragraph.push(escapeHtml(line));
|
|
108
|
+
});
|
|
109
|
+
flushParagraph();
|
|
110
|
+
flushList();
|
|
111
|
+
if (code !== null) html.push("<pre><code>" + code.join("\\n") + "</code></pre>");
|
|
112
|
+
return html.join("");
|
|
113
|
+
}
|
|
114
|
+
`;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const THEME_SCRIPT = `
|
|
2
|
+
// Theme registry. To add a theme: append its name here, add a matching
|
|
3
|
+
// [data-theme="name"]{...} token block in styles.ts (TOKEN_STYLES), and add an
|
|
4
|
+
// <option> in shell.ts. The first entry is the default when none is stored.
|
|
5
|
+
export const THEMES = Object.freeze(["control-room", "paper", "atlas"]);
|
|
6
|
+
|
|
7
|
+
function preferredTheme() {
|
|
8
|
+
const saved = localStorage.getItem("yui.theme");
|
|
9
|
+
return THEMES.includes(saved) ? saved : "control-room";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createThemeController(select) {
|
|
13
|
+
let theme = preferredTheme();
|
|
14
|
+
|
|
15
|
+
function apply(nextTheme, persist) {
|
|
16
|
+
if (!THEMES.includes(nextTheme)) return;
|
|
17
|
+
theme = nextTheme;
|
|
18
|
+
document.documentElement.dataset.theme = theme;
|
|
19
|
+
if (select) select.value = theme;
|
|
20
|
+
if (persist) localStorage.setItem("yui.theme", theme);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (select) {
|
|
24
|
+
select.addEventListener("change", function () { apply(select.value, true); });
|
|
25
|
+
}
|
|
26
|
+
apply(theme, false);
|
|
27
|
+
return {
|
|
28
|
+
getTheme: function () { return theme; },
|
|
29
|
+
setTheme: function (nextTheme) { apply(nextTheme, true); }
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
`;
|