cortico 0.1.0

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.
Files changed (311) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/package.json +43 -0
  4. package/src/boot.ts +46 -0
  5. package/src/bot.ts +1342 -0
  6. package/src/config-file.ts +41 -0
  7. package/src/core/README.md +92 -0
  8. package/src/core/billing.ts +26 -0
  9. package/src/core/blobs.ts +106 -0
  10. package/src/core/bus.ts +311 -0
  11. package/src/core/config-schema.ts +201 -0
  12. package/src/core/config.ts +262 -0
  13. package/src/core/core.ts +699 -0
  14. package/src/core/cost.ts +244 -0
  15. package/src/core/event-store.ts +304 -0
  16. package/src/core/fork.ts +113 -0
  17. package/src/core/generation.ts +118 -0
  18. package/src/core/instance-lock.ts +215 -0
  19. package/src/core/ipc-logger.ts +105 -0
  20. package/src/core/language.ts +62 -0
  21. package/src/core/log-context.ts +38 -0
  22. package/src/core/loop.ts +1858 -0
  23. package/src/core/markers.ts +48 -0
  24. package/src/core/prefix.ts +121 -0
  25. package/src/core/run.ts +95 -0
  26. package/src/core/secrets.ts +18 -0
  27. package/src/core/session.ts +56 -0
  28. package/src/core/sessions.ts +261 -0
  29. package/src/core/state.ts +104 -0
  30. package/src/core/template.ts +72 -0
  31. package/src/core/timers.ts +129 -0
  32. package/src/core/tool-log.ts +135 -0
  33. package/src/core/transcript.ts +92 -0
  34. package/src/core/truncate.ts +121 -0
  35. package/src/core/types.ts +1140 -0
  36. package/src/core/usage-log.ts +79 -0
  37. package/src/core/util.ts +386 -0
  38. package/src/deploy-listing.ts +160 -0
  39. package/src/deploy.ts +162 -0
  40. package/src/extensions/README.md +72 -0
  41. package/src/extensions/dry-mount.ts +390 -0
  42. package/src/extensions/manifest.ts +153 -0
  43. package/src/extensions/runtime.ts +42 -0
  44. package/src/extensions.ts +519 -0
  45. package/src/launcher.ts +285 -0
  46. package/src/paths.ts +155 -0
  47. package/src/protocol/open-responses/LICENSE +201 -0
  48. package/src/protocol/open-responses/README.md +14 -0
  49. package/src/protocol/open-responses/context-helpers.ts +27 -0
  50. package/src/protocol/open-responses/context-log.ts +59 -0
  51. package/src/protocol/open-responses/context.ts +89 -0
  52. package/src/protocol/open-responses/generated.ts +115 -0
  53. package/src/protocol/open-responses/index.ts +33 -0
  54. package/src/protocol/open-responses/openapi.json +4230 -0
  55. package/src/protocol/open-responses/stream.ts +146 -0
  56. package/src/protocol/open-responses/tokens.ts +31 -0
  57. package/src/providers/README.md +86 -0
  58. package/src/providers/base.ts +111 -0
  59. package/src/providers/configuration.ts +97 -0
  60. package/src/providers/console/config.ts +21 -0
  61. package/src/providers/console/settings.ts +443 -0
  62. package/src/providers/console/strings.ts +74 -0
  63. package/src/providers/console/types.ts +11 -0
  64. package/src/providers/llamacpp/archive.ts +148 -0
  65. package/src/providers/llamacpp/catalog.ts +247 -0
  66. package/src/providers/llamacpp/config.ts +36 -0
  67. package/src/providers/llamacpp/console/client.ts +6 -0
  68. package/src/providers/llamacpp/console/models-panel.ts +120 -0
  69. package/src/providers/llamacpp/console/runtime-panel.ts +173 -0
  70. package/src/providers/llamacpp/console/server.ts +153 -0
  71. package/src/providers/llamacpp/index.ts +95 -0
  72. package/src/providers/llamacpp/native.ts +51 -0
  73. package/src/providers/llamacpp/options.ts +117 -0
  74. package/src/providers/llamacpp/runtime-store.ts +139 -0
  75. package/src/providers/llamacpp/runtime.ts +214 -0
  76. package/src/providers/llamacpp/server.ts +286 -0
  77. package/src/providers/llamacpp/strings.ts +230 -0
  78. package/src/providers/openai-responses-compat/config.ts +20 -0
  79. package/src/providers/openai-responses-compat/index.ts +89 -0
  80. package/src/providers/openai-responses-compat/native.ts +127 -0
  81. package/src/providers/openai-responses-compat/strings.ts +19 -0
  82. package/src/providers/pricebook.ts +75 -0
  83. package/src/providers/registry.ts +137 -0
  84. package/src/providers/strings.ts +71 -0
  85. package/src/providers/transport/chat.ts +156 -0
  86. package/src/providers/transport/errors.ts +48 -0
  87. package/src/providers/transport/history.ts +68 -0
  88. package/src/providers/transport/native-input.ts +83 -0
  89. package/src/providers/transport/native-types.ts +13 -0
  90. package/src/providers/transport/response-assembly.ts +191 -0
  91. package/src/providers/transport/response-http.ts +196 -0
  92. package/src/providers/transport/response-meters.ts +27 -0
  93. package/src/providers/transport/responses-input.ts +56 -0
  94. package/src/web/README.md +84 -0
  95. package/src/web/client/console-pages/builtins/llm-settings/panel.ts +625 -0
  96. package/src/web/client/console-pages/builtins/llm-settings/pricing-panel.ts +168 -0
  97. package/src/web/client/console-pages/builtins/llm-settings/strings.ts +213 -0
  98. package/src/web/client/console-pages/builtins.ts +10 -0
  99. package/src/web/client/console-pages/context.ts +135 -0
  100. package/src/web/client/console-pages/host.ts +633 -0
  101. package/src/web/client/console-pages/loader.ts +132 -0
  102. package/src/web/client/console-pages/strings.ts +90 -0
  103. package/src/web/client/console-pages/tools/strings.ts +49 -0
  104. package/src/web/client/console-pages/tools/view.ts +215 -0
  105. package/src/web/client/core/api.ts +200 -0
  106. package/src/web/client/core/language.ts +56 -0
  107. package/src/web/client/core/lifecycle.ts +132 -0
  108. package/src/web/client/core/router.ts +199 -0
  109. package/src/web/client/core/stream.ts +157 -0
  110. package/src/web/client/core/websocket.ts +82 -0
  111. package/src/web/client/features/appearance/index.ts +333 -0
  112. package/src/web/client/features/appearance/strings.ts +87 -0
  113. package/src/web/client/features/config/index.ts +11 -0
  114. package/src/web/client/features/config/strings.ts +41 -0
  115. package/src/web/client/features/config/view.ts +428 -0
  116. package/src/web/client/features/core/events.ts +197 -0
  117. package/src/web/client/features/core/index.ts +254 -0
  118. package/src/web/client/features/core/run.ts +56 -0
  119. package/src/web/client/features/core/runlog.ts +214 -0
  120. package/src/web/client/features/core/sessions.ts +53 -0
  121. package/src/web/client/features/core/strings.ts +157 -0
  122. package/src/web/client/features/extensions/index.ts +422 -0
  123. package/src/web/client/features/extensions/strings.ts +164 -0
  124. package/src/web/client/features/feature.ts +93 -0
  125. package/src/web/client/features/live/context.ts +280 -0
  126. package/src/web/client/features/live/fork.ts +156 -0
  127. package/src/web/client/features/live/index.ts +392 -0
  128. package/src/web/client/features/live/onboarding.ts +113 -0
  129. package/src/web/client/features/live/protocol.ts +122 -0
  130. package/src/web/client/features/live/sessions.ts +71 -0
  131. package/src/web/client/features/live/status.ts +68 -0
  132. package/src/web/client/features/live/strings.ts +252 -0
  133. package/src/web/client/features/live/timeline.ts +590 -0
  134. package/src/web/client/features/prompts/editor.ts +292 -0
  135. package/src/web/client/features/prompts/index.ts +346 -0
  136. package/src/web/client/features/prompts/strings.ts +136 -0
  137. package/src/web/client/features/prompts/view.ts +256 -0
  138. package/src/web/client/features/providers/index.ts +100 -0
  139. package/src/web/client/features/providers/strings.ts +23 -0
  140. package/src/web/client/features/settings/general.ts +28 -0
  141. package/src/web/client/features/settings/index.ts +82 -0
  142. package/src/web/client/features/settings/strings.ts +33 -0
  143. package/src/web/client/features/storage/index.ts +1 -0
  144. package/src/web/client/features/storage/strings.ts +45 -0
  145. package/src/web/client/features/storage/view.ts +154 -0
  146. package/src/web/client/features/usage/chart.ts +486 -0
  147. package/src/web/client/features/usage/index.ts +407 -0
  148. package/src/web/client/features/usage/labels.ts +46 -0
  149. package/src/web/client/features/usage/range.ts +36 -0
  150. package/src/web/client/features/usage/state.ts +37 -0
  151. package/src/web/client/features/usage/strings.ts +191 -0
  152. package/src/web/client/features/usage/tooltip.ts +86 -0
  153. package/src/web/client/features/usage/types.ts +96 -0
  154. package/src/web/client/features/worlds/index.ts +424 -0
  155. package/src/web/client/features/worlds/strings.ts +121 -0
  156. package/src/web/client/main.ts +273 -0
  157. package/src/web/client/shell/avatar.ts +233 -0
  158. package/src/web/client/shell/index.ts +549 -0
  159. package/src/web/client/shell/strings.ts +89 -0
  160. package/src/web/client/theme/handoff.ts +43 -0
  161. package/src/web/client/theme/palette.ts +109 -0
  162. package/src/web/client/theme/registry.ts +130 -0
  163. package/src/web/client/theme/storage.ts +72 -0
  164. package/src/web/client/theme/strings.ts +123 -0
  165. package/src/web/client/theme/studio.ts +366 -0
  166. package/src/web/client/ui/actions.ts +109 -0
  167. package/src/web/client/ui/data.ts +185 -0
  168. package/src/web/client/ui/dom.ts +23 -0
  169. package/src/web/client/ui/fields.ts +191 -0
  170. package/src/web/client/ui/format.ts +75 -0
  171. package/src/web/client/ui/icons.ts +183 -0
  172. package/src/web/client/ui/images.ts +91 -0
  173. package/src/web/client/ui/index.ts +89 -0
  174. package/src/web/client/ui/lamp.ts +125 -0
  175. package/src/web/client/ui/log.ts +98 -0
  176. package/src/web/client/ui/overlay.ts +184 -0
  177. package/src/web/client/ui/page.ts +8 -0
  178. package/src/web/client/ui/prompt-input.tsx +311 -0
  179. package/src/web/client/ui/sheet.ts +131 -0
  180. package/src/web/client/ui/strings.ts +71 -0
  181. package/src/web/console-pages.ts +367 -0
  182. package/src/web/files.ts +107 -0
  183. package/src/web/path-picker.ts +296 -0
  184. package/src/web/public/index.html +17 -0
  185. package/src/web/public/styles.css +1715 -0
  186. package/src/web/server.ts +1883 -0
  187. package/src/web/shared/client-panel.ts +603 -0
  188. package/src/web/shared/console-protocol.ts +560 -0
  189. package/src/web/shared/css.d.ts +1 -0
  190. package/src/web/shared/path-picker.ts +23 -0
  191. package/src/web/shared/theme.ts +256 -0
  192. package/src/web/theme-store.ts +37 -0
  193. package/src/world.ts +449 -0
  194. package/src/worlds/bilibili/ENV_PROMPT.md +5 -0
  195. package/src/worlds/bilibili/README.md +176 -0
  196. package/src/worlds/bilibili/audience-admission.ts +995 -0
  197. package/src/worlds/bilibili/client.ts +528 -0
  198. package/src/worlds/bilibili/coalescing-buffer.ts +125 -0
  199. package/src/worlds/bilibili/config.ts +185 -0
  200. package/src/worlds/bilibili/console/client.ts +202 -0
  201. package/src/worlds/bilibili/definition.ts +35 -0
  202. package/src/worlds/bilibili/gift-frame.ts +131 -0
  203. package/src/worlds/bilibili/normalize.ts +452 -0
  204. package/src/worlds/bilibili/overlay/announcement.ts +76 -0
  205. package/src/worlds/bilibili/overlay/assets.ts +82 -0
  206. package/src/worlds/bilibili/overlay/model.ts +494 -0
  207. package/src/worlds/bilibili/overlay/project.ts +202 -0
  208. package/src/worlds/bilibili/overlay/server.ts +286 -0
  209. package/src/worlds/bilibili/overlay/types.ts +217 -0
  210. package/src/worlds/bilibili/overlay/web/app.js +690 -0
  211. package/src/worlds/bilibili/overlay/web/editor.css +297 -0
  212. package/src/worlds/bilibili/overlay/web/editor.html +102 -0
  213. package/src/worlds/bilibili/overlay/web/editor.js +2116 -0
  214. package/src/worlds/bilibili/overlay/web/overlay.html +13 -0
  215. package/src/worlds/bilibili/overlay/web/styles.css +258 -0
  216. package/src/worlds/bilibili/protobuf.ts +123 -0
  217. package/src/worlds/bilibili/wire.ts +94 -0
  218. package/src/worlds/bilibili/world.ts +1389 -0
  219. package/src/worlds/console-fixture/ENV_PROMPT.md +1 -0
  220. package/src/worlds/console-fixture/console/client.ts +72 -0
  221. package/src/worlds/console-fixture/world.ts +73 -0
  222. package/src/worlds/index.ts +15 -0
  223. package/src/worlds/minecraft/ENV_PROMPT.md +32 -0
  224. package/src/worlds/minecraft/ENV_PROMPT_CAMERA.md +2 -0
  225. package/src/worlds/minecraft/LICENSE-mineflayer-pathfinder.txt +21 -0
  226. package/src/worlds/minecraft/README.md +917 -0
  227. package/src/worlds/minecraft/blueprint-plan.ts +915 -0
  228. package/src/worlds/minecraft/blueprint-registry.ts +566 -0
  229. package/src/worlds/minecraft/blueprint-repair.ts +236 -0
  230. package/src/worlds/minecraft/blueprint-resource.ts +272 -0
  231. package/src/worlds/minecraft/blueprint.ts +492 -0
  232. package/src/worlds/minecraft/body-lease.ts +340 -0
  233. package/src/worlds/minecraft/bridge.ts +749 -0
  234. package/src/worlds/minecraft/check.ts +651 -0
  235. package/src/worlds/minecraft/chests.ts +292 -0
  236. package/src/worlds/minecraft/client-launch.ts +272 -0
  237. package/src/worlds/minecraft/client-options.ts +85 -0
  238. package/src/worlds/minecraft/client-skins.ts +164 -0
  239. package/src/worlds/minecraft/client-window.ps1 +91 -0
  240. package/src/worlds/minecraft/client.ts +423 -0
  241. package/src/worlds/minecraft/combat-context.ts +109 -0
  242. package/src/worlds/minecraft/combat.ts +1552 -0
  243. package/src/worlds/minecraft/config.ts +499 -0
  244. package/src/worlds/minecraft/console/access.ts +218 -0
  245. package/src/worlds/minecraft/console/client.ts +244 -0
  246. package/src/worlds/minecraft/console/log.ts +110 -0
  247. package/src/worlds/minecraft/console/mount.ts +420 -0
  248. package/src/worlds/minecraft/console/skin.ts +257 -0
  249. package/src/worlds/minecraft/console/style.css +154 -0
  250. package/src/worlds/minecraft/console/world.ts +449 -0
  251. package/src/worlds/minecraft/deaths.ts +135 -0
  252. package/src/worlds/minecraft/definition.ts +18 -0
  253. package/src/worlds/minecraft/engine-child.ts +274 -0
  254. package/src/worlds/minecraft/engine-ipc.ts +98 -0
  255. package/src/worlds/minecraft/entity-facts.ts +132 -0
  256. package/src/worlds/minecraft/escape.ts +407 -0
  257. package/src/worlds/minecraft/executor.ts +14494 -0
  258. package/src/worlds/minecraft/explored.ts +156 -0
  259. package/src/worlds/minecraft/geometry.ts +205 -0
  260. package/src/worlds/minecraft/goal-plan.ts +647 -0
  261. package/src/worlds/minecraft/item-break.ts +126 -0
  262. package/src/worlds/minecraft/item-facts.ts +146 -0
  263. package/src/worlds/minecraft/item-pick.ts +67 -0
  264. package/src/worlds/minecraft/level-dat.ts +176 -0
  265. package/src/worlds/minecraft/log.ts +132 -0
  266. package/src/worlds/minecraft/mineflayer-fixes.ts +1180 -0
  267. package/src/worlds/minecraft/names.ts +377 -0
  268. package/src/worlds/minecraft/pathfinder-lib.d.ts +13 -0
  269. package/src/worlds/minecraft/pathfinder-perf.ts +796 -0
  270. package/src/worlds/minecraft/piglin.ts +49 -0
  271. package/src/worlds/minecraft/policy.ts +547 -0
  272. package/src/worlds/minecraft/precheck.ts +655 -0
  273. package/src/worlds/minecraft/proxy.ts +445 -0
  274. package/src/worlds/minecraft/ranged.ts +635 -0
  275. package/src/worlds/minecraft/readouts.ts +102 -0
  276. package/src/worlds/minecraft/round.ts +26 -0
  277. package/src/worlds/minecraft/search-observation.ts +92 -0
  278. package/src/worlds/minecraft/server-config.ts +467 -0
  279. package/src/worlds/minecraft/server.ts +658 -0
  280. package/src/worlds/minecraft/show.ts +84 -0
  281. package/src/worlds/minecraft/skills.ts +1763 -0
  282. package/src/worlds/minecraft/terrain.ts +1735 -0
  283. package/src/worlds/minecraft/window.ts +96 -0
  284. package/src/worlds/minecraft/works.ts +179 -0
  285. package/src/worlds/minecraft/world.ts +6438 -0
  286. package/src/worlds/qq/ENV_PROMPT.md +5 -0
  287. package/src/worlds/qq/config.ts +108 -0
  288. package/src/worlds/qq/console/client.ts +126 -0
  289. package/src/worlds/qq/console/events.ts +155 -0
  290. package/src/worlds/qq/console/gate.ts +232 -0
  291. package/src/worlds/qq/console/roster.ts +203 -0
  292. package/src/worlds/qq/console/style.css +65 -0
  293. package/src/worlds/qq/conversation.ts +28 -0
  294. package/src/worlds/qq/definition.ts +62 -0
  295. package/src/worlds/qq/driver.ts +375 -0
  296. package/src/worlds/qq/history-tools.ts +209 -0
  297. package/src/worlds/qq/normalize.ts +323 -0
  298. package/src/worlds/qq/qface-map.ts +282 -0
  299. package/src/worlds/qq/vision-prompt.ts +27 -0
  300. package/src/worlds/qq/vision.ts +642 -0
  301. package/src/worlds/qq/vlm.ts +142 -0
  302. package/src/worlds/qq/world.ts +1503 -0
  303. package/src/worlds/terminal/ENV_PROMPT.md +11 -0
  304. package/src/worlds/terminal/config.ts +9 -0
  305. package/src/worlds/terminal/definition.ts +14 -0
  306. package/src/worlds/terminal/world.ts +700 -0
  307. package/src/worlds/websearch/ENV_PROMPT.md +1 -0
  308. package/src/worlds/websearch/brave-client.ts +261 -0
  309. package/src/worlds/websearch/config.ts +75 -0
  310. package/src/worlds/websearch/definition.ts +20 -0
  311. package/src/worlds/websearch/world.ts +171 -0
