@asrunjs/asrun 0.0.0-stage → 0.2.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 (286) hide show
  1. package/.claude/commands/explore-case.md +15 -0
  2. package/.claude/commands/gen-spec.md +13 -0
  3. package/.claude/commands/heal-spec.md +23 -0
  4. package/.claude/commands/make-case.md +8 -0
  5. package/.claude/settings.json +5 -0
  6. package/.claude/skills/exploring-cases/SKILL.md +132 -0
  7. package/.claude/skills/exploring-cases/references/assertion-targets.md +103 -0
  8. package/.claude/skills/exploring-cases/references/handoff-card.js +128 -0
  9. package/.claude/skills/exploring-cases/references/open-questions.md +112 -0
  10. package/.claude/skills/exploring-cases/references/readiness.md +137 -0
  11. package/.claude/skills/exploring-cases/references/user-handoff.md +147 -0
  12. package/.claude/skills/generating-specs/SKILL.md +207 -0
  13. package/.claude/skills/generating-specs/references/conventions.md +148 -0
  14. package/.claude/skills/making-cases/SKILL.md +64 -0
  15. package/.claude/skills/making-cases/references/gaps.md +36 -0
  16. package/.claude/skills/making-cases/references/normalizing.md +38 -0
  17. package/.claude/skills/making-cases/references/splitting.md +28 -0
  18. package/.claude/skills/making-cases/references/writes-and-email.md +36 -0
  19. package/AGENTS.md +154 -0
  20. package/CLAUDE.md +1 -0
  21. package/LICENSE +21 -0
  22. package/README.md +410 -3
  23. package/bin/asrun.mjs +49 -0
  24. package/dist/lib/account-lock.d.ts +6 -0
  25. package/dist/lib/account-lock.js +52 -0
  26. package/dist/lib/assertions.d.ts +14 -0
  27. package/dist/lib/assertions.js +39 -0
  28. package/dist/lib/baselines.d.ts +43 -0
  29. package/dist/lib/baselines.js +108 -0
  30. package/dist/lib/case-id.d.ts +11 -0
  31. package/dist/lib/case-id.js +17 -0
  32. package/dist/lib/case-ops.d.ts +26 -0
  33. package/dist/lib/case-ops.js +110 -0
  34. package/dist/lib/cases.d.ts +29 -0
  35. package/dist/lib/cases.js +124 -0
  36. package/dist/lib/cli.d.ts +10 -0
  37. package/dist/lib/cli.js +18 -0
  38. package/dist/lib/downloads.d.ts +42 -0
  39. package/dist/lib/downloads.js +84 -0
  40. package/dist/lib/email.d.ts +10 -0
  41. package/dist/lib/email.js +19 -0
  42. package/dist/lib/ffmpeg.d.ts +8 -0
  43. package/dist/lib/ffmpeg.js +50 -0
  44. package/dist/lib/fixtures.d.ts +40 -0
  45. package/dist/lib/fixtures.js +149 -0
  46. package/dist/lib/node-cli.d.ts +11 -0
  47. package/dist/lib/node-cli.js +26 -0
  48. package/dist/lib/paths.d.ts +25 -0
  49. package/dist/lib/paths.js +88 -0
  50. package/dist/lib/pdf.d.ts +71 -0
  51. package/dist/lib/pdf.js +140 -0
  52. package/dist/lib/profiles.d.ts +38 -0
  53. package/dist/lib/profiles.js +121 -0
  54. package/dist/lib/ready.d.ts +36 -0
  55. package/dist/lib/ready.js +53 -0
  56. package/dist/lib/records.d.ts +33 -0
  57. package/dist/lib/records.js +61 -0
  58. package/dist/lib/run-filters.d.ts +19 -0
  59. package/dist/lib/run-filters.js +30 -0
  60. package/dist/lib/run-summary.d.ts +90 -0
  61. package/dist/lib/run-summary.js +35 -0
  62. package/dist/lib/session.d.ts +18 -0
  63. package/dist/lib/session.js +45 -0
  64. package/dist/lib/summary-reporter.d.ts +16 -0
  65. package/dist/lib/summary-reporter.js +146 -0
  66. package/dist/lib/targets.d.ts +27 -0
  67. package/dist/lib/targets.js +51 -0
  68. package/dist/lib/ui-build.d.ts +10 -0
  69. package/dist/lib/ui-build.js +30 -0
  70. package/dist/lib/values.d.ts +29 -0
  71. package/dist/lib/values.js +85 -0
  72. package/dist/lib/write-gate.d.ts +41 -0
  73. package/dist/lib/write-gate.js +129 -0
  74. package/dist/playwright.config.d.ts +2 -0
  75. package/dist/playwright.config.js +92 -0
  76. package/dist/scripts/build.d.ts +1 -0
  77. package/dist/scripts/build.js +20 -0
  78. package/dist/scripts/check-cases.d.ts +1 -0
  79. package/dist/scripts/check-cases.js +81 -0
  80. package/dist/scripts/cleanup.d.ts +1 -0
  81. package/dist/scripts/cleanup.js +25 -0
  82. package/dist/scripts/cli.d.ts +1 -0
  83. package/dist/scripts/cli.js +28 -0
  84. package/dist/scripts/doctor.d.ts +1 -0
  85. package/dist/scripts/doctor.js +185 -0
  86. package/dist/scripts/explore.d.ts +1 -0
  87. package/dist/scripts/explore.js +54 -0
  88. package/dist/scripts/go.mjs +188 -0
  89. package/dist/scripts/heal-evidence.mjs +143 -0
  90. package/dist/scripts/init.d.ts +16 -0
  91. package/dist/scripts/init.js +284 -0
  92. package/dist/scripts/lint-specs.d.ts +1 -0
  93. package/dist/scripts/lint-specs.js +138 -0
  94. package/dist/scripts/login.d.ts +1 -0
  95. package/dist/scripts/login.js +47 -0
  96. package/dist/scripts/new-case.d.ts +1 -0
  97. package/dist/scripts/new-case.js +19 -0
  98. package/dist/scripts/render-pdf.d.ts +1 -0
  99. package/dist/scripts/render-pdf.js +44 -0
  100. package/dist/scripts/report.d.ts +1 -0
  101. package/dist/scripts/report.js +14 -0
  102. package/dist/scripts/review-baselines.d.ts +1 -0
  103. package/dist/scripts/review-baselines.js +30 -0
  104. package/dist/scripts/run.d.ts +1 -0
  105. package/dist/scripts/run.js +423 -0
  106. package/dist/scripts/status.d.ts +1 -0
  107. package/dist/scripts/status.js +31 -0
  108. package/dist/scripts/ui-build.d.ts +1 -0
  109. package/dist/scripts/ui-build.js +7 -0
  110. package/dist/scripts/ui-dev.d.ts +1 -0
  111. package/dist/scripts/ui-dev.js +3 -0
  112. package/dist/scripts/ui.d.ts +1 -0
  113. package/dist/scripts/ui.js +13 -0
  114. package/dist/ui/dev.d.ts +1 -0
  115. package/dist/ui/dev.js +36 -0
  116. package/dist/ui/server/actions.d.ts +91 -0
  117. package/dist/ui/server/actions.js +352 -0
  118. package/dist/ui/server/ai-stream.d.ts +41 -0
  119. package/dist/ui/server/ai-stream.js +202 -0
  120. package/dist/ui/server/app.d.ts +4 -0
  121. package/dist/ui/server/app.js +333 -0
  122. package/dist/ui/server/cases.d.ts +23 -0
  123. package/dist/ui/server/cases.js +170 -0
  124. package/dist/ui/server/files.d.ts +5 -0
  125. package/dist/ui/server/files.js +67 -0
  126. package/dist/ui/server/index.d.ts +1 -0
  127. package/dist/ui/server/index.js +33 -0
  128. package/dist/ui/server/jobs.d.ts +74 -0
  129. package/dist/ui/server/jobs.js +263 -0
  130. package/dist/ui/server/profiles.d.ts +16 -0
  131. package/dist/ui/server/profiles.js +195 -0
  132. package/dist/ui/server/watch-files.d.ts +4 -0
  133. package/dist/ui/server/watch-files.js +36 -0
  134. package/dist/ui/shared/ai-commands.d.ts +78 -0
  135. package/dist/ui/shared/ai-commands.js +154 -0
  136. package/dist/ui/shared/case-id.d.ts +1 -0
  137. package/dist/ui/shared/case-id.js +1 -0
  138. package/dist/ui/shared/jobs.d.ts +6 -0
  139. package/dist/ui/shared/jobs.js +7 -0
  140. package/dist/ui/shared/product.d.ts +3 -0
  141. package/dist/ui/shared/product.js +3 -0
  142. package/dist/ui/shared/run-filters.d.ts +1 -0
  143. package/dist/ui/shared/run-filters.js +1 -0
  144. package/dist/ui/shared/site-address.d.ts +6 -0
  145. package/dist/ui/shared/site-address.js +11 -0
  146. package/dist/ui/shared/types.d.ts +257 -0
  147. package/dist/ui/shared/types.js +1 -0
  148. package/docs/theme-guide.html +692 -0
  149. package/package.json +98 -3
  150. package/templates/.claude/commands/explore-case.md +21 -0
  151. package/templates/.claude/commands/gen-spec.md +20 -0
  152. package/templates/.claude/commands/heal-spec.md +30 -0
  153. package/templates/.claude/commands/make-case.md +12 -0
  154. package/templates/.claude/settings.json +5 -0
  155. package/templates/.mcp.json +8 -0
  156. package/templates/AGENTS.md +45 -0
  157. package/templates/CLAUDE.md +1 -0
  158. package/templates/cases/_template.md +63 -0
  159. package/templates/gitignore +13 -0
  160. package/templates/opencode.jsonc +11 -0
  161. package/templates/package.json +31 -0
  162. package/templates/playwright.config.ts +3 -0
  163. package/templates/pnpm-workspace.yaml +5 -0
  164. package/templates/profiles.yaml +29 -0
  165. package/templates/seed.spec.ts +9 -0
  166. package/templates/tsconfig.json +14 -0
  167. package/ui/dist/apple-touch-icon.png +0 -0
  168. package/ui/dist/assets/apl-gfMMiTNf.js +1 -0
  169. package/ui/dist/assets/asciiarmor-C_eisQCp.js +1 -0
  170. package/ui/dist/assets/asn1-DCl_8MCW.js +1 -0
  171. package/ui/dist/assets/asterisk-BEn0Cwpx.js +1 -0
  172. package/ui/dist/assets/brainfuck-D5EjA2JK.js +1 -0
  173. package/ui/dist/assets/clike-xvONpD0Z.js +1 -0
  174. package/ui/dist/assets/clojure-BflJGmDX.js +1 -0
  175. package/ui/dist/assets/cmake-84nbDH0I.js +1 -0
  176. package/ui/dist/assets/cobol-DHLQUbN7.js +1 -0
  177. package/ui/dist/assets/coffeescript-De_zI4J0.js +1 -0
  178. package/ui/dist/assets/commonlisp-DgAKBe0r.js +1 -0
  179. package/ui/dist/assets/crystal-Bl6YKCG_.js +1 -0
  180. package/ui/dist/assets/css-CnaYoQib.js +1 -0
  181. package/ui/dist/assets/cypher-p9eYesGn.js +1 -0
  182. package/ui/dist/assets/d-DWkQyNnU.js +1 -0
  183. package/ui/dist/assets/diff-ChtP43wD.js +1 -0
  184. package/ui/dist/assets/dist-BHaIOZdW.js +1 -0
  185. package/ui/dist/assets/dist-BSJMM_jp.js +1 -0
  186. package/ui/dist/assets/dist-BiE4suo9.js +1 -0
  187. package/ui/dist/assets/dist-BqAROxzI.js +1 -0
  188. package/ui/dist/assets/dist-Bw5-uxs9.js +1 -0
  189. package/ui/dist/assets/dist-BwYRbe5s.js +9 -0
  190. package/ui/dist/assets/dist-BzulKuec.js +1 -0
  191. package/ui/dist/assets/dist-CBV7_vDN.js +2 -0
  192. package/ui/dist/assets/dist-CgIQYY1o.js +1 -0
  193. package/ui/dist/assets/dist-DDJNvivM.js +1 -0
  194. package/ui/dist/assets/dist-DF3F_I8N.js +1 -0
  195. package/ui/dist/assets/dist-DHCY9KpV.js +1 -0
  196. package/ui/dist/assets/dist-DX8knLVr.js +1 -0
  197. package/ui/dist/assets/dist-D_I45xgJ.js +11 -0
  198. package/ui/dist/assets/dist-Dlorc3Sx.js +1 -0
  199. package/ui/dist/assets/dist-Dr9crbBz.js +13 -0
  200. package/ui/dist/assets/dist-gF2bFn-5.js +1 -0
  201. package/ui/dist/assets/dockerfile-DJkHQgkl.js +1 -0
  202. package/ui/dist/assets/dtd-DTF72YNO.js +1 -0
  203. package/ui/dist/assets/dylan-S7ucR4pR.js +1 -0
  204. package/ui/dist/assets/ebnf-D4c0_ac3.js +1 -0
  205. package/ui/dist/assets/ecl-BVIeWmwY.js +1 -0
  206. package/ui/dist/assets/eiffel-BEjRio4Q.js +1 -0
  207. package/ui/dist/assets/elm-Cshzl8qu.js +1 -0
  208. package/ui/dist/assets/erlang-BV0c4Hdn.js +1 -0
  209. package/ui/dist/assets/factor-LKH5gN8L.js +1 -0
  210. package/ui/dist/assets/fcl-ClOhbFR7.js +1 -0
  211. package/ui/dist/assets/forth-D6RzAl64.js +1 -0
  212. package/ui/dist/assets/fortran-Bt6PBEDR.js +1 -0
  213. package/ui/dist/assets/gas-BHEdbvp9.js +1 -0
  214. package/ui/dist/assets/gherkin-oBAE_ms0.js +1 -0
  215. package/ui/dist/assets/groovy-CUrWKPia.js +1 -0
  216. package/ui/dist/assets/haskell-BRsoo5mP.js +1 -0
  217. package/ui/dist/assets/haxe-DjVOVvNP.js +1 -0
  218. package/ui/dist/assets/http-CLVgA2GD.js +1 -0
  219. package/ui/dist/assets/idl-BUZw3wgd.js +1 -0
  220. package/ui/dist/assets/index-DmasQ6Rq.css +2 -0
  221. package/ui/dist/assets/index-I7uU93kk.js +294 -0
  222. package/ui/dist/assets/javascript-lS2r2ifg.js +1 -0
  223. package/ui/dist/assets/julia-CQfgDRfP.js +1 -0
  224. package/ui/dist/assets/livescript-BXxG0Yva.js +1 -0
  225. package/ui/dist/assets/lua-DPCEkfD_.js +1 -0
  226. package/ui/dist/assets/mathematica-u8YMmbU0.js +1 -0
  227. package/ui/dist/assets/mbox-BEMVcqjs.js +1 -0
  228. package/ui/dist/assets/mirc-C9zpzAZU.js +1 -0
  229. package/ui/dist/assets/mllike-B45xgp2S.js +1 -0
  230. package/ui/dist/assets/modelica-CqHI_q-D.js +1 -0
  231. package/ui/dist/assets/mscgen-qgSLujhx.js +1 -0
  232. package/ui/dist/assets/mumps-BSzj6Xyz.js +1 -0
  233. package/ui/dist/assets/nginx-C-ORUuei.js +1 -0
  234. package/ui/dist/assets/nsis-viPvXFiq.js +1 -0
  235. package/ui/dist/assets/ntriples-CzMBulkp.js +1 -0
  236. package/ui/dist/assets/octave-D2Q8cUp1.js +1 -0
  237. package/ui/dist/assets/oz-BwTct_5T.js +1 -0
  238. package/ui/dist/assets/pascal-Dp_KdFgX.js +1 -0
  239. package/ui/dist/assets/perl-BkSkiI9m.js +1 -0
  240. package/ui/dist/assets/pig-AF4Hwhju.js +1 -0
  241. package/ui/dist/assets/powershell-D8WpQPaX.js +1 -0
  242. package/ui/dist/assets/properties-C357ku5U.js +1 -0
  243. package/ui/dist/assets/protobuf-D1ij_kNL.js +1 -0
  244. package/ui/dist/assets/pug-BqCHZ5xT.js +1 -0
  245. package/ui/dist/assets/puppet-BZyAS7I4.js +1 -0
  246. package/ui/dist/assets/python-B_1Jd8HM.js +1 -0
  247. package/ui/dist/assets/q-B9NhS7L_.js +1 -0
  248. package/ui/dist/assets/r-CxDmU1I4.js +1 -0
  249. package/ui/dist/assets/rpm-gpDK5sbt.js +1 -0
  250. package/ui/dist/assets/ruby-ChfNwlCY.js +1 -0
  251. package/ui/dist/assets/sas-C2OyD2Y6.js +1 -0
  252. package/ui/dist/assets/scheme-D9qYN0V5.js +1 -0
  253. package/ui/dist/assets/shell-DwuoZtxw.js +1 -0
  254. package/ui/dist/assets/sieve-tLWDfEkZ.js +1 -0
  255. package/ui/dist/assets/simple-mode-DXiLM1-G.js +1 -0
  256. package/ui/dist/assets/smalltalk-BLp5-jwC.js +1 -0
  257. package/ui/dist/assets/solr-B78wSQJQ.js +1 -0
  258. package/ui/dist/assets/sparql-Dz-mlCAC.js +1 -0
  259. package/ui/dist/assets/spreadsheet-CTIncYxj.js +1 -0
  260. package/ui/dist/assets/sql-DuQMFDvy.js +1 -0
  261. package/ui/dist/assets/stex-DNzd62Q_.js +1 -0
  262. package/ui/dist/assets/stylus-BOVgKnng.js +1 -0
  263. package/ui/dist/assets/swift-DKB6_1j6.js +1 -0
  264. package/ui/dist/assets/tcl-BGfruO1u.js +1 -0
  265. package/ui/dist/assets/textile-C4jVoHG1.js +1 -0
  266. package/ui/dist/assets/tiddlywiki-VlUiK86J.js +1 -0
  267. package/ui/dist/assets/tiki-CvUEFusT.js +1 -0
  268. package/ui/dist/assets/toml-BPTmHmyx.js +1 -0
  269. package/ui/dist/assets/troff-C2dAgh7P.js +1 -0
  270. package/ui/dist/assets/ttcn-PPSTk1ow.js +1 -0
  271. package/ui/dist/assets/ttcn-cfg-2b4Gb5sE.js +1 -0
  272. package/ui/dist/assets/turtle-Cx3rYX2g.js +1 -0
  273. package/ui/dist/assets/vb-BthdM_4N.js +1 -0
  274. package/ui/dist/assets/vbscript-D9f6rSsU.js +1 -0
  275. package/ui/dist/assets/velocity-CqftRsjg.js +1 -0
  276. package/ui/dist/assets/verilog-CjI9Y6RI.js +1 -0
  277. package/ui/dist/assets/vhdl-kS471soi.js +1 -0
  278. package/ui/dist/assets/webidl-D5jiJvOU.js +1 -0
  279. package/ui/dist/assets/xquery-BKJx4mf9.js +1 -0
  280. package/ui/dist/assets/yacas-CXXgtw0p.js +1 -0
  281. package/ui/dist/assets/z80-Co3PbHdE.js +1 -0
  282. package/ui/dist/favicon.ico +0 -0
  283. package/ui/dist/favicon.svg +11 -0
  284. package/ui/dist/index.html +17 -0
  285. package/ui/dist/logos/claude.svg +1 -0
  286. package/ui/dist/logos/opencode.svg +1 -0
