@design-parity/adapter-stitch 0.1.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 ADDED
@@ -0,0 +1,59 @@
1
+ # @design-parity/adapter-stitch
2
+
3
+ The Google Stitch `ReferenceAdapter`. Stitch ships `@google/stitch-sdk` + an MCP
4
+ server but has **no Code Connect equivalent**, so — unlike Figma's machine link —
5
+ correspondence is resolved through the repo's `design-map.json`. The reference is
6
+ always produced with `linkMethod: "manifest"`.
7
+
8
+ ## What it does
9
+
10
+ `resolve(componentId, ref, ctx)`:
11
+
12
+ 1. **Resolve the handle.** A direct `stitch:<projectId>/<screenId>` ref is parsed
13
+ as-is. Otherwise the adapter loads `design-map.json` (`designMapPath`, or
14
+ `DESIGN_MAP_FILE`, else `<repoRoot>/design-map.json`) and `findByCode` maps the
15
+ component to its Stitch ref — the manifest is the only correspondence layer.
16
+ 2. **Fetch HTML+Tailwind** through the Stitch SDK (one or more screen variants).
17
+ 3. **Rasterize** each screen headlessly to a reference PNG; dimensions are read
18
+ straight from the PNG IHDR so they can't drift from the bytes the diff engine
19
+ compares against.
20
+ 4. **Extract Tailwind-derived tokens** — spacing/radius/background off the
21
+ container, colour + typography off the title and body — into `DesignTokens`.
22
+ 5. **Normalize** to a `DesignReference` with `linkMethod: "manifest"`.
23
+
24
+ ## Auth
25
+
26
+ The default SDK client reads a credential from `AdapterContext.env`, in order:
27
+ `STITCH_API_KEY`, `STITCH_TOKEN`, `STITCH_ACCESS_TOKEN`, `GOOGLE_STITCH_TOKEN`.
28
+
29
+ A missing credential raises `StitchAuthError`; a rejected one is mapped from the
30
+ SDK. A component with no matching `design-map.json` entry raises
31
+ `StitchManifestError`; an unparseable ref raises `StitchBadRefError`.
32
+
33
+ ## Usage
34
+
35
+ ```ts
36
+ import { createStitchAdapter } from "@design-parity/adapter-stitch";
37
+
38
+ const adapter = createStitchAdapter();
39
+
40
+ const reference = await adapter.resolve(
41
+ "ui/Card.kt#OfferCard",
42
+ "stitch:design/abc123", // or the code handle, resolved via design-map.json
43
+ { repoRoot: process.cwd(), env: process.env },
44
+ );
45
+ ```
46
+
47
+ ## Injectable seams
48
+
49
+ Both the SDK and the rasterizer are interfaces, so unit tests run with **no live
50
+ source and no browser** (see `test/`):
51
+
52
+ - `StitchClient.fetchDesign(ref)` → `StitchDesign` (`{ screens }`). The default
53
+ `createSdkStitchClient` lazily drives `@google/stitch-sdk`, kept out of the
54
+ package's hard dependencies so install stays clean; absence is a clear
55
+ `StitchSdkError`.
56
+ - `Rasterizer.rasterize({ html, css })` → PNG bytes. The default
57
+ `browserRasterizer` drives a headless Chrome/Chromium found on `PATH` (or
58
+ `CHROME_PATH`) — never a bundled browser-automation dependency, mirroring the
59
+ candidate renderer.
@@ -0,0 +1,22 @@
1
+ import { type AdapterContext, type DesignReference, type ReferenceAdapter } from "@design-parity/core";
2
+ import { type Rasterizer } from "./rasterizer.js";
3
+ import { type StitchClient } from "./stitch-client.js";
4
+ export interface StitchAdapterOptions {
5
+ /** Pre-built SDK client (tests inject a fake). Defaults to the SDK driver. */
6
+ client?: StitchClient;
7
+ /** HTML→PNG rasterizer (tests inject a fake). Defaults to headless Chrome. */
8
+ rasterizer?: Rasterizer;
9
+ /** Path to `design-map.json` when `ref` isn't a direct `stitch:` handle. */
10
+ designMapPath?: string;
11
+ /** Directory rasterized PNGs are written to. Defaults under the repo root. */
12
+ outDir?: string;
13
+ }
14
+ export declare class StitchAdapter implements ReferenceAdapter {
15
+ #private;
16
+ readonly source: "stitch";
17
+ constructor(opts?: StitchAdapterOptions);
18
+ resolve(componentId: string, ref: string, ctx: AdapterContext): Promise<DesignReference>;
19
+ }
20
+ /** Convenience factory. */
21
+ export declare function createStitchAdapter(opts?: StitchAdapterOptions): StitchAdapter;
22
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAUA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAQ5B,MAAM,WAAW,oBAAoB;IACnC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,qBAAa,aAAc,YAAW,gBAAgB;;IACpD,QAAQ,CAAC,MAAM,EAAG,QAAQ,CAAU;gBAGxB,IAAI,GAAE,oBAAyB;IAIrC,OAAO,CACX,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,eAAe,CAAC;CAmF5B;AAED,2BAA2B;AAC3B,wBAAgB,mBAAmB,CACjC,IAAI,GAAE,oBAAyB,GAC9B,aAAa,CAEf"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The Stitch {@link ReferenceAdapter}: resolve a component to a Stitch design
3
+ * via `design-map.json` (Stitch has no machine link), fetch HTML+Tailwind
4
+ * through the SDK, rasterize a reference image headlessly, extract
5
+ * Tailwind-derived tokens, and normalize to a {@link DesignReference}. The SDK
6
+ * and the rasterizer are both injectable, so unit tests run with no live source.
7
+ */
8
+ import { mkdir, writeFile } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import { findByCode, loadDesignMap, } from "@design-parity/core";
11
+ import { StitchManifestError } from "./errors.js";
12
+ import { normalizeReference } from "./normalize.js";
13
+ import { pngSize } from "./png.js";
14
+ import { browserRasterizer } from "./rasterizer.js";
15
+ import { createSdkStitchClient, } from "./stitch-client.js";
16
+ import { formatStitchRef, isStitchRef, parseStitchRef, } from "./stitch-ref.js";
17
+ function slug(s) {
18
+ return s.replace(/[^A-Za-z0-9._-]+/g, "_");
19
+ }
20
+ export class StitchAdapter {
21
+ source = "stitch";
22
+ #opts;
23
+ constructor(opts = {}) {
24
+ this.#opts = opts;
25
+ }
26
+ async resolve(componentId, ref, ctx) {
27
+ const stitchRef = await this.#resolveRef(componentId, ref, ctx);
28
+ const client = this.#opts.client ?? createSdkStitchClient(ctx.env);
29
+ const design = await client.fetchDesign(stitchRef);
30
+ const rasterizer = this.#opts.rasterizer ?? browserRasterizer(ctx.env);
31
+ const outDir = this.#opts.outDir ?? join(ctx.repoRoot, ".design-parity", "cache", "stitch");
32
+ await mkdir(outDir, { recursive: true });
33
+ const referenceImages = [];
34
+ for (const screen of design.screens) {
35
+ const rasterizeArgs = {
36
+ html: screen.html,
37
+ };
38
+ if (screen.css !== undefined)
39
+ rasterizeArgs.css = screen.css;
40
+ const bytes = await rasterizer.rasterize(rasterizeArgs);
41
+ const { width, height } = pngSize(bytes);
42
+ const file = join(outDir, `${slug(componentId)}--${screen.theme ?? screen.state ?? "default"}--${screen.size ?? "default"}.png`);
43
+ await writeFile(file, bytes);
44
+ const image = {
45
+ state: screen.state ?? "default",
46
+ uri: file,
47
+ width,
48
+ height,
49
+ };
50
+ if (screen.theme)
51
+ image.theme = screen.theme;
52
+ if (screen.size)
53
+ image.size = screen.size;
54
+ referenceImages.push(image);
55
+ }
56
+ return normalizeReference({
57
+ componentId,
58
+ ref: formatStitchRef(stitchRef),
59
+ design,
60
+ referenceImages,
61
+ });
62
+ }
63
+ /**
64
+ * Resolve the Stitch handle. A direct `stitch:` ref is parsed as-is;
65
+ * otherwise the `design-map.json` manifest is the only correspondence layer.
66
+ */
67
+ async #resolveRef(componentId, ref, ctx) {
68
+ if (isStitchRef(ref))
69
+ return parseStitchRef(ref);
70
+ const path = this.#opts.designMapPath ??
71
+ ctx.env.DESIGN_MAP_FILE ??
72
+ join(ctx.repoRoot, "design-map.json");
73
+ let map;
74
+ try {
75
+ map = await loadDesignMap(path);
76
+ }
77
+ catch (cause) {
78
+ throw new StitchManifestError(componentId, `cannot load design-map at '${path}'`, { cause });
79
+ }
80
+ const entry = findByCode(map, componentId);
81
+ if (!entry) {
82
+ throw new StitchManifestError(componentId, `no matching entry in '${path}'`);
83
+ }
84
+ if (entry.source !== "stitch") {
85
+ throw new StitchManifestError(componentId, `maps to source '${entry.source}', not 'stitch'`);
86
+ }
87
+ return parseStitchRef(entry.ref);
88
+ }
89
+ }
90
+ /** Convenience factory. */
91
+ export function createStitchAdapter(opts = {}) {
92
+ return new StitchAdapter(opts);
93
+ }
94
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /** Errors the Stitch adapter raises. All carry a stable `code` for callers. */
2
+ export type StitchErrorCode = "auth" | "manifest-miss" | "bad-ref" | "sdk" | "rasterize";
3
+ /** Base class so callers can `catch (e) { if (e instanceof StitchError) ... }`. */
4
+ export declare class StitchError extends Error {
5
+ readonly code: StitchErrorCode;
6
+ constructor(code: StitchErrorCode, message: string, options?: ErrorOptions);
7
+ }
8
+ /** No Stitch credential configured, or the SDK rejected the credential. */
9
+ export declare class StitchAuthError extends StitchError {
10
+ constructor(message: string, options?: ErrorOptions);
11
+ }
12
+ /**
13
+ * No `design-map.json` entry links this component to a Stitch design. Stitch has
14
+ * no machine link, so the manifest is the only correspondence layer.
15
+ */
16
+ export declare class StitchManifestError extends StitchError {
17
+ readonly componentId: string;
18
+ constructor(componentId: string, detail: string, options?: ErrorOptions);
19
+ }
20
+ /** The `ref` could not be parsed into a Stitch project/screen handle. */
21
+ export declare class StitchBadRefError extends StitchError {
22
+ constructor(ref: string, options?: ErrorOptions);
23
+ }
24
+ /** The SDK was unreachable, not installed, or returned an unusable payload. */
25
+ export declare class StitchSdkError extends StitchError {
26
+ constructor(message: string, options?: ErrorOptions);
27
+ }
28
+ /** Headless rasterization of the fetched HTML failed. */
29
+ export declare class StitchRasterizeError extends StitchError {
30
+ constructor(message: string, options?: ErrorOptions);
31
+ }
32
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAE/E,MAAM,MAAM,eAAe,GACvB,MAAM,GACN,eAAe,GACf,SAAS,GACT,KAAK,GACL,WAAW,CAAC;AAEhB,mFAAmF;AACnF,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;gBACnB,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAK3E;AAED,2EAA2E;AAC3E,qBAAa,eAAgB,SAAQ,WAAW;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAGpD;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,WAAW;IAClD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBACjB,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAQxE;AAED,yEAAyE;AACzE,qBAAa,iBAAkB,SAAQ,WAAW;gBACpC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAOhD;AAED,+EAA+E;AAC/E,qBAAa,cAAe,SAAQ,WAAW;gBACjC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAGpD;AAED,yDAAyD;AACzD,qBAAa,oBAAqB,SAAQ,WAAW;gBACvC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAGpD"}
package/dist/errors.js ADDED
@@ -0,0 +1,46 @@
1
+ /** Errors the Stitch adapter raises. All carry a stable `code` for callers. */
2
+ /** Base class so callers can `catch (e) { if (e instanceof StitchError) ... }`. */
3
+ export class StitchError extends Error {
4
+ code;
5
+ constructor(code, message, options) {
6
+ super(message, options);
7
+ this.name = new.target.name;
8
+ this.code = code;
9
+ }
10
+ }
11
+ /** No Stitch credential configured, or the SDK rejected the credential. */
12
+ export class StitchAuthError extends StitchError {
13
+ constructor(message, options) {
14
+ super("auth", message, options);
15
+ }
16
+ }
17
+ /**
18
+ * No `design-map.json` entry links this component to a Stitch design. Stitch has
19
+ * no machine link, so the manifest is the only correspondence layer.
20
+ */
21
+ export class StitchManifestError extends StitchError {
22
+ componentId;
23
+ constructor(componentId, detail, options) {
24
+ super("manifest-miss", `stitch: no design-map entry for '${componentId}' — ${detail}`, options);
25
+ this.componentId = componentId;
26
+ }
27
+ }
28
+ /** The `ref` could not be parsed into a Stitch project/screen handle. */
29
+ export class StitchBadRefError extends StitchError {
30
+ constructor(ref, options) {
31
+ super("bad-ref", `stitch: cannot parse ref '${ref}' — expected 'stitch:<projectId>/<screenId>'`, options);
32
+ }
33
+ }
34
+ /** The SDK was unreachable, not installed, or returned an unusable payload. */
35
+ export class StitchSdkError extends StitchError {
36
+ constructor(message, options) {
37
+ super("sdk", message, options);
38
+ }
39
+ }
40
+ /** Headless rasterization of the fetched HTML failed. */
41
+ export class StitchRasterizeError extends StitchError {
42
+ constructor(message, options) {
43
+ super("rasterize", message, options);
44
+ }
45
+ }
46
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAS/E,mFAAmF;AACnF,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,IAAI,CAAkB;IAC/B,YAAY,IAAqB,EAAE,OAAe,EAAE,OAAsB;QACxE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,2EAA2E;AAC3E,MAAM,OAAO,eAAgB,SAAQ,WAAW;IAC9C,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,mBAAoB,SAAQ,WAAW;IACzC,WAAW,CAAS;IAC7B,YAAY,WAAmB,EAAE,MAAc,EAAE,OAAsB;QACrE,KAAK,CACH,eAAe,EACf,oCAAoC,WAAW,OAAO,MAAM,EAAE,EAC9D,OAAO,CACR,CAAC;QACF,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;CACF;AAED,yEAAyE;AACzE,MAAM,OAAO,iBAAkB,SAAQ,WAAW;IAChD,YAAY,GAAW,EAAE,OAAsB;QAC7C,KAAK,CACH,SAAS,EACT,6BAA6B,GAAG,8CAA8C,EAC9E,OAAO,CACR,CAAC;IACJ,CAAC;CACF;AAED,+EAA+E;AAC/E,MAAM,OAAO,cAAe,SAAQ,WAAW;IAC7C,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;CACF;AAED,yDAAyD;AACzD,MAAM,OAAO,oBAAqB,SAAQ,WAAW;IACnD,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;CACF"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `@design-parity/adapter-stitch` — the Google Stitch `ReferenceAdapter`.
3
+ *
4
+ * Stitch ships `@google/stitch-sdk` + an MCP server but has **no Code Connect
5
+ * equivalent**, so correspondence is resolved through the repo's
6
+ * `design-map.json` (`linkMethod: "manifest"`). The adapter fetches HTML+Tailwind
7
+ * via the SDK, rasterizes a reference image headlessly, and extracts
8
+ * Tailwind-derived tokens. The SDK client and the rasterizer are injectable.
9
+ */
10
+ export { StitchAdapter, createStitchAdapter } from "./adapter.js";
11
+ export type { StitchAdapterOptions } from "./adapter.js";
12
+ export { createSdkStitchClient, type StitchClient, type StitchDesign, type StitchScreen, } from "./stitch-client.js";
13
+ export { browserRasterizer, type Rasterizer, type RasterizeInput, } from "./rasterizer.js";
14
+ export { tokensFromHtml } from "./tailwind-tokens.js";
15
+ export { parseStitchRef, formatStitchRef, isStitchRef, type StitchRef, } from "./stitch-ref.js";
16
+ export { StitchError, StitchAuthError, StitchManifestError, StitchBadRefError, StitchSdkError, StitchRasterizeError, type StitchErrorCode, } from "./errors.js";
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAClE,YAAY,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EACL,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EACL,cAAc,EACd,eAAe,EACf,WAAW,EACX,KAAK,SAAS,GACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,oBAAoB,EACpB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `@design-parity/adapter-stitch` — the Google Stitch `ReferenceAdapter`.
3
+ *
4
+ * Stitch ships `@google/stitch-sdk` + an MCP server but has **no Code Connect
5
+ * equivalent**, so correspondence is resolved through the repo's
6
+ * `design-map.json` (`linkMethod: "manifest"`). The adapter fetches HTML+Tailwind
7
+ * via the SDK, rasterizes a reference image headlessly, and extracts
8
+ * Tailwind-derived tokens. The SDK client and the rasterizer are injectable.
9
+ */
10
+ export { StitchAdapter, createStitchAdapter } from "./adapter.js";
11
+ export { createSdkStitchClient, } from "./stitch-client.js";
12
+ export { browserRasterizer, } from "./rasterizer.js";
13
+ export { tokensFromHtml } from "./tailwind-tokens.js";
14
+ export { parseStitchRef, formatStitchRef, isStitchRef, } from "./stitch-ref.js";
15
+ export { StitchError, StitchAuthError, StitchManifestError, StitchBadRefError, StitchSdkError, StitchRasterizeError, } from "./errors.js";
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGlE,OAAO,EACL,qBAAqB,GAItB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,GAGlB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EACL,cAAc,EACd,eAAe,EACf,WAAW,GAEZ,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,oBAAoB,GAErB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Build a {@link DesignReference} from a fetched Stitch design plus the images
3
+ * the adapter rasterized. Tokens come from the primary screen's Tailwind
4
+ * classes. No I/O — the adapter does the fetching and rasterizing.
5
+ */
6
+ import type { DesignReference, Image } from "@design-parity/core";
7
+ import type { StitchDesign } from "./stitch-client.js";
8
+ export interface NormalizeInput {
9
+ componentId: string;
10
+ ref: string;
11
+ design: StitchDesign;
12
+ referenceImages: Image[];
13
+ }
14
+ /** Normalize to a `DesignReference` with `linkMethod: "manifest"`. */
15
+ export declare function normalizeReference(input: NormalizeInput): DesignReference;
16
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,YAAY,CAAC;IACrB,eAAe,EAAE,KAAK,EAAE,CAAC;CAC1B;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAazE"}
@@ -0,0 +1,17 @@
1
+ import { tokensFromHtml } from "./tailwind-tokens.js";
2
+ /** Normalize to a `DesignReference` with `linkMethod: "manifest"`. */
3
+ export function normalizeReference(input) {
4
+ const primary = input.design.screens[0];
5
+ const tokens = primary ? tokensFromHtml(primary.html) : undefined;
6
+ const ref = {
7
+ componentId: input.componentId,
8
+ source: "stitch",
9
+ linkMethod: "manifest",
10
+ ref: input.ref,
11
+ referenceImages: input.referenceImages,
12
+ };
13
+ if (tokens)
14
+ ref.tokens = tokens;
15
+ return ref;
16
+ }
17
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAStD,sEAAsE;AACtE,MAAM,UAAU,kBAAkB,CAAC,KAAqB;IACtD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAElE,MAAM,GAAG,GAAoB;QAC3B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,MAAM,EAAE,QAAQ;QAChB,UAAU,EAAE,UAAU;QACtB,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,eAAe,EAAE,KAAK,CAAC,eAAe;KACvC,CAAC;IACF,IAAI,MAAM;QAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;IAChC,OAAO,GAAG,CAAC;AACb,CAAC"}
package/dist/png.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /** Read width/height from a PNG's IHDR chunk without pulling in an image lib. */
2
+ export declare function pngSize(bytes: Uint8Array): {
3
+ width: number;
4
+ height: number;
5
+ };
6
+ //# sourceMappingURL=png.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"png.d.ts","sourceRoot":"","sources":["../src/png.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,wBAAgB,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAS5E"}
package/dist/png.js ADDED
@@ -0,0 +1,12 @@
1
+ /** Read width/height from a PNG's IHDR chunk without pulling in an image lib. */
2
+ export function pngSize(bytes) {
3
+ const sig = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
4
+ if (bytes.length < 24 || sig.some((b, i) => bytes[i] !== b)) {
5
+ throw new Error("stitch: rasterized image is not a PNG");
6
+ }
7
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
8
+ // IHDR is the first chunk: 8-byte sig, 4-byte length, 4-byte type, then
9
+ // width (4) and height (4) big-endian at offsets 16 and 20.
10
+ return { width: view.getUint32(16), height: view.getUint32(20) };
11
+ }
12
+ //# sourceMappingURL=png.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"png.js","sourceRoot":"","sources":["../src/png.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,UAAU,OAAO,CAAC,KAAiB;IACvC,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7D,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5E,wEAAwE;IACxE,4DAA4D;IAC5D,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;AACnE,CAAC"}
@@ -0,0 +1,20 @@
1
+ /** What the adapter hands the rasterizer for one screen. */
2
+ export interface RasterizeInput {
3
+ /** Stitch's generated markup. */
4
+ html: string;
5
+ /** Optional stylesheet to inline alongside the markup. */
6
+ css?: string;
7
+ /** Viewport hints; the default browser uses them for the window size. */
8
+ width?: number;
9
+ height?: number;
10
+ }
11
+ /** Turns one screen's HTML into PNG bytes. */
12
+ export interface Rasterizer {
13
+ rasterize(input: RasterizeInput): Promise<Uint8Array>;
14
+ }
15
+ /**
16
+ * Drive a headless Chrome/Chromium found on `PATH` (or `CHROME_PATH`) to
17
+ * screenshot the HTML. The browser is never bundled — absence is a clear error.
18
+ */
19
+ export declare function browserRasterizer(env?: Record<string, string | undefined>): Rasterizer;
20
+ //# sourceMappingURL=rasterizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rasterizer.d.ts","sourceRoot":"","sources":["../src/rasterizer.ts"],"names":[],"mappings":"AAaA,4DAA4D;AAC5D,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,8CAA8C;AAC9C,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACvD;AAiBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,UAAU,CAiCZ"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Headless rasterization of Stitch's HTML to a PNG. Like the candidate
3
+ * renderer, this drives a tool already on `PATH` (headless Chrome/Chromium)
4
+ * rather than bundling a browser-automation dependency. Injectable so unit
5
+ * tests never launch a browser.
6
+ */
7
+ import { spawn } from "node:child_process";
8
+ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
+ import { StitchRasterizeError } from "./errors.js";
12
+ /** Chrome/Chromium binaries the default rasterizer probes, in order. */
13
+ const CHROME_BINS = [
14
+ "google-chrome",
15
+ "google-chrome-stable",
16
+ "chromium",
17
+ "chromium-browser",
18
+ ];
19
+ function htmlDocument(input) {
20
+ const style = input.css ? `<style>${input.css}</style>` : "";
21
+ // A bare reset so the captured frame matches the component box, not the
22
+ // browser's default 8px body margin.
23
+ return `<!doctype html><html><head><meta charset="utf-8"><style>*{box-sizing:border-box}html,body{margin:0;padding:0}</style>${style}</head><body>${input.html}</body></html>`;
24
+ }
25
+ /**
26
+ * Drive a headless Chrome/Chromium found on `PATH` (or `CHROME_PATH`) to
27
+ * screenshot the HTML. The browser is never bundled — absence is a clear error.
28
+ */
29
+ export function browserRasterizer(env = process.env) {
30
+ return {
31
+ async rasterize(input) {
32
+ const bin = env.CHROME_PATH ?? CHROME_BINS[0];
33
+ const dir = await mkdtemp(join(tmpdir(), "stitch-raster-"));
34
+ const htmlPath = join(dir, "screen.html");
35
+ const outPath = join(dir, "screen.png");
36
+ try {
37
+ await writeFile(htmlPath, htmlDocument(input), "utf8");
38
+ const size = input.width && input.height
39
+ ? [`--window-size=${input.width},${input.height}`]
40
+ : [];
41
+ await run(bin, [
42
+ "--headless",
43
+ "--disable-gpu",
44
+ "--hide-scrollbars",
45
+ "--default-background-color=00000000",
46
+ ...size,
47
+ `--screenshot=${outPath}`,
48
+ `file://${htmlPath}`,
49
+ ]);
50
+ return new Uint8Array(await readFile(outPath));
51
+ }
52
+ catch (cause) {
53
+ throw new StitchRasterizeError(`stitch: headless rasterization failed (is '${bin}' on PATH? set CHROME_PATH, or inject a Rasterizer)`, { cause });
54
+ }
55
+ finally {
56
+ await rm(dir, { recursive: true, force: true });
57
+ }
58
+ },
59
+ };
60
+ }
61
+ function run(bin, args) {
62
+ return new Promise((resolve, reject) => {
63
+ const child = spawn(bin, args, { stdio: "ignore" });
64
+ child.on("error", reject);
65
+ child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`${bin} exited with code ${code}`)));
66
+ });
67
+ }
68
+ //# sourceMappingURL=rasterizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rasterizer.js","sourceRoot":"","sources":["../src/rasterizer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAkBnD,wEAAwE;AACxE,MAAM,WAAW,GAAG;IAClB,eAAe;IACf,sBAAsB;IACtB,UAAU;IACV,kBAAkB;CACV,CAAC;AAEX,SAAS,YAAY,CAAC,KAAqB;IACzC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7D,wEAAwE;IACxE,qCAAqC;IACrC,OAAO,wHAAwH,KAAK,gBAAgB,KAAK,CAAC,IAAI,gBAAgB,CAAC;AACjL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAA0C,OAAO,CAAC,GAAG;IAErD,OAAO;QACL,KAAK,CAAC,SAAS,CAAC,KAAqB;YACnC,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;YAC9C,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;YAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;YAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;YACxC,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;gBACvD,MAAM,IAAI,GACR,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM;oBACzB,CAAC,CAAC,CAAC,iBAAiB,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBAClD,CAAC,CAAC,EAAE,CAAC;gBACT,MAAM,GAAG,CAAC,GAAG,EAAE;oBACb,YAAY;oBACZ,eAAe;oBACf,mBAAmB;oBACnB,qCAAqC;oBACrC,GAAG,IAAI;oBACP,gBAAgB,OAAO,EAAE;oBACzB,UAAU,QAAQ,EAAE;iBACrB,CAAC,CAAC;gBACH,OAAO,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;YACjD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,oBAAoB,CAC5B,8CAA8C,GAAG,qDAAqD,EACtG,EAAE,KAAK,EAAE,CACV,CAAC;YACJ,CAAC;oBAAS,CAAC;gBACT,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,GAAG,CAAC,GAAW,EAAE,IAAc;IACtC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACpD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CACzB,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,GAAG,qBAAqB,IAAI,EAAE,CAAC,CAAC,CAC9E,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The seam over Google Stitch. Stitch ships `@google/stitch-sdk` + an MCP
3
+ * server but has **no Code Connect equivalent**, so the design is fetched by a
4
+ * `design-map`-resolved handle. Unit tests inject a fake {@link StitchClient};
5
+ * the default client lazily drives the SDK so the package keeps zero hard
6
+ * third-party deps (mirrors the figma adapter's injectable `fetch`).
7
+ */
8
+ import type { Theme } from "@design-parity/core";
9
+ import type { StitchRef } from "./stitch-ref.js";
10
+ /** One screen variant the Stitch SDK exposes for a design. */
11
+ export interface StitchScreen {
12
+ /** Variant state, e.g. `"default"`, `"pressed"`. Defaults to `"default"`. */
13
+ state?: string;
14
+ theme?: Theme;
15
+ /** Window-size label, e.g. `"compact"`, `"medium"`, `"expanded"`. */
16
+ size?: string;
17
+ /** Stitch's generated markup, classed with Tailwind utilities. */
18
+ html: string;
19
+ /** Optional stylesheet the rasterizer should inline alongside the HTML. */
20
+ css?: string;
21
+ }
22
+ /** A Stitch design as returned by the SDK: one or more screen variants. */
23
+ export interface StitchDesign {
24
+ /** Echoes the requested component handle when the SDK supplies it. */
25
+ componentId?: string;
26
+ screens: StitchScreen[];
27
+ }
28
+ /** Driver over `@google/stitch-sdk`. Inject a fake in tests. */
29
+ export interface StitchClient {
30
+ fetchDesign(ref: StitchRef): Promise<StitchDesign>;
31
+ }
32
+ /**
33
+ * Build the default SDK-backed client from `AdapterContext.env`.
34
+ *
35
+ * @throws {StitchAuthError} when no credential is configured.
36
+ * @throws {StitchSdkError} when the SDK isn't installed or returns no markup.
37
+ */
38
+ export declare function createSdkStitchClient(env: Record<string, string | undefined>): StitchClient;
39
+ //# sourceMappingURL=stitch-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stitch-client.d.ts","sourceRoot":"","sources":["../src/stitch-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAGjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACpD;AAuCD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,YAAY,CAuEd"}
@@ -0,0 +1,86 @@
1
+ import { StitchAuthError, StitchSdkError } from "./errors.js";
2
+ /** Env keys the default SDK client reads a credential from, in order. */
3
+ const TOKEN_ENV = [
4
+ "STITCH_API_KEY",
5
+ "STITCH_TOKEN",
6
+ "STITCH_ACCESS_TOKEN",
7
+ "GOOGLE_STITCH_TOKEN",
8
+ ];
9
+ function asTheme(value) {
10
+ return value === "light" || value === "dark" ? value : undefined;
11
+ }
12
+ /**
13
+ * Build the default SDK-backed client from `AdapterContext.env`.
14
+ *
15
+ * @throws {StitchAuthError} when no credential is configured.
16
+ * @throws {StitchSdkError} when the SDK isn't installed or returns no markup.
17
+ */
18
+ export function createSdkStitchClient(env) {
19
+ const apiKey = TOKEN_ENV.map((k) => env[k]).find(Boolean);
20
+ if (!apiKey) {
21
+ throw new StitchAuthError(`stitch: no credential found — set one of ${TOKEN_ENV.join(", ")}, or inject a StitchClient`);
22
+ }
23
+ return {
24
+ async fetchDesign(ref) {
25
+ // Non-literal specifier so the optional SDK isn't a compile-time
26
+ // resolution target; absence surfaces as a clear, actionable error.
27
+ const specifier = "@google/stitch-sdk";
28
+ let sdk;
29
+ try {
30
+ sdk = (await import(specifier));
31
+ }
32
+ catch (cause) {
33
+ throw new StitchSdkError("stitch: '@google/stitch-sdk' is not installed — `npm i @google/stitch-sdk` to use the default client, or inject a StitchClient", { cause });
34
+ }
35
+ let raw;
36
+ try {
37
+ const client = new sdk.StitchClient({ apiKey });
38
+ raw = await client.getScreen({
39
+ projectId: ref.projectId,
40
+ screenId: ref.screenId,
41
+ });
42
+ }
43
+ catch (cause) {
44
+ const message = cause instanceof Error ? cause.message : String(cause);
45
+ if (/401|403|unauthor|forbidden|api key|credential/i.test(message)) {
46
+ throw new StitchAuthError(`stitch: SDK rejected the credential — ${message}`, {
47
+ cause,
48
+ });
49
+ }
50
+ throw new StitchSdkError(`stitch: SDK request failed — ${message}`, { cause });
51
+ }
52
+ const screens = raw.variants && raw.variants.length > 0
53
+ ? raw.variants.map((v) => {
54
+ const html = v.html ?? raw.html;
55
+ if (html === undefined) {
56
+ throw new StitchSdkError("stitch: SDK variant returned no HTML");
57
+ }
58
+ const screen = { html };
59
+ if (v.state !== undefined)
60
+ screen.state = v.state;
61
+ const theme = asTheme(v.theme);
62
+ if (theme)
63
+ screen.theme = theme;
64
+ if (v.size !== undefined)
65
+ screen.size = v.size;
66
+ if (v.css ?? raw.css)
67
+ screen.css = v.css ?? raw.css;
68
+ return screen;
69
+ })
70
+ : (() => {
71
+ if (raw.html === undefined) {
72
+ throw new StitchSdkError("stitch: SDK returned no HTML for the screen");
73
+ }
74
+ const screen = { html: raw.html };
75
+ if (raw.css !== undefined)
76
+ screen.css = raw.css;
77
+ return [screen];
78
+ })();
79
+ const design = { screens };
80
+ if (raw.componentId !== undefined)
81
+ design.componentId = raw.componentId;
82
+ return design;
83
+ },
84
+ };
85
+ }
86
+ //# sourceMappingURL=stitch-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stitch-client.js","sourceRoot":"","sources":["../src/stitch-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A Stitch design handle: the two coordinates the SDK needs to fetch a screen.
3
+ * Stitch has no machine link, so these come from `design-map.json`.
4
+ */
5
+ export interface StitchRef {
6
+ /** Stitch project / design id. */
7
+ projectId: string;
8
+ /** Screen id within the project. */
9
+ screenId: string;
10
+ }
11
+ /**
12
+ * Parse a design-map `ref` into a {@link StitchRef}.
13
+ *
14
+ * Accepts `stitch:<projectId>/<screenId>` (the manifest form). Both segments
15
+ * may contain letters, digits, `.`, `_`, and `-`.
16
+ *
17
+ * @throws {StitchBadRefError} if the shape doesn't match.
18
+ */
19
+ export declare function parseStitchRef(ref: string): StitchRef;
20
+ /** Whether a `ref` is a Stitch handle this adapter can parse directly. */
21
+ export declare function isStitchRef(ref: string): boolean;
22
+ /** Format a {@link StitchRef} back into the manifest `stitch:<project>/<screen>` form. */
23
+ export declare function formatStitchRef({ projectId, screenId }: StitchRef): string;
24
+ //# sourceMappingURL=stitch-ref.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stitch-ref.d.ts","sourceRoot":"","sources":["../src/stitch-ref.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAIrD;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOhD;AAED,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,SAAS,GAAG,MAAM,CAE1E"}
@@ -0,0 +1,30 @@
1
+ import { StitchBadRefError } from "./errors.js";
2
+ /**
3
+ * Parse a design-map `ref` into a {@link StitchRef}.
4
+ *
5
+ * Accepts `stitch:<projectId>/<screenId>` (the manifest form). Both segments
6
+ * may contain letters, digits, `.`, `_`, and `-`.
7
+ *
8
+ * @throws {StitchBadRefError} if the shape doesn't match.
9
+ */
10
+ export function parseStitchRef(ref) {
11
+ const m = /^stitch:([A-Za-z0-9._-]+)\/([A-Za-z0-9._-]+)$/.exec(ref.trim());
12
+ if (!m)
13
+ throw new StitchBadRefError(ref);
14
+ return { projectId: m[1], screenId: m[2] };
15
+ }
16
+ /** Whether a `ref` is a Stitch handle this adapter can parse directly. */
17
+ export function isStitchRef(ref) {
18
+ try {
19
+ parseStitchRef(ref);
20
+ return true;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ /** Format a {@link StitchRef} back into the manifest `stitch:<project>/<screen>` form. */
27
+ export function formatStitchRef({ projectId, screenId }) {
28
+ return `stitch:${projectId}/${screenId}`;
29
+ }
30
+ //# sourceMappingURL=stitch-ref.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stitch-ref.js","sourceRoot":"","sources":["../src/stitch-ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAahD;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,CAAC,GAAG,+CAA+C,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3E,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACzC,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC;AAC/C,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,cAAc,CAAC,GAAG,CAAC,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,eAAe,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAa;IAChE,OAAO,UAAU,SAAS,IAAI,QAAQ,EAAE,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Extract {@link DesignTokens} from Stitch's Tailwind-classed HTML. Pure string
3
+ * work — no DOM library — so it runs anywhere and is fully deterministic.
4
+ *
5
+ * Stitch emits a flat component: a root container plus a heading (title) and a
6
+ * paragraph (body). We read spacing/radius/background off the container and
7
+ * colour + typography off the title and body elements.
8
+ */
9
+ import type { DesignTokens } from "@design-parity/core";
10
+ /** Build `DesignTokens` from one screen's Tailwind-classed HTML. */
11
+ export declare function tokensFromHtml(html: string): DesignTokens | undefined;
12
+ //# sourceMappingURL=tailwind-tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailwind-tokens.d.ts","sourceRoot":"","sources":["../src/tailwind-tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,qBAAqB,CAAC;AAqJzE,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAqCrE"}
@@ -0,0 +1,173 @@
1
+ /** Tailwind's spacing scale is `n * 0.25rem`; at the 16px root that's `n * 4`. */
2
+ const SPACING_STEP = 4;
3
+ const RADIUS_NAMED = {
4
+ none: 0,
5
+ sm: 2,
6
+ "": 4, // bare `rounded`
7
+ md: 6,
8
+ lg: 8,
9
+ xl: 12,
10
+ "2xl": 16,
11
+ "3xl": 24,
12
+ full: 9999,
13
+ };
14
+ const TEXT_NAMED = {
15
+ xs: 12,
16
+ sm: 14,
17
+ base: 16,
18
+ lg: 18,
19
+ xl: 20,
20
+ "2xl": 24,
21
+ "3xl": 30,
22
+ "4xl": 36,
23
+ };
24
+ const WEIGHT_NAMED = {
25
+ thin: 100,
26
+ extralight: 200,
27
+ light: 300,
28
+ normal: 400,
29
+ medium: 500,
30
+ semibold: 600,
31
+ bold: 700,
32
+ extrabold: 800,
33
+ black: 900,
34
+ };
35
+ const HEADINGS = new Set(["h1", "h2", "h3", "h4", "h5", "h6"]);
36
+ /** Split the document into `{ tag, classes }` for every opening tag. */
37
+ function parseElements(html) {
38
+ const out = [];
39
+ const tagRe = /<([a-zA-Z][a-zA-Z0-9]*)\b([^>]*)>/g;
40
+ for (let m = tagRe.exec(html); m; m = tagRe.exec(html)) {
41
+ const tag = m[1].toLowerCase();
42
+ const classMatch = /class\s*=\s*"([^"]*)"/.exec(m[2]);
43
+ const classes = classMatch ? classMatch[1].split(/\s+/).filter(Boolean) : [];
44
+ out.push({ tag, classes });
45
+ }
46
+ return out;
47
+ }
48
+ /** `#abc` / `#aabbcc[aa]` → upper-cased, or pass non-hex arbitrary values through. */
49
+ function normalizeColor(value) {
50
+ return value.startsWith("#") ? `#${value.slice(1).toUpperCase()}` : value;
51
+ }
52
+ /** `[18px]` / `[#fff]` → `18px` / `#fff`; `null` if not an arbitrary value. */
53
+ function arbitrary(token) {
54
+ const m = /^\[(.+)\]$/.exec(token);
55
+ return m ? m[1] : null;
56
+ }
57
+ /** Pixels from a Tailwind length token like `18px`, `1.5rem`, `12`. */
58
+ function px(value) {
59
+ const m = /^(-?\d*\.?\d+)(px|rem)?$/.exec(value.trim());
60
+ if (!m)
61
+ return undefined;
62
+ const n = Number(m[1]);
63
+ if (Number.isNaN(n))
64
+ return undefined;
65
+ return m[2] === "rem" ? n * 16 : n;
66
+ }
67
+ function scale(value) {
68
+ const arb = arbitrary(value);
69
+ if (arb)
70
+ return px(arb);
71
+ const n = Number(value);
72
+ return Number.isNaN(n) ? undefined : n * SPACING_STEP;
73
+ }
74
+ function firstValue(classes, prefix, map) {
75
+ for (const c of classes) {
76
+ if (c === prefix || c.startsWith(`${prefix}-`)) {
77
+ const rest = c === prefix ? "" : c.slice(prefix.length + 1);
78
+ const val = map(rest);
79
+ if (val !== undefined)
80
+ return val;
81
+ }
82
+ }
83
+ return undefined;
84
+ }
85
+ function colorOf(classes, prefix) {
86
+ return firstValue(classes, prefix, (rest) => {
87
+ const arb = arbitrary(rest);
88
+ return arb && arb.startsWith("#") ? normalizeColor(arb) : undefined;
89
+ });
90
+ }
91
+ function radiusOf(classes) {
92
+ return firstValue(classes, "rounded", (rest) => {
93
+ const arb = arbitrary(rest);
94
+ if (arb)
95
+ return px(arb);
96
+ return rest in RADIUS_NAMED ? RADIUS_NAMED[rest] : undefined;
97
+ });
98
+ }
99
+ function typographyOf(classes) {
100
+ const token = {};
101
+ const family = firstValue(classes, "font", (rest) => {
102
+ const arb = arbitrary(rest);
103
+ return arb ? arb.replace(/_/g, " ") : undefined;
104
+ });
105
+ if (family)
106
+ token.fontFamily = family;
107
+ const weight = firstValue(classes, "font", (rest) => rest in WEIGHT_NAMED ? WEIGHT_NAMED[rest] : undefined);
108
+ if (weight !== undefined)
109
+ token.fontWeight = weight;
110
+ const size = firstValue(classes, "text", (rest) => {
111
+ const arb = arbitrary(rest);
112
+ if (arb)
113
+ return px(arb);
114
+ return rest in TEXT_NAMED ? TEXT_NAMED[rest] : undefined;
115
+ });
116
+ if (size !== undefined)
117
+ token.fontSize = size;
118
+ const lineHeight = firstValue(classes, "leading", (rest) => {
119
+ const arb = arbitrary(rest);
120
+ if (arb)
121
+ return px(arb);
122
+ const n = Number(rest);
123
+ return Number.isNaN(n) ? undefined : n * SPACING_STEP;
124
+ });
125
+ if (lineHeight !== undefined)
126
+ token.lineHeight = lineHeight;
127
+ return Object.keys(token).length ? token : undefined;
128
+ }
129
+ /** Build `DesignTokens` from one screen's Tailwind-classed HTML. */
130
+ export function tokensFromHtml(html) {
131
+ const elements = parseElements(html);
132
+ if (elements.length === 0)
133
+ return undefined;
134
+ const container = elements[0];
135
+ const title = elements.find((e) => HEADINGS.has(e.tag));
136
+ const body = elements.find((e) => e.tag === "p");
137
+ const tokens = {};
138
+ const spacing = {};
139
+ const padding = firstValue(container.classes, "p", scale);
140
+ if (padding !== undefined)
141
+ spacing.padding = padding;
142
+ const gap = firstValue(container.classes, "gap", scale);
143
+ if (gap !== undefined)
144
+ spacing.gap = gap;
145
+ if (Object.keys(spacing).length)
146
+ tokens.spacing = spacing;
147
+ const corner = radiusOf(container.classes);
148
+ if (corner !== undefined)
149
+ tokens.radius = { corner };
150
+ const colors = {};
151
+ const containerColor = colorOf(container.classes, "bg");
152
+ if (containerColor)
153
+ colors.container = containerColor;
154
+ const titleColor = title && colorOf(title.classes, "text");
155
+ if (titleColor)
156
+ colors.title = titleColor;
157
+ const bodyColor = body && colorOf(body.classes, "text");
158
+ if (bodyColor)
159
+ colors.body = bodyColor;
160
+ if (Object.keys(colors).length)
161
+ tokens.colors = colors;
162
+ const typography = {};
163
+ const titleType = title && typographyOf(title.classes);
164
+ if (titleType)
165
+ typography.title = titleType;
166
+ const bodyType = body && typographyOf(body.classes);
167
+ if (bodyType)
168
+ typography.body = bodyType;
169
+ if (Object.keys(typography).length)
170
+ tokens.typography = typography;
171
+ return Object.keys(tokens).length ? tokens : undefined;
172
+ }
173
+ //# sourceMappingURL=tailwind-tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailwind-tokens.js","sourceRoot":"","sources":["../src/tailwind-tokens.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@design-parity/adapter-stitch",
3
+ "version": "0.1.0",
4
+ "description": "Google Stitch ReferenceAdapter: resolves a component to a Stitch design via design-map.json, fetches HTML+Tailwind through the Stitch SDK, rasterizes a reference image, and normalizes to a DesignReference.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": "./dist/index.js"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc --build",
17
+ "test": "vitest run"
18
+ },
19
+ "dependencies": {
20
+ "@design-parity/core": "^0.1.0"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/yschimke/design-parity.git",
25
+ "directory": "packages/adapters/stitch"
26
+ },
27
+ "homepage": "https://github.com/yschimke/design-parity/tree/main/packages/adapters/stitch#readme",
28
+ "bugs": "https://github.com/yschimke/design-parity/issues"
29
+ }