@erclx/canon 5.26.0 → 5.28.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 (320) hide show
  1. package/README.md +2 -2
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/SKILL.md +2 -0
  4. package/claude/skills/auto-ship/SKILL.md +3 -11
  5. package/claude/skills/auto-ship/references/review-findings.md +20 -0
  6. package/claude/skills/backlog-triage/SKILL.md +2 -0
  7. package/claude/skills/bash-cli-script/SKILL.md +2 -0
  8. package/claude/skills/build-in-slices/SKILL.md +2 -0
  9. package/claude/skills/canon-cli/SKILL.md +2 -0
  10. package/claude/skills/canon-feedback/SKILL.md +2 -0
  11. package/claude/skills/canon-feedback-triage/SKILL.md +2 -0
  12. package/claude/skills/canon-operator/SKILL.md +2 -0
  13. package/claude/skills/canon-rollout/SKILL.md +3 -11
  14. package/claude/skills/canon-rollout/references/failure-recovery.md +20 -0
  15. package/claude/skills/canvas/SKILL.md +13 -0
  16. package/claude/skills/ci-workflow/SKILL.md +2 -0
  17. package/claude/skills/code-craft/SKILL.md +2 -0
  18. package/claude/skills/codebase-layout/SKILL.md +2 -0
  19. package/claude/skills/context-fold/SKILL.md +3 -17
  20. package/claude/skills/context-fold/references/completion-report.md +26 -0
  21. package/claude/skills/create-rule/SKILL.md +2 -0
  22. package/claude/skills/create-skill/SKILL.md +2 -0
  23. package/claude/skills/create-standard/SKILL.md +2 -0
  24. package/claude/skills/decision-escalate/SKILL.md +2 -0
  25. package/claude/skills/deploy-app/SKILL.md +2 -0
  26. package/claude/skills/deprecation-migration/SKILL.md +2 -0
  27. package/claude/skills/design-extract/SKILL.md +2 -0
  28. package/claude/skills/design-taste/SKILL.md +2 -0
  29. package/claude/skills/docs-sync/SKILL.md +2 -0
  30. package/claude/skills/document-health/SKILL.md +2 -0
  31. package/claude/skills/draft-and-pick/SKILL.md +2 -0
  32. package/claude/skills/draft-doc/SKILL.md +2 -0
  33. package/claude/skills/draft-doc/references/wiki.md +1 -1
  34. package/claude/skills/draft-figure/SKILL.md +2 -0
  35. package/claude/skills/draft-identity/SKILL.md +2 -0
  36. package/claude/skills/draft-ready/SKILL.md +5 -1
  37. package/claude/skills/draft-screencast/SKILL.md +2 -0
  38. package/claude/skills/draft-slides/SKILL.md +2 -0
  39. package/claude/skills/git-branch/SKILL.md +2 -0
  40. package/claude/skills/git-commit/SKILL.md +2 -0
  41. package/claude/skills/git-followup/SKILL.md +2 -0
  42. package/claude/skills/git-issue/SKILL.md +2 -0
  43. package/claude/skills/git-pr/SKILL.md +2 -0
  44. package/claude/skills/git-pr/references/evidence.md +7 -7
  45. package/claude/skills/git-ship/SKILL.md +2 -0
  46. package/claude/skills/git-split/SKILL.md +2 -0
  47. package/claude/skills/git-stage/SKILL.md +2 -0
  48. package/claude/skills/git-worktree/SKILL.md +2 -0
  49. package/claude/skills/index-lookup/SKILL.md +2 -0
  50. package/claude/skills/markdown-propose/SKILL.md +2 -0
  51. package/claude/skills/memory-capture/SKILL.md +2 -0
  52. package/claude/skills/memory-review/SKILL.md +2 -0
  53. package/claude/skills/plan-feature/SKILL.md +2 -0
  54. package/claude/skills/plan-groundwork/SKILL.md +2 -0
  55. package/claude/skills/plan-intake/SKILL.md +2 -0
  56. package/claude/skills/plan-intake-answer/SKILL.md +2 -0
  57. package/claude/skills/project-commands/SKILL.md +2 -0
  58. package/claude/skills/read-frames/SKILL.md +2 -0
  59. package/claude/skills/record-screencast/SKILL.md +2 -0
  60. package/claude/skills/repo-metadata/SKILL.md +2 -0
  61. package/claude/skills/restate-plainly/SKILL.md +2 -0
  62. package/claude/skills/review-address/REQUIREMENT.md +4 -2
  63. package/claude/skills/review-address/SKILL.md +19 -14
  64. package/claude/skills/review-address/references/post-review-findings.md +16 -0
  65. package/claude/skills/review-address/references/rebase-conflicts.md +4 -2
  66. package/claude/skills/review-branch/SKILL.md +2 -0
  67. package/claude/skills/review-craft/SKILL.md +2 -0
  68. package/claude/skills/review-craft/references/ui.md +6 -6
  69. package/claude/skills/review-pr/SKILL.md +4 -8
  70. package/claude/skills/review-pr/references/body-variants.md +52 -0
  71. package/claude/skills/review-pr/references/key-changes.md +16 -0
  72. package/claude/skills/review-pr/references/post.md +15 -43
  73. package/claude/skills/review-ui/REQUIREMENT.md +2 -2
  74. package/claude/skills/review-ui/SKILL.md +16 -12
  75. package/claude/skills/review-ui/references/driving.md +8 -8
  76. package/claude/skills/review-ui/references/post.md +13 -3
  77. package/claude/skills/review-ui/references/tick.md +22 -0
  78. package/claude/skills/role-orchestrator/REQUIREMENT.md +15 -15
  79. package/claude/skills/role-orchestrator/SKILL.md +31 -42
  80. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +18 -16
  81. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +13 -3
  82. package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +23 -0
  83. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +6 -34
  84. package/claude/skills/role-orchestrator/references/orchestrator-output.md +4 -4
  85. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +5 -5
  86. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +12 -23
  87. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +6 -6
  88. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +1 -1
  89. package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +38 -0
  90. package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +2 -2
  91. package/claude/skills/role-orchestrator/references/orchestrator-watch.md +26 -0
  92. package/claude/skills/role-orchestrator/scripts/poll.ts +304 -10
  93. package/claude/skills/role-planner/REQUIREMENT.md +1 -1
  94. package/claude/skills/role-planner/SKILL.md +4 -2
  95. package/claude/skills/role-reviewer/REQUIREMENT.md +1 -1
  96. package/claude/skills/role-reviewer/SKILL.md +3 -1
  97. package/claude/skills/role-worker/SKILL.md +5 -2
  98. package/claude/skills/search-craft/SKILL.md +2 -0
  99. package/claude/skills/seed-sync/SKILL.md +2 -0
  100. package/claude/skills/session-compact/SKILL.md +2 -0
  101. package/claude/skills/session-map/SKILL.md +2 -0
  102. package/claude/skills/session-relay/SKILL.md +2 -0
  103. package/claude/skills/session-resume/SKILL.md +2 -0
  104. package/claude/skills/session-worktree/SKILL.md +3 -11
  105. package/claude/skills/session-worktree/references/install-command.md +20 -0
  106. package/claude/skills/sketch-design/SKILL.md +2 -0
  107. package/claude/skills/systematic-debugging/SKILL.md +2 -0
  108. package/claude/skills/target-check/SKILL.md +2 -0
  109. package/claude/skills/target-setup/SKILL.md +2 -0
  110. package/claude/skills/task-board/SKILL.md +2 -0
  111. package/claude/skills/teach-workspace/SKILL.md +5 -15
  112. package/claude/skills/teach-workspace/references/lesson-build.md +75 -0
  113. package/claude/skills/teach-workspace/references/lesson-craft.md +13 -69
  114. package/claude/skills/teach-workspace/references/revisit.md +22 -0
  115. package/claude/skills/test-craft/SKILL.md +2 -0
  116. package/claude/skills/test-first/SKILL.md +2 -0
  117. package/claude/skills/ui-checklist/SKILL.md +3 -1
  118. package/claude/skills/ux-audit/SKILL.md +2 -0
  119. package/claude/skills/ux-measure/SKILL.md +2 -0
  120. package/claude/skills/ux-walkthrough/SKILL.md +2 -0
  121. package/claude/skills/video-craft/SKILL.md +2 -0
  122. package/claude/skills/write-human/SKILL.md +2 -0
  123. package/claude/skills/youtube-transcripts/SKILL.md +2 -0
  124. package/docs/agents/canvas.md +24 -8
  125. package/docs/agents/capture.md +5 -5
  126. package/docs/agents/commands.md +2 -2
  127. package/docs/agents/gate.md +3 -1
  128. package/docs/agents/index.md +5 -4
  129. package/docs/agents/install-and-sync.md +7 -0
  130. package/docs/agents/key-changes.md +1 -1
  131. package/docs/agents/pr-evidence.md +47 -23
  132. package/docs/agents/pr-local.md +19 -13
  133. package/docs/agents/pr-preview.md +44 -21
  134. package/docs/agents/pr-tick.md +72 -0
  135. package/docs/agents/sandbox.md +30 -6
  136. package/docs/agents/scripting.md +8 -1
  137. package/docs/agents/skills-audit.md +2 -1
  138. package/docs/agents/tasks-plans.md +1 -1
  139. package/docs/agents/tasks-validate.md +18 -18
  140. package/docs/agents/tasks.md +1 -1
  141. package/docs/workflow/operating-model.md +15 -21
  142. package/docs/workflow/parallel-features.md +24 -3
  143. package/docs/workflow/skill-map.md +3 -1
  144. package/docs/workflow/zshrc-aliases.md +1 -1
  145. package/package.json +1 -1
  146. package/scripts/core/regen-hero.ts +1 -1
  147. package/scripts/lib/sandbox-path.sh +6 -162
  148. package/src/audits/baseline.ts +1 -1
  149. package/src/audits/run.ts +2 -2
  150. package/src/canvas/address.ts +12 -3
  151. package/src/canvas/capture.ts +1 -1
  152. package/src/canvas/client/inspector/align-grid.tsx +162 -0
  153. package/src/canvas/client/inspector/color-field.tsx +201 -171
  154. package/src/canvas/client/inspector/color-inputs.tsx +139 -0
  155. package/src/canvas/client/inspector/color-picker.tsx +668 -0
  156. package/src/canvas/client/inspector/color-space.ts +240 -0
  157. package/src/canvas/client/inspector/field.tsx +66 -3
  158. package/src/canvas/client/inspector/fields.css +473 -6
  159. package/src/canvas/client/inspector/size-field.tsx +69 -0
  160. package/src/canvas/client/inspector/token-select.tsx +109 -0
  161. package/src/canvas/client/inspector/values.ts +117 -3
  162. package/src/canvas/client/inspector.tsx +311 -15
  163. package/src/canvas/client/keys.ts +54 -0
  164. package/src/canvas/client/selection.tsx +199 -0
  165. package/src/canvas/client/shell.css +210 -12
  166. package/src/canvas/client/state.ts +165 -14
  167. package/src/canvas/client/surface.tsx +341 -39
  168. package/src/canvas/client/tools.tsx +64 -0
  169. package/src/canvas/content.ts +51 -2
  170. package/src/canvas/edit.ts +49 -6
  171. package/src/canvas/editing.ts +203 -0
  172. package/src/canvas/global.d.ts +15 -0
  173. package/src/canvas/server.ts +149 -12
  174. package/src/canvas/tokens.ts +60 -18
  175. package/src/census/count.ts +1 -1
  176. package/src/claude/seeds.ts +3 -3
  177. package/src/claude/skills-audit.ts +28 -0
  178. package/src/claude/skills-drift.ts +1 -1
  179. package/src/claude/skills-families.ts +36 -0
  180. package/src/claude/skills-list.ts +76 -15
  181. package/src/claude/skills-reach.ts +1 -1
  182. package/src/commands/audits.ts +2 -2
  183. package/src/commands/canvas.ts +180 -3
  184. package/src/commands/claude.ts +70 -12
  185. package/src/commands/design.ts +145 -5
  186. package/src/commands/docs.ts +1 -1
  187. package/src/commands/feedback.ts +2 -2
  188. package/src/commands/gate.ts +9 -2
  189. package/src/commands/gov.ts +3 -3
  190. package/src/commands/hooks.ts +1 -1
  191. package/src/commands/indexes.ts +1 -1
  192. package/src/commands/init.ts +2 -2
  193. package/src/commands/intake.ts +1 -1
  194. package/src/commands/migrate.ts +2 -2
  195. package/src/commands/pr.ts +288 -65
  196. package/src/commands/records.ts +1 -1
  197. package/src/commands/repo.ts +1 -1
  198. package/src/commands/sandbox.ts +64 -15
  199. package/src/commands/sessions.ts +1 -1
  200. package/src/commands/slides.ts +1 -1
  201. package/src/commands/sync.ts +2 -2
  202. package/src/commands/tasks.ts +21 -4
  203. package/src/commands/teach.ts +1 -1
  204. package/src/commands/tooling.ts +1 -1
  205. package/src/commands/transcripts.ts +2 -2
  206. package/src/commands/upgrade.ts +1 -1
  207. package/src/commands/wiki.ts +1 -1
  208. package/src/commands/worktrees.ts +1 -1
  209. package/src/comments/scan.ts +1 -1
  210. package/src/comments/trend.ts +1 -1
  211. package/src/context/architecture.ts +1 -1
  212. package/src/context/citations.ts +3 -3
  213. package/src/context/classify/extract.ts +3 -3
  214. package/src/context/folders.ts +2 -2
  215. package/src/context/requirements.ts +1 -1
  216. package/src/design/project-fonts.ts +392 -0
  217. package/src/docs/read.ts +1 -1
  218. package/src/exec.ts +1 -1
  219. package/src/feedback/github.ts +2 -2
  220. package/src/gate/measures.ts +48 -10
  221. package/src/gate/sequencer.ts +56 -4
  222. package/src/gate/stages.ts +17 -0
  223. package/src/gate/suite-lock.ts +214 -0
  224. package/src/{git-files.ts → git/files.ts} +2 -2
  225. package/src/{git-ignore.ts → git/ignore.ts} +1 -1
  226. package/src/{worktree.ts → git/worktree.ts} +1 -1
  227. package/src/gov/citations.ts +1 -1
  228. package/src/gov/install.ts +1 -1
  229. package/src/gov/superseded.ts +1 -1
  230. package/src/gov/test-order.ts +2 -2
  231. package/src/help.ts +1 -1
  232. package/src/hooks/post-merge.ts +1 -1
  233. package/src/indexes/walk.ts +1 -1
  234. package/src/intake/folder.ts +1 -1
  235. package/src/labels/audit.ts +1 -1
  236. package/src/labels/map.ts +1 -1
  237. package/src/labels/phase.ts +3 -3
  238. package/src/markdown/files.ts +1 -1
  239. package/src/migrate/apply.ts +1 -1
  240. package/src/migrate/record-layout.ts +1 -1
  241. package/src/migrate/records.ts +11 -11
  242. package/src/migrate/scratch-evidence.ts +1 -1
  243. package/src/migrate/surface-roots.ts +6 -6
  244. package/src/pr/bijection.ts +1 -1
  245. package/src/pr/evidence.ts +250 -59
  246. package/src/pr/local.ts +16 -10
  247. package/src/pr/preview.ts +78 -2
  248. package/src/pr/tick.ts +56 -0
  249. package/src/process/harness.ts +1 -1
  250. package/src/records/backup.ts +2 -2
  251. package/src/records/ordinal.ts +1 -1
  252. package/src/records/prune.ts +1 -1
  253. package/src/records/size.ts +1 -1
  254. package/src/records/stale-canonical.ts +2 -2
  255. package/src/records/stale.ts +1 -1
  256. package/src/records/validate.ts +2 -2
  257. package/src/{project-root.ts → roots/project.ts} +1 -1
  258. package/src/{record-root.ts → roots/record.ts} +1 -1
  259. package/src/sandbox/coverage.ts +11 -12
  260. package/src/sandbox/equivalence.ts +57 -26
  261. package/src/sandbox/headless/dispatch.ts +264 -0
  262. package/src/sandbox/headless/run.ts +653 -0
  263. package/src/sandbox/headless/watch.ts +145 -0
  264. package/src/sandbox/hooks.ts +1 -1
  265. package/src/sandbox/provision.ts +90 -29
  266. package/src/sandbox/scenario.ts +588 -0
  267. package/src/sandbox/tree.ts +4 -8
  268. package/src/secrets/scan.ts +1 -1
  269. package/src/sessions/claim.ts +1 -1
  270. package/src/sessions/live.ts +4 -1
  271. package/src/sessions/resolve.ts +1 -1
  272. package/src/sessions/transfer/bundle.ts +2 -2
  273. package/src/slides/decks.ts +1 -1
  274. package/src/slides/package/fonts.ts +6 -2
  275. package/src/standards/audit.ts +2 -2
  276. package/src/standards/read.ts +1 -1
  277. package/src/sync/check.ts +2 -2
  278. package/src/sync/engine.ts +3 -3
  279. package/src/sync/git.ts +1 -1
  280. package/src/sync/history.ts +1 -1
  281. package/src/sync/layout.ts +2 -2
  282. package/src/sync/reverse.ts +1 -1
  283. package/src/sync/stamp.ts +1 -1
  284. package/src/sync/target.ts +1 -1
  285. package/src/sync/workflow.ts +1 -1
  286. package/src/targets/pulls.ts +2 -2
  287. package/src/targets/sweep.ts +2 -2
  288. package/src/{target.ts → targets/validate.ts} +1 -1
  289. package/src/tasks/answers.ts +1 -1
  290. package/src/tasks/archive.ts +1 -1
  291. package/src/tasks/label.ts +1 -1
  292. package/src/tasks/reach.ts +2 -2
  293. package/src/tasks/trunk.ts +1 -1
  294. package/src/tasks/validate.ts +38 -14
  295. package/src/teach/nav.ts +1 -1
  296. package/src/teach/workspace.ts +1 -1
  297. package/src/tooling/inject.ts +2 -2
  298. package/src/tooling/read.ts +1 -1
  299. package/src/tooling/scan.ts +1 -1
  300. package/src/tooling/subfolder.ts +1 -1
  301. package/src/version/installed.ts +1 -1
  302. package/src/version/skew.ts +1 -1
  303. package/src/web/readme-citations.ts +1 -1
  304. package/src/worktrees/bare-flag.ts +1 -1
  305. package/src/worktrees/reclaim.ts +2 -2
  306. package/src/worktrees/remove.ts +1 -1
  307. package/standards/board.md +3 -3
  308. package/standards/canvas.md +2 -1
  309. package/standards/plan.md +13 -7
  310. package/standards/skill.md +1 -0
  311. package/standards/wiki.md +12 -3
  312. package/tooling/base/configs/.github/workflows/pr-local-preview.yml +2 -2
  313. package/tsconfig.json +1 -1
  314. package/scripts/lib/sandbox-dispatch.sh +0 -190
  315. /package/src/{client-commands.ts → gate/client-commands.ts} +0 -0
  316. /package/src/{git-base.ts → git/base.ts} +0 -0
  317. /package/src/{git-env.ts → git/env.ts} +0 -0
  318. /package/src/{legacy-path.ts → roots/legacy.ts} +0 -0
  319. /package/src/{surface-root.ts → roots/surface.ts} +0 -0
  320. /package/src/{copy.ts → sync/copy.ts} +0 -0
