@erclx/canon 5.53.0 → 5.54.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 (94) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +10 -9
  3. package/claude/.claude-plugin/plugin.json +1 -1
  4. package/claude/skills/canon-cli/SKILL.md +5 -5
  5. package/claude/skills/design-identity/REQUIREMENT.md +2 -2
  6. package/claude/skills/design-identity/SKILL.md +6 -6
  7. package/claude/skills/design-record/REQUIREMENT.md +2 -2
  8. package/claude/skills/design-record/SKILL.md +4 -4
  9. package/claude/skills/design-record/references/folder.md +1 -1
  10. package/claude/skills/design-taste/REQUIREMENT.md +1 -1
  11. package/claude/skills/draw-figure/REQUIREMENT.md +1 -1
  12. package/claude/skills/draw-figure/SKILL.md +1 -1
  13. package/claude/skills/pick-by-eye/REQUIREMENT.md +4 -4
  14. package/claude/skills/pick-by-eye/SKILL.md +34 -41
  15. package/claude/skills/pick-by-eye/references/board.md +42 -0
  16. package/claude/skills/pick-by-eye/references/live-arms.md +3 -3
  17. package/claude/skills/plan-groundwork/SKILL.md +1 -1
  18. package/claude/skills/teach-craft/REQUIREMENT.md +1 -1
  19. package/claude/skills/teach-craft/SKILL.md +32 -1
  20. package/claude/skills/teach-craft/references/adopted.md +113 -0
  21. package/claude/skills/teach-craft/references/lesson-craft.md +10 -2
  22. package/claude/skills/teach-craft/references/pedagogy.md +29 -2
  23. package/docs/agents/install-and-sync.md +0 -8
  24. package/docs/target/migrations.md +8 -3
  25. package/docs/target/projects.md +1 -1
  26. package/docs/workflow/skill-map.md +0 -1
  27. package/docs/workflow/visual-design-workflow.md +1 -4
  28. package/governance/detect.toml +1 -6
  29. package/governance/renames.toml +0 -1
  30. package/package.json +2 -5
  31. package/src/claude/cases/misc.ts +0 -5
  32. package/src/claude/skills-audit.ts +1 -0
  33. package/src/cli/help.ts +0 -3
  34. package/src/cli.ts +0 -2
  35. package/src/commands/design.ts +16 -315
  36. package/src/commands/sync.ts +4 -5
  37. package/src/design/regen.ts +5 -32
  38. package/src/design/render.ts +1 -1
  39. package/src/design/tokens.ts +3 -2
  40. package/src/gate/stages.ts +4 -10
  41. package/src/gov/audit.ts +1 -6
  42. package/src/markdown/scope.ts +2 -4
  43. package/src/shipped/references.ts +1 -2
  44. package/src/sync/check.ts +4 -29
  45. package/src/sync/stamp.ts +6 -6
  46. package/src/sync/target.ts +1 -4
  47. package/tooling/claude/reference.md +1 -1
  48. package/tooling/claude/seeds/canon/DESIGN.md +1 -1
  49. package/claude/skills/canvas-board/REQUIREMENT.md +0 -35
  50. package/claude/skills/canvas-board/SKILL.md +0 -143
  51. package/claude/skills/canvas-board/references/canvas.md +0 -170
  52. package/governance/rules/ui/canvas.md +0 -13
  53. package/src/canvas/address.ts +0 -227
  54. package/src/canvas/arrange.ts +0 -66
  55. package/src/canvas/capture.ts +0 -391
  56. package/src/canvas/client/app.tsx +0 -188
  57. package/src/canvas/client/inspector/align-grid.tsx +0 -162
  58. package/src/canvas/client/inspector/color-field.tsx +0 -398
  59. package/src/canvas/client/inspector/color-inputs.tsx +0 -139
  60. package/src/canvas/client/inspector/color-picker.tsx +0 -668
  61. package/src/canvas/client/inspector/color-space.ts +0 -240
  62. package/src/canvas/client/inspector/field.tsx +0 -263
  63. package/src/canvas/client/inspector/fields.css +0 -763
  64. package/src/canvas/client/inspector/section.tsx +0 -27
  65. package/src/canvas/client/inspector/size-field.tsx +0 -69
  66. package/src/canvas/client/inspector/token-select.tsx +0 -109
  67. package/src/canvas/client/inspector/values.ts +0 -297
  68. package/src/canvas/client/inspector.tsx +0 -692
  69. package/src/canvas/client/keys.ts +0 -71
  70. package/src/canvas/client/layers.tsx +0 -118
  71. package/src/canvas/client/main.tsx +0 -71
  72. package/src/canvas/client/pages-panel.tsx +0 -212
  73. package/src/canvas/client/panel-handle.tsx +0 -90
  74. package/src/canvas/client/selection.tsx +0 -199
  75. package/src/canvas/client/shell.css +0 -762
  76. package/src/canvas/client/state.ts +0 -792
  77. package/src/canvas/client/surface.tsx +0 -803
  78. package/src/canvas/client/theme-panel.tsx +0 -55
  79. package/src/canvas/client/tools.tsx +0 -153
  80. package/src/canvas/content.ts +0 -781
  81. package/src/canvas/edit.ts +0 -575
  82. package/src/canvas/editing.ts +0 -203
  83. package/src/canvas/fit.ts +0 -238
  84. package/src/canvas/global.d.ts +0 -15
  85. package/src/canvas/history.ts +0 -116
  86. package/src/canvas/index.html +0 -20
  87. package/src/canvas/server.ts +0 -766
  88. package/src/canvas/tokens.ts +0 -221
  89. package/src/commands/canvas.ts +0 -1097
  90. package/src/design/adapter.ts +0 -59
  91. package/src/design/base.css +0 -203
  92. package/src/design/brand-favicon.ts +0 -18
  93. package/src/design/neutral.ts +0 -127
  94. package/src/design/project-fonts.ts +0 -392
