@penpair/core 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/approvals.js CHANGED
@@ -2,6 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import { mkdir, open, readFile, stat, unlink } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { atomicWriteFile } from "./atomic-output.js";
5
+ import { matchesDesignViewport } from "./design-viewport.js";
5
6
  import { observationHash } from "./journal.js";
6
7
  import { resolveProjectInput, resolveProjectOutput } from "./project-input.js";
7
8
  import { validateBaselineCase } from "./baselines-config.js";
@@ -100,7 +101,8 @@ export async function recordApproval(root, exported, words, intent, redact) {
100
101
  words = {
101
102
  ...words,
102
103
  };
103
- if (!exported.stable || !pngSize(exported.png)) {
104
+ const imageSize = pngSize(exported.png);
105
+ if (!exported.stable || !imageSize) {
104
106
  throw new Error("Approval requires a stable independently exported PNG");
105
107
  }
106
108
  if (typeof words.note !== "string" ||
@@ -114,9 +116,9 @@ export async function recordApproval(root, exported, words, intent, redact) {
114
116
  const sha256 = artifactSha(exported.png);
115
117
  const fingerprint = approvalFingerprint(exported.capture, sha256, intent);
116
118
  if (intent &&
117
- (!exported.capture.designSize ||
118
- exported.capture.designSize.width !== intent.viewport.width ||
119
- exported.capture.designSize.height !== intent.viewport.height ||
119
+ (!matchesDesignViewport(exported.capture.designSize, intent.viewport) ||
120
+ imageSize.width !== Math.round(intent.viewport.width * exported.capture.scale) ||
121
+ imageSize.height !== Math.round(intent.viewport.height * exported.capture.scale) ||
120
122
  (intent.activeThemeIds &&
121
123
  observationHash([
122
124
  ...(exported.capture.activeThemeIds ?? []),
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { createHash } from "node:crypto";
4
4
  import { resolveProjectOutput } from "./project-input.js";
5
5
  import { pngSize } from "./png.js";
6
+ import { matchesDesignViewport } from "./design-viewport.js";
6
7
  import { validateBaselines, } from "./baselines-config.js";
7
8
  import { observationHash } from "./journal.js";
8
9
  export const designCaseKey = (route, value) => observationHash({
@@ -75,8 +76,7 @@ export function baselineCoverage(config, results = []) {
75
76
  (!result.captured ||
76
77
  (evidence.fileId === result.captured.fileId &&
77
78
  evidence.pageId === result.captured.pageId)) &&
78
- evidence.designSize?.width === frame.case.viewport.width &&
79
- evidence.designSize?.height === frame.case.viewport.height &&
79
+ matchesDesignViewport(evidence.designSize, frame.case.viewport) &&
80
80
  result.imageWidth === Math.round(frame.case.viewport.width * evidence.scale) &&
81
81
  result.imageHeight === Math.round(frame.case.viewport.height * evidence.scale) &&
82
82
  (!frame.case.activeThemeIds ||
package/dist/baselines.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { matchingApproval } from "./approvals.js";
2
+ import { matchesDesignViewport } from "./design-viewport.js";
2
3
  import { pngSize } from "./png.js";
3
4
  export { pngSize } from "./png.js";
4
5
  import { createHash, randomUUID } from "node:crypto";
@@ -180,9 +181,7 @@ export async function exportBaselines(client, projectRoot, config, options = {})
180
181
  if (wanted.case) {
181
182
  const viewport = wanted.case.viewport;
182
183
  const size = pngSize(exported.png);
183
- if (!exported.capture.designSize ||
184
- exported.capture.designSize.width !== viewport.width ||
185
- exported.capture.designSize.height !== viewport.height) {
184
+ if (!matchesDesignViewport(exported.capture.designSize, viewport)) {
186
185
  caseProblems.push("Observed frame dimensions do not match the declared viewport");
187
186
  }
188
187
  if (size.width !== Math.round(viewport.width * exported.capture.scale) ||
@@ -0,0 +1,3 @@
1
+ import type { BaselineCase } from "./baselines-config.js";
2
+ /** Penpot transforms can introduce subpixel noise; exported image dimensions remain exact. */
3
+ export declare function matchesDesignViewport(designSize: BaselineCase["viewport"] | undefined, viewport: BaselineCase["viewport"]): boolean;
@@ -0,0 +1,9 @@
1
+ const designSizeTolerancePx = 0.01;
2
+ /** Penpot transforms can introduce subpixel noise; exported image dimensions remain exact. */
3
+ export function matchesDesignViewport(designSize, viewport) {
4
+ return Boolean(designSize &&
5
+ Number.isFinite(designSize.width) &&
6
+ Number.isFinite(designSize.height) &&
7
+ Math.abs(designSize.width - viewport.width) < designSizeTolerancePx &&
8
+ Math.abs(designSize.height - viewport.height) < designSizeTolerancePx);
9
+ }
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Kept in its own file so that the command line and the library entry can both read it without importing each other. Equals package.json (a test checks). */
2
- export declare const version = "0.3.5";
2
+ export declare const version = "0.3.6";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Kept in its own file so that the command line and the library entry can both read it without importing each other. Equals package.json (a test checks). */
2
- export const version = "0.3.5";
2
+ export const version = "0.3.6";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@penpair/core",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Design together. Build together. The library and engine behind penpair: connect AI agents and people to a Penpot design file, safely. Install @penpair/web or @penpair/docker for the command.",
5
5
  "license": "MIT",
6
6
  "repository": {