@erclx/canon 5.52.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.
- package/CHANGELOG.md +17 -0
- package/README.md +11 -13
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/canon-cli/SKILL.md +5 -8
- package/claude/skills/context-fold/REQUIREMENT.md +0 -1
- package/claude/skills/design-identity/REQUIREMENT.md +2 -2
- package/claude/skills/design-identity/SKILL.md +6 -6
- package/claude/skills/design-record/REQUIREMENT.md +2 -2
- package/claude/skills/design-record/SKILL.md +4 -4
- package/claude/skills/design-record/references/folder.md +1 -1
- package/claude/skills/design-taste/REQUIREMENT.md +1 -1
- package/claude/skills/design-taste/references/kinds.md +0 -1
- package/claude/skills/draw-figure/REQUIREMENT.md +3 -4
- package/claude/skills/draw-figure/SKILL.md +5 -6
- package/claude/skills/draw-figure/references/figures.md +0 -1
- package/claude/skills/draw-figure/references/hand-drawn.md +1 -1
- package/claude/skills/pick-by-eye/REQUIREMENT.md +4 -5
- package/claude/skills/pick-by-eye/SKILL.md +34 -41
- package/claude/skills/pick-by-eye/references/board.md +42 -0
- package/claude/skills/pick-by-eye/references/live-arms.md +3 -3
- package/claude/skills/plan-groundwork/SKILL.md +1 -1
- package/claude/skills/teach-craft/REQUIREMENT.md +30 -0
- package/claude/skills/teach-craft/SKILL.md +70 -0
- package/claude/skills/teach-craft/references/adopted.md +113 -0
- package/claude/skills/teach-craft/references/lesson-craft.md +60 -0
- package/claude/skills/{teach-workspace → teach-craft}/references/pedagogy.md +41 -6
- package/docs/agents/audits.md +0 -1
- package/docs/agents/install-and-sync.md +0 -8
- package/docs/target/migrations.md +16 -2
- package/docs/target/projects.md +1 -1
- package/docs/workflow/parallel-features.md +1 -1
- package/docs/workflow/skill-map-on-request.md +10 -22
- package/docs/workflow/skill-map.md +11 -14
- package/docs/workflow/visual-design-workflow.md +1 -4
- package/governance/detect.toml +1 -6
- package/governance/renames.toml +0 -1
- package/package.json +2 -8
- package/scripts/core/regen-web-previews.ts +2 -67
- package/src/audits/catalog.ts +0 -1
- package/src/browser/engine.ts +3 -6
- package/src/capture/render.ts +1 -1
- package/src/claude/cases/authoring.ts +0 -12
- package/src/claude/cases/misc.ts +0 -22
- package/src/claude/cases/workflow.ts +2 -38
- package/src/claude/skills-audit.ts +1 -1
- package/src/cli/help.ts +0 -10
- package/src/cli.ts +0 -8
- package/src/commands/design.ts +16 -315
- package/src/commands/records.ts +0 -1
- package/src/commands/serve.ts +2 -2
- package/src/commands/sync.ts +4 -5
- package/src/design/components.ts +8 -1514
- package/src/design/css.ts +5 -6
- package/src/design/fonts.ts +3 -5
- package/src/design/regen.ts +5 -32
- package/src/design/render.ts +1 -1
- package/src/design/tokens.ts +3 -2
- package/src/driver/drive.ts +3 -4
- package/src/driver/steps.ts +2 -5
- package/src/gate/stages.ts +4 -10
- package/src/gov/audit.ts +1 -6
- package/src/inventory/walk.ts +1 -1
- package/src/markdown/scope.ts +6 -35
- package/src/records/validate.ts +4 -176
- package/src/sandbox/equivalence.ts +0 -5
- package/src/shipped/references.ts +1 -2
- package/src/sync/check.ts +4 -29
- package/src/sync/stamp.ts +6 -6
- package/src/sync/target.ts +1 -4
- package/tooling/claude/reference.md +1 -1
- package/tooling/claude/seeds/canon/DESIGN.md +1 -1
- package/claude/skills/canvas-board/REQUIREMENT.md +0 -35
- package/claude/skills/canvas-board/SKILL.md +0 -143
- package/claude/skills/canvas-board/references/canvas.md +0 -170
- package/claude/skills/draft-and-pick/REQUIREMENT.md +0 -26
- package/claude/skills/draft-and-pick/SKILL.md +0 -11
- package/claude/skills/draft-doc/REQUIREMENT.md +0 -26
- package/claude/skills/draft-doc/SKILL.md +0 -11
- package/claude/skills/draft-figure/REQUIREMENT.md +0 -26
- package/claude/skills/draft-figure/SKILL.md +0 -11
- package/claude/skills/draft-identity/REQUIREMENT.md +0 -26
- package/claude/skills/draft-identity/SKILL.md +0 -11
- package/claude/skills/draft-ready/REQUIREMENT.md +0 -26
- package/claude/skills/draft-ready/SKILL.md +0 -11
- package/claude/skills/draft-screencast/REQUIREMENT.md +0 -26
- package/claude/skills/draft-screencast/SKILL.md +0 -11
- package/claude/skills/draft-slides/REQUIREMENT.md +0 -26
- package/claude/skills/draft-slides/SKILL.md +0 -11
- package/claude/skills/make-deck/REQUIREMENT.md +0 -36
- package/claude/skills/make-deck/SKILL.md +0 -80
- package/claude/skills/make-deck/references/deck.md +0 -183
- package/claude/skills/read-frames/REQUIREMENT.md +0 -30
- package/claude/skills/read-frames/SKILL.md +0 -55
- package/claude/skills/record-screencast/REQUIREMENT.md +0 -35
- package/claude/skills/record-screencast/SKILL.md +0 -99
- package/claude/skills/script-screencast/REQUIREMENT.md +0 -34
- package/claude/skills/script-screencast/SKILL.md +0 -70
- package/claude/skills/script-screencast/references/beats-template.md +0 -119
- package/claude/skills/teach-workspace/REQUIREMENT.md +0 -34
- package/claude/skills/teach-workspace/SKILL.md +0 -196
- package/claude/skills/teach-workspace/references/glossary.md +0 -90
- package/claude/skills/teach-workspace/references/lesson-build.md +0 -106
- package/claude/skills/teach-workspace/references/lesson-craft.md +0 -135
- package/claude/skills/teach-workspace/references/promotion-fold.md +0 -35
- package/claude/skills/teach-workspace/references/promotion.md +0 -116
- package/claude/skills/teach-workspace/references/revisit.md +0 -28
- package/claude/skills/teach-workspace/references/teach.md +0 -211
- package/claude/skills/video-craft/REQUIREMENT.md +0 -35
- package/claude/skills/video-craft/SKILL.md +0 -96
- package/claude/skills/video-craft/references/adopted.md +0 -110
- package/governance/rules/ui/canvas.md +0 -13
- package/src/canvas/address.ts +0 -227
- package/src/canvas/arrange.ts +0 -66
- package/src/canvas/capture.ts +0 -391
- package/src/canvas/client/app.tsx +0 -188
- package/src/canvas/client/inspector/align-grid.tsx +0 -162
- package/src/canvas/client/inspector/color-field.tsx +0 -398
- package/src/canvas/client/inspector/color-inputs.tsx +0 -139
- package/src/canvas/client/inspector/color-picker.tsx +0 -668
- package/src/canvas/client/inspector/color-space.ts +0 -240
- package/src/canvas/client/inspector/field.tsx +0 -263
- package/src/canvas/client/inspector/fields.css +0 -763
- package/src/canvas/client/inspector/section.tsx +0 -27
- package/src/canvas/client/inspector/size-field.tsx +0 -69
- package/src/canvas/client/inspector/token-select.tsx +0 -109
- package/src/canvas/client/inspector/values.ts +0 -297
- package/src/canvas/client/inspector.tsx +0 -692
- package/src/canvas/client/keys.ts +0 -71
- package/src/canvas/client/layers.tsx +0 -118
- package/src/canvas/client/main.tsx +0 -71
- package/src/canvas/client/pages-panel.tsx +0 -212
- package/src/canvas/client/panel-handle.tsx +0 -90
- package/src/canvas/client/selection.tsx +0 -199
- package/src/canvas/client/shell.css +0 -762
- package/src/canvas/client/state.ts +0 -792
- package/src/canvas/client/surface.tsx +0 -803
- package/src/canvas/client/theme-panel.tsx +0 -55
- package/src/canvas/client/tools.tsx +0 -153
- package/src/canvas/content.ts +0 -781
- package/src/canvas/edit.ts +0 -575
- package/src/canvas/editing.ts +0 -203
- package/src/canvas/fit.ts +0 -238
- package/src/canvas/global.d.ts +0 -15
- package/src/canvas/history.ts +0 -116
- package/src/canvas/index.html +0 -20
- package/src/canvas/server.ts +0 -766
- package/src/canvas/tokens.ts +0 -221
- package/src/commands/canvas.ts +0 -1097
- package/src/commands/demo.ts +0 -542
- package/src/commands/slides.ts +0 -184
- package/src/commands/teach.ts +0 -1401
- package/src/demo/beats.ts +0 -135
- package/src/demo/compile.ts +0 -350
- package/src/demo/container.ts +0 -219
- package/src/demo/cursors.ts +0 -55
- package/src/demo/drive.ts +0 -442
- package/src/demo/pointer.ts +0 -178
- package/src/demo/theme.ts +0 -112
- package/src/demo/timeline.ts +0 -64
- package/src/design/adapter.ts +0 -59
- package/src/design/base.css +0 -203
- package/src/design/brand-favicon.ts +0 -18
- package/src/design/neutral.ts +0 -127
- package/src/design/project-fonts.ts +0 -392
- package/src/slides/convert/chart.ts +0 -165
- package/src/slides/convert/deck.ts +0 -244
- package/src/slides/convert/export.ts +0 -567
- package/src/slides/convert/master.ts +0 -198
- package/src/slides/convert/shapes.ts +0 -804
- package/src/slides/convert/svg.ts +0 -38
- package/src/slides/convert/walk.ts +0 -671
- package/src/slides/decks.ts +0 -110
- package/src/slides/open.ts +0 -18
- package/src/slides/package/fonts.ts +0 -301
- package/src/slides/package/motion.ts +0 -316
- package/src/slides/package/test-face.ts +0 -113
- package/src/teach/browser/compile.ts +0 -44
- package/src/teach/browser/dismiss.ts +0 -76
- package/src/teach/browser/focus-line.ts +0 -19
- package/src/teach/browser/glossary-filter.ts +0 -44
- package/src/teach/browser/head.ts +0 -30
- package/src/teach/browser/quiz.ts +0 -19
- package/src/teach/browser/reload.ts +0 -9
- package/src/teach/browser/sidebar.ts +0 -233
- package/src/teach/browser/theme.ts +0 -18
- package/src/teach/components/code.tsx +0 -24
- package/src/teach/components/heading.tsx +0 -11
- package/src/teach/components/list.tsx +0 -17
- package/src/teach/components/paragraph.tsx +0 -29
- package/src/teach/components/refs.tsx +0 -28
- package/src/teach/cross-references.ts +0 -78
- package/src/teach/fonts.ts +0 -16
- package/src/teach/highlight.ts +0 -71
- package/src/teach/html/jsx-dev-runtime.ts +0 -13
- package/src/teach/html/jsx-runtime.ts +0 -113
- package/src/teach/html/raw.ts +0 -28
- package/src/teach/lesson.ts +0 -178
- package/src/teach/nav.ts +0 -1337
- package/src/teach/render.ts +0 -240
- package/src/teach/workspace.ts +0 -1164
- package/src/teach/write-if-changed.ts +0 -17
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, realpathSync, statSync } from 'node:fs'
|
|
2
|
-
import { basename, isAbsolute, join, relative, resolve } from 'node:path'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* What belongs to the deck rather than to one slide, read from `deck.json`
|
|
6
|
-
* beside the slides. A slide overrides a band through attributes on its own
|
|
7
|
-
* `<body>`, which `walk.ts` reads.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export const DECK_FILE = 'deck.json'
|
|
11
|
-
|
|
12
|
-
export const BAND_SLOTS = ['left', 'center', 'right'] as const
|
|
13
|
-
export type BandSlot = (typeof BAND_SLOTS)[number]
|
|
14
|
-
|
|
15
|
-
export interface Band {
|
|
16
|
-
readonly show: boolean
|
|
17
|
-
readonly left?: string
|
|
18
|
-
readonly center?: string
|
|
19
|
-
readonly right?: string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface DeckConfig {
|
|
23
|
-
readonly title: string
|
|
24
|
-
readonly header: Band
|
|
25
|
-
readonly footer: Band
|
|
26
|
-
/** Drawn at the right of the footer, so they show only where it does. */
|
|
27
|
-
readonly slideNumbers: boolean
|
|
28
|
-
/** Absolute, resolved against the deck folder. */
|
|
29
|
-
readonly mark?: string
|
|
30
|
-
/** The faces the package step embeds, in the order the deck lists them. */
|
|
31
|
-
readonly fonts: readonly DeckFont[]
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface DeckFont {
|
|
35
|
-
readonly family: string
|
|
36
|
-
readonly weight: number
|
|
37
|
-
readonly style: 'normal' | 'italic'
|
|
38
|
-
/** Absolute, resolved against the deck folder and kept inside it. */
|
|
39
|
-
readonly path: string
|
|
40
|
-
/** The path as `deck.json` wrote it, which a refusal names. */
|
|
41
|
-
readonly source: string
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export type DeckRead =
|
|
45
|
-
| { readonly status: 'read'; readonly deck: DeckConfig }
|
|
46
|
-
| {
|
|
47
|
-
readonly status: 'refused'
|
|
48
|
-
readonly field: string
|
|
49
|
-
readonly message: string
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const DECK_FIELDS = new Set([
|
|
53
|
-
'title',
|
|
54
|
-
'header',
|
|
55
|
-
'footer',
|
|
56
|
-
'slideNumbers',
|
|
57
|
-
'mark',
|
|
58
|
-
'fonts',
|
|
59
|
-
])
|
|
60
|
-
const BAND_FIELDS = new Set<string>(['show', ...BAND_SLOTS])
|
|
61
|
-
const FONT_FIELDS = new Set(['family', 'weight', 'style', 'path'])
|
|
62
|
-
|
|
63
|
-
class FieldError extends Error {
|
|
64
|
-
constructor(
|
|
65
|
-
readonly field: string,
|
|
66
|
-
message: string,
|
|
67
|
-
) {
|
|
68
|
-
super(message)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
73
|
-
typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
74
|
-
|
|
75
|
-
function rejectUnknown(
|
|
76
|
-
record: Record<string, unknown>,
|
|
77
|
-
known: ReadonlySet<string>,
|
|
78
|
-
prefix: string,
|
|
79
|
-
): void {
|
|
80
|
-
const unknown = Object.keys(record).find((key) => !known.has(key))
|
|
81
|
-
if (unknown !== undefined) {
|
|
82
|
-
throw new FieldError(
|
|
83
|
-
`${prefix}${unknown}`,
|
|
84
|
-
`unknown field ${prefix}${unknown}`,
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function stringField(value: unknown, field: string): string | undefined {
|
|
90
|
-
if (value === undefined) return undefined
|
|
91
|
-
if (typeof value !== 'string') {
|
|
92
|
-
throw new FieldError(field, `${field} must be a string`)
|
|
93
|
-
}
|
|
94
|
-
return value
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function booleanField(value: unknown, field: string): boolean | undefined {
|
|
98
|
-
if (value === undefined) return undefined
|
|
99
|
-
if (typeof value !== 'boolean') {
|
|
100
|
-
throw new FieldError(field, `${field} must be true or false`)
|
|
101
|
-
}
|
|
102
|
-
return value
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* A declared band merges over its default, so `{ "center": "x" }` keeps the
|
|
107
|
-
* footer's title on the left. Declaring any text turns a band on unless it
|
|
108
|
-
* also says `show: false`.
|
|
109
|
-
*/
|
|
110
|
-
function band(value: unknown, field: string, fallback: Band): Band {
|
|
111
|
-
if (value === undefined) return fallback
|
|
112
|
-
if (!isRecord(value)) {
|
|
113
|
-
throw new FieldError(field, `${field} must be an object`)
|
|
114
|
-
}
|
|
115
|
-
rejectUnknown(value, BAND_FIELDS, `${field}.`)
|
|
116
|
-
const slots: { -readonly [slot in BandSlot]?: string } = {}
|
|
117
|
-
for (const slot of BAND_SLOTS) {
|
|
118
|
-
const text = stringField(value[slot], `${field}.${slot}`)
|
|
119
|
-
if (text !== undefined) slots[slot] = text
|
|
120
|
-
}
|
|
121
|
-
const show =
|
|
122
|
-
booleanField(value.show, `${field}.show`) ??
|
|
123
|
-
(Object.keys(slots).length > 0 || fallback.show)
|
|
124
|
-
return { ...fallback, ...slots, show }
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function parse(raw: unknown, folder: string): DeckConfig {
|
|
128
|
-
if (!isRecord(raw)) {
|
|
129
|
-
throw new FieldError(DECK_FILE, `${DECK_FILE} must hold an object`)
|
|
130
|
-
}
|
|
131
|
-
rejectUnknown(raw, DECK_FIELDS, '')
|
|
132
|
-
const title = stringField(raw.title, 'title') ?? basename(folder)
|
|
133
|
-
const header = band(raw.header, 'header', { show: false })
|
|
134
|
-
const footer = band(raw.footer, 'footer', { show: true, left: title })
|
|
135
|
-
const slideNumbers = booleanField(raw.slideNumbers, 'slideNumbers') ?? true
|
|
136
|
-
if (slideNumbers && footer.right !== undefined) {
|
|
137
|
-
throw new FieldError(
|
|
138
|
-
'footer.right',
|
|
139
|
-
'footer.right is where slide numbers sit. Set slideNumbers to false to use it',
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
const markPath = stringField(raw.mark, 'mark')
|
|
143
|
-
let mark: string | undefined
|
|
144
|
-
if (markPath !== undefined) {
|
|
145
|
-
mark = join(folder, markPath)
|
|
146
|
-
if (!existsSync(mark) || !statSync(mark).isFile()) {
|
|
147
|
-
throw new FieldError('mark', `mark ${mark} does not exist`)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
const fonts = fontList(raw.fonts, folder)
|
|
151
|
-
return { title, header, footer, slideNumbers, mark, fonts }
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function isWithin(folder: string, path: string): boolean {
|
|
155
|
-
const inside = relative(folder, path)
|
|
156
|
-
return inside !== '' && !inside.startsWith('..') && !isAbsolute(inside)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* A face is read from the deck folder only, so a `deck.json` cannot point the
|
|
161
|
-
* export at a file elsewhere on the machine and carry it out inside the deck.
|
|
162
|
-
*/
|
|
163
|
-
function fontList(value: unknown, folder: string): DeckFont[] {
|
|
164
|
-
if (value === undefined) return []
|
|
165
|
-
if (!Array.isArray(value)) {
|
|
166
|
-
throw new FieldError('fonts', 'fonts must be a list')
|
|
167
|
-
}
|
|
168
|
-
return value.map((entry: unknown, index): DeckFont => {
|
|
169
|
-
const field = `fonts[${index}]`
|
|
170
|
-
if (!isRecord(entry)) {
|
|
171
|
-
throw new FieldError(field, `${field} must be an object`)
|
|
172
|
-
}
|
|
173
|
-
rejectUnknown(entry, FONT_FIELDS, `${field}.`)
|
|
174
|
-
const family = stringField(entry.family, `${field}.family`)
|
|
175
|
-
if (!family) {
|
|
176
|
-
throw new FieldError(`${field}.family`, `${field}.family is required`)
|
|
177
|
-
}
|
|
178
|
-
const weight = entry.weight ?? 400
|
|
179
|
-
if (
|
|
180
|
-
typeof weight !== 'number' ||
|
|
181
|
-
!Number.isInteger(weight) ||
|
|
182
|
-
weight < 1 ||
|
|
183
|
-
weight > 1000
|
|
184
|
-
) {
|
|
185
|
-
throw new FieldError(
|
|
186
|
-
`${field}.weight`,
|
|
187
|
-
`${field}.weight must be a whole number from 1 to 1000`,
|
|
188
|
-
)
|
|
189
|
-
}
|
|
190
|
-
const style = entry.style ?? 'normal'
|
|
191
|
-
if (style !== 'normal' && style !== 'italic') {
|
|
192
|
-
throw new FieldError(
|
|
193
|
-
`${field}.style`,
|
|
194
|
-
`${field}.style must be normal or italic`,
|
|
195
|
-
)
|
|
196
|
-
}
|
|
197
|
-
const source = stringField(entry.path, `${field}.path`)
|
|
198
|
-
if (!source) {
|
|
199
|
-
throw new FieldError(`${field}.path`, `${field}.path is required`)
|
|
200
|
-
}
|
|
201
|
-
const path = resolve(folder, source)
|
|
202
|
-
const outside = new FieldError(
|
|
203
|
-
`${field}.path`,
|
|
204
|
-
`${field}.path ${source} lies outside the deck folder`,
|
|
205
|
-
)
|
|
206
|
-
if (!isWithin(folder, path)) throw outside
|
|
207
|
-
if (!existsSync(path) || !statSync(path).isFile()) {
|
|
208
|
-
throw new FieldError(
|
|
209
|
-
`${field}.path`,
|
|
210
|
-
`${field}.path ${path} does not exist`,
|
|
211
|
-
)
|
|
212
|
-
}
|
|
213
|
-
// A link inside the folder can still lead out of it.
|
|
214
|
-
if (!isWithin(realpathSync(folder), realpathSync(path))) throw outside
|
|
215
|
-
return { family, weight, style, path, source }
|
|
216
|
-
})
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
export function readDeck(folder: string): DeckRead {
|
|
220
|
-
const path = join(folder, DECK_FILE)
|
|
221
|
-
let raw: unknown = {}
|
|
222
|
-
if (existsSync(path)) {
|
|
223
|
-
try {
|
|
224
|
-
raw = JSON.parse(readFileSync(path, 'utf8'))
|
|
225
|
-
} catch (error) {
|
|
226
|
-
const reason = error instanceof Error ? error.message : String(error)
|
|
227
|
-
return {
|
|
228
|
-
status: 'refused',
|
|
229
|
-
field: DECK_FILE,
|
|
230
|
-
message: `${path}: not valid JSON (${reason})`,
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
try {
|
|
235
|
-
return { status: 'read', deck: parse(raw, folder) }
|
|
236
|
-
} catch (error) {
|
|
237
|
-
if (!(error instanceof FieldError)) throw error
|
|
238
|
-
return {
|
|
239
|
-
status: 'refused',
|
|
240
|
-
field: error.field,
|
|
241
|
-
message: `${path}: ${error.message}`,
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|