@erclx/canon 5.24.0 → 5.25.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.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/backlog-triage/REQUIREMENT.md +2 -2
- package/claude/skills/backlog-triage/SKILL.md +19 -6
- package/claude/skills/canvas/REQUIREMENT.md +49 -0
- package/claude/skills/canvas/SKILL.md +106 -0
- package/claude/skills/design-extract/REQUIREMENT.md +1 -1
- package/claude/skills/draft-doc/REQUIREMENT.md +1 -1
- package/claude/skills/draft-figure/REQUIREMENT.md +14 -3
- package/claude/skills/draft-figure/SKILL.md +24 -51
- package/claude/skills/draft-figure/references/architecture-set.md +35 -0
- package/claude/skills/draft-figure/references/hand-drawn.md +31 -0
- package/claude/skills/draft-figure/references/mermaid.md +55 -0
- package/claude/skills/git-followup/SKILL.md +17 -6
- package/claude/skills/git-pr/references/evidence.md +4 -2
- package/claude/skills/git-split/SKILL.md +1 -1
- package/claude/skills/index-lookup/SKILL.md +2 -2
- package/claude/skills/review-address/SKILL.md +6 -13
- package/claude/skills/review-address/references/rest-reads.md +43 -0
- package/claude/skills/review-craft/references/ui.md +7 -1
- package/claude/skills/review-pr/REQUIREMENT.md +1 -0
- package/claude/skills/review-pr/SKILL.md +9 -3
- package/claude/skills/review-pr/references/close-out.md +2 -2
- package/claude/skills/review-pr/references/post.md +17 -5
- package/claude/skills/review-pr/references/scope.md +10 -8
- package/claude/skills/review-ui/SKILL.md +12 -4
- package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +30 -1
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +4 -0
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +4 -4
- package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +6 -0
- package/claude/skills/role-orchestrator/scripts/poll.ts +90 -27
- package/claude/skills/role-orchestrator/scripts/repo.ts +3 -5
- package/claude/skills/role-orchestrator/scripts/watch.ts +4 -7
- package/docs/agents/canvas.md +1 -1
- package/docs/agents/commands.md +45 -45
- package/docs/agents/context-audit-checks.md +2 -2
- package/docs/agents/index.md +0 -1
- package/docs/agents/key-changes.md +3 -3
- package/docs/agents/pr-evidence.md +11 -2
- package/docs/agents/pr-reads.md +5 -4
- package/docs/target/projects.md +4 -4
- package/docs/workflow/ai-workflow.md +0 -1
- package/docs/workflow/skill-map.md +3 -3
- package/docs/workflow/visual-design-workflow.md +1 -0
- package/governance/renames.toml +0 -4
- package/governance/rules/standards/markdown.md +1 -0
- package/package.json +1 -2
- package/scripts/core/install-check.sh +1 -1
- package/src/audits/catalog.ts +2 -2
- package/src/canvas/client/inspector/field.tsx +200 -0
- package/src/canvas/client/inspector/fields.css +151 -0
- package/src/canvas/client/inspector/section.tsx +27 -0
- package/src/canvas/client/inspector/values.ts +60 -0
- package/src/canvas/client/inspector.tsx +154 -115
- package/src/canvas/client/shell.css +0 -101
- package/src/claude/cases/misc.ts +5 -0
- package/src/claude/cases/workflow.ts +1 -1
- package/src/claude/seeds.ts +0 -1
- package/src/commands/design.ts +2 -64
- package/src/commands/pr.ts +219 -202
- package/src/context/audit.ts +7 -6
- package/src/help.ts +2 -2
- package/src/migrate/skill-names.ts +7 -1
- package/src/pr/evidence.ts +25 -5
- package/src/pr/rest.ts +137 -0
- package/standards/canvas.md +112 -0
- package/standards/context.md +2 -1
- package/standards/figures.md +0 -1
- package/standards/index.md +1 -1
- package/standards/markdown.md +1 -0
- package/standards/mermaid.md +1 -7
- package/tooling/claude/reference.md +0 -5
- package/claude/skills/draft-diagram/REQUIREMENT.md +0 -45
- package/claude/skills/draft-diagram/SKILL.md +0 -177
- package/docs/agents/design-board.md +0 -33
- package/governance/rules/standards/diagrams.md +0 -14
- package/governance/rules/standards/mermaid.md +0 -15
- package/scripts/core/check-gallery-exclusion.sh +0 -50
- package/src/design/board.ts +0 -576
- package/standards/diagrams.md +0 -132
- package/tooling/claude/seeds/.claude/diagrams/index.md +0 -8
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
/** @jsxImportSource preact */
|
|
2
2
|
import type { JSX } from 'preact'
|
|
3
3
|
import { addressOf, elementAt, excerpt, isRawText } from '@/canvas/address'
|
|
4
|
+
import {
|
|
5
|
+
Field,
|
|
6
|
+
ReadOnlyField,
|
|
7
|
+
type Scrub,
|
|
8
|
+
} from '@/canvas/client/inspector/field'
|
|
9
|
+
import { Section } from '@/canvas/client/inspector/section'
|
|
10
|
+
import {
|
|
11
|
+
clampScrub,
|
|
12
|
+
displayValue,
|
|
13
|
+
toCssValue,
|
|
14
|
+
} from '@/canvas/client/inspector/values'
|
|
4
15
|
import {
|
|
5
16
|
currentPage,
|
|
6
17
|
editElement,
|
|
@@ -16,8 +27,7 @@ import {
|
|
|
16
27
|
tokens,
|
|
17
28
|
writeError,
|
|
18
29
|
} from '@/canvas/client/state'
|
|
19
|
-
|
|
20
|
-
const FIELDS = ['x', 'y', 'width', 'height'] as const
|
|
30
|
+
import '@/canvas/client/inspector/fields.css'
|
|
21
31
|
|
|
22
32
|
/** Elements the parser never gives content, so they hold no text to edit. */
|
|
23
33
|
const VOID = new Set([
|
|
@@ -37,44 +47,43 @@ const VOID = new Set([
|
|
|
37
47
|
])
|
|
38
48
|
|
|
39
49
|
interface StyleField {
|
|
50
|
+
/** The full word, which names the field. */
|
|
40
51
|
readonly label: string
|
|
52
|
+
readonly glyph: string
|
|
41
53
|
readonly property: string
|
|
42
|
-
readonly
|
|
54
|
+
readonly isWide?: boolean
|
|
55
|
+
readonly placeholder?: string
|
|
43
56
|
}
|
|
44
57
|
|
|
45
58
|
/**
|
|
46
|
-
* The basic set,
|
|
47
|
-
* property outside it, so
|
|
59
|
+
* The basic set, grouped as the panel lists it. The writer refuses any
|
|
60
|
+
* property outside it, so these lists and the writer's have to agree.
|
|
48
61
|
*/
|
|
49
|
-
const
|
|
50
|
-
{ label: '
|
|
51
|
-
{ label: '
|
|
52
|
-
{ label: 'size', property: 'font-size' },
|
|
53
|
-
{ label: 'weight', property: 'font-weight' },
|
|
54
|
-
{ label: 'width', property: 'width' },
|
|
55
|
-
{ label: 'height', property: 'height' },
|
|
56
|
-
{ label: 'padding', property: 'padding' },
|
|
57
|
-
{ label: 'gap', property: 'gap' },
|
|
58
|
-
{ label: 'direction', property: 'flex-direction' },
|
|
62
|
+
const SIZE_FIELDS: readonly StyleField[] = [
|
|
63
|
+
{ label: 'width', glyph: 'W', property: 'width' },
|
|
64
|
+
{ label: 'height', glyph: 'H', property: 'height' },
|
|
59
65
|
]
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
67
|
+
const FLEX_FIELDS: readonly StyleField[] = [
|
|
68
|
+
{ label: 'direction', glyph: 'Dir', property: 'flex-direction' },
|
|
69
|
+
{ label: 'gap', glyph: 'Gap', property: 'gap', placeholder: '0' },
|
|
70
|
+
{ label: 'padding', glyph: 'Pad', property: 'padding', isWide: true },
|
|
71
|
+
]
|
|
65
72
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
const TYPE_FIELDS: readonly StyleField[] = [
|
|
74
|
+
{ label: 'size', glyph: 'Size', property: 'font-size' },
|
|
75
|
+
{ label: 'weight', glyph: 'Wt', property: 'font-weight' },
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
const FILL_FIELDS: readonly StyleField[] = [
|
|
79
|
+
{ label: 'color', glyph: 'Fg', property: 'color' },
|
|
80
|
+
{
|
|
81
|
+
label: 'background',
|
|
82
|
+
glyph: 'Bg',
|
|
83
|
+
property: 'background-color',
|
|
84
|
+
placeholder: 'None',
|
|
85
|
+
},
|
|
86
|
+
]
|
|
78
87
|
|
|
79
88
|
function inlineValue(element: Element, property: string): string {
|
|
80
89
|
return 'style' in element
|
|
@@ -137,44 +146,19 @@ function elementName(element: Element): string {
|
|
|
137
146
|
return [tag, ...element.classList].join('.')
|
|
138
147
|
}
|
|
139
148
|
|
|
140
|
-
interface
|
|
149
|
+
interface TokenPickerProps {
|
|
141
150
|
readonly label: string
|
|
142
151
|
readonly initial: string
|
|
143
152
|
readonly isBusy: boolean
|
|
144
153
|
readonly onCommit: (value: string) => void
|
|
145
154
|
}
|
|
146
155
|
|
|
147
|
-
/**
|
|
148
|
-
* Commits on change, which a text input fires on Enter or on leaving it, and
|
|
149
|
-
* sends nothing when the value is what it started at. Escape puts it back.
|
|
150
|
-
*/
|
|
151
|
-
function Field({ label, initial, isBusy, onCommit }: FieldProps): JSX.Element {
|
|
152
|
-
return (
|
|
153
|
-
<input
|
|
154
|
-
class="field"
|
|
155
|
-
type="text"
|
|
156
|
-
aria-label={label}
|
|
157
|
-
title={initial}
|
|
158
|
-
defaultValue={initial}
|
|
159
|
-
disabled={isBusy}
|
|
160
|
-
spellcheck={false}
|
|
161
|
-
onChange={(event) => {
|
|
162
|
-
const value = event.currentTarget.value.trim()
|
|
163
|
-
if (value !== initial.trim()) onCommit(value)
|
|
164
|
-
}}
|
|
165
|
-
onKeyDown={(event) => {
|
|
166
|
-
if (event.key === 'Escape') event.currentTarget.value = initial
|
|
167
|
-
}}
|
|
168
|
-
/>
|
|
169
|
-
)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
156
|
function TokenPicker({
|
|
173
157
|
label,
|
|
174
158
|
initial,
|
|
175
159
|
isBusy,
|
|
176
160
|
onCommit,
|
|
177
|
-
}:
|
|
161
|
+
}: TokenPickerProps): JSX.Element | null {
|
|
178
162
|
const colors =
|
|
179
163
|
tokens.value?.groups?.find((group) => group.kind === 'color')?.tokens ?? []
|
|
180
164
|
if (colors.length === 0) return null
|
|
@@ -219,45 +203,80 @@ function ElementFields({
|
|
|
219
203
|
const commit = (property: string) => (value: string) => {
|
|
220
204
|
if (address) void editElement(frameRef, key, address, property, value)
|
|
221
205
|
}
|
|
222
|
-
const
|
|
206
|
+
const rect = node.getBoundingClientRect()
|
|
207
|
+
const font = currentValue(node, 'font-family')
|
|
223
208
|
const text = node.textContent ?? ''
|
|
224
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Previews into the frame's own inline style and writes through the same
|
|
212
|
+
* edit as typing. A reload mid-drag replaces the document, so a release
|
|
213
|
+
* against one no longer on screen posts nothing.
|
|
214
|
+
*/
|
|
215
|
+
const scrubOf = (property: string): Scrub => ({
|
|
216
|
+
clamp: (value) => clampScrub(property, value),
|
|
217
|
+
begin: () => {
|
|
218
|
+
const original = inlineValue(node, property)
|
|
219
|
+
const style = (node as HTMLElement).style
|
|
220
|
+
return {
|
|
221
|
+
preview: (shown) =>
|
|
222
|
+
style.setProperty(property, toCssValue(property, shown)),
|
|
223
|
+
restore: () =>
|
|
224
|
+
original
|
|
225
|
+
? style.setProperty(property, original)
|
|
226
|
+
: style.removeProperty(property),
|
|
227
|
+
commit: (shown) => {
|
|
228
|
+
if (frameDocuments.value.get(key) !== doc || !node.isConnected) return
|
|
229
|
+
commit(property)(toCssValue(property, shown))
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
const styleField = (field: StyleField): JSX.Element => (
|
|
236
|
+
<Field
|
|
237
|
+
key={field.property}
|
|
238
|
+
label={field.label}
|
|
239
|
+
glyph={field.glyph}
|
|
240
|
+
initial={displayValue(field.property, currentValue(node, field.property))}
|
|
241
|
+
isBusy={isBusy}
|
|
242
|
+
isWide={field.isWide}
|
|
243
|
+
placeholder={field.placeholder}
|
|
244
|
+
onCommit={(typed) =>
|
|
245
|
+
commit(field.property)(toCssValue(field.property, typed))
|
|
246
|
+
}
|
|
247
|
+
scrub={'style' in node ? scrubOf(field.property) : undefined}
|
|
248
|
+
/>
|
|
249
|
+
)
|
|
250
|
+
|
|
225
251
|
return (
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
<
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
{
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
<Field
|
|
255
|
-
label={field.label}
|
|
256
|
-
initial={initial}
|
|
257
|
-
isBusy={isBusy}
|
|
258
|
-
onCommit={commit(field.property)}
|
|
259
|
-
/>
|
|
260
|
-
{field.isColor && isRawInline(node, field.property) ? (
|
|
252
|
+
<>
|
|
253
|
+
<Section title="Layout">
|
|
254
|
+
<ReadOnlyField
|
|
255
|
+
label="x"
|
|
256
|
+
glyph="X"
|
|
257
|
+
value={String(Math.round(rect.left))}
|
|
258
|
+
/>
|
|
259
|
+
<ReadOnlyField
|
|
260
|
+
label="y"
|
|
261
|
+
glyph="Y"
|
|
262
|
+
value={String(Math.round(rect.top))}
|
|
263
|
+
/>
|
|
264
|
+
{SIZE_FIELDS.map(styleField)}
|
|
265
|
+
</Section>
|
|
266
|
+
<Section title="Flex">{FLEX_FIELDS.map(styleField)}</Section>
|
|
267
|
+
<Section title="Typography">
|
|
268
|
+
{TYPE_FIELDS.map(styleField)}
|
|
269
|
+
{font ? (
|
|
270
|
+
<ReadOnlyField label="font" glyph="Font" value={font} isWide />
|
|
271
|
+
) : null}
|
|
272
|
+
</Section>
|
|
273
|
+
<Section title="Fill">
|
|
274
|
+
{FILL_FIELDS.map((field) => {
|
|
275
|
+
const initial = currentValue(node, field.property)
|
|
276
|
+
return (
|
|
277
|
+
<div key={field.property} class="fill-row is-wide">
|
|
278
|
+
{styleField(field)}
|
|
279
|
+
{isRawInline(node, field.property) ? (
|
|
261
280
|
<span
|
|
262
281
|
class="raw"
|
|
263
282
|
title="Set as a raw value, so it will not follow the theme. Pick a token to fix it"
|
|
@@ -265,19 +284,33 @@ function ElementFields({
|
|
|
265
284
|
raw
|
|
266
285
|
</span>
|
|
267
286
|
) : null}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
287
|
+
<TokenPicker
|
|
288
|
+
label={field.label}
|
|
289
|
+
initial={initial}
|
|
290
|
+
isBusy={isBusy}
|
|
291
|
+
onCommit={commit(field.property)}
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
)
|
|
295
|
+
})}
|
|
296
|
+
</Section>
|
|
297
|
+
<Section title="Text">
|
|
298
|
+
{holdsTextAlone(node) ? (
|
|
299
|
+
<Field
|
|
300
|
+
label="text"
|
|
301
|
+
glyph="T"
|
|
302
|
+
initial={text}
|
|
303
|
+
isBusy={isBusy}
|
|
304
|
+
isWide
|
|
305
|
+
onCommit={commit('text')}
|
|
306
|
+
/>
|
|
307
|
+
) : (
|
|
308
|
+
<p class="text-excerpt is-wide" title={excerpt(text)}>
|
|
309
|
+
{excerpt(text)}
|
|
310
|
+
</p>
|
|
311
|
+
)}
|
|
312
|
+
</Section>
|
|
313
|
+
</>
|
|
281
314
|
)
|
|
282
315
|
}
|
|
283
316
|
|
|
@@ -342,14 +375,20 @@ export function Inspector(): JSX.Element {
|
|
|
342
375
|
<p class="detail" title={frame.name}>
|
|
343
376
|
{frame.name}
|
|
344
377
|
</p>
|
|
345
|
-
<
|
|
346
|
-
{
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
378
|
+
<div class="field-grid">
|
|
379
|
+
<ReadOnlyField label="x" glyph="X" value={String(frame.x)} />
|
|
380
|
+
<ReadOnlyField label="y" glyph="Y" value={String(frame.y)} />
|
|
381
|
+
<ReadOnlyField
|
|
382
|
+
label="width"
|
|
383
|
+
glyph="W"
|
|
384
|
+
value={String(frame.width)}
|
|
385
|
+
/>
|
|
386
|
+
<ReadOnlyField
|
|
387
|
+
label="height"
|
|
388
|
+
glyph="H"
|
|
389
|
+
value={String(frame.height)}
|
|
390
|
+
/>
|
|
391
|
+
</div>
|
|
353
392
|
</>
|
|
354
393
|
) : (
|
|
355
394
|
<p class="empty">Select a frame to see its box</p>
|
|
@@ -320,29 +320,6 @@ code {
|
|
|
320
320
|
outline-offset: 6px;
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
.box {
|
|
324
|
-
display: grid;
|
|
325
|
-
grid-template-columns: 1fr 1fr;
|
|
326
|
-
gap: var(--space-xs) var(--space-md);
|
|
327
|
-
margin: var(--space-xs) 0 0;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
.box-row {
|
|
331
|
-
display: flex;
|
|
332
|
-
justify-content: space-between;
|
|
333
|
-
gap: var(--space-sm);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
.box dt {
|
|
337
|
-
color: var(--color-muted);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
.box dd {
|
|
341
|
-
margin: 0;
|
|
342
|
-
color: var(--color-text);
|
|
343
|
-
font-variant-numeric: tabular-nums;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
323
|
.toolbar {
|
|
347
324
|
position: absolute;
|
|
348
325
|
right: var(--space-md);
|
|
@@ -436,84 +413,6 @@ code {
|
|
|
436
413
|
white-space: nowrap;
|
|
437
414
|
}
|
|
438
415
|
|
|
439
|
-
/* One property a row, since a computed color or font stack outgrows a half. */
|
|
440
|
-
.styles {
|
|
441
|
-
grid-template-columns: 1fr;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
.styles dd {
|
|
445
|
-
min-width: 0;
|
|
446
|
-
overflow: hidden;
|
|
447
|
-
text-overflow: ellipsis;
|
|
448
|
-
white-space: nowrap;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
.styles .box-row {
|
|
452
|
-
align-items: center;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
.styles dt {
|
|
456
|
-
flex: none;
|
|
457
|
-
width: 4.5rem;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
.styles dd {
|
|
461
|
-
flex: 1;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
.field-cell {
|
|
465
|
-
display: flex;
|
|
466
|
-
gap: var(--space-xs);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
.field,
|
|
470
|
-
.token-picker {
|
|
471
|
-
min-width: 0;
|
|
472
|
-
min-height: 24px;
|
|
473
|
-
padding: 2px var(--space-xs);
|
|
474
|
-
border: 1px solid var(--color-border);
|
|
475
|
-
border-radius: var(--radius-action);
|
|
476
|
-
background: var(--color-surface);
|
|
477
|
-
color: var(--color-text);
|
|
478
|
-
font: inherit;
|
|
479
|
-
font-variant-numeric: tabular-nums;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
.field {
|
|
483
|
-
flex: 1;
|
|
484
|
-
width: 100%;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
.token-picker {
|
|
488
|
-
flex: none;
|
|
489
|
-
width: 4.5rem;
|
|
490
|
-
cursor: pointer;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
.raw {
|
|
494
|
-
flex: none;
|
|
495
|
-
align-self: center;
|
|
496
|
-
color: var(--color-text-secondary);
|
|
497
|
-
cursor: help;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
.field:hover,
|
|
501
|
-
.token-picker:hover {
|
|
502
|
-
border-color: var(--color-muted);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
.field:disabled,
|
|
506
|
-
.token-picker:disabled {
|
|
507
|
-
opacity: 0.5;
|
|
508
|
-
cursor: not-allowed;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
.saved {
|
|
512
|
-
margin-left: var(--space-sm);
|
|
513
|
-
color: var(--color-success);
|
|
514
|
-
font-weight: 400;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
416
|
.tabs {
|
|
518
417
|
display: flex;
|
|
519
418
|
gap: 2px;
|
package/src/claude/cases/misc.ts
CHANGED
|
@@ -27,6 +27,11 @@ export const MISC_CASES: readonly SkillCase[] = [
|
|
|
27
27
|
"I found two sites whose look I want us to draw from. Put them side by side and let's settle which one the design should follow before anyone writes a token.",
|
|
28
28
|
expect: 'sketch-design',
|
|
29
29
|
},
|
|
30
|
+
{
|
|
31
|
+
prompt:
|
|
32
|
+
'Draw the signup screen as a frame on the canvas, then restyle the element I just selected there.',
|
|
33
|
+
expect: 'canvas',
|
|
34
|
+
},
|
|
30
35
|
{
|
|
31
36
|
prompt: 'Fire up the dev server the way this project documents it.',
|
|
32
37
|
expect: 'project-commands',
|
package/src/claude/seeds.ts
CHANGED
package/src/commands/design.ts
CHANGED
|
@@ -6,30 +6,22 @@ import {
|
|
|
6
6
|
DESIGN_INSTALL_DIR,
|
|
7
7
|
DESIGN_PROJECT_SUBDIR,
|
|
8
8
|
} from '@/design/adapter'
|
|
9
|
-
import { generateBoard } from '@/design/board'
|
|
10
9
|
import { buildDesignCss } from '@/design/css'
|
|
11
10
|
import { HAND_DRAWN_FONT_FACES } from '@/design/fonts'
|
|
12
11
|
import { renderDesignDoc } from '@/design/render'
|
|
13
12
|
import { DESIGN_BASE_CSS, DESIGN_DOCUMENT, regenDesign } from '@/design/regen'
|
|
14
|
-
import {
|
|
15
|
-
checkoutMismatchWarning,
|
|
16
|
-
isOwnCheckout,
|
|
17
|
-
PROJECT_ROOT,
|
|
18
|
-
} from '@/project-root'
|
|
13
|
+
import { checkoutMismatchWarning, PROJECT_ROOT } from '@/project-root'
|
|
19
14
|
import { creationRel, SCRATCH } from '@/record-root'
|
|
20
15
|
import { surfaceDir } from '@/surface-root'
|
|
21
16
|
import { recordStamp, runDomainSync } from '@/sync/engine'
|
|
22
17
|
import { resolveTarget } from '@/target'
|
|
23
18
|
import { intro, logAdd, logError, logInfo, logWarn, outro, palette } from '@/ui'
|
|
24
|
-
import { mainWorktreeRoot } from '@/worktree'
|
|
25
19
|
|
|
26
20
|
export function register(program: Command): void {
|
|
27
21
|
const design = program
|
|
28
22
|
.command('design')
|
|
29
23
|
.helpOption('-h, --help', 'Show this help message')
|
|
30
|
-
.description(
|
|
31
|
-
'Design system commands (regen, css, render, board, install, sync)',
|
|
32
|
-
)
|
|
24
|
+
.description('Design system commands (regen, css, render, install, sync)')
|
|
33
25
|
|
|
34
26
|
design
|
|
35
27
|
.command('regen')
|
|
@@ -145,60 +137,6 @@ export function register(program: Command): void {
|
|
|
145
137
|
)
|
|
146
138
|
})
|
|
147
139
|
|
|
148
|
-
design
|
|
149
|
-
.command('board')
|
|
150
|
-
.helpOption('-h, --help', 'Show this help message')
|
|
151
|
-
.description(
|
|
152
|
-
'Generate the design board, an index over a project’s design surfaces',
|
|
153
|
-
)
|
|
154
|
-
.option(
|
|
155
|
-
'-o, --out <path>',
|
|
156
|
-
'Output directory',
|
|
157
|
-
creationRel(process.cwd(), SCRATCH, 'render', 'board'),
|
|
158
|
-
)
|
|
159
|
-
.option('--root <path>', 'Project root, defaulting to the main worktree')
|
|
160
|
-
.addHelpText(
|
|
161
|
-
'after',
|
|
162
|
-
[
|
|
163
|
-
'',
|
|
164
|
-
'Reads its sources from --root, defaulting to the main worktree of',
|
|
165
|
-
'whatever project the caller stands in, and writes a static page',
|
|
166
|
-
'set, never installed or synced. The surfaces panel’s landing-page',
|
|
167
|
-
'half and the whole components panel render only when that root is',
|
|
168
|
-
'this toolkit’s own checkout. Open the result with canon serve <out>.',
|
|
169
|
-
'',
|
|
170
|
-
].join('\n'),
|
|
171
|
-
)
|
|
172
|
-
.action(async (opts: { out: string; root?: string }) => {
|
|
173
|
-
const outDir = resolve(process.cwd(), opts.out)
|
|
174
|
-
const root = opts.root
|
|
175
|
-
? resolve(process.cwd(), opts.root)
|
|
176
|
-
: await mainWorktreeRoot()
|
|
177
|
-
const isToolkitCheckout = isOwnCheckout(root)
|
|
178
|
-
const { GREEN, GREY, NC, RED, WHITE } = palette(process.stderr)
|
|
179
|
-
const mismatch = checkoutMismatchWarning(process.cwd())
|
|
180
|
-
process.stderr.write(
|
|
181
|
-
`${GREY}┌${NC}\n${GREY}│${NC} ${WHITE}Generate design board${NC}\n`,
|
|
182
|
-
)
|
|
183
|
-
if (mismatch !== undefined) logWarn(mismatch)
|
|
184
|
-
const result = generateBoard(
|
|
185
|
-
root,
|
|
186
|
-
outDir,
|
|
187
|
-
process.cwd(),
|
|
188
|
-
isToolkitCheckout,
|
|
189
|
-
)
|
|
190
|
-
if (!result.ok) {
|
|
191
|
-
process.stderr.write(
|
|
192
|
-
`${GREY}│${NC} ${RED}✗${NC} ${result.detail}\n${GREY}└${NC}\n`,
|
|
193
|
-
)
|
|
194
|
-
process.exitCode = 1
|
|
195
|
-
return
|
|
196
|
-
}
|
|
197
|
-
process.stderr.write(
|
|
198
|
-
`${GREY}│${NC} ${GREEN}✓${NC} ${relative(process.cwd(), result.indexPath)}\n${GREY}│${NC} Open with: canon serve ${relative(process.cwd(), result.outDir)}\n${GREY}└${NC}\n`,
|
|
199
|
-
)
|
|
200
|
-
})
|
|
201
|
-
|
|
202
140
|
design
|
|
203
141
|
.command('install')
|
|
204
142
|
.description('Install the base stylesheet into a project')
|