@penpair/web 0.1.1 → 0.1.2

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 CHANGED
@@ -128,14 +128,22 @@ Everyone who works with the assistant does their own `npx @penpair/web connect`:
128
128
  | [`@penpair/docker`](https://www.npmjs.com/package/@penpair/docker) | The same command, plus Penpot in Docker on your computer: `up`, `down`, `backup`, `restore`. Includes everything in the core | 0.1.1 |
129
129
  | [`@penpair/core`](https://www.npmjs.com/package/@penpair/core) | The library and engine, with the skills. No command of its own; the two above are built on it | 0.1.1 |
130
130
  | `penpair` | The short name for `@penpair/web`, so that `npx penpair setup` works | Waiting for npm to allow the name (it is blocked as too close to an unrelated package). Until then use `npx @penpair/web`. |
131
- | `@penpair/marwes-ui` | The adapter for the Marwes UI component library | Planned, see below |
131
+ | [`@penpair/marwes-ui`](https://www.npmjs.com/package/@penpair/marwes-ui) | The adapter for the Marwes UI component library: Marwes tokens (and your theme) in Penpot, with drift checks | Tokens now; components planned. See below |
132
132
  | `@penpair/reflection-check` | The link to Reflection's `reflection-check`, as its own package (today `penpair baselines` does it) | Planned |
133
133
 
134
134
  You install **one** command package, `@penpair/web` or `@penpair/docker`; the core comes with it.
135
135
 
136
136
  ## Use it with Marwes
137
137
 
138
- Using [Marwes UI](https://marwes.io), the themeable component library for React, Vue and Svelte? Connecting Marwes is the recommended setup: your Penpot file then speaks the same tokens and components as your code, so what is designed is what gets built. The adapter, `@penpair/marwes-ui`, is the next package on the plan: it will import the Marwes tokens into your Penpot file and check that frames use them. Until it ships, set up penpair as above and name Marwes as your library in the project facts the setup skill asks you to fill in; the assistant then works against Marwes's tokens and components.
138
+ Using [Marwes UI](https://marwes.io), the themeable component library for React, Vue and Svelte? Connecting Marwes is the recommended setup: your Penpot file then speaks the same tokens as your code, so what is designed is what gets built.
139
+
140
+ ```bash
141
+ npm i -D @penpair/marwes-ui
142
+ npx penpair marwes tokens snapshot --write # the expected tokens, from the installed Marwes and your own theme
143
+ npx penpair marwes tokens push --apply # Penpot gets them: Marwes sets, plus a small overlay of what your theme changes
144
+ ```
145
+
146
+ The part that keeps it solid is drift: a Marwes upgrade, a theme edit or a hand edit in Penpot is found, not discovered later. `penpair marwes tokens check` runs offline in CI and fails when the committed snapshot no longer matches the installed Marwes; `diff` shows what Penpot lacks and how many shapes each changed token reaches. How it works and how to put it in CI: [Marwes tokens in Penpot, and keeping them true](https://github.com/niklas-westman/penpair/blob/main/docs/marwes-tokens.md). Marwes components in Penpot are not covered yet; they are built as your pages need them.
139
147
 
140
148
  ## Check each page with Reflection
141
149
 
@@ -184,7 +192,7 @@ If you were told to set penpair up, read [the setup skill](https://github.com/ni
184
192
  - Run `npx @penpair/web doctor --json` to see where things stand; every failing check carries a `remedy`.
185
193
  - Web is the default; use Docker only when the person asks for offline or local data.
186
194
  - **The personal MCP key never goes through you.** The person pastes it themselves into `npx @penpair/web connect` (a hidden prompt), or pipes it from their clipboard.
187
- - Done means `doctor` reports `ok: true` and "editor and plugin" connected. Then `npx @penpair/web skills install` gives you the rules for working in Penpot (`penpot-design`) and for judging a screen (`design-judgment`).
195
+ - Done means `doctor` reports `ok: true` and "editor and plugin" connected. Then `npx @penpair/web skills install` gives you the rules for working in Penpot (`penpair-design`) and for judging a screen (`design-judgment`).
188
196
 
189
197
  ## Commands
190
198
 
@@ -211,6 +219,7 @@ If you were told to set penpair up, read [the setup skill](https://github.com/ni
211
219
 
212
220
  - [Getting started](https://github.com/niklas-westman/penpair/blob/main/docs/getting-started.md): step by step, for designers and for whoever sets it up
213
221
  - [Guide](https://github.com/niklas-westman/penpair/blob/main/docs/guide.md): extension points, instances, commands
222
+ - [Marwes tokens](https://github.com/niklas-westman/penpair/blob/main/docs/marwes-tokens.md): Marwes tokens in Penpot and drift checks
214
223
  - [Compatibility](https://github.com/niklas-westman/penpair/blob/main/docs/compatibility.md): what was checked on which Penpot
215
224
  - [Releasing](https://github.com/niklas-westman/penpair/blob/main/docs/releasing.md), [Changelog](https://github.com/niklas-westman/penpair/blob/main/CHANGELOG.md)
216
225
 
@@ -62,7 +62,7 @@ If you want to know what your Penpot offers before you start, `npx @penpair/web
62
62
  ```
63
63
  npx @penpair/web skills install
64
64
  ```
65
- This copies the rules the assistant follows (how to work safely in Penpot, and how to judge a screen) into `.skills/` and makes them visible to Codex and Claude Code. It prints a line for each to add to your `AGENTS.md`. Then fill in `.skills/penpot-design/references/project-facts.md` (your file, never replaced): the Penpot file, the token sets, the names you use, who approves.
65
+ This copies the rules the assistant follows (how to work safely in Penpot, and how to judge a screen) into `.skills/` and makes them visible to Codex and Claude Code. It prints a line for each to add to your `AGENTS.md`. Then fill in `.skills/penpair-design/references/project-facts.md` (your file, never replaced): the Penpot file, the token sets, the names you use, who approves.
66
66
 
67
67
  ### Every day
68
68
  Open the file in Penpot, choose **File, MCP Server, Connect**, keep the tab visible, and start working with the assistant.
package/docs/guide.md CHANGED
@@ -6,7 +6,7 @@ The contract an adopting project works through. It grows with the code; each sec
6
6
  | Point | A project supplies | First implementations |
7
7
  | --- | --- | --- |
8
8
  | Connection profile | endpoint, Penpot version, where the token comes from | local Docker, shared self-hosted, hosted |
9
- | Library adapter | where tokens and components come from | Marwes |
9
+ | Library adapter | where tokens and components come from; commands the adapter adds, listed under `adapters` in `penpair.config.json` (`defineAdapter` in `@penpair/core`) | `@penpair/marwes-ui` (tokens) |
10
10
  | Snippet registry | named plugin snippets with inputs and a size check | tree, audit, tokens, archive |
11
11
  | Verification provider | what "matches the design" means | `penpair baselines` (images and Reflection cases, below), a page-verify gate |
12
12
  | Skill pack | rules the agent follows | the Penpot skill, design judgment |
@@ -0,0 +1,119 @@
1
+ # Marwes tokens in Penpot, and keeping them true
2
+
3
+ `@penpair/marwes-ui` puts the design tokens of [Marwes UI](https://marwes.io) (and your own theme on top of them) into your Penpot file, and keeps them from drifting away from the Marwes your app actually uses. The idea in one line: **the installed Marwes and your theme file are the truth; Penpot is made to match, and anything that stops matching is found.**
4
+
5
+ ## How it fits together
6
+
7
+ ```
8
+ installed Marwes ─┐
9
+ (@marwes-ui/core) ├──▶ expected tokens ──▶ committed snapshot ──▶ Penpot file
10
+ your theme file ─┘ (generated) design/…snapshot.json (sets and themes)
11
+ (acmeLight …) reviewed in git
12
+ ```
13
+
14
+ - **Nothing is typed in twice.** The tokens are generated from the Marwes the app resolves and from the theme file the app imports (the same file and export), so what you design with is what the app renders.
15
+ - **Your choices stay visible.** Marwes's own values go in `Marwes · Core` and `Marwes · Dark`. What your theme changes goes in a small overlay set on top (`Acme · Light`, `Acme · Dark`), holding only the differences. A Marwes update moves the Marwes sets and never silently loses your choices, and the colours Marwes derives from yours (hover, pressed, disabled) follow automatically.
16
+ - **The snapshot is the record.** `design/marwes-tokens.snapshot.json` is generated, committed and reviewed like code. A Marwes bump shows up as a readable git diff of exactly which tokens changed.
17
+
18
+ ## Setup
19
+
20
+ ```bash
21
+ npm i -D @penpair/marwes-ui # next to @penpair/web (or @penpair/docker)
22
+ ```
23
+
24
+ In `penpair.config.json`:
25
+
26
+ ```json
27
+ {
28
+ "adapters": ["@penpair/marwes-ui"],
29
+ "marwes": {
30
+ "name": "Acme",
31
+ "project": "packages/app",
32
+ "themes": {
33
+ "light": { "module": "packages/app/src/theme.ts", "export": "acmeLight" },
34
+ "dark": { "module": "packages/app/src/theme.ts", "export": "acmeDark" }
35
+ },
36
+ "snapshot": "design/marwes-tokens.snapshot.json"
37
+ }
38
+ }
39
+ ```
40
+
41
+ | Setting | Meaning |
42
+ | --- | --- |
43
+ | `name` | Prefix for your overlay sets. Defaults to the instance name, capitalised. |
44
+ | `project` | The folder of the package that depends on Marwes (a workspace package in a monorepo). Default: the project root. |
45
+ | `themes` | Your own light and dark themes: a file and the name of its export (a Marwes `ThemeInput`; TypeScript is fine). Leave them out and only the Marwes sets are made. |
46
+ | `snapshot` | Where the committed snapshot lives. Default: `design/marwes-tokens.snapshot.json`. |
47
+
48
+ Then:
49
+
50
+ ```bash
51
+ penpair marwes tokens snapshot --write # generate the expectation and save it; commit the file
52
+ penpair marwes tokens push --apply # make Penpot match (look first without --apply)
53
+ ```
54
+
55
+ ## The commands
56
+
57
+ | Command | Needs Penpot | What it does |
58
+ | --- | --- | --- |
59
+ | `penpair marwes tokens snapshot [--write]` | no | Builds the expected tokens from the installed Marwes and your theme, and shows how they differ from the committed snapshot. `--write` saves it. |
60
+ | `penpair marwes tokens check [--json]` | no | Is the committed snapshot current? Exit 0 yes, 2 stale or missing, 1 broken (for example a half-finished Marwes upgrade). Made for CI. |
61
+ | `penpair marwes tokens diff [--json]` | yes | Penpot against the snapshot: what is missing, what differs, what was added by hand, and **how many shapes use each changed token**. |
62
+ | `penpair marwes tokens push [--apply] [--prune]` | yes | Brings Penpot to the snapshot. Looks first and prints the plan; `--apply` writes. |
63
+ | `penpair marwes tokens status` | optional | One screen: the installed Marwes packages, the snapshot, and Penpot. |
64
+
65
+ ## What can drift, and what catches it
66
+
67
+ | What drifted | Caught by | Resolved by |
68
+ | --- | --- | --- |
69
+ | Marwes was upgraded (any token changed, added or removed) | `check` (offline, in CI): the snapshot is stale | `snapshot --write`, review the git diff, commit, then `push --apply` |
70
+ | Your theme file changed | `check`: the snapshot is stale | the same |
71
+ | Marwes packages are at different versions (a half-finished upgrade) | `check` fails with exit 1 | finish the upgrade; Marwes releases its packages together |
72
+ | **Marwes grew a kind of variable the adapter does not turn into a token** | `check`: the snapshot lists every variable it does not cover, so a new one changes it | review it, then extend the adapter or accept it by writing the snapshot |
73
+ | Someone edited a token in Penpot by hand | `diff`: shown as a difference, with what it reaches | `push --apply` puts it back (Marwes is authoritative) |
74
+ | Someone added a token to a Marwes set by hand | `diff`: "not in the expectation" | kept unless you push with `--prune` |
75
+ | A set was deleted or a theme changed in Penpot | `diff` | `push --apply` |
76
+ | The recorded Marwes version (in every token's description) is old | `diff` | `push --apply` updates it, so the file says which Marwes it came from |
77
+
78
+ ## After a Marwes bump
79
+
80
+ ```bash
81
+ pnpm up @marwes-ui/react # the app moves to the new Marwes
82
+ penpair marwes tokens check # fails: the snapshot is stale, and says what changed
83
+ penpair marwes tokens snapshot --write # regenerate; review `git diff design/` (the tokens that moved)
84
+ penpair marwes tokens diff # what Penpot lacks, and how many shapes each change reaches
85
+ penpair marwes tokens push --apply # bring Penpot along; it reads everything back to verify
86
+ ```
87
+
88
+ `push` refuses to run while the snapshot is stale: the repository's record must lead, so a token never reaches Penpot that has not been through a commit. After the push, export your pages and let [Reflection](https://github.com/niklas-westman/reflection) show you what the new tokens changed visually.
89
+
90
+ ## Keeping it from drifting in the first place
91
+
92
+ - **CI.** Add the check to the same pipeline that runs your tests: `penpair marwes tokens check`. It needs no Penpot and no key, so it runs anywhere. A pull request that bumps Marwes (by hand or by a dependency bot) or edits the theme without a new snapshot cannot merge.
93
+ - **A test, if you prefer.** The adapter exports the same check for your test runner:
94
+
95
+ ```ts
96
+ import { checkTokenSnapshot } from "@penpair/marwes-ui";
97
+
98
+ it("the Penpot token snapshot is current", async () => {
99
+ const check = await checkTokenSnapshot(process.cwd());
100
+ expect(check.problems).toEqual([]);
101
+ expect(check.current).toBe(true);
102
+ });
103
+ ```
104
+
105
+ - **Before designing.** `penpair marwes tokens status` takes a second and says whether the design file is on the Marwes the app uses.
106
+
107
+ ## What `push` does and does not touch
108
+
109
+ - It manages only the sets the snapshot names (`Marwes · …` and your `Acme · Light/Dark`). Any other set in the file is yours and is never touched.
110
+ - It creates new sets inactive and builds the Light and Dark themes. If the file has **no active theme**, it activates Light so the tokens take part; a file that already has an active theme keeps its choice.
111
+ - Plain values are updated in place. Typography tokens are removed and added again, because Penpot stores a typography value assigned in place un-normalised.
112
+ - Tokens go in batches that stay under Penpot's request limit, and it stops at the first token Penpot rejects and says which.
113
+ - Afterwards it reads everything back: the file must match the snapshot, and **no shape may have moved or been rebound** (a fingerprint of every page's structure is compared before and after). Otherwise it says so and exits 1.
114
+
115
+ ## What is covered, and what is not
116
+
117
+ Everything Marwes resolves for a theme becomes a token: colours, density sizes, the two radii, the three font families and the typography styles, plus the spacing scale. A variable that is not (today: `--mw-density-spacing-multiplier`, a multiplier) is listed in the snapshot's `unmapped` field and in `snapshot` output, so the gap is visible and a change to it is noticed.
118
+
119
+ Not covered yet: Marwes **components** in Penpot. Tokens come first; components are built as pages need them.
package/docs/releasing.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Releasing
2
2
 
3
- All packages in this repository are released **together, at one version, even a package that did not change**: `@penpair/core`, `@penpair/web`, `@penpair/docker`, `penpair`, and anything added under `packages/` later. The root of the repository is a private workspace and is never published. The rule is enforced, not remembered:
3
+ All packages in this repository are released **together, at one version, even a package that did not change**: `@penpair/core`, `@penpair/web`, `@penpair/docker`, `@penpair/marwes-ui`, `penpair`, and anything added under `packages/` later. The root of the repository is a private workspace and is never published. The rule is enforced, not remembered:
4
4
 
5
5
  | Guard | What it stops |
6
6
  | --- | --- |
@@ -63,13 +63,14 @@ The release is then reported as `INCOMPLETE` (exit code 3) on purpose, and `pnpm
63
63
  npm view @penpair/core version
64
64
  npm view @penpair/web version
65
65
  npm view @penpair/docker version
66
+ npm view @penpair/marwes-ui version
66
67
  ```
67
68
 
68
69
  Then, in an empty folder, run `npx @penpair/web --version`, `npx @penpair/web setup --help` and `npx @penpair/docker --version` (and, once the short name is published, the same with `npx penpair`). Tag the release if you want one: `git tag v<version> && git push origin v<version>`.
69
70
 
70
71
  ## What each package ships
71
72
 
72
- The core ships `dist`, `skills`, `LICENSE` and a short `README.md`. The web package ships `dist`, `docs`, `LICENSE` and the repository's `README.md` (copied in by `prepack`, git-ignored there). The Docker package ships `dist`, `LICENSE` and `README.md`. The short name ships `bin`, `LICENSE` and `README.md`. Check with `npm pack --dry-run --ignore-scripts` in a package.
73
+ The core ships `dist`, `skills`, `LICENSE` and a short `README.md`. The web package ships `dist`, `docs`, `LICENSE` and the repository's `README.md` (copied in by `prepack`, git-ignored there). The Docker package and the Marwes adapter ship `dist`, `LICENSE` and `README.md`. The short name ships `bin`, `LICENSE` and `README.md`. Check with `npm pack --dry-run --ignore-scripts` in a package.
73
74
 
74
75
  ## After publishing
75
76
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@penpair/web",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "The penpair command for Penpot on the web (design.penpot.app or your team's own): guided setup, a safe write protocol, skills for AI agents. Includes everything in @penpair/core.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -20,8 +20,8 @@
20
20
  "types": "./dist/index.d.ts",
21
21
  "exports": {
22
22
  ".": {
23
- "import": "./dist/index.js",
24
- "types": "./dist/index.d.ts"
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
25
  },
26
26
  "./cli": "./dist/cli.js",
27
27
  "./package.json": "./package.json"
@@ -47,7 +47,7 @@
47
47
  "access": "public"
48
48
  },
49
49
  "dependencies": {
50
- "@penpair/core": "0.1.1"
50
+ "@penpair/core": "0.1.2"
51
51
  },
52
52
  "scripts": {
53
53
  "build": "tsc -p tsconfig.json",