@draftably/runtime 0.0.0-stage → 0.2.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.
Files changed (62) hide show
  1. package/README.md +28 -2
  2. package/dist/LICENSE +21 -0
  3. package/dist/THIRD_PARTY_NOTICES +286 -0
  4. package/dist/assets/render.worker-fWW85Tf9.js +7661 -0
  5. package/dist/element.js +131 -0
  6. package/dist/font-data-tnqJ-kTK.js +30 -0
  7. package/dist/fonts/inter-latin-500-normal.woff2 +0 -0
  8. package/dist/fonts/inter-latin-600-normal.woff2 +0 -0
  9. package/dist/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
  10. package/dist/fonts/source-serif-4-latin-600-normal.woff2 +0 -0
  11. package/dist/fonts.css +4 -0
  12. package/dist/index.js +9 -0
  13. package/dist/render-ZzhO-2H6.js +7676 -0
  14. package/dist/types/src/dsl/cells.d.ts +91 -0
  15. package/dist/types/src/dsl/channels.d.ts +53 -0
  16. package/dist/types/src/dsl/compile.d.ts +162 -0
  17. package/dist/types/src/dsl/frame.d.ts +38 -0
  18. package/dist/types/src/dsl/lanes.d.ts +23 -0
  19. package/dist/types/src/dsl/legend-notes.d.ts +3 -0
  20. package/dist/types/src/dsl/project.d.ts +39 -0
  21. package/dist/types/src/dsl/syntax.d.ts +317 -0
  22. package/dist/types/src/engines/explorer-engine.d.ts +67 -0
  23. package/dist/types/src/engines/layout-engine.d.ts +120 -0
  24. package/dist/types/src/engines/routing-engine.d.ts +108 -0
  25. package/dist/types/src/engines/sculpt-engine.d.ts +71 -0
  26. package/dist/types/src/lib/font-data.d.ts +9 -0
  27. package/dist/types/src/model/appearance.d.ts +209 -0
  28. package/dist/types/src/model/bounds.d.ts +19 -0
  29. package/dist/types/src/model/colors.d.ts +22 -0
  30. package/dist/types/src/model/document.d.ts +24 -0
  31. package/dist/types/src/model/font-faces.d.ts +24 -0
  32. package/dist/types/src/model/fonts.d.ts +47 -0
  33. package/dist/types/src/model/footnote.d.ts +34 -0
  34. package/dist/types/src/model/frame.d.ts +71 -0
  35. package/dist/types/src/model/geometry.d.ts +29 -0
  36. package/dist/types/src/model/graph.d.ts +53 -0
  37. package/dist/types/src/model/heading.d.ts +38 -0
  38. package/dist/types/src/model/icons.d.ts +2 -0
  39. package/dist/types/src/model/labels.d.ts +88 -0
  40. package/dist/types/src/model/legend.d.ts +25 -0
  41. package/dist/types/src/model/link.d.ts +63 -0
  42. package/dist/types/src/model/markers.d.ts +47 -0
  43. package/dist/types/src/model/note.d.ts +75 -0
  44. package/dist/types/src/model/panels.d.ts +11 -0
  45. package/dist/types/src/model/rich-text.d.ts +24 -0
  46. package/dist/types/src/model/routes.d.ts +170 -0
  47. package/dist/types/src/model/stack.d.ts +8 -0
  48. package/dist/types/src/model/status.d.ts +84 -0
  49. package/dist/types/src/model/styles.d.ts +13 -0
  50. package/dist/types/src/model/surface.d.ts +31 -0
  51. package/dist/types/src/model/types.d.ts +277 -0
  52. package/dist/types/src/runtime/element.d.ts +14 -0
  53. package/dist/types/src/runtime/index.d.ts +5 -0
  54. package/dist/types/src/runtime/policy.d.ts +7 -0
  55. package/dist/types/src/runtime/render.d.ts +10 -0
  56. package/dist/types/src/runtime/svg-renderer.d.ts +38 -0
  57. package/dist/types/src/runtime/types.d.ts +109 -0
  58. package/dist/types/src/runtime/version.d.ts +2 -0
  59. package/dist/types/src/runtime/viewer.d.ts +89 -0
  60. package/dist/viewer.css +110 -0
  61. package/dist/viewer.js +438 -0
  62. package/package.json +49 -3
