@snowcone-app/canvas 0.16.0 → 0.17.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/dist/{CanvasStateV1-BQD8fCBU.js → CanvasStateV1-C6i6vU6p.js} +63 -63
- package/dist/{CanvasStateV1-BQD8fCBU.js.map → CanvasStateV1-C6i6vU6p.js.map} +1 -1
- package/dist/EditorCanvasImpl-DkgE5-C2.js +68 -0
- package/dist/EditorCanvasImpl-DkgE5-C2.js.map +1 -0
- package/dist/EditorContext-CyanYzo2.js +1131 -0
- package/dist/EditorContext-CyanYzo2.js.map +1 -0
- package/dist/{ImportManager-eotl7r5d.js → ElementFactory-C05JJkRp.js} +1510 -5121
- package/dist/ElementFactory-C05JJkRp.js.map +1 -0
- package/dist/{HybridHistoryManager-NL3zyEl4.js → HybridHistoryManager-Bht98XRv.js} +98 -98
- package/dist/{HybridHistoryManager-NL3zyEl4.js.map → HybridHistoryManager-Bht98XRv.js.map} +1 -1
- package/dist/ImportManager-kRYfIX-D.js +3617 -0
- package/dist/ImportManager-kRYfIX-D.js.map +1 -0
- package/dist/SnowconeCanvas-C3eZ9PP8.js +1047 -0
- package/dist/SnowconeCanvas-C3eZ9PP8.js.map +1 -0
- package/dist/ThemeContext-Rjz25x9X.js +75 -0
- package/dist/ThemeContext-Rjz25x9X.js.map +1 -0
- package/dist/UnifiedFontService-BAtqqkU4.js +1478 -0
- package/dist/UnifiedFontService-BAtqqkU4.js.map +1 -0
- package/dist/advanced.mjs +210 -207
- package/dist/advanced.mjs.map +1 -1
- package/dist/api/embed.d.ts +8 -4
- package/dist/api/testing.d.ts +1 -0
- package/dist/components/editor/EditorBuy.d.ts +49 -0
- package/dist/components/editor/EditorCanvas.d.ts +30 -0
- package/dist/components/editor/EditorCanvasImpl.d.ts +33 -0
- package/dist/components/editor/EditorPreview.d.ts +39 -0
- package/dist/components/editor/EditorRoot.d.ts +86 -0
- package/dist/components/editor/EditorTextField.d.ts +22 -0
- package/dist/components/editor/RootInternals.d.ts +77 -0
- package/dist/components/editor/designStateStore.d.ts +45 -0
- package/dist/components/editor/index.d.ts +23 -0
- package/dist/components/embed-realtime/RealtimeCanvas.d.ts +1 -1
- package/dist/components/embed-realtime/RealtimeEmbed.d.ts +5 -78
- package/dist/components/embed-realtime/RealtimeMockup.d.ts +5 -34
- package/dist/components/embed-realtime/context.d.ts +12 -2
- package/dist/components/embed-realtime/validateEmbed.d.ts +17 -3
- package/dist/design/design-builder.d.ts +81 -6
- package/dist/editorError-DMENTZ-3.js +19 -0
- package/dist/editorError-DMENTZ-3.js.map +1 -0
- package/dist/editorErrors-DbIEtCTt.js +29 -0
- package/dist/editorErrors-DbIEtCTt.js.map +1 -0
- package/dist/embed-Bdj4PKUA.js +737 -0
- package/dist/embed-Bdj4PKUA.js.map +1 -0
- package/dist/embed.mjs +38 -230
- package/dist/embed.mjs.map +1 -1
- package/dist/errors/editorError.d.ts +57 -0
- package/dist/iconify-D6eVIEy4.js +1089 -0
- package/dist/iconify-D6eVIEy4.js.map +1 -0
- package/dist/index.mjs +48 -46
- package/dist/index.mjs.map +1 -1
- package/dist/internals.mjs +105 -104
- package/dist/internals.mjs.map +1 -1
- package/dist/template-fields-GOM4MNfq.js +643 -0
- package/dist/template-fields-GOM4MNfq.js.map +1 -0
- package/dist/testing/editorErrors.d.ts +30 -0
- package/dist/testing.mjs +28 -23
- package/dist/testing.mjs.map +1 -1
- package/dist/{usePieceGuideBundle-Cyl695jv.js → usePieceGuideBundle-CGERAQJl.js} +2 -2
- package/dist/{usePieceGuideBundle-Cyl695jv.js.map → usePieceGuideBundle-CGERAQJl.js.map} +1 -1
- package/dist/validation-DEd6EOCP.js +19947 -0
- package/dist/validation-DEd6EOCP.js.map +1 -0
- package/dist/workers/export-worker.bundle.string.d.ts +1 -1
- package/package.json +9 -4
- package/dist/ImportManager-eotl7r5d.js.map +0 -1
- package/dist/ThemeContext-D9AaUw9v.js +0 -1200
- package/dist/ThemeContext-D9AaUw9v.js.map +0 -1
- package/dist/template-fields-PcSPApl5.js +0 -1632
- package/dist/template-fields-PcSPApl5.js.map +0 -1
- package/dist/validation-BpEod0Rn.js +0 -22953
- package/dist/validation-BpEod0Rn.js.map +0 -1
|
@@ -69,11 +69,52 @@ export interface ShapeOptions extends NamedOption {
|
|
|
69
69
|
sides?: number;
|
|
70
70
|
padding?: number;
|
|
71
71
|
}
|
|
72
|
+
/** The bindable layer kinds. Shapes are not bindable in v1. */
|
|
73
|
+
export type LayerKind = 'text' | 'image';
|
|
74
|
+
/**
|
|
75
|
+
* Any design builder, regardless of its compile-time layer-name unions.
|
|
76
|
+
* CONSUMERS of a builder (props, context) must use this — `DesignBuilder`
|
|
77
|
+
* (= `DesignBuilder<string, string>`) is not a supertype of a statically
|
|
78
|
+
* typed builder because `layer()`'s name constraint narrows with the design.
|
|
79
|
+
*/
|
|
80
|
+
export type AnyDesign = DesignBuilder<any, any>;
|
|
81
|
+
declare const LAYER_TOKEN_BRAND: unique symbol;
|
|
82
|
+
/**
|
|
83
|
+
* A validated claim that layer `N` of kind `K` exists in the design that
|
|
84
|
+
* minted it. The ONLY currency `Editor.TextField` (and future `ImageField`)
|
|
85
|
+
* accepts — bare strings are rejected (`E_LAYER_TOKEN_REQUIRED`). Mint with
|
|
86
|
+
* {@link DesignBuilder.layer}; not constructible by hand (branded).
|
|
87
|
+
*
|
|
88
|
+
* @experimental ADR-0085; subject to change until the P4 gate.
|
|
89
|
+
*/
|
|
90
|
+
export interface LayerToken<K extends LayerKind = LayerKind, N extends string = string> {
|
|
91
|
+
readonly kind: K;
|
|
92
|
+
readonly name: N;
|
|
93
|
+
/** @internal */
|
|
94
|
+
readonly [LAYER_TOKEN_BRAND]: true;
|
|
95
|
+
}
|
|
96
|
+
/** Runtime check used by parts before trusting a `layer` prop. */
|
|
97
|
+
export declare function isLayerToken(value: unknown): value is LayerToken;
|
|
98
|
+
/** The token kind inferred for name `N` against the builder's accumulated
|
|
99
|
+
* per-kind name unions. A dynamically-loaded design (both unions degraded to
|
|
100
|
+
* `string`) yields the full `'text' | 'image'` union — typing degrades, the
|
|
101
|
+
* runtime validation in `layer()` is the net that still fires (0G#2). */
|
|
102
|
+
type InferKind<N extends string, TText extends string, TImage extends string> = [
|
|
103
|
+
N
|
|
104
|
+
] extends [TText] ? ([N] extends [TImage] ? LayerKind : 'text') : 'image';
|
|
72
105
|
/**
|
|
73
106
|
* A composed design. Add elements by intent, then emit it for the editor or the
|
|
74
107
|
* render path. Methods are chainable.
|
|
108
|
+
*
|
|
109
|
+
* The two type parameters accumulate the design's NAMED layer literals per
|
|
110
|
+
* kind (ADR-0085 §1): naming a layer in `.text()` / `.image()` /
|
|
111
|
+
* `.background()` widens the corresponding union, so `design.layer("naem")`
|
|
112
|
+
* is a compile error listing the valid names. Designs loaded from JSON
|
|
113
|
+
* ({@link DesignBuilder.fromState}) degrade both unions to `string` — same
|
|
114
|
+
* API, runtime-validated instead (the degradation is deliberate and LOUD in
|
|
115
|
+
* docs; never block the dynamic case).
|
|
75
116
|
*/
|
|
76
|
-
export declare class DesignBuilder {
|
|
117
|
+
export declare class DesignBuilder<out TText extends string = string, out TImage extends string = string> {
|
|
77
118
|
private readonly artboard;
|
|
78
119
|
private readonly els;
|
|
79
120
|
private idCounter;
|
|
@@ -101,13 +142,41 @@ export declare class DesignBuilder {
|
|
|
101
142
|
private nextId;
|
|
102
143
|
/** Full-bleed background image (fills the whole artboard). Cover-fit (no
|
|
103
144
|
* distortion) when `aspectRatio` is supplied. Inserted at the bottom of the
|
|
104
|
-
* stack regardless of call order.
|
|
105
|
-
|
|
145
|
+
* stack regardless of call order. The default layer name `'Background'`
|
|
146
|
+
* joins the IMAGE-layer token union (auto-injected names are deliberately
|
|
147
|
+
* not one union with text — ADR-0085 §1). */
|
|
148
|
+
background<const N extends string>(imageUrl: string, opts: BackgroundOptions & {
|
|
149
|
+
name: N;
|
|
150
|
+
}): DesignBuilder<TText, TImage | N>;
|
|
151
|
+
background(imageUrl: string, opts?: BackgroundOptions): DesignBuilder<TText, TImage | 'Background'>;
|
|
106
152
|
/** An anchored image element (not full-bleed). */
|
|
107
|
-
image(imageUrl: string, opts
|
|
153
|
+
image<const N extends string>(imageUrl: string, opts: ImageOptions & {
|
|
154
|
+
name: N;
|
|
155
|
+
}): DesignBuilder<TText, TImage | N>;
|
|
156
|
+
image(imageUrl: string, opts?: ImageOptions): DesignBuilder<TText, TImage>;
|
|
108
157
|
/** An anchored text layer. Horizontal centring is exact (textAlign); vertical
|
|
109
158
|
* anchoring uses the font size as the line-height estimate. */
|
|
110
|
-
text(text: string, opts
|
|
159
|
+
text<const N extends string>(text: string, opts: TextOptions & {
|
|
160
|
+
name: N;
|
|
161
|
+
}): DesignBuilder<TText | N, TImage>;
|
|
162
|
+
text(text: string, opts?: TextOptions): DesignBuilder<TText, TImage>;
|
|
163
|
+
/**
|
|
164
|
+
* Mint a {@link LayerToken} for a named layer — the only currency
|
|
165
|
+
* `Editor.TextField` accepts (ADR-0085 §1). Compile time: `name` is
|
|
166
|
+
* constrained to this design's declared layer names, kind inferred from
|
|
167
|
+
* which per-kind union it falls in. Runtime: validated EAGERLY against the
|
|
168
|
+
* built elements (nearest the mistake), so CMS/JSON-loaded designs get the
|
|
169
|
+
* same protection with `string` typing.
|
|
170
|
+
*
|
|
171
|
+
* @throws EditorError `E_LAYER_NOT_FOUND` — no layer with this name; lists
|
|
172
|
+
* the valid names per kind.
|
|
173
|
+
* @throws EditorError `E_LAYER_WRONG_KIND` — the name matches a layer that
|
|
174
|
+
* is not bindable (shape/group/path).
|
|
175
|
+
* @experimental ADR-0085; subject to change until the P4 gate.
|
|
176
|
+
*/
|
|
177
|
+
layer<N extends TText | TImage>(name: N): LayerToken<InferKind<N, TText, TImage>, N>;
|
|
178
|
+
/** Names of bindable layers of one kind, in stack order. @internal */
|
|
179
|
+
layerNamesOfKind(kind: LayerKind): string[];
|
|
111
180
|
/** An anchored vector shape. */
|
|
112
181
|
shape(kind: ShapeKind, opts?: ShapeOptions): this;
|
|
113
182
|
/** The composed elements, in render order (background first). Editor shape. */
|
|
@@ -142,5 +211,11 @@ export declare class DesignBuilder {
|
|
|
142
211
|
* // …or hand d.toJSON() to <SnowconeCanvas/> for interactive editing — the
|
|
143
212
|
* // `name`s let you bind layers with useImageBinding('art') / useTextBinding('headline').
|
|
144
213
|
*/
|
|
145
|
-
export declare function design(artboard: DesignArtboard): DesignBuilder
|
|
214
|
+
export declare function design(artboard: DesignArtboard): DesignBuilder<never, never>;
|
|
215
|
+
/**
|
|
216
|
+
* Resolve a layer's kind against a live element/config list — the mount-time
|
|
217
|
+
* revalidation `Editor.TextField` runs (the token is a validated claim; the
|
|
218
|
+
* mounted Root's design state is the authority). @internal
|
|
219
|
+
*/
|
|
220
|
+
export declare function layerKindOfConfig(el: AnyElementConfig): LayerKind | null;
|
|
146
221
|
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { y as t } from "./HybridHistoryManager-Bht98XRv.js";
|
|
2
|
+
class e extends Error {
|
|
3
|
+
constructor(o, i, s = {}) {
|
|
4
|
+
super(`[Editor] ${o}: ${i}`), this.name = "EditorError", this.code = o, this.data = { code: o, ...s };
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
function E(r) {
|
|
8
|
+
return r instanceof Error && r.name === "EditorError" && "code" in r;
|
|
9
|
+
}
|
|
10
|
+
function a() {
|
|
11
|
+
var r;
|
|
12
|
+
return typeof t < "u" && ((r = t.env) == null ? void 0 : r.NODE_ENV) !== "production";
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
e as E,
|
|
16
|
+
E as a,
|
|
17
|
+
a as i
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=editorError-DMENTZ-3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorError-DMENTZ-3.js","sources":["../src/errors/editorError.ts"],"sourcesContent":["/**\n * Structured error contract for the `Editor.*` composable primitives (ADR-0085,\n * decision E3): every public throw carries a stable machine-readable code plus\n * an `error.data` payload, with the human/agent message RENDERED from that\n * data. Tests assert on `code` + `data` fields, never on prose substrings.\n *\n * Message style (E2): verifiable facts from data in hand only — name the\n * violated contract, the offending part, the valid alternatives, and the exact\n * corrective code. Never heuristic guesses.\n */\n\n/** Stable codes for composition/runtime violations. ADR-0085 catalog. */\nexport type EditorErrorCode =\n /** An `Editor.*` part mounted outside `<Editor.Root>`. */\n | 'E_OUTSIDE_ROOT'\n /** A layer token (or its name) matches no layer in the mounted design. */\n | 'E_LAYER_NOT_FOUND'\n /** The layer exists but is not the kind the part binds (e.g. an image\n * layer passed to `Editor.TextField`). */\n | 'E_LAYER_WRONG_KIND'\n /** `Editor.TextField` received a bare string (or other non-token) — layers\n * bind by `design.layer(\"name\")` token only (ADR-0085 §1). */\n | 'E_LAYER_TOKEN_REQUIRED'\n /** Programmatic rename/delete of a layer a mounted TextField is bound to. */\n | 'E_BOUND_LAYER_MUTATION'\n /** Two `Editor.Canvas` parts mounted under one Root. */\n | 'E_DUPLICATE_CANVAS'\n /** A part that needs a mounted `Editor.Canvas` found none under this Root. */\n | 'E_REQUIRES_CANVAS'\n /** `Editor.Buy` mounted without product context. */\n | 'E_BUY_NO_PRODUCT'\n /** The lazy renderer chunk failed to load (network/CDN). Retryable. */\n | 'E_CHUNK_LOAD_FAILED'\n /** Embed wiring invalid at mount (missing grant/mockupIds, variant or\n * placement mismatch) — the throw-mode upgrade of `validateEmbed` (F3). */\n | 'E_EMBED_CONFIG';\n\nexport interface EditorErrorData {\n code: EditorErrorCode;\n /** Slug under https://developers.snowcone.app/ for the violated contract. */\n docSlug?: string;\n /** Valid alternatives when the violation is a bad identifier. */\n validNames?: readonly string[];\n /** Structured facts about the offending values. */\n [key: string]: unknown;\n}\n\n/**\n * The one error class every `Editor.*` invariant throws. `code` is the stable\n * test/agent contract; `message` is rendered from `data` and may be reworded.\n */\nexport class EditorError extends Error {\n readonly code: EditorErrorCode;\n readonly data: EditorErrorData;\n\n constructor(code: EditorErrorCode, message: string, data: Omit<EditorErrorData, 'code'> = {}) {\n super(`[Editor] ${code}: ${message}`);\n this.name = 'EditorError';\n this.code = code;\n this.data = { code, ...data };\n }\n}\n\nexport function isEditorError(err: unknown): err is EditorError {\n return err instanceof Error && err.name === 'EditorError' && 'code' in err;\n}\n\n/** True outside production bundles — mirrors the embed's existing typeof-guarded\n * pattern (non-Node bundlers may not define `process`). */\nexport function isDevOrTest(): boolean {\n return typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production';\n}\n"],"names":["EditorError","code","message","data","isEditorError","err","isDevOrTest","process"],"mappings":";AAmDO,MAAMA,UAAoB,MAAM;AAAA,EAIrC,YAAYC,GAAuBC,GAAiBC,IAAsC,CAAA,GAAI;AAC5F,UAAM,YAAYF,CAAI,KAAKC,CAAO,EAAE,GACpC,KAAK,OAAO,eACZ,KAAK,OAAOD,GACZ,KAAK,OAAO,EAAE,MAAAA,GAAM,GAAGE,EAAA;AAAA,EACzB;AACF;AAEO,SAASC,EAAcC,GAAkC;AAC9D,SAAOA,aAAe,SAASA,EAAI,SAAS,iBAAiB,UAAUA;AACzE;AAIO,SAASC,IAAuB;;AACrC,SAAO,OAAOC,IAAY,SAAeA,IAAAA,EAAQ,QAARA,gBAAAA,EAAa,cAAa;AACrE;"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { a as n, i as t } from "./editorError-DMENTZ-3.js";
|
|
2
|
+
const r = [];
|
|
3
|
+
function i(e) {
|
|
4
|
+
t() && r.push(e);
|
|
5
|
+
}
|
|
6
|
+
function d() {
|
|
7
|
+
return [...r];
|
|
8
|
+
}
|
|
9
|
+
function a() {
|
|
10
|
+
r.length = 0;
|
|
11
|
+
}
|
|
12
|
+
function c() {
|
|
13
|
+
if (r.length === 0) return;
|
|
14
|
+
const e = r.map((o) => n(o) ? `${o.code}: ${o.message}` : `${o.name}: ${o.message}`).join(`
|
|
15
|
+
- `);
|
|
16
|
+
throw r.length = 0, new Error(
|
|
17
|
+
`expectNoEditorErrors: ${e.includes(`
|
|
18
|
+
`) ? "errors were" : "an error was"} contained by <Editor.Root>'s boundary during this test:
|
|
19
|
+
- ${e}
|
|
20
|
+
A contained error renders an error card in prod — this composition is broken even if the test rendered "successfully".`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
a,
|
|
25
|
+
c as e,
|
|
26
|
+
d as g,
|
|
27
|
+
i as r
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=editorErrors-DbIEtCTt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorErrors-DbIEtCTt.js","sources":["../src/testing/editorErrors.ts"],"sourcesContent":["/**\n * Test-side hooks into the `Editor.*` error contract (ADR-0085 / D11).\n *\n * Containment is a PROD feature: under `NODE_ENV=test`, Root's boundary\n * rethrows render-time composition errors, so a broken composition fails the\n * test naturally. The remaining blind spot is the NON-throwing channel —\n * runtime failures routed straight to the error card (e.g. the lazy renderer\n * chunk failing to load, `E_CHUNK_LOAD_FAILED`) and prod-routed handler\n * invariants. Those are recorded here in test envs so\n * {@link expectNoEditorErrors} can fail a green-looking test that actually\n * rendered an error card.\n *\n * ```ts\n * import { expectNoEditorErrors } from '@snowcone-app/canvas/testing';\n *\n * afterEach(() => expectNoEditorErrors());\n * ```\n */\n\nimport { isEditorError, isDevOrTest } from '../errors/editorError.js';\n\nconst recorded: Error[] = [];\n\n/** Record a contained editor error (test/dev envs only). @internal */\nexport function recordContainedEditorError(error: Error): void {\n if (isDevOrTest()) recorded.push(error);\n}\n\n/** Errors contained since the last drain, without draining. */\nexport function getContainedEditorErrors(): readonly Error[] {\n return [...recorded];\n}\n\n/** Drop all recorded errors (e.g. after asserting an EXPECTED failure). */\nexport function resetContainedEditorErrors(): void {\n recorded.length = 0;\n}\n\n/**\n * Assert that no editor error was contained (rendered as an error card)\n * since the last reset/call. Drains the record either way so one leaked\n * error doesn't fail every later test in the file.\n */\nexport function expectNoEditorErrors(): void {\n if (recorded.length === 0) return;\n const summary = recorded\n .map((e) => (isEditorError(e) ? `${e.code}: ${e.message}` : `${e.name}: ${e.message}`))\n .join('\\n - ');\n recorded.length = 0;\n throw new Error(\n `expectNoEditorErrors: ${summary.includes('\\n') ? 'errors were' : 'an error was'} contained by ` +\n `<Editor.Root>'s boundary during this test:\\n - ${summary}\\n` +\n 'A contained error renders an error card in prod — this composition is broken even if the test rendered \"successfully\".',\n );\n}\n"],"names":["recorded","recordContainedEditorError","error","isDevOrTest","getContainedEditorErrors","resetContainedEditorErrors","expectNoEditorErrors","summary","e","isEditorError"],"mappings":";AAqBA,MAAMA,IAAoB,CAAA;AAGnB,SAASC,EAA2BC,GAAoB;AAC7D,EAAIC,EAAA,KAAeH,EAAS,KAAKE,CAAK;AACxC;AAGO,SAASE,IAA6C;AAC3D,SAAO,CAAC,GAAGJ,CAAQ;AACrB;AAGO,SAASK,IAAmC;AACjD,EAAAL,EAAS,SAAS;AACpB;AAOO,SAASM,IAA6B;AAC3C,MAAIN,EAAS,WAAW,EAAG;AAC3B,QAAMO,IAAUP,EACb,IAAI,CAACQ,MAAOC,EAAcD,CAAC,IAAI,GAAGA,EAAE,IAAI,KAAKA,EAAE,OAAO,KAAK,GAAGA,EAAE,IAAI,KAAKA,EAAE,OAAO,EAAG,EACrF,KAAK;AAAA,KAAQ;AAChB,QAAAR,EAAS,SAAS,GACZ,IAAI;AAAA,IACR,yBAAyBO,EAAQ,SAAS;AAAA,CAAI,IAAI,gBAAgB,cAAc;AAAA,MAC3BA,CAAO;AAAA;AAAA,EAAA;AAGhE;"}
|