stitch2 0.15.2 → 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 +5 -3
- 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 +6 -3
- package/skills/stitch2/references/screens.md +14 -5
- package/skills/stitch2-app-sync/SKILL.md +8 -4
- package/skills/stitch2-copy/SKILL.md +93 -0
- package/skills/stitch2-i18n/SKILL.md +6 -0
- package/skills/stitch2-translate/SKILL.md +91 -0
- package/skills/stitch2-translate/references/locales.md +40 -0
- package/skills/stitch2-translate/references/review.md +30 -0
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.
|
|
@@ -83,7 +84,7 @@ file that already exists. From there the agent spends its effort on the design.
|
|
|
83
84
|
- **Skills** for agents (`skills/`): `stitch2` (the design workflow, principles, typography, components,
|
|
84
85
|
versions), `stitch2-design-md` (building the design system), `stitch2-alternatives` (fresh-eyes versions and
|
|
85
86
|
testing skill changes), `stitch2-consistency` (verifying and fixing
|
|
86
|
-
consistency), `stitch2-i18n` (screens in every language), `stitch2-app-sync` (keeping the app and the designs in step: design first, then the app) and,
|
|
87
|
+
consistency), `stitch2-i18n` (screens in every language), `stitch2-copy` (writing the text: a brief, plain words, the reader's outcome), `stitch2-translate` (translating by meaning, with a glossary from the product, a review by error type and a check in context), `stitch2-app-sync` (keeping the app and the designs in step: design first, then the app) and,
|
|
87
88
|
optional, `stitch2-import-from-google-stitch` (drafting screens in Google Stitch through its MCP server).
|
|
88
89
|
|
|
89
90
|
## Setup
|
|
@@ -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
|
@@ -46,7 +46,8 @@ References, read before the first screen:
|
|
|
46
46
|
variant). Assemble it from the components; plain markup only where none
|
|
47
47
|
applies, with token classes, `.type-<level>` classes for text, the spacing scale, and the states DESIGN.md
|
|
48
48
|
defines. When the config has `i18n`, mark every product string with its message key (`data-t`, see
|
|
49
|
-
**stitch2-i18n**).
|
|
49
|
+
**stitch2-i18n**). Write the text as **stitch2-copy** says (the reader's outcome, plain words, the product's terms),
|
|
50
|
+
and translate it with **stitch2-translate**. Never add classes to a component to change how it looks, and never write a colour value (hex,
|
|
50
51
|
`rgb()`, `hsl()`): only DESIGN.md's colours, as token classes or `var(--<prefix>-<name>)`.
|
|
51
52
|
5. **Check** `stitch2 check <path> --shots --strict`. Fix every error (overflow, wrapped labels, content under
|
|
52
53
|
fixed bars, fixed bars overlapping each other, tap targets, contrast, cramped text) and every warning about type (`type-*`), hand-built
|
|
@@ -65,7 +66,8 @@ References, read before the first screen:
|
|
|
65
66
|
6. **Look and critique.** View the render in `<design>/renders/` (crop the first screenful). Score it with the
|
|
66
67
|
rubric in principles.md, fix what fails, repeat 5–6 at most three times.
|
|
67
68
|
7. **Hand over.** Set the version's status to `review`, point the user to it on the canvas, and report what
|
|
68
|
-
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).
|
|
69
71
|
|
|
70
72
|
## Commands
|
|
71
73
|
|
|
@@ -79,7 +81,8 @@ References, read before the first screen:
|
|
|
79
81
|
| `i18n [filter] [--locale l] [--suggest]` | Message keys screens use that the source or a locale lacks, and text with no key (stitch2-i18n) |
|
|
80
82
|
| `consistency [kind…]` | Every shared component cropped from every screen, next to the catalog's version |
|
|
81
83
|
| `type [filter] [--json]` | Every text style a screen uses and the type level it matches |
|
|
82
|
-
| `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 |
|
|
83
86
|
| `compare [screen…]` | The live app against the approved versions: missing or extra components, order, looks; a side-by-side report |
|
|
84
87
|
| `elements <entry>` | Bundles the app's components, turned into elements with `stitch2/elements`, for the screens |
|
|
85
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
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stitch2-copy
|
|
3
|
+
description: Write the words on screens and landing pages so they say what the product does for the reader, in plain language a translator can carry into any language. Use when writing or rewriting a screen's text, a landing page, onboarding, empty states or buttons, when the user says the copy is weak, generic, too technical or "not selling", and before translating (stitch2-translate starts from this source).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Copy with stitch2
|
|
7
|
+
|
|
8
|
+
The words are part of the design. A screen can pass every layout check and still fail because its text is vague,
|
|
9
|
+
written for the wrong reader, or impossible to translate well. This skill writes the **source** text; translating
|
|
10
|
+
it is stitch2-translate.
|
|
11
|
+
|
|
12
|
+
## 1. The brief: design/COPY.md
|
|
13
|
+
|
|
14
|
+
Write it once per product, before the text, and keep it next to DESIGN.md. It is short:
|
|
15
|
+
|
|
16
|
+
- **Reader.** Who opens this page and what they want done today, in their words ("know what to lift and log it
|
|
17
|
+
without fuss"), not in the product's ("a training management platform"). A page has one main reader; others
|
|
18
|
+
(developers, self-hosters, partners) get their own section further down, written for them.
|
|
19
|
+
- **Promise.** One sentence: what the product does for that reader. Every headline on the page serves it.
|
|
20
|
+
- **Proof.** Real, specific facts that back the promise: numbers, names, what happens on screen. No round fake
|
|
21
|
+
numbers.
|
|
22
|
+
- **Voice.** Three words for how it sounds, and what it is not ("calm, direct, practical; never hype, never
|
|
23
|
+
slang").
|
|
24
|
+
- **Words.** The product's terms, one per concept, as the UI uses them (*set*, *routine*, *program*), and the words
|
|
25
|
+
it never uses. stitch2-translate turns this into a glossary per language.
|
|
26
|
+
- **Section intents.** For a landing page, one line per section: what the reader should understand or do after
|
|
27
|
+
it. The intent travels with the strings to the translators.
|
|
28
|
+
|
|
29
|
+
## 2. Writing rules
|
|
30
|
+
|
|
31
|
+
Sources: Google developer style guide (*Write for a global audience*), Nielsen Norman Group (people scan: 79% of
|
|
32
|
+
users scan, 16% read word by word), Mozilla's marketing style guides, Material and Apple writing guidance.
|
|
33
|
+
|
|
34
|
+
- **Lead with the reader's outcome**, then how the product gets there. "Know what to lift today" before "a
|
|
35
|
+
progression engine".
|
|
36
|
+
- **Concrete beats clever.** Name the thing on the screen ("last time's weight sits next to every set") instead of
|
|
37
|
+
an abstraction ("data-driven insights"). A headline should still make sense with the page's design removed.
|
|
38
|
+
- **One idea per sentence; short sentences.** They scan better and translate better.
|
|
39
|
+
- **Plain words.** *use*, not *leverage*; *start*, not *commence*. No idioms, puns, phrasal verbs or cultural
|
|
40
|
+
references in the source: the translator has to rebuild them, and usually cannot.
|
|
41
|
+
- **Second person, active voice, present tense.** "You log a set", "gymgym fills in the weight".
|
|
42
|
+
- **Sentence case** for headings and buttons. A button is a verb and its object ("Start training", "Send
|
|
43
|
+
answers").
|
|
44
|
+
- **No hype.** Not *seamless, powerful, ultimate, revolutionary, effortless, journey, unlock, supercharge*; no
|
|
45
|
+
exclamation marks. Proof replaces adjectives.
|
|
46
|
+
- **Technical words only for the reader who uses them.** Licences, hosting, protocols and stack belong in the
|
|
47
|
+
section for developers, and even there say what they mean for the reader ("your data stays in your database")
|
|
48
|
+
before naming the technology.
|
|
49
|
+
- **Write for translation.** Keep each sentence whole in one string (never split a sentence across keys or build
|
|
50
|
+
it by concatenation); use placeholders for numbers and names, with plural forms; avoid words with two meanings
|
|
51
|
+
in context (*set* the noun vs the verb). See stitch2-i18n for keys and placeholders.
|
|
52
|
+
|
|
53
|
+
### A landing page
|
|
54
|
+
|
|
55
|
+
- **Hero**: an eyebrow (what it is, or its strongest fact), a title of up to about eight words with the outcome,
|
|
56
|
+
a lead of one or two sentences with how and why to trust it, one primary action and at most one secondary.
|
|
57
|
+
- **Sections**: eyebrow = the topic (one or two words); title = the benefit; lead = how it works, in product
|
|
58
|
+
terms. Feature cards: title = what the reader gets, text = what happens on screen.
|
|
59
|
+
- **Lists** (checklists, "also included"): parallel grammar, each item a short claim the reader can check.
|
|
60
|
+
- **Calls to action** repeat the same verb for the same action across the page.
|
|
61
|
+
|
|
62
|
+
### In the product
|
|
63
|
+
|
|
64
|
+
Labels are nouns, buttons are verbs, hints say what happens next, empty states say what this place is for and
|
|
65
|
+
offer the first action, errors say what happened and what to do. Reuse the app's existing words before inventing
|
|
66
|
+
new ones.
|
|
67
|
+
|
|
68
|
+
## 3. Process
|
|
69
|
+
|
|
70
|
+
1. Write or update the brief.
|
|
71
|
+
2. **Read the strings in order.** `stitch2 i18n <screen> --json` lists a screen's keys; read the source text top
|
|
72
|
+
to bottom as one story, without the design. Mark every sentence that is vague, technical for the reader,
|
|
73
|
+
repeats another, or would not survive translation.
|
|
74
|
+
3. Rewrite in the source messages (the app's files: one copy of every string). Keep keys whose meaning stays;
|
|
75
|
+
a string whose meaning changes gets its new text under the same key only if every place that uses it wants
|
|
76
|
+
the new meaning.
|
|
77
|
+
4. Self-review with the checklist below, then `stitch2 check <screen> --shots` for fit.
|
|
78
|
+
5. **Cold read.** Give a fresh agent only the page's strings (no brief, no design) and ask: what is this product,
|
|
79
|
+
who is it for, what would you do next? If the answer misses the promise, the page does not say it yet. A
|
|
80
|
+
`stitch2 sandbox` folder keeps the fresh agent from seeing your reasoning.
|
|
81
|
+
6. Then translate with stitch2-translate: the brief's section intents are its context.
|
|
82
|
+
|
|
83
|
+
## Checklist
|
|
84
|
+
|
|
85
|
+
- [ ] The hero says what the product does for the main reader, in their words.
|
|
86
|
+
- [ ] Every section title is a benefit; every lead says how, concretely.
|
|
87
|
+
- [ ] No hype words, no exclamation marks, no idioms or puns.
|
|
88
|
+
- [ ] Technical terms only in the section for the readers who use them.
|
|
89
|
+
- [ ] One term per concept, the same as the UI.
|
|
90
|
+
- [ ] Sentences short, whole in one string, with placeholders for numbers.
|
|
91
|
+
- [ ] Buttons are verb + object, the same verb for the same action.
|
|
92
|
+
- [ ] The cold read names the promise.
|
|
93
|
+
|
|
@@ -110,6 +110,12 @@ Use logical properties so layouts flip by themselves: `ms-*`/`me-*`, `ps-*`/`pe-
|
|
|
110
110
|
`text-start`, `rounded-s-*`. Icons that point (back, next, chevrons, progress) get `rtl:-scale-x-100`;
|
|
111
111
|
icons that show objects (a dumbbell, a clock) and numbers, charts and media controls do not flip.
|
|
112
112
|
|
|
113
|
+
## Writing and translating the words
|
|
114
|
+
|
|
115
|
+
This skill makes every language fit. What the text says, and how well each language says it, is
|
|
116
|
+
**stitch2-copy** (the source) and **stitch2-translate** (meaning, not words: a glossary from the app, the
|
|
117
|
+
language's conventions, a review pass by error type, then this skill's check in context).
|
|
118
|
+
|
|
113
119
|
## Hand over
|
|
114
120
|
|
|
115
121
|
Tell the user which languages you checked, what you changed to make each fit (layout, word, component), and
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stitch2-translate
|
|
3
|
+
description: Translate a product's screens and landing pages by meaning, as a native writer of each language would put it, not word for word — with a glossary taken from the product, the language's own conventions, a review pass that marks errors by type and severity, and a check in context. Use when adding or updating translations, when a language reads like a translation (calques, wrong terms, odd tone), when the user complains about a language, or after stitch2-copy changes the source.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Translating with stitch2
|
|
7
|
+
|
|
8
|
+
A good translation says what the source *means* and has the effect it is meant to have, in words a native
|
|
9
|
+
speaker would choose. "Avoid literal translation at all costs" (Mozilla's localization style guide). For product
|
|
10
|
+
UI that means the product's own terms, every time; for marketing it means transcreation: the headline may change
|
|
11
|
+
its words entirely to keep its point.
|
|
12
|
+
|
|
13
|
+
Machine and model translation in one pass is fluent but literal. It does better when it prepares like a human
|
|
14
|
+
translator does and then revises against named errors: preparing keywords, topic and examples first (MAPS, He et
|
|
15
|
+
al. 2023, arXiv:2305.04118), translating, then estimating errors and refining (TEaR, Feng et al. 2024,
|
|
16
|
+
arXiv:2402.16379), with errors marked by MQM type and severity (GEMBA-MQM, Kocmi & Federmann 2023,
|
|
17
|
+
arXiv:2310.13988), and with separate roles for translator, localizer and reviewer (TransAgents, Wu et al. 2024,
|
|
18
|
+
arXiv:2405.11804). This skill is that process.
|
|
19
|
+
|
|
20
|
+
## 0. Before translating
|
|
21
|
+
|
|
22
|
+
- **The source is good.** If the source is vague or full of idioms, fix it first (stitch2-copy): a translation
|
|
23
|
+
cannot be better than what it says.
|
|
24
|
+
- **The brief** (design/COPY.md): reader, promise, voice, section intents. Every string is translated knowing
|
|
25
|
+
what it is for.
|
|
26
|
+
|
|
27
|
+
## 1. Prepare, per language
|
|
28
|
+
|
|
29
|
+
1. **Glossary from the product.** Before writing a word, read how the app already says the core concepts in this
|
|
30
|
+
language (the locale's messages: set, rep, rest, workout, routine, program, history…; search the message files
|
|
31
|
+
for the source terms). Write them into design/COPY.md under the language. The landing page uses the app's
|
|
32
|
+
words; when the app's own terms collide (one word for two concepts), fix the collision in the app first and
|
|
33
|
+
note the decision.
|
|
34
|
+
2. **Language conventions** (references/locales.md): form of address used by the app (detect it from existing
|
|
35
|
+
strings: *du*/*Sie*, *tu*/*vous*, *ты*/*вы*), quotation marks, spaces before punctuation, dashes, number
|
|
36
|
+
formatting, capitalization.
|
|
37
|
+
3. **Context per string.** Where it appears (hero title, button, chip, checklist item), how much room it has
|
|
38
|
+
(`stitch2 i18n <screen> --json` lists keys per screen; the renders show the space), and the intent from the
|
|
39
|
+
brief.
|
|
40
|
+
|
|
41
|
+
## 2. Draft as a native writer
|
|
42
|
+
|
|
43
|
+
- Read the whole section in the source first; translate the section, not string by string.
|
|
44
|
+
- Ask, as Mozilla's guide does: what does this mean, what is the author trying to say, how would I say that in my
|
|
45
|
+
language? Then write it the way a good local fitness, finance or tech site (whatever the product is) would.
|
|
46
|
+
- Headlines and slogans: transcreate. Keep the point and the length class (short stays short), drop the source's
|
|
47
|
+
wording when it does not work. Write two or three options and keep the best.
|
|
48
|
+
- UI strings: the glossary term, the language's usual verb for the action ("Войти", "Anmelden"), the same form of
|
|
49
|
+
address everywhere.
|
|
50
|
+
- Keep placeholders, plural categories (CLDR) and markup slots; never concatenate.
|
|
51
|
+
- Units, numbers and dates come from the locale; product names, brands and code stay as written.
|
|
52
|
+
|
|
53
|
+
## 3. Review: mark errors, then fix
|
|
54
|
+
|
|
55
|
+
Review as a **separate pass**, ideally a separate agent that sees the source, the draft, the glossary and the
|
|
56
|
+
conventions, but not the drafter's reasoning. Mark every problem with an MQM category and severity
|
|
57
|
+
(references/review.md), for example:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
site.hero.title ru "Знайте, что поднимать." fluency/unnatural (calque of "Know what to lift") major
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The questions that catch most errors:
|
|
64
|
+
|
|
65
|
+
- Would a native copywriter ever write this sentence? If it only makes sense to someone who knows the English, it
|
|
66
|
+
is a calque.
|
|
67
|
+
- Does it say exactly what the source means, nothing added, nothing lost?
|
|
68
|
+
- Is every product concept the glossary term? Is the form of address the app's?
|
|
69
|
+
- Punctuation, quotes, spaces and numbers as the language writes them?
|
|
70
|
+
|
|
71
|
+
Fix every critical and major error and review again, until a pass finds none. A back-translation can confirm the
|
|
72
|
+
meaning; it is never a target for style.
|
|
73
|
+
|
|
74
|
+
## 4. In context
|
|
75
|
+
|
|
76
|
+
1. `stitch2 check <screen> --locales <l> --shots`: nothing cut, wrapped badly or overlapping (stitch2-i18n has the
|
|
77
|
+
fixes, in order).
|
|
78
|
+
2. Read the render top to bottom in the language. Headlines that broke onto an orphan word, a term that changed
|
|
79
|
+
between two sections, a tone that changed: fix in the messages.
|
|
80
|
+
|
|
81
|
+
## 5. Hand over
|
|
82
|
+
|
|
83
|
+
Say which languages you reviewed in context, the terminology decisions (with the collisions you fixed), and which
|
|
84
|
+
strings deserve a native speaker's read before a launch: headlines and anything legal. Be honest about the
|
|
85
|
+
languages you are less sure of.
|
|
86
|
+
|
|
87
|
+
## References
|
|
88
|
+
|
|
89
|
+
- references/locales.md: conventions per language.
|
|
90
|
+
- references/review.md: the error types and severities, and the review output.
|
|
91
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Conventions per language
|
|
2
|
+
|
|
3
|
+
The defaults for consumer products; when the product already uses another form of address, keep the product's.
|
|
4
|
+
Numbers, dates and units come from `Intl` in the app; in prose, write them the way the language does.
|
|
5
|
+
|
|
6
|
+
| Language | Address | Quotes | Punctuation and spacing | Numbers | Notes |
|
|
7
|
+
|---|---|---|---|---|---|
|
|
8
|
+
| English (en) | you | “ ” | sentence case; serial comma by house style | 1,324.5 | the source: plain, no idioms |
|
|
9
|
+
| German (de) | du in consumer apps, Sie in banking and B2B | „ “ | nouns capitalised; compounds closed (Pausentimer) | 1.324,5 | avoid Denglisch where a German word is usual; keep accepted loanwords (Coach, Workout) |
|
|
10
|
+
| French (fr) | tu or vous by product; be consistent | « » with narrow no-break spaces | narrow no-break space before ; : ! ? and inside « » | 1 324,5 | sentence case; avoid anglicismes (séance, série, répétition) |
|
|
11
|
+
| Spanish (es) | tú | « » or “ ” | ¿ ¡ open questions and exclamations | 1324,5 / 1.324,5 | neutral Spanish for Spain and Latin America; avoid regional slang |
|
|
12
|
+
| Italian (it) | tu | « » or “ ” | sentence case | 1.324,5 | allenamento, serie, ripetizioni, recupero |
|
|
13
|
+
| Portuguese, Portugal (pt) | tu (or você) by product | « » or “ ” | sentence case | 1324,5 | ginásio, ecrã, telemóvel, registar — never the Brazilian forms |
|
|
14
|
+
| Portuguese, Brazil (pt-BR) | você | “ ” | sentence case | 1.324,5 | academia, tela, celular, registrar |
|
|
15
|
+
| Polish (pl) | ty (imperative 2nd person singular) | „ ” | sentence case | 1324,5 | seria, powtórzenia, przerwa; avoid noun chains |
|
|
16
|
+
| Turkish (tr) | sen (or siz) by product | “ ” | sentence case; suffixes after numbers and names with an apostrophe (Strong’dan) | 1.324,5 | vowel harmony in suffixes after placeholders needs care: prefer phrasing without a suffix on {name} |
|
|
17
|
+
| Russian (ru) | вы (lowercase) | «ёлочки», „лапки“ inside | spaced em dash —; no Title Case; ё where it matters | 1 324,5 (no-break space) | lifter vocabulary: подход, повторение (повтор), отдых, разминка, жим лёжа; no calques (see below) |
|
|
18
|
+
| Ukrainian (uk) | ви | « » | apostrophe ’ inside words (м’язи) | 1 324,5 | not Russian with Ukrainian letters: own vocabulary (підхід, повторення, відпочинок, тренування) |
|
|
19
|
+
| Hungarian (hu) | te in consumer apps | „ ” | sentence case | 1 324,5 | word order differs a lot: rebuild the sentence |
|
|
20
|
+
| Arabic (ar) | neutral second person, masculine as default where unavoidable | « » or ” “ | Arabic comma ، and question mark ؟; right to left | Western digits by product choice | Modern Standard Arabic, plain; mirror arrows |
|
|
21
|
+
| Hindi (hi) | आप | “ ” | । as full stop | 1,324.5 (Indian grouping for large numbers) | gym words are often English in Devanagari (सेट, रेप्स, वर्कआउट): use what Indian fitness apps use |
|
|
22
|
+
| Thai (th) | neutral, no polite particles in UI | “ ” | no spaces between words; a space ends a phrase | 1,324.5 | short; avoid long noun phrases |
|
|
23
|
+
| Korean (ko) | 해요체 (polite informal) in consumer apps | “ ” | spaces between words (띄어쓰기) | 1,324.5 | consistent ending style across the product |
|
|
24
|
+
| Chinese, Simplified (zh) | 你 | “ ” | full-width punctuation ,。:;?! | 1,324.5 | a space between Chinese and Latin letters or numbers is house style: keep the product's |
|
|
25
|
+
|
|
26
|
+
## Calques to catch (Russian examples; every language has its own)
|
|
27
|
+
|
|
28
|
+
| Source | Literal (wrong) | Natural |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| Know what to lift. | Знайте, что поднимать. | Всегда знаете, какой вес брать. |
|
|
31
|
+
| Keep every rep. | Сохраните каждое повторение. | Ни один подход не потеряется. |
|
|
32
|
+
| A coach that reads your training. | Тренер, который читает ваши тренировки. | Тренер, который знает ваши тренировки. |
|
|
33
|
+
| Get gymgym | Получить gymgym | Как начать |
|
|
34
|
+
| Use ours, or run your own. | Пользуйтесь нашим или запустите свой. | Пользуйтесь нашим сервером или разверните свой. |
|
|
35
|
+
| at a glance | с первого взгляда | всё на одном экране |
|
|
36
|
+
|
|
37
|
+
Signs of a calque: the sentence keeps English word order; a possessive on every noun (ваши, ваш, вашего); a verb
|
|
38
|
+
that only makes sense in English (*read* training, *keep* reps); nouns where the language prefers a verb; an
|
|
39
|
+
English idiom kept word for word.
|
|
40
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Review: error types and severity
|
|
2
|
+
|
|
3
|
+
Based on the MQM (Multidimensional Quality Metrics) typology, cut down to what product text needs. Mark each
|
|
4
|
+
error on the string it is in.
|
|
5
|
+
|
|
6
|
+
| Category | Subtypes | Example |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| Accuracy | mistranslation, omission, addition, untranslated | "rest timer" as "остаток времени" |
|
|
9
|
+
| Fluency | grammar, spelling, unnatural (calque), inconsistency within the string | "Знайте, что поднимать" |
|
|
10
|
+
| Terminology | not the glossary term, two terms for one concept | routine as "программа" here and "тренировка" there |
|
|
11
|
+
| Style | register or form of address unlike the product, tone unlike the brief, wordy | "вы" in a "ты" product; marketing hype |
|
|
12
|
+
| Locale conventions | punctuation, quotes, spacing, numbers, dates, capitalization | "Hello : world" spacing in fr, Title Case in ru |
|
|
13
|
+
| Design | does not fit, breaks a line badly, wrong plural form | (stitch2 check finds most) |
|
|
14
|
+
|
|
15
|
+
Severity:
|
|
16
|
+
|
|
17
|
+
- **critical**: says something false or harmful (wrong price, wrong action on a button, legal or medical
|
|
18
|
+
mistake), or unreadable.
|
|
19
|
+
- **major**: a reader notices: unnatural, wrong term, wrong tone, meaning shifted.
|
|
20
|
+
- **minor**: a careful reader notices: punctuation, a slightly better word exists.
|
|
21
|
+
|
|
22
|
+
Output, one line per error, then a verdict:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
<key> <locale> "<text>" <category>/<subtype> <severity> <why, and the fix>
|
|
26
|
+
verdict: <locale> n critical, n major, n minor
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
A language ships when its last pass has no critical or major errors.
|
|
30
|
+
|