stitch2 0.15.3 → 0.16.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 +4 -2
- package/dist/approve.d.ts +1 -0
- package/dist/approve.js +24 -0
- package/dist/audit.js +3 -0
- package/dist/cli.js +2 -1
- package/dist/list.js +11 -2
- package/dist/screens.d.ts +14 -0
- package/dist/screens.js +49 -0
- package/dist/serve.js +4 -9
- package/dist/typecheck.js +3 -0
- package/package.json +1 -1
- package/skills/stitch2/SKILL.md +4 -2
- package/skills/stitch2/references/screens.md +14 -5
- package/skills/stitch2-app-sync/SKILL.md +8 -4
package/README.md
CHANGED
|
@@ -72,7 +72,8 @@ file that already exists. From there the agent spends its effort on the design.
|
|
|
72
72
|
```
|
|
73
73
|
- **Versions, variants and approval**: a screen has versions (v1, v2, …), and each version has variants, its
|
|
74
74
|
layouts for mobile and desktop (`mobile-v3.html`, `desktop-v3.html`). The canvas groups them; you approve a
|
|
75
|
-
version as a whole or one variant at a time, leave notes
|
|
75
|
+
version as a whole or one variant at a time, and leave notes. The approved version is the one current version:
|
|
76
|
+
approving it archives every earlier one, and only it gets implemented.
|
|
76
77
|
- **DESIGN.md builder**: `stitch2 init` writes a complete DESIGN.md from a few choices (brand colour, modes,
|
|
77
78
|
fonts, roundness, density), with colours checked for contrast; `stitch2 extract` measures one from an existing
|
|
78
79
|
site or screens.
|
|
@@ -253,7 +254,8 @@ layout first, then a shorter natural word, never a smaller type size.
|
|
|
253
254
|
| `stitch2 i18n [filter] [--locale l] [--suggest] [--json]` | Message keys the source or a locale lacks, and screen text with no key |
|
|
254
255
|
| `stitch2 consistency [kind…]` | The consistency report, `<design>/consistency/index.html` |
|
|
255
256
|
| `stitch2 type [filter] [--json]` | The type audit |
|
|
256
|
-
| `stitch2 screens [name] [--status s] [--json]` | Versions, statuses and notes |
|
|
257
|
+
| `stitch2 screens [name] [--status s] [--json] [--strict]` | Versions, statuses and notes; flags a screen whose current version is unclear |
|
|
258
|
+
| `stitch2 approve <path or filter…>` | Approve variants (the user's decision); every earlier version is archived |
|
|
257
259
|
| `stitch2 tokens` | Tokens from DESIGN.md |
|
|
258
260
|
| `stitch2 compare [screen…]` | The live app against the approved screens (`app` in the config) |
|
|
259
261
|
| `stitch2 elements <entry>` | Bundle your components (`stitch2/elements`) for the screens |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/approve.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approves screen variants, as the user does on the canvas: each becomes the current version of its screen on its
|
|
3
|
+
* device, and every earlier version there is archived. Agents run it when the user approves in words ("approve
|
|
4
|
+
* the v3 screens"), never on their own.
|
|
5
|
+
* Usage: stitch2 approve <path or filter…> (a path under the design folder, or part of one: "connect/", "-v3")
|
|
6
|
+
*/
|
|
7
|
+
import { approve, listScreens } from './screens.js';
|
|
8
|
+
const filters = process.argv.slice(2).filter((a) => !a.startsWith('--'));
|
|
9
|
+
if (!filters.length) {
|
|
10
|
+
console.error('stitch2 approve <path or filter…>: which variants? ("home/mobile-v3", "connect/", "-v3")');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
const targets = listScreens().filter((s) => s.kind === 'screen' && s.status !== 'archived' && filters.some((f) => s.path.includes(f)));
|
|
14
|
+
if (!targets.length) {
|
|
15
|
+
console.error(`No screen variant matches ${filters.join(', ')}.`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
// Oldest first, so approving several versions of one screen leaves the latest current.
|
|
19
|
+
targets.sort((a, b) => a.path.localeCompare(b.path, undefined, { numeric: true }));
|
|
20
|
+
for (const t of targets) {
|
|
21
|
+
const archived = approve(t.path);
|
|
22
|
+
const note = archived.length ? `; archived ${archived.map((s) => s.version).join(', ')}` : '';
|
|
23
|
+
console.log(`approved ${t.screen} ${t.version} ${t.device}${note}`);
|
|
24
|
+
}
|
package/dist/audit.js
CHANGED
|
@@ -36,6 +36,9 @@ export async function audit(opts) {
|
|
|
36
36
|
if (closed && !closed.querySelector(':scope > summary')?.contains(el))
|
|
37
37
|
return false;
|
|
38
38
|
const r = el.getBoundingClientRect();
|
|
39
|
+
// Text kept for screen readers only (Tailwind's sr-only: a 1px box, clipped away) is not drawn.
|
|
40
|
+
if (r.width <= 1 && r.height <= 1 && cs.position === 'absolute')
|
|
41
|
+
return false;
|
|
39
42
|
return r.width > 0 && r.height > 0;
|
|
40
43
|
};
|
|
41
44
|
const opacity = (el) => {
|
package/dist/cli.js
CHANGED
|
@@ -42,7 +42,8 @@ const COMMANDS = {
|
|
|
42
42
|
'i18n-report.ts',
|
|
43
43
|
'[filter] [--locale l] [--suggest] [--json] Keys screens use that a locale lacks, and screen text with no key',
|
|
44
44
|
],
|
|
45
|
-
screens: ['list.ts', '[name] [--status s] [--json] Screen versions, statuses and
|
|
45
|
+
screens: ['list.ts', '[name] [--status s] [--json] [--strict] Screen versions, statuses, notes, and which version is current'],
|
|
46
|
+
approve: ['approve.ts', '<path or filter…> Approve variants for the user; earlier versions are archived'],
|
|
46
47
|
localize: ['assets.ts', '[filter] Copy remote images a screen uses into its assets/ folder'],
|
|
47
48
|
sandbox: [
|
|
48
49
|
'sandbox.ts',
|
package/dist/list.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lists every screen version, variant by variant (mobile, 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
|
-
*
|
|
4
|
+
* Ends with what makes the current version unclear (versionProblems); --strict fails on it.
|
|
5
|
+
* Usage: stitch2 screens [screen name] [--status approved|review|draft|archived] [--json] [--strict]
|
|
5
6
|
*/
|
|
6
|
-
import { listScreens } from './screens.js';
|
|
7
|
+
import { listScreens, versionProblems } from './screens.js';
|
|
7
8
|
const args = process.argv.slice(2);
|
|
8
9
|
const status = args.includes('--status') ? args[args.indexOf('--status') + 1] : undefined;
|
|
9
10
|
const name = args.find((a, i) => !a.startsWith('--') && args[i - 1] !== '--status');
|
|
@@ -17,3 +18,11 @@ if (args.includes('--json'))
|
|
|
17
18
|
else
|
|
18
19
|
for (const s of rows)
|
|
19
20
|
console.log(`${s.screen.padEnd(20)} ${s.version.padEnd(8)} ${s.device.padEnd(8)} ${s.status.padEnd(9)} ${s.path}${s.note ? `\n${' '.repeat(39)}note: ${s.note}` : ''}`);
|
|
21
|
+
const problems = versionProblems().filter((p) => !name || p.startsWith(`${name} `));
|
|
22
|
+
if (problems.length && !args.includes('--json')) {
|
|
23
|
+
console.log(`\nWhich version is current is unclear (${problems.length}):`);
|
|
24
|
+
for (const p of problems)
|
|
25
|
+
console.log(` ${p}`);
|
|
26
|
+
}
|
|
27
|
+
if (problems.length && args.includes('--strict'))
|
|
28
|
+
process.exit(1);
|
package/dist/screens.d.ts
CHANGED
|
@@ -32,3 +32,17 @@ export declare const meta: (html: string, name: string) => string | undefined;
|
|
|
32
32
|
export declare function listScreens(): Screen[];
|
|
33
33
|
/** Sets (or removes, with undefined) <prefix>-* meta tags in a screen file, keeping the rest of it untouched. */
|
|
34
34
|
export declare function writeMeta(path: string, values: Record<string, string | undefined>): void;
|
|
35
|
+
/**
|
|
36
|
+
* Approves a variant. Approval makes it the one current version of its screen on its device: every earlier
|
|
37
|
+
* version there (approved, in review or a draft) and any other approved one become archived, so nobody can mistake
|
|
38
|
+
* an older one for what is implemented. Later versions (a newer proposal) keep their status.
|
|
39
|
+
* Returns the variants it archived.
|
|
40
|
+
*/
|
|
41
|
+
export declare function approve(path: string): Screen[];
|
|
42
|
+
/**
|
|
43
|
+
* What makes it unclear which version of a screen is current: two approved on one device, or an earlier version
|
|
44
|
+
* still in review or a draft next to an approved later one. One line each; empty when every screen is clear.
|
|
45
|
+
*/
|
|
46
|
+
export declare function versionProblems(screens?: Screen[]): string[];
|
|
47
|
+
/** A version's number: 3 for "v3"; NaN for one without (a Stitch export, a trial). */
|
|
48
|
+
export declare const versionNumber: (s: Pick<Screen, 'version'>) => number;
|
package/dist/screens.js
CHANGED
|
@@ -83,3 +83,52 @@ export function writeMeta(path, values) {
|
|
|
83
83
|
}
|
|
84
84
|
writeFileSync(file, html);
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Approves a variant. Approval makes it the one current version of its screen on its device: every earlier
|
|
88
|
+
* version there (approved, in review or a draft) and any other approved one become archived, so nobody can mistake
|
|
89
|
+
* an older one for what is implemented. Later versions (a newer proposal) keep their status.
|
|
90
|
+
* Returns the variants it archived.
|
|
91
|
+
*/
|
|
92
|
+
export function approve(path) {
|
|
93
|
+
const all = listScreens();
|
|
94
|
+
const target = all.find((s) => s.path === path);
|
|
95
|
+
if (!target || target.kind !== 'screen')
|
|
96
|
+
throw new Error(`not a screen: ${path}`);
|
|
97
|
+
writeMeta(path, { [tag('status')]: 'approved' });
|
|
98
|
+
const n = versionNumber(target);
|
|
99
|
+
const outdated = all.filter((s) => s.path !== path &&
|
|
100
|
+
s.kind === 'screen' &&
|
|
101
|
+
s.screen === target.screen &&
|
|
102
|
+
s.device === target.device &&
|
|
103
|
+
s.status !== 'archived' &&
|
|
104
|
+
(s.status === 'approved' || versionNumber(s) < n));
|
|
105
|
+
for (const s of outdated)
|
|
106
|
+
writeMeta(s.path, { [tag('status')]: 'archived' });
|
|
107
|
+
return outdated;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* What makes it unclear which version of a screen is current: two approved on one device, or an earlier version
|
|
111
|
+
* still in review or a draft next to an approved later one. One line each; empty when every screen is clear.
|
|
112
|
+
*/
|
|
113
|
+
export function versionProblems(screens = listScreens()) {
|
|
114
|
+
const out = [];
|
|
115
|
+
const groups = new Map();
|
|
116
|
+
for (const s of screens) {
|
|
117
|
+
if (s.kind !== 'screen' || s.status === 'archived')
|
|
118
|
+
continue;
|
|
119
|
+
const key = `${s.screen} ${s.device}`;
|
|
120
|
+
groups.set(key, [...(groups.get(key) ?? []), s]);
|
|
121
|
+
}
|
|
122
|
+
for (const [key, list] of groups) {
|
|
123
|
+
const approved = list.filter((s) => s.status === 'approved');
|
|
124
|
+
if (approved.length > 1)
|
|
125
|
+
out.push(`${key}: ${approved.map((s) => s.version).join(' and ')} are all approved; approve the current one again`);
|
|
126
|
+
const current = Math.max(...approved.map(versionNumber).filter((n) => !Number.isNaN(n)));
|
|
127
|
+
for (const s of list)
|
|
128
|
+
if (s.status !== 'approved' && versionNumber(s) < current)
|
|
129
|
+
out.push(`${key}: ${s.version} is still ${s.status} although v${current} is approved; archive it (${s.path})`);
|
|
130
|
+
}
|
|
131
|
+
return out.sort();
|
|
132
|
+
}
|
|
133
|
+
/** A version's number: 3 for "v3"; NaN for one without (a Stitch export, a trial). */
|
|
134
|
+
export const versionNumber = (s) => Number(/^v(\d+)$/.exec(s.version)?.[1] ?? Number.NaN);
|
package/dist/serve.js
CHANGED
|
@@ -28,7 +28,7 @@ function inlineTailwind(html, file) {
|
|
|
28
28
|
return `<style type="text/tailwindcss">${readFileSync(css, 'utf8').replace(/\s*\n\s*/g, ' ')}</style>`;
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
|
-
import { listScreens, ROOT, STATUSES, tag, writeMeta } from './screens.js';
|
|
31
|
+
import { listScreens, ROOT, STATUSES, tag, writeMeta, approve } from './screens.js';
|
|
32
32
|
/**
|
|
33
33
|
* The consistency report covers every screen that is not archived and the components; the app comparison covers
|
|
34
34
|
* the approved screens. A report is stale once one of those files is newer than it (a status change counts: it
|
|
@@ -131,14 +131,9 @@ export function startServer(port) {
|
|
|
131
131
|
if (note !== undefined)
|
|
132
132
|
values[tag('note')] = note.trim() || undefined;
|
|
133
133
|
writeMeta(path, values);
|
|
134
|
-
// One
|
|
135
|
-
if (status === 'approved')
|
|
136
|
-
|
|
137
|
-
if (s.path !== path &&
|
|
138
|
-
s.screen === target.screen &&
|
|
139
|
-
s.device === target.device &&
|
|
140
|
-
s.status === 'approved')
|
|
141
|
-
writeMeta(s.path, { [tag('status')]: 'archived' });
|
|
134
|
+
// One current version per screen and device: approving archives every earlier one (screens.ts).
|
|
135
|
+
if (status === 'approved' && target.kind === 'screen')
|
|
136
|
+
approve(path);
|
|
142
137
|
json(listScreens());
|
|
143
138
|
}
|
|
144
139
|
catch (e) {
|
package/dist/typecheck.js
CHANGED
|
@@ -30,6 +30,9 @@ export function collectText() {
|
|
|
30
30
|
const r = el.getBoundingClientRect();
|
|
31
31
|
if (!r.width || !r.height)
|
|
32
32
|
continue;
|
|
33
|
+
// Text for screen readers only (Tailwind's sr-only: a 1px box, clipped away) is not drawn.
|
|
34
|
+
if (r.width <= 1 && r.height <= 1 && cs.position === 'absolute')
|
|
35
|
+
continue;
|
|
33
36
|
let opacity = 1;
|
|
34
37
|
for (let e = el; e; e = e.parentElement)
|
|
35
38
|
opacity *= Number(getComputedStyle(e).opacity);
|
package/package.json
CHANGED
package/skills/stitch2/SKILL.md
CHANGED
|
@@ -66,7 +66,8 @@ References, read before the first screen:
|
|
|
66
66
|
6. **Look and critique.** View the render in `<design>/renders/` (crop the first screenful). Score it with the
|
|
67
67
|
rubric in principles.md, fix what fails, repeat 5–6 at most three times.
|
|
68
68
|
7. **Hand over.** Set the version's status to `review`, point the user to it on the canvas, and report what
|
|
69
|
-
changed, how you answered their notes, and any rubric item you could not satisfy. The user approves
|
|
69
|
+
changed, how you answered their notes, and any rubric item you could not satisfy. The user approves; only then
|
|
70
|
+
does it go into the app (the approved version is the one current version: see screens.md).
|
|
70
71
|
|
|
71
72
|
## Commands
|
|
72
73
|
|
|
@@ -80,7 +81,8 @@ References, read before the first screen:
|
|
|
80
81
|
| `i18n [filter] [--locale l] [--suggest]` | Message keys screens use that the source or a locale lacks, and text with no key (stitch2-i18n) |
|
|
81
82
|
| `consistency [kind…]` | Every shared component cropped from every screen, next to the catalog's version |
|
|
82
83
|
| `type [filter] [--json]` | Every text style a screen uses and the type level it matches |
|
|
83
|
-
| `screens [name] [--status s] [--json]` | Every version with its status and the user's note |
|
|
84
|
+
| `screens [name] [--status s] [--json] [--strict]` | Every version with its status and the user's note, and anything that makes the current version unclear |
|
|
85
|
+
| `approve <path or filter…>` | Approves variants for the user (only on their word); every earlier version of the screen on that device is archived |
|
|
84
86
|
| `compare [screen…]` | The live app against the approved versions: missing or extra components, order, looks; a side-by-side report |
|
|
85
87
|
| `elements <entry>` | Bundles the app's components, turned into elements with `stitch2/elements`, for the screens |
|
|
86
88
|
| `tokens` | `tokens.css` (colours, `.type-*` classes) and `tailwind.tokens.js` from DESIGN.md |
|
|
@@ -35,8 +35,13 @@ and build on it. Meta tag names below use the project's prefix (`<p>`, from `sti
|
|
|
35
35
|
|---|---|---|
|
|
36
36
|
| `draft` | Work in progress | The agent, while working |
|
|
37
37
|
| `review` | Finished, waiting for the user ("Needs review") | The agent, when done |
|
|
38
|
-
| `approved` | The
|
|
39
|
-
| `archived` |
|
|
38
|
+
| `approved` | The current version: the reference to build on and match, and the only one the app implements | The user only (on the canvas, or in words: then the agent runs `stitch2 approve`) |
|
|
39
|
+
| `archived` | Outdated: superseded or rejected; hidden on the canvas | Approval: approving a variant archives every earlier version of that screen on that device, whatever its status |
|
|
40
|
+
|
|
41
|
+
**One current version.** For each screen and device there is at most one approved variant, and it is what the app
|
|
42
|
+
shows. Approving one archives every earlier version there (approved, in review or a draft), so nothing older can be
|
|
43
|
+
mistaken for the current one; a later version, a newer proposal, keeps its status. `stitch2 screens` ends with
|
|
44
|
+
anything that breaks this (two approved, an older version still in review); `--strict` fails on it.
|
|
40
45
|
|
|
41
46
|
The canvas shows each version with its variants side by side under one heading, and one status for the version:
|
|
42
47
|
needs review while any variant does. **Approve version** approves every variant in review at once; a single
|
|
@@ -47,8 +52,12 @@ variant can still be approved, archived or noted on its own.
|
|
|
47
52
|
1. Before designing, run `stitch2 screens` (a name or `--status approved` narrows it) and open the approved
|
|
48
53
|
versions of the screens next to yours.
|
|
49
54
|
2. A note is the user's feedback: address every point in your next version or edit, and say how.
|
|
50
|
-
3. Never
|
|
51
|
-
|
|
55
|
+
3. Never approve on your own and never build on an archived version unless asked. When the user approves in words
|
|
56
|
+
("approve the v3 screens"), run `stitch2 approve <path or filter…>` rather than editing status tags: it archives the
|
|
57
|
+
earlier versions as the canvas does.
|
|
58
|
+
4. **Implement only approved versions.** A version in review or a draft is not built in the app, not even partly. If
|
|
59
|
+
the user asks to implement one that is not approved yet, that request is their approval: run `stitch2 approve` on it
|
|
60
|
+
first and say so. If the app needs something no approved version shows, design it and hand it over first.
|
|
52
61
|
5. When the user pastes a reference copied from the canvas, such as `home mobile v2
|
|
53
62
|
(design/screens/home/mobile-v2.html)`, open that file: it names exactly the version they mean.
|
|
54
63
|
6. A reference to an element adds its source line, its tag and its text: `home mobile v1
|
|
@@ -59,7 +68,7 @@ variant can still be approved, archived or noted on its own.
|
|
|
59
68
|
|
|
60
69
|
## Implementing an approved version
|
|
61
70
|
|
|
62
|
-
Build the screen in the app from the same components the screens use, in the version's order, with the app's
|
|
71
|
+
Check first with `stitch2 screens <screen>` that the version is approved and is the only approved one on its device. Build the screen in the app from the same components the screens use, in the version's order, with the app's
|
|
63
72
|
own data, strings and behaviour; keep the app's tests passing. Then run `stitch2 compare <screen>` with the app
|
|
64
73
|
running: fix every `missing` component and look at the side-by-side report. `extra` and `order` warnings are
|
|
65
74
|
fine when the app needs them (a desktop-only control, a frame drawn by layout); say so when you hand over. When
|
|
@@ -19,7 +19,10 @@ a component rebuilt by hand. The checks can only judge what the canvas draws, so
|
|
|
19
19
|
approved screen if the change is visible there; a catalog entry for a new variant or state), and run
|
|
20
20
|
`stitch2 check <screens> --strict`. Look at the screens yourself: the check finds overlaps and overflow, not
|
|
21
21
|
ugliness.
|
|
22
|
-
3. **Approval**: the user approves the new versions (or the catalog change) as for any design.
|
|
22
|
+
3. **Approval**: the user approves the new versions (or the catalog change) as for any design. Nothing goes into
|
|
23
|
+
the app before this: a version in review is not implemented, not even partly. When the user asks to implement
|
|
24
|
+
one in review, run `stitch2 approve` on it first; that archives the earlier versions, so the approved one is the
|
|
25
|
+
single current version and the app matches it.
|
|
23
26
|
4. **App**: the app picks the component up; wire it, then run `stitch2 compare <screen>`.
|
|
24
27
|
|
|
25
28
|
Fixing a bug found in the app follows the same path: reproduce it on the canvas (a screen or catalog state that
|
|
@@ -62,7 +65,8 @@ Read the findings as:
|
|
|
62
65
|
|
|
63
66
|
## Before you call it done
|
|
64
67
|
|
|
65
|
-
1. `stitch2
|
|
66
|
-
2. `stitch2
|
|
67
|
-
3.
|
|
68
|
+
1. Every screen you implemented is approved, and `stitch2 screens --strict` passes: one current version each.
|
|
69
|
+
2. `stitch2 check --strict` passes on the screens you touched (archived screens excepted).
|
|
70
|
+
3. `stitch2 compare` has no errors for the screens you touched; warnings are explained (a kept feature) or fixed.
|
|
71
|
+
4. The user has seen the canvas versions of anything that changed how a component looks.
|
|
68
72
|
|