@erclx/canon 4.92.0 → 4.94.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/README.md +5 -5
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/teach-workspace/references/lesson-craft.md +1 -1
- package/claude/skills/test-craft/REQUIREMENT.md +54 -0
- package/claude/skills/test-craft/SKILL.md +77 -0
- package/claude/skills/test-craft/references/adopted.md +44 -0
- package/claude/skills/test-craft/references/component.md +42 -0
- package/claude/skills/test-craft/references/e2e.md +44 -0
- package/claude/skills/test-craft/references/unit.md +49 -0
- package/claude/skills/test-first/REQUIREMENT.md +2 -0
- package/claude/skills/test-first/SKILL.md +4 -2
- package/docs/agents/capture.md +12 -5
- package/docs/agents/commands.md +3 -3
- package/docs/agents/rule-citations.md +1 -1
- package/docs/agents/teach.md +1 -1
- package/docs/workflow/ai-workflow.md +9 -8
- package/governance/rules/core/010-testing.md +4 -2
- package/governance/rules/lib/300-testing-ts.md +4 -0
- package/governance/rules/lib/305-e2e-reliability.md +3 -2
- package/governance/rules/lib/306-test-scope.md +1 -2
- package/governance/rules/lib/330-testing-py.md +4 -0
- package/governance/stacks/astro.toml +1 -1
- package/package.json +3 -2
- package/scripts/core/regen-hero.sh +4 -3
- package/src/capture/render.ts +15 -1
- package/src/claude/cases/misc.ts +5 -0
- package/src/cli.ts +1 -1
- package/src/commands/capture.ts +25 -1
- package/src/commands/serve.ts +11 -1
- package/src/design/components.ts +40 -26
- package/src/gate/measures.ts +5 -4
- package/src/gate/stages.ts +1 -1
- package/src/serve/static.ts +90 -6
- package/src/teach/fonts.ts +9 -19
- package/src/teach/render.ts +2 -2
- package/tooling/astro/configs/playwright.config.ts +2 -0
- package/tooling/nextjs/configs/playwright.config.ts +2 -0
- package/tooling/vite-react/configs/playwright.config.ts +2 -0
- package/src/teach/render-fixture.tsx +0 -95
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Enforce settled waits and falsifiable guards in end-to-end tests
|
|
3
3
|
paths:
|
|
4
|
-
- 'e2e
|
|
5
|
-
- 'e2e/**/*.ts'
|
|
4
|
+
- '**/e2e/**/*.ts'
|
|
6
5
|
---
|
|
7
6
|
|
|
8
7
|
# End-to-end reliability standards
|
|
@@ -14,6 +13,8 @@ paths:
|
|
|
14
13
|
- Bound every settle with an explicit timeout.
|
|
15
14
|
- Do not raise a timeout to clear a failure that reproduces under load. Replace the wait with a settle.
|
|
16
15
|
- Do not read a value once after a pause. Poll it.
|
|
16
|
+
- Settle a smooth scroll on the `scrollend` event. Fall back to a stillness window only when the target was already in view.
|
|
17
|
+
- Run with motion reduced unless the test asserts motion, and opt back in per test.
|
|
17
18
|
|
|
18
19
|
## Falsifiable guards
|
|
19
20
|
|
|
@@ -8,6 +8,10 @@ paths:
|
|
|
8
8
|
|
|
9
9
|
# Python testing tooling
|
|
10
10
|
|
|
11
|
+
## Layer
|
|
12
|
+
|
|
13
|
+
- Load the `canon:test-craft` skill to pick the layer a test belongs at before choosing the tooling below, and report it rather than proceeding silently when the skill does not resolve.
|
|
14
|
+
|
|
11
15
|
## Framework
|
|
12
16
|
|
|
13
17
|
- Use `pytest` for all tests. Do not use `unittest.TestCase`.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
extends = "node"
|
|
2
|
-
rules = ["210-astro", "350-security-web", "400-ui", "410-a11y", "420-forms", "430-ux-completeness", "440-surface-capture", "450-link-behavior", "460-design-taste", "470-motion"]
|
|
2
|
+
rules = ["210-astro", "300-testing-ts", "305-e2e-reliability", "306-test-scope", "350-security-web", "400-ui", "410-a11y", "420-forms", "430-ux-completeness", "440-surface-capture", "450-link-behavior", "460-design-taste", "470-motion"]
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erclx/canon",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.94.0",
|
|
5
5
|
"description": "Infrastructure and quality tooling for developer workflows",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"tsconfig.json",
|
|
20
20
|
"!scripts/sandbox",
|
|
21
21
|
"!scripts/eval",
|
|
22
|
-
"!**/*.test.ts"
|
|
22
|
+
"!**/*.test.ts",
|
|
23
|
+
"!**/*.test.tsx"
|
|
23
24
|
],
|
|
24
25
|
"publishConfig": {
|
|
25
26
|
"access": "public"
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
# Only the HTML regenerates here. The PNG beside it is a chromium render whose
|
|
19
19
|
# bytes move with the browser version, so asserting it in verify.sh would fail
|
|
20
20
|
# on a machine whose chromium differs rather than on a stale count. Rebuild the
|
|
21
|
-
# images with `canon capture assets/captures --selector .window --out assets`
|
|
21
|
+
# images with `canon capture assets/captures --selector .window --out assets/evidence`
|
|
22
22
|
# after this script reports a change. The markup and the image sit in two
|
|
23
|
-
# folders, so that run reads the sources here and sends every PNG and stamp
|
|
24
|
-
#
|
|
23
|
+
# folders, so that run reads the sources here and sends every PNG and stamp to
|
|
24
|
+
# assets/evidence/, where the documents point and where `canon pr evidence`
|
|
25
|
+
# compares a changed image. The selector has no default, since
|
|
25
26
|
# the element a capture crops to belongs to the page rather than to the command,
|
|
26
27
|
# and `.window` is the class this repository's own sources declare.
|
|
27
28
|
# That capture also writes a .stamp beside each PNG, which records the digest of
|
package/src/capture/render.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { formatStamp, hashSource, stampPath } from '@/capture/stamp'
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
const DEVICE_SCALE_FACTOR = 2
|
|
28
|
+
const DEFAULT_VIEWPORT_HEIGHT = 720
|
|
28
29
|
const FONT_PROBE_SIZE = 72
|
|
29
30
|
const FONT_PROBE_TEXT = 'canon capture 0123456789'
|
|
30
31
|
const ABSENT_FAMILY = '__canon_absent_family__'
|
|
@@ -32,6 +33,7 @@ const ABSENT_FAMILY = '__canon_absent_family__'
|
|
|
32
33
|
export interface CaptureOptions {
|
|
33
34
|
selector: string
|
|
34
35
|
outDir?: string
|
|
36
|
+
width?: number
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
export type CaptureResult =
|
|
@@ -54,7 +56,9 @@ export async function captureSources(
|
|
|
54
56
|
const browser = await chromium.launch()
|
|
55
57
|
try {
|
|
56
58
|
return await Promise.all(
|
|
57
|
-
sources.map((source) =>
|
|
59
|
+
sources.map((source) =>
|
|
60
|
+
captureOne(browser, source, options.selector, options.width),
|
|
61
|
+
),
|
|
58
62
|
)
|
|
59
63
|
} finally {
|
|
60
64
|
await browser.close()
|
|
@@ -69,9 +73,19 @@ async function captureOne(
|
|
|
69
73
|
browser: Browser,
|
|
70
74
|
source: CaptureSource,
|
|
71
75
|
selector: string,
|
|
76
|
+
width: number | undefined,
|
|
72
77
|
): Promise<CaptureResult> {
|
|
78
|
+
/**
|
|
79
|
+
* A viewport rather than a wrapper, since a media query answers to the
|
|
80
|
+
* viewport. Absent leaves the option out so the browser default stands and
|
|
81
|
+
* every committed capture renders as before. The height is Playwright's own
|
|
82
|
+
* default, which keeps `vh` units where they were.
|
|
83
|
+
*/
|
|
73
84
|
const page = await browser.newPage({
|
|
74
85
|
deviceScaleFactor: DEVICE_SCALE_FACTOR,
|
|
86
|
+
...(width === undefined
|
|
87
|
+
? {}
|
|
88
|
+
: { viewport: { width, height: DEFAULT_VIEWPORT_HEIGHT } }),
|
|
75
89
|
})
|
|
76
90
|
try {
|
|
77
91
|
await page.goto(
|
package/src/claude/cases/misc.ts
CHANGED
|
@@ -54,6 +54,11 @@ export const MISC_CASES: readonly SkillCase[] = [
|
|
|
54
54
|
"I'm about to write the retry helper we settled on and nothing covers it yet, so get the failing case in first.",
|
|
55
55
|
expect: 'test-first',
|
|
56
56
|
},
|
|
57
|
+
{
|
|
58
|
+
prompt:
|
|
59
|
+
'The loading state already shipped. Should its test be an e2e test or a component one, and is the one I wrote any good?',
|
|
60
|
+
expect: 'test-craft',
|
|
61
|
+
},
|
|
57
62
|
{
|
|
58
63
|
prompt: 'Does our github about text still match what the readme says?',
|
|
59
64
|
expect: 'repo-metadata',
|
package/src/cli.ts
CHANGED
|
@@ -121,7 +121,7 @@ function showHelp(): void {
|
|
|
121
121
|
`${GREY}│${NC} canon design board`,
|
|
122
122
|
`${GREY}│${NC} canon slides render`,
|
|
123
123
|
`${GREY}│${NC} canon slides list --json`,
|
|
124
|
-
`${GREY}│${NC} canon capture assets/captures/install.html --selector .window --out assets`,
|
|
124
|
+
`${GREY}│${NC} canon capture assets/captures/install.html --selector .window --out assets/evidence`,
|
|
125
125
|
`${GREY}│${NC} canon serve .canon/teach`,
|
|
126
126
|
`${GREY}│${NC} canon inventory focus --json`,
|
|
127
127
|
`${GREY}│${NC} canon drive http://localhost:4173 run.json --json`,
|
package/src/commands/capture.ts
CHANGED
|
@@ -54,10 +54,14 @@ export function register(program: Command): void {
|
|
|
54
54
|
'Output directory for a file source, or the destination PNG for a URL source; defaults beside the source',
|
|
55
55
|
)
|
|
56
56
|
.option('-s, --selector <selector>', 'Element to capture')
|
|
57
|
+
.option(
|
|
58
|
+
'--width <px>',
|
|
59
|
+
'Viewport width in pixels, so a media query resolves at that width',
|
|
60
|
+
)
|
|
57
61
|
.action(
|
|
58
62
|
async (
|
|
59
63
|
source: string,
|
|
60
|
-
opts: { out?: string; selector?: string },
|
|
64
|
+
opts: { out?: string; selector?: string; width?: string },
|
|
61
65
|
): Promise<void> => {
|
|
62
66
|
/**
|
|
63
67
|
* Refused rather than defaulted, and refused ahead of every other
|
|
@@ -75,6 +79,15 @@ export function register(program: Command): void {
|
|
|
75
79
|
}
|
|
76
80
|
const selector = opts.selector
|
|
77
81
|
|
|
82
|
+
const width = parseWidth(opts.width)
|
|
83
|
+
if (Number.isNaN(width)) {
|
|
84
|
+
frameError(
|
|
85
|
+
`--width takes a whole number of pixels of 1 or more, got ${opts.width}.`,
|
|
86
|
+
)
|
|
87
|
+
process.exitCode = 1
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
78
91
|
const isUrl = isUrlSource(source)
|
|
79
92
|
const sourcePath = isUrl ? source : resolve(process.cwd(), source)
|
|
80
93
|
if (!isUrl && !existsSync(sourcePath)) {
|
|
@@ -89,6 +102,7 @@ export function register(program: Command): void {
|
|
|
89
102
|
const renderer = await import('@/capture/render')
|
|
90
103
|
results = await renderer.captureSources(sourcePath, {
|
|
91
104
|
selector,
|
|
105
|
+
width,
|
|
92
106
|
outDir: opts.out ? resolve(process.cwd(), opts.out) : undefined,
|
|
93
107
|
})
|
|
94
108
|
} catch (error) {
|
|
@@ -122,6 +136,16 @@ export function register(program: Command): void {
|
|
|
122
136
|
)
|
|
123
137
|
}
|
|
124
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Undefined for an absent flag and `NaN` for a value that is not a positive
|
|
141
|
+
* integer, so the caller can tell no width from a bad one.
|
|
142
|
+
*/
|
|
143
|
+
function parseWidth(raw: string | undefined): number | undefined {
|
|
144
|
+
if (raw === undefined) return undefined
|
|
145
|
+
const value = Number(raw)
|
|
146
|
+
return Number.isInteger(value) && value >= 1 ? value : Number.NaN
|
|
147
|
+
}
|
|
148
|
+
|
|
125
149
|
/**
|
|
126
150
|
* Closes an open frame around a failure that stopped the whole run rather than
|
|
127
151
|
* one source. Two of them are setup states rather than defects and each names
|
package/src/commands/serve.ts
CHANGED
|
@@ -7,6 +7,7 @@ const DEFAULT_DIR = '.'
|
|
|
7
7
|
|
|
8
8
|
interface ServeCommandOptions {
|
|
9
9
|
readonly entry?: string
|
|
10
|
+
readonly index?: boolean
|
|
10
11
|
readonly json?: boolean
|
|
11
12
|
readonly port?: string
|
|
12
13
|
}
|
|
@@ -19,6 +20,10 @@ export function register(program: Command): void {
|
|
|
19
20
|
.helpOption('-h, --help', 'Show this help message')
|
|
20
21
|
.option('--port <number>', `Port to try first, default ${DEFAULT_PORT}`)
|
|
21
22
|
.option('--entry <path>', 'Page the printed link opens, default index.html')
|
|
23
|
+
.option(
|
|
24
|
+
'--index',
|
|
25
|
+
'List a directory that has no index.html instead of answering 404',
|
|
26
|
+
)
|
|
22
27
|
.option('--json', 'Emit a machine-readable record on stdout')
|
|
23
28
|
.addHelpText(
|
|
24
29
|
'after',
|
|
@@ -39,6 +44,7 @@ export function register(program: Command): void {
|
|
|
39
44
|
' canon serve .canon/teach',
|
|
40
45
|
' canon serve .canon/teach --entry 03-fde-system-design/index.html',
|
|
41
46
|
' canon serve dist --port 4000 --json',
|
|
47
|
+
' canon serve .canon/groundwork --index',
|
|
42
48
|
'',
|
|
43
49
|
].join('\n'),
|
|
44
50
|
)
|
|
@@ -61,7 +67,11 @@ async function runServe(
|
|
|
61
67
|
)
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
const outcome = startServer(dir, {
|
|
70
|
+
const outcome = startServer(dir, {
|
|
71
|
+
port,
|
|
72
|
+
entry: opts.entry,
|
|
73
|
+
index: opts.index,
|
|
74
|
+
})
|
|
65
75
|
const code = report(outcome, emitJson)
|
|
66
76
|
if (!outcome.ok) return code
|
|
67
77
|
|
package/src/design/components.ts
CHANGED
|
@@ -225,11 +225,16 @@ const TEACH_CHROME: Component = {
|
|
|
225
225
|
'--teach-chrome',
|
|
226
226
|
'--teach-shadow',
|
|
227
227
|
'--color-teach-accent-bg',
|
|
228
|
+
'--type-body-family',
|
|
229
|
+
'--type-code-family',
|
|
230
|
+
'--t3',
|
|
231
|
+
'--t4',
|
|
232
|
+
'--t5',
|
|
228
233
|
],
|
|
229
234
|
rules: `:root {
|
|
230
|
-
--teach-sans:
|
|
231
|
-
--teach-hand: 'Virgil', '
|
|
232
|
-
--teach-mono:
|
|
235
|
+
--teach-sans: var(--type-body-family);
|
|
236
|
+
--teach-hand: 'Virgil', 'Excalifont', cursive;
|
|
237
|
+
--teach-mono: var(--type-code-family);
|
|
233
238
|
--teach-measure: 52rem;
|
|
234
239
|
--teach-chrome: 52rem;
|
|
235
240
|
--teach-mast-h: 4.4rem;
|
|
@@ -268,7 +273,7 @@ body {
|
|
|
268
273
|
background: var(--color-background);
|
|
269
274
|
color: var(--color-text-body);
|
|
270
275
|
font-family: var(--teach-sans);
|
|
271
|
-
font-size:
|
|
276
|
+
font-size: var(--t3);
|
|
272
277
|
line-height: 1.62;
|
|
273
278
|
-webkit-font-smoothing: antialiased;
|
|
274
279
|
}
|
|
@@ -297,7 +302,7 @@ main { max-width: calc(var(--teach-measure) + 3rem); margin: 0 auto; padding: 2.
|
|
|
297
302
|
justify-content: space-between;
|
|
298
303
|
align-items: center;
|
|
299
304
|
gap: 1rem;
|
|
300
|
-
font-size:
|
|
305
|
+
font-size: var(--t5);
|
|
301
306
|
color: var(--color-muted);
|
|
302
307
|
}
|
|
303
308
|
|
|
@@ -332,7 +337,7 @@ main { max-width: calc(var(--teach-measure) + 3rem); margin: 0 auto; padding: 2.
|
|
|
332
337
|
border-radius: 8px;
|
|
333
338
|
text-decoration: none;
|
|
334
339
|
color: var(--color-text-body);
|
|
335
|
-
font-size:
|
|
340
|
+
font-size: var(--t4);
|
|
336
341
|
font-weight: 600;
|
|
337
342
|
line-height: 1.3;
|
|
338
343
|
box-shadow: var(--teach-shadow);
|
|
@@ -370,14 +375,14 @@ main { max-width: calc(var(--teach-measure) + 3rem); margin: 0 auto; padding: 2.
|
|
|
370
375
|
value for a character even a decorative one. */
|
|
371
376
|
.crumb-sep {
|
|
372
377
|
color: var(--color-muted);
|
|
373
|
-
font-size:
|
|
378
|
+
font-size: var(--t4);
|
|
374
379
|
user-select: none;
|
|
375
380
|
}
|
|
376
381
|
|
|
377
382
|
/* One arrow, on the first segment, since that is the only one that goes up. */
|
|
378
383
|
.crumb-back {
|
|
379
384
|
margin-right: 0.3rem;
|
|
380
|
-
font-size:
|
|
385
|
+
font-size: var(--t4);
|
|
381
386
|
line-height: 1;
|
|
382
387
|
}
|
|
383
388
|
|
|
@@ -612,6 +617,9 @@ const TEACH_ARTICLE: Component = {
|
|
|
612
617
|
'--color-accent',
|
|
613
618
|
'--teach-hand',
|
|
614
619
|
'--teach-mono',
|
|
620
|
+
'--t4',
|
|
621
|
+
'--t5',
|
|
622
|
+
'--t6',
|
|
615
623
|
],
|
|
616
624
|
rules: `/* ---- Type ---- */
|
|
617
625
|
|
|
@@ -664,7 +672,7 @@ em { font-style: italic; }
|
|
|
664
672
|
font-weight: 700;
|
|
665
673
|
letter-spacing: 0.06em;
|
|
666
674
|
text-transform: uppercase;
|
|
667
|
-
font-size:
|
|
675
|
+
font-size: var(--t6);
|
|
668
676
|
margin-bottom: 0.2rem;
|
|
669
677
|
}
|
|
670
678
|
|
|
@@ -680,7 +688,7 @@ code {
|
|
|
680
688
|
|
|
681
689
|
pre {
|
|
682
690
|
font-family: var(--teach-mono);
|
|
683
|
-
font-size:
|
|
691
|
+
font-size: var(--t5);
|
|
684
692
|
line-height: 1.7;
|
|
685
693
|
background: var(--color-surface);
|
|
686
694
|
padding: 1.15rem 1.3rem;
|
|
@@ -745,12 +753,12 @@ tbody tr.mark td { background: var(--color-teach-accent-bg); }
|
|
|
745
753
|
}
|
|
746
754
|
|
|
747
755
|
.toc a:hover { background: var(--color-surface); }
|
|
748
|
-
.toc .num { font-family: var(--teach-mono); font-size:
|
|
756
|
+
.toc .num { font-family: var(--teach-mono); font-size: var(--t5); color: var(--color-accent); }
|
|
749
757
|
.toc b { font-size: 1.0625rem; font-weight: 700; letter-spacing: -0.012em; }
|
|
750
758
|
|
|
751
759
|
.toc .blurb {
|
|
752
760
|
grid-column: 2;
|
|
753
|
-
font-size:
|
|
761
|
+
font-size: var(--t4);
|
|
754
762
|
line-height: 1.5;
|
|
755
763
|
color: var(--color-text-secondary);
|
|
756
764
|
margin-top: 0.1rem;
|
|
@@ -799,7 +807,7 @@ tbody tr.mark td { background: var(--color-teach-accent-bg); }
|
|
|
799
807
|
ol.succ {
|
|
800
808
|
margin: 0 0 2.5rem;
|
|
801
809
|
padding-left: 1.35rem;
|
|
802
|
-
font-size:
|
|
810
|
+
font-size: var(--t4);
|
|
803
811
|
line-height: 1.6;
|
|
804
812
|
color: var(--color-text-secondary);
|
|
805
813
|
}
|
|
@@ -846,13 +854,13 @@ ol.succ li { margin-bottom: 0.4rem; }
|
|
|
846
854
|
|
|
847
855
|
.road-n {
|
|
848
856
|
font-family: var(--teach-mono);
|
|
849
|
-
font-size:
|
|
857
|
+
font-size: var(--t5);
|
|
850
858
|
color: var(--color-accent);
|
|
851
859
|
}
|
|
852
860
|
|
|
853
861
|
.road-b { display: block; }
|
|
854
862
|
.road-b b { display: block; margin-bottom: 0.2rem; }
|
|
855
|
-
.road-b span { color: var(--color-text-secondary); font-size:
|
|
863
|
+
.road-b span { color: var(--color-text-secondary); font-size: var(--t4); line-height: 1.55; }
|
|
856
864
|
|
|
857
865
|
.road-t {
|
|
858
866
|
font-family: var(--teach-mono);
|
|
@@ -889,6 +897,8 @@ const TEACH_QUIZ: Component = {
|
|
|
889
897
|
'--teach-mono',
|
|
890
898
|
'--teach-shadow',
|
|
891
899
|
'--color-teach-accent-bg',
|
|
900
|
+
'--t4',
|
|
901
|
+
'--t5',
|
|
892
902
|
],
|
|
893
903
|
rules: `/* ---- Quiz ---- */
|
|
894
904
|
|
|
@@ -907,7 +917,7 @@ const TEACH_QUIZ: Component = {
|
|
|
907
917
|
margin-bottom: 0.45rem;
|
|
908
918
|
cursor: pointer;
|
|
909
919
|
font-family: var(--teach-sans);
|
|
910
|
-
font-size:
|
|
920
|
+
font-size: var(--t4);
|
|
911
921
|
background: transparent;
|
|
912
922
|
width: 100%;
|
|
913
923
|
text-align: left;
|
|
@@ -967,7 +977,7 @@ const TEACH_QUIZ: Component = {
|
|
|
967
977
|
|
|
968
978
|
.fb {
|
|
969
979
|
display: none;
|
|
970
|
-
font-size:
|
|
980
|
+
font-size: var(--t4);
|
|
971
981
|
line-height: 1.55;
|
|
972
982
|
color: var(--color-text-secondary);
|
|
973
983
|
background: var(--color-surface);
|
|
@@ -983,7 +993,7 @@ footer {
|
|
|
983
993
|
margin-top: 4rem;
|
|
984
994
|
padding-top: 1.4rem;
|
|
985
995
|
border-top: 1px solid var(--color-border);
|
|
986
|
-
font-size:
|
|
996
|
+
font-size: var(--t5);
|
|
987
997
|
line-height: 1.6;
|
|
988
998
|
color: var(--color-muted);
|
|
989
999
|
}`,
|
|
@@ -1004,6 +1014,8 @@ const TEACH_GLOSSARY: Component = {
|
|
|
1004
1014
|
'--color-accent',
|
|
1005
1015
|
'--teach-sans',
|
|
1006
1016
|
'--teach-mono',
|
|
1017
|
+
'--t4',
|
|
1018
|
+
'--t5',
|
|
1007
1019
|
],
|
|
1008
1020
|
rules: `/* ---- Glossary view, generated from the markdown source ---- */
|
|
1009
1021
|
|
|
@@ -1022,7 +1034,7 @@ h2 .count {
|
|
|
1022
1034
|
.filter {
|
|
1023
1035
|
width: 100%;
|
|
1024
1036
|
font-family: var(--teach-sans);
|
|
1025
|
-
font-size:
|
|
1037
|
+
font-size: var(--t4);
|
|
1026
1038
|
color: var(--color-text-body);
|
|
1027
1039
|
background: var(--color-background);
|
|
1028
1040
|
border: 1px solid var(--color-border);
|
|
@@ -1064,7 +1076,7 @@ h2 .count {
|
|
|
1064
1076
|
display: none;
|
|
1065
1077
|
padding: 1.2rem 0;
|
|
1066
1078
|
color: var(--color-muted);
|
|
1067
|
-
font-size:
|
|
1079
|
+
font-size: var(--t4);
|
|
1068
1080
|
}
|
|
1069
1081
|
|
|
1070
1082
|
.gloss.none .empty { display: block; }
|
|
@@ -1072,14 +1084,14 @@ h2 .count {
|
|
|
1072
1084
|
.gterm {
|
|
1073
1085
|
padding: 0.7rem 0;
|
|
1074
1086
|
border-bottom: 1px solid var(--color-border);
|
|
1075
|
-
font-size:
|
|
1087
|
+
font-size: var(--t4);
|
|
1076
1088
|
line-height: 1.55;
|
|
1077
1089
|
}
|
|
1078
1090
|
|
|
1079
1091
|
.gloss-group {
|
|
1080
1092
|
margin: 1.1rem 0 0.4rem;
|
|
1081
1093
|
font-family: var(--teach-sans);
|
|
1082
|
-
font-size:
|
|
1094
|
+
font-size: var(--t5);
|
|
1083
1095
|
font-weight: 600;
|
|
1084
1096
|
color: var(--color-muted);
|
|
1085
1097
|
text-transform: uppercase;
|
|
@@ -1101,7 +1113,7 @@ h2 .count {
|
|
|
1101
1113
|
ol.succ {
|
|
1102
1114
|
margin: 0 0 2.5rem;
|
|
1103
1115
|
padding-left: 1.35rem;
|
|
1104
|
-
font-size:
|
|
1116
|
+
font-size: var(--t4);
|
|
1105
1117
|
line-height: 1.6;
|
|
1106
1118
|
color: var(--color-text-secondary);
|
|
1107
1119
|
}
|
|
@@ -1137,6 +1149,7 @@ const TEACH_OUTLINE: Component = {
|
|
|
1137
1149
|
'--teach-sans',
|
|
1138
1150
|
'--teach-measure',
|
|
1139
1151
|
'--teach-mast-h',
|
|
1152
|
+
'--t5',
|
|
1140
1153
|
],
|
|
1141
1154
|
rules: `/* ---- In-lesson outline, in the gutter the measure leaves over ---- */
|
|
1142
1155
|
|
|
@@ -1151,7 +1164,7 @@ const TEACH_OUTLINE: Component = {
|
|
|
1151
1164
|
width: calc(13rem + 8px);
|
|
1152
1165
|
max-height: calc(100vh - var(--teach-mast-h) - 4rem);
|
|
1153
1166
|
overflow-y: auto;
|
|
1154
|
-
font-size:
|
|
1167
|
+
font-size: var(--t5);
|
|
1155
1168
|
line-height: 1.45;
|
|
1156
1169
|
}
|
|
1157
1170
|
|
|
@@ -1218,12 +1231,13 @@ const TEACH_REFERENCES: Component = {
|
|
|
1218
1231
|
'--color-accent',
|
|
1219
1232
|
'--teach-mono',
|
|
1220
1233
|
'--color-teach-accent-bg',
|
|
1234
|
+
'--t5',
|
|
1221
1235
|
],
|
|
1222
1236
|
rules: `footer {
|
|
1223
1237
|
margin-top: 4rem;
|
|
1224
1238
|
padding-top: 1.4rem;
|
|
1225
1239
|
border-top: 1px solid var(--color-border);
|
|
1226
|
-
font-size:
|
|
1240
|
+
font-size: var(--t5);
|
|
1227
1241
|
line-height: 1.6;
|
|
1228
1242
|
color: var(--color-muted);
|
|
1229
1243
|
}
|
|
@@ -1252,7 +1266,7 @@ ol.refs {
|
|
|
1252
1266
|
counter-reset: r;
|
|
1253
1267
|
padding: 0;
|
|
1254
1268
|
margin: 0.9rem 0 1.1rem;
|
|
1255
|
-
font-size:
|
|
1269
|
+
font-size: var(--t5);
|
|
1256
1270
|
line-height: 1.55;
|
|
1257
1271
|
}
|
|
1258
1272
|
|
package/src/gate/measures.ts
CHANGED
|
@@ -142,7 +142,7 @@ export function auditsBaselineRel(root: string): string {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
export const CAPTURE_STAMP_FAILURE =
|
|
145
|
-
'A capture set disagrees with the stamp written when its image was captured. Run canon capture assets/captures --selector .window --out assets and commit each frame with its image and its stamp.'
|
|
145
|
+
'A capture set disagrees with the stamp written when its image was captured. Run canon capture assets/captures --selector .window --out assets/evidence and commit each frame with its image and its stamp.'
|
|
146
146
|
|
|
147
147
|
function parseJson(payload: string): unknown {
|
|
148
148
|
try {
|
|
@@ -1118,11 +1118,12 @@ async function collectPluginManifests(ctx: MeasureContext): Promise<string[]> {
|
|
|
1118
1118
|
/**
|
|
1119
1119
|
* A capture set spans two folders. The markup and the template beside it are
|
|
1120
1120
|
* the authored half and sit under `assets/captures/`, while the image a
|
|
1121
|
-
* document points at and the stamp answering for it
|
|
1122
|
-
* where `--out assets` sends them
|
|
1121
|
+
* document points at and the stamp answering for it sit under
|
|
1122
|
+
* `assets/evidence/`, which is where `--out assets/evidence` sends them and where
|
|
1123
|
+
* `canon pr evidence` looks for a changed image.
|
|
1123
1124
|
*/
|
|
1124
1125
|
const CAPTURE_MARKUP_DIR = 'assets/captures'
|
|
1125
|
-
const CAPTURE_OUTPUT_DIR = 'assets'
|
|
1126
|
+
const CAPTURE_OUTPUT_DIR = 'assets/evidence'
|
|
1126
1127
|
|
|
1127
1128
|
/**
|
|
1128
1129
|
* Every capture under `assets/captures/`, named by the base its three files
|
package/src/gate/stages.ts
CHANGED
|
@@ -201,7 +201,7 @@ export const STAGES: readonly Stage[] = [
|
|
|
201
201
|
kind: 'drift',
|
|
202
202
|
pathspec: 'assets/captures/*.html',
|
|
203
203
|
failure:
|
|
204
|
-
'A generated frame drifted from the catalogs or the design source. Run bun run check, then canon capture assets/captures --selector .window --out assets, and commit each assets/captures/*.html with its .png and .stamp.',
|
|
204
|
+
'A generated frame drifted from the catalogs or the design source. Run bun run check, then canon capture assets/captures --selector .window --out assets/evidence, and commit each assets/captures/*.html with its .png and .stamp.',
|
|
205
205
|
},
|
|
206
206
|
{ kind: 'measure', measure: captureStamps },
|
|
207
207
|
],
|