@@ -1,59 +0,0 @@
1
- import { basename, join } from 'node:path'
2
- import { DESIGN_BASE_CSS } from '@/design/regen'
3
- import type { InstalledFile, SyncAdapter } from '@/sync/engine'
4
-
5
- /**
6
- * The channel a design value reaches a target on.
7
- *
8
- * Base plus a target-owned override, reconciled by the three-way merge the sync
9
- * engine already implements. Overwrite was dropped before this was built and
10
- * the reason is a measurement rather than an argument: a headless sync in
11
- * another domain deleted a deploy job, replaced a screenshot harness, and
12
- * reverted a shipped fix, because naming a stack was read as consent to lose an
13
- * edit inside it.
14
- *
15
- * Layering is what bounds what the merge has to arbitrate. Nothing a project
16
- * wrote lives in the base, so the base changes freely, and the merge shrinks to
17
- * the properties a target actually overrode.
18
- */
19
-
20
- /** The folder a design install writes into, under the target's `.claude/`. */
21
- export const DESIGN_INSTALL_DIR = join('.claude', 'design')
22
-
23
- /**
24
- * Where a target's own values go. `projectSubdir` makes every file here
25
- * project-authored by location rather than by the name inference, so an
26
- * override named `base.css` is still the target's and never overwritten.
27
- *
28
- * It ships absent rather than empty. An empty override is a file a project did
29
- * not ask for and did not write, the merge already handles a missing side, and
30
- * an empty file invites a target to fill it before it has an opinion.
31
- */
32
- export const DESIGN_PROJECT_SUBDIR = 'project'
33
-
34
- export function designSourceDir(root: string): string {
35
- return join(root, 'src', 'design')
36
- }
37
-
38
- export function createDesignAdapter(root: string): SyncAdapter {
39
- const base = join(root, DESIGN_BASE_CSS)
40
-
41
- return {
42
- banner: 'canon design sync',
43
- label: 'design',
44
- missingMessage:
45
- "No design surface found in target. Run 'canon design install' first.",
46
- unit: 'changes',
47
- installPattern: '**/*.css',
48
- installedRoot: (target: string) => join(target, DESIGN_INSTALL_DIR),
49
- /**
50
- * One shipped file, matched by name the way the gov adapter matches a rule,
51
- * so a base that moved inside the toolkit still syncs into the place the
52
- * target already holds it.
53
- */
54
- locateSource: (file: InstalledFile) =>
55
- basename(file.path) === basename(base) ? base : undefined,
56
- projectSubdir: DESIGN_PROJECT_SUBDIR,
57
- stamp: { domain: 'design', toolkitRoot: root },
58
- }
59
- }
@@ -1,203 +0,0 @@
1
- /* Generated by `canon design regen` from src/design/neutral.ts. Do not edit.
2
- This is the base half of a design install. A target overrides a value in
3
- .claude/design/project/, which the three-way merge leaves alone, so this
4
- file changes freely and nothing a project wrote lives in it. */
5
-
6
- :root {
7
- --color-background: #0e0e0e;
8
- --color-surface: #141414;
9
- --color-chrome: #1a1a1a;
10
- --color-border: #292929;
11
- --color-text: #dddddd;
12
- --color-text-body: #b2b2b2;
13
- --color-text-secondary: #8e8e8e;
14
- --color-muted: #7f7f7f;
15
- --color-accent: #ececec;
16
- --color-success: #61c454;
17
- --color-light-background: #fafafa;
18
- --color-light-surface: #f1f1f1;
19
- --color-light-chrome: #e8e8e8;
20
- --color-light-text: #252525;
21
- --color-light-text-body: #444444;
22
- --color-light-text-secondary: #5f5f5f;
23
- --color-light-muted: #6c6c6c;
24
- --color-light-accent: #191919;
25
- --color-light-success: #2d6b22;
26
- --color-light-border: #d4d4d4;
27
- --space-xs: 0.25rem;
28
- --space-sm: 0.5rem;
29
- --space-md: 0.875rem;
30
- --space-lg: 1.5rem;
31
- --space-xl: 2.5rem;
32
- --space-2xl: 4rem;
33
- --space-3xl: 6rem;
34
- --t0: 3.175rem;
35
- --t1: 2.375rem;
36
- --t2: 1.375rem;
37
- --t3: 1.125rem;
38
- --t4: 0.9375rem;
39
- --t5: 0.8125rem;
40
- --t6: 0.6875rem;
41
- --type-display-family: Geist Variable, DejaVu Sans, sans-serif;
42
- --type-display-size: 2.375rem;
43
- --type-display-lh: 1.3;
44
- --type-page-display-family: Geist Variable, DejaVu Sans, sans-serif;
45
- --type-page-display-size: 3.175rem;
46
- --type-page-display-lh: 1.1;
47
- --type-heading-family: Geist Variable, DejaVu Sans, sans-serif;
48
- --type-heading-size: 1.375rem;
49
- --type-heading-lh: 1.3;
50
- --type-body-family: Geist Variable, DejaVu Sans, sans-serif;
51
- --type-body-size: 0.9375rem;
52
- --type-body-lh: 1.65;
53
- --type-label-family: Geist Variable, DejaVu Sans, sans-serif;
54
- --type-label-size: 0.8125rem;
55
- --type-label-lh: 1.45;
56
- --type-code-family: Noto Sans Mono, DejaVu Sans Mono, monospace;
57
- --type-code-size: 0.8125rem;
58
- --type-code-lh: 1.3;
59
- --radius-frame: 12px;
60
- --radius-panel: 10px;
61
- --radius-action: 7px;
62
- --radius-pill: 999px;
63
- --radius-marker: 999px;
64
- }
65
-
66
- [data-theme='light'] {
67
- --color-background: var(--color-light-background);
68
- --color-surface: var(--color-light-surface);
69
- --color-chrome: var(--color-light-chrome);
70
- --color-text: var(--color-light-text);
71
- --color-text-body: var(--color-light-text-body);
72
- --color-text-secondary: var(--color-light-text-secondary);
73
- --color-muted: var(--color-light-muted);
74
- --color-accent: var(--color-light-accent);
75
- --color-success: var(--color-light-success);
76
- --color-border: var(--color-light-border);
77
- }
78
-
79
- /* status
80
- A dot and a word, not a pill. A bordered uppercase chip is a second
81
- vocabulary for a fact the dot already carries, so the marker is the only
82
- status shape and `.is-done` is the only variant. */
83
-
84
- .status {
85
- display: inline-flex;
86
- align-items: center;
87
- gap: 0.42rem;
88
- font-size: 0.75rem;
89
- font-weight: 500;
90
- letter-spacing: 0;
91
- text-transform: none;
92
- color: var(--color-muted);
93
- white-space: nowrap;
94
- }
95
-
96
- .status::before {
97
- content: '';
98
- width: 6px;
99
- height: 6px;
100
- flex: none;
101
- border-radius: var(--radius-marker);
102
- background: var(--color-border);
103
- }
104
-
105
- .status.is-done::before {
106
- background: var(--color-accent);
107
- }
108
-
109
- /* scrollbar
110
- Every scrolling region takes the same bar, the page included. Scoping it
111
- to one component leaves its neighbors on the browser default, which is
112
- what reads as two designs on one page. `scrollbar-color` covers Firefox
113
- and the `::-webkit-` rules cover the rest, both from the same two tokens
114
- so the two engines cannot drift apart. */
115
-
116
- * {
117
- scrollbar-width: thin;
118
- scrollbar-color: var(--color-border) transparent;
119
- }
120
-
121
- *::-webkit-scrollbar {
122
- width: 10px;
123
- height: 10px;
124
- }
125
-
126
- *::-webkit-scrollbar-track {
127
- background: transparent;
128
- }
129
-
130
- *::-webkit-scrollbar-thumb {
131
- border-radius: var(--radius-marker);
132
- background: var(--color-border);
133
- /* Inset by painting a border in the page color, which is what keeps the
134
- thumb off the edges without a second element. */
135
- border: 3px solid var(--color-background);
136
- }
137
-
138
- *::-webkit-scrollbar-thumb:hover {
139
- background: var(--color-muted);
140
- }
141
-
142
- *::-webkit-scrollbar-corner {
143
- background: transparent;
144
- }
145
-
146
- /* hand-drawn-figure
147
- The hand-drawn SVG diagram figure and its caption, sized wider than the
148
- reading measure and cleared past an outline rail on a wide viewport. */
149
-
150
- /* ---- Figures: hand-drawn, and wider than the measure ---- */
151
-
152
- :root {
153
- --figure-hand: 'Virgil', 'Excalifont', cursive;
154
- --figure-wide: 64rem;
155
- }
156
-
157
- figure {
158
- margin: 2.75rem 0;
159
- width: var(--figure-wide);
160
- max-width: 92vw;
161
- margin-left: 50%;
162
- transform: translateX(-50%);
163
- }
164
-
165
- figure svg {
166
- width: 100%;
167
- height: auto;
168
- display: block;
169
- }
170
-
171
- /* CSS beats an SVG presentation attribute, so the diagrams pick up the
172
- hand face without editing a single lesson. */
173
- figure svg text {
174
- font-family: var(--figure-hand);
175
- }
176
-
177
- figcaption {
178
- font-family: var(--figure-hand);
179
- font-size: 1rem;
180
- color: var(--color-muted);
181
- margin: 1rem auto 0;
182
- line-height: 1.5;
183
- max-width: 52rem;
184
- }
185
-
186
- /* A figure breaks the measure and must still clear an outline rail. Deriving
187
- the ceiling from the viewport does not hold, since a rail positioned from
188
- the centre keeps overlapping as the window widens, so the ceiling is fixed
189
- instead. */
190
- @media (min-width: 1421px) {
191
- figure {
192
- max-width: 54rem;
193
- }
194
- }
195
-
196
- @media (max-width: 640px) {
197
- figure {
198
- width: 100%;
199
- max-width: 100%;
200
- margin-left: 0;
201
- transform: none;
202
- }
203
- }
@@ -1,18 +0,0 @@
1
- import { readFileSync } from 'node:fs'
2
- import { join } from 'node:path'
3
- import { FAVICON_COLORS, renderFavicon } from '@/design/favicon'
4
- import { PROJECT_ROOT } from '@/roots/project'
5
-
6
- const BRAND_MARK = 'assets/brand/mark.svg'
7
-
8
- /**
9
- * The brand icon, built from the mark and the favicon's own pair, the same two
10
- * inputs `web/public/favicon.svg` is generated from. Every surface that shows
11
- * the icon reads it here rather than keeping a copy that drifts from the mark.
12
- * It sits apart from `favicon.ts` because the card build imports that module
13
- * through an `@` alias pointed at `web/src`, so it must import nothing.
14
- */
15
- export function brandFavicon(): string {
16
- const mark = readFileSync(join(PROJECT_ROOT, BRAND_MARK), 'utf8')
17
- return renderFavicon(mark, FAVICON_COLORS)
18
- }
@@ -1,127 +0,0 @@
1
- import type { ColorToken, DesignTokens } from '@/design/tokens'
2
- import { TOKENS } from '@/design/tokens'
3
-
4
- /**
5
- * The token set `canon design install` hands a target, achromatic so the only
6
- * palette a target renders is one it wrote under `.claude/design/project/`.
7
- * Type, spacing, and radii are this repository's by spread, and only the color
8
- * list is a second copy, held to the same roles by `neutral.test.ts`.
9
- *
10
- * Grounds, surface, chrome, and border take the OKLCH lightness of the toolkit
11
- * role they replace at chroma 0. Text roles are solved against `surface` for
12
- * the contrast targets `TOKENS` records (13.5, 8.6, 5.6, 4.6), and the accent
13
- * for 15.5, past `text` so it still reads apart from body copy. The state
14
- * roles keep the toolkit's values, since a state color is a meaning rather
15
- * than a palette.
16
- */
17
-
18
- const DARK_GROUNDS = ['background', 'surface'] as const
19
- const LIGHT_GROUNDS = ['light-background', 'light-surface'] as const
20
-
21
- function toolkitRole(role: string): ColorToken {
22
- const token = TOKENS.color.find((candidate) => candidate.role === role)
23
- if (token === undefined) throw new Error(`TOKENS declares no ${role} role`)
24
- return token
25
- }
26
-
27
- const NEUTRAL_COLOR: readonly ColorToken[] = [
28
- { role: 'background', intent: 'page canvas', value: '#0e0e0e' },
29
- {
30
- role: 'surface',
31
- intent: 'cards, panels, raised blocks',
32
- value: '#141414',
33
- },
34
- {
35
- role: 'chrome',
36
- intent: 'the window titlebar, one step above the canvas',
37
- value: '#1a1a1a',
38
- },
39
- { role: 'border', intent: 'every rule and panel edge', value: '#292929' },
40
- {
41
- role: 'text',
42
- intent: 'headings, counts, emphasized runs',
43
- value: '#dddddd',
44
- grounds: DARK_GROUNDS,
45
- },
46
- {
47
- role: 'text-body',
48
- intent: 'default body copy',
49
- value: '#b2b2b2',
50
- grounds: DARK_GROUNDS,
51
- },
52
- {
53
- role: 'text-secondary',
54
- intent: 'labels, captions, supporting copy',
55
- value: '#8e8e8e',
56
- grounds: DARK_GROUNDS,
57
- },
58
- {
59
- role: 'muted',
60
- intent: 'the faintest step, trailing notes',
61
- value: '#7f7f7f',
62
- grounds: DARK_GROUNDS,
63
- },
64
- {
65
- role: 'accent',
66
- intent: 'install command, mark, primary action',
67
- value: '#ececec',
68
- grounds: DARK_GROUNDS,
69
- },
70
- toolkitRole('success'),
71
- toolkitRole('warning'),
72
- toolkitRole('error'),
73
- {
74
- role: 'light-background',
75
- intent: 'page canvas on a light ground',
76
- value: '#fafafa',
77
- },
78
- {
79
- role: 'light-surface',
80
- intent: 'cards and panels on a light ground',
81
- value: '#f1f1f1',
82
- },
83
- {
84
- role: 'light-chrome',
85
- intent: 'the window titlebar, one step above the canvas',
86
- value: '#e8e8e8',
87
- },
88
- {
89
- role: 'light-text',
90
- intent: 'primary text on a light ground',
91
- value: '#252525',
92
- grounds: LIGHT_GROUNDS,
93
- },
94
- {
95
- role: 'light-text-body',
96
- intent: 'default body copy on a light ground',
97
- value: '#444444',
98
- grounds: LIGHT_GROUNDS,
99
- },
100
- {
101
- role: 'light-text-secondary',
102
- intent: 'labels, captions, supporting copy on a light ground',
103
- value: '#5f5f5f',
104
- grounds: LIGHT_GROUNDS,
105
- },
106
- {
107
- role: 'light-muted',
108
- intent: 'secondary text on a light ground',
109
- value: '#6c6c6c',
110
- grounds: LIGHT_GROUNDS,
111
- },
112
- {
113
- role: 'light-accent',
114
- intent: 'links and primary action on light',
115
- value: '#191919',
116
- grounds: LIGHT_GROUNDS,
117
- },
118
- toolkitRole('light-success'),
119
- {
120
- role: 'light-border',
121
- intent: 'rules and panel edges on light',
122
- value: '#d4d4d4',
123
- verify: true,
124
- },
125
- ]
126
-
127
- export const NEUTRAL_TOKENS: DesignTokens = { ...TOKENS, color: NEUTRAL_COLOR }