@penpair/web 0.3.5 → 0.3.7
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 +1 -1
- package/docs/adapters.md +1 -1
- package/docs/coverage.md +1 -1
- package/docs/guide.md +1 -1
- package/docs/releasing.md +58 -28
- package/examples/reflection-browser/README.md +18 -1
- package/examples/reflection-browser/capture.mjs +23 -5
- package/examples/reflection-browser/provider.mjs +35 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/niklas-westman/penpair/main/docs/assets/
|
|
2
|
+
<img src="https://raw.githubusercontent.com/niklas-westman/penpair/main/docs/assets/banner.png" alt="Penpair: Design together. Build together. A shared workflow from design and contracts to build, verification and human review." width="100%">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
# penpair
|
package/docs/adapters.md
CHANGED
|
@@ -87,4 +87,4 @@ The design-only workflow can start with one command package. Frontend projects s
|
|
|
87
87
|
|
|
88
88
|
The shipped setup/handoff skills should recommend the appropriate companion, preserve existing adapter configuration and report readiness at each stage. They must keep missing references or provider work visible and never invent owner approval.
|
|
89
89
|
|
|
90
|
-
Use the shipped [real-browser provider starter](../examples/reflection-browser/README.md) to supply capture plumbing while keeping route/theme/state/fixture hooks project-owned. It includes measured font, pointer, scroll and crop evidence; a browser dependency is not added to the core.
|
|
90
|
+
Use the shipped [real-browser provider starter](../examples/reflection-browser/README.md) to supply capture plumbing while keeping route/theme/state/fixture hooks project-owned. It includes measured font, pointer, scroll and crop evidence; a browser dependency is not added to the core. The starter saves static lossless WebP with a project-supplied encoder; the adapter also reads legacy PNG evidence without converting approved references.
|
package/docs/coverage.md
CHANGED
|
@@ -20,7 +20,7 @@ A baseline frame may declare an explicit viewport, theme label, UI state, fixtur
|
|
|
20
20
|
|
|
21
21
|
`theme`, `state` and `fixture` are declared design intent. A label alone does not prove actual theme activation, application state or data loading. Optional `activeThemeIds` asserts the complete exact active native theme-ID set at capture; omit it for a manually styled frame and retain that uncertainty. Baseline capture remains read-only toward the Penpot document: it never switches themes. Use the separate [mode trial](mode-verification.md) for temporary theme switching.
|
|
22
22
|
|
|
23
|
-
Frame geometry and PNG dimensions must
|
|
23
|
+
Frame geometry must match the declared CSS viewport within less than 0.01 CSS px per axis, allowing floating-point noise from Penpot transforms and auto sizing. Baseline capture, case approval and offline coverage use the same tolerance. Exported PNG dimensions must still exactly equal the declared viewport multiplied by the observed export scale and rounded to whole pixels; the geometry tolerance never applies to PNG dimensions. A failed viewport/theme assertion marks `case-mismatch`, exits incomplete and preserves the previous generation. New provenance retains raw, unrounded design dimensions, active native theme IDs where available, API/tool version, renderer description and capture time. Font names are declarations; loaded font versions remain unobserved.
|
|
24
24
|
|
|
25
25
|
Case metadata is part of approval identity. Changing a fixture/state/theme/viewport declaration requires `--apply --update` even if the PNG bytes happen to match. Moving a matching image to a different file/page/frame identity also requires review. Recapture preserves the original approval until that explicit update. Metadata is pinned before asynchronous capture.
|
|
26
26
|
|
package/docs/guide.md
CHANGED
|
@@ -169,6 +169,6 @@ Use `inspect <shape-id> --page <name> --all` for bounded structured evidence wit
|
|
|
169
169
|
|
|
170
170
|
## State, theme and responsive coverage
|
|
171
171
|
|
|
172
|
-
Add explicit `frames[].case` metadata and a required matrix under `baselines.coverage`. `baselines coverage --json` validates approved images offline and exposes missing/unsupported slots. Rich cases remain pending browser setup mappings; see [coverage semantics and approval](coverage.md).
|
|
172
|
+
Add explicit `frames[].case` metadata and a required matrix under `baselines.coverage`. Case geometry allows less than 0.01 CSS px of Penpot floating-point noise per axis; exported PNG dimensions remain exact at the capture scale, and raw geometry stays in provenance. `baselines coverage --json` validates approved images offline and exposes missing/unsupported slots. Rich cases remain pending browser setup mappings; see [coverage semantics and approval](coverage.md).
|
|
173
173
|
|
|
174
174
|
For project-specific agent tools, use the [explicit-root stdio server and client examples](mcp.md). It exposes typed guarded services and defaults to omitting design-write tools.
|
package/docs/releasing.md
CHANGED
|
@@ -8,7 +8,7 @@ All packages in this repository are released **together, at one version, even a
|
|
|
8
8
|
| `pnpm version-packages` | One Changesets fixed group versions every package, synchronizes `version.ts` and refreshes the lockfile |
|
|
9
9
|
| `pnpm check:versions` | Missing, duplicate, ignored or separately grouped release packages, including packages added later |
|
|
10
10
|
| `prepublishOnly` in every package | `pnpm publish` inside one package: it refuses unless `pnpm release` started it, and checks the versions again |
|
|
11
|
-
| `pnpm release` | A half release: it checks,
|
|
11
|
+
| `pnpm release` | A half release: it checks, uploads the whole set with at most three workers, verifies registry visibility concurrently, and reports anything missing |
|
|
12
12
|
|
|
13
13
|
Publishing cannot be undone: a version, once published, cannot be replaced. Check first, publish once.
|
|
14
14
|
|
|
@@ -34,10 +34,11 @@ Publishing cannot be undone: a version, once published, cannot be replaced. Chec
|
|
|
34
34
|
publishing. The protected `npm-release` environment may require an additional
|
|
35
35
|
owner approval, depending on its configured protection.
|
|
36
36
|
5. The existing `pnpm release` entrypoint reruns checks and all package smoke
|
|
37
|
-
installs,
|
|
38
|
-
provenance,
|
|
39
|
-
the
|
|
40
|
-
|
|
37
|
+
installs, uploads packed tarballs with at most three workers using npm OIDC and
|
|
38
|
+
provenance, then verifies every required registry version concurrently (all six by default).
|
|
39
|
+
It checks out the exact reviewed release SHA and verifies that it belongs to main's
|
|
40
|
+
history. Feature PRs may merge while that immutable snapshot publishes; newer
|
|
41
|
+
feature commits and their pending Changesets cannot alter the release artifacts.
|
|
41
42
|
|
|
42
43
|
Automation is opt-in through the activation switches below. Merging a feature PR
|
|
43
44
|
creates or updates the version PR; publishing starts only after that reviewed
|
|
@@ -134,20 +135,36 @@ pnpm release # dry run; publishes nothing
|
|
|
134
135
|
pnpm release --publish --confirm-version X.Y.Z
|
|
135
136
|
```
|
|
136
137
|
|
|
138
|
+
Local OTP publication uploads one package at a time to keep interactive prompts
|
|
139
|
+
separate, then uses the same concurrent family visibility barrier.
|
|
140
|
+
|
|
137
141
|
The manual GitHub **Publish reviewed Penpair release** dispatch also remains:
|
|
138
|
-
enter the exact version
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
enter the exact reviewed version. Optionally enter its full 40-character `release_sha`
|
|
143
|
+
when main has advanced; omitting it selects the dispatch commit. Both automatic
|
|
144
|
+
and manual releases require the selected SHA to match a merged, same-repository
|
|
145
|
+
release PR, contain the increased shared version, and have no pending Changesets
|
|
146
|
+
in that snapshot. Set `continue_release` only after inspecting an interrupted
|
|
147
|
+
release. The dispatch still passes through the protected environment and release guards.
|
|
148
|
+
Historical recovery requires the selected commit's `scripts/release-protocol.json`
|
|
149
|
+
to declare the controller's supported protocol (currently version 1). Missing or
|
|
150
|
+
unsupported protocols are rejected before checkout or publication approval; releases
|
|
151
|
+
predating this tooling must use their original recovery process. Incompatible future
|
|
152
|
+
publisher safeguards must increment both the marker and the supported protocol so
|
|
153
|
+
older publisher scripts cannot bypass them.
|
|
141
154
|
|
|
142
155
|
## If a release stops half way
|
|
143
156
|
|
|
144
157
|
After a successful upload, npm may still be processing the version. The release
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
158
|
+
does not block the remaining uploads on that package's registry visibility.
|
|
159
|
+
Up to three uploads run together. After all started uploads settle, every accepted
|
|
160
|
+
upload and already published required package is checked concurrently under one
|
|
161
|
+
shared ten-minute visibility deadline, with immediate checks followed by minute
|
|
162
|
+
polls. Each metadata query has a 30-second maximum (or the remaining deadline,
|
|
148
163
|
whichever is smaller), with npm's internal retries disabled. Query time counts
|
|
149
|
-
toward the deadline.
|
|
150
|
-
|
|
164
|
+
toward the deadline. Registry errors remain unknown failures rather than evidence
|
|
165
|
+
of absence. Visibility checks never republish. A failed upload stops new scheduling;
|
|
166
|
+
already started uploads finish and their results are retained before the command exits.
|
|
167
|
+
A partial release still requires explicit recovery.
|
|
151
168
|
|
|
152
169
|
A publish can fail after some packages are out (a network error, a wrong one-time code, a name npm refuses). The version is then on npm for some packages and not for others, which must not stay that way. Fix the cause and run:
|
|
153
170
|
|
|
@@ -158,18 +175,26 @@ pnpm release --publish --continue # publishes only what is missing at this ve
|
|
|
158
175
|
Without `--continue`, `pnpm release` refuses to run while only some packages are at the version, so a partial release cannot be forgotten.
|
|
159
176
|
|
|
160
177
|
For GitHub recovery, run **Publish reviewed Penpair release** on `main`, enter
|
|
161
|
-
the exact interrupted version
|
|
162
|
-
`npm-release` when requested. This checks
|
|
163
|
-
already present and publishes only missing
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
178
|
+
the exact interrupted version, its full `release_sha` when main has advanced, and
|
|
179
|
+
set `continue_release=true`. Approve `npm-release` when requested. This checks
|
|
180
|
+
the registry again, skips versions already present and publishes only missing
|
|
181
|
+
required packages from that reviewed snapshot. If an accepted upload is still
|
|
182
|
+
propagating, wait and independently inspect the registry before requesting recovery.
|
|
183
|
+
A newer published `latest` version on any required package refuses the older
|
|
184
|
+
release rather than moving that tag backward. Tags are checked both during
|
|
185
|
+
preflight and immediately before uploads. Inspect final package/provenance
|
|
186
|
+
verification before counting the release complete.
|
|
187
|
+
|
|
188
|
+
Feature merges may continue during publication or recovery: pending Changesets
|
|
189
|
+
on newer main commits do not change the reviewed release snapshot. Keep release
|
|
190
|
+
versions in sequence and inspect a failed family before approving a newer release.
|
|
191
|
+
Only actual publish jobs enter the `penpair-package-release` concurrency group;
|
|
192
|
+
`queue: max` retains up to 100 pending jobs rather than replacing the previous one.
|
|
193
|
+
Queue arrival order can differ from version order, so the latest-version rollback
|
|
194
|
+
check remains required. See [GitHub concurrency documentation](https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency).
|
|
195
|
+
Do not discard pending package-release notes or weaken guards to force recovery through.
|
|
196
|
+
|
|
197
|
+
For legacy releases predating snapshot-aware recovery, an empty, tooling-only Changeset contains no package release entries. Changesets
|
|
173
198
|
can consume that metadata without changing versions. If such a note blocks an
|
|
174
199
|
interrupted release, handle it in a reviewed metadata-only cleanup, preserve its
|
|
175
200
|
summary in the completion report and verify unchanged package/source versions
|
|
@@ -185,7 +210,7 @@ the release's own evidence; never relax the existing release guards.
|
|
|
185
210
|
pnpm release --publish --except penpair
|
|
186
211
|
```
|
|
187
212
|
|
|
188
|
-
The release is then reported as `INCOMPLETE` (exit code 3) on purpose
|
|
213
|
+
The release is then reported as `INCOMPLETE` (exit code 3) on purpose. Recovery at that version requires explicit continuation and refuses if a newer required package version has already taken `latest`. Nothing else can be left out, and only by naming it.
|
|
189
214
|
|
|
190
215
|
## Verify
|
|
191
216
|
|
|
@@ -217,10 +242,15 @@ See [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) and
|
|
|
217
242
|
[provenance requirements](https://docs.npmjs.com/generating-provenance-statements/).
|
|
218
243
|
Publishing validates the workflow context and rejects long-lived npm token
|
|
219
244
|
fallback, packs with pnpm to rewrite `workspace:*` to exact versions, and publishes
|
|
220
|
-
single tarballs with npm and provenance.
|
|
245
|
+
single tarballs with npm and provenance. For SHA-selected recovery, only the npm
|
|
246
|
+
publish subprocess receives `GITHUB_SHA` set to the verified source SHA, so npm's
|
|
247
|
+
provenance source digest identifies the artifact checkout. Workflow identity/ref,
|
|
248
|
+
OIDC issuance and run invocation retain the actual dispatch context. These local checks are operational guards;
|
|
221
249
|
npm authenticates the actual identity against owner-managed publisher settings.
|
|
222
250
|
|
|
223
|
-
npm cannot atomically publish multiple packages.
|
|
251
|
+
npm cannot atomically publish multiple packages. With concurrent uploads a dependent
|
|
252
|
+
package may briefly appear before its exact dependency has propagated. Consumers
|
|
253
|
+
should use a version after final family verification succeeds. A network/permission failure can
|
|
224
254
|
leave some published and others missing; the release reports failure and requires
|
|
225
255
|
explicit continuation at the same version. A rerun never silently resumes.
|
|
226
256
|
|
|
@@ -8,12 +8,13 @@ In your trusted project provider:
|
|
|
8
8
|
|
|
9
9
|
```js
|
|
10
10
|
import { chromium } from 'playwright'; // the adopter's pinned version
|
|
11
|
+
import sharp from 'sharp'; // declare the adopter's reviewed, pinned codec
|
|
11
12
|
import { createBrowserProvider } from './provider.mjs';
|
|
12
13
|
import { hooks } from './project-hooks.mjs';
|
|
13
14
|
export async function runCase(test) {
|
|
14
15
|
const browser = await chromium.launch();
|
|
15
16
|
try {
|
|
16
|
-
return await createBrowserProvider({ browser, baseURL: 'http://127.0.0.1:5173', projectRoot: process.cwd(), hooks }).runCase(test);
|
|
17
|
+
return await createBrowserProvider({ browser, baseURL: 'http://127.0.0.1:5173', projectRoot: process.cwd(), hooks, encodeLosslessWebp: (bytes) => sharp(bytes).webp({ lossless: true, exact: true, effort: 6 }).toBuffer() }).runCase(test);
|
|
17
18
|
} finally { await browser.close(); }
|
|
18
19
|
}
|
|
19
20
|
```
|
|
@@ -47,3 +48,19 @@ From this repository, run `pnpm smoke:browser` using a locally installed pinned
|
|
|
47
48
|
The script starts/stops a loopback fixture, captures desktop/mobile × light/dark × loaded/empty, tests wrong theme, missing face/weight, delayed font loading, scale mismatch, hover and sticky occlusion, and verifies unchanged baseline bytes. Its browser references are deliberately **unapproved synthetic fixtures**. They exercise plumbing; they are not Penpot acceptance or owner-approved design baselines.
|
|
48
49
|
|
|
49
50
|
The fixture's Abel Regular font is redistributed under [SIL OFL](fixture/OFL.txt), retrieved from [Google Fonts](https://github.com/google/fonts/tree/main/ofl/abel), SHA-256 `8809dcad25318225052f88333e208c5aad4adcb7b2c934c135735ec19aa410b4`. Browser APIs follow [Playwright screenshot documentation](https://playwright.dev/docs/api/class-page#page-screenshot) and [font-loading evidence](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet).
|
|
51
|
+
|
|
52
|
+
The starter captures a temporary PNG through Playwright and writes static lossless
|
|
53
|
+
WebP evidence by default. `encodeLosslessWebp` is an explicit project-owned codec
|
|
54
|
+
hook; declare Sharp directly in the adopting project when using the example above.
|
|
55
|
+
`lossless: true, exact: true` preserves image pixels, including RGB under transparent
|
|
56
|
+
pixels; `quality: 100` alone does not select lossless WebP. The provider compares
|
|
57
|
+
the encoded image with the capture before saving and refuses changed decoded pixels.
|
|
58
|
+
See [Sharp WebP output options](https://sharp.pixelplumbing.com/api-output#webp).
|
|
59
|
+
|
|
60
|
+
Comparison reads both PNG and WebP references by content. Existing approved PNG
|
|
61
|
+
references keep their original hashes. `imageFormat: "png"` explicitly selects the
|
|
62
|
+
legacy output path without requiring an encoder. Browser capture metadata, exact
|
|
63
|
+
scale/viewport checks, independent evidence paths and owner review remain unchanged.
|
|
64
|
+
The adapter rejects lossy/animated WebP and validates container metadata without
|
|
65
|
+
adding a native decoder dependency. The repository's pinned Sharp dev dependency
|
|
66
|
+
exists only for smoke validation; it is not shipped as an adapter runtime dependency.
|
|
@@ -264,12 +264,12 @@ export async function captureCase(page, test, hooks, { timeoutMs = 10000 } = {})
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
/** Exact decoded RGBA pixel difference, without resizing or threshold-based approval. */
|
|
267
|
-
export async function
|
|
267
|
+
export async function compareImages(page, baseline, screenshot) {
|
|
268
268
|
return page.evaluate(
|
|
269
269
|
async (images) => {
|
|
270
|
-
const decode = async (base64) => {
|
|
270
|
+
const decode = async ({ mime, base64 }) => {
|
|
271
271
|
const image = new Image();
|
|
272
|
-
image.src = "data:
|
|
272
|
+
image.src = "data:" + mime + ";base64," + base64;
|
|
273
273
|
await image.decode();
|
|
274
274
|
return image;
|
|
275
275
|
};
|
|
@@ -304,8 +304,26 @@ export async function comparePng(page, baseline, screenshot) {
|
|
|
304
304
|
return changed / (canvas.width * canvas.height);
|
|
305
305
|
},
|
|
306
306
|
[
|
|
307
|
-
baseline
|
|
308
|
-
screenshot
|
|
307
|
+
imageData(baseline),
|
|
308
|
+
imageData(screenshot),
|
|
309
309
|
],
|
|
310
310
|
);
|
|
311
311
|
}
|
|
312
|
+
|
|
313
|
+
function imageData(bytes) {
|
|
314
|
+
const png = bytes.length >= 8 && bytes.subarray(0, 8).toString("hex") === "89504e470d0a1a0a";
|
|
315
|
+
const webp =
|
|
316
|
+
bytes.length >= 12 &&
|
|
317
|
+
bytes.toString("ascii", 0, 4) === "RIFF" &&
|
|
318
|
+
bytes.toString("ascii", 8, 12) === "WEBP";
|
|
319
|
+
if (!png && !webp) {
|
|
320
|
+
throw new Error("Comparison requires PNG or WebP bytes");
|
|
321
|
+
}
|
|
322
|
+
return {
|
|
323
|
+
mime: png ? "image/png" : "image/webp",
|
|
324
|
+
base64: bytes.toString("base64"),
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** Existing PNG callers retain the same decoded-pixel comparison. */
|
|
329
|
+
export const comparePng = compareImages;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { mkdir, readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { captureCase,
|
|
4
|
+
import { captureCase, compareImages } from "./capture.mjs";
|
|
5
5
|
|
|
6
6
|
/** Pass a browser from the adopting project's pinned Playwright; this example installs nothing. */
|
|
7
7
|
export function createBrowserProvider({
|
|
@@ -12,10 +12,25 @@ export function createBrowserProvider({
|
|
|
12
12
|
deviceScaleFactor = 1,
|
|
13
13
|
timeoutMs = 10000,
|
|
14
14
|
io,
|
|
15
|
+
imageFormat = "webp",
|
|
16
|
+
encodeLosslessWebp,
|
|
15
17
|
}) {
|
|
16
18
|
if (!browser || !projectRoot || !baseURL || typeof hooks?.route !== "function") {
|
|
17
19
|
throw new Error("Browser, project root, app origin and explicit route hook required");
|
|
18
20
|
}
|
|
21
|
+
if (
|
|
22
|
+
![
|
|
23
|
+
"webp",
|
|
24
|
+
"png",
|
|
25
|
+
].includes(imageFormat)
|
|
26
|
+
) {
|
|
27
|
+
throw new Error("Evidence imageFormat must be webp or png");
|
|
28
|
+
}
|
|
29
|
+
if (imageFormat === "webp" && typeof encodeLosslessWebp !== "function") {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"Lossless WebP evidence requires encodeLosslessWebp from the project's pinned image codec",
|
|
32
|
+
);
|
|
33
|
+
}
|
|
19
34
|
return {
|
|
20
35
|
async runCase(test) {
|
|
21
36
|
const { atomicWriteFile, resolveProjectInput, resolveProjectOutput } =
|
|
@@ -48,12 +63,27 @@ export function createBrowserProvider({
|
|
|
48
63
|
if (page.url() !== target.href) {
|
|
49
64
|
throw new Error("Application navigated away from the intended route");
|
|
50
65
|
}
|
|
51
|
-
const diffPixelRatio = await
|
|
66
|
+
const diffPixelRatio = await compareImages(page, before, capture.screenshot);
|
|
67
|
+
const imageBytes =
|
|
68
|
+
imageFormat === "webp"
|
|
69
|
+
? Buffer.from(await encodeLosslessWebp(capture.screenshot))
|
|
70
|
+
: capture.screenshot;
|
|
71
|
+
if (imageFormat === "webp") {
|
|
72
|
+
if (
|
|
73
|
+
imageBytes.toString("ascii", 0, 4) !== "RIFF" ||
|
|
74
|
+
imageBytes.toString("ascii", 8, 12) !== "WEBP"
|
|
75
|
+
) {
|
|
76
|
+
throw new Error("Lossless WebP encoder returned a different image format");
|
|
77
|
+
}
|
|
78
|
+
if ((await compareImages(page, capture.screenshot, imageBytes)) !== 0) {
|
|
79
|
+
throw new Error("Lossless WebP encoding changed captured pixels");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
52
82
|
const screenshot = path.join(
|
|
53
83
|
".penpair/reflection",
|
|
54
84
|
createHash("sha256")
|
|
55
85
|
.update(test.id + ":" + test.setup)
|
|
56
|
-
.digest("hex") +
|
|
86
|
+
.digest("hex") + `.${imageFormat}`,
|
|
57
87
|
);
|
|
58
88
|
const output = await resolveProjectOutput(projectRoot, screenshot);
|
|
59
89
|
if (output === (await resolveProjectInput(projectRoot, test.baseline.path))) {
|
|
@@ -62,7 +92,7 @@ export function createBrowserProvider({
|
|
|
62
92
|
await mkdir(path.dirname(output), {
|
|
63
93
|
recursive: true,
|
|
64
94
|
});
|
|
65
|
-
await atomicWriteFile(output,
|
|
95
|
+
await atomicWriteFile(output, imageBytes, {
|
|
66
96
|
replace: true,
|
|
67
97
|
});
|
|
68
98
|
return {
|
|
@@ -79,7 +109,7 @@ export function createBrowserProvider({
|
|
|
79
109
|
capture: capture.capture,
|
|
80
110
|
provider: {
|
|
81
111
|
name: "penpair-browser-starter-rgba-exact",
|
|
82
|
-
version: "
|
|
112
|
+
version: "2",
|
|
83
113
|
},
|
|
84
114
|
};
|
|
85
115
|
} finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@penpair/web",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
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": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@penpair/core": "0.3.
|
|
51
|
+
"@penpair/core": "0.3.7"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"build": "tsc -p tsconfig.json",
|