@@ -0,0 +1,333 @@
1
+ /** 外观页读取 ThemeStudio 状态;调色草稿仅用于预览,卸载时撤销预览。 */
2
+
3
+ import type { Disposable } from '../../../shared/client-panel.ts';
4
+ import type { FeatureContext, FrameworkFeature } from '../feature.ts';
5
+ import { pageIntro } from '../../ui/page.ts';
6
+ import {
7
+ THEME_APPEARANCE_LABELS,
8
+ THEME_MODES,
9
+ THEME_MODE_LABELS,
10
+ groupedThemeTokens,
11
+ normalizeHex,
12
+ type ThemeMode,
13
+ type ThemePalette,
14
+ } from '../../theme/registry.ts';
15
+ import { getThemeStudio, type ThemeSnapshot } from '../../theme/studio.ts';
16
+ import { S } from './strings.ts';
17
+
18
+ /** "再点一次确认删除"的解除武装时限。 */
19
+ const DELETE_ARM_MS = 4000;
20
+ const DELETE_LABEL = S.deleteScheme;
21
+
22
+ /** 一格颜色的三个节点。改值时按 token key 找回来。 */
23
+ interface ColorRow {
24
+ row: HTMLElement;
25
+ picker: HTMLInputElement;
26
+ hex: HTMLInputElement;
27
+ }
28
+
29
+ function schemeIdAt(target: EventTarget | null, stop: HTMLElement): string | null {
30
+ let node = target as HTMLElement | null;
31
+ while (node && node !== stop) {
32
+ const id = node.dataset?.schemeId;
33
+ if (id) return id;
34
+ node = node.parentElement;
35
+ }
36
+ return null;
37
+ }
38
+
39
+ export function mountAppearance(ctx: FeatureContext, opts: { embedded?: boolean } = {}): void {
40
+ const { ui, root, signal } = ctx;
41
+ const doc = root.ownerDocument;
42
+ const studio = getThemeStudio({ doc });
43
+ const intro = pageIntro(ui, S.pageTitle);
44
+
45
+ // 最先登记 = 最后释放:等订阅摘掉之后再撤预览,免得回调打在一个正在拆的页面上。
46
+ ctx.lifecycle.add(() => studio.resetPreview());
47
+
48
+ /** 用户正在调、尚未保存的一份调色板。 */
49
+ let draft: ThemePalette = studio.snapshot().palette;
50
+ let deleteArmed = false;
51
+ let disarmTimer: Disposable | null = null;
52
+
53
+ // ── 方案卡 ─────────────────────────────────────────────────────────
54
+
55
+ const schemeSheet = ui.sheet({
56
+ title: S.schemeSheetTitle,
57
+ en: 'theme schema',
58
+ desc: S.schemeSheetDesc,
59
+ });
60
+ const currentEl = ui.h('div', 'theme-current', '—');
61
+ const modes = ui.segmented(
62
+ THEME_MODES.map((m) => ({ value: m, label: THEME_MODE_LABELS[m] })),
63
+ {
64
+ value: studio.mode,
65
+ size: 'sm',
66
+ onSelect: (value) => {
67
+ studio.setMode(value as ThemeMode);
68
+ setMsg(S.switchedMode(THEME_MODE_LABELS[value as ThemeMode]));
69
+ },
70
+ },
71
+ );
72
+ const modeBar = ui.rowbar();
73
+ modeBar.append(modes.el, ui.h('span', 'grow'), currentEl);
74
+ const schemesBox = ui.h('div', 'theme-schemes');
75
+ schemesBox.addEventListener(
76
+ 'click',
77
+ (ev) => {
78
+ const id = schemeIdAt(ev.target, schemesBox);
79
+ if (!id || !studio.select(id)) return;
80
+ setMsg(S.switchedScheme(studio.snapshot().scheme.name));
81
+ },
82
+ { signal },
83
+ );
84
+ schemeSheet.body.append(modeBar, schemesBox);
85
+
86
+ // ── 调色盘 ─────────────────────────────────────────────────────────
87
+
88
+ const paletteSheet = ui.sheet({
89
+ title: S.paletteSheetTitle,
90
+ en: 'palette',
91
+ desc: S.paletteSheetDesc,
92
+ });
93
+ const nameInput = ui.input({ cls: 'mono', placeholder: S.schemeName });
94
+ nameInput.maxLength = 40;
95
+ const paletteBox = ui.h('div', 'theme-palette');
96
+ const colorRows = new Map<string, ColorRow>();
97
+
98
+ for (const bucket of groupedThemeTokens()) {
99
+ const section = ui.h('section', 'theme-color-group');
100
+ section.appendChild(ui.h('h4', null, bucket.group));
101
+ const grid = ui.h('div', 'theme-color-grid');
102
+ for (const token of bucket.tokens) {
103
+ const row = ui.h('label', 'theme-color');
104
+ const picker = ui.h('input');
105
+ picker.type = 'color';
106
+ picker.setAttribute('aria-label', S.colorAria(token.label));
107
+ const hex = ui.h('input');
108
+ hex.type = 'text';
109
+ hex.maxLength = 7;
110
+ hex.spellcheck = false;
111
+ hex.setAttribute('aria-label', S.hexAria(token.label));
112
+ const edit = (value: string, from: HTMLInputElement): void => {
113
+ const normalized = normalizeHex(value);
114
+ row.classList.toggle('invalid', normalized === null);
115
+ if (!normalized) return;
116
+ draft[token.key] = normalized;
117
+ // 只回填**另一个**控件:回填正在打字的那个会把光标弹到末尾。
118
+ if (from !== picker) picker.value = normalized;
119
+ if (from !== hex) hex.value = normalized.toUpperCase();
120
+ studio.preview(draft);
121
+ setMsg(S.previewing);
122
+ };
123
+ picker.addEventListener('input', () => edit(picker.value, picker), { signal });
124
+ picker.addEventListener('change', () => { hex.value = picker.value.toUpperCase(); }, { signal });
125
+ hex.addEventListener('input', () => edit(hex.value, hex), { signal });
126
+ row.append(picker, ui.h('span', 'theme-color-label', token.label), hex);
127
+ grid.appendChild(row);
128
+ colorRows.set(token.key, { row, picker, hex });
129
+ }
130
+ section.appendChild(grid);
131
+ paletteBox.appendChild(section);
132
+ }
133
+
134
+ const msg = ui.msgline('');
135
+ msg.className = 'msgline grow';
136
+ msg.setAttribute('aria-live', 'polite');
137
+
138
+ const resetBtn = ui.button(S.resetPreview, {
139
+ onClick: () => {
140
+ studio.resetPreview();
141
+ setMsg(S.previewReset);
142
+ },
143
+ });
144
+ const deleteBtn = ui.button(DELETE_LABEL, {
145
+ variant: 'danger',
146
+ onClick: () => {
147
+ const snap = studio.snapshot();
148
+ if (!snap.scheme.custom) return;
149
+ if (!deleteArmed) {
150
+ deleteArmed = true;
151
+ deleteBtn.textContent = S.confirmDelete;
152
+ setMsg(S.aboutToDelete(snap.scheme.name), true);
153
+ disarmTimer?.dispose();
154
+ // 计时器走 lifecycle:离开这一页时自动取消,不会在一个已卸载的页面上回调。
155
+ disarmTimer = ctx.lifecycle.timeout(() => {
156
+ disarmDelete();
157
+ setMsg(S.deleteCancelled);
158
+ }, DELETE_ARM_MS);
159
+ return;
160
+ }
161
+ disarmDelete();
162
+ studio.removeCurrent();
163
+ setMsg(S.deleted(studio.snapshot().scheme.name));
164
+ },
165
+ });
166
+ const copyBtn = ui.button(S.copyAsNew, {
167
+ onClick: () => {
168
+ const snap = studio.snapshot();
169
+ const base = nameInput.value.trim() || snap.scheme.name;
170
+ studio.saveAs(base + (snap.scheme.custom ? S.copySuffix : S.customSuffix), draft);
171
+ setMsg(S.copied);
172
+ },
173
+ });
174
+ const saveBtn = ui.button(S.saveScheme, {
175
+ variant: 'primary',
176
+ onClick: () => {
177
+ const snap = studio.snapshot();
178
+ const raw = nameInput.value.trim();
179
+ if (snap.scheme.custom) {
180
+ studio.saveCurrent(raw || snap.scheme.name, draft);
181
+ setMsg(S.savedVariant(THEME_APPEARANCE_LABELS[snap.appearance]));
182
+ } else {
183
+ studio.saveAs(raw === snap.scheme.name ? S.customName(raw) : raw, draft);
184
+ setMsg(S.savedAsCustom);
185
+ }
186
+ },
187
+ });
188
+
189
+ const actions = ui.actions();
190
+ actions.classList.add('theme-actions');
191
+ actions.append(msg, resetBtn, deleteBtn, copyBtn, saveBtn);
192
+ paletteSheet.body.append(ui.field(S.schemeName, nameInput), paletteBox, actions);
193
+
194
+ const workbench = ui.h('div', 'theme-workbench');
195
+ workbench.append(paletteSheet.el, specimen(ctx));
196
+ if (!opts.embedded) root.appendChild(intro);
197
+ root.append(schemeSheet.el, workbench);
198
+
199
+ // ── 刷新 ───────────────────────────────────────────────────────────
200
+
201
+ function setMsg(text: string, bad = false): void {
202
+ msg.textContent = text;
203
+ msg.className = `msgline grow${bad ? ' bad' : ''}`;
204
+ }
205
+
206
+ function disarmDelete(): void {
207
+ disarmTimer?.dispose();
208
+ disarmTimer = null;
209
+ if (!deleteArmed) return;
210
+ deleteArmed = false;
211
+ deleteBtn.textContent = DELETE_LABEL;
212
+ }
213
+
214
+ function renderSchemes(snap: ThemeSnapshot): void {
215
+ schemesBox.textContent = '';
216
+ for (const scheme of snap.schemes) {
217
+ const card = ui.h('button', `theme-scheme${scheme.id === snap.selectedId ? ' active' : ''}`);
218
+ card.type = 'button';
219
+ card.dataset.schemeId = scheme.id;
220
+ card.setAttribute('aria-pressed', scheme.id === snap.selectedId ? 'true' : 'false');
221
+ const head = ui.h('div', 'theme-scheme-head');
222
+ head.append(
223
+ ui.h('span', null, scheme.name),
224
+ ui.h('span', 'theme-scheme-kind', scheme.builtin ? S.builtin : S.custom),
225
+ );
226
+ const swatches = ui.h('div', 'theme-swatches');
227
+ for (const color of scheme.swatches) {
228
+ const sw = ui.h('span');
229
+ sw.style.background = color;
230
+ swatches.appendChild(sw);
231
+ }
232
+ card.append(head, ui.h('div', 'theme-scheme-note', scheme.note), swatches);
233
+ schemesBox.appendChild(card);
234
+ }
235
+ }
236
+
237
+ function syncPalette(palette: ThemePalette): void {
238
+ for (const [key, row] of colorRows) {
239
+ const value = palette[key] ?? '#000000';
240
+ row.picker.value = value;
241
+ row.hex.value = value.toUpperCase();
242
+ row.row.classList.remove('invalid');
243
+ }
244
+ }
245
+
246
+ function refresh(): void {
247
+ disarmDelete();
248
+ const snap = studio.snapshot();
249
+ draft = snap.palette;
250
+ currentEl.textContent = `${snap.scheme.name} · ${THEME_APPEARANCE_LABELS[snap.appearance]}`;
251
+ nameInput.value = snap.scheme.name;
252
+ modes.setValue(snap.mode);
253
+ deleteBtn.classList.toggle('hidden', !snap.scheme.custom);
254
+ saveBtn.textContent = snap.scheme.custom ? S.saveCurrent : S.saveAsCustom;
255
+ renderSchemes(snap);
256
+ syncPalette(snap.palette);
257
+ if (snap.saveError) setMsg(S.saveFailed(snap.saveError), true);
258
+ else setMsg(snap.scheme.builtin ? S.builtinHint : S.customHint);
259
+ }
260
+
261
+ // 预览那一串变化不重画自己:值就是这一页刚写进去的,回填只会打断正在打字的输入框。
262
+ ctx.lifecycle.own(studio.onChange((change) => {
263
+ if (!change.preview) refresh();
264
+ }));
265
+ refresh();
266
+ }
267
+
268
+ /** 无交互的主题预览示例。 */
269
+ function specimen(ctx: FeatureContext): HTMLElement {
270
+ const { ui } = ctx;
271
+ const card = ui.sheet({
272
+ title: S.specimenTitle,
273
+ en: 'live specimen',
274
+
275
+ });
276
+ card.el.classList.add('theme-preview-sheet');
277
+
278
+ const box = ui.h('div', 'theme-specimen');
279
+
280
+ // USER 组:内容列在前、记号栏在后(镜像)
281
+ const world = ui.h('div', 'grp r usergrp');
282
+ const worldCol = ui.h('div', 'gcol r');
283
+ const worldHead = ui.h('div', 'ghead');
284
+ worldHead.append(ui.h('span', 'meta ordinal', '#1'), ui.h('span', 'badge', 'USER'));
285
+ const worldBubble = ui.h('div', 'world');
286
+ worldBubble.appendChild(ui.h('div', 'world-line', '[system] 2 new events arrived.'));
287
+ worldCol.append(worldHead, worldBubble);
288
+ const worldGutter = ui.h('div', 'gutter');
289
+ worldGutter.appendChild(ui.h('span', 'glyph', '>_'));
290
+ world.append(worldCol, worldGutter);
291
+
292
+ // ASSISTANT 组:头像栏 + 内容列
293
+ const turn = ui.h('div', 'tcard turn grp');
294
+ const avatar = ui.h('div', 'gutter avatar');
295
+ avatar.appendChild(ui.h('span', 'avatar-fallback', 'B'));
296
+ const turnCol = ui.h('div', 'gcol');
297
+ const turnHead = ui.h('div', 'ghead turnhead');
298
+ turnHead.append(ui.h('span', 'badge', 'ASSISTANT'), ui.h('span', 'meta ordinal', '#2'));
299
+ const think = ui.h('div', 'think');
300
+ think.append(
301
+ ui.h('div', 'think-label', S.thinkLabel),
302
+ ui.h('div', 'think-body', S.thinkBody),
303
+ );
304
+ const monolog = ui.h('div', 'monolog');
305
+ monolog.appendChild(ui.h('div', 'monolog-body', S.monologBody));
306
+ turnCol.append(turnHead, think, monolog);
307
+ turn.append(avatar, turnCol);
308
+
309
+ const chart = ui.h('div', 'theme-chart-preview');
310
+ chart.setAttribute('aria-label', S.chartAria);
311
+ const bars: Array<[string, string]> = [
312
+ ['42%', 'chart-hit'], ['66%', 'chart-miss'], ['86%', 'chart-output'],
313
+ ['58%', 'chart-2'], ['74%', 'chart-3'], ['50%', 'chart-4'],
314
+ ];
315
+ for (const [height, key] of bars) {
316
+ const bar = ui.h('span');
317
+ bar.style.height = height;
318
+ bar.style.background = `var(--${key})`;
319
+ chart.appendChild(bar);
320
+ }
321
+
322
+ box.append(world, turn, chart);
323
+ card.body.appendChild(box);
324
+ return card.el;
325
+ }
326
+
327
+ /** 外观设置保存在浏览器本地,不依赖服务端能力。 */
328
+ export const appearanceFeature: FrameworkFeature = {
329
+ route: 'appearance',
330
+ label: S.navLabel,
331
+ navMode: 'hidden',
332
+ mount: mountAppearance,
333
+ };
@@ -0,0 +1,87 @@
1
+ import { pick } from '../../core/language.ts';
2
+
3
+ const zh = {
4
+ pageTitle: '外观',
5
+ deleteScheme: '删除方案',
6
+ schemeSheetTitle: '颜色方案',
7
+ schemeSheetDesc: "选择立即应用并保存在当前浏览器。",
8
+ switchedMode: (label: string) => `已切换到「${label}」`,
9
+ switchedScheme: (name: string) => `✓ 已切换到「${name}」`,
10
+ paletteSheetTitle: '调色盘',
11
+ paletteSheetDesc: "预览仅修改当前浅色或深色配色;保存后保留。",
12
+ schemeName: '方案名',
13
+ colorAria: (label: string) => `${label}颜色`,
14
+ hexAria: (label: string) => `${label}十六进制颜色`,
15
+ previewing: '● 正在预览,尚未保存',
16
+ resetPreview: '撤销预览',
17
+ previewReset: '已撤销未保存的调色预览',
18
+ confirmDelete: '再次点击确认删除',
19
+ aboutToDelete: (name: string) => `即将删除「${name}」;再次点击确认。`,
20
+ deleteCancelled: '已取消删除',
21
+ deleted: (name: string) => `已删除自定义方案,并切回「${name}」`,
22
+ copyAsNew: '复制为新方案',
23
+ copySuffix: ' 副本',
24
+ customSuffix: ' 自定义',
25
+ copied: '✓ 已复制为新的自定义方案',
26
+ saveScheme: '保存方案',
27
+ savedVariant: (appearance: string) => `✓ 当前${appearance}变体已保存`,
28
+ customName: (name: string) => `${name} 自定义`,
29
+ savedAsCustom: '✓ 内置方案已另存为自定义',
30
+ builtin: '内置',
31
+ custom: '自定义',
32
+ saveCurrent: '保存当前方案',
33
+ saveAsCustom: '另存为自定义',
34
+ builtinHint: '内置方案不可覆盖;调色后可另存为自定义。',
35
+ saveFailed: (reason: string) => `配色没能存进部署:${reason}`,
36
+ customHint: '自定义方案可直接保存;浅色与黑夜变体互相独立。',
37
+ specimenTitle: '组件试色',
38
+ thinkLabel: '思考',
39
+ thinkBody: "思考文本预览",
40
+ monologBody: "正文预览",
41
+ chartAria: '图表系列色预览',
42
+ navLabel: '外观',
43
+ };
44
+
45
+ const en: typeof zh = {
46
+ pageTitle: 'Appearance',
47
+ deleteScheme: 'Delete scheme',
48
+ schemeSheetTitle: 'Color scheme',
49
+ schemeSheetDesc: "Selection applies immediately and is saved in this browser.",
50
+ switchedMode: (label: string) => `Switched to "${label}"`,
51
+ switchedScheme: (name: string) => `✓ Switched to "${name}"`,
52
+ paletteSheetTitle: 'Palette',
53
+ paletteSheetDesc: "Preview edits only the current light or dark palette. Save to keep the changes.",
54
+ schemeName: 'Scheme name',
55
+ colorAria: (label: string) => `${label} color`,
56
+ hexAria: (label: string) => `${label} hex color`,
57
+ previewing: '● Previewing, not saved yet',
58
+ resetPreview: 'Discard preview',
59
+ previewReset: 'Unsaved palette preview discarded',
60
+ confirmDelete: 'Click again to confirm deletion',
61
+ aboutToDelete: (name: string) => `About to delete "${name}"; click again to confirm.`,
62
+ deleteCancelled: 'Deletion cancelled',
63
+ deleted: (name: string) => `Custom scheme deleted, switched back to "${name}"`,
64
+ copyAsNew: 'Copy as new scheme',
65
+ copySuffix: ' copy',
66
+ customSuffix: ' custom',
67
+ copied: '✓ Copied as a new custom scheme',
68
+ saveScheme: 'Save scheme',
69
+ savedVariant: (appearance: string) => `✓ Current ${appearance} variant saved`,
70
+ customName: (name: string) => `${name} custom`,
71
+ savedAsCustom: '✓ Built-in scheme saved as a custom scheme',
72
+ builtin: 'Built-in',
73
+ custom: 'Custom',
74
+ saveCurrent: 'Save current scheme',
75
+ saveAsCustom: 'Save as custom',
76
+ builtinHint: 'Built-in schemes cannot be overwritten; edit the palette and save as custom.',
77
+ saveFailed: (reason: string) => `The palette was not saved to the deployment: ${reason}`,
78
+ customHint: 'Custom schemes save directly; the light and dark variants are independent.',
79
+ specimenTitle: 'Component specimen',
80
+ thinkLabel: 'Reasoning',
81
+ thinkBody: "Reasoning text preview",
82
+ monologBody: "Message text preview",
83
+ chartAria: 'Chart series color preview',
84
+ navLabel: 'Appearance',
85
+ };
86
+
87
+ export const S = pick({ zh, en });
@@ -0,0 +1,11 @@
1
+ export {
2
+ configField,
3
+ createConfigView,
4
+ type ConfigField,
5
+ type ConfigGroup,
6
+ type ConfigGroupEntry,
7
+ type ConfigProperty,
8
+ type ConfigValue,
9
+ type ConfigView,
10
+ type ConfigViewDeps,
11
+ } from './view.ts';
@@ -0,0 +1,41 @@
1
+ import { pick } from '../../core/language.ts';
2
+
3
+ const zh = {
4
+ loading: '加载中…',
5
+ optionCurrent: '(当前)',
6
+ ownerPersona: 'Persona',
7
+ chooseFile: '选择文件',
8
+ chooseDirectory: '选择目录',
9
+ recommendedDir: (dir: string) => `推荐目录:${dir}`,
10
+ download: '下载',
11
+ on: '开启',
12
+ leaveBlank: '留空',
13
+ saving: '保存中…',
14
+ saveFailed: (err: string) => '失败: ' + err,
15
+ emptyDefault: "此页没有配置项。",
16
+ noSchema: "未提供配置项。",
17
+ restartWorld: '重启 World 生效',
18
+ restartProcess: '重启生效',
19
+ loadFailed: (err: string) => '配置项加载失败: ' + err,
20
+ };
21
+
22
+ const en: typeof zh = {
23
+ loading: 'Loading…',
24
+ optionCurrent: '(current)',
25
+ ownerPersona: 'Persona',
26
+ chooseFile: 'Choose file',
27
+ chooseDirectory: 'Choose directory',
28
+ recommendedDir: (dir: string) => `Recommended directory: ${dir}`,
29
+ download: 'Download',
30
+ on: 'On',
31
+ leaveBlank: 'Leave blank',
32
+ saving: 'Saving…',
33
+ saveFailed: (err: string) => 'Failed: ' + err,
34
+ emptyDefault: "No configuration fields on this page.",
35
+ noSchema: "No configuration fields provided.",
36
+ restartWorld: 'takes effect after World restart',
37
+ restartProcess: 'takes effect after restart',
38
+ loadFailed: (err: string) => 'Failed to load config: ' + err,
39
+ };
40
+
41
+ export const S = pick({ zh, en });