@@ -128,10 +128,164 @@
128
128
  font-weight: 400;
129
129
  }
130
130
 
131
+ /* An icon in a section's header lane or beside a field, square at row height. */
132
+ .section-icon {
133
+ flex: none;
134
+ display: grid;
135
+ place-items: center;
136
+ width: 24px;
137
+ height: 24px;
138
+ padding: 0;
139
+ border: 0;
140
+ border-radius: var(--radius-action);
141
+ background: transparent;
142
+ color: var(--color-muted);
143
+ cursor: pointer;
144
+ }
145
+
146
+ .section-icon:hover,
147
+ .section-icon[aria-pressed='true'] {
148
+ background: var(--color-surface);
149
+ color: var(--color-text);
150
+ }
151
+
152
+ .section-icon:disabled {
153
+ opacity: 0.5;
154
+ cursor: not-allowed;
155
+ }
156
+
157
+ .section-icon svg {
158
+ width: 14px;
159
+ height: 14px;
160
+ fill: none;
161
+ stroke: currentColor;
162
+ stroke-width: 1.5;
163
+ stroke-linecap: round;
164
+ stroke-linejoin: round;
165
+ }
166
+
167
+ /* The alignment grid on the left, the direction and wrap stacked beside it. */
168
+ .flex-layout {
169
+ display: grid;
170
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
171
+ gap: var(--space-xs);
172
+ align-items: start;
173
+ }
174
+
175
+ .flex-layout-side {
176
+ display: flex;
177
+ align-items: center;
178
+ gap: var(--space-xs);
179
+ min-width: 0;
180
+ }
181
+
182
+ .flex-layout-side .glyph-field {
183
+ flex: 1;
184
+ }
185
+
186
+ .align-grid {
187
+ display: grid;
188
+ grid-template-columns: repeat(3, 1fr);
189
+ grid-template-rows: repeat(3, 24px);
190
+ padding: 2px;
191
+ border: 1px solid var(--color-border);
192
+ border-radius: var(--radius-action);
193
+ background: var(--color-surface);
194
+ }
195
+
196
+ .align-cell {
197
+ display: grid;
198
+ place-items: center;
199
+ min-width: 0;
200
+ padding: 0;
201
+ border: 0;
202
+ border-radius: var(--radius-action);
203
+ background: transparent;
204
+ color: var(--color-muted);
205
+ cursor: pointer;
206
+ }
207
+
208
+ .align-cell:hover {
209
+ color: var(--color-text);
210
+ }
211
+
212
+ .align-cell:disabled {
213
+ opacity: 0.5;
214
+ cursor: not-allowed;
215
+ }
216
+
217
+ .align-dot {
218
+ width: 3px;
219
+ height: 3px;
220
+ border-radius: 50%;
221
+ background: currentColor;
222
+ }
223
+
224
+ .align-cell[aria-checked='true'] {
225
+ color: var(--color-accent);
226
+ }
227
+
228
+ .align-cell[aria-checked='true'] .align-dot {
229
+ width: 10px;
230
+ height: 10px;
231
+ border-radius: 2px;
232
+ }
233
+
234
+ /*
235
+ * A size's mode menu trails its number. The select is transparent over the
236
+ * chevron, so the platform's own list opens and the field keeps one height.
237
+ */
238
+ .size-mode {
239
+ position: relative;
240
+ flex: none;
241
+ display: grid;
242
+ place-items: center;
243
+ width: 24px;
244
+ height: 22px;
245
+ border-radius: var(--radius-action);
246
+ color: var(--color-muted);
247
+ }
248
+
249
+ .size-mode:hover,
250
+ .size-mode:focus-within {
251
+ color: var(--color-text);
252
+ background: var(--color-chrome);
253
+ }
254
+
255
+ .size-mode-select {
256
+ position: absolute;
257
+ inset: 0;
258
+ width: 100%;
259
+ height: 100%;
260
+ opacity: 0;
261
+ cursor: pointer;
262
+ }
263
+
264
+ .size-mode-select:disabled {
265
+ cursor: not-allowed;
266
+ }
267
+
268
+ .size-mode:has(.size-mode-select:disabled) {
269
+ opacity: 0.5;
270
+ }
271
+
272
+ /* The select is invisible, so the project ring draws on the lane holding it. */
273
+ .size-mode:has(.size-mode-select:focus-visible) {
274
+ outline: 2px solid var(--color-accent);
275
+ outline-offset: 0;
276
+ }
277
+
278
+ .size-mode-chevron {
279
+ width: 12px;
280
+ height: 12px;
281
+ fill: none;
282
+ stroke: currentColor;
283
+ stroke-width: 1.5;
284
+ }
285
+
131
286
  /*
132
287
  * A color is one field: a swatch leading in the glyph's lane, the hex, and the
133
- * opacity right-aligned. The picker opens in the flow under it, so it can never
134
- * reach past the panel's edge or scroll it sideways.
288
+ * opacity right-aligned, with the tokens and the eyedropper trailing the row.
135
289
  */