@@ -0,0 +1,692 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Inkstone theme guide · AsRun UI</title>
7
+ <style>
8
+ /* Inkstone theme, designed and created by Zoro Chen. */
9
+ /* Tokens copied from ui/web/src/index.css. Keep both in sync. */
10
+ :root {
11
+ --paper: #f6f2e9;
12
+ --card: #ffffff;
13
+ --warm: #fcf9f3;
14
+ --ink: #1f1f1f;
15
+ --ink-2: #6b6b6b;
16
+ --ink-3: #9c9c9c;
17
+ --line: #e5e1d8;
18
+ --line-mid: #dcd7cb;
19
+
20
+ --seal: #b5443a;
21
+ --seal-deep: #8a2f28;
22
+ --seal-bg: #f1dad7;
23
+ --seal-soft: #e8a79f;
24
+ --seal-glow: #ff7b6e;
25
+
26
+ --gold: #c9a54a;
27
+ --gold-deep: #7a5a1e;
28
+ --gold-bg: #f4e9c9;
29
+ --gold-glow: #ffd23f;
30
+ --warn-ink: #6e5b24;
31
+
32
+ --qing: #5b8aa6;
33
+ --qing-deep: #33506b;
34
+ --qing-bg: #e2eaf0;
35
+ --qing-glow: #4fc3ff;
36
+
37
+ --jade: #4da791;
38
+ --jade-deep: #3a8572;
39
+ --jade-btn: #2e6b5a;
40
+ --jade-bg: #e0e8d5;
41
+ --jade-glow: #3de0b0;
42
+
43
+ --jiang: #a65998;
44
+
45
+ --cheng: #de8435;
46
+
47
+ --dai: #2e4a5c;
48
+ --dai-deep: #1e3340;
49
+ --dai-bg: #e2ecef;
50
+
51
+ --serif: 'Iowan Old Style', Georgia, 'Songti SC', 'Noto Serif SC', 'SimSun', serif;
52
+ --sans: 'PingFang SC', 'Noto Sans SC', 'Microsoft YaHei', ui-sans-serif, system-ui, sans-serif;
53
+ --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
54
+
55
+ --theme: var(--jade);
56
+ --theme-ink: #2e6b5a;
57
+ --theme-btn: #2e6b5a;
58
+ --theme-bg: #e0e8d5;
59
+ }
60
+
61
+ * { box-sizing: border-box; }
62
+ html { scroll-behavior: smooth; }
63
+ body {
64
+ margin: 0;
65
+ background: var(--paper);
66
+ color: var(--ink);
67
+ font-family: var(--sans);
68
+ line-height: 1.6;
69
+ -webkit-font-smoothing: antialiased;
70
+ }
71
+ a { color: var(--qing-deep); }
72
+ code, .mono { font-family: var(--mono); font-size: 0.85em; }
73
+ code { background: var(--warm); border: 1px solid var(--line); border-radius: 3px; padding: 1px 5px; }
74
+
75
+ .layout { display: grid; grid-template-columns: 220px minmax(0, 1fr); max-width: 1100px; margin: 0 auto; }
76
+ nav.toc {
77
+ position: sticky; top: 0; align-self: start; height: 100vh; overflow: auto;
78
+ padding: 32px 20px; border-right: 1px solid var(--line-mid);
79
+ }
80
+ nav.toc .brand { font-family: var(--serif); font-weight: 700; letter-spacing: 0.1em; margin-bottom: 16px; }
81
+ nav.toc a {
82
+ display: block; padding: 4px 8px; margin: 1px 0; border-radius: 4px;
83
+ color: var(--ink-2); text-decoration: none; font-size: 14px;
84
+ }
85
+ nav.toc a:hover { background: var(--warm); color: var(--ink); }
86
+ main { padding: 32px 40px 80px; min-width: 0; }
87
+ @media (max-width: 800px) {
88
+ .layout { display: block; }
89
+ nav.toc { position: static; height: auto; border-right: 0; border-bottom: 1px solid var(--line-mid); }
90
+ main { padding: 24px 20px 60px; }
91
+ }
92
+
93
+ h1, h2, h3 { font-family: var(--serif); }
94
+ h1 { font-size: 30px; margin: 0 0 4px; border-left: 4px solid var(--jade); padding-left: 12px; }
95
+ h2 { font-size: 22px; margin: 48px 0 8px; padding-bottom: 6px; border-bottom: 1px solid var(--line-mid); }
96
+ h3 { font-size: 16px; margin: 24px 0 8px; }
97
+ p { margin: 8px 0; }
98
+ .author { margin: 0 0 12px; padding-left: 16px; }
99
+ .lead { color: var(--ink-2); margin-bottom: 24px; }
100
+ .muted { color: var(--ink-2); }
101
+ .small { font-size: 13px; }
102
+
103
+ .card {
104
+ background:
105
+ radial-gradient(120% 100% at 16% -8%, color-mix(in srgb, var(--theme) 9%, white) 0%, transparent 58%),
106
+ var(--card);
107
+ border: 1px solid var(--line);
108
+ border-radius: 4px;
109
+ box-shadow:
110
+ 0 1px 2px color-mix(in srgb, var(--theme) 16%, transparent),
111
+ 0 8px 20px -10px color-mix(in srgb, var(--theme) 34%, transparent);
112
+ padding: 16px;
113
+ }
114
+
115
+ table { width: 100%; border-collapse: collapse; font-size: 14px; background: var(--card); border: 1px solid var(--line); }
116
+ th, td { text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--line); vertical-align: top; }
117
+ th { background: var(--warm); font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
118
+
119
+ .grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
120
+ .swatch { border: 1px solid var(--line-mid); border-radius: 4px; overflow: hidden; background: var(--card); font-size: 12px; }
121
+ .swatch .chip { height: 52px; }
122
+ .swatch .meta { padding: 6px 8px; }
123
+ .swatch .meta b { display: block; font-size: 13px; }
124
+ .swatch .meta span { color: var(--ink-2); font-family: var(--mono); }
125
+
126
+ .row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin: 12px 0; }
127
+ .demo { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 16px; background: var(--paper); border: 1px dashed var(--line-mid); border-radius: 4px; }
128
+ .label { font-size: 12px; color: var(--ink-2); font-family: var(--mono); }
129
+
130
+ /* Buttons (mirror buttonClass in ui/web/src/components/status.tsx). */
131
+ .btn {
132
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
133
+ border: 0; border-radius: 4px; padding: 6px 12px; font: 500 14px var(--sans); cursor: pointer;
134
+ transition: transform .15s, filter .15s, box-shadow .15s, background .15s;
135
+ }
136
+ .btn:hover { transform: translateY(-1px); }
137
+ .btn[disabled] { opacity: .5; cursor: not-allowed; box-shadow: none; transform: none; }
138
+ .btn svg { flex: none; }
139
+ .btn-primary, .btn-success {
140
+ color: #fff;
141
+ background: var(--theme-btn);
142
+ box-shadow: inset 0 1px 0 rgb(255 255 255 / .18), 0 3px 8px -2px color-mix(in srgb, var(--theme-btn) 50%, transparent);
143
+ }
144
+ .btn-success { background: var(--jade-btn); box-shadow: inset 0 1px 0 rgb(255 255 255 / .18), 0 3px 8px -2px color-mix(in srgb, var(--jade-btn) 50%, transparent); }
145
+ .btn-primary:hover, .btn-success:hover { filter: brightness(1.1); }
146
+ .btn-stop { color: #fff; background: var(--seal); box-shadow: inset 0 0 0 0 transparent, 0 3px 8px -2px color-mix(in srgb, var(--seal) 50%, transparent); }
147
+ .btn-stop:hover { filter: brightness(1.1); }
148
+ .btn-secondary { background: var(--card); color: var(--ink); box-shadow: 0 0 0 1px var(--line-mid); }
149
+ .btn-secondary:hover { background: var(--warm); }
150
+ .btn-info { background: var(--qing-bg); color: var(--qing-deep); box-shadow: 0 0 0 1px color-mix(in srgb, var(--qing) 50%, transparent); }
151
+ .btn-info:hover { filter: brightness(.95); }
152
+ .btn-danger { background: var(--card); color: var(--seal-deep); box-shadow: 0 0 0 1px color-mix(in srgb, var(--seal) 40%, transparent); }
153
+ .btn-danger:hover { background: var(--seal-bg); }
154
+
155
+ /* Badges (mirror TONES in status.tsx). */
156
+ .badge { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 2px; font-size: 12px; font-weight: 500; }
157
+ .badge.green { background: var(--jade-bg); color: var(--jade-btn); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--jade-deep) 30%, transparent); }
158
+ .badge.red { background: var(--seal-bg); color: var(--seal-deep); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--seal) 35%, transparent); }
159
+ .badge.amber { background: var(--gold-bg); color: var(--gold-deep); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold-deep) 30%, transparent); }
160
+ .badge.blue { background: var(--qing-bg); color: var(--qing-deep); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--qing) 40%, transparent); }
161
+ .badge.gray { background: var(--warm); color: var(--ink-2); box-shadow: inset 0 0 0 1px var(--line-mid); }
162
+ /* Environment markers (mirror envFillClass in status.tsx): solid fill, white text. */
163
+ .env { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 2px; font-size: 12px; font-weight: 700; letter-spacing: 0.05em; color: #fff; }
164
+ .env.production { background: var(--jiang); }
165
+ .env.staging { background: var(--cheng); }
166
+ .env.other { background: var(--gold-deep); }
167
+
168
+ /* AI command select (mirror AI_COMMAND_STYLE in ui/web/src/pages/cases.tsx). */
169
+ .cmd { position: relative; display: inline-block; }
170
+ .cmd .dot { position: absolute; left: 12px; top: 50%; width: 8px; height: 8px; border-radius: 50%; transform: translateY(-50%); }
171
+ .cmd .fake {
172
+ display: inline-flex; align-items: center; height: 32px; padding: 0 12px 0 28px; border: 1px solid;
173
+ border-radius: 4px; font-size: 14px; font-weight: 500;
174
+ }
175
+ .cmd.review .fake { border-color: color-mix(in srgb, var(--qing) 50%, transparent); background: var(--qing-bg); color: var(--qing-deep); }
176
+ .cmd.review .dot { background: var(--qing); }
177
+ .cmd.gen .fake { border-color: color-mix(in srgb, var(--gold-deep) 40%, transparent); background: var(--gold-bg); color: var(--gold-deep); }
178
+ .cmd.gen .dot { background: var(--gold); }
179
+ .cmd.heal .fake { border-color: color-mix(in srgb, var(--seal) 40%, transparent); background: var(--seal-bg); color: var(--seal-deep); }
180
+ .cmd.heal .dot { background: var(--seal); }
181
+
182
+ /* Dropdown demo (mirror the Select component in ui/web/src/components/status.tsx). */
183
+ .select { position: relative; display: inline-block; }
184
+ .select .fake {
185
+ display: inline-flex; align-items: center; height: 32px; min-width: 120px; padding: 0 32px 0 8px; border: 1px solid var(--line-mid);
186
+ border-radius: 4px; background: #fff; color: var(--ink); font-size: 14px;
187
+ }
188
+ .select .chev { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); color: var(--ink-2); pointer-events: none; }
189
+ .select.focus .fake { border-color: var(--theme-demo, #5b8aa6); box-shadow: 0 0 0 2px color-mix(in srgb, var(--theme-demo, #5b8aa6) 25%, transparent); }
190
+ .select.invalid .fake { border-color: var(--seal); background: var(--seal-bg); color: var(--seal-deep); }
191
+ .select.invalid .chev { color: var(--seal-deep); }
192
+ .select.dotted .fake { padding-left: 28px; font-weight: 500; }
193
+ .select .dot { position: absolute; left: 12px; top: 50%; width: 8px; height: 8px; border-radius: 50%; transform: translateY(-50%); background: var(--gold); }
194
+ .select.dotted .fake { border-color: color-mix(in srgb, var(--gold-deep) 40%, transparent); background: var(--gold-bg); color: var(--gold-deep); }
195
+ .select-label { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; color: var(--ink-2); }
196
+ .select-label.invalid { color: var(--seal-deep); }
197
+
198
+ /* Sidebar sections (mirror SectionHeader, CaseSearch and the case list in ui/web/src/pages/cases.tsx). */
199
+ .side { width: 300px; background: var(--card); border: 1px solid var(--line-mid); }
200
+ .side .sec {
201
+ display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--theme-btn); color: #fff; font-family: var(--serif);
202
+ font-size: 14px; font-weight: 700; letter-spacing: 0.12em; border-top: 1px solid var(--theme-btn); border-bottom: 1px solid var(--theme-btn);
203
+ }
204
+ .side .sec svg { flex: none; }
205
+ .side .sec .aside { margin-left: auto; font-family: var(--sans); font-size: 12px; font-weight: 400; letter-spacing: 0; color: rgb(255 255 255 / .75); }
206
+ .side .sec .toggle { margin-left: auto; display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 4px; background: rgb(255 255 255 / .15); flex: none; }
207
+ .side .sec .aside + .toggle { margin-left: 0; }
208
+ .side .search { position: relative; padding: 8px; border-bottom: 1px solid var(--line-mid); }
209
+ .side .search svg { position: absolute; left: 20px; top: 50%; transform: translateY(-50%); color: var(--ink-3); }
210
+ .side .search .fake {
211
+ display: block; padding: 6px 8px 6px 32px; border: 1px solid var(--line-mid); border-radius: 4px; background: #fff;
212
+ color: var(--ink-3); font-size: 14px;
213
+ }
214
+ .side .folder { display: flex; align-items: center; gap: 8px; padding: 6px 12px; background: var(--warm); border-bottom: 1px solid var(--line); font-size: 12px; font-weight: 500; color: var(--ink-2); }
215
+ .side .item { padding: 8px 16px 8px 12px; border-bottom: 1px solid var(--line); border-left: 4px solid transparent; font-family: var(--mono); font-size: 14px; }
216
+ .side .item.selected { background: var(--theme-bg); border-left-color: var(--theme); }
217
+
218
+ .seal-stamp {
219
+ display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 3px;
220
+ background: var(--seal); color: var(--paper); font-family: var(--serif); font-weight: 700; font-size: 16px; line-height: 1;
221
+ box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--paper) 32%, transparent), 0 3px 8px -2px color-mix(in srgb, var(--seal) 55%, transparent);
222
+ transform: rotate(-3deg);
223
+ }
224
+ .theme-demo { padding: 12px 16px; border-radius: 4px; }
225
+ .nav-pill { display: inline-block; padding: 6px 12px; border-radius: 4px; font-size: 14px; font-weight: 500; background: var(--theme-bg); color: var(--theme-ink); position: relative; }
226
+ .nav-pill::after { content: ''; position: absolute; left: 12px; right: 12px; bottom: -7px; height: 2px; background: var(--theme); }
227
+
228
+ /* Markdown editor sample (mirror the .inkstone-editor rules in ui/web/src/index.css). */
229
+ .editor-demo { background: var(--warm); border: 1px solid var(--line-mid); border-radius: 4px; padding: 12px 16px; font: 13px/1.7 var(--mono); }
230
+ .editor-demo .ed-h { color: var(--seal-deep); font-weight: 700; }
231
+ .editor-demo .ed-mark { color: var(--ink-3); }
232
+ .editor-demo .ed-list { color: var(--gold-deep); font-weight: 700; }
233
+ .editor-demo .ed-prop { color: var(--qing-deep); }
234
+ .editor-demo .ed-punct { color: var(--ink-2); }
235
+ .editor-demo .ed-code { color: var(--jade-btn); background: color-mix(in srgb, var(--jade-bg) 70%, transparent); border-radius: 2px; }
236
+ .editor-demo .ed-url { color: var(--ink-3); text-decoration: underline; }
237
+ .editor-demo .ed-link { color: var(--qing-deep); }
238
+ .editor-demo .ed-quote { color: var(--ink-2); font-style: italic; }
239
+ .editor-demo .ed-active { background: color-mix(in srgb, var(--theme-bg) 40%, transparent); margin: 0 -16px; padding: 0 16px; }
240
+
241
+ .rule { border-left: 3px solid var(--gold); background: var(--gold-bg); color: var(--warn-ink); padding: 8px 12px; border-radius: 0 4px 4px 0; font-size: 14px; }
242
+ ul.tight { margin: 6px 0; padding-left: 20px; }
243
+ ul.tight li { margin: 2px 0; }
244
+ footer { margin-top: 64px; color: var(--ink-3); font-size: 12px; }
245
+ footer .creator { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px; margin-top: 8px; }
246
+ footer .creator span, footer .creator a { display: inline-flex; align-items: center; gap: 4px; color: inherit; text-decoration: none; }
247
+ footer .creator a:hover { color: var(--ink); }
248
+ </style>
249
+ </head>
250
+ <body>
251
+ <div class="layout">
252
+ <nav class="toc" aria-label="Contents">
253
+ <div class="brand">Inkstone</div>
254
+ <div class="small muted" style="margin:-10px 0 16px">AsRun UI theme</div>
255
+ <a href="#principles">Principles</a>
256
+ <a href="#palette">Color palette</a>
257
+ <a href="#page-themes">Page themes</a>
258
+ <a href="#typography">Typography</a>
259
+ <a href="#buttons">Buttons</a>
260
+ <a href="#badges">Badges</a>
261
+ <a href="#action-colors">Action colors</a>
262
+ <a href="#dropdowns">Dropdowns</a>
263
+ <a href="#icons">Icons</a>
264
+ <a href="#tooltips">Tooltips</a>
265
+ <a href="#editor">Markdown editor</a>
266
+ <a href="#surfaces">Surfaces</a>
267
+ <a href="#sections">Sidebar sections</a>
268
+ <a href="#checklist">Checklist</a>
269
+ </nav>
270
+
271
+ <main>
272
+ <h1>Inkstone theme guide</h1>
273
+ <p class="author small muted"><b>Inkstone</b> theme, designed and created by <b>Zoro Chen</b>.</p>
274
+ <p class="lead">
275
+ Inkstone is the look of the local web UI (<code>pnpm ui</code>): a rice-paper base, ink text and mineral pigments as accents.
276
+ Source of truth: <code>ui/web/src/index.css</code> (tokens) and <code>ui/web/src/components/status.tsx</code>
277
+ (buttons, badges). This page is static and has no dependencies; open it in any browser.
278
+ </p>
279
+
280
+ <h2 id="principles">Principles</h2>
281
+ <ul class="tight">
282
+ <li><b>Only the tokens below.</b> The default Tailwind palette is removed on purpose (<code>--color-*: initial</code>), so an unknown color class simply does nothing.</li>
283
+ <li><b>Color means impact.</b> Green = go / safe, blue = read-only or informational, amber = writes or needs attention, red = destructive, failed or rewrites existing data.</li>
284
+ <li><b>One accent per page.</b> Each top-level page sets <code>data-theme</code>; decorations, shadows and primary buttons follow it.</li>
285
+ <li><b>Text-safe pairs.</b> Use <code>-deep</code> / <code>-ink</code> for text, <code>-bg</code> for tinted backgrounds, <code>-btn</code> for fills that carry white text.</li>
286
+ </ul>
287
+
288
+ <h2 id="palette">Color palette</h2>
289
+ <h3>Base</h3>
290
+ <div class="grid">
291
+ <div class="swatch"><div class="chip" style="background:#f6f2e9"></div><div class="meta"><b>paper</b><span>#f6f2e9</span><br />page background</div></div>
292
+ <div class="swatch"><div class="chip" style="background:#ffffff"></div><div class="meta"><b>card</b><span>#ffffff</span><br />cards, inputs</div></div>
293
+ <div class="swatch"><div class="chip" style="background:#fcf9f3"></div><div class="meta"><b>warm</b><span>#fcf9f3</span><br />table heads, hover</div></div>
294
+ <div class="swatch"><div class="chip" style="background:#1f1f1f"></div><div class="meta"><b>ink</b><span>#1f1f1f</span><br />primary text</div></div>
295
+ <div class="swatch"><div class="chip" style="background:#6b6b6b"></div><div class="meta"><b>ink-2</b><span>#6b6b6b</span><br />secondary text</div></div>
296
+ <div class="swatch"><div class="chip" style="background:#9c9c9c"></div><div class="meta"><b>ink-3</b><span>#9c9c9c</span><br />placeholders</div></div>
297
+ <div class="swatch"><div class="chip" style="background:#e5e1d8"></div><div class="meta"><b>line</b><span>#e5e1d8</span><br />default borders</div></div>
298
+ <div class="swatch"><div class="chip" style="background:#dcd7cb"></div><div class="meta"><b>line-mid</b><span>#dcd7cb</span><br />stronger borders</div></div>
299
+ </div>
300
+
301
+ <h3>Pigments (semantic)</h3>
302
+ <table>
303
+ <thead><tr><th>Family</th><th>Tokens</th><th>Meaning</th></tr></thead>
304
+ <tbody>
305
+ <tr>
306
+ <td><b>jade</b> (green)</td>
307
+ <td>
308
+ <span class="badge green">jade-bg #e0e8d5</span>
309
+ <code>jade #4da791</code> <code>jade-deep #3a8572</code> <code>jade-btn #2e6b5a</code> <code>jade-glow #3de0b0</code>
310
+ </td>
311
+ <td>Success, logged in, Run buttons</td>
312
+ </tr>
313
+ <tr>
314
+ <td><b>qing</b> (blue)</td>
315
+ <td>
316
+ <span class="badge blue">qing-bg #e2eaf0</span>
317
+ <code>qing #5b8aa6</code> <code>qing-deep #33506b</code> <code>qing-glow #4fc3ff</code>
318
+ </td>
319
+ <td>Info, running, read-only actions</td>
320
+ </tr>
321
+ <tr>
322
+ <td><b>gold</b> (amber)</td>
323
+ <td>
324
+ <span class="badge amber">gold-bg #f4e9c9</span>
325
+ <code>gold #c9a54a</code> <code>gold-deep #7a5a1e</code> <code>gold-glow #ffd23f</code> <code>warn-ink #6e5b24</code>
326
+ </td>
327
+ <td>Warnings, unverified, actions that write files</td>
328
+ </tr>
329
+ <tr>
330
+ <td><b>seal</b> (red)</td>
331
+ <td>
332
+ <span class="badge red">seal-bg #f1dad7</span>
333
+ <code>seal #b5443a</code> <code>seal-deep #8a2f28</code> <code>seal-soft #e8a79f</code> <code>seal-glow #ff7b6e</code>
334
+ </td>
335
+ <td>Errors, expired sessions, cancel, actions that rewrite files</td>
336
+ </tr>
337
+ <tr>
338
+ <td><b>jiang</b> (purple)</td>
339
+ <td>
340
+ <code>jiang #a65998</code>
341
+ </td>
342
+ <td>The <b>production</b> environment marker only (white text, 4.7:1 contrast). Not red on purpose: production must not look like an error.</td>
343
+ </tr>
344
+ <tr>
345
+ <td><b>cheng</b> (orange)</td>
346
+ <td>
347
+ <code>cheng #de8435</code>
348
+ </td>
349
+ <td>The <b>staging</b> environment marker only (white text). Not amber (gold) on purpose: gold means warnings and writes.</td>
350
+ </tr>
351
+ <tr>
352
+ <td><b>dai</b> (deep teal)</td>
353
+ <td>
354
+ <span class="badge gray" style="background:#e2ecef;color:#1e3340">dai-bg #e2ecef</span>
355
+ <code>dai #2e4a5c</code> <code>dai-deep #1e3340</code>
356
+ </td>
357
+ <td>Reports page accent, code blocks, modal scrims</td>
358
+ </tr>
359
+ </tbody>
360
+ </table>
361
+
362
+ <h2 id="page-themes">Page themes</h2>
363
+ <p>
364
+ <code>Layout</code> picks <code>data-theme</code> from the route. The theme drives
365
+ <code>--theme</code>, <code>--theme-ink</code>, <code>--theme-btn</code>, <code>--theme-bg</code>
366
+ (and a neighbouring hue <code>--theme-adj</code> for the card gradient). Use the Tailwind tokens
367
+ <code>text-theme-ink</code>, <code>bg-theme-btn</code>, <code>bg-theme-bg</code>, <code>border-theme</code>.
368
+ </p>
369
+ <table>
370
+ <thead><tr><th>Page</th><th><code>data-theme</code></th><th>Accent</th><th>Preview</th></tr></thead>
371
+ <tbody>
372
+ <tr><td>Profiles</td><td><code>jade</code></td><td>#4da791</td>
373
+ <td><span class="nav-pill" style="--theme:#4da791;--theme-ink:#2e6b5a;--theme-bg:#e0e8d5">Profiles</span></td></tr>
374
+ <tr><td>Cases</td><td><code>qing</code></td><td>#5b8aa6</td>
375
+ <td><span class="nav-pill" style="--theme:#5b8aa6;--theme-ink:#33506b;--theme-bg:#e2eaf0">Cases</span></td></tr>
376
+ <tr><td>Run tests</td><td><code>seal</code></td><td>#b5443a</td>
377
+ <td><span class="nav-pill" style="--theme:#b5443a;--theme-ink:#8a2f28;--theme-bg:#f1dad7">Run tests</span></td></tr>
378
+ <tr><td>Reports</td><td><code>dai</code></td><td>#2e4a5c</td>
379
+ <td><span class="nav-pill" style="--theme:#2e4a5c;--theme-ink:#1e3340;--theme-bg:#e2ecef">Reports</span></td></tr>
380
+ <tr><td>AI guide</td><td><code>gold</code></td><td>#c9a54a</td>
381
+ <td><span class="nav-pill" style="--theme:#c9a54a;--theme-ink:#7a5a1e;--theme-bg:#f4e9c9">AI guide</span></td></tr>
382
+ <tr><td>Help (<code>?</code> icon, no label)</td><td><code>qing</code></td><td>#5b8aa6</td>
383
+ <td><span class="nav-pill" style="--theme:#5b8aa6;--theme-ink:#33506b;--theme-bg:#e2eaf0">?</span></td></tr>
384
+ </tbody>
385
+ </table>
386
+ <p class="small muted">
387
+ The page theme only decorates. It must not carry meaning: a green Run button stays green on the red "Run tests" page.
388
+ To add a page: add a nav entry with a theme in <code>layout.tsx</code>; for a new accent also add a
389
+ <code>[data-theme='name']</code> block in <code>index.css</code>.
390
+ </p>
391
+
392
+ <h2 id="typography">Typography</h2>
393
+ <div class="card">
394
+ <p style="font-family:var(--serif);font-size:24px;font-weight:700;margin:0">Serif: headings, brand name, eyebrows</p>
395
+ <p class="mono small muted" style="margin:2px 0 12px">Iowan Old Style, Georgia, Songti SC, Noto Serif SC, SimSun</p>
396
+ <p style="margin:0">Sans: body text, buttons, tables, forms. The quick brown fox jumps over the lazy dog.</p>
397
+ <p class="mono small muted" style="margin:2px 0 12px">PingFang SC, Noto Sans SC, Microsoft YaHei, system-ui</p>
398
+ <p class="mono" style="margin:0">Mono: commands, ids, paths, job output (pnpm asrun --profile example-production)</p>
399
+ </div>
400
+ <p class="small muted">Page titles: serif, bold, <code>text-2xl</code>, with a 4px accent bar on the left (<code>border-l-4 border-theme pl-3</code>).</p>
401
+
402
+ <h2 id="buttons">Buttons</h2>
403
+ <p>Use <code>buttonClass(variant)</code> from <code>status.tsx</code>. Never hand-style a button.</p>
404
+ <div class="demo">
405
+ <button class="btn btn-success">
406
+ <!-- Play icon from Feather (https://feathericons.com/) -->
407
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
408
+ Run tests
409
+ </button>
410
+ <button class="btn btn-success">
411
+ <!-- Send icon from Feather (https://feathericons.com/) -->
412
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
413
+ Run AI
414
+ </button>
415
+ <button class="btn btn-stop">
416
+ <!-- XCircle icon from Feather (https://feathericons.com/) -->
417
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>
418
+ Close browser
419
+ </button>
420
+ <button class="btn btn-primary">Log in</button>
421
+ <button class="btn btn-secondary">Verify</button>
422
+ <button class="btn btn-info">Full screen</button>
423
+ <button class="btn btn-danger">Cancel</button>
424
+ <button class="btn btn-secondary" disabled>Disabled</button>
425
+ </div>
426
+ <table>
427
+ <thead><tr><th>Variant</th><th>Use for</th></tr></thead>
428
+ <tbody>
429
+ <tr><td><code>success</code> (green)</td><td>Starting a run. Starting tests ("Run tests" on the Run page) always has the Feather <code>Play</code> icon. Sending a command to an AI tool or a shell job ("Run AI" in the case toolbar, "Make cases") always has the Feather <code>Send</code> icon. The icon goes on the left; never use <code>Play</code> for an AI command.</td></tr>
430
+ <tr><td><code>stop</code> (solid red)</td><td>Forcibly ends something that is running, shown in place of the button that started it: "Close browser" on the Profiles page. Always with the Feather <code>XCircle</code> icon.</td></tr>
431
+ <tr><td><code>primary</code> (page accent)</td><td>The main call to action of a page that is not a run: Log in, Save.</td></tr>
432
+ <tr><td><code>info</code> (light blue)</td><td>A view toggle that must be easy to find, not an action on data: "Full screen" / "Restore" in the Jobs drawer bar. Blue = read-only.</td></tr>
433
+ <tr><td><code>secondary</code> (white)</td><td>Everything else: Verify, Visit, Discard, Copy, navigation.</td></tr>
434
+ <tr><td><code>danger</code> (white, red text)</td><td>Cancel a running job, destructive confirmations.</td></tr>
435
+ </tbody>
436
+ </table>
437
+ <p class="small muted">Hover lifts the button 1px; disabled is 50% opacity with no shadow. With an icon, add <code>gap-1.5</code> to the button.</p>
438
+
439
+ <h2 id="badges">Badges</h2>
440
+ <div class="demo">
441
+ <span class="badge green">Logged in</span>
442
+ <span class="badge red">Session expired</span>
443
+ <span class="badge amber">Saved, not verified</span>
444
+ <span class="badge blue">Running</span>
445
+ <span class="badge amber">Needs your input</span>
446
+ <span class="badge gray">Cancelled</span>
447
+ </div>
448
+ <table>
449
+ <thead><tr><th>Tone</th><th>Profile status</th><th>Job status</th></tr></thead>
450
+ <tbody>
451
+ <tr><td><span class="badge green">green</span></td><td>ok: Logged in</td><td>Succeeded</td></tr>
452
+ <tr><td><span class="badge red">red</span></td><td>expired, missing</td><td>Failed</td></tr>
453
+ <tr><td><span class="badge amber">amber</span></td><td>unverified, error</td><td>Needs your input (an AI run that ended with <code>RESULT: needs-input</code>)</td></tr>
454
+ <tr><td><span class="badge blue">blue</span></td><td>-</td><td>Running</td></tr>
455
+ <tr><td><span class="badge gray">gray</span></td><td>-</td><td>Cancelled</td></tr>
456
+ </tbody>
457
+ </table>
458
+
459
+ <h3>Environment markers</h3>
460
+ <div class="demo">
461
+ <span class="env production">PRODUCTION</span>
462
+ <span class="env staging">STAGING</span>
463
+ <span class="env other">OTHER</span>
464
+ </div>
465
+ <p class="small muted">
466
+ The environment must never be missed, so it is a solid block with white text, from <code>envFillClass()</code> in <code>status.tsx</code>
467
+ (used by <code>EnvBadge</code> and the banner on top of the Reports page). Production is <code>jiang</code> (purple), staging <code>cheng</code> (orange),
468
+ any other environment <code>gold-deep</code>. Never red: red means errors, failures and writes, and production must not look like an error.
469
+ </p>
470
+
471
+ <h2 id="action-colors">Action colors (AI commands)</h2>
472
+ <p>
473
+ Each AI action has one color that tells how much it changes. The select, its dot and its options share the color;
474
+ a tooltip spells the impact out. Defined in <code>AI_COMMAND_STYLE</code> in <code>pages/cases.tsx</code>.
475
+ </p>
476
+ <div class="demo">
477
+ <span class="cmd review"><span class="dot"></span><span class="fake">Explore case</span></span>
478
+ <span class="cmd gen"><span class="dot"></span><span class="fake">Generate spec</span></span>
479
+ <span class="cmd heal"><span class="dot"></span><span class="fake">Heal spec</span></span>
480
+ </div>
481
+ <table>
482
+ <thead><tr><th>Action</th><th>Color</th><th>Impact</th></tr></thead>
483
+ <tbody>
484
+ <tr><td><code>explore-case</code></td><td>blue (qing)</td><td>Edits only the case file (wording, observations, open questions, inputs). The agent only reads pages; you do the steps that write data.</td></tr>
485
+ <tr><td><code>gen-spec</code></td><td>amber (gold)</td><td>Writes: creates the spec and fills the case's <code>## Values</code>. Does not run the tests.</td></tr>
486
+ <tr><td><code>heal-spec</code></td><td>red (seal)</td><td>Rewrites an existing spec and runs the tests.</td></tr>
487
+ </tbody>
488
+ </table>
489
+ <p class="small muted">
490
+ A new action must pick the color by impact, not by taste: read-only or touches only the case file is blue, adds files (spec, case values) is amber, rewrites or deletes is red. Two actions must never share a color.
491
+ The select itself is the <a href="#dropdowns">Dropdown</a> component with <code>tone</code> and <code>dot</code>. Native <code>&lt;option&gt;</code> backgrounds are honored by Chrome and Edge but ignored by Safari; the closed select is always colored.
492
+ </p>
493
+
494
+ <h2 id="dropdowns">Dropdowns</h2>
495
+ <p>
496
+ Every dropdown is the <code>Select</code> component from <code>ui/web/src/components/status.tsx</code>.
497
+ <b>Never write a bare <code>&lt;select&gt;</code></b> and never copy its classes: a native select draws the
498
+ browser's own arrow and a different height, so it looks wrong next to the others (this has gone wrong twice).
499
+ </p>
500
+ <div class="demo">
501
+ <span class="select"><span class="fake">staging</span><svg class="chev" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></span>
502
+ <span class="select focus"><span class="fake">staging</span><svg class="chev" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></span>
503
+ <span class="select-label invalid">Explore in
504
+ <span class="select invalid"><span class="fake">Choose...</span><svg class="chev" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></span>
505
+ </span>
506
+ <span class="select dotted"><span class="dot"></span><span class="fake">Generate spec</span><svg class="chev" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></span>
507
+ </div>
508
+ <p class="small muted">Left to right: normal, focused, required and empty (invalid), and colored with a dot (the AI command select).</p>
509
+ <pre class="card mono" style="overflow:auto">import {Select} from '../components/status';
510
+
511
+ &lt;Select value={env} onChange={(e) =&gt; setEnv(e.target.value)} invalid={env === ''} aria-label="Environment"&gt;
512
+ &lt;option value=""&gt;Choose...&lt;/option&gt;
513
+ ...
514
+ &lt;/Select&gt;</pre>
515
+ <table>
516
+ <thead><tr><th>Part</th><th>Rule</th></tr></thead>
517
+ <tbody>
518
+ <tr><td>Size</td><td>Height 32px (<code>h-8</code>), text <code>text-sm</code>, corners <code>rounded</code>. Same height as <code>buttonClass()</code> buttons, so they line up in a toolbar.</td></tr>
519
+ <tr><td>Arrow</td><td>One Feather <code>ChevronDown</code>, size 14, <code>text-ink-2</code>, at the right. The native arrow is removed with <code>appearance-none</code>. The component does both; do not add your own.</td></tr>
520
+ <tr><td>Normal</td><td>White background, <code>border-line-mid</code>, <code>text-ink</code>. Focus: <code>theme</code> border and a 2px ring at 25%.</td></tr>
521
+ <tr><td>Required and empty</td><td>Pass <code>invalid</code>: <code>seal</code> border, <code>seal-bg</code> background, <code>seal-deep</code> text and arrow. Also color the label <code>text-seal-deep</code> and set <code>required</code>. Do not add a separate error message when the placeholder option (<code>Choose...</code>) already says what to do.</td></tr>
522
+ <tr><td>Colored by choice</td><td>Pass <code>tone</code> (border, background, text and ring classes) and <code>dot</code> (dot background) when each option has a meaning color, as in <a href="#action-colors">Action colors</a>.</td></tr>
523
+ <tr><td>Code values</td><td>Pass <code>mono</code> for ids and file names.</td></tr>
524
+ <tr><td>Label</td><td>Put a visible label in front (<code>text-sm text-ink-2</code>) or an <code>aria-label</code>.</td></tr>
525
+ <tr><td>Remembering a choice</td><td>Use <code>usePersistedState(key, default, guard)</code> from <code>lib/persisted.ts</code> with the key prefix <code>asrun-ui.</code>. Treat a stored value that is no longer in the options as empty.</td></tr>
526
+ </tbody>
527
+ </table>
528
+
529
+ <h2 id="icons">Icons</h2>
530
+ <p>
531
+ Icons come from <a href="https://feathericons.com/" target="_blank" rel="noopener">Feather</a> through the
532
+ <code>react-feather</code> package. <b>Do not draw or paste custom SVGs in the app.</b>
533
+ </p>
534
+ <div class="demo">
535
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
536
+ <span class="label">Play (run tests)</span>
537
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
538
+ <span class="label">Send (run AI / shell commands)</span>
539
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>
540
+ <span class="label">ChevronDown</span>
541
+ </div>
542
+ <pre class="card mono" style="overflow:auto">import {Play} from 'react-feather';
543
+
544
+ &lt;button className={`${buttonClass('success')} gap-1.5`}&gt;
545
+ &lt;Play size={14} aria-hidden="true" /&gt;
546
+ Run tests
547
+ &lt;/button&gt;</pre>
548
+ <ul class="tight">
549
+ <li>Size 14 inside buttons and selects, 16-20 elsewhere. Stroke width stays at Feather's default (2).</li>
550
+ <li>Icons inherit <code>currentColor</code>; color them with the text color classes, not fills.</li>
551
+ <li>Decorative icons get <code>aria-hidden="true"</code>; icon-only buttons need an <code>aria-label</code>.</li>
552
+ <li>A button that is just an icon in a dense list has no background or border: <code>text-jade-deep hover:text-jade-btn</code> for "run" (the run button in the Cases sidebar), <code>text-ink-3 hover:text-ink</code> for neutral ones (clear search).</li>
553
+ <li>Find names at <a href="https://feathericons.com/" target="_blank" rel="noopener">feathericons.com</a> and import them in PascalCase (<code>arrow-right</code> becomes <code>ArrowRight</code>).</li>
554
+ <li>Exception, brand logos: Feather has no logos of other products, so the Claude and OpenCode logos of the AI agent switch are static SVG files in <code>ui/web/public/logos/</code> (single path, <code>viewBox="0 0 24 24"</code>). Show them with the <code>Logo</code> mask in <code>components/tool-switch.tsx</code> so they take the text color. Never paste logo SVGs into components.</li>
555
+ </ul>
556
+
557
+ <h2 id="tooltips">Tooltips</h2>
558
+ <p>
559
+ Write a plain <code>title="..."</code> on the element. <code>lib/tooltip.ts</code> (tippy.js) turns every <code>title</code> into
560
+ a dark tooltip with an arrow on hover, so the browser's native tooltip never shows. Do not set up tippy per component and do not
561
+ use a <code>title</code> on an <code>iframe</code>. Each <code>\n</code> in the text starts a new paragraph, and a line that starts with <code># </code> is a bold heading. Keep it short: a heading, then one or two sentences. Never put a command line or other long raw text in a tooltip. Disabled buttons and inputs get the
562
+ tooltip too, so a <code>title</code> that says why something is disabled works. The box is styled by <code>.tippy-box[data-theme~='tip']</code>
563
+ in <code>index.css</code>; the keyword help of the case editor has its own <code>inkstone</code> theme.
564
+ </p>
565
+
566
+ <h2 id="editor">Markdown editor</h2>
567
+ <p class="muted">
568
+ The case editor (CodeMirror) is the "draft" next to the white preview "sheet". Layout and colors live in the
569
+ <code>.inkstone-editor</code> rules of <code>index.css</code>; <code>ui/web/src/lib/editor-theme.ts</code> maps syntax tags to
570
+ the <code>tok-*</code> classes. Use both for any new editor, and never the built-in CodeMirror themes.
571
+ The editor is monospace only: no serif or sans font, not even for headings.
572
+ The preview renders the front matter as a key/value table (keys in <code>warm</code> cells, list values as blue badges).
573
+ </p>
574
+ <div class="editor-demo">
575
+ <div class="ed-active"><span class="ed-mark">---</span></div>
576
+ <div><span class="ed-prop">suites</span><span class="ed-punct">:</span> <span class="ed-punct">[</span>smoke<span class="ed-punct">]</span></div>
577
+ <div><span class="ed-mark">---</span></div>
578
+ <div><span class="ed-mark">##</span> <span class="ed-h">Steps</span></div>
579
+ <div><span class="ed-list">1.</span> Open <span class="ed-mark">`</span><span class="ed-code">/app/documents</span><span class="ed-mark">`</span>.</div>
580
+ <div><span class="ed-list">-</span> See <span class="ed-mark">[</span><span class="ed-link">docs</span><span class="ed-mark">](</span><span class="ed-url">https://example.com</span><span class="ed-mark">)</span></div>
581
+ <div><span class="ed-mark">&gt;</span> <span class="ed-quote">A quoted note</span></div>
582
+ </div>
583
+ <table style="margin-top:12px">
584
+ <tr><th>Element</th><th>Token</th></tr>
585
+ <tr><td>Editor background</td><td><code>warm</code> over the paper grain; gutter <code>paper</code>, separated by a <code>line-mid</code> border</td></tr>
586
+ <tr><td>Cursor, active line, active line number</td><td><code>theme-btn</code>, <code>theme-bg</code> at 40%, <code>theme-ink</code>; they follow the page theme</td></tr>
587
+ <tr><td>Selection</td><td><code>theme</code> at 30%</td></tr>
588
+ <tr><td>Bracket match</td><td><code>gold-bg</code> with <code>gold-deep</code> text</td></tr>
589
+ <tr><td>Headings</td><td><code>seal-deep</code>, bold, monospace (<code>theme-ink</code> on the red <code>seal</code> page)</td></tr>
590
+ <tr><td>Marks (<code>#</code> <code>&gt;</code> backticks, <code>---</code>)</td><td><code>ink-3</code></td></tr>
591
+ <tr><td>List markers</td><td><code>gold-deep</code>, bold</td></tr>
592
+ <tr><td>Bold, italic</td><td><code>ink</code>, bold or italic</td></tr>
593
+ <tr><td>Quote</td><td><code>ink-2</code>, italic</td></tr>
594
+ <tr><td>Link text, URL</td><td><code>qing-deep</code>; <code>ink-3</code> underlined</td></tr>
595
+ <tr><td>Inline code</td><td><code>jade-btn</code> on <code>jade-bg</code></td></tr>
596
+ <tr><td>Front matter keys, types, tags</td><td><code>qing-deep</code></td></tr>
597
+ <tr><td>Code blocks</td><td>keywords <code>seal-deep</code>, strings <code>jade-btn</code>, numbers <code>gold-deep</code>, functions <code>dai</code>, comments <code>ink-3</code>, punctuation <code>ink-2</code></td></tr>
598
+ </table>
599
+
600
+ <h3>Keyword highlights and their tooltip</h3>
601
+ <p class="muted">
602
+ Words that need a human are marked in the editor and in the preview (<code>&lt;mark&gt;</code>) with the same
603
+ <code>kw-*</code> classes: <code>TODO [blocking]</code> <code>seal</code>, other <code>TODO</code> <code>gold</code>,
604
+ "to be confirmed by QA" / "not observed" <code>qing</code>, <code>Answer:</code> <code>jade</code>; the emoji in front
605
+ of a marker is part of it and takes the same color. Hovering one shows a
606
+ small dark card with an arrow (tippy.js, theme <code>inkstone</code>: <code>dai-deep</code> background like the preview code
607
+ blocks, <code>paper</code> text, <code>shadow-lift</code>) so that it stands out on both the white preview and the beige
608
+ editor. A 6px left bar and the title, both in the bright <code>*-glow</code> color of the kind (<code>seal-glow</code>,
609
+ <code>gold-glow</code>, <code>qing-glow</code>, <code>jade-glow</code>; they exist only for dark surfaces),
610
+ show the kind; below come one line of meaning (<code>line-mid</code>) and a numbered "You can:" list. The texts live in
611
+ <code>keywordHelp</code> in <code>ui/web/src/lib/highlight-keywords.ts</code>; keep every line short and direct.
612
+ Do not add another tooltip library: use <code>tippy.js</code> and the <code>inkstone</code> theme.
613
+ </p>
614
+ <p class="muted">
615
+ After every marker comes a small remove button (<code>kw-remove</code>, <code>kw-remove-&lt;kind&gt;</code>): the
616
+ Feather <code>X</code> at 14px in the same text and background colors as the marker, 20px square, 2px radius,
617
+ <code>theme</code> focus outline. It is a plain button (a CodeMirror widget in the editor, an element in the preview),
618
+ never a new icon. It deletes the marker, or the whole question block of a <code>TODO</code> (the sentence above stays),
619
+ or what is typed after an <code>Answer:</code>; the logic is in <code>ui/web/src/lib/keyword-removal.ts</code>.
620
+ </p>
621
+ <p class="muted">
622
+ The preview block that holds the editor caret is shaded with <code>theme-bg</code> at 60% and a 2px <code>theme</code> left
623
+ border (an overlay, so it follows the page theme). The editor and the preview scroll together and a click in the preview
624
+ moves the caret; the logic is in <code>ui/web/src/lib/preview-sync.ts</code>.
625
+ </p>
626
+
627
+ <h2 id="surfaces">Surfaces and shadows</h2>
628
+ <div class="row">
629
+ <div class="card" style="width:240px">
630
+ <b>card-paper</b>
631
+ <p class="small muted" style="margin:4px 0 0">White sheet, 4px radius, a faint accent tint at the top left, colored shadow that follows the page theme.</p>
632
+ </div>
633
+ <div style="display:flex;align-items:center;gap:10px">
634
+ <span class="seal-stamp">A</span>
635
+ <span class="small muted"><code>.seal</code>: the stamp. The only slightly rotated element; use it once, in the header.</span>
636
+ </div>
637
+ </div>
638
+ <ul class="tight">
639
+ <li>Radius: 4px for cards, buttons and inputs; 2px for badges; no pills.</li>
640
+ <li>Shadows are tinted with the page accent (<code>shadow-paper</code>, <code>shadow-lift</code>, <code>shadow-btn</code>, <code>shadow-btn-lift</code>), never plain gray.</li>
641
+ <li>The page background carries a subtle paper grain (<code>--grain</code>).</li>
642
+ <li>The Jobs drawer, header and top-level separators use a 2px <code>ink</code> border; everything inside uses <code>line</code> / <code>line-mid</code>.</li>
643
+ <li>The Jobs drawer's title bar is a solid <code>dai-deep</code> bar with white text, so it reads as a handle. It ends with the same small chevron block as a sidebar section header and brightens on hover.</li>
644
+ <li><b>Every expand/collapse animates.</b> Use <code>Collapse</code> from <code>components/collapse.tsx</code> (height and fade, 200ms, <code>ease-out</code>) for content that appears and disappears, and rotate the chevron with <code>transition-transform duration-200</code> instead of swapping two icons. Always add <code>motion-reduce:transition-none</code>. Turn transitions off while the user drags a resize handle.</li>
645
+ </ul>
646
+
647
+ <h2 id="sections">Sidebar sections and selection</h2>
648
+ <div class="row" style="align-items:flex-start">
649
+ <div class="side">
650
+ <div class="sec"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>New cases<span class="toggle"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg></span></div>
651
+ <div class="sec"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>Case list<span class="aside">3 / 12</span></div>
652
+ <div class="search">
653
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
654
+ <span class="fake">Search name, title, suite...</span>
655
+ </div>
656
+ <div class="folder">documents <span style="margin-left:auto;color:var(--ink-3)">2</span></div>
657
+ <div class="item selected">hub-landing</div>
658
+ <div class="item">header-logo</div>
659
+ </div>
660
+ <div style="flex:1;min-width:260px">
661
+ <ul class="tight">
662
+ <li>Sections of a sidebar (<code>SectionHeader</code> in <code>pages/cases.tsx</code>) are all on one level: a solid page-accent bar (<code>bg-theme-btn</code>, white text and icon, serif bold label), so a section header is never mistaken for a folder row (gray). A collapsible one ends with a small button block (<code>bg-white/15</code>, Feather <code>ChevronDown</code> when collapsed, <code>ChevronUp</code> when open, size 16) that brightens on hover; a static one has no block, only an optional count. The block is what says "this can be expanded".</li>
663
+ <li><code>bg-theme-bg</code> means <b>selected</b> and nothing else. Never use it for a header, so a header cannot be mistaken for a selected row. A selected row also gets <code>border-l-4 border-l-theme</code>.</li>
664
+ <li>Levels inside a list: section header, then folder (<code>bg-warm</code>, small, regular weight, no accent), then item.</li>
665
+ <li>A search box is a <code>fieldClass</code> input with a Feather <code>Search</code> icon at the left and a clear button (<code>X</code>, <code>aria-label</code>) at the right. It filters while typing, with no submit; <code>Esc</code> clears it.</li>
666
+ </ul>
667
+ <p class="small muted">The icons in the demo are inline only because this page has no build step; in the app they always come from <code>react-feather</code>.</p>
668
+ </div>
669
+ </div>
670
+
671
+ <h2 id="checklist">Checklist for a new UI element</h2>
672
+ <ol class="tight">
673
+ <li>Pick colors only from the tokens above (<code>text-ink-2</code>, <code>bg-warm</code>, <code>bg-seal-bg</code>, ...).</li>
674
+ <li>Decide the impact: safe/go = green, read-only/info = blue, writes/attention = amber, destructive/rewrite/error = red.</li>
675
+ <li>Use <code>buttonClass()</code> and <code>Badge</code> instead of new styles.</li>
676
+ <li>Dropdowns: use <code>Select</code>, never a bare <code>&lt;select&gt;</code> (see <a href="#dropdowns">Dropdowns</a>).</li>
677
+ <li>Icons: Feather via <code>react-feather</code> only.</li>
678
+ <li>If you add or change a token, update <code>index.css</code> and this page together.</li>
679
+ </ol>
680
+ <div class="rule" style="margin-top:16px">
681
+ Exception: if a color's meaning has to differ from this guide (for example a destructive action that is not red), say why in a code comment.
682
+ </div>
683
+
684
+ <footer>Inkstone theme guide for the AsRun UI. Static file: <code>docs/theme-guide.html</code>. Inkstone theme, designed and created by Zoro Chen.
685
+ <div class="creator">
686
+ <span>Created by Zoro Chen</span>
687
+ </div>
688
+ </footer>
689
+ </main>
690
+ </div>
691
+ </body>
692
+ </html>