carboncanvas 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.
@@ -0,0 +1,2231 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import React, { useState, useCallback, useEffect, lazy, useMemo, Suspense, useRef } from "react";
5
+ import { createPortal } from "react-dom";
6
+ let portalRoot = null;
7
+ function setPortalRoot(el) {
8
+ portalRoot = el;
9
+ }
10
+ __name(setPortalRoot, "setPortalRoot");
11
+ function getPortalRoot() {
12
+ return portalRoot && portalRoot.isConnected ? portalRoot : document.body;
13
+ }
14
+ __name(getPortalRoot, "getPortalRoot");
15
+ function resolveCanvasId(explicitId) {
16
+ return explicitId;
17
+ }
18
+ __name(resolveCanvasId, "resolveCanvasId");
19
+ function isDevHost(host, unstableHostPatterns) {
20
+ if (!host || host === "localhost" || host === "127.0.0.1" || host === "::1") return true;
21
+ if (unstableHostPatterns.some((re) => re.test(host))) return true;
22
+ return false;
23
+ }
24
+ __name(isDevHost, "isDevHost");
25
+ function isLiveCommentsHost(unstableHostPatterns = []) {
26
+ const host = typeof window !== "undefined" ? window.location.hostname : "";
27
+ return !isDevHost(host, unstableHostPatterns);
28
+ }
29
+ __name(isLiveCommentsHost, "isLiveCommentsHost");
30
+ const FONT$1 = 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif';
31
+ const cardStyle = {
32
+ width: 300,
33
+ background: "#fff",
34
+ border: "1px solid #e4e4e7",
35
+ borderRadius: 12,
36
+ boxShadow: "0 8px 28px rgba(0,0,0,0.18)",
37
+ fontFamily: FONT$1,
38
+ fontSize: 13,
39
+ color: "#1f1f23",
40
+ overflow: "hidden"
41
+ };
42
+ const textareaStyle = {
43
+ width: "100%",
44
+ minHeight: 56,
45
+ resize: "vertical",
46
+ border: "1px solid #e4e4e7",
47
+ borderRadius: 8,
48
+ padding: "8px 10px",
49
+ font: "inherit",
50
+ outline: "none",
51
+ boxSizing: "border-box"
52
+ };
53
+ const primaryBtn = {
54
+ font: "inherit",
55
+ fontWeight: 600,
56
+ border: "none",
57
+ borderRadius: 8,
58
+ padding: "7px 14px",
59
+ background: "linear-gradient(135deg,#cbd2de 0%,#868fa0 100%)",
60
+ color: "#16181b",
61
+ cursor: "pointer"
62
+ };
63
+ const ghostBtn = {
64
+ font: "inherit",
65
+ fontWeight: 600,
66
+ border: "1px solid #e4e4e7",
67
+ borderRadius: 8,
68
+ padding: "7px 12px",
69
+ background: "transparent",
70
+ color: "#3c3c43",
71
+ cursor: "pointer"
72
+ };
73
+ const linkBtn = {
74
+ font: "inherit",
75
+ fontWeight: 600,
76
+ border: "none",
77
+ background: "transparent",
78
+ color: "#9aa6bb",
79
+ cursor: "pointer",
80
+ padding: 0
81
+ };
82
+ function initials(name) {
83
+ const parts = name.trim().split(/\s+/).filter(Boolean);
84
+ if (parts.length === 0) return "?";
85
+ if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
86
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
87
+ }
88
+ __name(initials, "initials");
89
+ const AVATAR_BG = ["#2D7FFF", "#7A3FF2", "#1C8B4B", "#C8841C", "#C8203A", "#0E8C8C"];
90
+ function Avatar({ user, size = 24 }) {
91
+ const name = (user == null ? void 0 : user.displayName) || (user == null ? void 0 : user.username) || "?";
92
+ const hash = [...name].reduce((a, c) => a + c.charCodeAt(0), 0);
93
+ const bg = AVATAR_BG[hash % AVATAR_BG.length];
94
+ if (user == null ? void 0 : user.avatarUrl) {
95
+ return /* @__PURE__ */ jsx(
96
+ "img",
97
+ {
98
+ src: user.avatarUrl,
99
+ alt: name,
100
+ width: size,
101
+ height: size,
102
+ style: { width: size, height: size, borderRadius: "50%", objectFit: "cover", flex: "0 0 auto", alignSelf: "flex-start" }
103
+ }
104
+ );
105
+ }
106
+ return /* @__PURE__ */ jsx(
107
+ "div",
108
+ {
109
+ style: {
110
+ width: size,
111
+ height: size,
112
+ borderRadius: "50%",
113
+ background: bg,
114
+ color: "#fff",
115
+ fontSize: size * 0.42,
116
+ fontWeight: 700,
117
+ display: "flex",
118
+ alignItems: "center",
119
+ justifyContent: "center",
120
+ flex: "0 0 auto",
121
+ fontFamily: FONT$1
122
+ },
123
+ children: initials(name)
124
+ }
125
+ );
126
+ }
127
+ __name(Avatar, "Avatar");
128
+ function relativeTime(iso) {
129
+ const then = new Date(iso).getTime();
130
+ if (Number.isNaN(then)) return "";
131
+ const s = Math.max(0, Math.round((Date.now() - then) / 1e3));
132
+ if (s < 45) return "now";
133
+ const m = Math.round(s / 60);
134
+ if (m < 60) return `${m}m`;
135
+ const h = Math.round(m / 60);
136
+ if (h < 24) return `${h}h`;
137
+ const d = Math.round(h / 24);
138
+ if (d < 7) return `${d}d`;
139
+ return new Date(iso).toLocaleDateString(void 0, { month: "short", day: "numeric" });
140
+ }
141
+ __name(relativeTime, "relativeTime");
142
+ const DARK = "#18191b";
143
+ const PANEL_W = 340;
144
+ const PANEL_GAP = 16;
145
+ function simpleCopy(state) {
146
+ switch (state) {
147
+ case "inactive":
148
+ return {
149
+ title: "Comments are paused",
150
+ body: "This canvas’s plan no longer includes comments — they’re read-only until the subscription is renewed. Reviewers’ existing threads are safe.",
151
+ cta: "Renew to re-enable"
152
+ };
153
+ case "unauthorized":
154
+ default:
155
+ return {
156
+ title: "Sign in to comment",
157
+ body: "Your session has expired or the access key is invalid. Sign in again to comment on this canvas.",
158
+ cta: "Manage access"
159
+ };
160
+ }
161
+ }
162
+ __name(simpleCopy, "simpleCopy");
163
+ function agentPrompt(serviceUrl) {
164
+ const base = "Enable Carbon Canvas comments on this project. My canvas ID is <paste-your-canvas-ID-here>.";
165
+ if (serviceUrl) {
166
+ return `${base} Backend: ${serviceUrl}. Follow the instructions at ${serviceUrl}/skill.md to wire it up.`;
167
+ }
168
+ return `${base} Follow the Carbon Canvas skill (served at <your-backend>/skill.md) to wire it up.`;
169
+ }
170
+ __name(agentPrompt, "agentPrompt");
171
+ function CommentsUpsell({
172
+ state,
173
+ enableUrl,
174
+ serviceUrl,
175
+ onClose
176
+ }) {
177
+ const href = enableUrl || "#";
178
+ return /* @__PURE__ */ jsxs(
179
+ "div",
180
+ {
181
+ "data-cl-ui": true,
182
+ "data-cl-upsell": true,
183
+ style: {
184
+ position: "fixed",
185
+ top: 16,
186
+ right: PANEL_GAP,
187
+ width: PANEL_W,
188
+ zIndex: 2e3,
189
+ background: DARK,
190
+ borderRadius: 10,
191
+ boxShadow: "0 16px 48px rgba(0,0,0,0.4)",
192
+ display: "flex",
193
+ flexDirection: "column",
194
+ fontFamily: FONT$1,
195
+ color: "#f5f1e8",
196
+ overflow: "hidden"
197
+ },
198
+ onPointerDown: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onPointerDown"),
199
+ children: [
200
+ /* @__PURE__ */ jsxs(
201
+ "div",
202
+ {
203
+ style: {
204
+ padding: "12px 14px",
205
+ borderBottom: "1px solid rgba(255,255,255,0.08)",
206
+ display: "flex",
207
+ alignItems: "center",
208
+ gap: 8
209
+ },
210
+ children: [
211
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 16, fontWeight: 600, color: "#fff", letterSpacing: -0.2 }, children: "Comments" }),
212
+ /* @__PURE__ */ jsx(
213
+ "button",
214
+ {
215
+ type: "button",
216
+ "aria-label": "Close comments",
217
+ onClick: onClose,
218
+ style: {
219
+ marginLeft: "auto",
220
+ border: "none",
221
+ background: "transparent",
222
+ color: "rgba(245,241,232,0.7)",
223
+ cursor: "pointer",
224
+ fontSize: 18,
225
+ lineHeight: 1,
226
+ padding: 0
227
+ },
228
+ children: "×"
229
+ }
230
+ )
231
+ ]
232
+ }
233
+ ),
234
+ state === "locked" ? /* @__PURE__ */ jsx(LockedSteps, { href, serviceUrl }) : /* @__PURE__ */ jsx(SimpleUpsell, { state, href })
235
+ ]
236
+ }
237
+ );
238
+ }
239
+ __name(CommentsUpsell, "CommentsUpsell");
240
+ function LockedSteps({ href, serviceUrl }) {
241
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "16px 16px 18px", display: "flex", flexDirection: "column", gap: 14 }, children: [
242
+ /* @__PURE__ */ jsxs("div", { children: [
243
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 15, fontWeight: 600, color: "#fff" }, children: "Enable comments for your team" }),
244
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12.5, lineHeight: 1.5, color: "rgba(245,241,232,0.6)", marginTop: 6 }, children: "Comments live on your deployed prototype so your whole team can review in place. Two quick steps:" })
245
+ ] }),
246
+ /* @__PURE__ */ jsxs(Step, { n: 1, title: "Create a canvas", children: [
247
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 12.5, lineHeight: 1.5, color: "rgba(245,241,232,0.6)" }, children: [
248
+ "Sign in and create a canvas — you’ll get a canvas ID like ",
249
+ /* @__PURE__ */ jsx("code", { style: codeInline, children: "cnv_…" }),
250
+ "."
251
+ ] }),
252
+ /* @__PURE__ */ jsx(
253
+ "a",
254
+ {
255
+ href,
256
+ target: "_blank",
257
+ rel: "noopener noreferrer",
258
+ style: { ...ctaStyle, alignSelf: "flex-start", marginTop: 8 },
259
+ onClick: /* @__PURE__ */ __name((e) => {
260
+ if (href === "#") e.preventDefault();
261
+ }, "onClick"),
262
+ children: "Sign in & create a canvas →"
263
+ }
264
+ )
265
+ ] }),
266
+ /* @__PURE__ */ jsxs(Step, { n: 2, title: "Give the ID to your coding agent", children: [
267
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12.5, lineHeight: 1.5, color: "rgba(245,241,232,0.6)" }, children: "Paste this into Cursor, Claude Code, or whatever agent built this — with your ID dropped in:" }),
268
+ /* @__PURE__ */ jsx(CopyPrompt, { text: agentPrompt(serviceUrl) })
269
+ ] }),
270
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, lineHeight: 1.5, color: "rgba(245,241,232,0.4)", borderTop: "1px solid rgba(255,255,255,0.06)", paddingTop: 12 }, children: "Your agent follows the Carbon Canvas skill to wire it in and redeploy — then the Comments tab turns on for everyone who opens the prototype. The canvas and inspector stay free either way." })
271
+ ] });
272
+ }
273
+ __name(LockedSteps, "LockedSteps");
274
+ function Step({ n, title, children }) {
275
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 12 }, children: [
276
+ /* @__PURE__ */ jsx(
277
+ "div",
278
+ {
279
+ style: {
280
+ flex: "0 0 auto",
281
+ width: 22,
282
+ height: 22,
283
+ borderRadius: 11,
284
+ background: "rgba(154,166,187,0.15)",
285
+ color: "#7aa8ff",
286
+ fontSize: 12,
287
+ fontWeight: 700,
288
+ display: "flex",
289
+ alignItems: "center",
290
+ justifyContent: "center",
291
+ marginTop: 1
292
+ },
293
+ children: n
294
+ }
295
+ ),
296
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 6, minWidth: 0, flex: 1 }, children: [
297
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 13.5, fontWeight: 600, color: "#fff" }, children: title }),
298
+ children
299
+ ] })
300
+ ] });
301
+ }
302
+ __name(Step, "Step");
303
+ function CopyPrompt({ text }) {
304
+ const [copied, setCopied] = useState(false);
305
+ return /* @__PURE__ */ jsxs("div", { style: { marginTop: 8, position: "relative" }, children: [
306
+ /* @__PURE__ */ jsx(
307
+ "div",
308
+ {
309
+ style: {
310
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
311
+ fontSize: 11.5,
312
+ lineHeight: 1.5,
313
+ color: "rgba(245,241,232,0.85)",
314
+ background: "rgba(0,0,0,0.35)",
315
+ border: "1px solid rgba(255,255,255,0.08)",
316
+ borderRadius: 8,
317
+ padding: "10px 12px 34px",
318
+ whiteSpace: "pre-wrap",
319
+ wordBreak: "break-word"
320
+ },
321
+ children: text
322
+ }
323
+ ),
324
+ /* @__PURE__ */ jsx(
325
+ "button",
326
+ {
327
+ type: "button",
328
+ onClick: /* @__PURE__ */ __name(() => {
329
+ var _a;
330
+ void ((_a = navigator.clipboard) == null ? void 0 : _a.writeText(text).then(
331
+ () => {
332
+ setCopied(true);
333
+ window.setTimeout(() => setCopied(false), 1600);
334
+ },
335
+ () => {
336
+ }
337
+ ));
338
+ }, "onClick"),
339
+ style: {
340
+ position: "absolute",
341
+ right: 8,
342
+ bottom: 8,
343
+ border: "1px solid rgba(255,255,255,0.14)",
344
+ background: copied ? "rgba(154,166,187,0.25)" : "rgba(255,255,255,0.06)",
345
+ color: copied ? "#7aa8ff" : "rgba(245,241,232,0.8)",
346
+ borderRadius: 6,
347
+ padding: "3px 9px",
348
+ fontSize: 11,
349
+ fontWeight: 600,
350
+ fontFamily: FONT$1,
351
+ cursor: "pointer"
352
+ },
353
+ children: copied ? "Copied ✓" : "Copy"
354
+ }
355
+ )
356
+ ] });
357
+ }
358
+ __name(CopyPrompt, "CopyPrompt");
359
+ function SimpleUpsell({ state, href }) {
360
+ const { title, body, cta } = simpleCopy(state);
361
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "18px 16px", display: "flex", flexDirection: "column", gap: 12 }, children: [
362
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 15, fontWeight: 600, color: "#fff" }, children: title }),
363
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 13, lineHeight: 1.5, color: "rgba(245,241,232,0.72)" }, children: body }),
364
+ /* @__PURE__ */ jsx(
365
+ "a",
366
+ {
367
+ href,
368
+ target: "_blank",
369
+ rel: "noopener noreferrer",
370
+ style: { ...ctaStyle, alignSelf: "flex-start" },
371
+ onClick: /* @__PURE__ */ __name((e) => {
372
+ if (href === "#") e.preventDefault();
373
+ }, "onClick"),
374
+ children: cta
375
+ }
376
+ ),
377
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "rgba(245,241,232,0.4)" }, children: "The canvas and inspector stay free — this only turns on team comments." })
378
+ ] });
379
+ }
380
+ __name(SimpleUpsell, "SimpleUpsell");
381
+ const ctaStyle = {
382
+ display: "inline-block",
383
+ padding: "9px 14px",
384
+ borderRadius: 8,
385
+ background: "linear-gradient(135deg,#cbd2de 0%,#868fa0 100%)",
386
+ color: "#16181b",
387
+ fontSize: 13,
388
+ fontWeight: 600,
389
+ textDecoration: "none",
390
+ textAlign: "center"
391
+ };
392
+ const codeInline = {
393
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
394
+ fontSize: 11.5,
395
+ background: "rgba(255,255,255,0.08)",
396
+ borderRadius: 4,
397
+ padding: "1px 5px"
398
+ };
399
+ const LOCKED_FEATURES = {};
400
+ function trimTrailingSlashes(s) {
401
+ return s.replace(/\/+$/, "");
402
+ }
403
+ __name(trimTrailingSlashes, "trimTrailingSlashes");
404
+ function readStoredToken(backend) {
405
+ try {
406
+ const origin = backend ? new URL(backend).origin : window.location.origin;
407
+ return window.localStorage.getItem(`quilt-comments-token:${origin}`);
408
+ } catch {
409
+ return null;
410
+ }
411
+ }
412
+ __name(readStoredToken, "readStoredToken");
413
+ function useEntitlement(opts = {}) {
414
+ var _a, _b, _c, _d;
415
+ const { backend = "", canvasId, enabled = true } = opts;
416
+ const explicitToken = opts.token;
417
+ const [view, setView] = useState(null);
418
+ const [loading, setLoading] = useState(enabled);
419
+ const [error, setError] = useState(null);
420
+ const [nonce, setNonce] = useState(0);
421
+ const refresh = useCallback(() => setNonce((n) => n + 1), []);
422
+ useEffect(() => {
423
+ if (!enabled) {
424
+ setLoading(false);
425
+ setView(null);
426
+ setError(null);
427
+ return;
428
+ }
429
+ let cancelled = false;
430
+ const controller = new AbortController();
431
+ setLoading(true);
432
+ setError(null);
433
+ const base = trimTrailingSlashes(backend);
434
+ const token = explicitToken != null ? explicitToken : readStoredToken(base);
435
+ const qs = canvasId ? `?canvasId=${encodeURIComponent(canvasId)}` : "";
436
+ const headers = {};
437
+ if (token) headers.Authorization = `Bearer ${token}`;
438
+ fetch(`${base}/api/entitlements${qs}`, {
439
+ headers,
440
+ signal: controller.signal
441
+ // No credentials — bearer token only (matches networkClient.ts + open CORS).
442
+ }).then(async (res) => {
443
+ var _a2;
444
+ if (cancelled) return;
445
+ if (!res.ok) {
446
+ const data2 = await res.json().catch(() => ({}));
447
+ setView(null);
448
+ setError((_a2 = data2.error) != null ? _a2 : `http_${res.status}`);
449
+ return;
450
+ }
451
+ const data = await res.json();
452
+ setView(data);
453
+ }).catch((err) => {
454
+ if (cancelled || (err == null ? void 0 : err.name) === "AbortError") return;
455
+ setView(null);
456
+ setError("network_error");
457
+ }).finally(() => {
458
+ if (!cancelled) setLoading(false);
459
+ });
460
+ return () => {
461
+ cancelled = true;
462
+ controller.abort();
463
+ };
464
+ }, [backend, canvasId, explicitToken, enabled, nonce]);
465
+ const features = (_a = view == null ? void 0 : view.features) != null ? _a : LOCKED_FEATURES;
466
+ const can = useCallback(
467
+ (capability) => features[capability] === true,
468
+ [features]
469
+ );
470
+ return {
471
+ features,
472
+ tier: (_b = view == null ? void 0 : view.tier) != null ? _b : null,
473
+ status: (_c = view == null ? void 0 : view.status) != null ? _c : null,
474
+ canvasLimit: (_d = view == null ? void 0 : view.canvasLimit) != null ? _d : null,
475
+ loading,
476
+ error,
477
+ can,
478
+ refresh
479
+ };
480
+ }
481
+ __name(useEntitlement, "useEntitlement");
482
+ function deriveLockState(i) {
483
+ if (i.writeBlocked) return "inactive";
484
+ if (!i.configured) return "locked";
485
+ if (i.loading) return "loading";
486
+ if (i.comments) return "active";
487
+ if (i.error === "payment_required" || i.status === "past_due" || i.status === "canceled") {
488
+ return "inactive";
489
+ }
490
+ if (i.error === "unauthorized" || i.error === "http_401") return "unauthorized";
491
+ return "locked";
492
+ }
493
+ __name(deriveLockState, "deriveLockState");
494
+ function shouldMountRuntime(state) {
495
+ return state === "active" || state === "unauthorized";
496
+ }
497
+ __name(shouldMountRuntime, "shouldMountRuntime");
498
+ function shouldShowUpsell(state) {
499
+ return state === "locked" || state === "inactive";
500
+ }
501
+ __name(shouldShowUpsell, "shouldShowUpsell");
502
+ const CommentsRuntime = lazy(() => import("./CommentsRuntime-CRhC_WJU.js"));
503
+ function CommentLayer({
504
+ config,
505
+ mode,
506
+ setMode,
507
+ activePageId = null,
508
+ pages,
509
+ onSwitchPage,
510
+ onUnreadChange
511
+ }) {
512
+ const configured = !!config.devMode || !!config.serviceUrl;
513
+ const effectiveCanvasId = useMemo(() => resolveCanvasId(config.canvasId), [config.canvasId]);
514
+ const ent = useEntitlement({
515
+ backend: config.serviceUrl,
516
+ canvasId: effectiveCanvasId,
517
+ enabled: !config.devMode && configured
518
+ });
519
+ const [writeBlocked, setWriteBlocked] = useState(false);
520
+ useEffect(() => {
521
+ if (ent.can("comments")) setWriteBlocked(false);
522
+ }, [ent]);
523
+ const lockState = config.devMode ? "active" : deriveLockState({
524
+ loading: ent.loading,
525
+ error: ent.error,
526
+ comments: ent.can("comments"),
527
+ status: ent.status,
528
+ configured,
529
+ writeBlocked
530
+ });
531
+ const mountRuntime = config.devMode || shouldMountRuntime(lockState);
532
+ const showUpsell = !config.devMode && shouldShowUpsell(lockState);
533
+ useEffect(() => {
534
+ if (!mountRuntime) onUnreadChange == null ? void 0 : onUnreadChange(0);
535
+ }, [mountRuntime, onUnreadChange]);
536
+ if (mountRuntime) {
537
+ return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
538
+ CommentsRuntime,
539
+ {
540
+ config,
541
+ mode,
542
+ setMode,
543
+ activePageId,
544
+ pages,
545
+ onSwitchPage,
546
+ onUnreadChange,
547
+ onPaymentRequired: /* @__PURE__ */ __name(() => setWriteBlocked(true), "onPaymentRequired"),
548
+ onAuthChanged: ent.refresh
549
+ }
550
+ ) });
551
+ }
552
+ if (showUpsell && mode === "comment") {
553
+ return createPortal(
554
+ /* @__PURE__ */ jsx(
555
+ CommentsUpsell,
556
+ {
557
+ state: lockState,
558
+ enableUrl: config.enableUrl,
559
+ serviceUrl: config.serviceUrl,
560
+ onClose: /* @__PURE__ */ __name(() => setMode("cursor"), "onClose")
561
+ }
562
+ ),
563
+ getPortalRoot()
564
+ );
565
+ }
566
+ return null;
567
+ }
568
+ __name(CommentLayer, "CommentLayer");
569
+ const FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif';
570
+ const ACCENT = "linear-gradient(135deg,#cbd2de 0%,#868fa0 100%)";
571
+ function Seg({
572
+ active,
573
+ disabled,
574
+ title,
575
+ onClick,
576
+ icon,
577
+ label,
578
+ badge
579
+ }) {
580
+ return /* @__PURE__ */ jsxs(
581
+ "button",
582
+ {
583
+ type: "button",
584
+ disabled,
585
+ onClick,
586
+ title,
587
+ "aria-pressed": active,
588
+ style: {
589
+ display: "flex",
590
+ alignItems: "center",
591
+ gap: 7,
592
+ border: "none",
593
+ borderRadius: 8,
594
+ padding: "7px 11px",
595
+ font: `600 12.5px ${FONT}`,
596
+ cursor: disabled ? "default" : "pointer",
597
+ color: active ? "#16181b" : disabled ? "rgba(245,241,232,0.4)" : "rgba(245,241,232,0.82)",
598
+ background: active ? ACCENT : "transparent",
599
+ opacity: disabled ? 0.7 : 1,
600
+ transition: "background 120ms ease, color 120ms ease",
601
+ whiteSpace: "nowrap"
602
+ },
603
+ children: [
604
+ /* @__PURE__ */ jsxs("span", { style: { display: "flex", flexShrink: 0, position: "relative" }, children: [
605
+ icon,
606
+ badge && /* @__PURE__ */ jsx(
607
+ "span",
608
+ {
609
+ style: {
610
+ position: "absolute",
611
+ top: -3,
612
+ right: -4,
613
+ width: 7,
614
+ height: 7,
615
+ borderRadius: "50%",
616
+ background: "#9aa6bb",
617
+ boxShadow: "0 0 0 1.5px #18191b"
618
+ }
619
+ }
620
+ )
621
+ ] }),
622
+ label
623
+ ]
624
+ }
625
+ );
626
+ }
627
+ __name(Seg, "Seg");
628
+ const Divider = /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx(
629
+ "span",
630
+ {
631
+ style: {
632
+ width: 1,
633
+ alignSelf: "stretch",
634
+ margin: "5px 3px",
635
+ background: "rgba(255,255,255,0.1)",
636
+ flexShrink: 0
637
+ }
638
+ }
639
+ ), "Divider");
640
+ const ICON = {
641
+ page: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: [
642
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "1.5", width: "10", height: "11", rx: "1.6", stroke: "currentColor", strokeWidth: "1.3" }),
643
+ /* @__PURE__ */ jsx("path", { d: "M4.5 4.5h5M4.5 7h5M4.5 9.5h3", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
644
+ ] }),
645
+ cursor: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx(
646
+ "path",
647
+ {
648
+ d: "M2.5 1.8l8.3 4-3.4 1.1-1.1 3.4-3.8-8.5z",
649
+ fill: "currentColor",
650
+ stroke: "currentColor",
651
+ strokeWidth: "0.6",
652
+ strokeLinejoin: "round"
653
+ }
654
+ ) }),
655
+ comment: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx(
656
+ "path",
657
+ {
658
+ d: "M2 3.2A1.2 1.2 0 0 1 3.2 2h7.6A1.2 1.2 0 0 1 12 3.2v5A1.2 1.2 0 0 1 10.8 9.4H6l-2.8 2.3V9.4H3.2A1.2 1.2 0 0 1 2 8.2v-5z",
659
+ stroke: "currentColor",
660
+ strokeWidth: "1.3",
661
+ strokeLinejoin: "round"
662
+ }
663
+ ) }),
664
+ dev: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ jsx(
665
+ "path",
666
+ {
667
+ d: "M6 1v3M6 8v3M1 6h3M8 6h3M6 6l5 5",
668
+ stroke: "currentColor",
669
+ strokeWidth: "1.6",
670
+ strokeLinecap: "round"
671
+ }
672
+ ) })
673
+ };
674
+ const ZOOM_PRESETS = [1, 0.5, 0.25];
675
+ function ZoomControl() {
676
+ const [open, setOpen] = useState(false);
677
+ const [pct, setPct] = useState(100);
678
+ const ref = useRef(null);
679
+ const apiRef = useRef(null);
680
+ useEffect(() => {
681
+ let unsub = null;
682
+ let tries = 0;
683
+ let raf = 0;
684
+ const attach = /* @__PURE__ */ __name(() => {
685
+ var _a;
686
+ const vp = document.querySelector("[data-dc-viewport]");
687
+ const api = (_a = vp == null ? void 0 : vp.__dcViewport) != null ? _a : null;
688
+ if (api) {
689
+ apiRef.current = api;
690
+ unsub = api.subscribe((t) => setPct(Math.round(t.scale * 100)));
691
+ return;
692
+ }
693
+ if (tries++ < 60) raf = requestAnimationFrame(attach);
694
+ }, "attach");
695
+ attach();
696
+ return () => {
697
+ if (raf) cancelAnimationFrame(raf);
698
+ if (unsub) unsub();
699
+ };
700
+ }, []);
701
+ useEffect(() => {
702
+ if (!open) return;
703
+ const onDown = /* @__PURE__ */ __name((e) => {
704
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
705
+ }, "onDown");
706
+ const onKey = /* @__PURE__ */ __name((e) => {
707
+ if (e.key === "Escape") setOpen(false);
708
+ }, "onKey");
709
+ document.addEventListener("pointerdown", onDown, true);
710
+ document.addEventListener("keydown", onKey);
711
+ return () => {
712
+ document.removeEventListener("pointerdown", onDown, true);
713
+ document.removeEventListener("keydown", onKey);
714
+ };
715
+ }, [open]);
716
+ const zoomTo = /* @__PURE__ */ __name((scale) => {
717
+ const api = apiRef.current;
718
+ const vp = document.querySelector("[data-dc-viewport]");
719
+ if (!api || !vp) return;
720
+ const r = vp.getBoundingClientRect();
721
+ const cx = r.width / 2;
722
+ const cy = r.height / 2;
723
+ const t = api.getTransform();
724
+ const k = scale / t.scale;
725
+ api.animateTransform({ x: cx - (cx - t.x) * k, y: cy - (cy - t.y) * k, scale }, 200);
726
+ setOpen(false);
727
+ }, "zoomTo");
728
+ return /* @__PURE__ */ jsxs("div", { ref, style: { position: "relative", display: "flex" }, children: [
729
+ /* @__PURE__ */ jsxs(
730
+ "button",
731
+ {
732
+ type: "button",
733
+ onClick: /* @__PURE__ */ __name(() => setOpen((o) => !o), "onClick"),
734
+ title: "Zoom",
735
+ style: {
736
+ display: "flex",
737
+ alignItems: "center",
738
+ gap: 6,
739
+ border: "none",
740
+ borderRadius: 8,
741
+ padding: "7px 9px 7px 11px",
742
+ font: `600 12.5px ${FONT}`,
743
+ cursor: "pointer",
744
+ color: "rgba(245,241,232,0.82)",
745
+ background: open ? "rgba(255,255,255,0.08)" : "transparent",
746
+ transition: "background 120ms ease, color 120ms ease",
747
+ whiteSpace: "nowrap",
748
+ fontVariantNumeric: "tabular-nums",
749
+ minWidth: 56,
750
+ justifyContent: "space-between"
751
+ },
752
+ children: [
753
+ /* @__PURE__ */ jsxs("span", { children: [
754
+ pct,
755
+ "%"
756
+ ] }),
757
+ /* @__PURE__ */ jsx(
758
+ "svg",
759
+ {
760
+ width: "9",
761
+ height: "9",
762
+ viewBox: "0 0 11 11",
763
+ fill: "none",
764
+ stroke: "currentColor",
765
+ strokeWidth: "1.8",
766
+ strokeLinecap: "round",
767
+ style: { opacity: 0.6 },
768
+ children: /* @__PURE__ */ jsx("path", { d: open ? "M2 7l3.5-3.5L9 7" : "M2 4l3.5 3.5L9 4" })
769
+ }
770
+ )
771
+ ]
772
+ }
773
+ ),
774
+ open && /* @__PURE__ */ jsx(
775
+ "div",
776
+ {
777
+ style: {
778
+ position: "absolute",
779
+ bottom: "100%",
780
+ left: 0,
781
+ marginBottom: 8,
782
+ background: "#202123",
783
+ borderRadius: 10,
784
+ boxShadow: "0 10px 32px rgba(0,0,0,0.4)",
785
+ padding: 4,
786
+ minWidth: 120,
787
+ zIndex: 10
788
+ },
789
+ children: ZOOM_PRESETS.map((z) => {
790
+ const zp = Math.round(z * 100);
791
+ const isActive = zp === pct;
792
+ return /* @__PURE__ */ jsxs(
793
+ "button",
794
+ {
795
+ type: "button",
796
+ onClick: /* @__PURE__ */ __name(() => zoomTo(z), "onClick"),
797
+ style: {
798
+ display: "flex",
799
+ alignItems: "center",
800
+ justifyContent: "space-between",
801
+ width: "100%",
802
+ textAlign: "left",
803
+ border: "none",
804
+ cursor: "pointer",
805
+ background: isActive ? "rgba(255,255,255,0.1)" : "transparent",
806
+ color: "#fff",
807
+ padding: "8px 10px",
808
+ borderRadius: 6,
809
+ font: `${isActive ? 600 : 400} 13px ${FONT}`,
810
+ fontVariantNumeric: "tabular-nums"
811
+ },
812
+ onMouseEnter: /* @__PURE__ */ __name((e) => {
813
+ if (!isActive) e.currentTarget.style.background = "rgba(255,255,255,0.05)";
814
+ }, "onMouseEnter"),
815
+ onMouseLeave: /* @__PURE__ */ __name((e) => {
816
+ if (!isActive) e.currentTarget.style.background = "transparent";
817
+ }, "onMouseLeave"),
818
+ children: [
819
+ zp,
820
+ "%"
821
+ ]
822
+ },
823
+ z
824
+ );
825
+ })
826
+ }
827
+ )
828
+ ] });
829
+ }
830
+ __name(ZoomControl, "ZoomControl");
831
+ function PageSwitcher({
832
+ pages,
833
+ activePageId,
834
+ setActivePage,
835
+ fallbackName
836
+ }) {
837
+ const [open, setOpen] = useState(false);
838
+ const ref = useRef(null);
839
+ const multi = !!(pages && pages.length > 1);
840
+ const active = pages == null ? void 0 : pages.find((p) => p.id === activePageId);
841
+ const label = active ? active.title : fallbackName;
842
+ useEffect(() => {
843
+ if (!open) return;
844
+ const onDown = /* @__PURE__ */ __name((e) => {
845
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
846
+ }, "onDown");
847
+ const onKey = /* @__PURE__ */ __name((e) => {
848
+ if (e.key === "Escape") setOpen(false);
849
+ }, "onKey");
850
+ document.addEventListener("pointerdown", onDown, true);
851
+ document.addEventListener("keydown", onKey);
852
+ return () => {
853
+ document.removeEventListener("pointerdown", onDown, true);
854
+ document.removeEventListener("keydown", onKey);
855
+ };
856
+ }, [open]);
857
+ return /* @__PURE__ */ jsxs("div", { ref, style: { position: "relative", display: "flex" }, children: [
858
+ /* @__PURE__ */ jsxs(
859
+ "button",
860
+ {
861
+ type: "button",
862
+ disabled: !multi,
863
+ onClick: /* @__PURE__ */ __name(() => multi && setOpen((o) => !o), "onClick"),
864
+ title: multi ? "Switch page" : `Page · ${label}`,
865
+ style: {
866
+ display: "flex",
867
+ alignItems: "center",
868
+ gap: 7,
869
+ border: "none",
870
+ borderRadius: 8,
871
+ padding: "7px 11px",
872
+ font: `600 12.5px ${FONT}`,
873
+ cursor: multi ? "pointer" : "default",
874
+ color: multi ? "rgba(245,241,232,0.82)" : "rgba(245,241,232,0.5)",
875
+ background: open ? "rgba(255,255,255,0.08)" : "transparent",
876
+ transition: "background 120ms ease, color 120ms ease",
877
+ whiteSpace: "nowrap"
878
+ },
879
+ children: [
880
+ /* @__PURE__ */ jsx("span", { style: { display: "flex", flexShrink: 0 }, children: ICON.page }),
881
+ label,
882
+ multi && /* @__PURE__ */ jsx(
883
+ "svg",
884
+ {
885
+ width: "9",
886
+ height: "9",
887
+ viewBox: "0 0 11 11",
888
+ fill: "none",
889
+ stroke: "currentColor",
890
+ strokeWidth: "1.8",
891
+ strokeLinecap: "round",
892
+ style: { opacity: 0.6, marginLeft: -1 },
893
+ children: /* @__PURE__ */ jsx("path", { d: open ? "M2 7l3.5-3.5L9 7" : "M2 4l3.5 3.5L9 4" })
894
+ }
895
+ )
896
+ ]
897
+ }
898
+ ),
899
+ open && multi && pages && /* @__PURE__ */ jsx(
900
+ "div",
901
+ {
902
+ style: {
903
+ position: "absolute",
904
+ bottom: "100%",
905
+ left: 0,
906
+ marginBottom: 8,
907
+ background: "#202123",
908
+ borderRadius: 10,
909
+ boxShadow: "0 10px 32px rgba(0,0,0,0.4)",
910
+ padding: 4,
911
+ minWidth: 180,
912
+ zIndex: 10
913
+ },
914
+ children: pages.map((p) => {
915
+ const isActive = p.id === activePageId;
916
+ return /* @__PURE__ */ jsxs(
917
+ "button",
918
+ {
919
+ type: "button",
920
+ onClick: /* @__PURE__ */ __name(() => {
921
+ setActivePage == null ? void 0 : setActivePage(p.id);
922
+ setOpen(false);
923
+ }, "onClick"),
924
+ style: {
925
+ display: "flex",
926
+ alignItems: "center",
927
+ gap: 8,
928
+ width: "100%",
929
+ textAlign: "left",
930
+ border: "none",
931
+ cursor: "pointer",
932
+ background: isActive ? "rgba(255,255,255,0.1)" : "transparent",
933
+ color: "#fff",
934
+ padding: "8px 10px",
935
+ borderRadius: 6,
936
+ font: `${isActive ? 600 : 400} 13px ${FONT}`
937
+ },
938
+ onMouseEnter: /* @__PURE__ */ __name((e) => {
939
+ if (!isActive) e.currentTarget.style.background = "rgba(255,255,255,0.05)";
940
+ }, "onMouseEnter"),
941
+ onMouseLeave: /* @__PURE__ */ __name((e) => {
942
+ if (!isActive) e.currentTarget.style.background = "transparent";
943
+ }, "onMouseLeave"),
944
+ children: [
945
+ /* @__PURE__ */ jsx("span", { style: { display: "flex", flexShrink: 0, opacity: isActive ? 1 : 0.6 }, children: ICON.page }),
946
+ p.title
947
+ ]
948
+ },
949
+ p.id
950
+ );
951
+ })
952
+ }
953
+ )
954
+ ] });
955
+ }
956
+ __name(PageSwitcher, "PageSwitcher");
957
+ function ModeBar({
958
+ mode,
959
+ setMode,
960
+ hasComments,
961
+ hasInspector,
962
+ commentUnread = 0,
963
+ canvasLabel,
964
+ pages,
965
+ activePageId,
966
+ setActivePage
967
+ }) {
968
+ const pageName = canvasLabel ? canvasLabel.split(":").pop() || "Canvas" : "Canvas";
969
+ const shell = {
970
+ position: "fixed",
971
+ left: 16,
972
+ bottom: 16,
973
+ zIndex: 2001,
974
+ display: "flex",
975
+ alignItems: "center",
976
+ gap: 2,
977
+ padding: 4,
978
+ background: "#18191b",
979
+ borderRadius: 12,
980
+ border: "1px solid rgba(255,255,255,0.12)",
981
+ boxShadow: "0 6px 22px rgba(0,0,0,0.32)",
982
+ fontFamily: FONT
983
+ };
984
+ return /* @__PURE__ */ jsxs("div", { "data-dc-modebar": true, style: shell, onPointerDown: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onPointerDown"), children: [
985
+ /* @__PURE__ */ jsx(ZoomControl, {}),
986
+ /* @__PURE__ */ jsx(Divider, {}),
987
+ /* @__PURE__ */ jsx(
988
+ PageSwitcher,
989
+ {
990
+ pages,
991
+ activePageId,
992
+ setActivePage,
993
+ fallbackName: pageName
994
+ }
995
+ ),
996
+ /* @__PURE__ */ jsx(Divider, {}),
997
+ /* @__PURE__ */ jsx(
998
+ Seg,
999
+ {
1000
+ title: "Cursor — interact with the prototypes, pan & zoom",
1001
+ active: mode === "cursor",
1002
+ onClick: /* @__PURE__ */ __name(() => setMode("cursor"), "onClick"),
1003
+ icon: ICON.cursor,
1004
+ label: "Cursor"
1005
+ }
1006
+ ),
1007
+ hasComments && /* @__PURE__ */ jsx(
1008
+ Seg,
1009
+ {
1010
+ title: "Comment — drop pins and open the comments sidebar",
1011
+ active: mode === "comment",
1012
+ onClick: /* @__PURE__ */ __name(() => setMode("comment"), "onClick"),
1013
+ icon: ICON.comment,
1014
+ label: "Comment",
1015
+ badge: commentUnread > 0 && mode !== "comment"
1016
+ }
1017
+ ),
1018
+ hasInspector && /* @__PURE__ */ jsx(
1019
+ Seg,
1020
+ {
1021
+ title: "Dev — inspect components, tokens & accessibility",
1022
+ active: mode === "dev",
1023
+ onClick: /* @__PURE__ */ __name(() => setMode("dev"), "onClick"),
1024
+ icon: ICON.dev,
1025
+ label: "Dev"
1026
+ }
1027
+ )
1028
+ ] });
1029
+ }
1030
+ __name(ModeBar, "ModeBar");
1031
+ function getComponentName(type) {
1032
+ if (!type) return null;
1033
+ if (typeof type === "string") return null;
1034
+ if (typeof type === "function") return type.displayName || type.name || null;
1035
+ if (type.$$typeof && type.render) {
1036
+ return type.displayName || type.render.displayName || type.render.name || null;
1037
+ }
1038
+ if (type.$$typeof && type.type) {
1039
+ return type.displayName || getComponentName(type.type);
1040
+ }
1041
+ return null;
1042
+ }
1043
+ __name(getComponentName, "getComponentName");
1044
+ const genericAdapter = {
1045
+ libraryLabel: "Library",
1046
+ libraryShortLabel: "Lib",
1047
+ // No design system connected → nothing is ever a library instance.
1048
+ isLibraryComponent(_type) {
1049
+ return false;
1050
+ },
1051
+ componentName(type) {
1052
+ return getComponentName(type);
1053
+ },
1054
+ classify(type) {
1055
+ return { kind: "user", name: getComponentName(type) };
1056
+ },
1057
+ // No icon library to match against.
1058
+ findIconName(_fiber) {
1059
+ return null;
1060
+ },
1061
+ iconWrapperSize(_fiber) {
1062
+ return null;
1063
+ },
1064
+ iconImportSnippet(name) {
1065
+ return `import { ${name} } from 'icons';`;
1066
+ },
1067
+ iconLabel: "Icon",
1068
+ tokens: {
1069
+ // Empty prefix → builders harvest no CSS vars → inspector shows raw values.
1070
+ color: { cssVarPrefix: "" },
1071
+ shadow: { cssVarPrefix: "" },
1072
+ spacing: { cssVarPrefix: "" },
1073
+ radius: { cssVarPrefix: "" },
1074
+ typography: {
1075
+ // No utility-class convention known → never matches a typography token.
1076
+ classMatcher: /* @__PURE__ */ __name(() => [], "classMatcher")
1077
+ },
1078
+ // No utility-class convention known. Empty prefixes are guarded by the core
1079
+ // so they don't match every class.
1080
+ utilityClassPrefixes: {
1081
+ typography: "",
1082
+ text: "",
1083
+ bg: "",
1084
+ border: ""
1085
+ }
1086
+ }
1087
+ };
1088
+ let activeAdapter = genericAdapter;
1089
+ const adapterChangeListeners = [];
1090
+ function onDesignSystemAdapterChange(fn) {
1091
+ adapterChangeListeners.push(fn);
1092
+ }
1093
+ __name(onDesignSystemAdapterChange, "onDesignSystemAdapterChange");
1094
+ function setDesignSystemAdapter(adapter) {
1095
+ if (adapter === activeAdapter) return;
1096
+ activeAdapter = adapter;
1097
+ for (const fn of adapterChangeListeners) fn(adapter);
1098
+ }
1099
+ __name(setDesignSystemAdapter, "setDesignSystemAdapter");
1100
+ function getDesignSystemAdapter() {
1101
+ return activeAdapter;
1102
+ }
1103
+ __name(getDesignSystemAdapter, "getDesignSystemAdapter");
1104
+ function adapterHasTokens(adapter = activeAdapter) {
1105
+ const t = adapter.tokens;
1106
+ return Boolean(
1107
+ t.color.cssVarPrefix || t.shadow.cssVarPrefix || t.spacing.cssVarPrefix || t.radius.cssVarPrefix
1108
+ );
1109
+ }
1110
+ __name(adapterHasTokens, "adapterHasTokens");
1111
+ const Inspector = React.lazy(() => import("./Inspector-D9W1Ie5_.js"));
1112
+ const DC = {
1113
+ bg: "#18191b",
1114
+ grid: "rgba(255,255,255,0.55)",
1115
+ label: "rgba(233,234,237,0.6)",
1116
+ title: "rgba(240,241,244,0.92)",
1117
+ subtitle: "rgba(233,234,237,0.5)",
1118
+ postitBg: "#fef4a8",
1119
+ postitText: "#5a4a2a",
1120
+ font: '-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif'
1121
+ };
1122
+ function ensureDcStyles() {
1123
+ if (typeof document === "undefined" || document.getElementById("dc-styles")) return;
1124
+ const s = document.createElement("style");
1125
+ s.id = "dc-styles";
1126
+ s.textContent = [
1127
+ ".dc-editable{cursor:text;outline:none;white-space:nowrap;border-radius:3px;padding:0 2px;margin:0 -2px}",
1128
+ ".dc-editable:focus{background:rgba(255,255,255,.1);color:#fff;box-shadow:0 0 0 1.5px #9aa6bb}",
1129
+ // Keyboard-only focus ring for hierarchy-tree rows (roving tabindex); no ring
1130
+ // on mouse focus so click-to-select stays visually unchanged.
1131
+ "[data-dc-treeitem]:focus{outline:none}",
1132
+ "[data-dc-treeitem]:focus-visible{outline:2px solid #9aa6bb;outline-offset:-2px}",
1133
+ // An ENGAGED artboard (clicked into; owns wheel/scroll until you click out)
1134
+ // gets a subtle accent ring so it is clear where scroll/zoom will land.
1135
+ "[data-dc-slot][data-dc-engaged] .dc-card{box-shadow:0 0 0 1.5px var(--quilt-accent,#9aa6bb),0 6px 24px rgba(154,166,187,.16)}",
1136
+ ".dc-card{transition:box-shadow .15s,transform .15s}",
1137
+ ".dc-card *{scrollbar-width:none}",
1138
+ ".dc-card *::-webkit-scrollbar{display:none}",
1139
+ ".dc-labelrow{display:flex;align-items:center;gap:4px;height:24px}",
1140
+ ".dc-labeltext{cursor:pointer;border-radius:4px;padding:3px 6px;display:flex;align-items:center;transition:background .12s}",
1141
+ ".dc-labeltext:hover{background:rgba(255,255,255,.08)}",
1142
+ ".dc-labeltext.dc-selected{background:rgba(154,166,187,.12)}",
1143
+ ".dc-share-pop{position:absolute;bottom:100%;left:-4px;margin-bottom:32px;z-index:5;display:flex;align-items:center;",
1144
+ " background:#202123;color:#f5f1e8;border-radius:7px;padding:5px 6px;gap:2px;",
1145
+ " box-shadow:0 6px 24px rgba(0,0,0,.28);white-space:nowrap}",
1146
+ ".dc-share-pop button{display:flex;align-items:center;gap:6px;border:none;background:transparent;color:inherit;",
1147
+ ' cursor:pointer;font:500 12px/1 -apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif;padding:5px 8px;border-radius:5px;transition:background .12s}',
1148
+ ".dc-share-pop button:hover{background:rgba(255,255,255,.12)}",
1149
+ ".dc-expand{position:absolute;bottom:100%;right:0;margin-bottom:5px;z-index:2;opacity:0;transition:opacity .12s,background .12s;",
1150
+ " width:22px;height:22px;border-radius:5px;border:none;cursor:pointer;padding:0;",
1151
+ " background:transparent;color:rgba(233,234,237,.55);display:flex;align-items:center;justify-content:center}",
1152
+ ".dc-expand:hover{background:rgba(255,255,255,.08);color:#fff}",
1153
+ "[data-dc-slot]:hover .dc-expand{opacity:1}"
1154
+ ].join("\n");
1155
+ document.head.appendChild(s);
1156
+ }
1157
+ __name(ensureDcStyles, "ensureDcStyles");
1158
+ const DCCtx = React.createContext(null);
1159
+ const DCViewportCtx = React.createContext(null);
1160
+ const DCArtboardActiveCtx = React.createContext(true);
1161
+ const useArtboardActive = /* @__PURE__ */ __name(() => React.useContext(DCArtboardActiveCtx), "useArtboardActive");
1162
+ function DesignCanvas({
1163
+ children,
1164
+ minScale,
1165
+ maxScale,
1166
+ minActiveScale,
1167
+ style,
1168
+ inspector = false,
1169
+ comments = false,
1170
+ adapter
1171
+ }) {
1172
+ React.useState(() => {
1173
+ ensureDcStyles();
1174
+ if (adapter) setDesignSystemAdapter(adapter);
1175
+ return null;
1176
+ });
1177
+ React.useEffect(() => {
1178
+ if (adapter) setDesignSystemAdapter(adapter);
1179
+ }, [adapter]);
1180
+ const [state, setState] = React.useState({ sections: {}, focus: null, selected: null });
1181
+ const [mode, setMode] = React.useState("cursor");
1182
+ const [commentUnread, setCommentUnread] = React.useState(0);
1183
+ const pages = [];
1184
+ React.Children.forEach(children, (p) => {
1185
+ var _a, _b;
1186
+ if (!React.isValidElement(p) || p.type !== DCPage) return;
1187
+ const pp = p.props;
1188
+ const pid = (_a = pp.id) != null ? _a : pp.title;
1189
+ if (!pid) return;
1190
+ pages.push({ id: pid, title: (_b = pp.title) != null ? _b : pid, children: pp.children });
1191
+ });
1192
+ const paged = pages.length > 0;
1193
+ const [activePageId, setActivePageId] = React.useState(() => {
1194
+ if (!paged) return null;
1195
+ const ids = pages.map((p) => p.id);
1196
+ let fromUrl = null;
1197
+ if (typeof window !== "undefined") {
1198
+ fromUrl = new URLSearchParams(window.location.search).get("page");
1199
+ }
1200
+ return fromUrl && ids.includes(fromUrl) ? fromUrl : pages[0].id;
1201
+ });
1202
+ React.useEffect(() => {
1203
+ if (paged && !pages.some((p) => p.id === activePageId)) {
1204
+ setActivePageId(pages[0].id);
1205
+ }
1206
+ }, [paged, pages.map((p) => p.id).join("|"), activePageId]);
1207
+ const switchPage = React.useCallback((id) => {
1208
+ setActivePageId(id);
1209
+ setState((s) => ({ ...s, focus: null }));
1210
+ if (typeof window !== "undefined" && window.history) {
1211
+ const url = new URL(window.location.href);
1212
+ url.searchParams.set("page", id);
1213
+ window.history.replaceState(null, "", url);
1214
+ }
1215
+ }, []);
1216
+ const activePage = paged ? pages.find((p) => p.id === activePageId) || pages[0] : null;
1217
+ const activeChildren = activePage ? activePage.children : children;
1218
+ const commentsConfig = comments && comments.canvasId ? comments : null;
1219
+ const hasComments = !!commentsConfig;
1220
+ const pageList = React.useMemo(
1221
+ () => paged ? pages.map((p) => ({ id: p.id, title: p.title })) : void 0,
1222
+ [paged, pages]
1223
+ );
1224
+ const registry = {};
1225
+ const sectionMeta = {};
1226
+ const sectionOrder = [];
1227
+ React.Children.forEach(activeChildren, (sec) => {
1228
+ var _a, _b;
1229
+ if (!React.isValidElement(sec) || sec.type !== DCSection) return;
1230
+ const sp = sec.props;
1231
+ const sid = (_a = sp.id) != null ? _a : sp.title;
1232
+ if (!sid) return;
1233
+ sectionOrder.push(sid);
1234
+ const persisted = state.sections[sid] || {};
1235
+ const srcIds = [];
1236
+ React.Children.forEach(sp.children, (ab) => {
1237
+ var _a2;
1238
+ if (!React.isValidElement(ab) || ab.type !== DCArtboard) return;
1239
+ const abp = ab.props;
1240
+ const aid = (_a2 = abp.id) != null ? _a2 : abp.label;
1241
+ if (!aid) return;
1242
+ registry[`${sid}/${aid}`] = { sectionId: sid, artboard: ab };
1243
+ srcIds.push(aid);
1244
+ });
1245
+ const kept = (persisted.order || []).filter((k) => srcIds.includes(k));
1246
+ sectionMeta[sid] = {
1247
+ title: (_b = persisted.title) != null ? _b : sp.title,
1248
+ subtitle: sp.subtitle,
1249
+ slotIds: [...kept, ...srcIds.filter((k) => !kept.includes(k))]
1250
+ };
1251
+ });
1252
+ const api = React.useMemo(() => ({
1253
+ state,
1254
+ section: /* @__PURE__ */ __name((id) => state.sections[id] || {}, "section"),
1255
+ patchSection: /* @__PURE__ */ __name((id, p) => setState((s) => ({
1256
+ ...s,
1257
+ sections: { ...s.sections, [id]: { ...s.sections[id], ...typeof p === "function" ? p(s.sections[id] || {}) : p } }
1258
+ })), "patchSection"),
1259
+ setFocus: /* @__PURE__ */ __name((slotId) => setState((s) => ({ ...s, focus: slotId })), "setFocus"),
1260
+ setSelected: /* @__PURE__ */ __name((slotId) => setState((s) => s.selected === slotId ? s : { ...s, selected: slotId }), "setSelected"),
1261
+ activePageId
1262
+ }), [state, activePageId]);
1263
+ React.useEffect(() => {
1264
+ const onKey = /* @__PURE__ */ __name((e) => {
1265
+ if (e.key === "Escape") setState((s) => s.focus || s.selected ? { ...s, focus: null, selected: null } : s);
1266
+ }, "onKey");
1267
+ const onPd = /* @__PURE__ */ __name((e) => {
1268
+ const ae = document.activeElement;
1269
+ if (ae && ae.isContentEditable && !ae.contains(e.target)) ae.blur();
1270
+ if (!(e.target instanceof Element) || !e.target.closest(".dc-labelrow, .dc-share-pop")) {
1271
+ setState((s) => s.selected ? { ...s, selected: null } : s);
1272
+ }
1273
+ }, "onPd");
1274
+ document.addEventListener("keydown", onKey);
1275
+ document.addEventListener("pointerdown", onPd, true);
1276
+ return () => {
1277
+ document.removeEventListener("keydown", onKey);
1278
+ document.removeEventListener("pointerdown", onPd, true);
1279
+ };
1280
+ }, []);
1281
+ const artboardDeeplinkDone = React.useRef(false);
1282
+ React.useEffect(() => {
1283
+ if (artboardDeeplinkDone.current || typeof window === "undefined") return;
1284
+ artboardDeeplinkDone.current = true;
1285
+ const aid = new URLSearchParams(window.location.search).get("artboard");
1286
+ if (!aid) return;
1287
+ let tries = 0;
1288
+ const tick = /* @__PURE__ */ __name(() => {
1289
+ const el = document.querySelector(`[data-dc-slot="${CSS.escape(aid)}"]`);
1290
+ if (el) {
1291
+ panToSlot(el);
1292
+ const sec = el.closest("[data-dc-section]");
1293
+ if (sec) setState((s) => ({ ...s, selected: `${sec.getAttribute("data-dc-section")}/${aid}` }));
1294
+ return;
1295
+ }
1296
+ if (tries++ < 40) requestAnimationFrame(tick);
1297
+ }, "tick");
1298
+ requestAnimationFrame(tick);
1299
+ }, []);
1300
+ const hasSections = React.Children.toArray(activeChildren).some(
1301
+ (c) => React.isValidElement(c) && c.type === DCSection
1302
+ );
1303
+ return /* @__PURE__ */ jsxs(DCCtx.Provider, { value: api, children: [
1304
+ /* @__PURE__ */ jsx(DCViewport, { minScale, maxScale, minActiveScale, style, mode, fitKey: paged ? activePageId : null, children: paged && !hasSections ? /* @__PURE__ */ jsx(DCEmptyPage, { title: activePage == null ? void 0 : activePage.title }) : activeChildren }),
1305
+ state.focus && registry[state.focus] && /* @__PURE__ */ jsx(DCFocusOverlay, { entry: registry[state.focus], sectionMeta, sectionOrder }),
1306
+ inspector && /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(Inspector, { mode, setMode }) }),
1307
+ commentsConfig && /* @__PURE__ */ jsx(
1308
+ CommentLayer,
1309
+ {
1310
+ config: commentsConfig,
1311
+ mode,
1312
+ setMode,
1313
+ activePageId: paged ? activePageId : null,
1314
+ pages: pageList,
1315
+ onSwitchPage: switchPage,
1316
+ onUnreadChange: setCommentUnread
1317
+ }
1318
+ ),
1319
+ (inspector || hasComments) && /* @__PURE__ */ jsx(
1320
+ ModeBar,
1321
+ {
1322
+ mode,
1323
+ setMode,
1324
+ hasComments,
1325
+ hasInspector: !!inspector,
1326
+ commentUnread,
1327
+ canvasLabel: commentsConfig ? commentsConfig.canvasId : void 0,
1328
+ pages: paged ? pages.map((p) => ({ id: p.id, title: p.title })) : void 0,
1329
+ activePageId,
1330
+ setActivePage: switchPage
1331
+ }
1332
+ )
1333
+ ] });
1334
+ }
1335
+ __name(DesignCanvas, "DesignCanvas");
1336
+ function DCPage(_props) {
1337
+ return null;
1338
+ }
1339
+ __name(DCPage, "DCPage");
1340
+ function panToSlot(slotEl) {
1341
+ const vpEl = document.querySelector("[data-dc-viewport]");
1342
+ if (!vpEl) return;
1343
+ const api = vpEl.__dcViewport;
1344
+ if (!api) return;
1345
+ const vpRect = vpEl.getBoundingClientRect();
1346
+ const tf = api.getTransform();
1347
+ const r = slotEl.getBoundingClientRect();
1348
+ if (r.width === 0 && r.height === 0) return;
1349
+ const worldW = r.width / tf.scale;
1350
+ const worldH = r.height / tf.scale;
1351
+ const worldCx = (r.left + r.width / 2 - vpRect.left - tf.x) / tf.scale;
1352
+ const worldCy = (r.top + r.height / 2 - vpRect.top - tf.y) / tf.scale;
1353
+ const scale = Math.min(2, Math.max(
1354
+ 0.15,
1355
+ Math.min((vpRect.width - 160) / worldW, (vpRect.height - 200) / worldH)
1356
+ ));
1357
+ api.animateTransform({
1358
+ x: vpRect.width / 2 - worldCx * scale,
1359
+ y: vpRect.height / 2 - worldCy * scale,
1360
+ scale
1361
+ }, 320);
1362
+ }
1363
+ __name(panToSlot, "panToSlot");
1364
+ function DCEmptyPage({ title }) {
1365
+ return /* @__PURE__ */ jsxs("div", { style: {
1366
+ position: "absolute",
1367
+ inset: 0,
1368
+ display: "flex",
1369
+ flexDirection: "column",
1370
+ alignItems: "center",
1371
+ justifyContent: "center",
1372
+ gap: 8,
1373
+ pointerEvents: "none",
1374
+ fontFamily: DC.font,
1375
+ color: DC.subtitle
1376
+ }, children: [
1377
+ /* @__PURE__ */ jsxs("svg", { width: "40", height: "40", viewBox: "0 0 24 24", fill: "none", style: { opacity: 0.5 }, children: [
1378
+ /* @__PURE__ */ jsx("rect", { x: "4", y: "3", width: "16", height: "18", rx: "2", stroke: "currentColor", strokeWidth: "1.4" }),
1379
+ /* @__PURE__ */ jsx("path", { d: "M8 8h8M8 12h8M8 16h4", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" })
1380
+ ] }),
1381
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 18, fontWeight: 600, color: DC.title }, children: title }),
1382
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 14 }, children: "Nothing here yet — add artboards in code." })
1383
+ ] });
1384
+ }
1385
+ __name(DCEmptyPage, "DCEmptyPage");
1386
+ function DCViewport({
1387
+ children,
1388
+ minScale = 0.1,
1389
+ maxScale = 8,
1390
+ minActiveScale = 0.35,
1391
+ style = {},
1392
+ mode = "cursor",
1393
+ fitKey = null
1394
+ }) {
1395
+ const vpRef = React.useRef(null);
1396
+ const worldRef = React.useRef(null);
1397
+ const tf = React.useRef({ x: 0, y: 0, scale: 1 });
1398
+ const subsRef = React.useRef(/* @__PURE__ */ new Set());
1399
+ const engagedRef = React.useRef(null);
1400
+ const apply = React.useCallback(() => {
1401
+ const { x, y, scale } = tf.current;
1402
+ const el = worldRef.current;
1403
+ if (el) el.style.transform = `translate3d(${x}px, ${y}px, 0) scale(${scale})`;
1404
+ subsRef.current.forEach((fn) => fn(tf.current));
1405
+ }, []);
1406
+ const setTransform = React.useCallback((next) => {
1407
+ const t = tf.current;
1408
+ t.x = next.x;
1409
+ t.y = next.y;
1410
+ t.scale = Math.min(maxScale, Math.max(minScale, next.scale));
1411
+ apply();
1412
+ }, [apply, minScale, maxScale]);
1413
+ const tweenTokenRef = React.useRef(0);
1414
+ const animateTransform = React.useCallback((next, ms = 240) => {
1415
+ const start = { ...tf.current };
1416
+ const target = {
1417
+ x: next.x,
1418
+ y: next.y,
1419
+ scale: Math.min(maxScale, Math.max(minScale, next.scale))
1420
+ };
1421
+ const token = ++tweenTokenRef.current;
1422
+ const t0 = performance.now();
1423
+ const step = /* @__PURE__ */ __name((now) => {
1424
+ if (token !== tweenTokenRef.current) return;
1425
+ const u = Math.min(1, (now - t0) / ms);
1426
+ const k = 1 - Math.pow(1 - u, 3);
1427
+ tf.current.x = start.x + (target.x - start.x) * k;
1428
+ tf.current.y = start.y + (target.y - start.y) * k;
1429
+ tf.current.scale = start.scale + (target.scale - start.scale) * k;
1430
+ apply();
1431
+ if (u < 1) requestAnimationFrame(step);
1432
+ }, "step");
1433
+ requestAnimationFrame(step);
1434
+ }, [apply, minScale, maxScale]);
1435
+ const fitToContent = React.useCallback((ms = 260) => {
1436
+ const vp = vpRef.current;
1437
+ if (!vp) return;
1438
+ const vpRect = vp.getBoundingClientRect();
1439
+ if (!vpRect.width || !vpRect.height) return;
1440
+ const t = tf.current;
1441
+ const slots = Array.from(vp.querySelectorAll("[data-dc-slot]")).filter((el) => {
1442
+ const parent = el.parentElement;
1443
+ return !!(parent && parent.closest && parent.closest("[data-dc-viewport]") === vp);
1444
+ });
1445
+ if (!slots.length) return;
1446
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
1447
+ for (const el of slots) {
1448
+ const r = el.getBoundingClientRect();
1449
+ const x = (r.left - vpRect.left - t.x) / t.scale;
1450
+ const y = (r.top - vpRect.top - t.y) / t.scale;
1451
+ minX = Math.min(minX, x);
1452
+ minY = Math.min(minY, y);
1453
+ maxX = Math.max(maxX, x + r.width / t.scale);
1454
+ maxY = Math.max(maxY, y + r.height / t.scale);
1455
+ }
1456
+ const bw = maxX - minX, bh = maxY - minY;
1457
+ if (!(bw > 0) || !(bh > 0)) return;
1458
+ const margin = 0.88;
1459
+ const cap = Math.min(maxScale, 1);
1460
+ const scale = Math.min(cap, Math.max(minScale, Math.min(vpRect.width / bw, vpRect.height / bh) * margin));
1461
+ const cx = (minX + maxX) / 2, cy = (minY + maxY) / 2;
1462
+ animateTransform({ x: vpRect.width / 2 - cx * scale, y: vpRect.height / 2 - cy * scale, scale }, ms);
1463
+ }, [animateTransform, minScale, maxScale]);
1464
+ const fitKeyRef = React.useRef(null);
1465
+ React.useEffect(() => {
1466
+ if (fitKey == null) return;
1467
+ if (fitKeyRef.current === null) {
1468
+ fitKeyRef.current = fitKey;
1469
+ return;
1470
+ }
1471
+ if (fitKeyRef.current === fitKey) return;
1472
+ fitKeyRef.current = fitKey;
1473
+ let raf2 = 0;
1474
+ const raf1 = requestAnimationFrame(() => {
1475
+ raf2 = requestAnimationFrame(() => fitToContent());
1476
+ });
1477
+ return () => {
1478
+ cancelAnimationFrame(raf1);
1479
+ if (raf2) cancelAnimationFrame(raf2);
1480
+ };
1481
+ }, [fitKey, fitToContent]);
1482
+ const viewportCtx = React.useMemo(() => ({
1483
+ subscribe: /* @__PURE__ */ __name((fn) => {
1484
+ subsRef.current.add(fn);
1485
+ fn(tf.current);
1486
+ return () => {
1487
+ subsRef.current.delete(fn);
1488
+ };
1489
+ }, "subscribe"),
1490
+ getTransform: /* @__PURE__ */ __name(() => tf.current, "getTransform"),
1491
+ setTransform,
1492
+ animateTransform,
1493
+ minActiveScale
1494
+ }), [minActiveScale, setTransform, animateTransform]);
1495
+ React.useEffect(() => {
1496
+ const vp = vpRef.current;
1497
+ if (!vp) return;
1498
+ vp.__dcViewport = viewportCtx;
1499
+ return () => {
1500
+ if (vp.__dcViewport === viewportCtx) delete vp.__dcViewport;
1501
+ };
1502
+ }, [viewportCtx]);
1503
+ React.useEffect(() => {
1504
+ const vp = vpRef.current;
1505
+ if (!vp) return;
1506
+ const zoomAt = /* @__PURE__ */ __name((cx, cy, factor) => {
1507
+ const r = vp.getBoundingClientRect();
1508
+ const px = cx - r.left, py = cy - r.top;
1509
+ const t = tf.current;
1510
+ const next = Math.min(maxScale, Math.max(minScale, t.scale * factor));
1511
+ const k = next / t.scale;
1512
+ t.x = px - (px - t.x) * k;
1513
+ t.y = py - (py - t.y) * k;
1514
+ t.scale = next;
1515
+ apply();
1516
+ }, "zoomAt");
1517
+ const interacting = { drag: false, gesture: false };
1518
+ let wheelTimer = null;
1519
+ const syncInteractingAttr = /* @__PURE__ */ __name(() => {
1520
+ const active = interacting.drag || interacting.gesture || wheelTimer !== null;
1521
+ if (active) vp.setAttribute("data-dc-interacting", "true");
1522
+ else vp.removeAttribute("data-dc-interacting");
1523
+ }, "syncInteractingAttr");
1524
+ const pulseWheelInteracting = /* @__PURE__ */ __name(() => {
1525
+ if (wheelTimer) clearTimeout(wheelTimer);
1526
+ wheelTimer = setTimeout(() => {
1527
+ wheelTimer = null;
1528
+ syncInteractingAttr();
1529
+ }, 150);
1530
+ syncInteractingAttr();
1531
+ }, "pulseWheelInteracting");
1532
+ const isMouseWheel = /* @__PURE__ */ __name((e) => e.deltaMode !== 0 || e.deltaX === 0 && Number.isInteger(e.deltaY) && Math.abs(e.deltaY) >= 40, "isMouseWheel");
1533
+ const canNativelyScroll = /* @__PURE__ */ __name((e) => {
1534
+ if (e.ctrlKey || isMouseWheel(e)) return false;
1535
+ const target = e.target;
1536
+ if (!target || !target.closest) return false;
1537
+ let el = target.closest("[data-dc-allow-scroll]");
1538
+ while (el) {
1539
+ const cs = getComputedStyle(el);
1540
+ const overY = cs.overflowY;
1541
+ const scrollableY = (overY === "auto" || overY === "scroll") && el.scrollHeight > el.clientHeight;
1542
+ if (scrollableY) {
1543
+ const dy = e.deltaY;
1544
+ const atTop = el.scrollTop <= 0 && dy < 0;
1545
+ const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1 && dy > 0;
1546
+ if (!atTop && !atBottom) return true;
1547
+ }
1548
+ el = el.parentElement ? el.parentElement.closest("[data-dc-allow-scroll]") : null;
1549
+ }
1550
+ return false;
1551
+ }, "canNativelyScroll");
1552
+ const isScrollableInDir = /* @__PURE__ */ __name((el, e) => {
1553
+ if (!el || el === vp || !el.getBoundingClientRect) return false;
1554
+ const cs = getComputedStyle(el);
1555
+ const dy = e.deltaY, dx = e.deltaX;
1556
+ if (dy !== 0 && (cs.overflowY === "auto" || cs.overflowY === "scroll") && el.scrollHeight > el.clientHeight) {
1557
+ const atTop = el.scrollTop <= 0 && dy < 0;
1558
+ const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1 && dy > 0;
1559
+ if (!atTop && !atBottom) return true;
1560
+ }
1561
+ if (dx !== 0 && (cs.overflowX === "auto" || cs.overflowX === "scroll") && el.scrollWidth > el.clientWidth) {
1562
+ const atLeft = el.scrollLeft <= 0 && dx < 0;
1563
+ const atRight = el.scrollLeft + el.clientWidth >= el.scrollWidth - 1 && dx > 0;
1564
+ if (!atLeft && !atRight) return true;
1565
+ }
1566
+ return false;
1567
+ }, "isScrollableInDir");
1568
+ const wheelConsumableWithin = /* @__PURE__ */ __name((root, e) => {
1569
+ let el = e.target;
1570
+ while (el && el.nodeType === 1) {
1571
+ if (el.matches && el.matches("[data-dc-viewport]") && el !== vp) return true;
1572
+ if (el !== root && isScrollableInDir(el, e)) return true;
1573
+ if (el === root) break;
1574
+ el = el.parentElement;
1575
+ }
1576
+ return false;
1577
+ }, "wheelConsumableWithin");
1578
+ const onWheel = /* @__PURE__ */ __name((e) => {
1579
+ if (canNativelyScroll(e)) return;
1580
+ const eng = engagedRef.current;
1581
+ if (eng && eng.isConnected && eng.contains(e.target) && wheelConsumableWithin(eng, e)) return;
1582
+ e.preventDefault();
1583
+ e.stopPropagation();
1584
+ if (isGesturing) return;
1585
+ if (e.ctrlKey) {
1586
+ zoomAt(e.clientX, e.clientY, Math.exp(-e.deltaY * 0.01));
1587
+ } else if (isMouseWheel(e)) {
1588
+ zoomAt(e.clientX, e.clientY, Math.exp(-Math.sign(e.deltaY) * 0.18));
1589
+ } else {
1590
+ tf.current.x -= e.deltaX;
1591
+ tf.current.y -= e.deltaY;
1592
+ apply();
1593
+ }
1594
+ pulseWheelInteracting();
1595
+ }, "onWheel");
1596
+ let gsBase = 1;
1597
+ let isGesturing = false;
1598
+ const onGestureStart = /* @__PURE__ */ __name((e) => {
1599
+ e.preventDefault();
1600
+ isGesturing = true;
1601
+ gsBase = tf.current.scale;
1602
+ interacting.gesture = true;
1603
+ syncInteractingAttr();
1604
+ }, "onGestureStart");
1605
+ const onGestureChange = /* @__PURE__ */ __name((e) => {
1606
+ e.preventDefault();
1607
+ const g = e;
1608
+ zoomAt(g.clientX, g.clientY, gsBase * g.scale / tf.current.scale);
1609
+ }, "onGestureChange");
1610
+ const onGestureEnd = /* @__PURE__ */ __name((e) => {
1611
+ e.preventDefault();
1612
+ isGesturing = false;
1613
+ interacting.gesture = false;
1614
+ syncInteractingAttr();
1615
+ }, "onGestureEnd");
1616
+ let drag = null;
1617
+ const onPointerDown = /* @__PURE__ */ __name((e) => {
1618
+ const onBg = !e.target.closest("[data-dc-slot], .dc-editable");
1619
+ if (!(e.button === 1 || e.button === 0 && onBg)) return;
1620
+ e.preventDefault();
1621
+ vp.setPointerCapture(e.pointerId);
1622
+ drag = { id: e.pointerId, lx: e.clientX, ly: e.clientY };
1623
+ vp.style.cursor = "grabbing";
1624
+ interacting.drag = true;
1625
+ syncInteractingAttr();
1626
+ }, "onPointerDown");
1627
+ const onPointerMove = /* @__PURE__ */ __name((e) => {
1628
+ if (!drag || e.pointerId !== drag.id) return;
1629
+ tf.current.x += e.clientX - drag.lx;
1630
+ tf.current.y += e.clientY - drag.ly;
1631
+ drag.lx = e.clientX;
1632
+ drag.ly = e.clientY;
1633
+ apply();
1634
+ }, "onPointerMove");
1635
+ const onPointerUp = /* @__PURE__ */ __name((e) => {
1636
+ if (!drag || e.pointerId !== drag.id) return;
1637
+ vp.releasePointerCapture(e.pointerId);
1638
+ drag = null;
1639
+ vp.style.cursor = "";
1640
+ interacting.drag = false;
1641
+ syncInteractingAttr();
1642
+ }, "onPointerUp");
1643
+ const slotConsumable = /* @__PURE__ */ __name((slot) => {
1644
+ if (!slot) return false;
1645
+ if (slot.querySelector("[data-dc-viewport]")) return true;
1646
+ for (const el of Array.from(slot.querySelectorAll("*"))) {
1647
+ const overflowsY = el.scrollHeight > el.clientHeight;
1648
+ const overflowsX = el.scrollWidth > el.clientWidth;
1649
+ if (!overflowsY && !overflowsX) continue;
1650
+ const cs = getComputedStyle(el);
1651
+ if (overflowsY && (cs.overflowY === "auto" || cs.overflowY === "scroll")) return true;
1652
+ if (overflowsX && (cs.overflowX === "auto" || cs.overflowX === "scroll")) return true;
1653
+ }
1654
+ return false;
1655
+ }, "slotConsumable");
1656
+ const setEngaged = /* @__PURE__ */ __name((slot) => {
1657
+ const next = slot && slotConsumable(slot) ? slot : null;
1658
+ if (next === engagedRef.current) return;
1659
+ if (engagedRef.current) engagedRef.current.removeAttribute("data-dc-engaged");
1660
+ engagedRef.current = next;
1661
+ if (next) next.setAttribute("data-dc-engaged", "");
1662
+ }, "setEngaged");
1663
+ const slotForThisCanvas = /* @__PURE__ */ __name((target) => {
1664
+ let el = target;
1665
+ while (el && el !== vp && el.nodeType === 1) {
1666
+ if (el.matches && el.matches("[data-dc-slot]")) {
1667
+ const parent = el.parentElement;
1668
+ const nearestVp = parent && parent.closest ? parent.closest("[data-dc-viewport]") : null;
1669
+ if (nearestVp === vp) return el;
1670
+ }
1671
+ el = el.parentElement;
1672
+ }
1673
+ return null;
1674
+ }, "slotForThisCanvas");
1675
+ const onEngageDown = /* @__PURE__ */ __name((e) => {
1676
+ setEngaged(slotForThisCanvas(e.target));
1677
+ }, "onEngageDown");
1678
+ vp.addEventListener("wheel", onWheel, { passive: false, capture: true });
1679
+ vp.addEventListener("pointerdown", onEngageDown, true);
1680
+ vp.addEventListener("gesturestart", onGestureStart, { passive: false });
1681
+ vp.addEventListener("gesturechange", onGestureChange, { passive: false });
1682
+ vp.addEventListener("gestureend", onGestureEnd, { passive: false });
1683
+ vp.addEventListener("pointerdown", onPointerDown);
1684
+ vp.addEventListener("pointermove", onPointerMove);
1685
+ vp.addEventListener("pointerup", onPointerUp);
1686
+ vp.addEventListener("pointercancel", onPointerUp);
1687
+ return () => {
1688
+ vp.removeEventListener("wheel", onWheel, { capture: true });
1689
+ vp.removeEventListener("pointerdown", onEngageDown, true);
1690
+ vp.removeEventListener("gesturestart", onGestureStart);
1691
+ vp.removeEventListener("gesturechange", onGestureChange);
1692
+ vp.removeEventListener("gestureend", onGestureEnd);
1693
+ vp.removeEventListener("pointerdown", onPointerDown);
1694
+ vp.removeEventListener("pointermove", onPointerMove);
1695
+ vp.removeEventListener("pointerup", onPointerUp);
1696
+ vp.removeEventListener("pointercancel", onPointerUp);
1697
+ if (engagedRef.current) {
1698
+ engagedRef.current.removeAttribute("data-dc-engaged");
1699
+ engagedRef.current = null;
1700
+ }
1701
+ if (wheelTimer) {
1702
+ clearTimeout(wheelTimer);
1703
+ wheelTimer = null;
1704
+ }
1705
+ vp.removeAttribute("data-dc-interacting");
1706
+ };
1707
+ }, [apply, minScale, maxScale]);
1708
+ const gridSvg = `url("data:image/svg+xml,%3Csvg width='80' height='80' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M37 40h6M40 37v6' fill='none' stroke='${encodeURIComponent(DC.grid)}' stroke-width='0.75'/%3E%3C/svg%3E")`;
1709
+ return /* @__PURE__ */ jsx(
1710
+ "div",
1711
+ {
1712
+ ref: vpRef,
1713
+ className: "design-canvas",
1714
+ "data-dc-viewport": true,
1715
+ "data-dc-mode": mode,
1716
+ style: {
1717
+ height: "100vh",
1718
+ width: "100vw",
1719
+ background: DC.bg,
1720
+ overflow: "hidden",
1721
+ overscrollBehavior: "none",
1722
+ touchAction: "none",
1723
+ position: "relative",
1724
+ fontFamily: DC.font,
1725
+ boxSizing: "border-box",
1726
+ ...style
1727
+ },
1728
+ children: /* @__PURE__ */ jsxs(
1729
+ "div",
1730
+ {
1731
+ ref: worldRef,
1732
+ style: {
1733
+ position: "absolute",
1734
+ top: 0,
1735
+ left: 0,
1736
+ transformOrigin: "0 0",
1737
+ willChange: "transform",
1738
+ width: "max-content",
1739
+ minWidth: "100%",
1740
+ minHeight: "100%",
1741
+ padding: "60px 0 80px"
1742
+ },
1743
+ children: [
1744
+ /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: -6e3, backgroundImage: gridSvg, backgroundSize: "80px 80px", pointerEvents: "none", zIndex: -1 } }),
1745
+ /* @__PURE__ */ jsx(DCViewportCtx.Provider, { value: viewportCtx, children })
1746
+ ]
1747
+ }
1748
+ )
1749
+ }
1750
+ );
1751
+ }
1752
+ __name(DCViewport, "DCViewport");
1753
+ function DCSection({ id, title, subtitle, children, gap = 48 }) {
1754
+ var _a;
1755
+ const ctx = React.useContext(DCCtx);
1756
+ const sid = id != null ? id : title;
1757
+ const all = React.Children.toArray(children);
1758
+ const artboards = all.filter(
1759
+ (c) => React.isValidElement(c) && c.type === DCArtboard
1760
+ );
1761
+ const rest = all.filter((c) => !(React.isValidElement(c) && c.type === DCArtboard));
1762
+ const srcOrder = artboards.map((a) => {
1763
+ var _a2;
1764
+ return (_a2 = a.props.id) != null ? _a2 : a.props.label;
1765
+ });
1766
+ const sec = (ctx && sid ? ctx.section(sid) : void 0) || {};
1767
+ const order = React.useMemo(() => {
1768
+ const kept = (sec.order || []).filter((k) => srcOrder.includes(k));
1769
+ return [...kept, ...srcOrder.filter((k) => !kept.includes(k))];
1770
+ }, [sec.order, srcOrder.join("|")]);
1771
+ const byId = Object.fromEntries(
1772
+ artboards.map((a) => {
1773
+ var _a2;
1774
+ return [(_a2 = a.props.id) != null ? _a2 : a.props.label, a];
1775
+ })
1776
+ );
1777
+ return /* @__PURE__ */ jsxs("div", { "data-dc-section": sid, style: { marginBottom: 80, position: "relative" }, children: [
1778
+ /* @__PURE__ */ jsxs("div", { style: { padding: "0 60px 56px" }, children: [
1779
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 28, fontWeight: 600, color: DC.title, letterSpacing: -0.4, marginBottom: 6, display: "inline-block" }, children: (_a = sec.title) != null ? _a : title }),
1780
+ subtitle && /* @__PURE__ */ jsx("div", { style: { fontSize: 16, color: DC.subtitle }, children: subtitle })
1781
+ ] }),
1782
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap, padding: "0 60px", alignItems: "flex-start", width: "max-content" }, children: order.map((k) => {
1783
+ var _a2;
1784
+ return /* @__PURE__ */ jsx(
1785
+ DCArtboardFrame,
1786
+ {
1787
+ sectionId: sid,
1788
+ artboard: byId[k],
1789
+ label: (_a2 = (sec.labels || {})[k]) != null ? _a2 : byId[k].props.label,
1790
+ selected: !!ctx && ctx.state.selected === `${sid}/${k}`,
1791
+ onSelect: /* @__PURE__ */ __name(() => {
1792
+ if (ctx) ctx.setSelected(`${sid}/${k}`);
1793
+ }, "onSelect"),
1794
+ activePageId: ctx ? ctx.activePageId : null,
1795
+ onRename: /* @__PURE__ */ __name((v) => {
1796
+ if (ctx) ctx.patchSection(sid, (x) => ({ labels: { ...x.labels, [k]: v } }));
1797
+ }, "onRename"),
1798
+ onFocus: /* @__PURE__ */ __name(() => {
1799
+ if (ctx) ctx.setFocus(`${sid}/${k}`);
1800
+ }, "onFocus")
1801
+ },
1802
+ k
1803
+ );
1804
+ }) }),
1805
+ rest
1806
+ ] });
1807
+ }
1808
+ __name(DCSection, "DCSection");
1809
+ function DCArtboard(_props) {
1810
+ return null;
1811
+ }
1812
+ __name(DCArtboard, "DCArtboard");
1813
+ function DCArtboardFrame({ sectionId: _sectionId, artboard, label, selected, onSelect, activePageId, onRename, onFocus }) {
1814
+ const { id: rawId, label: rawLabel, width = 260, height = 480, children, style = {}, contain = false } = artboard.props;
1815
+ const id = rawId != null ? rawId : rawLabel;
1816
+ const ref = React.useRef(null);
1817
+ const viewport = React.useContext(DCViewportCtx);
1818
+ const [active, setActive] = React.useState(true);
1819
+ React.useEffect(() => {
1820
+ if (!viewport) return;
1821
+ let last = true;
1822
+ const check = /* @__PURE__ */ __name((t) => {
1823
+ const el = ref.current;
1824
+ if (!el) return;
1825
+ const tooSmall = t.scale < viewport.minActiveScale;
1826
+ const r = el.getBoundingClientRect();
1827
+ const m = 400;
1828
+ const inView = r.right > -m && r.left < window.innerWidth + m && r.bottom > -m && r.top < window.innerHeight + m;
1829
+ const next = inView && !tooSmall;
1830
+ if (next !== last) {
1831
+ last = next;
1832
+ setActive(next);
1833
+ }
1834
+ }, "check");
1835
+ const unsub = viewport.subscribe(check);
1836
+ const onResize = /* @__PURE__ */ __name(() => check(viewport.getTransform()), "onResize");
1837
+ window.addEventListener("resize", onResize);
1838
+ return () => {
1839
+ unsub();
1840
+ window.removeEventListener("resize", onResize);
1841
+ };
1842
+ }, [viewport]);
1843
+ const [editing, setEditing] = React.useState(false);
1844
+ const labelRef = React.useRef(null);
1845
+ const startRename = /* @__PURE__ */ __name(() => {
1846
+ setEditing(true);
1847
+ requestAnimationFrame(() => {
1848
+ const el = labelRef.current;
1849
+ if (!el) return;
1850
+ el.focus();
1851
+ const range = document.createRange();
1852
+ range.selectNodeContents(el);
1853
+ const sel = window.getSelection();
1854
+ sel == null ? void 0 : sel.removeAllRanges();
1855
+ sel == null ? void 0 : sel.addRange(range);
1856
+ });
1857
+ }, "startRename");
1858
+ const commitRename = /* @__PURE__ */ __name(() => {
1859
+ var _a;
1860
+ setEditing(false);
1861
+ if (labelRef.current) onRename((_a = labelRef.current.textContent) != null ? _a : "");
1862
+ }, "commitRename");
1863
+ const [copied, setCopied] = React.useState(false);
1864
+ const copyShareLink = /* @__PURE__ */ __name(() => {
1865
+ var _a;
1866
+ const url = new URL(window.location.href);
1867
+ url.searchParams.delete("pin");
1868
+ if (activePageId) url.searchParams.set("page", activePageId);
1869
+ url.searchParams.set("artboard", id);
1870
+ (_a = navigator.clipboard) == null ? void 0 : _a.writeText(url.toString());
1871
+ setCopied(true);
1872
+ setTimeout(() => setCopied(false), 1500);
1873
+ }, "copyShareLink");
1874
+ React.useEffect(() => {
1875
+ if (!selected) setCopied(false);
1876
+ }, [selected]);
1877
+ return /* @__PURE__ */ jsxs("div", { ref, "data-dc-slot": id, style: { position: "relative", flexShrink: 0 }, children: [
1878
+ /* @__PURE__ */ jsx("div", { className: "dc-labelrow", style: { position: "absolute", bottom: "100%", left: -4, marginBottom: 4, color: DC.label }, children: /* @__PURE__ */ jsx(
1879
+ "div",
1880
+ {
1881
+ className: `dc-labeltext${selected ? " dc-selected" : ""}`,
1882
+ onClick: /* @__PURE__ */ __name(() => !editing && onSelect(), "onClick"),
1883
+ onDoubleClick: startRename,
1884
+ title: editing ? void 0 : "Click to select · double-click to rename",
1885
+ children: /* @__PURE__ */ jsx(
1886
+ "span",
1887
+ {
1888
+ ref: labelRef,
1889
+ className: "dc-editable",
1890
+ contentEditable: editing,
1891
+ suppressContentEditableWarning: true,
1892
+ onPointerDown: /* @__PURE__ */ __name((e) => {
1893
+ if (editing) e.stopPropagation();
1894
+ }, "onPointerDown"),
1895
+ onBlur: /* @__PURE__ */ __name(() => editing && commitRename(), "onBlur"),
1896
+ onKeyDown: /* @__PURE__ */ __name((e) => {
1897
+ if (e.key === "Enter") {
1898
+ e.preventDefault();
1899
+ e.currentTarget.blur();
1900
+ }
1901
+ }, "onKeyDown"),
1902
+ style: { fontSize: 15, fontWeight: 500, color: selected ? "#9aa6bb" : DC.label, lineHeight: 1, cursor: editing ? "text" : "pointer" },
1903
+ children: label
1904
+ }
1905
+ )
1906
+ }
1907
+ ) }),
1908
+ selected && /* @__PURE__ */ jsx("div", { className: "dc-share-pop", onPointerDown: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onPointerDown"), children: /* @__PURE__ */ jsxs("button", { onClick: copyShareLink, children: [
1909
+ copied ? /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "#7ed3a0", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M2 6.5L4.8 9.2 10 3.5" }) }) : /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", children: [
1910
+ /* @__PURE__ */ jsx("path", { d: "M5 7a2.4 2.4 0 0 0 3.4.2l1.8-1.8a2.4 2.4 0 0 0-3.4-3.4l-1 1" }),
1911
+ /* @__PURE__ */ jsx("path", { d: "M7 5a2.4 2.4 0 0 0-3.4-.2L1.8 6.6A2.4 2.4 0 0 0 5.2 10l1-1" })
1912
+ ] }),
1913
+ copied ? "Link copied" : "Copy link to artboard"
1914
+ ] }) }),
1915
+ /* @__PURE__ */ jsx("button", { className: "dc-expand", onClick: onFocus, onPointerDown: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onPointerDown"), title: "Focus", children: /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", children: /* @__PURE__ */ jsx("path", { d: "M7 1h4v4M5 11H1V7M11 1L7.5 4.5M1 11l3.5-3.5" }) }) }),
1916
+ /* @__PURE__ */ jsx(
1917
+ "div",
1918
+ {
1919
+ className: "dc-card",
1920
+ "data-vt-window": contain ? "" : void 0,
1921
+ style: {
1922
+ borderRadius: 2,
1923
+ boxShadow: "0 1px 3px rgba(0,0,0,.08),0 4px 16px rgba(0,0,0,.06)",
1924
+ overflow: "hidden",
1925
+ width,
1926
+ height,
1927
+ background: "#fff",
1928
+ ...contain ? { contain: "layout" } : {},
1929
+ ...style,
1930
+ ...selected ? { outline: "2px solid #9aa6bb", outlineOffset: 2 } : null
1931
+ },
1932
+ children: /* @__PURE__ */ jsx(DCArtboardActiveCtx.Provider, { value: active, children: children || /* @__PURE__ */ jsx("div", { style: { height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "#bbb", fontSize: 13, fontFamily: DC.font }, children: id }) })
1933
+ }
1934
+ )
1935
+ ] });
1936
+ }
1937
+ __name(DCArtboardFrame, "DCArtboardFrame");
1938
+ function DCFocusOverlay({ entry, sectionMeta, sectionOrder }) {
1939
+ var _a, _b;
1940
+ const ctx = React.useContext(DCCtx);
1941
+ const { sectionId, artboard } = entry;
1942
+ const sec = ctx.section(sectionId);
1943
+ const meta = sectionMeta[sectionId];
1944
+ const peers = meta.slotIds;
1945
+ const aid = (_a = artboard.props.id) != null ? _a : artboard.props.label;
1946
+ const idx = peers.indexOf(aid);
1947
+ const secIdx = sectionOrder.indexOf(sectionId);
1948
+ const go = /* @__PURE__ */ __name((d) => {
1949
+ const n = peers[(idx + d + peers.length) % peers.length];
1950
+ if (n) ctx.setFocus(`${sectionId}/${n}`);
1951
+ }, "go");
1952
+ const goSection = /* @__PURE__ */ __name((d) => {
1953
+ const ns = sectionOrder[(secIdx + d + sectionOrder.length) % sectionOrder.length];
1954
+ const first = sectionMeta[ns] && sectionMeta[ns].slotIds[0];
1955
+ if (first) ctx.setFocus(`${ns}/${first}`);
1956
+ }, "goSection");
1957
+ React.useEffect(() => {
1958
+ const k = /* @__PURE__ */ __name((e) => {
1959
+ if (e.key === "ArrowLeft") {
1960
+ e.preventDefault();
1961
+ go(-1);
1962
+ }
1963
+ if (e.key === "ArrowRight") {
1964
+ e.preventDefault();
1965
+ go(1);
1966
+ }
1967
+ if (e.key === "ArrowUp") {
1968
+ e.preventDefault();
1969
+ goSection(-1);
1970
+ }
1971
+ if (e.key === "ArrowDown") {
1972
+ e.preventDefault();
1973
+ goSection(1);
1974
+ }
1975
+ }, "k");
1976
+ document.addEventListener("keydown", k);
1977
+ return () => document.removeEventListener("keydown", k);
1978
+ });
1979
+ const { width = 260, height = 480, children } = artboard.props;
1980
+ const [vp, setVp] = React.useState({ w: window.innerWidth, h: window.innerHeight });
1981
+ React.useEffect(() => {
1982
+ const r = /* @__PURE__ */ __name(() => setVp({ w: window.innerWidth, h: window.innerHeight }), "r");
1983
+ window.addEventListener("resize", r);
1984
+ return () => window.removeEventListener("resize", r);
1985
+ }, []);
1986
+ const scale = Math.max(0.1, Math.min((vp.w - 200) / width, (vp.h - 260) / height, 2));
1987
+ const [ddOpen, setDd] = React.useState(false);
1988
+ const Arrow = /* @__PURE__ */ __name(({ dir, onClick }) => /* @__PURE__ */ jsx(
1989
+ "button",
1990
+ {
1991
+ onClick: /* @__PURE__ */ __name((e) => {
1992
+ e.stopPropagation();
1993
+ onClick();
1994
+ }, "onClick"),
1995
+ style: {
1996
+ position: "absolute",
1997
+ top: "50%",
1998
+ [dir]: 28,
1999
+ transform: "translateY(-50%)",
2000
+ border: "none",
2001
+ background: "rgba(255,255,255,.08)",
2002
+ color: "rgba(255,255,255,.9)",
2003
+ width: 44,
2004
+ height: 44,
2005
+ borderRadius: 22,
2006
+ fontSize: 18,
2007
+ cursor: "pointer",
2008
+ display: "flex",
2009
+ alignItems: "center",
2010
+ justifyContent: "center",
2011
+ transition: "background .15s"
2012
+ },
2013
+ onMouseEnter: /* @__PURE__ */ __name((e) => e.currentTarget.style.background = "rgba(255,255,255,.18)", "onMouseEnter"),
2014
+ onMouseLeave: /* @__PURE__ */ __name((e) => e.currentTarget.style.background = "rgba(255,255,255,.08)", "onMouseLeave"),
2015
+ children: /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ jsx("path", { d: dir === "left" ? "M11 3L5 9l6 6" : "M7 3l6 6-6 6" }) })
2016
+ }
2017
+ ), "Arrow");
2018
+ return createPortal(
2019
+ /* @__PURE__ */ jsxs(
2020
+ "div",
2021
+ {
2022
+ onClick: /* @__PURE__ */ __name(() => ctx.setFocus(null), "onClick"),
2023
+ onWheel: /* @__PURE__ */ __name((e) => e.preventDefault(), "onWheel"),
2024
+ style: {
2025
+ position: "fixed",
2026
+ inset: 0,
2027
+ zIndex: 100,
2028
+ background: "rgba(24,25,27,.6)",
2029
+ backdropFilter: "blur(14px)",
2030
+ fontFamily: DC.font,
2031
+ color: "#fff"
2032
+ },
2033
+ children: [
2034
+ /* @__PURE__ */ jsxs(
2035
+ "div",
2036
+ {
2037
+ onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick"),
2038
+ style: { position: "absolute", top: 0, left: 0, right: 0, height: 72, display: "flex", alignItems: "flex-start", padding: "16px 20px 0", gap: 16 },
2039
+ children: [
2040
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
2041
+ /* @__PURE__ */ jsxs(
2042
+ "button",
2043
+ {
2044
+ onClick: /* @__PURE__ */ __name(() => setDd((o) => !o), "onClick"),
2045
+ style: {
2046
+ border: "none",
2047
+ background: "transparent",
2048
+ color: "#fff",
2049
+ cursor: "pointer",
2050
+ padding: "6px 8px",
2051
+ borderRadius: 6,
2052
+ textAlign: "left",
2053
+ fontFamily: "inherit"
2054
+ },
2055
+ children: [
2056
+ /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
2057
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 18, fontWeight: 600, letterSpacing: -0.3 }, children: meta.title }),
2058
+ /* @__PURE__ */ jsx("svg", { width: "11", height: "11", viewBox: "0 0 11 11", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", style: { opacity: 0.7 }, children: /* @__PURE__ */ jsx("path", { d: "M2 4l3.5 3.5L9 4" }) })
2059
+ ] }),
2060
+ meta.subtitle && /* @__PURE__ */ jsx("span", { style: { display: "block", fontSize: 13, opacity: 0.6, fontWeight: 400, marginTop: 2 }, children: meta.subtitle })
2061
+ ]
2062
+ }
2063
+ ),
2064
+ ddOpen && /* @__PURE__ */ jsx("div", { style: {
2065
+ position: "absolute",
2066
+ top: "100%",
2067
+ left: 0,
2068
+ marginTop: 4,
2069
+ background: "#202123",
2070
+ borderRadius: 8,
2071
+ boxShadow: "0 8px 32px rgba(0,0,0,.4)",
2072
+ padding: 4,
2073
+ minWidth: 200,
2074
+ zIndex: 10
2075
+ }, children: sectionOrder.map((sid) => /* @__PURE__ */ jsx(
2076
+ "button",
2077
+ {
2078
+ onClick: /* @__PURE__ */ __name(() => {
2079
+ setDd(false);
2080
+ const f = sectionMeta[sid].slotIds[0];
2081
+ if (f) ctx.setFocus(`${sid}/${f}`);
2082
+ }, "onClick"),
2083
+ style: {
2084
+ display: "block",
2085
+ width: "100%",
2086
+ textAlign: "left",
2087
+ border: "none",
2088
+ cursor: "pointer",
2089
+ background: sid === sectionId ? "rgba(255,255,255,.1)" : "transparent",
2090
+ color: "#fff",
2091
+ padding: "8px 12px",
2092
+ borderRadius: 5,
2093
+ fontSize: 14,
2094
+ fontWeight: sid === sectionId ? 600 : 400,
2095
+ fontFamily: "inherit"
2096
+ },
2097
+ children: sectionMeta[sid].title
2098
+ },
2099
+ sid
2100
+ )) })
2101
+ ] }),
2102
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
2103
+ /* @__PURE__ */ jsx(
2104
+ "button",
2105
+ {
2106
+ onClick: /* @__PURE__ */ __name(() => ctx.setFocus(null), "onClick"),
2107
+ onMouseEnter: /* @__PURE__ */ __name((e) => e.currentTarget.style.background = "rgba(255,255,255,.12)", "onMouseEnter"),
2108
+ onMouseLeave: /* @__PURE__ */ __name((e) => e.currentTarget.style.background = "transparent", "onMouseLeave"),
2109
+ style: {
2110
+ border: "none",
2111
+ background: "transparent",
2112
+ color: "rgba(255,255,255,.7)",
2113
+ width: 32,
2114
+ height: 32,
2115
+ borderRadius: 16,
2116
+ fontSize: 20,
2117
+ cursor: "pointer",
2118
+ lineHeight: 1,
2119
+ transition: "background .12s"
2120
+ },
2121
+ children: "×"
2122
+ }
2123
+ )
2124
+ ]
2125
+ }
2126
+ ),
2127
+ /* @__PURE__ */ jsxs(
2128
+ "div",
2129
+ {
2130
+ style: { position: "absolute", top: 64, bottom: 56, left: 100, right: 100, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 16 },
2131
+ children: [
2132
+ /* @__PURE__ */ jsx("div", { onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick"), style: { width: width * scale, height: height * scale, position: "relative" }, children: /* @__PURE__ */ jsx("div", { style: {
2133
+ width,
2134
+ height,
2135
+ transform: `scale(${scale})`,
2136
+ transformOrigin: "top left",
2137
+ background: "#fff",
2138
+ borderRadius: 2,
2139
+ overflow: "hidden",
2140
+ boxShadow: "0 20px 80px rgba(0,0,0,.4)"
2141
+ }, children: children || /* @__PURE__ */ jsx("div", { style: { height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "#bbb" }, children: aid }) }) }),
2142
+ /* @__PURE__ */ jsxs("div", { onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick"), style: { fontSize: 14, fontWeight: 500, opacity: 0.85, textAlign: "center" }, children: [
2143
+ (_b = (sec.labels || {})[aid]) != null ? _b : artboard.props.label,
2144
+ /* @__PURE__ */ jsxs("span", { style: { opacity: 0.5, marginLeft: 10, fontVariantNumeric: "tabular-nums" }, children: [
2145
+ idx + 1,
2146
+ " / ",
2147
+ peers.length
2148
+ ] })
2149
+ ] })
2150
+ ]
2151
+ }
2152
+ ),
2153
+ /* @__PURE__ */ jsx(Arrow, { dir: "left", onClick: /* @__PURE__ */ __name(() => go(-1), "onClick") }),
2154
+ /* @__PURE__ */ jsx(Arrow, { dir: "right", onClick: /* @__PURE__ */ __name(() => go(1), "onClick") }),
2155
+ /* @__PURE__ */ jsx(
2156
+ "div",
2157
+ {
2158
+ onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick"),
2159
+ style: { position: "absolute", bottom: 20, left: "50%", transform: "translateX(-50%)", display: "flex", gap: 8 },
2160
+ children: peers.map((p, i) => /* @__PURE__ */ jsx(
2161
+ "button",
2162
+ {
2163
+ onClick: /* @__PURE__ */ __name(() => ctx.setFocus(`${sectionId}/${p}`), "onClick"),
2164
+ style: {
2165
+ border: "none",
2166
+ padding: 0,
2167
+ cursor: "pointer",
2168
+ width: 6,
2169
+ height: 6,
2170
+ borderRadius: 3,
2171
+ background: i === idx ? "#fff" : "rgba(255,255,255,.3)"
2172
+ }
2173
+ },
2174
+ p
2175
+ ))
2176
+ }
2177
+ )
2178
+ ]
2179
+ }
2180
+ ),
2181
+ getPortalRoot()
2182
+ );
2183
+ }
2184
+ __name(DCFocusOverlay, "DCFocusOverlay");
2185
+ function DCPostIt({ children, top, left, right, bottom, rotate = -2, width = 180 }) {
2186
+ return /* @__PURE__ */ jsx("div", { style: {
2187
+ position: "absolute",
2188
+ top,
2189
+ left,
2190
+ right,
2191
+ bottom,
2192
+ width,
2193
+ background: DC.postitBg,
2194
+ padding: "14px 16px",
2195
+ fontFamily: '"Comic Sans MS", "Marker Felt", "Segoe Print", cursive',
2196
+ fontSize: 14,
2197
+ lineHeight: 1.4,
2198
+ color: DC.postitText,
2199
+ boxShadow: "0 2px 8px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.08)",
2200
+ transform: `rotate(${rotate}deg)`,
2201
+ zIndex: 5
2202
+ }, children });
2203
+ }
2204
+ __name(DCPostIt, "DCPostIt");
2205
+ export {
2206
+ Avatar as A,
2207
+ DCArtboard as D,
2208
+ FONT$1 as F,
2209
+ adapterHasTokens as a,
2210
+ getComponentName as b,
2211
+ getPortalRoot as c,
2212
+ cardStyle as d,
2213
+ ghostBtn as e,
2214
+ resolveCanvasId as f,
2215
+ getDesignSystemAdapter as g,
2216
+ DCPage as h,
2217
+ isLiveCommentsHost as i,
2218
+ DCPostIt as j,
2219
+ DCSection as k,
2220
+ linkBtn as l,
2221
+ DesignCanvas as m,
2222
+ genericAdapter as n,
2223
+ onDesignSystemAdapterChange as o,
2224
+ primaryBtn as p,
2225
+ setPortalRoot as q,
2226
+ relativeTime as r,
2227
+ setDesignSystemAdapter as s,
2228
+ textareaStyle as t,
2229
+ useArtboardActive as u
2230
+ };
2231
+ //# sourceMappingURL=lib-CSrTsI4Y.js.map