136
290
  .color-field {
137
291
  min-width: 0;
@@ -246,15 +400,218 @@
246
400
  user-select: none;
247
401
  }
248
402
 
249
- .color-picker {
403
+ /*
404
+ * The picker and the token list float beside the panel, in the top layer
405
+ * where the browser has one. The panel scrolls and clips, so neither sits in
406
+ * its flow. The script places each at fixed coordinates inside the window, and
407
+ * the caps below hold a window too small for the whole picker.
408
+ */
409
+ .color-popover {
410
+ position: fixed;
411
+ inset: auto;
412
+ z-index: 10;
413
+ max-width: calc(100vw - 16px);
414
+ max-height: calc(100vh - 16px);
415
+ margin: 0;
416
+ padding: var(--space-sm);
417
+ overflow: auto;
418
+ border: 1px solid var(--color-border);
419
+ border-radius: var(--radius-panel);
420
+ background: var(--color-chrome);
421
+ color: var(--color-text);
422
+ box-shadow: 0 8px 24px rgb(0 0 0 / 0.35);
423
+ }
424
+
425
+ .color-popover.is-tokens {
426
+ width: 14rem;
427
+ padding: var(--space-xs);
428
+ }
429
+
430
+ .picker {
431
+ --picker-hue: red;
432
+ --picker-solid: transparent;
433
+ display: grid;
434
+ grid-template-columns: 14rem 14px 14px 13rem;
435
+ gap: var(--space-sm);
436
+ align-items: stretch;
437
+ }
438
+
439
+ /*
440
+ * The area draws white to the hue left to right, then black over it bottom
441
+ * to top, so a point reads as saturation across and brightness up.
442
+ */
443
+ .picker-area {
444
+ position: relative;
445
+ aspect-ratio: 1;
446
+ border-radius: var(--radius-action);
447
+ background:
448
+ linear-gradient(to top, #000, transparent),
449
+ linear-gradient(to right, #fff, var(--picker-hue));
450
+ cursor: crosshair;
451
+ touch-action: none;
452
+ }
453
+
454
+ .picker-thumb {
455
+ position: absolute;
456
+ width: 12px;
457
+ height: 12px;
458
+ border: 2px solid #fff;
459
+ border-radius: 50%;
460
+ box-shadow: 0 0 0 1px rgb(0 0 0 / 0.5);
461
+ transform: translate(-50%, -50%);
462
+ pointer-events: none;
463
+ }
464
+
465
+ .picker-slider {
466
+ appearance: none;
467
+ width: 14px;
468
+ height: 100%;
469
+ margin: 0;
470
+ border-radius: var(--radius-action);
471
+ writing-mode: vertical-lr;
472
+ cursor: pointer;
473
+ }
474
+
475
+ /* Hue runs red at the top through the wheel and back to red at the foot. */
476
+ .picker-hue {
477
+ background: linear-gradient(
478
+ to bottom,
479
+ #f00,
480
+ #ff0 17%,
481
+ #0f0 33%,
482
+ #0ff 50%,
483
+ #00f 67%,
484
+ #f0f 83%,
485
+ #f00
486
+ );
487
+ }
488
+
489
+ /* Alpha runs opaque at the top to clear at the foot, over a checker. */
490
+ .picker-alpha {
491
+ direction: rtl;
492
+ background-image:
493
+ linear-gradient(to bottom, var(--picker-solid), transparent),
494
+ linear-gradient(45deg, var(--color-border) 25%, transparent 25%),
495
+ linear-gradient(-45deg, var(--color-border) 25%, transparent 25%),
496
+ linear-gradient(45deg, transparent 75%, var(--color-border) 75%),
497
+ linear-gradient(-45deg, transparent 75%, var(--color-border) 75%);
498
+ background-size:
499
+ auto,
500
+ 8px 8px,
501
+ 8px 8px,
502
+ 8px 8px,
503
+ 8px 8px;
504
+ background-position:
505
+ 0 0,
506
+ 0 0,
507
+ 0 4px,
508
+ 4px -4px,
509
+ -4px 0;
510
+ }
511
+
512
+ .picker-slider::-webkit-slider-thumb {
513
+ appearance: none;
514
+ width: 14px;
515
+ height: 6px;
516
+ border: 2px solid #fff;
517
+ border-radius: 2px;
518
+ box-shadow: 0 0 0 1px rgb(0 0 0 / 0.5);
519
+ }
520
+
521
+ .picker-slider::-moz-range-thumb {
522
+ width: 10px;
523
+ height: 2px;
524
+ border: 2px solid #fff;
525
+ border-radius: 2px;
526
+ background: transparent;
527
+ box-shadow: 0 0 0 1px rgb(0 0 0 / 0.5);
528
+ }
529
+
530
+ .picker-side {
250
531
  display: flex;
251
532
  flex-direction: column;
252
533
  gap: var(--space-xs);
253
- margin-top: var(--space-xs);
254
- padding: var(--space-xs);
534
+ min-width: 0;
535
+ }
536
+
537
+ .picker-compare {
538
+ display: grid;
539
+ grid-template-columns: 1fr 1fr;
540
+ height: 3.5rem;
541
+ overflow: hidden;
255
542
  border: 1px solid var(--color-border);
256
543
  border-radius: var(--radius-action);
257
- background: var(--color-chrome);
544
+ }
545
+
546
+ /* Each half paints over the checker, so an alpha below full reads as one. */
547
+ .picker-previous,
548
+ .picker-new {
549
+ --paint: transparent;
550
+ padding: 0;
551
+ border: 0;
552
+ background-image:
553
+ linear-gradient(var(--paint), var(--paint)),
554
+ linear-gradient(45deg, var(--color-border) 25%, transparent 25%),
555
+ linear-gradient(-45deg, var(--color-border) 25%, transparent 25%),
556
+ linear-gradient(45deg, transparent 75%, var(--color-border) 75%),
557
+ linear-gradient(-45deg, transparent 75%, var(--color-border) 75%);
558
+ background-size:
559
+ auto,
560
+ 8px 8px,
561
+ 8px 8px,
562
+ 8px 8px,
563
+ 8px 8px;
564
+ background-position:
565
+ 0 0,
566
+ 0 0,
567
+ 0 4px,
568
+ 4px -4px,
569
+ -4px 0;
570
+ }
571
+
572
+ .picker-previous {
573
+ cursor: pointer;
574
+ }
575
+
576
+ .picker-compare-labels {
577
+ display: grid;
578
+ grid-template-columns: 1fr 1fr;
579
+ color: var(--color-text-secondary);
580
+ text-align: center;
581
+ }
582
+
583
+ .picker-row {
584
+ display: grid;
585
+ grid-template-columns: repeat(3, minmax(0, 1fr));
586
+ gap: 1px;
587
+ overflow: hidden;
588
+ border-radius: var(--radius-action);
589
+ }
590
+
591
+ .picker-channel {
592
+ display: flex;
593
+ flex-direction: column;
594
+ align-items: stretch;
595
+ min-width: 0;
596
+ }
597
+
598
+ .picker-number {
599
+ min-width: 0;
600
+ min-height: 24px;
601
+ padding: 2px var(--space-xs);
602
+ border: 0;
603
+ background: var(--color-surface);
604
+ color: var(--color-text);
605
+ font: inherit;
606
+ font-variant-numeric: tabular-nums;
607
+ text-align: center;
608
+ }
609
+
610
+ .picker-letter {
611
+ color: var(--color-muted);
612
+ font-size: 0.75em;
613
+ text-align: center;
614
+ user-select: none;
258
615
  }
259
616
 
260
617
  .token-list {
@@ -290,3 +647,113 @@
290
647
  text-overflow: ellipsis;
291
648
  white-space: nowrap;
292
649
  }
650
+
651
+ /* A trailing icon on the color row, square at the row's height. */
652
+ .color-icon {
653
+ flex: none;
654
+ display: grid;
655
+ place-items: center;
656
+ width: 24px;
657
+ height: 24px;
658
+ padding: 0;
659
+ border: 0;
660
+ border-radius: var(--radius-action);
661
+ background: transparent;
662
+ color: var(--color-muted);
663
+ cursor: pointer;
664
+ }
665
+
666
+ .color-icon:hover,
667
+ .color-icon[aria-expanded='true'] {
668
+ background: var(--color-surface);
669
+ color: var(--color-text);
670
+ }
671
+
672
+ .color-icon:disabled {
673
+ opacity: 0.5;
674
+ cursor: not-allowed;
675
+ }
676
+
677
+ .color-icon svg {
678
+ width: 14px;
679
+ height: 14px;
680
+ fill: currentColor;
681
+ }
682
+
683
+ /*
684
+ * A value picked from a fixed list. The native select draws its value as the
685
+ * field's text and the chevron sits over its right edge, so the platform list
686
+ * opens on press and the field keeps the height of every other one.
687
+ */
688
+ .glyph-field.is-select {
689
+ position: relative;
690
+ }
691
+
692
+ .glyph-select {
693
+ appearance: none;
694
+ padding-right: 22px;
695
+ cursor: pointer;
696
+ }
697
+
698
+ .select-chevron {
699
+ position: absolute;
700
+ right: 6px;
701
+ width: 12px;
702
+ height: 12px;
703
+ fill: none;
704
+ stroke: var(--color-muted);
705
+ stroke-width: 1.5;
706
+ pointer-events: none;
707
+ }
708
+
709
+ /* A raw marker inside a field sits before its menu, clear of the value. */
710
+ .glyph-field .raw {
711
+ padding: 0 2px 0 var(--space-xs);
712
+ }
713
+
714
+ /* Mutually exclusive choices for one value, drawn as one field split evenly. */
715
+ .segmented {
716
+ display: grid;
717
+ grid-template-columns: repeat(4, minmax(0, 1fr));
718
+ gap: 2px;
719
+ min-height: 24px;
720
+ padding: 1px;
721
+ border: 1px solid var(--color-border);
722
+ border-radius: var(--radius-action);
723
+ background: var(--color-surface);
724
+ }
725
+
726
+ .segment {
727
+ display: grid;
728
+ place-items: center;
729
+ min-height: 22px;
730
+ padding: 0;
731
+ border: 0;
732
+ border-radius: var(--radius-action);
733
+ background: transparent;
734
+ color: var(--color-muted);
735
+ cursor: pointer;
736
+ }
737
+
738
+ .segment:hover {
739
+ color: var(--color-text);
740
+ }
741
+
742
+ .segment[aria-pressed='true'] {
743
+ background: var(--color-chrome);
744
+ color: var(--color-text);
745
+ }
746
+
747
+ .segment:disabled {
748
+ opacity: 0.5;
749
+ cursor: not-allowed;
750
+ }
751
+
752
+ .segment svg {
753
+ width: 14px;
754
+ height: 14px;
755
+ fill: none;
756
+ stroke: currentColor;
757
+ stroke-width: 1.5;
758
+ stroke-linecap: round;
759
+ }
@@ -0,0 +1,69 @@
1
+ /** @jsxImportSource preact */
2
+ import type { JSX } from 'preact'
3
+ import { Field, type Scrub } from '@/canvas/client/inspector/field'
4
+
5
+ /** The modes a size takes besides a number, by the name the field shows. */
6
+ const MODES = ['Fill', 'Fit'] as const
7
+
8
+ interface SizeFieldProps {
9
+ readonly label: string
10
+ readonly glyph: string
11
+ /** The value as the panel shows it, a mode name or a number. */
12
+ readonly initial: string
13
+ readonly isBusy: boolean
14
+ /** Takes a typed number or a picked mode name, each as shown. */
15
+ readonly onCommit: (value: string) => void
16
+ readonly scrub?: Scrub
17
+ }
18
+
19
+ /**
20
+ * A size that takes a number or a mode. The chevron is a native select, so
21
+ * its list opens above the panel's clipping and answers the keyboard as the
22
+ * platform does, and typing a number replaces the mode.
23
+ */
24
+ export function SizeField({
25
+ label,
26
+ glyph,
27
+ initial,
28
+ isBusy,
29
+ onCommit,
30
+ scrub,
31
+ }: SizeFieldProps): JSX.Element {
32
+ const mode = (MODES as readonly string[]).includes(initial) ? initial : ''
33
+ return (
34
+ <Field
35
+ label={label}
36
+ glyph={glyph}
37
+ initial={initial}
38
+ isBusy={isBusy}
39
+ onCommit={onCommit}
40
+ scrub={scrub}
41
+ trailing={
42
+ <span class="size-mode">
43
+ <select
44
+ class="size-mode-select"
45
+ aria-label={`${label} mode`}
46
+ value={mode}
47
+ disabled={isBusy}
48
+ onChange={(event) => {
49
+ const picked = event.currentTarget.value
50
+ if (picked && picked !== mode) onCommit(picked)
51
+ }}
52
+ >
53
+ <option value="" disabled hidden>
54
+ Fixed
55
+ </option>
56
+ {MODES.map((name) => (
57
+ <option key={name} value={name}>
58
+ {name}
59
+ </option>
60
+ ))}
61
+ </select>
62
+ <svg class="size-mode-chevron" viewBox="0 0 16 16" aria-hidden="true">
63
+ <path d="M4.5 6.5 8 10l3.5-3.5" />
64
+ </svg>
65
+ </span>
66
+ }
67
+ />
68
+ )
69
+ }
@@ -0,0 +1,109 @@
1
+ /** @jsxImportSource preact */
2
+ import type { JSX } from 'preact'
3
+ import { Field, type Scrub } from '@/canvas/client/inspector/field'
4
+
5
+ /** The menu entry that hands the value back to the field for typing. */
6
+ const RAW = ''
7
+
8
+ interface TokenSelectProps {
9
+ readonly label: string
10
+ readonly glyph: string
11
+ /** The value as the panel shows it, a token's name where one stands. */
12
+ readonly initial: string
13
+ /** One token group's names, in the order the token sheet sorts them. */
14
+ readonly tokens: readonly string[]
15
+ /** The token the element names, where it names one. */
16
+ readonly token?: string
17
+ readonly isRaw: boolean
18
+ readonly isBusy: boolean
19
+ readonly isWide?: boolean
20
+ readonly placeholder?: string
21
+ /** Takes a typed value as typed. */
22
+ readonly onCommit: (value: string) => void
23
+ /** Takes the name of a token picked from the menu or typed whole. */
24
+ readonly onPick: (name: string) => void
25
+ readonly scrub?: Scrub
26
+ }
27
+
28
+ /**
29
+ * A value that takes a token or a typed literal. The menu lists the group's
30
+ * tokens first and a raw value last, which returns focus to the field, so a
31
+ * token is one pick away and a literal is still one keystroke. A group with no
32
+ * tokens leaves the field alone, with no menu and no raw marker.
33
+ */
34
+ export function TokenSelect({
35
+ label,
36
+ glyph,
37
+ initial,
38
+ tokens,
39
+ token,
40
+ isRaw,
41
+ isBusy,
42
+ isWide,
43
+ placeholder,
44
+ onCommit,
45
+ onPick,
46
+ scrub,
47
+ }: TokenSelectProps): JSX.Element {
48
+ const hasTokens = tokens.length > 0
49
+ return (
50
+ <Field
51
+ label={label}
52
+ glyph={glyph}
53
+ initial={initial}
54
+ isBusy={isBusy}
55
+ isWide={isWide}
56
+ placeholder={placeholder}
57
+ onCommit={(typed) => {
58
+ if (tokens.includes(typed)) onPick(typed)
59
+ else onCommit(typed)
60
+ }}
61
+ scrub={scrub}
62
+ trailing={
63
+ hasTokens ? (
64
+ <>
65
+ {isRaw ? (
66
+ <span
67
+ class="raw"
68
+ title="Set as a raw value, so it will not follow the theme. Pick a token to fix it"
69
+ >
70
+ raw
71
+ </span>
72
+ ) : null}
73
+ <span class="size-mode">
74
+ <select
75
+ class="size-mode-select"
76
+ aria-label={`${label} tokens`}
77
+ value={token ?? RAW}
78
+ disabled={isBusy}
79
+ onChange={(event) => {
80
+ const picked = event.currentTarget.value
81
+ if (picked === RAW) {
82
+ event.currentTarget
83
+ .closest('.glyph-field')
84
+ ?.querySelector<HTMLInputElement>('.glyph-field-input')
85
+ ?.focus()
86
+ } else if (picked !== token) onPick(picked)
87
+ }}
88
+ >
89
+ {tokens.map((name) => (
90
+ <option key={name} value={name}>
91
+ {name}
92
+ </option>
93
+ ))}
94
+ <option value={RAW}>Raw value</option>
95
+ </select>
96
+ <svg
97
+ class="size-mode-chevron"
98
+ viewBox="0 0 16 16"
99
+ aria-hidden="true"
100
+ >
101
+ <path d="M4.5 6.5 8 10l3.5-3.5" />
102
+ </svg>
103
+ </span>
104
+ </>
105
+ ) : null
106
+ }
107
+ />
108
+ )
109
+ }