stitch2 0.20.1 → 0.22.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/dist/approve.js +3 -2
- package/dist/consistency.js +1 -1
- package/dist/list.js +10 -3
- package/dist/screens.d.ts +32 -2
- package/dist/screens.js +76 -2
- package/package.json +1 -1
- package/skills/stitch2/SKILL.md +3 -2
- package/skills/stitch2/references/components.md +7 -3
- package/skills/stitch2/references/screens.md +9 -0
package/dist/approve.js
CHANGED
|
@@ -18,7 +18,8 @@ if (!targets.length) {
|
|
|
18
18
|
// Oldest first, so approving several versions of one screen leaves the latest current.
|
|
19
19
|
targets.sort((a, b) => a.path.localeCompare(b.path, undefined, { numeric: true }));
|
|
20
20
|
for (const t of targets) {
|
|
21
|
-
const archived = approve(t.path);
|
|
21
|
+
const { archived, relinked } = approve(t.path);
|
|
22
22
|
const note = archived.length ? `; archived ${archived.map((s) => s.version).join(', ')}` : '';
|
|
23
|
-
|
|
23
|
+
const links = relinked.length ? `; relinked ${relinked.join(', ')}` : '';
|
|
24
|
+
console.log(`approved ${t.screen} ${t.version} ${t.device}${note}${links}`);
|
|
24
25
|
}
|
package/dist/consistency.js
CHANGED
|
@@ -335,7 +335,7 @@ export function reuseIssues(path, page) {
|
|
|
335
335
|
if (familyOf(path) !== 'screens')
|
|
336
336
|
return [];
|
|
337
337
|
return page.handBuilt.slice(0, 10).map((h) => ({
|
|
338
|
-
severity: '
|
|
338
|
+
severity: 'error',
|
|
339
339
|
type: 'reuse',
|
|
340
340
|
message: `${nameOf(h.kind)} built by hand; use <${NAMES[h.kind]?.[1] ?? 'the catalog component'}> (see the component catalog, ${config.catalogDir})`,
|
|
341
341
|
selector: h.selector,
|
package/dist/list.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lists every screen version, variant by variant (mobile, tablet, desktop), with its status and the reviewer's note, for agents (and anyone without the
|
|
3
3
|
* canvas open). Approved versions are the reference; review versions wait for the user; notes are feedback.
|
|
4
|
-
* Ends with what makes the current version unclear (versionProblems)
|
|
4
|
+
* Ends with what makes the current version unclear (versionProblems) and links to a previous variant
|
|
5
|
+
* (staleLinks); --strict fails on either.
|
|
5
6
|
* Usage: stitch2 screens [screen name] [--status approved|review|draft|archived] [--json] [--strict]
|
|
6
7
|
*/
|
|
7
|
-
import { listScreens, stateOf, versionProblems } from './screens.js';
|
|
8
|
+
import { listScreens, staleLinks, stateOf, versionProblems } from './screens.js';
|
|
8
9
|
const args = process.argv.slice(2);
|
|
9
10
|
const status = args.includes('--status') ? args[args.indexOf('--status') + 1] : undefined;
|
|
10
11
|
const name = args.find((a, i) => !a.startsWith('--') && args[i - 1] !== '--status');
|
|
@@ -35,5 +36,11 @@ if (problems.length && !args.includes('--json')) {
|
|
|
35
36
|
for (const p of problems)
|
|
36
37
|
console.log(` ${p}`);
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
+
const links = staleLinks().filter((p) => !name || p.startsWith(`${name}/`) || p.includes(` ${name} `));
|
|
40
|
+
if (links.length && !args.includes('--json')) {
|
|
41
|
+
console.log(`\nLinks to a previous variant (${links.length}):`);
|
|
42
|
+
for (const p of links)
|
|
43
|
+
console.log(` ${p}`);
|
|
44
|
+
}
|
|
45
|
+
if ((problems.length || links.length) && args.includes('--strict'))
|
|
39
46
|
process.exit(1);
|
package/dist/screens.d.ts
CHANGED
|
@@ -40,13 +40,43 @@ export declare function listScreens(): Screen[];
|
|
|
40
40
|
export declare const stateOf: (s: Pick<Screen, 'screen' | 'page'>) => string;
|
|
41
41
|
/** Sets (or removes, with undefined) <prefix>-* meta tags in a screen file, keeping the rest of it untouched. */
|
|
42
42
|
export declare function writeMeta(path: string, values: Record<string, string | undefined>): void;
|
|
43
|
+
/**
|
|
44
|
+
* Links between screens: a screen embeds or points at another screen's variant,
|
|
45
|
+
* `src="/screens/workout/mobile-v3.html"`. Such a link always names the current approved version of that
|
|
46
|
+
* screen on that device; approval moves every link that names an earlier version to the approved one.
|
|
47
|
+
*/
|
|
48
|
+
/** Every src/href reference to a screen variant inside a screen file. */
|
|
49
|
+
export interface ScreenRef {
|
|
50
|
+
screen: string;
|
|
51
|
+
device: 'mobile' | 'tablet' | 'desktop';
|
|
52
|
+
version: number;
|
|
53
|
+
}
|
|
54
|
+
export declare function findScreenRefs(html: string): ScreenRef[];
|
|
55
|
+
/** Bumps every reference to an earlier version of one screen variant to the given version. */
|
|
56
|
+
export declare function rewriteScreenRefs(html: string, screen: string, device: string, version: number): string;
|
|
57
|
+
/**
|
|
58
|
+
* Moves every link that names an earlier version of the target's screen on its device to the target's
|
|
59
|
+
* version, in every screen file. Returns the files it changed.
|
|
60
|
+
*/
|
|
61
|
+
export declare function rewriteStaleLinks(target: Pick<Screen, 'screen' | 'device' | 'version'>): string[];
|
|
62
|
+
/**
|
|
63
|
+
* Links that name a variant which is not current: the file is gone, the variant is archived, or a newer
|
|
64
|
+
* version is approved on that screen and device. A link to a version still in review or a draft is a
|
|
65
|
+
* deliberate preview of a proposal, not stale. One line each; empty when every link is current.
|
|
66
|
+
*/
|
|
67
|
+
export declare function staleLinks(screens?: Screen[]): string[];
|
|
43
68
|
/**
|
|
44
69
|
* Approves a variant. Approval makes it the one current version of its screen on its device: every earlier
|
|
45
70
|
* version there (approved, in review or a draft) and any other approved one become archived, so nobody can mistake
|
|
46
71
|
* an older one for what is implemented. Later versions (a newer proposal) keep their status.
|
|
47
|
-
*
|
|
72
|
+
* Links from other screens to an earlier version of this screen on this device move to the approved one,
|
|
73
|
+
* so no screen keeps pointing at a previous variant.
|
|
74
|
+
* Returns the variants it archived and the files whose links moved.
|
|
48
75
|
*/
|
|
49
|
-
export declare function approve(path: string):
|
|
76
|
+
export declare function approve(path: string): {
|
|
77
|
+
archived: Screen[];
|
|
78
|
+
relinked: string[];
|
|
79
|
+
};
|
|
50
80
|
/**
|
|
51
81
|
* What makes it unclear which version of a screen is current: two approved on one device, or an earlier version
|
|
52
82
|
* still in review or a draft next to an approved later one. One line each; empty when every screen is clear.
|
package/dist/screens.js
CHANGED
|
@@ -93,11 +93,85 @@ export function writeMeta(path, values) {
|
|
|
93
93
|
}
|
|
94
94
|
writeFileSync(file, html);
|
|
95
95
|
}
|
|
96
|
+
const refRe = /(?:src|href)="[^"]*?(?:screens\/)?([a-z0-9-]+)\/(mobile|tablet|desktop)-v(\d+)\.html/g;
|
|
97
|
+
export function findScreenRefs(html) {
|
|
98
|
+
const out = [];
|
|
99
|
+
for (const m of html.matchAll(refRe))
|
|
100
|
+
out.push({ screen: m[1], device: m[2], version: Number(m[3]) });
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/** Bumps every reference to an earlier version of one screen variant to the given version. */
|
|
104
|
+
export function rewriteScreenRefs(html, screen, device, version) {
|
|
105
|
+
return html.replace(refRe, (m, s, d, v) => s === screen && d === device && Number(v) < version ? m.replace(`-v${v}.html`, `-v${version}.html`) : m);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Moves every link that names an earlier version of the target's screen on its device to the target's
|
|
109
|
+
* version, in every screen file. Returns the files it changed.
|
|
110
|
+
*/
|
|
111
|
+
export function rewriteStaleLinks(target) {
|
|
112
|
+
const n = versionNumber(target);
|
|
113
|
+
if (Number.isNaN(n))
|
|
114
|
+
return [];
|
|
115
|
+
const changed = [];
|
|
116
|
+
for (const dir of config.screenDirs) {
|
|
117
|
+
for (const file of htmlFiles(join(ROOT, dir))) {
|
|
118
|
+
const html = readFileSync(file, 'utf8');
|
|
119
|
+
const next = rewriteScreenRefs(html, target.screen, target.device, n);
|
|
120
|
+
if (next !== html) {
|
|
121
|
+
writeFileSync(file, next);
|
|
122
|
+
changed.push(relative(ROOT, file));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return changed.sort();
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Links that name a variant which is not current: the file is gone, the variant is archived, or a newer
|
|
130
|
+
* version is approved on that screen and device. A link to a version still in review or a draft is a
|
|
131
|
+
* deliberate preview of a proposal, not stale. One line each; empty when every link is current.
|
|
132
|
+
*/
|
|
133
|
+
export function staleLinks(screens = listScreens()) {
|
|
134
|
+
const out = [];
|
|
135
|
+
const current = new Map();
|
|
136
|
+
for (const s of screens) {
|
|
137
|
+
if (s.kind !== 'screen' || s.status !== 'approved')
|
|
138
|
+
continue;
|
|
139
|
+
const key = `${s.screen} ${s.device}`;
|
|
140
|
+
const n = versionNumber(s);
|
|
141
|
+
if (!Number.isNaN(n))
|
|
142
|
+
current.set(key, Math.max(current.get(key) ?? -1, n));
|
|
143
|
+
}
|
|
144
|
+
const byVariant = new Map(screens.map((s) => [`${s.screen} ${s.device} v${versionNumber(s)}`, s]));
|
|
145
|
+
const seen = new Set();
|
|
146
|
+
for (const dir of config.screenDirs) {
|
|
147
|
+
for (const file of htmlFiles(join(ROOT, dir))) {
|
|
148
|
+
const rel = relative(ROOT, file);
|
|
149
|
+
const html = readFileSync(file, 'utf8');
|
|
150
|
+
for (const r of findScreenRefs(html)) {
|
|
151
|
+
const key = `${r.screen} ${r.device}`;
|
|
152
|
+
const cur = current.get(key);
|
|
153
|
+
if (cur === undefined || r.version >= cur)
|
|
154
|
+
continue;
|
|
155
|
+
const hit = byVariant.get(`${key} v${r.version}`);
|
|
156
|
+
if (hit && hit.status !== 'approved' && hit.status !== 'archived')
|
|
157
|
+
continue;
|
|
158
|
+
const line = `${rel}: points at ${r.screen} ${r.device}-v${r.version} (${hit ? hit.status : 'missing'}); current is v${cur}`;
|
|
159
|
+
if (!seen.has(line)) {
|
|
160
|
+
seen.add(line);
|
|
161
|
+
out.push(line);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return out.sort();
|
|
167
|
+
}
|
|
96
168
|
/**
|
|
97
169
|
* Approves a variant. Approval makes it the one current version of its screen on its device: every earlier
|
|
98
170
|
* version there (approved, in review or a draft) and any other approved one become archived, so nobody can mistake
|
|
99
171
|
* an older one for what is implemented. Later versions (a newer proposal) keep their status.
|
|
100
|
-
*
|
|
172
|
+
* Links from other screens to an earlier version of this screen on this device move to the approved one,
|
|
173
|
+
* so no screen keeps pointing at a previous variant.
|
|
174
|
+
* Returns the variants it archived and the files whose links moved.
|
|
101
175
|
*/
|
|
102
176
|
export function approve(path) {
|
|
103
177
|
const all = listScreens();
|
|
@@ -114,7 +188,7 @@ export function approve(path) {
|
|
|
114
188
|
(s.status === 'approved' || versionNumber(s) < n));
|
|
115
189
|
for (const s of outdated)
|
|
116
190
|
writeMeta(s.path, { [tag('status')]: 'archived' });
|
|
117
|
-
return outdated;
|
|
191
|
+
return { archived: outdated, relinked: rewriteStaleLinks(target) };
|
|
118
192
|
}
|
|
119
193
|
/**
|
|
120
194
|
* What makes it unclear which version of a screen is current: two approved on one device, or an earlier version
|
package/package.json
CHANGED
package/skills/stitch2/SKILL.md
CHANGED
|
@@ -54,8 +54,9 @@ References, read before the first screen:
|
|
|
54
54
|
and translate it with **stitch2-translate**. Never add classes to a component to change how it looks, and never write a colour value (hex,
|
|
55
55
|
`rgb()`, `hsl()`): only DESIGN.md's colours, as token classes or `var(--<prefix>-<name>)`.
|
|
56
56
|
5. **Check** `stitch2 check <path> --shots --strict`. Fix every error (overflow, wrapped labels, content under
|
|
57
|
-
fixed bars, fixed bars overlapping each other, tap targets, contrast, cramped text)
|
|
58
|
-
|
|
57
|
+
fixed bars, fixed bars overlapping each other, tap targets, contrast, cramped text), a hand-built copy of a
|
|
58
|
+
catalog component (`reuse`: use the component or extend it, never rebuild it), and every warning about type
|
|
59
|
+
(`type-*`), components that differ from the catalog (`consistency`) and colour values (`color`).
|
|
59
60
|
Text running into other text (`text-overlap`) or out of its card (`overflow`) is an error; a short label cut
|
|
60
61
|
short with an ellipsis (`truncated`) is a warning to fix with room, not a smaller size. Google's rules are
|
|
61
62
|
warnings too (`text-small`, `edge-margin`, `measure`, `tap-spacing`, `placeholder-label`, `unnamed-control`,
|
|
@@ -11,6 +11,8 @@ colour, icons and states. Users learn an element once; every variant costs atten
|
|
|
11
11
|
|
|
12
12
|
## The ladder
|
|
13
13
|
|
|
14
|
+
Open the catalog first (on the canvas, or its render under `<design>/renders/`): every element of the
|
|
15
|
+
screen is mapped to a component before any markup is written, and the mapping is part of the hand-over.
|
|
14
16
|
Go down only when the step above cannot work:
|
|
15
17
|
|
|
16
18
|
1. **Use** a component as it is.
|
|
@@ -69,9 +71,11 @@ series) is added to DESIGN.md first, with a line in the prose saying where it is
|
|
|
69
71
|
|
|
70
72
|
## What the checks report
|
|
71
73
|
|
|
72
|
-
- **reuse
|
|
73
|
-
|
|
74
|
-
label).
|
|
74
|
+
- **reuse** (an error: it fails `check --strict` and blocks the hand-over): on a lab screen, an element that
|
|
75
|
+
looks like a catalog component but was built by hand (a bottom bar with three or more buttons, an h1,
|
|
76
|
+
a primary-filled button, a pill chip, a header icon button, a caps label). A screen that duplicates a
|
|
77
|
+
component instead of using it is not handed over: use the component, or extend it (step 4 of the ladder)
|
|
78
|
+
when it cannot do what the screen needs.
|
|
75
79
|
- **consistency**: a component whose size, radius, font, icon set or icons differ from the catalog's version
|
|
76
80
|
(or, without a catalog, from the majority of screens in its family), or that appears in two versions on one
|
|
77
81
|
screen. `stitch2 consistency` shows the same as cropped images; see the stitch2-consistency skill.
|
|
@@ -86,6 +86,15 @@ The canvas shows each version with its variants side by side under one heading,
|
|
|
86
86
|
needs review while any variant does. **Approve version** approves every variant in review at once; a single
|
|
87
87
|
variant can still be approved, archived or noted on its own.
|
|
88
88
|
|
|
89
|
+
## Links between screens
|
|
90
|
+
|
|
91
|
+
A screen that embeds or points at another screen names its file
|
|
92
|
+
(`src="/screens/workout/mobile-v3.html"`): always the current approved version of that screen on that
|
|
93
|
+
device, never a previous one. Approving a variant moves every such link to it at once (the canvas Approve
|
|
94
|
+
does the same as `stitch2 approve`), and `stitch2 screens` lists any link that still names a previous variant
|
|
95
|
+
— fix it, or move it to the current version yourself when the link was deliberate. A link to a version still
|
|
96
|
+
in review or a draft is a preview of a proposal and stays.
|
|
97
|
+
|
|
89
98
|
## How to use them
|
|
90
99
|
|
|
91
100
|
1. Before designing, run `stitch2 screens` (a name or `--status approved` narrows it) and open the approved
|