package/dist/viewer.js ADDED
@@ -0,0 +1,438 @@
1
+ import { r as ce, F as ee } from "./render-ZzhO-2H6.js";
2
+ import { f as Ee, a as ke } from "./render-ZzhO-2H6.js";
3
+ const de = "http://www.w3.org/2000/svg", le = new Set(
4
+ "svg title desc metadata defs style marker g a path rect ellipse circle line polyline polygon text tspan".split(
5
+ " "
6
+ )
7
+ ), fe = new Set(
8
+ "aria-labelledby role id data-id data-phase data-state data-theme xmlns viewBox width height x y x1 y1 x2 y2 cx cy r rx ry d points transform fill fill-rule fill-opacity stroke stroke-opacity stroke-width stroke-linecap stroke-linejoin stroke-dasharray opacity font-family font-size font-weight text-anchor dominant-baseline dy text-decoration marker-start marker-end markerWidth markerHeight markerUnits refX refY orient class clip-path nonce href target rel".split(
9
+ " "
10
+ )
11
+ );
12
+ let me = 0;
13
+ function ue(y, c) {
14
+ const V = new y.defaultView.DOMParser().parseFromString(c, "image/svg+xml"), s = /* @__PURE__ */ new Map();
15
+ for (const v of Array.from(V.querySelectorAll("text[font-family]"))) {
16
+ const l = (v.getAttribute("font-family") ?? "").split(",")[0].trim().replace(/^['"]|['"]$/g, ""), b = v.getAttribute("font-weight") ?? "400";
17
+ if (!Object.values(ee).some((o) => o.family === l && String(o.weight) === b))
18
+ throw new Error("Runtime SVG requested an unknown bundled font face.");
19
+ s.set(`${l}:${b}`, { family: l, weight: b });
20
+ }
21
+ return [...s.values()];
22
+ }
23
+ function we(y, c, V) {
24
+ const s = new y.defaultView.DOMParser().parseFromString(c, "image/svg+xml"), v = /* @__PURE__ */ new Map();
25
+ for (const l of Array.from(s.querySelectorAll("style"))) {
26
+ const b = l.textContent ?? "";
27
+ for (const [, o] of b.matchAll(/@font-face\s*\{([^{}]{1,200000})\}/g)) {
28
+ const f = /font-family\s*:\s*['"]([^'"]+)['"]/i.exec(o)?.[1], w = /font-weight\s*:\s*(\d{1,4})/i.exec(o)?.[1], p = /src\s*:\s*url\((data:font\/woff2;base64,[A-Za-z0-9+/=]+)\)/i.exec(o)?.[1];
29
+ !f || !w || !p || Object.values(ee).some((m) => m.family === f && String(m.weight) === w) && v.set(`${f}:${w}`, { family: f, weight: w, data: p });
30
+ }
31
+ }
32
+ return V.map((l) => {
33
+ const b = v.get(`${l.family}:${l.weight}`);
34
+ if (!b) throw new Error(`Embedded font ${l.family} ${l.weight} is missing.`);
35
+ return b;
36
+ });
37
+ }
38
+ function ge(y, c, V, s = 1e5) {
39
+ const v = new y.defaultView.DOMParser().parseFromString(c, "image/svg+xml");
40
+ if (v.querySelector("parsererror") || v.documentElement.localName !== "svg")
41
+ throw new Error("Runtime returned invalid SVG.");
42
+ const l = new Set(
43
+ Array.from(v.querySelectorAll("[id]"), (f) => f.getAttribute("id") ?? "")
44
+ );
45
+ let b = 0;
46
+ const o = (f) => {
47
+ if (++b > s || !le.has(f.localName))
48
+ throw new Error("Runtime SVG exceeded the allowed drawing vocabulary.");
49
+ const w = y.createElementNS(de, f.localName);
50
+ for (const { name: p, value: m } of Array.from(f.attributes)) {
51
+ if (/^on/i.test(p)) continue;
52
+ if (p === "href") {
53
+ if (f.localName !== "a") continue;
54
+ try {
55
+ const a = new URL(m, y.baseURI);
56
+ (a.protocol === "http:" || a.protocol === "https:") && w.setAttribute("href", a.href);
57
+ } catch {
58
+ }
59
+ continue;
60
+ }
61
+ if (!fe.has(p) && !/^data-[a-z0-9-]+$/.test(p)) continue;
62
+ const x = [...m.matchAll(/url\(#([^)]*)\)/g)].map((a) => a[1]);
63
+ /url\s*\(/i.test(m) && (x.length === 0 || x.some((a) => !l.has(a))) || p === "aria-labelledby" && m.split(/\s+/).some((a) => !l.has(a)) || w.setAttribute(p, m);
64
+ }
65
+ f.localName === "a" && w.getAttribute("target") === "_blank" && w.setAttribute("rel", "noopener noreferrer");
66
+ for (const p of Array.from(f.childNodes))
67
+ if (p.nodeType === 1) w.append(o(p));
68
+ else if (p.nodeType === 3) {
69
+ const m = p.nodeValue ?? "";
70
+ w.append(
71
+ y.createTextNode(
72
+ f.localName === "style" && V ? m.replace(/@font-face\s*\{[^{}]*\}/g, "") : m
73
+ )
74
+ );
75
+ }
76
+ return w;
77
+ };
78
+ return o(v.documentElement);
79
+ }
80
+ function K(y, c, V) {
81
+ return {
82
+ ok: !1,
83
+ reason: y,
84
+ diagnostics: [{ code: c, severity: "error", message: V, itemIds: [] }]
85
+ };
86
+ }
87
+ function ve(y, c = {}) {
88
+ const V = y?.ownerDocument?.defaultView;
89
+ if (!V || !(y instanceof V.HTMLElement))
90
+ throw new TypeError("host must be an HTMLElement.");
91
+ if (y.childNodes.length) throw new Error("Viewer host must be empty and exclusively owned.");
92
+ if ((c.execution ?? { mode: "worker" }).mode === "worker" && c.render?.links?.mode === "safe" && c.render.links.resolve)
93
+ throw new TypeError('A link resolver requires execution: { mode: "main" }.');
94
+ const s = y.ownerDocument, v = V, l = s.createElement("section");
95
+ l.className = "draft-viewer", c.outline !== "visible" && l.classList.add("draft-viewer--outline-hidden"), l.setAttribute("aria-label", c.label ?? "Diagram viewer");
96
+ const b = s.createElement("div");
97
+ b.className = "draft-viewer__toolbar", b.setAttribute("role", "toolbar"), b.setAttribute("aria-label", "Diagram controls");
98
+ const o = s.createElement("div");
99
+ o.className = "draft-viewer__viewport", o.tabIndex = 0, o.setAttribute("aria-label", c.label ?? "Diagram");
100
+ const f = s.createElement("p");
101
+ f.className = "draft-viewer__status", f.setAttribute("role", "status"), f.setAttribute("aria-live", "polite");
102
+ const w = s.createElement("ol");
103
+ w.className = "draft-viewer__outline", w.setAttribute("aria-label", "Diagram outline"), l.append(b, o, f, w), y.append(l);
104
+ const p = `draftViewer${(++me).toString(36)}${Math.random().toString(36).slice(2, 9)}`;
105
+ let m = 0, x = !1, a = { centerX: 0, centerY: 0, zoom: 1 }, _ = !1, u, F, q, te = 0;
106
+ const N = /* @__PURE__ */ new Map(), G = /* @__PURE__ */ new Map(), B = /* @__PURE__ */ new Map(), L = (e, r) => {
107
+ for (const t of G.get(e) ?? [])
108
+ try {
109
+ t(r);
110
+ } catch {
111
+ }
112
+ }, j = (e, r) => {
113
+ const t = s.createElement("button");
114
+ t.type = "button", t.textContent = e, t.setAttribute("aria-label", e), t.addEventListener("click", r), b.append(t);
115
+ }, re = () => {
116
+ if (!u) return 1;
117
+ const e = o.getBoundingClientRect(), [, , r, t] = u.viewBox;
118
+ return Math.min(e.width / r, e.height / t);
119
+ }, R = () => {
120
+ const e = o.querySelector("svg");
121
+ if (!e || !u) return;
122
+ const [r, t] = u.viewBox, S = o.getBoundingClientRect(), g = a.zoom, n = S.width / 2 - (a.centerX - r) * g, h = S.height / 2 - (a.centerY - t) * g;
123
+ e.style.transformOrigin = "0 0", e.style.transform = `translate(${n}px, ${h}px) scale(${g})`, e.style.width = `${u.width}px`, e.style.height = `${u.height}px`, e.style.maxWidth = "none";
124
+ }, Y = (e = "fit") => {
125
+ if (!u) return;
126
+ const r = o.getBoundingClientRect();
127
+ if (!r.width || !r.height) return;
128
+ const [t, S, g, n] = u.viewBox;
129
+ a = {
130
+ centerX: t + g / 2,
131
+ centerY: S + n / 2,
132
+ zoom: Math.min(1, re() * 0.92)
133
+ }, _ = !1, R(), L("camera", { camera: { ...a }, reason: e });
134
+ }, P = (e) => {
135
+ a.zoom = Math.max(0.02, Math.min(8, a.zoom * e)), _ = !0, R(), L("camera", { camera: { ...a }, reason: "user" });
136
+ }, ne = () => {
137
+ a.zoom = 1, _ = !0, R(), L("camera", { camera: { ...a }, reason: "user" });
138
+ };
139
+ c.toolbar !== !1 && (j("Zoom in", () => P(1.2)), j("Zoom out", () => P(1 / 1.2)), j("Fit diagram", () => Y()), j("Actual size", ne));
140
+ const J = new v.ResizeObserver(() => {
141
+ u && !o.getBoundingClientRect().width || (_ ? R() : Y("resize"));
142
+ });
143
+ J.observe(o);
144
+ const X = (e, r) => {
145
+ f.textContent = r.map((t) => t.message).join(" "), l.classList.add("draft-viewer--stale"), L("diagnostics", { revision: e, diagnostics: r, stale: !!u });
146
+ }, oe = async (e, r) => {
147
+ const t = ue(s, e);
148
+ if (!t.length) return [];
149
+ if (!s.fonts) throw new Error("The host document cannot load diagram fonts.");
150
+ if (r === "host") {
151
+ for (const n of t) {
152
+ const h = `${n.weight} 12px "${n.family}"`;
153
+ if (!(await s.fonts.load(h)).some(
154
+ (E) => E.family.replace(/^['"]|['"]$/g, "") === n.family && E.weight === n.weight && E.status === "loaded"
155
+ ) || !s.fonts.check(h))
156
+ throw new Error(`Host font ${n.family} ${n.weight} is unavailable.`);
157
+ }
158
+ return [];
159
+ }
160
+ const S = we(s, e, t), g = v.FontFace;
161
+ if (!g) throw new Error("The browser cannot load embedded diagram fonts.");
162
+ return Promise.all(
163
+ S.map(async (n) => {
164
+ const h = `${n.family}:${n.weight}`, C = N.get(h);
165
+ if (C?.status === "loaded" && Array.from(s.fonts).includes(C))
166
+ return { key: h, face: C };
167
+ const d = new g(n.family, `url("${n.data}")`, { weight: n.weight });
168
+ if (await d.load(), d.status !== "loaded")
169
+ throw new Error(`Embedded font ${n.family} ${n.weight} did not load.`);
170
+ return { key: h, face: d };
171
+ })
172
+ );
173
+ }, z = (e) => {
174
+ const r = q;
175
+ q = void 0, r && (v.clearTimeout(r.timer), r.cleanup(), r.finish(void 0, e ?? new Error("Viewer render was cancelled."))), F?.terminate(), F = void 0;
176
+ }, ae = (e, r, t) => (c.execution ?? { mode: "worker" }).mode === "main" ? ce(e, { ...r, signal: t }) : new Promise((S, g) => {
177
+ const n = ++te;
178
+ let h = !1;
179
+ const C = () => {
180
+ F && (F.removeEventListener("message", E), F.removeEventListener("error", W), F.removeEventListener("messageerror", U), t?.removeEventListener("abort", H));
181
+ }, d = (k, D) => {
182
+ if (h) return;
183
+ h = !0;
184
+ const i = q;
185
+ i?.id === n && (q = void 0, v.clearTimeout(i.timer), i.cleanup()), D ? g(D) : S(k);
186
+ }, E = (k) => {
187
+ k.data?.id === n && d(k.data.result);
188
+ }, W = (k) => {
189
+ k.preventDefault(), z(new Error(k.message || "Viewer render worker failed."));
190
+ }, U = () => z(new Error("Viewer render worker returned an unreadable result.")), H = () => z(new Error("Viewer render was aborted."));
191
+ try {
192
+ F ??= c.execution?.mode === "worker" && c.execution.createWorker ? c.execution.createWorker() : new Worker(new URL(
193
+ /* @vite-ignore */
194
+ "" + new URL("assets/render.worker-fWW85Tf9.js", import.meta.url).href,
195
+ import.meta.url
196
+ ), { type: "module" }), F.addEventListener("message", E), F.addEventListener("error", W), F.addEventListener("messageerror", U), t?.addEventListener("abort", H, { once: !0 });
197
+ const k = c.execution?.mode === "worker" ? c.execution.timeoutMs ?? 5e3 : 5e3, D = v.setTimeout(
198
+ () => z(new Error("Viewer render worker timed out.")),
199
+ k
200
+ );
201
+ q = { id: n, timer: D, finish: d, cleanup: C }, F.postMessage({ id: n, source: e, options: r });
202
+ } catch (k) {
203
+ z(
204
+ k instanceof Error ? k : new Error("Viewer render worker could not start.")
205
+ ), g(k);
206
+ }
207
+ });
208
+ let A;
209
+ const ie = (e) => {
210
+ const r = o.querySelector("svg"), t = r?.querySelector(":scope > rect");
211
+ return e === o || e === r || e === t;
212
+ };
213
+ o.addEventListener(
214
+ "wheel",
215
+ (e) => {
216
+ e.preventDefault(), P(e.deltaY < 0 ? 1.1 : 1 / 1.1);
217
+ },
218
+ { passive: !1 }
219
+ ), o.addEventListener("pointerdown", (e) => {
220
+ !ie(e.target) || !u || (A = {
221
+ pointerId: e.pointerId,
222
+ x: e.clientX,
223
+ y: e.clientY,
224
+ centerX: a.centerX,
225
+ centerY: a.centerY,
226
+ scale: a.zoom
227
+ }, o.setPointerCapture(e.pointerId));
228
+ }), o.addEventListener("pointermove", (e) => {
229
+ A && (a.centerX = A.centerX - (e.clientX - A.x) / A.scale, a.centerY = A.centerY - (e.clientY - A.y) / A.scale, _ = !0, R());
230
+ });
231
+ const Q = () => {
232
+ if (A) {
233
+ try {
234
+ o.releasePointerCapture(A.pointerId);
235
+ } catch {
236
+ }
237
+ A = void 0, L("camera", { camera: { ...a }, reason: "user" });
238
+ }
239
+ };
240
+ o.addEventListener("pointerup", Q), o.addEventListener("pointercancel", Q), o.addEventListener("click", (e) => {
241
+ const r = e.target instanceof v.Element ? e.target : null, t = r?.closest("a[href]");
242
+ if (t && o.contains(t)) {
243
+ const n = t.querySelector("[data-id]")?.getAttribute("data-id"), h = t.getAttribute("href");
244
+ n && h && L("link", {
245
+ itemId: n,
246
+ href: h,
247
+ target: t.getAttribute("target") === "_blank" ? "_blank" : "_self",
248
+ preventDefault: () => e.preventDefault()
249
+ });
250
+ return;
251
+ }
252
+ const S = r?.closest("[data-id]")?.getAttribute("data-id"), g = u?.items.find((n) => n.id === S);
253
+ g && L("itemactivate", { id: g.id, kind: g.kind, input: "pointer", revision: m });
254
+ }), o.addEventListener("keydown", (e) => {
255
+ e.target !== o || e.altKey || e.ctrlKey || e.metaKey || (e.key === "+" || e.key === "=" ? P(1.2) : e.key === "-" ? P(1 / 1.2) : e.key.toLowerCase() === "f" && Y());
256
+ });
257
+ const se = (e, r) => {
258
+ for (const [t, S] of B)
259
+ t !== e && S.resolve({ status: r, revision: t });
260
+ };
261
+ return {
262
+ update: (e, r = {}) => {
263
+ const t = ++m;
264
+ return x ? Promise.resolve({ status: "destroyed", revision: t }) : (se(t, "superseded"), z(new Error("Viewer update was superseded.")), new Promise((S) => {
265
+ let g = !1;
266
+ const n = (d) => {
267
+ if (g) return;
268
+ g = !0, r.signal?.removeEventListener("abort", h), B.delete(t);
269
+ const E = x ? { status: "destroyed", revision: t } : t !== m ? { status: "superseded", revision: t } : r.signal?.aborted ? { status: "aborted", revision: t } : d;
270
+ S(E);
271
+ }, h = () => {
272
+ t === m && (c.execution ?? { mode: "worker" }).mode === "worker" && z(new Error("Viewer render was aborted.")), n({ status: "aborted", revision: t });
273
+ };
274
+ if (B.set(t, { resolve: n }), r.signal?.aborted) {
275
+ h();
276
+ return;
277
+ }
278
+ r.signal?.addEventListener("abort", h, { once: !0 });
279
+ const C = {
280
+ ...c.render ?? {},
281
+ target: "inline",
282
+ idPrefix: p,
283
+ fonts: c.render?.fonts ?? "embed",
284
+ ...r.state !== void 0 ? { state: r.state } : {}
285
+ };
286
+ ae(e, C, r.signal).then(async (d) => {
287
+ if (x || t !== m || r.signal?.aborted) {
288
+ n({ status: "superseded", revision: t });
289
+ return;
290
+ }
291
+ if (!d.ok) {
292
+ X(t, d.diagnostics), n({ status: "error", revision: t, error: d });
293
+ return;
294
+ }
295
+ let E;
296
+ try {
297
+ E = await oe(d.value.svg, C.fonts ?? "embed");
298
+ } catch (i) {
299
+ if (x || t !== m || r.signal?.aborted) {
300
+ n({
301
+ status: r.signal?.aborted ? "aborted" : "superseded",
302
+ revision: t
303
+ });
304
+ return;
305
+ }
306
+ const $ = K(
307
+ "environment",
308
+ "DRAFT_FONT_UNAVAILABLE",
309
+ i instanceof Error ? i.message : "Diagram fonts are unavailable."
310
+ );
311
+ X(t, $.diagnostics), n({ status: "error", revision: t, error: $ });
312
+ return;
313
+ }
314
+ if (x || t !== m || r.signal?.aborted) {
315
+ n({ status: "superseded", revision: t });
316
+ return;
317
+ }
318
+ let W;
319
+ try {
320
+ W = ge(s, d.value.svg, C.fonts !== "host");
321
+ } catch (i) {
322
+ const $ = i instanceof Error ? i.message : "Could not build safe SVG.", M = K("internal", "DRAFT_VIEWER_DOM", $);
323
+ X(t, M.diagnostics), n({ status: "error", revision: t, error: M });
324
+ return;
325
+ }
326
+ const U = [];
327
+ try {
328
+ for (const i of E)
329
+ N.has(i.key) || (s.fonts.add(i.face), N.set(i.key, i.face), U.push(i));
330
+ } catch (i) {
331
+ for (const { key: M, face: Z } of U)
332
+ s.fonts.delete(Z), N.delete(M);
333
+ const $ = K(
334
+ "environment",
335
+ "DRAFT_FONT_UNAVAILABLE",
336
+ i instanceof Error ? i.message : "Loaded fonts could not be registered."
337
+ );
338
+ X(t, $.diagnostics), n({ status: "error", revision: t, error: $ });
339
+ return;
340
+ }
341
+ const H = !!u;
342
+ u = d.value, o.replaceChildren(W), w.replaceChildren(
343
+ ...u.items.map((i) => {
344
+ const $ = s.createElement("li"), M = s.createElement("button");
345
+ M.type = "button";
346
+ const Z = i.label.trim() || (i.kind === "edge" ? `${i.from ?? "?"} to ${i.to ?? "?"}` : i.id);
347
+ return M.textContent = `${i.kind}: ${Z}`, M.dataset.itemId = i.id, M.addEventListener("click", () => {
348
+ L("itemactivate", {
349
+ id: i.id,
350
+ kind: i.kind,
351
+ input: "keyboard",
352
+ revision: t
353
+ });
354
+ const O = Array.from(o.querySelectorAll("[data-id]")).find(
355
+ (T) => T.getAttribute("data-id") === i.id
356
+ );
357
+ if (O) {
358
+ const T = O.getBoundingClientRect(), I = o.getBoundingClientRect();
359
+ (T.width || T.height) && I.width && I.height && (a = {
360
+ centerX: a.centerX + (T.left + T.width / 2 - (I.left + I.width / 2)) / a.zoom,
361
+ centerY: a.centerY + (T.top + T.height / 2 - (I.top + I.height / 2)) / a.zoom,
362
+ zoom: Math.max(1, a.zoom)
363
+ }, _ = !0, R(), L("camera", { camera: { ...a }, reason: "user" }));
364
+ }
365
+ }), $.append(M), $;
366
+ })
367
+ );
368
+ const k = new Set(E.map((i) => i.key));
369
+ for (const [i, $] of N)
370
+ k.has(i) || (s.fonts.delete($), N.delete(i));
371
+ l.classList.remove("draft-viewer--stale"), f.textContent = d.diagnostics.map((i) => i.message).join(" "), !H || r.camera === "fit" ? Y() : R();
372
+ const D = {
373
+ status: "committed",
374
+ revision: t,
375
+ value: u,
376
+ diagnostics: d.diagnostics
377
+ };
378
+ n(D), L("render", { revision: t, value: u }), L("diagnostics", { revision: t, diagnostics: d.diagnostics, stale: !1 });
379
+ }).catch((d) => {
380
+ if (x || t !== m || r.signal?.aborted) {
381
+ n({
382
+ status: r.signal?.aborted ? "aborted" : "superseded",
383
+ revision: t
384
+ });
385
+ return;
386
+ }
387
+ const E = K(
388
+ "environment",
389
+ "DRAFT_VIEWER_WORKER",
390
+ d instanceof Error ? d.message : "Viewer render failed."
391
+ );
392
+ X(t, E.diagnostics), n({ status: "error", revision: t, error: E });
393
+ });
394
+ }));
395
+ },
396
+ on(e, r) {
397
+ if (x) return () => {
398
+ };
399
+ const t = G.get(e) ?? /* @__PURE__ */ new Set();
400
+ return t.add(r), G.set(e, t), () => t.delete(r);
401
+ },
402
+ getCamera: () => ({ ...a }),
403
+ setCamera(e) {
404
+ x || !Number.isFinite(e.centerX) || !Number.isFinite(e.centerY) || !Number.isFinite(e.zoom) || e.zoom <= 0 || (a = { ...e }, _ = !0, R(), L("camera", { camera: { ...a }, reason: "api" }));
405
+ },
406
+ fit: () => {
407
+ x || Y();
408
+ },
409
+ focusItem(e) {
410
+ if (!u?.items.some((t) => t.id === e)) return !1;
411
+ const r = Array.from(w.querySelectorAll("button")).find(
412
+ (t) => t.dataset.itemId === e
413
+ );
414
+ return r?.focus(), !!r;
415
+ },
416
+ destroy() {
417
+ if (!x) {
418
+ if (x = !0, m++, A) {
419
+ try {
420
+ o.releasePointerCapture(A.pointerId);
421
+ } catch {
422
+ }
423
+ A = void 0;
424
+ }
425
+ for (const [e, r] of B)
426
+ r.resolve({ status: "destroyed", revision: e });
427
+ B.clear(), z(new Error("Viewer was destroyed."));
428
+ for (const e of N.values()) s.fonts.delete(e);
429
+ N.clear(), J.disconnect(), G.clear(), y.replaceChildren(), u = void 0;
430
+ }
431
+ }
432
+ };
433
+ }
434
+ export {
435
+ Ee as fontMetricsVersion,
436
+ ve as mountViewer,
437
+ ke as runtimeVersion
438
+ };
package/package.json CHANGED
@@ -1,6 +1,52 @@
1
1
  {
2
2
  "name": "@draftably/runtime",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "private": false,
5
+ "description": "Portable native Draftably SVG rendering, validation, and optional viewer.",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "sideEffects": [
9
+ "**/*.css",
10
+ "./dist/element.js"
11
+ ],
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/types/src/runtime/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ },
21
+ "./viewer": {
22
+ "types": "./dist/types/src/runtime/viewer.d.ts",
23
+ "import": "./dist/viewer.js"
24
+ },
25
+ "./element": {
26
+ "types": "./dist/types/src/runtime/element.d.ts",
27
+ "import": "./dist/element.js"
28
+ },
29
+ "./viewer.css": "./dist/viewer.css",
30
+ "./fonts.css": "./dist/fonts.css",
31
+ "./browser": {
32
+ "types": "./dist/types/src/runtime/index.d.ts",
33
+ "import": "./dist/index.js"
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "engines": {
38
+ "node": ">=22.21.0"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/byronwall/draftably.git",
43
+ "directory": "packages/runtime"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public",
47
+ "registry": "https://registry.npmjs.org"
48
+ },
49
+ "scripts": {
50
+ "build": "node ../../scripts/build-runtime.ts"
51
+ }
6
52
  }