@syntrologie/runtime-sdk 2.4.1 → 2.6.0-canary.1

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/CAPABILITIES.md +66 -1
  2. package/dist/SmartCanvasApp.d.ts +5 -3
  3. package/dist/actions/executors/index.d.ts +1 -2
  4. package/dist/actions/index.d.ts +2 -2
  5. package/dist/actions/schema.d.ts +19111 -20709
  6. package/dist/actions/schema.js +5 -2
  7. package/dist/actions/types.d.ts +45 -48
  8. package/dist/anchor/AnchorResolver.d.ts +18 -0
  9. package/dist/anchor/index.d.ts +2 -0
  10. package/dist/api.d.ts +3 -8
  11. package/dist/apps/examples/gamification-app.example.d.ts +8 -8
  12. package/dist/{chunk-4NYS7GAW.js → chunk-BU4Z6PD7.js} +45 -7
  13. package/dist/chunk-BU4Z6PD7.js.map +7 -0
  14. package/dist/{chunk-QGWATS3Z.js → chunk-Q4WGXNKC.js} +2775 -1052
  15. package/dist/chunk-Q4WGXNKC.js.map +7 -0
  16. package/dist/{chunk-OGTCFYR3.js → chunk-R5DNAIRI.js} +45 -34
  17. package/dist/chunk-R5DNAIRI.js.map +7 -0
  18. package/dist/chunk-XDYJ64IN.js +178 -0
  19. package/dist/chunk-XDYJ64IN.js.map +7 -0
  20. package/dist/components/ShadowCanvasOverlay.d.ts +2 -20
  21. package/dist/components/TileIcon.d.ts +14 -0
  22. package/dist/config/schema.d.ts +5529 -3620
  23. package/dist/config/schema.js +21 -61
  24. package/dist/config/schema.js.map +3 -3
  25. package/dist/context/schema.d.ts +16 -16
  26. package/dist/decisions/schema.d.ts +357 -2789
  27. package/dist/decisions/schema.js +7 -1
  28. package/dist/decisions/types.d.ts +24 -2
  29. package/dist/events/registerConfigPredicates.d.ts +7 -10
  30. package/dist/events/schema.d.ts +12 -12
  31. package/dist/experiments/adapters/growthbook.d.ts +2 -0
  32. package/dist/experiments/types.d.ts +7 -0
  33. package/dist/hooks/useShadowCanvasConfig.d.ts +6 -4
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.js +154 -186
  36. package/dist/index.js.map +3 -3
  37. package/dist/overlays/schema.d.ts +70 -70
  38. package/dist/react.js +4 -3
  39. package/dist/react.js.map +1 -1
  40. package/dist/runtime.d.ts +3 -0
  41. package/dist/smart-canvas.esm.js +123 -84
  42. package/dist/smart-canvas.esm.js.map +4 -4
  43. package/dist/smart-canvas.js +3271 -1210
  44. package/dist/smart-canvas.js.map +4 -4
  45. package/dist/smart-canvas.min.js +123 -84
  46. package/dist/smart-canvas.min.js.map +4 -4
  47. package/dist/surfaces/types.d.ts +2 -1
  48. package/dist/theme/ThemeProvider.d.ts +11 -16
  49. package/dist/theme/defaultTheme.d.ts +6 -1
  50. package/dist/theme/index.d.ts +3 -4
  51. package/dist/theme/types.d.ts +11 -0
  52. package/dist/version.d.ts +1 -1
  53. package/dist/widgets/WidgetRegistry.d.ts +1 -0
  54. package/package.json +9 -8
  55. package/schema/canvas-config.schema.json +10583 -1049
  56. package/scripts/validate-config.mjs +180 -0
  57. package/dist/actions/executors/tour.d.ts +0 -18
  58. package/dist/chunk-4NYS7GAW.js.map +0 -7
  59. package/dist/chunk-OGTCFYR3.js.map +0 -7
  60. package/dist/chunk-QGWATS3Z.js.map +0 -7
  61. package/dist/hooks/useHostPatches.d.ts +0 -9
  62. package/dist/theme/extractHostTheme.d.ts +0 -14
@@ -3,7 +3,7 @@ import {
3
3
  __privateGet,
4
4
  __privateSet,
5
5
  __publicField
6
- } from "./chunk-4NYS7GAW.js";
6
+ } from "./chunk-BU4Z6PD7.js";
7
7
 
8
8
  // ../adaptives/adaptive-content/dist/reconciliation-guard.js
9
9
  function guardAgainstReconciliation(container, anchor, reinsertFn, opts) {
@@ -124,13 +124,30 @@ function sanitizeHtml(html) {
124
124
 
125
125
  // ../adaptives/adaptive-content/dist/runtime.js
126
126
  var executeInsertHtml = async (action, context) => {
127
- const anchorEl = context.resolveAnchor(action.anchorId);
127
+ var _a2;
128
+ let anchorEl = context.resolveAnchor(action.anchorId);
129
+ if (!anchorEl && context.waitForAnchor) {
130
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
131
+ }
128
132
  if (!anchorEl) {
129
- throw new Error(`Anchor not found: ${action.anchorId}`);
133
+ console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);
134
+ return { cleanup: () => {
135
+ } };
130
136
  }
131
137
  const sanitizedHtml = sanitizeHtml(action.html);
138
+ const dedupAttr = "data-syntro-insert-label";
139
+ const label = action.label;
140
+ if (label) {
141
+ const escapedLabel = CSS.escape(label);
142
+ const searchRoot = (_a2 = anchorEl.parentElement) != null ? _a2 : anchorEl;
143
+ const existing = searchRoot.querySelector(`[${dedupAttr}="${escapedLabel}"]`);
144
+ if (existing)
145
+ existing.remove();
146
+ }
132
147
  const container = document.createElement("div");
133
148
  container.setAttribute("data-syntro-action-id", context.generateId());
149
+ if (label)
150
+ container.setAttribute(dedupAttr, label);
134
151
  container.innerHTML = sanitizedHtml;
135
152
  let originalContent = null;
136
153
  switch (action.position) {
@@ -151,6 +168,20 @@ var executeInsertHtml = async (action, context) => {
151
168
  anchorEl.replaceWith(container);
152
169
  break;
153
170
  }
171
+ let deepLinkHandler = null;
172
+ if (action.deepLink) {
173
+ const { tileId, itemId } = action.deepLink;
174
+ deepLinkHandler = () => {
175
+ var _a3, _b, _c;
176
+ const handle = (_a3 = window.SynOS) == null ? void 0 : _a3.handle;
177
+ if (handle) {
178
+ handle.open();
179
+ (_c = (_b = handle.runtime) == null ? void 0 : _b.events) == null ? void 0 : _c.publish("notification.deep_link", { tileId, itemId });
180
+ }
181
+ };
182
+ container.style.cursor = "pointer";
183
+ container.addEventListener("click", deepLinkHandler);
184
+ }
154
185
  context.publishEvent("action.applied", {
155
186
  id: context.generateId(),
156
187
  kind: "content:insertHtml",
@@ -178,6 +209,9 @@ var executeInsertHtml = async (action, context) => {
178
209
  const guardCleanup = guardAgainstReconciliation(container, anchorEl, reinsertFn);
179
210
  return {
180
211
  cleanup: () => {
212
+ if (deepLinkHandler) {
213
+ container.removeEventListener("click", deepLinkHandler);
214
+ }
181
215
  guardCleanup();
182
216
  if (action.position === "replace" && originalContent !== null) {
183
217
  const restoredEl = document.createElement(anchorEl.tagName);
@@ -199,9 +233,14 @@ var executeInsertHtml = async (action, context) => {
199
233
  };
200
234
  var executeSetText = async (action, context) => {
201
235
  var _a2;
202
- const anchorEl = context.resolveAnchor(action.anchorId);
236
+ let anchorEl = context.resolveAnchor(action.anchorId);
237
+ if (!anchorEl && context.waitForAnchor) {
238
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
239
+ }
203
240
  if (!anchorEl) {
204
- throw new Error(`Anchor not found: ${action.anchorId}`);
241
+ console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);
242
+ return { cleanup: () => {
243
+ } };
205
244
  }
206
245
  const originalText = (_a2 = anchorEl.textContent) != null ? _a2 : "";
207
246
  anchorEl.textContent = action.text;
@@ -222,9 +261,14 @@ var executeSetText = async (action, context) => {
222
261
  };
223
262
  };
224
263
  var executeSetAttr = async (action, context) => {
225
- const anchorEl = context.resolveAnchor(action.anchorId);
264
+ let anchorEl = context.resolveAnchor(action.anchorId);
265
+ if (!anchorEl && context.waitForAnchor) {
266
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
267
+ }
226
268
  if (!anchorEl) {
227
- throw new Error(`Anchor not found: ${action.anchorId}`);
269
+ console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);
270
+ return { cleanup: () => {
271
+ } };
228
272
  }
229
273
  const lowerAttr = action.attr.toLowerCase();
230
274
  if (lowerAttr.startsWith("on")) {
@@ -262,9 +306,14 @@ var executeSetAttr = async (action, context) => {
262
306
  };
263
307
  };
264
308
  var executeAddClass = async (action, context) => {
265
- const anchorEl = context.resolveAnchor(action.anchorId);
309
+ let anchorEl = context.resolveAnchor(action.anchorId);
310
+ if (!anchorEl && context.waitForAnchor) {
311
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
312
+ }
266
313
  if (!anchorEl) {
267
- throw new Error(`Anchor not found: ${action.anchorId}`);
314
+ console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);
315
+ return { cleanup: () => {
316
+ } };
268
317
  }
269
318
  const hadClass = anchorEl.classList.contains(action.className);
270
319
  anchorEl.classList.add(action.className);
@@ -283,9 +332,14 @@ var executeAddClass = async (action, context) => {
283
332
  };
284
333
  };
285
334
  var executeRemoveClass = async (action, context) => {
286
- const anchorEl = context.resolveAnchor(action.anchorId);
335
+ let anchorEl = context.resolveAnchor(action.anchorId);
336
+ if (!anchorEl && context.waitForAnchor) {
337
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
338
+ }
287
339
  if (!anchorEl) {
288
- throw new Error(`Anchor not found: ${action.anchorId}`);
340
+ console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);
341
+ return { cleanup: () => {
342
+ } };
289
343
  }
290
344
  const hadClass = anchorEl.classList.contains(action.className);
291
345
  anchorEl.classList.remove(action.className);
@@ -304,9 +358,14 @@ var executeRemoveClass = async (action, context) => {
304
358
  };
305
359
  };
306
360
  var executeSetStyle = async (action, context) => {
307
- const anchorEl = context.resolveAnchor(action.anchorId);
361
+ let anchorEl = context.resolveAnchor(action.anchorId);
362
+ if (!anchorEl && context.waitForAnchor) {
363
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
364
+ }
308
365
  if (!anchorEl) {
309
- throw new Error(`Anchor not found: ${action.anchorId}`);
366
+ console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);
367
+ return { cleanup: () => {
368
+ } };
310
369
  }
311
370
  const originalStyles = /* @__PURE__ */ new Map();
312
371
  for (const prop of Object.keys(action.styles)) {
@@ -357,6 +416,701 @@ var runtime = {
357
416
  executors
358
417
  };
359
418
 
419
+ // ../adaptives/adaptive-overlays/dist/celebrations/effects/confetti.js
420
+ var INTENSITY_COUNTS = { light: 50, medium: 100, heavy: 200 };
421
+ var DEFAULT_COLORS = [
422
+ "#ff0000",
423
+ "#00ff00",
424
+ "#0000ff",
425
+ "#ffff00",
426
+ "#ff00ff",
427
+ "#00ffff",
428
+ "#ff8800",
429
+ "#8800ff"
430
+ ];
431
+ var confettiEffect = {
432
+ init(width, height, config) {
433
+ const count = INTENSITY_COUNTS[config.intensity];
434
+ const colors = config.colors.length > 0 ? config.colors : DEFAULT_COLORS;
435
+ const particles = [];
436
+ for (let i = 0; i < count; i++) {
437
+ particles.push({
438
+ x: Math.random() * width,
439
+ y: Math.random() * -height * 0.3,
440
+ vx: (Math.random() - 0.5) * 4,
441
+ vy: Math.random() * 2 + 1,
442
+ rotation: Math.random() * Math.PI * 2,
443
+ rotationSpeed: (Math.random() - 0.5) * 0.2,
444
+ size: Math.random() * 6 + 4,
445
+ color: colors[Math.floor(Math.random() * colors.length)],
446
+ opacity: 1,
447
+ shape: Math.random() > 0.5 ? "rect" : "circle"
448
+ });
449
+ }
450
+ return particles;
451
+ },
452
+ update(particles, _dt, _elapsed) {
453
+ let anyVisible = false;
454
+ for (const p of particles) {
455
+ p.vy += 0.15;
456
+ p.vx *= 0.99;
457
+ p.x += p.vx;
458
+ p.y += p.vy;
459
+ p.rotation += p.rotationSpeed;
460
+ if (p.y > 0 && p.opacity > 0) {
461
+ if (p.y > 500) {
462
+ p.opacity -= 0.02;
463
+ if (p.opacity < 0)
464
+ p.opacity = 0;
465
+ }
466
+ }
467
+ if (p.opacity > 0.01) {
468
+ anyVisible = true;
469
+ }
470
+ }
471
+ return anyVisible;
472
+ },
473
+ render(ctx, particles) {
474
+ for (const p of particles) {
475
+ if (p.opacity < 0.01)
476
+ continue;
477
+ ctx.save();
478
+ ctx.globalAlpha = p.opacity;
479
+ ctx.fillStyle = p.color;
480
+ ctx.translate(p.x, p.y);
481
+ ctx.rotate(p.rotation);
482
+ if (p.shape === "circle") {
483
+ ctx.beginPath();
484
+ ctx.arc(0, 0, p.size / 2, 0, Math.PI * 2);
485
+ ctx.fill();
486
+ } else {
487
+ ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size * 0.6);
488
+ }
489
+ ctx.restore();
490
+ }
491
+ }
492
+ };
493
+
494
+ // ../adaptives/adaptive-overlays/dist/celebrations/effects/emoji-rain.js
495
+ var INTENSITY_COUNTS2 = { light: 20, medium: 40, heavy: 80 };
496
+ var DEFAULT_EMOJI = "\u{1F389}";
497
+ var emojiRainEffect = {
498
+ init(width, _height, config) {
499
+ var _a2;
500
+ const count = INTENSITY_COUNTS2[config.intensity];
501
+ const emoji = typeof ((_a2 = config.props) == null ? void 0 : _a2.emoji) === "string" ? config.props.emoji : DEFAULT_EMOJI;
502
+ const particles = [];
503
+ for (let i = 0; i < count; i++) {
504
+ particles.push({
505
+ x: Math.random() * width,
506
+ y: Math.random() * -200,
507
+ vx: 0,
508
+ vy: Math.random() * 1.5 + 1,
509
+ rotation: 0,
510
+ rotationSpeed: 0,
511
+ size: Math.random() * 12 + 16,
512
+ color: "#000000",
513
+ opacity: 1,
514
+ shape: "emoji",
515
+ emoji,
516
+ data: {
517
+ /** Phase offset for horizontal wobble */
518
+ wobblePhase: Math.random() * Math.PI * 2,
519
+ /** Amplitude of horizontal wobble */
520
+ wobbleAmp: Math.random() * 1.5 + 0.5,
521
+ /** Original x for wobble base */
522
+ originX: 0
523
+ }
524
+ });
525
+ }
526
+ for (const p of particles) {
527
+ if (p.data)
528
+ p.data.originX = p.x;
529
+ }
530
+ return particles;
531
+ },
532
+ update(particles, _dt, elapsed) {
533
+ var _a2, _b, _c, _d, _e, _f;
534
+ let anyVisible = false;
535
+ for (const p of particles) {
536
+ p.y += p.vy;
537
+ const phase = (_b = (_a2 = p.data) == null ? void 0 : _a2.wobblePhase) != null ? _b : 0;
538
+ const amp = (_d = (_c = p.data) == null ? void 0 : _c.wobbleAmp) != null ? _d : 1;
539
+ const originX = (_f = (_e = p.data) == null ? void 0 : _e.originX) != null ? _f : p.x;
540
+ p.x = originX + Math.sin(elapsed * 3e-3 + phase) * amp * 20;
541
+ if (p.y > 600) {
542
+ p.opacity -= 0.02;
543
+ if (p.opacity < 0)
544
+ p.opacity = 0;
545
+ }
546
+ if (p.opacity > 0.01) {
547
+ anyVisible = true;
548
+ }
549
+ }
550
+ return anyVisible;
551
+ },
552
+ render(ctx, particles) {
553
+ for (const p of particles) {
554
+ if (p.opacity < 0.01 || !p.emoji)
555
+ continue;
556
+ ctx.save();
557
+ ctx.globalAlpha = p.opacity;
558
+ ctx.font = `${p.size}px serif`;
559
+ ctx.textAlign = "center";
560
+ ctx.textBaseline = "middle";
561
+ ctx.fillText(p.emoji, p.x, p.y);
562
+ ctx.restore();
563
+ }
564
+ }
565
+ };
566
+
567
+ // ../adaptives/adaptive-overlays/dist/celebrations/effects/fireworks.js
568
+ var PARTICLES_PER_BURST = { light: 20, medium: 40, heavy: 80 };
569
+ var BURST_COUNTS = { light: 3, medium: 4, heavy: 5 };
570
+ var fireworksEffect = {
571
+ init(width, height, config) {
572
+ const perBurst = PARTICLES_PER_BURST[config.intensity];
573
+ const burstCount = BURST_COUNTS[config.intensity];
574
+ const colors = config.colors.length > 0 ? config.colors : ["#ff4444", "#44ff44", "#4444ff"];
575
+ const particles = [];
576
+ for (let b = 0; b < burstCount; b++) {
577
+ const cx = Math.random() * width * 0.8 + width * 0.1;
578
+ const cy = Math.random() * height * 0.6;
579
+ const burstColor = colors[Math.floor(Math.random() * colors.length)];
580
+ for (let i = 0; i < perBurst; i++) {
581
+ const angle = Math.PI * 2 * i / perBurst + (Math.random() - 0.5) * 0.3;
582
+ const speed = Math.random() * 3 + 2;
583
+ particles.push({
584
+ x: cx,
585
+ y: cy,
586
+ vx: Math.cos(angle) * speed,
587
+ vy: Math.sin(angle) * speed,
588
+ rotation: 0,
589
+ rotationSpeed: 0,
590
+ size: Math.random() * 3 + 2,
591
+ color: burstColor,
592
+ opacity: 1,
593
+ shape: "circle",
594
+ data: { centerX: cx, centerY: cy }
595
+ });
596
+ }
597
+ }
598
+ return particles;
599
+ },
600
+ update(particles, _dt, _elapsed) {
601
+ let anyVisible = false;
602
+ for (const p of particles) {
603
+ p.vx *= 0.97;
604
+ p.vy *= 0.97;
605
+ p.vy += 0.03;
606
+ p.x += p.vx;
607
+ p.y += p.vy;
608
+ p.opacity -= 8e-3;
609
+ if (p.opacity < 0)
610
+ p.opacity = 0;
611
+ if (p.opacity > 0.01) {
612
+ anyVisible = true;
613
+ }
614
+ }
615
+ return anyVisible;
616
+ },
617
+ render(ctx, particles) {
618
+ for (const p of particles) {
619
+ if (p.opacity < 0.01)
620
+ continue;
621
+ ctx.save();
622
+ ctx.globalAlpha = p.opacity;
623
+ ctx.fillStyle = p.color;
624
+ ctx.shadowBlur = 12;
625
+ ctx.shadowColor = p.color;
626
+ ctx.beginPath();
627
+ ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
628
+ ctx.fill();
629
+ ctx.restore();
630
+ }
631
+ }
632
+ };
633
+
634
+ // ../adaptives/adaptive-overlays/dist/celebrations/effects/sparkles.js
635
+ var INTENSITY_COUNTS3 = { light: 30, medium: 60, heavy: 120 };
636
+ var sparklesEffect = {
637
+ init(width, height, config) {
638
+ const count = INTENSITY_COUNTS3[config.intensity];
639
+ const colors = config.colors.length > 0 ? config.colors : ["#ffd700", "#ffffff", "#fffacd"];
640
+ const particles = [];
641
+ for (let i = 0; i < count; i++) {
642
+ particles.push({
643
+ x: Math.random() * width,
644
+ y: Math.random() * height,
645
+ vx: (Math.random() - 0.5) * 0.3,
646
+ vy: -(Math.random() * 0.5 + 0.2),
647
+ rotation: Math.random() * Math.PI * 2,
648
+ rotationSpeed: (Math.random() - 0.5) * 0.1,
649
+ size: Math.random() * 4 + 2,
650
+ color: colors[Math.floor(Math.random() * colors.length)],
651
+ opacity: Math.random() * 0.5 + 0.5,
652
+ shape: "circle",
653
+ data: {
654
+ /** Phase offset for sine-wave twinkle */
655
+ phase: Math.random() * Math.PI * 2,
656
+ /** Base opacity before twinkle modulation */
657
+ baseOpacity: Math.random() * 0.5 + 0.5
658
+ }
659
+ });
660
+ }
661
+ return particles;
662
+ },
663
+ update(particles, _dt, elapsed) {
664
+ var _a2, _b, _c, _d, _e;
665
+ let anyVisible = false;
666
+ for (const p of particles) {
667
+ p.x += p.vx;
668
+ p.y += p.vy;
669
+ p.rotation += p.rotationSpeed;
670
+ if (p.data) {
671
+ p.data.baseOpacity = ((_a2 = p.data.baseOpacity) != null ? _a2 : 1) - 1e-3;
672
+ if (p.data.baseOpacity < 0)
673
+ p.data.baseOpacity = 0;
674
+ }
675
+ const phase = (_c = (_b = p.data) == null ? void 0 : _b.phase) != null ? _c : 0;
676
+ const baseOpacity = (_e = (_d = p.data) == null ? void 0 : _d.baseOpacity) != null ? _e : 1;
677
+ if (baseOpacity <= 0.01) {
678
+ p.opacity = 0;
679
+ } else {
680
+ const twinkle = Math.sin(elapsed * 5e-3 + phase) * 0.4 + 0.6;
681
+ p.opacity = baseOpacity * twinkle;
682
+ }
683
+ if (p.opacity > 0.01) {
684
+ anyVisible = true;
685
+ }
686
+ }
687
+ return anyVisible;
688
+ },
689
+ render(ctx, particles) {
690
+ for (const p of particles) {
691
+ if (p.opacity < 0.01)
692
+ continue;
693
+ ctx.save();
694
+ ctx.globalAlpha = p.opacity;
695
+ ctx.fillStyle = p.color;
696
+ ctx.translate(p.x, p.y);
697
+ ctx.rotate(p.rotation);
698
+ const s = p.size;
699
+ ctx.beginPath();
700
+ ctx.moveTo(0, -s);
701
+ ctx.lineTo(s * 0.3, -s * 0.3);
702
+ ctx.lineTo(s, 0);
703
+ ctx.lineTo(s * 0.3, s * 0.3);
704
+ ctx.lineTo(0, s);
705
+ ctx.lineTo(-s * 0.3, s * 0.3);
706
+ ctx.lineTo(-s, 0);
707
+ ctx.lineTo(-s * 0.3, -s * 0.3);
708
+ ctx.closePath();
709
+ ctx.fill();
710
+ ctx.restore();
711
+ }
712
+ }
713
+ };
714
+
715
+ // ../adaptives/adaptive-overlays/dist/celebrations/engine.js
716
+ var CelebrationEngine = class {
717
+ constructor() {
718
+ this.canvas = null;
719
+ this.ctx = null;
720
+ this.rafId = null;
721
+ this.particles = [];
722
+ this.startTime = 0;
723
+ this.lastFrame = 0;
724
+ this.duration = 0;
725
+ this.effect = null;
726
+ this.container = null;
727
+ }
728
+ start(container, effect, config) {
729
+ this.container = container;
730
+ this.effect = effect;
731
+ this.duration = config.duration;
732
+ const canvas = document.createElement("canvas");
733
+ canvas.setAttribute("data-syntro-celebrate", "");
734
+ Object.assign(canvas.style, {
735
+ position: "fixed",
736
+ inset: "0",
737
+ pointerEvents: "none",
738
+ zIndex: "2147483646"
739
+ });
740
+ const dpr = window.devicePixelRatio || 1;
741
+ const width = window.innerWidth;
742
+ const height = window.innerHeight;
743
+ canvas.width = width * dpr;
744
+ canvas.height = height * dpr;
745
+ canvas.style.width = `${width}px`;
746
+ canvas.style.height = `${height}px`;
747
+ const ctx = canvas.getContext("2d");
748
+ if (!ctx) {
749
+ return;
750
+ }
751
+ ctx.scale(dpr, dpr);
752
+ this.canvas = canvas;
753
+ this.ctx = ctx;
754
+ container.appendChild(canvas);
755
+ this.particles = effect.init(width, height, config);
756
+ this.startTime = performance.now();
757
+ this.lastFrame = this.startTime;
758
+ this.tick = this.tick.bind(this);
759
+ this.rafId = requestAnimationFrame(this.tick);
760
+ }
761
+ stop() {
762
+ if (this.rafId !== null) {
763
+ cancelAnimationFrame(this.rafId);
764
+ this.rafId = null;
765
+ }
766
+ if (this.canvas && this.container) {
767
+ this.canvas.remove();
768
+ this.canvas = null;
769
+ }
770
+ this.ctx = null;
771
+ this.effect = null;
772
+ this.container = null;
773
+ this.particles = [];
774
+ }
775
+ tick(now) {
776
+ if (!this.ctx || !this.canvas || !this.effect)
777
+ return;
778
+ const elapsed = now - this.startTime;
779
+ const dt = now - this.lastFrame;
780
+ this.lastFrame = now;
781
+ if (elapsed >= this.duration) {
782
+ this.stop();
783
+ return;
784
+ }
785
+ const width = this.canvas.width / (window.devicePixelRatio || 1);
786
+ const height = this.canvas.height / (window.devicePixelRatio || 1);
787
+ this.ctx.clearRect(0, 0, width, height);
788
+ const alive = this.effect.update(this.particles, dt, elapsed);
789
+ if (!alive) {
790
+ this.stop();
791
+ return;
792
+ }
793
+ this.effect.render(this.ctx, this.particles);
794
+ this.rafId = requestAnimationFrame(this.tick);
795
+ }
796
+ };
797
+
798
+ // ../adaptives/adaptive-overlays/dist/celebrations/index.js
799
+ var FALLBACK_COLORS = [
800
+ "#ff0000",
801
+ "#00ff00",
802
+ "#0000ff",
803
+ "#ffff00",
804
+ "#ff00ff",
805
+ "#00ffff",
806
+ "#ff8800",
807
+ "#8800ff"
808
+ ];
809
+ function buildThemePalette(primary, hover) {
810
+ return [primary, hover, `${primary}cc`, `${hover}cc`, "#ffffff", `${primary}80`];
811
+ }
812
+ function readThemeColors(overlayRoot) {
813
+ var _a2, _b;
814
+ try {
815
+ const styles = getComputedStyle(overlayRoot);
816
+ const primary = (_a2 = styles.getPropertyValue("--sc-color-primary")) == null ? void 0 : _a2.trim();
817
+ const hover = (_b = styles.getPropertyValue("--sc-color-primary-hover")) == null ? void 0 : _b.trim();
818
+ if ((primary == null ? void 0 : primary.startsWith("#")) && primary.length >= 7) {
819
+ return buildThemePalette(primary, hover || primary);
820
+ }
821
+ } catch {
822
+ }
823
+ return null;
824
+ }
825
+ var effectRegistry = /* @__PURE__ */ new Map([
826
+ ["confetti", confettiEffect],
827
+ ["fireworks", fireworksEffect],
828
+ ["sparkles", sparklesEffect],
829
+ ["emoji-rain", emojiRainEffect]
830
+ ]);
831
+ var executeCelebrate = async (action, context) => {
832
+ var _a2, _b, _c, _d;
833
+ const effect = effectRegistry.get(action.effect);
834
+ if (!effect) {
835
+ console.warn(`[adaptive-overlays] Unknown celebration effect: "${action.effect}". Available: ${[...effectRegistry.keys()].join(", ")}`);
836
+ return { cleanup: () => {
837
+ } };
838
+ }
839
+ const colors = (_b = (_a2 = action.colors) != null ? _a2 : readThemeColors(context.overlayRoot)) != null ? _b : FALLBACK_COLORS;
840
+ const config = {
841
+ duration: (_c = action.duration) != null ? _c : 3e3,
842
+ intensity: (_d = action.intensity) != null ? _d : "medium",
843
+ colors,
844
+ props: action.props
845
+ };
846
+ const engine = new CelebrationEngine();
847
+ engine.start(context.overlayRoot, effect, config);
848
+ context.publishEvent("action.applied", {
849
+ id: context.generateId(),
850
+ kind: "overlays:celebrate",
851
+ effect: action.effect
852
+ });
853
+ return {
854
+ cleanup: () => {
855
+ engine.stop();
856
+ }
857
+ };
858
+ };
859
+
860
+ // ../adaptives/adaptive-overlays/dist/executors/tour.js
861
+ var ACTIVE_TOUR_KEY = "syntro_active_tour";
862
+ var activeTours = /* @__PURE__ */ new Map();
863
+ function getTourState(tourId) {
864
+ try {
865
+ const data = localStorage.getItem(ACTIVE_TOUR_KEY);
866
+ if (!data)
867
+ return null;
868
+ const state = JSON.parse(data);
869
+ if (state.tourId !== tourId)
870
+ return null;
871
+ return state;
872
+ } catch {
873
+ return null;
874
+ }
875
+ }
876
+ function saveTourState(state) {
877
+ try {
878
+ localStorage.setItem(ACTIVE_TOUR_KEY, JSON.stringify(state));
879
+ } catch {
880
+ }
881
+ }
882
+ function clearTourState() {
883
+ try {
884
+ localStorage.removeItem(ACTIVE_TOUR_KEY);
885
+ } catch {
886
+ }
887
+ }
888
+ function getCurrentRoute() {
889
+ return window.location.pathname;
890
+ }
891
+ function stepMatchesRoute(step) {
892
+ if (!step.route)
893
+ return true;
894
+ const currentRoute = getCurrentRoute();
895
+ if (step.route.includes("*")) {
896
+ const pattern = new RegExp(`^${step.route.replace(/\*/g, ".*")}$`);
897
+ return pattern.test(currentRoute);
898
+ }
899
+ return currentRoute === step.route;
900
+ }
901
+ var executeTour = async (action, context) => {
902
+ const { tourId, steps, startStep, autoStart = true } = action;
903
+ if (steps.length === 0) {
904
+ throw new Error(`Tour "${tourId}" has no steps`);
905
+ }
906
+ if (activeTours.has(tourId)) {
907
+ return {
908
+ cleanup: async () => {
909
+ const existing = activeTours.get(tourId);
910
+ if (existing) {
911
+ await existing.cleanup();
912
+ }
913
+ }
914
+ };
915
+ }
916
+ if (!context.applyAction) {
917
+ throw new Error("Tour executor requires applyAction in context");
918
+ }
919
+ let state = getTourState(tourId);
920
+ const isResumingTour = !!state;
921
+ if (!isResumingTour && !autoStart) {
922
+ return {
923
+ cleanup: () => {
924
+ }
925
+ };
926
+ }
927
+ if (!state) {
928
+ state = {
929
+ tourId,
930
+ currentStepId: startStep || steps[0].id,
931
+ startedAt: Date.now()
932
+ };
933
+ saveTourState(state);
934
+ }
935
+ let currentStepIndex = steps.findIndex((s) => s.id === state.currentStepId);
936
+ if (currentStepIndex === -1) {
937
+ const initialStepId = startStep || steps[0].id;
938
+ currentStepIndex = steps.findIndex((s) => s.id === initialStepId);
939
+ if (currentStepIndex === -1)
940
+ currentStepIndex = 0;
941
+ state.currentStepId = steps[currentStepIndex].id;
942
+ saveTourState(state);
943
+ }
944
+ const currentStep = steps[currentStepIndex];
945
+ if (!stepMatchesRoute(currentStep)) {
946
+ context.publishEvent("tour.waiting_for_route", {
947
+ tourId,
948
+ stepId: currentStep.id,
949
+ expectedRoute: currentStep.route,
950
+ currentRoute: getCurrentRoute()
951
+ });
952
+ return {
953
+ cleanup: () => {
954
+ }
955
+ };
956
+ }
957
+ let isDestroyed = false;
958
+ let currentActionHandle = null;
959
+ let eventUnsubscribe = null;
960
+ let routeWatcher = null;
961
+ const cleanupCurrentStep = async () => {
962
+ if (eventUnsubscribe) {
963
+ eventUnsubscribe();
964
+ eventUnsubscribe = null;
965
+ }
966
+ if (currentActionHandle == null ? void 0 : currentActionHandle.isApplied()) {
967
+ await currentActionHandle.revert();
968
+ currentActionHandle = null;
969
+ }
970
+ };
971
+ const advanceToStep = async (nextStepId) => {
972
+ if (isDestroyed)
973
+ return;
974
+ await cleanupCurrentStep();
975
+ if (nextStepId === "end") {
976
+ clearTourState();
977
+ context.publishEvent("tour.completed", {
978
+ tourId,
979
+ totalSteps: steps.length
980
+ });
981
+ isDestroyed = true;
982
+ return;
983
+ }
984
+ const nextStep = steps.find((s) => s.id === nextStepId);
985
+ if (!nextStep) {
986
+ console.error(`[Tour] Step "${nextStepId}" not found`);
987
+ return;
988
+ }
989
+ state.currentStepId = nextStepId;
990
+ saveTourState(state);
991
+ context.publishEvent("tour.step_changed", {
992
+ tourId,
993
+ previousStepId: currentStep.id,
994
+ nextStepId
995
+ });
996
+ if (nextStep.route && nextStep.route !== getCurrentRoute()) {
997
+ context.publishEvent("tour.awaiting_navigation", {
998
+ tourId,
999
+ stepId: nextStepId,
1000
+ targetRoute: nextStep.route
1001
+ });
1002
+ return;
1003
+ }
1004
+ await executeStep(nextStep);
1005
+ };
1006
+ const executeStep = async (step) => {
1007
+ if (isDestroyed)
1008
+ return;
1009
+ context.publishEvent("tour.step_started", {
1010
+ tourId,
1011
+ stepId: step.id,
1012
+ stepIndex: steps.findIndex((s) => s.id === step.id),
1013
+ totalSteps: steps.length
1014
+ });
1015
+ try {
1016
+ currentActionHandle = await context.applyAction(step.action);
1017
+ } catch (error2) {
1018
+ console.error(`[Tour] Failed to execute step "${step.id}":`, error2);
1019
+ context.publishEvent("tour.step_failed", {
1020
+ tourId,
1021
+ stepId: step.id,
1022
+ error: String(error2)
1023
+ });
1024
+ return;
1025
+ }
1026
+ if (step.onAction && context.subscribeEvent) {
1027
+ eventUnsubscribe = context.subscribeEvent("action.modal_cta_clicked", (props) => {
1028
+ const actionId = props == null ? void 0 : props.actionId;
1029
+ if (actionId && step.onAction) {
1030
+ const nextStepId = step.onAction[actionId];
1031
+ if (nextStepId) {
1032
+ advanceToStep(nextStepId);
1033
+ }
1034
+ }
1035
+ });
1036
+ const tooltipUnsubscribe = context.subscribeEvent("action.tooltip_cta_clicked", (props) => {
1037
+ const actionId = props == null ? void 0 : props.actionId;
1038
+ if (actionId && step.onAction) {
1039
+ const nextStepId = step.onAction[actionId];
1040
+ if (nextStepId) {
1041
+ advanceToStep(nextStepId);
1042
+ }
1043
+ }
1044
+ });
1045
+ const originalUnsubscribe = eventUnsubscribe;
1046
+ eventUnsubscribe = () => {
1047
+ originalUnsubscribe();
1048
+ tooltipUnsubscribe();
1049
+ };
1050
+ }
1051
+ };
1052
+ const setupRouteWatcher = () => {
1053
+ let lastPath = getCurrentRoute();
1054
+ const checkRoute = () => {
1055
+ const currentPath = getCurrentRoute();
1056
+ if (currentPath !== lastPath) {
1057
+ lastPath = currentPath;
1058
+ context.publishEvent("tour.route_changed", {
1059
+ tourId,
1060
+ newRoute: currentPath
1061
+ });
1062
+ }
1063
+ };
1064
+ if (context.subscribeNavigation) {
1065
+ return context.subscribeNavigation(() => checkRoute());
1066
+ }
1067
+ window.addEventListener("popstate", checkRoute);
1068
+ const origPushState = history.pushState.bind(history);
1069
+ const origReplaceState = history.replaceState.bind(history);
1070
+ history.pushState = (...args) => {
1071
+ origPushState(...args);
1072
+ queueMicrotask(checkRoute);
1073
+ };
1074
+ history.replaceState = (...args) => {
1075
+ origReplaceState(...args);
1076
+ queueMicrotask(checkRoute);
1077
+ };
1078
+ return () => {
1079
+ window.removeEventListener("popstate", checkRoute);
1080
+ history.pushState = origPushState;
1081
+ history.replaceState = origReplaceState;
1082
+ };
1083
+ };
1084
+ routeWatcher = setupRouteWatcher();
1085
+ if (!isResumingTour) {
1086
+ context.publishEvent("tour.started", {
1087
+ tourId,
1088
+ totalSteps: steps.length,
1089
+ startStepId: state.currentStepId
1090
+ });
1091
+ } else {
1092
+ context.publishEvent("tour.resumed", {
1093
+ tourId,
1094
+ stepId: state.currentStepId
1095
+ });
1096
+ }
1097
+ await executeStep(currentStep);
1098
+ const cleanup = async () => {
1099
+ isDestroyed = true;
1100
+ activeTours.delete(tourId);
1101
+ await cleanupCurrentStep();
1102
+ if (routeWatcher) {
1103
+ routeWatcher();
1104
+ }
1105
+ context.publishEvent("tour.paused", {
1106
+ tourId,
1107
+ stepId: state.currentStepId
1108
+ });
1109
+ };
1110
+ activeTours.set(tourId, { cleanup });
1111
+ return { cleanup };
1112
+ };
1113
+
360
1114
  // ../design-system/src/tokens/colors.ts
361
1115
  var base = {
362
1116
  white: "#ffffff",
@@ -959,6 +1713,13 @@ function sanitizeHtml2(html) {
959
1713
  }
960
1714
 
961
1715
  // ../adaptives/adaptive-overlays/dist/modal.js
1716
+ var V = {
1717
+ bg: "var(--sc-overlay-background, #ffffff)",
1718
+ title: "var(--sc-overlay-title-color, var(--sc-overlay-text-color, #111827))",
1719
+ text: "var(--sc-overlay-text-color, #4b5563)",
1720
+ accent: "var(--sc-color-primary, #4f46e5)",
1721
+ radius: "var(--sc-border-radius, 12px)"
1722
+ };
962
1723
  var executeModal = async (action, context) => {
963
1724
  var _a2, _b;
964
1725
  const { content, size = "md", blocking = false, scrim, dismiss, ctaButtons } = action;
@@ -985,8 +1746,8 @@ var executeModal = async (action, context) => {
985
1746
  transform: translate(-50%, -50%) scale(0.95);
986
1747
  max-width: ${sizeMap[size]};
987
1748
  width: 90%;
988
- background: ${base.white};
989
- border-radius: 12px;
1749
+ background: ${V.bg};
1750
+ border-radius: ${V.radius};
990
1751
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
991
1752
  z-index: 2147483646;
992
1753
  opacity: 0;
@@ -995,9 +1756,9 @@ var executeModal = async (action, context) => {
995
1756
  `;
996
1757
  let html = "";
997
1758
  if (content.title) {
998
- html += `<h2 class="syntro-modal-title" style="margin: 0 0 12px 0; font-size: 18px; font-weight: 600; color: #111827;">${sanitizeHtml2(content.title)}</h2>`;
1759
+ html += `<h2 class="syntro-modal-title" style="margin: 0 0 12px 0; font-size: 18px; font-weight: 600; color: ${V.title};">${sanitizeHtml2(content.title)}</h2>`;
999
1760
  }
1000
- html += `<div class="syntro-modal-body" style="color: #4b5563; line-height: 1.5;">${sanitizeHtml2(content.body)}</div>`;
1761
+ html += `<div class="syntro-modal-body" style="color: ${V.text}; line-height: 1.5;">${sanitizeHtml2(content.body)}</div>`;
1001
1762
  if ((dismiss == null ? void 0 : dismiss.closeButton) !== false) {
1002
1763
  html += `
1003
1764
  <button class="syntro-modal-close" data-syntro-action="dismiss" style="
@@ -1008,7 +1769,8 @@ var executeModal = async (action, context) => {
1008
1769
  border: none;
1009
1770
  cursor: pointer;
1010
1771
  padding: 4px;
1011
- color: #6b7280;
1772
+ color: ${V.text};
1773
+ opacity: 0.6;
1012
1774
  " aria-label="Close">
1013
1775
  <svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
1014
1776
  <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
@@ -1031,7 +1793,7 @@ var executeModal = async (action, context) => {
1031
1793
  font-weight: 500;
1032
1794
  cursor: pointer;
1033
1795
  transition: background 150ms ease;
1034
- ${isPrimary ? "background: #4f46e5; color: white; border: none;" : "background: white; color: #374151; border: 1px solid #d1d5db;"}
1796
+ ${isPrimary ? `background: ${V.accent}; color: white; border: none;` : `background: transparent; color: ${V.accent}; border: 1px solid currentColor; opacity: 0.7;`}
1035
1797
  "
1036
1798
  >
1037
1799
  ${sanitizeHtml2(btn.label)}
@@ -1297,26 +2059,40 @@ function showTooltip(anchorEl, overlayRoot, opts) {
1297
2059
  }
1298
2060
  const attachTrigger = () => {
1299
2061
  if (opts.trigger === "hover") {
1300
- const enter = () => {
2062
+ let hideTimeout = null;
2063
+ const show = () => {
2064
+ if (hideTimeout) {
2065
+ clearTimeout(hideTimeout);
2066
+ hideTimeout = null;
2067
+ }
1301
2068
  div.style.visibility = "visible";
1302
2069
  div.style.opacity = "1";
1303
2070
  };
1304
- const leave = () => {
1305
- div.style.visibility = "hidden";
1306
- div.style.opacity = "0";
2071
+ const scheduleHide = () => {
2072
+ hideTimeout = setTimeout(() => {
2073
+ div.style.visibility = "hidden";
2074
+ div.style.opacity = "0";
2075
+ hideTimeout = null;
2076
+ }, 100);
1307
2077
  };
1308
2078
  div.style.visibility = "hidden";
1309
2079
  div.style.opacity = "0";
1310
2080
  div.style.transition = "opacity 200ms ease, visibility 200ms";
1311
- anchorEl.addEventListener("mouseenter", enter);
1312
- anchorEl.addEventListener("mouseleave", leave);
1313
- anchorEl.addEventListener("focus", enter);
1314
- anchorEl.addEventListener("blur", leave);
2081
+ anchorEl.addEventListener("mouseenter", show);
2082
+ anchorEl.addEventListener("mouseleave", scheduleHide);
2083
+ div.addEventListener("mouseenter", show);
2084
+ div.addEventListener("mouseleave", scheduleHide);
2085
+ anchorEl.addEventListener("focus", show);
2086
+ anchorEl.addEventListener("blur", scheduleHide);
1315
2087
  return () => {
1316
- anchorEl.removeEventListener("mouseenter", enter);
1317
- anchorEl.removeEventListener("mouseleave", leave);
1318
- anchorEl.removeEventListener("focus", enter);
1319
- anchorEl.removeEventListener("blur", leave);
2088
+ if (hideTimeout)
2089
+ clearTimeout(hideTimeout);
2090
+ anchorEl.removeEventListener("mouseenter", show);
2091
+ anchorEl.removeEventListener("mouseleave", scheduleHide);
2092
+ div.removeEventListener("mouseenter", show);
2093
+ div.removeEventListener("mouseleave", scheduleHide);
2094
+ anchorEl.removeEventListener("focus", show);
2095
+ anchorEl.removeEventListener("blur", scheduleHide);
1320
2096
  };
1321
2097
  }
1322
2098
  if (opts.trigger === "click") {
@@ -1366,21 +2142,330 @@ function showTooltip(anchorEl, overlayRoot, opts) {
1366
2142
  return handle;
1367
2143
  }
1368
2144
 
2145
+ // ../adaptives/adaptive-overlays/dist/WorkflowWidget.js
2146
+ import { jsx as _jsx2 } from "react/jsx-runtime";
2147
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
2148
+ import { createRoot } from "react-dom/client";
2149
+
2150
+ // ../adaptives/adaptive-overlays/dist/WorkflowTracker.js
2151
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2152
+ function ProgressBar({ completed, total }) {
2153
+ const percent = total > 0 ? Math.round(completed / total * 100) : 0;
2154
+ return _jsx("div", { className: "se-w-full se-h-1.5 se-rounded-full se-bg-white/[0.08] se-overflow-hidden", children: _jsx("div", { role: "progressbar", "aria-valuenow": percent, "aria-valuemin": 0, "aria-valuemax": 100, className: "se-h-full se-rounded-full se-bg-blue-5 se-transition-all se-duration-300", style: { width: `${percent}%` } }) });
2155
+ }
2156
+ function StepItem({ step, isCompleted, isCurrent, tourId, onStepClick }) {
2157
+ return _jsxs("button", { type: "button", "data-testid": `step-${step.id}`, "data-current": isCurrent ? "true" : void 0, "data-completed": isCompleted ? "true" : void 0, "aria-current": isCurrent ? "step" : void 0, className: `se-flex se-items-center se-gap-2 se-w-full se-py-1.5 se-px-2 se-rounded se-border-none se-bg-transparent se-cursor-pointer se-text-left se-text-xs ${isCurrent ? "se-font-semibold se-text-slate-grey-10" : "se-text-slate-grey-8"}`, onClick: () => onStepClick(tourId, step.id), children: [_jsx("span", { className: "se-shrink-0 se-w-4 se-text-center", children: isCompleted ? _jsx("span", { role: "img", "aria-label": "completed", className: "se-text-green-5", children: "\u2713" }) : isCurrent ? _jsx("span", { className: "se-inline-block se-w-1.5 se-h-1.5 se-rounded-full se-bg-blue-5" }) : _jsx("span", { className: "se-inline-block se-w-1.5 se-h-1.5 se-rounded-full se-bg-white/[0.12]" }) }), _jsx("span", { className: "se-flex-1 se-overflow-hidden se-text-ellipsis se-whitespace-nowrap", children: step.title })] });
2158
+ }
2159
+ function WorkflowCard({ workflow, expanded, onStepClick, onDismiss }) {
2160
+ const completedCount = workflow.completedSteps.length;
2161
+ const totalSteps = workflow.steps.length;
2162
+ return _jsxs("div", { className: "se-p-3 se-rounded-lg se-border se-border-white/[0.08] se-bg-white/[0.02]", children: [_jsxs("div", { className: "se-flex se-items-center se-gap-2 se-mb-2", children: [workflow.meta.icon && _jsx("span", { "data-testid": "workflow-icon", className: "se-shrink-0 se-text-sm", children: workflow.meta.icon }), _jsx("span", { className: "se-flex-1 se-text-[13px] se-font-semibold se-text-slate-grey-10 se-overflow-hidden se-text-ellipsis se-whitespace-nowrap", children: workflow.meta.title }), _jsx("button", { type: "button", "data-testid": `dismiss-${workflow.tourId}`, className: "se-shrink-0 se-py-0.5 se-px-1.5 se-rounded se-border-none se-bg-transparent se-text-slate-grey-7 se-text-xs se-cursor-pointer se-leading-none", onClick: () => onDismiss(workflow.tourId), "aria-label": `Dismiss ${workflow.meta.title}`, children: "\u2715" })] }), _jsxs("div", { className: "se-mb-2", children: [_jsx(ProgressBar, { completed: completedCount, total: totalSteps }), _jsxs("div", { className: "se-text-[10px] se-text-slate-grey-7 se-mt-1", children: [completedCount, " of ", totalSteps, " steps"] })] }), _jsx("div", { className: "se-flex se-flex-col", children: workflow.steps.map((step) => _jsx(StepItem, { step, isCompleted: workflow.completedSteps.includes(step.id), isCurrent: workflow.currentStepId === step.id, tourId: workflow.tourId, onStepClick }, step.id)) }), expanded && workflow.meta.description && _jsx("div", { className: "se-mt-2 se-text-[11px] se-text-slate-grey-7", children: workflow.meta.description })] });
2163
+ }
2164
+ function WorkflowTracker({ workflows, expanded, onStepClick, onDismiss }) {
2165
+ if (workflows.length === 0) {
2166
+ return _jsx("div", { className: "se-flex se-items-center se-justify-center se-py-6 se-text-xs se-text-slate-grey-7", children: "No active workflows" });
2167
+ }
2168
+ return _jsx("div", { className: "se-flex se-flex-col se-gap-2", children: workflows.map((workflow) => _jsx(WorkflowCard, { workflow, expanded, onStepClick, onDismiss }, workflow.tourId)) });
2169
+ }
2170
+
2171
+ // ../adaptives/adaptive-overlays/dist/WorkflowWidget.js
2172
+ function showWorkflowToast(container, notification) {
2173
+ const toast = document.createElement("div");
2174
+ toast.setAttribute("data-testid", "workflow-toast");
2175
+ toast.className = "se-fixed se-bottom-4 se-right-4 se-z-50";
2176
+ Object.assign(toast.style, {
2177
+ position: "fixed",
2178
+ bottom: "16px",
2179
+ right: "16px",
2180
+ zIndex: "2147483646",
2181
+ padding: "12px 16px",
2182
+ borderRadius: "8px",
2183
+ backgroundColor: "var(--se-color-bg-surface, #fff)",
2184
+ color: "var(--se-color-text-primary, #1a1a1a)",
2185
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
2186
+ maxWidth: "320px",
2187
+ fontFamily: "var(--se-font-family, system-ui, sans-serif)",
2188
+ fontSize: "14px",
2189
+ lineHeight: "1.4",
2190
+ transition: "opacity 0.3s ease"
2191
+ });
2192
+ const titleEl = document.createElement("div");
2193
+ titleEl.className = "se-font-semibold";
2194
+ titleEl.style.fontWeight = "600";
2195
+ titleEl.textContent = notification.title;
2196
+ toast.appendChild(titleEl);
2197
+ if (notification.body) {
2198
+ const bodyEl = document.createElement("div");
2199
+ bodyEl.className = "se-mt-1 se-text-sm";
2200
+ bodyEl.style.marginTop = "4px";
2201
+ bodyEl.style.fontSize = "13px";
2202
+ bodyEl.style.color = "var(--se-color-text-secondary, #666)";
2203
+ bodyEl.textContent = notification.body;
2204
+ toast.appendChild(bodyEl);
2205
+ }
2206
+ container.appendChild(toast);
2207
+ let removeTimer;
2208
+ const fadeTimer = setTimeout(() => {
2209
+ toast.style.opacity = "0";
2210
+ removeTimer = setTimeout(() => {
2211
+ toast.remove();
2212
+ }, 300);
2213
+ }, 4e3);
2214
+ return () => {
2215
+ clearTimeout(fadeTimer);
2216
+ clearTimeout(removeTimer);
2217
+ toast.remove();
2218
+ };
2219
+ }
2220
+ function extractWorkflowsFromActive(activeActions) {
2221
+ const workflows = /* @__PURE__ */ new Map();
2222
+ for (const entry of activeActions) {
2223
+ const action = entry.action;
2224
+ if (action.kind === "core:tour" && action.workflow && action.tourId) {
2225
+ const meta = action.workflow;
2226
+ const rawSteps = action.steps || [];
2227
+ const steps = rawSteps.map((s) => {
2228
+ var _a2;
2229
+ return {
2230
+ id: s.id,
2231
+ title: ((_a2 = meta.stepTitles) == null ? void 0 : _a2[s.id]) || s.id
2232
+ };
2233
+ });
2234
+ workflows.set(action.tourId, { meta, steps });
2235
+ }
2236
+ }
2237
+ return workflows;
2238
+ }
2239
+ function WorkflowWidgetInner({ runtime: runtime3 }) {
2240
+ var _a2, _b;
2241
+ const [actionVersion, setActionVersion] = useState(0);
2242
+ const tourWorkflows = useMemo(() => {
2243
+ var _a3, _b2;
2244
+ const active = ((_b2 = (_a3 = runtime3 == null ? void 0 : runtime3.actions) == null ? void 0 : _a3.getActive) == null ? void 0 : _b2.call(_a3)) || [];
2245
+ return extractWorkflowsFromActive(active);
2246
+ }, [runtime3, actionVersion]);
2247
+ const tourWorkflowsRef = useRef(tourWorkflows);
2248
+ tourWorkflowsRef.current = tourWorkflows;
2249
+ useEffect(() => {
2250
+ var _a3;
2251
+ if (!((_a3 = runtime3 == null ? void 0 : runtime3.events) == null ? void 0 : _a3.subscribe))
2252
+ return;
2253
+ return runtime3.events.subscribe({ names: ["tour.started", "tour.resumed"] }, () => setActionVersion((v) => v + 1));
2254
+ }, [runtime3]);
2255
+ const stateNs = useMemo(() => {
2256
+ var _a3, _b2, _c;
2257
+ return (_c = (_b2 = (_a3 = runtime3 == null ? void 0 : runtime3.state) == null ? void 0 : _a3.user) == null ? void 0 : _b2.ns) == null ? void 0 : _c.call(_b2, "workflows");
2258
+ }, [runtime3]);
2259
+ const notifiedRef = useRef(/* @__PURE__ */ new Set());
2260
+ const toastCleanupsRef = useRef([]);
2261
+ const containerRef = useRef(null);
2262
+ const completedMapRef = useRef({});
2263
+ const [workflowEntries, setWorkflowEntries] = useState([]);
2264
+ const persistInitialized = useRef(false);
2265
+ if (!persistInitialized.current && stateNs) {
2266
+ const notified = ((_a2 = stateNs.get) == null ? void 0 : _a2.call(stateNs, "notified")) || [];
2267
+ for (const id of notified) {
2268
+ notifiedRef.current.add(id);
2269
+ }
2270
+ const completed = ((_b = stateNs.get) == null ? void 0 : _b.call(stateNs, "completed")) || {};
2271
+ completedMapRef.current = { ...completed };
2272
+ persistInitialized.current = true;
2273
+ }
2274
+ useEffect(() => {
2275
+ var _a3, _b2, _c;
2276
+ if (tourWorkflows.size === 0)
2277
+ return;
2278
+ const dismissed = ((_a3 = stateNs == null ? void 0 : stateNs.get) == null ? void 0 : _a3.call(stateNs, "dismissed")) || [];
2279
+ const completed = ((_b2 = stateNs == null ? void 0 : stateNs.get) == null ? void 0 : _b2.call(stateNs, "completed")) || {};
2280
+ setWorkflowEntries((prev) => {
2281
+ const existingIds = new Set(prev.map((e) => e.tourId));
2282
+ const newEntries = [];
2283
+ for (const [tourId, { meta, steps }] of tourWorkflows) {
2284
+ if (existingIds.has(tourId))
2285
+ continue;
2286
+ let status = "active";
2287
+ if (dismissed.includes(tourId)) {
2288
+ status = "dismissed";
2289
+ } else if (completed[tourId]) {
2290
+ status = "completed";
2291
+ }
2292
+ newEntries.push({
2293
+ tourId,
2294
+ meta,
2295
+ steps,
2296
+ currentStepId: null,
2297
+ completedSteps: [],
2298
+ status,
2299
+ completedAt: completed[tourId] || void 0
2300
+ });
2301
+ }
2302
+ return newEntries.length > 0 ? [...prev, ...newEntries] : prev;
2303
+ });
2304
+ for (const [tourId, { meta }] of tourWorkflows) {
2305
+ if (!notifiedRef.current.has(tourId) && meta.notification && containerRef.current && !dismissed.includes(tourId) && !completed[tourId]) {
2306
+ notifiedRef.current.add(tourId);
2307
+ (_c = stateNs == null ? void 0 : stateNs.set) == null ? void 0 : _c.call(stateNs, "notified", [...notifiedRef.current]);
2308
+ const cleanup = showWorkflowToast(containerRef.current, meta.notification);
2309
+ toastCleanupsRef.current.push(cleanup);
2310
+ }
2311
+ }
2312
+ }, [tourWorkflows, stateNs]);
2313
+ useEffect(() => {
2314
+ var _a3;
2315
+ if (!((_a3 = runtime3 == null ? void 0 : runtime3.events) == null ? void 0 : _a3.subscribe))
2316
+ return;
2317
+ const unsubscribe = runtime3.events.subscribe({ patterns: ["^tour\\."] }, (event) => {
2318
+ var _a4;
2319
+ const tourId = (_a4 = event.props) == null ? void 0 : _a4.tourId;
2320
+ if (!tourId)
2321
+ return;
2322
+ const currentWorkflows = tourWorkflowsRef.current;
2323
+ if (!currentWorkflows.has(tourId) && event.name === "tour.started") {
2324
+ setActionVersion((v) => v + 1);
2325
+ return;
2326
+ }
2327
+ if (!currentWorkflows.has(tourId))
2328
+ return;
2329
+ setWorkflowEntries((prev) => {
2330
+ const updated = prev.map((entry) => {
2331
+ var _a5, _b2, _c, _d, _e, _f, _g, _h;
2332
+ if (entry.tourId !== tourId)
2333
+ return entry;
2334
+ switch (event.name) {
2335
+ case "tour.started": {
2336
+ const startStepId = ((_a5 = event.props) == null ? void 0 : _a5.startStepId) || ((_b2 = entry.steps[0]) == null ? void 0 : _b2.id) || null;
2337
+ if (!notifiedRef.current.has(tourId)) {
2338
+ notifiedRef.current.add(tourId);
2339
+ (_c = stateNs == null ? void 0 : stateNs.set) == null ? void 0 : _c.call(stateNs, "notified", [...notifiedRef.current]);
2340
+ const workflow = currentWorkflows.get(tourId);
2341
+ if ((workflow == null ? void 0 : workflow.meta.notification) && containerRef.current) {
2342
+ const cleanup = showWorkflowToast(containerRef.current, workflow.meta.notification);
2343
+ toastCleanupsRef.current.push(cleanup);
2344
+ }
2345
+ }
2346
+ const activeIds = prev.filter((e) => e.status === "active" || e.tourId === tourId).map((e) => e.tourId);
2347
+ if (!activeIds.includes(tourId)) {
2348
+ activeIds.push(tourId);
2349
+ }
2350
+ (_d = stateNs == null ? void 0 : stateNs.set) == null ? void 0 : _d.call(stateNs, "active", [...new Set(activeIds)]);
2351
+ return {
2352
+ ...entry,
2353
+ status: "active",
2354
+ currentStepId: startStepId,
2355
+ completedSteps: entry.status === "active" ? entry.completedSteps : []
2356
+ };
2357
+ }
2358
+ case "tour.step_started": {
2359
+ const stepId = (_e = event.props) == null ? void 0 : _e.stepId;
2360
+ return {
2361
+ ...entry,
2362
+ currentStepId: stepId || entry.currentStepId
2363
+ };
2364
+ }
2365
+ case "tour.step_changed": {
2366
+ const previousStepId = (_f = event.props) == null ? void 0 : _f.previousStepId;
2367
+ const nextStepId = (_g = event.props) == null ? void 0 : _g.nextStepId;
2368
+ const completedSteps = previousStepId && !entry.completedSteps.includes(previousStepId) ? [...entry.completedSteps, previousStepId] : entry.completedSteps;
2369
+ return {
2370
+ ...entry,
2371
+ currentStepId: nextStepId || entry.currentStepId,
2372
+ completedSteps
2373
+ };
2374
+ }
2375
+ case "tour.completed": {
2376
+ const completedAt = Date.now();
2377
+ completedMapRef.current[tourId] = completedAt;
2378
+ (_h = stateNs == null ? void 0 : stateNs.set) == null ? void 0 : _h.call(stateNs, "completed", { ...completedMapRef.current });
2379
+ return {
2380
+ ...entry,
2381
+ status: "completed",
2382
+ currentStepId: null,
2383
+ completedSteps: entry.steps.map((s) => s.id),
2384
+ completedAt
2385
+ };
2386
+ }
2387
+ case "tour.paused": {
2388
+ return entry;
2389
+ }
2390
+ default:
2391
+ return entry;
2392
+ }
2393
+ });
2394
+ return updated;
2395
+ });
2396
+ });
2397
+ return () => {
2398
+ unsubscribe();
2399
+ for (const cleanup of toastCleanupsRef.current) {
2400
+ cleanup();
2401
+ }
2402
+ toastCleanupsRef.current = [];
2403
+ };
2404
+ }, [runtime3, stateNs]);
2405
+ const handleStepClick = useCallback((tourId, stepId) => {
2406
+ var _a3, _b2;
2407
+ (_b2 = (_a3 = runtime3 == null ? void 0 : runtime3.events) == null ? void 0 : _a3.publish) == null ? void 0 : _b2.call(_a3, "workflow:jump_to_step", { tourId, stepId });
2408
+ }, [runtime3]);
2409
+ const handleDismiss = useCallback((tourId) => {
2410
+ setWorkflowEntries((prev) => {
2411
+ var _a3;
2412
+ const updated = prev.map((entry) => entry.tourId === tourId ? { ...entry, status: "dismissed" } : entry);
2413
+ const dismissedIds = updated.filter((e) => e.status === "dismissed").map((e) => e.tourId);
2414
+ (_a3 = stateNs == null ? void 0 : stateNs.set) == null ? void 0 : _a3.call(stateNs, "dismissed", dismissedIds);
2415
+ return updated;
2416
+ });
2417
+ }, [stateNs]);
2418
+ const activeWorkflows = useMemo(() => workflowEntries.filter((w) => w.status === "active"), [workflowEntries]);
2419
+ return _jsx2("div", { ref: containerRef, children: _jsx2(WorkflowTracker, { workflows: activeWorkflows, onStepClick: handleStepClick, onDismiss: handleDismiss }) });
2420
+ }
2421
+ var WorkflowMountableWidget = {
2422
+ mount(container, config) {
2423
+ const runtime3 = (config == null ? void 0 : config.runtime) || null;
2424
+ const root = createRoot(container);
2425
+ root.render(React.createElement(WorkflowWidgetInner, {
2426
+ runtime: runtime3
2427
+ }));
2428
+ return () => {
2429
+ root.unmount();
2430
+ };
2431
+ }
2432
+ };
2433
+
1369
2434
  // ../adaptives/adaptive-overlays/dist/runtime.js
1370
2435
  var executeHighlight = async (action, context) => {
1371
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1372
- const anchorEl = context.resolveAnchor(action.anchorId);
2436
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2437
+ let anchorEl = context.resolveAnchor(action.anchorId);
2438
+ if (!anchorEl && context.waitForAnchor) {
2439
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
2440
+ }
1373
2441
  if (!anchorEl) {
1374
- throw new Error(`Anchor not found: ${action.anchorId}`);
2442
+ console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
2443
+ return { cleanup: () => {
2444
+ } };
2445
+ }
2446
+ const existing = anchorEl.getAttribute("data-syntro-highlight");
2447
+ if (existing) {
2448
+ const prev = context.overlayRoot.querySelectorAll(".syntro-spotlight-scrim, .syntro-spotlight-ring");
2449
+ prev.forEach((el) => el.remove());
2450
+ }
2451
+ anchorEl.setAttribute("data-syntro-highlight", "true");
2452
+ let ringColor = (_a2 = action.style) == null ? void 0 : _a2.color;
2453
+ if (!ringColor) {
2454
+ try {
2455
+ const primary = (_b = getComputedStyle(context.overlayRoot).getPropertyValue("--sc-color-primary")) == null ? void 0 : _b.trim();
2456
+ if (primary)
2457
+ ringColor = primary;
2458
+ } catch {
2459
+ }
1375
2460
  }
1376
2461
  const handle = showHighlight(anchorEl, context.overlayRoot, {
1377
- paddingPx: (_b = (_a2 = action.style) == null ? void 0 : _a2.paddingPx) != null ? _b : 12,
1378
- radiusPx: (_d = (_c = action.style) == null ? void 0 : _c.radiusPx) != null ? _d : 12,
1379
- scrimOpacity: (_f = (_e = action.style) == null ? void 0 : _e.scrimOpacity) != null ? _f : 0.55,
1380
- ringColor: (_g = action.style) == null ? void 0 : _g.color,
1381
- blocking: (_h = action.blocking) != null ? _h : false,
1382
- onClickOutside: (_i = action.onClickOutside) != null ? _i : true,
1383
- onEsc: (_j = action.onEsc) != null ? _j : true
2462
+ paddingPx: (_d = (_c = action.style) == null ? void 0 : _c.paddingPx) != null ? _d : 12,
2463
+ radiusPx: (_f = (_e = action.style) == null ? void 0 : _e.radiusPx) != null ? _f : 12,
2464
+ scrimOpacity: (_h = (_g = action.style) == null ? void 0 : _g.scrimOpacity) != null ? _h : 0.55,
2465
+ ringColor,
2466
+ blocking: (_i = action.blocking) != null ? _i : false,
2467
+ onClickOutside: (_j = action.onClickOutside) != null ? _j : true,
2468
+ onEsc: (_k = action.onEsc) != null ? _k : true
1384
2469
  });
1385
2470
  context.publishEvent("action.applied", {
1386
2471
  id: context.generateId(),
@@ -1390,33 +2475,82 @@ var executeHighlight = async (action, context) => {
1390
2475
  return {
1391
2476
  cleanup: () => {
1392
2477
  handle.destroy();
2478
+ anchorEl.removeAttribute("data-syntro-highlight");
1393
2479
  }
1394
2480
  };
1395
2481
  };
1396
2482
  var executePulse = async (action, context) => {
1397
- var _a2;
1398
- const anchorEl = context.resolveAnchor(action.anchorId);
2483
+ var _a2, _b, _c;
2484
+ let anchorEl = context.resolveAnchor(action.anchorId);
2485
+ if (!anchorEl && context.waitForAnchor) {
2486
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
2487
+ }
1399
2488
  if (!anchorEl) {
1400
- throw new Error(`Anchor not found: ${action.anchorId}`);
2489
+ console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
2490
+ return { cleanup: () => {
2491
+ } };
2492
+ }
2493
+ const duration = (_a2 = action.duration) != null ? _a2 : 4e3;
2494
+ await new Promise((resolve) => requestAnimationFrame(resolve));
2495
+ const parseHex = (hex) => ({
2496
+ r: parseInt(hex.slice(1, 3), 16),
2497
+ g: parseInt(hex.slice(3, 5), 16),
2498
+ b: parseInt(hex.slice(5, 7), 16)
2499
+ });
2500
+ const fallback = { r: 79, g: 70, b: 229 };
2501
+ let primary = fallback;
2502
+ let secondary = null;
2503
+ try {
2504
+ const styles = getComputedStyle(context.overlayRoot);
2505
+ const pHex = (_b = styles.getPropertyValue("--sc-color-primary")) == null ? void 0 : _b.trim();
2506
+ const sHex = (_c = styles.getPropertyValue("--sc-color-primary-hover")) == null ? void 0 : _c.trim();
2507
+ if ((pHex == null ? void 0 : pHex.startsWith("#")) && pHex.length >= 7) {
2508
+ primary = parseHex(pHex);
2509
+ }
2510
+ if ((sHex == null ? void 0 : sHex.startsWith("#")) && sHex.length >= 7) {
2511
+ secondary = parseHex(sHex);
2512
+ }
2513
+ } catch {
1401
2514
  }
1402
- const duration = (_a2 = action.duration) != null ? _a2 : 2e3;
1403
- if (!document.querySelector("[data-syntro-pulse-styles]")) {
1404
- const style = document.createElement("style");
1405
- style.setAttribute("data-syntro-pulse-styles", "");
2515
+ const existing = document.querySelector("[data-syntro-pulse-styles]");
2516
+ if (existing)
2517
+ existing.remove();
2518
+ const style = document.createElement("style");
2519
+ style.setAttribute("data-syntro-pulse-styles", "");
2520
+ const { r: pr, g: pg, b: pb } = primary;
2521
+ if (secondary) {
2522
+ const { r: sr, g: sg, b: sb } = secondary;
2523
+ style.textContent = `
2524
+ @keyframes syntro-pulse-anim {
2525
+ 0%, 100% {
2526
+ box-shadow: 0 0 0 0 rgba(${pr}, ${pg}, ${pb}, 0.35);
2527
+ }
2528
+ 25% {
2529
+ box-shadow: 0 0 0 12px rgba(${pr}, ${pg}, ${pb}, 0);
2530
+ }
2531
+ 50% {
2532
+ box-shadow: 0 0 0 0 rgba(${sr}, ${sg}, ${sb}, 0.35);
2533
+ }
2534
+ 75% {
2535
+ box-shadow: 0 0 0 12px rgba(${sr}, ${sg}, ${sb}, 0);
2536
+ }
2537
+ }
2538
+ `;
2539
+ } else {
1406
2540
  style.textContent = `
1407
2541
  @keyframes syntro-pulse-anim {
1408
2542
  0%, 100% {
1409
- box-shadow: 0 0 0 0 rgba(79, 70, 229, 0.4);
2543
+ box-shadow: 0 0 0 0 rgba(${pr}, ${pg}, ${pb}, 0.35);
1410
2544
  }
1411
2545
  50% {
1412
- box-shadow: 0 0 0 8px rgba(79, 70, 229, 0);
2546
+ box-shadow: 0 0 0 12px rgba(${pr}, ${pg}, ${pb}, 0);
1413
2547
  }
1414
2548
  }
1415
2549
  `;
1416
- document.head.appendChild(style);
1417
2550
  }
2551
+ document.head.appendChild(style);
1418
2552
  const originalAnimation = anchorEl.style.animation;
1419
- anchorEl.style.animation = "syntro-pulse-anim 1s cubic-bezier(0.4, 0, 0.6, 1) infinite";
2553
+ anchorEl.style.animation = "syntro-pulse-anim 2.5s cubic-bezier(0.4, 0, 0.6, 1) infinite";
1420
2554
  anchorEl.setAttribute("data-syntro-pulse", "true");
1421
2555
  const timeoutId = setTimeout(() => {
1422
2556
  anchorEl.style.animation = originalAnimation;
@@ -1437,14 +2571,27 @@ var executePulse = async (action, context) => {
1437
2571
  };
1438
2572
  };
1439
2573
  var executeBadge = async (action, context) => {
1440
- var _a2;
1441
- const anchorEl = context.resolveAnchor(action.anchorId);
2574
+ var _a2, _b;
2575
+ let anchorEl = context.resolveAnchor(action.anchorId);
2576
+ if (!anchorEl && context.waitForAnchor) {
2577
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
2578
+ }
1442
2579
  if (!anchorEl) {
1443
- throw new Error(`Anchor not found: ${action.anchorId}`);
2580
+ console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
2581
+ return { cleanup: () => {
2582
+ } };
2583
+ }
2584
+ let badgeColor = "#4f46e5";
2585
+ try {
2586
+ const primary = (_a2 = getComputedStyle(context.overlayRoot).getPropertyValue("--sc-color-primary")) == null ? void 0 : _a2.trim();
2587
+ if ((primary == null ? void 0 : primary.startsWith("#")) && primary.length >= 7) {
2588
+ badgeColor = primary;
2589
+ }
2590
+ } catch {
1444
2591
  }
1445
2592
  const badge2 = document.createElement("div");
1446
2593
  badge2.textContent = action.content;
1447
- badge2.setAttribute("data-syntro-badge", action.anchorId);
2594
+ badge2.setAttribute("data-syntro-badge", action.anchorId.selector);
1448
2595
  Object.assign(badge2.style, {
1449
2596
  position: "absolute",
1450
2597
  padding: "2px 6px",
@@ -1452,13 +2599,13 @@ var executeBadge = async (action, context) => {
1452
2599
  fontWeight: "600",
1453
2600
  lineHeight: "1",
1454
2601
  color: "white",
1455
- background: "var(--syntro-accent, #4f46e5)",
2602
+ background: badgeColor,
1456
2603
  borderRadius: "9999px",
1457
2604
  pointerEvents: "none",
1458
2605
  zIndex: "2147483646",
1459
2606
  whiteSpace: "nowrap"
1460
2607
  });
1461
- const position = (_a2 = action.position) != null ? _a2 : "top-right";
2608
+ const position = (_b = action.position) != null ? _b : "top-right";
1462
2609
  const originalPosition = anchorEl.style.position;
1463
2610
  if (getComputedStyle(anchorEl).position === "static") {
1464
2611
  anchorEl.style.position = "relative";
@@ -1501,9 +2648,14 @@ var executeBadge = async (action, context) => {
1501
2648
  };
1502
2649
  var executeTooltip = async (action, context) => {
1503
2650
  var _a2, _b, _c, _d;
1504
- const anchorEl = context.resolveAnchor(action.anchorId);
2651
+ let anchorEl = context.resolveAnchor(action.anchorId);
2652
+ if (!anchorEl && context.waitForAnchor) {
2653
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
2654
+ }
1505
2655
  if (!anchorEl) {
1506
- throw new Error(`Anchor not found: ${action.anchorId}`);
2656
+ console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
2657
+ return { cleanup: () => {
2658
+ } };
1507
2659
  }
1508
2660
  const { content } = action;
1509
2661
  let html = "";
@@ -1576,14 +2728,27 @@ var executors2 = [
1576
2728
  { kind: "overlays:pulse", executor: executePulse },
1577
2729
  { kind: "overlays:badge", executor: executeBadge },
1578
2730
  { kind: "overlays:tooltip", executor: executeTooltip },
1579
- { kind: "overlays:modal", executor: executeModal }
2731
+ { kind: "overlays:modal", executor: executeModal },
2732
+ { kind: "core:tour", executor: executeTour },
2733
+ { kind: "overlays:celebrate", executor: executeCelebrate }
1580
2734
  ];
1581
2735
  var runtime2 = {
1582
2736
  id: "adaptive-overlays",
1583
2737
  version: "1.0.0",
1584
2738
  name: "Overlays",
1585
- description: "Tooltips, highlights, badges, modals, and visual overlays",
1586
- executors: executors2
2739
+ description: "Tooltips, highlights, badges, modals, celebrations, visual overlays, and workflow tracking",
2740
+ executors: executors2,
2741
+ widgets: [
2742
+ {
2743
+ id: "adaptive-overlays:workflow-tracker",
2744
+ component: WorkflowMountableWidget,
2745
+ metadata: {
2746
+ name: "Workflow Tracker",
2747
+ icon: "\u{1F4CB}",
2748
+ description: "Tracks multi-step workflow progress across tours"
2749
+ }
2750
+ }
2751
+ ]
1587
2752
  };
1588
2753
 
1589
2754
  // src/apps/AppContext.ts
@@ -2193,7 +3358,7 @@ function getAntiFlickerSnippet(config = {}) {
2193
3358
  }
2194
3359
 
2195
3360
  // src/version.ts
2196
- var SDK_VERSION = "2.4.1";
3361
+ var SDK_VERSION = "2.6.0-canary.1";
2197
3362
 
2198
3363
  // src/types.ts
2199
3364
  var SDK_SCHEMA_VERSION = "2.0";
@@ -2434,44 +3599,69 @@ var createCanvasConfigFetcher = ({
2434
3599
  };
2435
3600
 
2436
3601
  // src/events/registerConfigPredicates.ts
2437
- function registerConfigPredicates(tiles, accumulator) {
2438
- var _a2;
3602
+ var ELEMENT_CHAIN_FIELDS = /* @__PURE__ */ new Set(["tag_name", "$el_text"]);
3603
+ function isElementChainField(key) {
3604
+ return ELEMENT_CHAIN_FIELDS.has(key) || key.startsWith("attr__");
3605
+ }
3606
+ function checkMatchOp(op, value) {
3607
+ if (op.equals !== void 0) {
3608
+ return value === op.equals;
3609
+ }
3610
+ if (op.contains !== void 0) {
3611
+ return typeof value === "string" && value.includes(op.contains);
3612
+ }
3613
+ return false;
3614
+ }
3615
+ function buildPredicate(counter) {
3616
+ const { events: eventNames, match } = counter;
3617
+ return (event) => {
3618
+ var _a2, _b;
3619
+ if (!eventNames.includes(event.name)) return false;
3620
+ if (match) {
3621
+ for (const [key, op] of Object.entries(match)) {
3622
+ if (isElementChainField(key)) {
3623
+ const elements = (_a2 = event.props) == null ? void 0 : _a2.elements;
3624
+ if (!elements) return false;
3625
+ const anyMatch = elements.some((el) => checkMatchOp(op, el[key]));
3626
+ if (!anyMatch) return false;
3627
+ } else {
3628
+ if (!checkMatchOp(op, (_b = event.props) == null ? void 0 : _b[key])) return false;
3629
+ }
3630
+ }
3631
+ }
3632
+ return true;
3633
+ };
3634
+ }
3635
+ function registerFromTriggerWhen(triggerWhen, accumulator) {
3636
+ for (const rule of triggerWhen.rules) {
3637
+ for (const cond of rule.conditions) {
3638
+ if (cond.type === "event_count" && cond.key) {
3639
+ const counter = cond.counter;
3640
+ const predicate = counter ? buildPredicate(counter) : () => true;
3641
+ accumulator.register(cond.key, predicate);
3642
+ }
3643
+ }
3644
+ }
3645
+ }
3646
+ function registerConfigPredicates(tiles, accumulator, actions) {
3647
+ var _a2, _b;
2439
3648
  for (const tile of tiles) {
2440
3649
  const props = tile.props;
2441
3650
  if (!props) continue;
2442
- const scope = props.scope;
2443
- if (!(scope == null ? void 0 : scope.events)) continue;
2444
- const actions = props.actions;
2445
- if (!actions) continue;
2446
- const keys = /* @__PURE__ */ new Set();
2447
- for (const action of actions) {
2448
- if (((_a2 = action.showWhen) == null ? void 0 : _a2.type) === "rules") {
2449
- for (const rule of action.showWhen.rules) {
2450
- for (const cond of rule.conditions) {
2451
- if (cond.type === "event_count" && cond.key) {
2452
- keys.add(cond.key);
2453
- }
2454
- }
2455
- }
3651
+ const tileActions = props.actions;
3652
+ if (!tileActions) continue;
3653
+ for (const action of tileActions) {
3654
+ if (((_a2 = action.triggerWhen) == null ? void 0 : _a2.type) === "rules") {
3655
+ registerFromTriggerWhen(action.triggerWhen, accumulator);
2456
3656
  }
2457
3657
  }
2458
- if (keys.size === 0) continue;
2459
- const { events: eventNames, urlContains, props: propFilters } = scope;
2460
- for (const key of keys) {
2461
- accumulator.register(key, (event) => {
2462
- var _a3, _b, _c;
2463
- if (!eventNames.includes(event.name)) return false;
2464
- if (urlContains) {
2465
- const pathname = String((_b = (_a3 = event.props) == null ? void 0 : _a3.pathname) != null ? _b : "");
2466
- if (!pathname.includes(urlContains)) return false;
2467
- }
2468
- if (propFilters) {
2469
- for (const [k, v] of Object.entries(propFilters)) {
2470
- if (((_c = event.props) == null ? void 0 : _c[k]) !== v) return false;
2471
- }
2472
- }
2473
- return true;
2474
- });
3658
+ }
3659
+ if (actions) {
3660
+ for (const rawAction of actions) {
3661
+ const action = rawAction;
3662
+ if (((_b = action.triggerWhen) == null ? void 0 : _b.type) === "rules") {
3663
+ registerFromTriggerWhen(action.triggerWhen, accumulator);
3664
+ }
2475
3665
  }
2476
3666
  }
2477
3667
  }
@@ -2482,9 +3672,15 @@ var SmartCanvasController = class {
2482
3672
  __publicField(this, "state");
2483
3673
  __publicField(this, "listeners", /* @__PURE__ */ new Set());
2484
3674
  var _a2;
2485
- this.state = {
2486
- open: (_a2 = initial == null ? void 0 : initial.open) != null ? _a2 : false
2487
- };
3675
+ let open = (_a2 = initial == null ? void 0 : initial.open) != null ? _a2 : false;
3676
+ if ((initial == null ? void 0 : initial.open) === void 0) {
3677
+ try {
3678
+ const stored = sessionStorage == null ? void 0 : sessionStorage.getItem("syntro:editor:panel-open");
3679
+ if (stored !== null) open = JSON.parse(stored);
3680
+ } catch {
3681
+ }
3682
+ }
3683
+ this.state = { open };
2488
3684
  }
2489
3685
  getState() {
2490
3686
  return this.state;
@@ -2498,6 +3694,10 @@ var SmartCanvasController = class {
2498
3694
  setOpen(open) {
2499
3695
  if (this.state.open === open) return;
2500
3696
  this.state = { ...this.state, open };
3697
+ try {
3698
+ sessionStorage == null ? void 0 : sessionStorage.setItem("syntro:editor:panel-open", JSON.stringify(open));
3699
+ } catch {
3700
+ }
2501
3701
  this.emit();
2502
3702
  }
2503
3703
  toggle() {
@@ -2616,208 +3816,42 @@ function overlayCompleted(recipeId, recipeName) {
2616
3816
  return createCanvasEvent(StandardEvents.OVERLAY_COMPLETED, {
2617
3817
  recipeId,
2618
3818
  recipeName
2619
- });
2620
- }
2621
- function overlayDismissed(recipeId, recipeName, stepIndex) {
2622
- return createCanvasEvent(StandardEvents.OVERLAY_DISMISSED, {
2623
- recipeId,
2624
- recipeName,
2625
- stepIndex
2626
- });
2627
- }
2628
- function overlayStepViewed(recipeId, stepIndex, stepTitle) {
2629
- return createCanvasEvent(StandardEvents.OVERLAY_STEP_VIEWED, {
2630
- recipeId,
2631
- stepIndex,
2632
- stepTitle
2633
- });
2634
- }
2635
- function customCanvasEvent(name, props) {
2636
- const eventName = name.startsWith("canvas.") ? name : `canvas.${name}`;
2637
- return createCanvasEvent(eventName, props);
2638
- }
2639
- var CanvasEvents = {
2640
- canvasOpened,
2641
- canvasClosed,
2642
- tileViewed,
2643
- tileExpanded,
2644
- tileCollapsed,
2645
- tileAction,
2646
- overlayStarted,
2647
- overlayCompleted,
2648
- overlayDismissed,
2649
- overlayStepViewed,
2650
- custom: customCanvasEvent
2651
- };
2652
-
2653
- // src/theme/defaultTheme.ts
2654
- function withAlpha(hex, alpha) {
2655
- return `${hex}${Math.round(alpha * 255).toString(16).padStart(2, "0")}`;
2656
- }
2657
- var darkTheme = {
2658
- // Brand colors
2659
- colorPrimary: brand[3],
2660
- colorPrimaryHover: brand[4],
2661
- colorPrimaryMuted: brand[0],
2662
- // Surface colors (with alpha for glass/vibrancy effects)
2663
- colorBackground: withAlpha(slateGrey[1], 0.95),
2664
- colorBackgroundElevated: withAlpha(slateGrey[3], 0.95),
2665
- colorBackgroundSubtle: withAlpha(slateGrey[0], 0.2),
2666
- colorSurface: withAlpha(slateGrey[4], 0.8),
2667
- colorSurfaceHover: withAlpha(slateGrey[5], 0.8),
2668
- // Text colors
2669
- colorText: text.primary,
2670
- colorTextSecondary: text.secondary,
2671
- colorTextMuted: text.tertiary,
2672
- colorTextInverse: slateGrey[1],
2673
- // Border colors
2674
- colorBorder: border.primary,
2675
- colorBorderSubtle: border.secondary,
2676
- // Semantic colors
2677
- colorSuccess: green[4],
2678
- colorSuccessMuted: green[0],
2679
- colorWarning: yellow[4],
2680
- colorWarningMuted: yellow[0],
2681
- colorError: red[4],
2682
- colorErrorMuted: red[0],
2683
- colorInfo: blue[4],
2684
- colorInfoMuted: blue[0],
2685
- // Notification badge
2686
- colorNotification: brand[4],
2687
- colorNotificationMuted: withAlpha(brand[4], 0.4),
2688
- // Glass morphism
2689
- glassBackground: withAlpha(slateGrey[1], 0.6),
2690
- glassBackgroundHover: withAlpha(slateGrey[1], 0.7),
2691
- glassBorder: "rgba(255, 255, 255, 0.08)",
2692
- glassBlur: "blur(24px)",
2693
- glassSaturate: "saturate(1.2)",
2694
- // Typography (SF Pro stack)
2695
- fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif",
2696
- fontFamilyMono: "'SF Mono', 'Fira Code', Consolas, monospace",
2697
- fontSizeXs: "0.7rem",
2698
- fontSizeSm: "0.8rem",
2699
- fontSizeMd: "0.9rem",
2700
- fontSizeLg: "1rem",
2701
- fontSizeXl: "1.25rem",
2702
- fontSizeXxl: "1.5rem",
2703
- fontWeightNormal: "400",
2704
- fontWeightMedium: "500",
2705
- fontWeightSemibold: "600",
2706
- fontWeightBold: "700",
2707
- lineHeightTight: "1.25",
2708
- lineHeightNormal: "1.5",
2709
- lineHeightRelaxed: "1.75",
2710
- // Spacing
2711
- spacingXs: "0.25rem",
2712
- spacingSm: "0.5rem",
2713
- spacingMd: "0.75rem",
2714
- spacingLg: "1rem",
2715
- spacingXl: "1.5rem",
2716
- // Border radius
2717
- borderRadiusSm: "6px",
2718
- borderRadiusMd: "10px",
2719
- borderRadiusLg: "14px",
2720
- borderRadiusXl: "20px",
2721
- borderRadiusFull: "9999px",
2722
- // Shadows
2723
- shadowSm: "0 1px 2px rgba(0, 0, 0, 0.2)",
2724
- shadowMd: "0 2px 12px rgba(0, 0, 0, 0.3)",
2725
- shadowLg: "0 8px 24px rgba(0, 0, 0, 0.4)",
2726
- shadowXl: "0 16px 48px rgba(0, 0, 0, 0.5)",
2727
- // Backdrop effects
2728
- backdropBlur: "blur(20px)",
2729
- backdropSaturate: "saturate(180%)",
2730
- // Animation
2731
- transitionFast: "0.1s ease",
2732
- transitionNormal: "0.25s ease",
2733
- transitionSlow: "0.4s ease"
2734
- };
2735
- var lightTheme = {
2736
- // Brand colors
2737
- colorPrimary: brand[3],
2738
- colorPrimaryHover: brand[2],
2739
- colorPrimaryMuted: withAlpha(brand[5], 0.1),
2740
- // Surface colors
2741
- colorBackground: withAlpha(slateGrey[12], 0.95),
2742
- colorBackgroundElevated: withAlpha(slateGrey[11], 0.95),
2743
- colorBackgroundSubtle: withAlpha(slateGrey[0], 0.02),
2744
- colorSurface: withAlpha(slateGrey[11], 0.6),
2745
- colorSurfaceHover: withAlpha(slateGrey[10], 0.6),
2746
- // Text colors
2747
- colorText: slateGrey[1],
2748
- colorTextSecondary: slateGrey[6],
2749
- colorTextMuted: slateGrey[8],
2750
- colorTextInverse: slateGrey[12],
2751
- // Border colors
2752
- colorBorder: withAlpha(slateGrey[0], 0.12),
2753
- colorBorderSubtle: withAlpha(slateGrey[0], 0.06),
2754
- // Semantic colors
2755
- colorSuccess: green[4],
2756
- colorSuccessMuted: withAlpha(green[4], 0.12),
2757
- colorWarning: yellow[4],
2758
- colorWarningMuted: withAlpha(yellow[4], 0.12),
2759
- colorError: red[4],
2760
- colorErrorMuted: withAlpha(red[4], 0.12),
2761
- colorInfo: blue[4],
2762
- colorInfoMuted: withAlpha(blue[4], 0.12),
2763
- // Notification badge
2764
- colorNotification: brand[3],
2765
- colorNotificationMuted: withAlpha(brand[3], 0.4),
2766
- // Glass morphism
2767
- glassBackground: withAlpha(slateGrey[12], 0.7),
2768
- glassBackgroundHover: withAlpha(slateGrey[12], 0.8),
2769
- glassBorder: "rgba(0, 0, 0, 0.06)",
2770
- glassBlur: "blur(24px)",
2771
- glassSaturate: "saturate(1.2)",
2772
- // Typography (same as dark)
2773
- fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif",
2774
- fontFamilyMono: "'SF Mono', 'Fira Code', Consolas, monospace",
2775
- fontSizeXs: "0.7rem",
2776
- fontSizeSm: "0.8rem",
2777
- fontSizeMd: "0.9rem",
2778
- fontSizeLg: "1rem",
2779
- fontSizeXl: "1.25rem",
2780
- fontSizeXxl: "1.5rem",
2781
- fontWeightNormal: "400",
2782
- fontWeightMedium: "500",
2783
- fontWeightSemibold: "600",
2784
- fontWeightBold: "700",
2785
- lineHeightTight: "1.25",
2786
- lineHeightNormal: "1.5",
2787
- lineHeightRelaxed: "1.75",
2788
- // Spacing (same as dark)
2789
- spacingXs: "0.25rem",
2790
- spacingSm: "0.5rem",
2791
- spacingMd: "0.75rem",
2792
- spacingLg: "1rem",
2793
- spacingXl: "1.5rem",
2794
- // Border radius (same as dark)
2795
- borderRadiusSm: "6px",
2796
- borderRadiusMd: "10px",
2797
- borderRadiusLg: "14px",
2798
- borderRadiusXl: "20px",
2799
- borderRadiusFull: "9999px",
2800
- // Shadows (lighter for light mode)
2801
- shadowSm: "0 1px 2px rgba(0, 0, 0, 0.08)",
2802
- shadowMd: "0 2px 12px rgba(0, 0, 0, 0.12)",
2803
- shadowLg: "0 8px 24px rgba(0, 0, 0, 0.16)",
2804
- shadowXl: "0 16px 48px rgba(0, 0, 0, 0.2)",
2805
- // Backdrop effects
2806
- backdropBlur: "blur(20px)",
2807
- backdropSaturate: "saturate(180%)",
2808
- // Animation
2809
- transitionFast: "0.1s ease",
2810
- transitionNormal: "0.25s ease",
2811
- transitionSlow: "0.4s ease"
3819
+ });
3820
+ }
3821
+ function overlayDismissed(recipeId, recipeName, stepIndex) {
3822
+ return createCanvasEvent(StandardEvents.OVERLAY_DISMISSED, {
3823
+ recipeId,
3824
+ recipeName,
3825
+ stepIndex
3826
+ });
3827
+ }
3828
+ function overlayStepViewed(recipeId, stepIndex, stepTitle) {
3829
+ return createCanvasEvent(StandardEvents.OVERLAY_STEP_VIEWED, {
3830
+ recipeId,
3831
+ stepIndex,
3832
+ stepTitle
3833
+ });
3834
+ }
3835
+ function customCanvasEvent(name, props) {
3836
+ const eventName = name.startsWith("canvas.") ? name : `canvas.${name}`;
3837
+ return createCanvasEvent(eventName, props);
3838
+ }
3839
+ var CanvasEvents = {
3840
+ canvasOpened,
3841
+ canvasClosed,
3842
+ tileViewed,
3843
+ tileExpanded,
3844
+ tileCollapsed,
3845
+ tileAction,
3846
+ overlayStarted,
3847
+ overlayCompleted,
3848
+ overlayDismissed,
3849
+ overlayStepViewed,
3850
+ custom: customCanvasEvent
2812
3851
  };
2813
3852
 
2814
- // src/theme/ThemeProvider.tsx
2815
- import { createContext as createContext2, useContext as useContext2, useEffect, useMemo, useState } from "react";
2816
- import { jsx as jsx2 } from "react/jsx-runtime";
2817
- var ThemeContext = createContext2(null);
2818
-
2819
3853
  // src/notifications/NotificationToastStack.tsx
2820
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
3854
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
2821
3855
  var TOAST_STYLES_ID = "syntro-toast-styles";
2822
3856
  var TOAST_CSS = `
2823
3857
  @keyframes syntro-toast-slide-in {
@@ -2854,7 +3888,7 @@ function NotificationToastStack({
2854
3888
  const { shadowRoot } = useShadowRoot();
2855
3889
  ensureToastStyles(shadowRoot);
2856
3890
  if (notifications.length === 0) return null;
2857
- return /* @__PURE__ */ jsx3(
3891
+ return /* @__PURE__ */ jsx2(
2858
3892
  "div",
2859
3893
  {
2860
3894
  "data-testid": "notification-toast-stack",
@@ -2878,11 +3912,11 @@ function NotificationToastStack({
2878
3912
  {
2879
3913
  className: "syntro-toast-enter",
2880
3914
  style: {
2881
- background: withAlpha(slateGrey[0], 0.95),
3915
+ background: "var(--sc-notification-background)",
2882
3916
  backdropFilter: "blur(12px)",
2883
3917
  WebkitBackdropFilter: "blur(12px)",
2884
- border: `1px solid ${slateGrey[5]}`,
2885
- borderRadius: "12px",
3918
+ border: "var(--sc-notification-border)",
3919
+ borderRadius: "var(--sc-notification-border-radius)",
2886
3920
  overflow: "hidden",
2887
3921
  cursor: "pointer",
2888
3922
  transition: "transform 0.15s ease"
@@ -2909,14 +3943,14 @@ function NotificationToastStack({
2909
3943
  padding: "10px 12px"
2910
3944
  },
2911
3945
  children: [
2912
- /* @__PURE__ */ jsx3(
3946
+ /* @__PURE__ */ jsx2(
2913
3947
  "div",
2914
3948
  {
2915
3949
  style: {
2916
3950
  width: "28px",
2917
3951
  height: "28px",
2918
3952
  borderRadius: "8px",
2919
- background: withAlpha(brand[3], 0.15),
3953
+ background: "var(--sc-notification-icon-background)",
2920
3954
  display: "flex",
2921
3955
  alignItems: "center",
2922
3956
  justifyContent: "center",
@@ -2927,13 +3961,13 @@ function NotificationToastStack({
2927
3961
  }
2928
3962
  ),
2929
3963
  /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
2930
- /* @__PURE__ */ jsx3(
3964
+ /* @__PURE__ */ jsx2(
2931
3965
  "div",
2932
3966
  {
2933
3967
  style: {
2934
3968
  fontSize: "13px",
2935
3969
  fontWeight: 600,
2936
- color: text.primary,
3970
+ color: "var(--sc-notification-text-color)",
2937
3971
  lineHeight: "1.3",
2938
3972
  whiteSpace: "nowrap",
2939
3973
  overflow: "hidden",
@@ -2942,12 +3976,12 @@ function NotificationToastStack({
2942
3976
  children: notif.title
2943
3977
  }
2944
3978
  ),
2945
- notif.body && /* @__PURE__ */ jsx3(
3979
+ notif.body && /* @__PURE__ */ jsx2(
2946
3980
  "div",
2947
3981
  {
2948
3982
  style: {
2949
3983
  fontSize: "12px",
2950
- color: text.secondary,
3984
+ color: "var(--sc-notification-text-secondary-color)",
2951
3985
  lineHeight: "1.3",
2952
3986
  marginTop: "2px",
2953
3987
  whiteSpace: "nowrap",
@@ -2958,7 +3992,7 @@ function NotificationToastStack({
2958
3992
  }
2959
3993
  )
2960
3994
  ] }),
2961
- /* @__PURE__ */ jsx3(
3995
+ /* @__PURE__ */ jsx2(
2962
3996
  "button",
2963
3997
  {
2964
3998
  type: "button",
@@ -2970,7 +4004,7 @@ function NotificationToastStack({
2970
4004
  style: {
2971
4005
  background: "none",
2972
4006
  border: "none",
2973
- color: text.tertiary,
4007
+ color: "var(--sc-notification-text-secondary-color)",
2974
4008
  cursor: "pointer",
2975
4009
  padding: "2px",
2976
4010
  fontSize: "14px",
@@ -2983,13 +4017,13 @@ function NotificationToastStack({
2983
4017
  ]
2984
4018
  }
2985
4019
  ),
2986
- /* @__PURE__ */ jsx3("div", { style: { height: "2px", background: withAlpha(base.white, 0.06) }, children: /* @__PURE__ */ jsx3(
4020
+ /* @__PURE__ */ jsx2("div", { style: { height: "2px", background: "rgba(0, 0, 0, 0.08)" }, children: /* @__PURE__ */ jsx2(
2987
4021
  "div",
2988
4022
  {
2989
4023
  className: "syntro-toast-progress",
2990
4024
  style: {
2991
4025
  height: "100%",
2992
- background: `linear-gradient(90deg, ${brand[3]}, ${brand[4]})`,
4026
+ background: "var(--sc-notification-progress-gradient)",
2993
4027
  animationDuration: `${notif.ttl}ms`
2994
4028
  }
2995
4029
  }
@@ -3054,12 +4088,12 @@ function matchEvent(event, tiles) {
3054
4088
  }
3055
4089
 
3056
4090
  // src/notifications/useNotifications.ts
3057
- import { useCallback, useEffect as useEffect2, useRef, useState as useState2 } from "react";
4091
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
3058
4092
  function useNotifications(eventBus, tiles) {
3059
4093
  const [notifications, setNotifications] = useState2([]);
3060
- const cooldownMap = useRef(/* @__PURE__ */ new Map());
3061
- const timerIds = useRef(/* @__PURE__ */ new Map());
3062
- const publishDismissed = useCallback(
4094
+ const cooldownMap = useRef2(/* @__PURE__ */ new Map());
4095
+ const timerIds = useRef2(/* @__PURE__ */ new Map());
4096
+ const publishDismissed = useCallback2(
3063
4097
  (notif) => {
3064
4098
  eventBus == null ? void 0 : eventBus.publish(StandardEvents.NOTIFICATION_DISMISSED, {
3065
4099
  notificationId: notif.id,
@@ -3069,7 +4103,7 @@ function useNotifications(eventBus, tiles) {
3069
4103
  },
3070
4104
  [eventBus]
3071
4105
  );
3072
- const scheduleDismiss = useCallback(
4106
+ const scheduleDismiss = useCallback2(
3073
4107
  (notif) => {
3074
4108
  const timerId = setTimeout(() => {
3075
4109
  setNotifications((prev) => prev.filter((n) => n.id !== notif.id));
@@ -3080,7 +4114,7 @@ function useNotifications(eventBus, tiles) {
3080
4114
  },
3081
4115
  [publishDismissed]
3082
4116
  );
3083
- const dismiss = useCallback(
4117
+ const dismiss = useCallback2(
3084
4118
  (id) => {
3085
4119
  setNotifications((prev) => {
3086
4120
  const notif = prev.find((n) => n.id === id);
@@ -3102,7 +4136,7 @@ function useNotifications(eventBus, tiles) {
3102
4136
  const match = matchEvent(event, tiles);
3103
4137
  if (!match) return;
3104
4138
  const { notification: matched, rule: matchedRule } = match;
3105
- const cooldownKey = `${matched.tileId}:${event.name}`;
4139
+ const cooldownKey = matched.itemId ? `${matched.tileId}:${event.name}:${matched.itemId}` : `${matched.tileId}:${event.name}`;
3106
4140
  const lastFired = cooldownMap.current.get(cooldownKey);
3107
4141
  const now = Date.now();
3108
4142
  const cooldown = (_a2 = matchedRule.cooldown) != null ? _a2 : DEFAULT_COOLDOWN;
@@ -3146,7 +4180,7 @@ function useNotifications(eventBus, tiles) {
3146
4180
  }
3147
4181
 
3148
4182
  // src/notifications/useNotifyWatcher.ts
3149
- import { useEffect as useEffect3, useRef as useRef2 } from "react";
4183
+ import { useEffect as useEffect3, useRef as useRef3 } from "react";
3150
4184
  function collectEntries(tiles, appRegistry2) {
3151
4185
  var _a2, _b;
3152
4186
  const entries = [];
@@ -3161,7 +4195,7 @@ function collectEntries(tiles, appRegistry2) {
3161
4195
  return entries;
3162
4196
  }
3163
4197
  function useNotifyWatcher(runtime3, tiles, appRegistry2) {
3164
- const prevStateRef = useRef2(/* @__PURE__ */ new Map());
4198
+ const prevStateRef = useRef3(/* @__PURE__ */ new Map());
3165
4199
  useEffect3(() => {
3166
4200
  if (!(runtime3 == null ? void 0 : runtime3.events) || !appRegistry2) return;
3167
4201
  const entries = collectEntries(tiles, appRegistry2);
@@ -3196,9 +4230,9 @@ function useNotifyWatcher(runtime3, tiles, appRegistry2) {
3196
4230
  }
3197
4231
 
3198
4232
  // src/RuntimeProvider.tsx
3199
- import { createContext as createContext3, useContext as useContext3, useEffect as useEffect4, useMemo as useMemo2, useState as useState3 } from "react";
3200
- import { jsx as jsx4 } from "react/jsx-runtime";
3201
- var RuntimeReactContext = createContext3({
4233
+ import { createContext as createContext2, useContext as useContext2, useEffect as useEffect4, useMemo as useMemo2, useState as useState3 } from "react";
4234
+ import { jsx as jsx3 } from "react/jsx-runtime";
4235
+ var RuntimeReactContext = createContext2({
3202
4236
  runtime: null,
3203
4237
  context: null
3204
4238
  });
@@ -3215,14 +4249,14 @@ function RuntimeProvider({ runtime: runtime3, children }) {
3215
4249
  return unsubscribe;
3216
4250
  }, [runtime3]);
3217
4251
  const value = useMemo2(() => ({ runtime: runtime3, context }), [runtime3, context]);
3218
- return /* @__PURE__ */ jsx4(RuntimeReactContext.Provider, { value, children });
4252
+ return /* @__PURE__ */ jsx3(RuntimeReactContext.Provider, { value, children });
3219
4253
  }
3220
4254
  function useRuntime() {
3221
- const { runtime: runtime3 } = useContext3(RuntimeReactContext);
4255
+ const { runtime: runtime3 } = useContext2(RuntimeReactContext);
3222
4256
  return runtime3;
3223
4257
  }
3224
4258
  function useRuntimeContext() {
3225
- const { context } = useContext3(RuntimeReactContext);
4259
+ const { context } = useContext2(RuntimeReactContext);
3226
4260
  return context;
3227
4261
  }
3228
4262
  function usePageContext() {
@@ -3279,15 +4313,60 @@ function useDecision(strategy, defaultValue) {
3279
4313
  }
3280
4314
 
3281
4315
  // src/components/TileCard.tsx
3282
- import { useCallback as useCallback2, useEffect as useEffect5, useReducer, useRef as useRef3, useState as useState4 } from "react";
4316
+ import {
4317
+ useCallback as useCallback3,
4318
+ useEffect as useEffect5,
4319
+ useMemo as useMemo3,
4320
+ useReducer,
4321
+ useRef as useRef4,
4322
+ useState as useState4
4323
+ } from "react";
4324
+
4325
+ // src/components/TileIcon.tsx
4326
+ import {
4327
+ Compass,
4328
+ FileText,
4329
+ Gamepad2,
4330
+ HelpCircle,
4331
+ Layers,
4332
+ MessageCircle,
4333
+ Sparkles,
4334
+ Trophy
4335
+ } from "lucide-react";
4336
+ import { jsx as jsx4 } from "react/jsx-runtime";
4337
+ var ICON_MAP = {
4338
+ "\u2753": HelpCircle,
4339
+ "\u{1F9ED}": Compass,
4340
+ "\u{1F4DD}": FileText,
4341
+ "\u{1F3AF}": Layers,
4342
+ "\u{1F3C6}": Trophy,
4343
+ "\u2728": Sparkles,
4344
+ "\u{1F4AC}": MessageCircle,
4345
+ "\u{1F3AE}": Gamepad2
4346
+ };
4347
+ function TileIcon({
4348
+ emoji,
4349
+ size = 18,
4350
+ color = "currentColor"
4351
+ }) {
4352
+ const Icon = ICON_MAP[emoji];
4353
+ if (!Icon) {
4354
+ return /* @__PURE__ */ jsx4("span", { children: emoji });
4355
+ }
4356
+ return /* @__PURE__ */ jsx4(Icon, { size, color });
4357
+ }
4358
+
4359
+ // src/components/TileCard.tsx
3283
4360
  import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
3284
4361
  function WidgetMount({ widgetId, props }) {
3285
4362
  var _a2;
3286
4363
  const runtime3 = useRuntime();
3287
- const containerRef = useRef3(null);
3288
- const handleRef = useRef3(null);
4364
+ const parentRef = useRef4(null);
4365
+ const handleRef = useRef4(null);
3289
4366
  const registry = runtime3 == null ? void 0 : runtime3.widgets;
3290
4367
  const widgetAvailable = (_a2 = registry == null ? void 0 : registry.has(widgetId)) != null ? _a2 : false;
4368
+ const propsRef = useRef4(props);
4369
+ propsRef.current = props;
3291
4370
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
3292
4371
  useEffect5(() => {
3293
4372
  if (!registry || widgetAvailable) return;
@@ -3298,14 +4377,27 @@ function WidgetMount({ widgetId, props }) {
3298
4377
  });
3299
4378
  }, [registry, widgetId, widgetAvailable]);
3300
4379
  useEffect5(() => {
3301
- if (!containerRef.current || !registry || !widgetAvailable) return;
3302
- const handle = registry.mount(widgetId, containerRef.current, props);
4380
+ if (!parentRef.current || !registry || !widgetAvailable) return;
4381
+ const container = document.createElement("div");
4382
+ container.style.width = "100%";
4383
+ container.style.minHeight = "40px";
4384
+ parentRef.current.appendChild(container);
4385
+ const handle = registry.mount(widgetId, container, propsRef.current);
3303
4386
  handleRef.current = handle;
3304
4387
  return () => {
3305
4388
  handle.unmount();
3306
4389
  handleRef.current = null;
4390
+ container.remove();
3307
4391
  };
3308
- }, [registry, widgetId, props, widgetAvailable]);
4392
+ }, [registry, widgetId, widgetAvailable]);
4393
+ const propsJson = JSON.stringify(props);
4394
+ const prevPropsJsonRef = useRef4(propsJson);
4395
+ useEffect5(() => {
4396
+ var _a3;
4397
+ if (prevPropsJsonRef.current === propsJson) return;
4398
+ prevPropsJsonRef.current = propsJson;
4399
+ (_a3 = handleRef.current) == null ? void 0 : _a3.update(propsRef.current);
4400
+ }, [propsJson]);
3309
4401
  if (!registry || !registry.has(widgetId)) {
3310
4402
  return /* @__PURE__ */ jsxs2(
3311
4403
  "div",
@@ -3313,7 +4405,7 @@ function WidgetMount({ widgetId, props }) {
3313
4405
  style: {
3314
4406
  padding: "0.75rem",
3315
4407
  fontSize: "0.8rem",
3316
- color: text.tertiary,
4408
+ color: "var(--sc-tile-text-color)",
3317
4409
  fontStyle: "italic"
3318
4410
  },
3319
4411
  children: [
@@ -3323,173 +4415,585 @@ function WidgetMount({ widgetId, props }) {
3323
4415
  }
3324
4416
  );
3325
4417
  }
3326
- return /* @__PURE__ */ jsx5("div", { ref: containerRef, style: { width: "100%", minHeight: "40px" } });
4418
+ return /* @__PURE__ */ jsx5("div", { ref: parentRef });
4419
+ }
4420
+ function TileCard({
4421
+ config,
4422
+ surface: _surface,
4423
+ telemetry: _telemetry,
4424
+ style
4425
+ }) {
4426
+ const { title, subtitle, widget, props, icon } = config;
4427
+ const [, setTick] = useState4(0);
4428
+ const runtime3 = useRuntime();
4429
+ useEffect5(() => {
4430
+ if (runtime3) setTick((t) => t + 1);
4431
+ }, [runtime3]);
4432
+ const registration = useMemo3(
4433
+ () => {
4434
+ var _a2, _b;
4435
+ return (_b = (_a2 = runtime3 == null ? void 0 : runtime3.widgets) == null ? void 0 : _a2.getRegistration) == null ? void 0 : _b.call(_a2, widget);
4436
+ },
4437
+ [runtime3 == null ? void 0 : runtime3.widgets, widget]
4438
+ );
4439
+ const resolvedIcon = useMemo3(() => {
4440
+ var _a2, _b;
4441
+ if (icon) return icon;
4442
+ return (_b = (_a2 = registration == null ? void 0 : registration.metadata) == null ? void 0 : _a2.icon) != null ? _b : "+";
4443
+ }, [icon, registration]);
4444
+ const resolvedSubtitle = useMemo3(() => {
4445
+ var _a2;
4446
+ if (subtitle) return subtitle;
4447
+ return (_a2 = registration == null ? void 0 : registration.metadata) == null ? void 0 : _a2.subtitle;
4448
+ }, [subtitle, registration]);
4449
+ const [hovered, setHovered] = useState4(false);
4450
+ const onMouseEnter = useCallback3(() => setHovered(true), []);
4451
+ const onMouseLeave = useCallback3(() => setHovered(false), []);
4452
+ const cardStyle = {
4453
+ display: "flex",
4454
+ flexDirection: "column",
4455
+ borderRadius: "var(--sc-tile-border-radius)",
4456
+ background: hovered ? "var(--sc-tile-background-hover)" : "var(--sc-tile-background)",
4457
+ backdropFilter: "blur(16px) saturate(1.2)",
4458
+ WebkitBackdropFilter: "blur(16px) saturate(1.2)",
4459
+ border: "var(--sc-tile-border)",
4460
+ boxShadow: "var(--sc-tile-shadow)",
4461
+ color: "var(--sc-tile-title-color)",
4462
+ fontFamily: "var(--sc-font-family)",
4463
+ overflow: "hidden",
4464
+ cursor: "default",
4465
+ transition: "all 0.25s ease",
4466
+ transform: hovered ? "scale(1.01)" : "scale(1)",
4467
+ ...style
4468
+ };
4469
+ const headerStyle = {
4470
+ display: "flex",
4471
+ alignItems: "center",
4472
+ gap: "var(--sc-tile-gap, 0.25rem)",
4473
+ padding: "var(--sc-tile-header-padding, 0.375rem 0.75rem)",
4474
+ minHeight: "44px"
4475
+ };
4476
+ const iconStyle = {
4477
+ display: "flex",
4478
+ alignItems: "center",
4479
+ justifyContent: "center",
4480
+ flexShrink: 0
4481
+ };
4482
+ return /* @__PURE__ */ jsxs2(
4483
+ "article",
4484
+ {
4485
+ "data-shadow-canvas-id": `tile-${config.id}`,
4486
+ style: cardStyle,
4487
+ onMouseEnter,
4488
+ onMouseLeave,
4489
+ children: [
4490
+ /* @__PURE__ */ jsxs2("div", { style: headerStyle, children: [
4491
+ /* @__PURE__ */ jsx5("div", { style: iconStyle, children: /* @__PURE__ */ jsx5(TileIcon, { emoji: resolvedIcon, size: resolvedSubtitle ? 36 : 24 }) }),
4492
+ /* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0 }, children: [
4493
+ /* @__PURE__ */ jsx5(
4494
+ "h3",
4495
+ {
4496
+ style: {
4497
+ fontSize: "1.14rem",
4498
+ fontWeight: 600,
4499
+ color: "var(--sc-tile-title-color)",
4500
+ margin: "0.125rem 0 0",
4501
+ whiteSpace: "nowrap",
4502
+ overflow: "hidden",
4503
+ textOverflow: "ellipsis"
4504
+ },
4505
+ children: title != null ? title : widget
4506
+ }
4507
+ ),
4508
+ resolvedSubtitle && /* @__PURE__ */ jsx5(
4509
+ "p",
4510
+ {
4511
+ style: {
4512
+ fontSize: "0.8rem",
4513
+ fontWeight: 400,
4514
+ color: "var(--sc-tile-text-color)",
4515
+ margin: "0.125rem 0 0",
4516
+ whiteSpace: "nowrap",
4517
+ overflow: "hidden",
4518
+ textOverflow: "ellipsis"
4519
+ },
4520
+ children: resolvedSubtitle
4521
+ }
4522
+ )
4523
+ ] })
4524
+ ] }),
4525
+ /* @__PURE__ */ jsx5(
4526
+ "div",
4527
+ {
4528
+ style: {
4529
+ padding: "var(--sc-tile-body-padding, 0 0.75rem 0.5rem)",
4530
+ borderTop: "1px solid rgba(255, 255, 255, 0.06)"
4531
+ },
4532
+ children: /* @__PURE__ */ jsx5("div", { style: { paddingTop: "var(--sc-tile-gap, 0.25rem)" }, children: /* @__PURE__ */ jsx5(WidgetMount, { widgetId: widget, props: { ...props, instanceId: config.id } }) })
4533
+ }
4534
+ )
4535
+ ]
4536
+ }
4537
+ );
4538
+ }
4539
+
4540
+ // src/components/ShadowCanvasOverlay.tsx
4541
+ import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMemo5, useRef as useRef5, useState as useState5 } from "react";
4542
+ import { createPortal } from "react-dom";
4543
+
4544
+ // src/theme/defaultTheme.ts
4545
+ function withAlpha(hex, alpha) {
4546
+ return `${hex}${Math.round(alpha * 255).toString(16).padStart(2, "0")}`;
4547
+ }
4548
+ var darkTheme = {
4549
+ // Brand colors
4550
+ colorPrimary: brand[3],
4551
+ colorPrimaryHover: brand[4],
4552
+ colorPrimaryMuted: brand[0],
4553
+ // Surface colors (with alpha for glass/vibrancy effects)
4554
+ colorBackground: withAlpha(slateGrey[1], 0.95),
4555
+ colorBackgroundElevated: withAlpha(slateGrey[3], 0.95),
4556
+ colorBackgroundSubtle: withAlpha(slateGrey[0], 0.2),
4557
+ colorSurface: withAlpha(slateGrey[4], 0.8),
4558
+ colorSurfaceHover: withAlpha(slateGrey[5], 0.8),
4559
+ // Text colors
4560
+ colorText: text.primary,
4561
+ colorTextSecondary: text.secondary,
4562
+ colorTextMuted: text.tertiary,
4563
+ colorTextInverse: slateGrey[1],
4564
+ // Border colors
4565
+ colorBorder: border.primary,
4566
+ colorBorderSubtle: border.secondary,
4567
+ // Semantic colors
4568
+ colorSuccess: green[4],
4569
+ colorSuccessMuted: green[0],
4570
+ colorWarning: yellow[4],
4571
+ colorWarningMuted: yellow[0],
4572
+ colorError: red[4],
4573
+ colorErrorMuted: red[0],
4574
+ colorInfo: blue[4],
4575
+ colorInfoMuted: blue[0],
4576
+ // Notification badge
4577
+ colorNotification: brand[4],
4578
+ colorNotificationMuted: withAlpha(brand[4], 0.4),
4579
+ // Glass morphism
4580
+ glassBackground: withAlpha(slateGrey[1], 0.6),
4581
+ glassBackgroundHover: withAlpha(slateGrey[1], 0.7),
4582
+ glassBorder: "rgba(255, 255, 255, 0.08)",
4583
+ glassBlur: "blur(24px)",
4584
+ glassSaturate: "saturate(1.2)",
4585
+ // Typography (SF Pro stack)
4586
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif",
4587
+ fontFamilyMono: "'SF Mono', 'Fira Code', Consolas, monospace",
4588
+ fontSizeXs: "0.7rem",
4589
+ fontSizeSm: "0.8rem",
4590
+ fontSizeMd: "0.9rem",
4591
+ fontSizeLg: "1rem",
4592
+ fontSizeXl: "1.25rem",
4593
+ fontSizeXxl: "1.5rem",
4594
+ fontWeightNormal: "400",
4595
+ fontWeightMedium: "500",
4596
+ fontWeightSemibold: "600",
4597
+ fontWeightBold: "700",
4598
+ lineHeightTight: "1.25",
4599
+ lineHeightNormal: "1.5",
4600
+ lineHeightRelaxed: "1.75",
4601
+ // Spacing
4602
+ spacingXs: "0.25rem",
4603
+ spacingSm: "0.5rem",
4604
+ spacingMd: "0.75rem",
4605
+ spacingLg: "1rem",
4606
+ spacingXl: "1.5rem",
4607
+ // Border radius
4608
+ borderRadiusSm: "6px",
4609
+ borderRadiusMd: "10px",
4610
+ borderRadiusLg: "14px",
4611
+ borderRadiusXl: "20px",
4612
+ borderRadiusFull: "9999px",
4613
+ // Shadows
4614
+ shadowSm: "0 1px 2px rgba(0, 0, 0, 0.2)",
4615
+ shadowMd: "0 2px 12px rgba(0, 0, 0, 0.3)",
4616
+ shadowLg: "0 8px 24px rgba(0, 0, 0, 0.4)",
4617
+ shadowXl: "0 16px 48px rgba(0, 0, 0, 0.5)",
4618
+ // Backdrop effects
4619
+ backdropBlur: "blur(20px)",
4620
+ backdropSaturate: "saturate(180%)",
4621
+ // Animation
4622
+ transitionFast: "0.1s ease",
4623
+ transitionNormal: "0.25s ease",
4624
+ transitionSlow: "0.4s ease"
4625
+ };
4626
+ var lightTheme = {
4627
+ // Brand colors
4628
+ colorPrimary: brand[3],
4629
+ colorPrimaryHover: brand[2],
4630
+ colorPrimaryMuted: withAlpha(brand[5], 0.1),
4631
+ // Surface colors
4632
+ colorBackground: withAlpha(slateGrey[12], 0.95),
4633
+ colorBackgroundElevated: withAlpha(slateGrey[11], 0.95),
4634
+ colorBackgroundSubtle: withAlpha(slateGrey[0], 0.02),
4635
+ colorSurface: withAlpha(slateGrey[11], 0.6),
4636
+ colorSurfaceHover: withAlpha(slateGrey[10], 0.6),
4637
+ // Text colors
4638
+ colorText: slateGrey[1],
4639
+ colorTextSecondary: slateGrey[6],
4640
+ colorTextMuted: slateGrey[8],
4641
+ colorTextInverse: slateGrey[12],
4642
+ // Border colors
4643
+ colorBorder: withAlpha(slateGrey[0], 0.12),
4644
+ colorBorderSubtle: withAlpha(slateGrey[0], 0.06),
4645
+ // Semantic colors
4646
+ colorSuccess: green[4],
4647
+ colorSuccessMuted: withAlpha(green[4], 0.12),
4648
+ colorWarning: yellow[4],
4649
+ colorWarningMuted: withAlpha(yellow[4], 0.12),
4650
+ colorError: red[4],
4651
+ colorErrorMuted: withAlpha(red[4], 0.12),
4652
+ colorInfo: blue[4],
4653
+ colorInfoMuted: withAlpha(blue[4], 0.12),
4654
+ // Notification badge
4655
+ colorNotification: brand[3],
4656
+ colorNotificationMuted: withAlpha(brand[3], 0.4),
4657
+ // Glass morphism
4658
+ glassBackground: withAlpha(slateGrey[12], 0.7),
4659
+ glassBackgroundHover: withAlpha(slateGrey[12], 0.8),
4660
+ glassBorder: "rgba(0, 0, 0, 0.06)",
4661
+ glassBlur: "blur(24px)",
4662
+ glassSaturate: "saturate(1.2)",
4663
+ // Typography (same as dark)
4664
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif",
4665
+ fontFamilyMono: "'SF Mono', 'Fira Code', Consolas, monospace",
4666
+ fontSizeXs: "0.7rem",
4667
+ fontSizeSm: "0.8rem",
4668
+ fontSizeMd: "0.9rem",
4669
+ fontSizeLg: "1rem",
4670
+ fontSizeXl: "1.25rem",
4671
+ fontSizeXxl: "1.5rem",
4672
+ fontWeightNormal: "400",
4673
+ fontWeightMedium: "500",
4674
+ fontWeightSemibold: "600",
4675
+ fontWeightBold: "700",
4676
+ lineHeightTight: "1.25",
4677
+ lineHeightNormal: "1.5",
4678
+ lineHeightRelaxed: "1.75",
4679
+ // Spacing (same as dark)
4680
+ spacingXs: "0.25rem",
4681
+ spacingSm: "0.5rem",
4682
+ spacingMd: "0.75rem",
4683
+ spacingLg: "1rem",
4684
+ spacingXl: "1.5rem",
4685
+ // Border radius (same as dark)
4686
+ borderRadiusSm: "6px",
4687
+ borderRadiusMd: "10px",
4688
+ borderRadiusLg: "14px",
4689
+ borderRadiusXl: "20px",
4690
+ borderRadiusFull: "9999px",
4691
+ // Shadows (lighter for light mode)
4692
+ shadowSm: "0 1px 2px rgba(0, 0, 0, 0.08)",
4693
+ shadowMd: "0 2px 12px rgba(0, 0, 0, 0.12)",
4694
+ shadowLg: "0 8px 24px rgba(0, 0, 0, 0.16)",
4695
+ shadowXl: "0 16px 48px rgba(0, 0, 0, 0.2)",
4696
+ // Backdrop effects
4697
+ backdropBlur: "blur(20px)",
4698
+ backdropSaturate: "saturate(180%)",
4699
+ // Animation
4700
+ transitionFast: "0.1s ease",
4701
+ transitionNormal: "0.25s ease",
4702
+ transitionSlow: "0.4s ease"
4703
+ };
4704
+ function kebabCase(str) {
4705
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
3327
4706
  }
3328
- function TileCard({
3329
- config,
3330
- surface: _surface,
3331
- telemetry: _telemetry,
3332
- style
3333
- }) {
3334
- const { title, widget, props } = config;
3335
- const [, setTick] = useState4(0);
3336
- const runtime3 = useRuntime();
3337
- useEffect5(() => {
3338
- if (runtime3) setTick((t) => t + 1);
3339
- }, [runtime3]);
3340
- const [hovered, setHovered] = useState4(false);
3341
- const onMouseEnter = useCallback2(() => setHovered(true), []);
3342
- const onMouseLeave = useCallback2(() => setHovered(false), []);
3343
- const cardStyle = {
3344
- display: "flex",
3345
- flexDirection: "column",
3346
- borderRadius: "14px",
3347
- background: hovered ? withAlpha(slateGrey[1], 0.7) : withAlpha(slateGrey[1], 0.6),
3348
- backdropFilter: "blur(16px) saturate(1.2)",
3349
- WebkitBackdropFilter: "blur(16px) saturate(1.2)",
4707
+ var darkDefaults = {
4708
+ mode: "dark",
4709
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif",
4710
+ colorPrimary: brand[3],
4711
+ colorPrimaryHover: brand[4],
4712
+ borderRadius: "12px",
4713
+ canvas: {
4714
+ position: "right",
4715
+ layout: "overlay",
4716
+ background: withAlpha(slateGrey[1], 0.6),
4717
+ blur: "blur(24px)",
4718
+ border: "none",
4719
+ width: "clamp(380px, 25vw, 520px)",
4720
+ transitionDuration: "300ms",
4721
+ transitionEasing: "cubic-bezier(0.16, 1, 0.3, 1)",
4722
+ transitionFade: "6%"
4723
+ },
4724
+ launcher: {
4725
+ background: button.primary.backgroundDefault,
4726
+ backgroundHover: button.primary.backgroundHover,
4727
+ color: button.primary.icon,
4728
+ size: "56px",
4729
+ shadow: `0 8px 32px ${withAlpha(slateGrey[0], 0.6)}`,
4730
+ borderRadius: "9999px"
4731
+ },
4732
+ tile: {
4733
+ background: withAlpha(slateGrey[1], 0.6),
4734
+ backgroundHover: withAlpha(slateGrey[1], 0.7),
3350
4735
  border: "1px solid rgba(255, 255, 255, 0.08)",
3351
- boxShadow: "0 2px 12px rgba(0, 0, 0, 0.3)",
3352
- color: text.primary,
3353
- fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif",
3354
- overflow: "hidden",
3355
- cursor: "default",
3356
- transition: "all 0.25s ease",
3357
- transform: hovered ? "scale(1.01)" : "scale(1)",
3358
- ...style
3359
- };
3360
- const headerStyle = {
3361
- display: "flex",
3362
- alignItems: "center",
3363
- gap: "0.75rem",
3364
- padding: "0.875rem 1rem",
3365
- minHeight: "72px"
4736
+ borderRadius: "14px",
4737
+ shadow: "0 2px 12px rgba(0, 0, 0, 0.3)",
4738
+ titleColor: text.primary,
4739
+ textColor: text.secondary,
4740
+ iconBackground: `linear-gradient(135deg, ${brand[3]} 0%, ${brand[3]}cc 100%)`,
4741
+ iconShadow: `0 2px 8px ${brand[3]}40`,
4742
+ headerPadding: "0.375rem 0.75rem",
4743
+ bodyPadding: "0 0.75rem 0.5rem",
4744
+ gap: "0.25rem"
4745
+ },
4746
+ overlay: {
4747
+ background: withAlpha(slateGrey[1], 0.6),
4748
+ textColor: text.primary,
4749
+ titleColor: text.primary,
4750
+ arrowColor: withAlpha(slateGrey[1], 0.6),
4751
+ arrowSize: "8px",
4752
+ border: "none",
4753
+ borderRadius: "0",
4754
+ scrimOpacity: "0",
4755
+ highlightRing: `2px solid ${brand[3]}`
4756
+ },
4757
+ notification: {
4758
+ background: withAlpha(slateGrey[0], 0.95),
4759
+ textColor: text.primary,
4760
+ textSecondaryColor: text.secondary,
4761
+ border: `1px solid ${slateGrey[5]}`,
4762
+ borderRadius: "12px",
4763
+ successColor: green[4],
4764
+ warningColor: yellow[4],
4765
+ errorColor: red[4],
4766
+ iconBackground: withAlpha(brand[3], 0.15),
4767
+ progressGradient: `linear-gradient(90deg, ${brand[3]}, ${brand[4]})`
4768
+ },
4769
+ content: {
4770
+ background: withAlpha(slateGrey[3], 0.8),
4771
+ backgroundHover: withAlpha(slateGrey[5], 0.6),
4772
+ border: `1px solid ${withAlpha(slateGrey[5], 0.5)}`,
4773
+ borderRadius: "8px",
4774
+ textColor: text.primary,
4775
+ textSecondaryColor: text.secondary,
4776
+ itemDivider: "none",
4777
+ itemGap: "6px",
4778
+ itemPadding: "12px 16px",
4779
+ itemFontSize: "15px",
4780
+ bodyPadding: "0 16px 12px 16px",
4781
+ bodyFontSize: "14px",
4782
+ categoryPadding: "8px 4px 4px 4px",
4783
+ categoryGap: "4px",
4784
+ categoryFontSize: "12px",
4785
+ searchBackground: withAlpha(slateGrey[3], 0.8),
4786
+ searchColor: text.primary,
4787
+ chevronColor: "currentColor"
4788
+ }
4789
+ };
4790
+ var lightDefaults = {
4791
+ mode: "light",
4792
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif",
4793
+ colorPrimary: brand[3],
4794
+ colorPrimaryHover: brand[2],
4795
+ borderRadius: "12px",
4796
+ canvas: {
4797
+ position: "right",
4798
+ layout: "overlay",
4799
+ background: withAlpha(slateGrey[12], 0.7),
4800
+ blur: "blur(24px)",
4801
+ border: "none",
4802
+ width: "clamp(380px, 25vw, 520px)",
4803
+ transitionDuration: "300ms",
4804
+ transitionEasing: "cubic-bezier(0.16, 1, 0.3, 1)",
4805
+ transitionFade: "6%"
4806
+ },
4807
+ launcher: {
4808
+ background: brand[3],
4809
+ backgroundHover: brand[2],
4810
+ color: "#ffffff",
4811
+ size: "56px",
4812
+ shadow: "0 8px 32px rgba(0, 0, 0, 0.15)",
4813
+ borderRadius: "9999px"
4814
+ },
4815
+ tile: {
4816
+ background: withAlpha(slateGrey[12], 0.7),
4817
+ backgroundHover: withAlpha(slateGrey[12], 0.8),
4818
+ border: "1px solid rgba(0, 0, 0, 0.06)",
4819
+ borderRadius: "14px",
4820
+ shadow: "0 2px 12px rgba(0, 0, 0, 0.12)",
4821
+ titleColor: slateGrey[1],
4822
+ textColor: slateGrey[6],
4823
+ iconBackground: `linear-gradient(135deg, ${brand[3]} 0%, ${brand[3]}cc 100%)`,
4824
+ iconShadow: `0 2px 8px ${brand[3]}40`,
4825
+ headerPadding: "0.375rem 0.75rem",
4826
+ bodyPadding: "0 0.75rem 0.5rem",
4827
+ gap: "0.25rem"
4828
+ },
4829
+ overlay: {
4830
+ background: withAlpha(slateGrey[12], 0.7),
4831
+ textColor: slateGrey[1],
4832
+ titleColor: slateGrey[1],
4833
+ arrowColor: withAlpha(slateGrey[12], 0.7),
4834
+ arrowSize: "8px",
4835
+ border: "none",
4836
+ borderRadius: "0",
4837
+ scrimOpacity: "0",
4838
+ highlightRing: `2px solid ${brand[3]}`
4839
+ },
4840
+ notification: {
4841
+ background: withAlpha(slateGrey[12], 0.95),
4842
+ textColor: slateGrey[1],
4843
+ textSecondaryColor: slateGrey[6],
4844
+ border: `1px solid ${withAlpha(slateGrey[0], 0.12)}`,
4845
+ borderRadius: "12px",
4846
+ successColor: green[4],
4847
+ warningColor: yellow[4],
4848
+ errorColor: red[4],
4849
+ iconBackground: withAlpha(brand[3], 0.1),
4850
+ progressGradient: `linear-gradient(90deg, ${brand[3]}, ${brand[2]})`
4851
+ },
4852
+ content: {
4853
+ background: withAlpha(slateGrey[12], 0.8),
4854
+ backgroundHover: withAlpha(slateGrey[11], 0.6),
4855
+ border: `1px solid ${slateGrey[11]}`,
4856
+ borderRadius: "8px",
4857
+ textColor: slateGrey[1],
4858
+ textSecondaryColor: slateGrey[6],
4859
+ itemDivider: "none",
4860
+ itemGap: "6px",
4861
+ itemPadding: "12px 16px",
4862
+ itemFontSize: "15px",
4863
+ bodyPadding: "0 16px 12px 16px",
4864
+ bodyFontSize: "14px",
4865
+ categoryPadding: "8px 4px 4px 4px",
4866
+ categoryGap: "4px",
4867
+ categoryFontSize: "12px",
4868
+ searchBackground: slateGrey[12],
4869
+ searchColor: slateGrey[1],
4870
+ chevronColor: "currentColor"
4871
+ }
4872
+ };
4873
+ function mergeThemeConfig(customer) {
4874
+ var _a2, _b, _c, _d, _e;
4875
+ const base2 = customer.mode === "light" ? lightDefaults : darkDefaults;
4876
+ return {
4877
+ mode: (_a2 = customer.mode) != null ? _a2 : base2.mode,
4878
+ fontFamily: (_b = customer.fontFamily) != null ? _b : base2.fontFamily,
4879
+ colorPrimary: (_c = customer.colorPrimary) != null ? _c : base2.colorPrimary,
4880
+ colorPrimaryHover: (_d = customer.colorPrimaryHover) != null ? _d : base2.colorPrimaryHover,
4881
+ borderRadius: (_e = customer.borderRadius) != null ? _e : base2.borderRadius,
4882
+ canvas: { ...base2.canvas, ...customer.canvas },
4883
+ launcher: { ...base2.launcher, ...customer.launcher },
4884
+ tile: { ...base2.tile, ...customer.tile },
4885
+ overlay: { ...base2.overlay, ...customer.overlay },
4886
+ notification: { ...base2.notification, ...customer.notification },
4887
+ content: { ...base2.content, ...customer.content }
3366
4888
  };
3367
- const iconStyle = {
3368
- width: "40px",
3369
- height: "40px",
3370
- borderRadius: "10px",
3371
- background: `linear-gradient(135deg, ${brand[3]} 0%, ${brand[3]}cc 100%)`,
3372
- display: "flex",
3373
- alignItems: "center",
3374
- justifyContent: "center",
3375
- fontSize: "1.25rem",
3376
- flexShrink: 0,
3377
- boxShadow: `0 2px 8px ${brand[3]}40`
4889
+ }
4890
+ function mergeThemeWithWorkspace(workspaceTheme, configTheme) {
4891
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
4892
+ const mode = (_b = (_a2 = configTheme.mode) != null ? _a2 : workspaceTheme.mode) != null ? _b : "dark";
4893
+ const base2 = mode === "light" ? lightDefaults : darkDefaults;
4894
+ return {
4895
+ mode,
4896
+ fontFamily: (_d = (_c = configTheme.fontFamily) != null ? _c : workspaceTheme.fontFamily) != null ? _d : base2.fontFamily,
4897
+ colorPrimary: (_f = (_e = configTheme.colorPrimary) != null ? _e : workspaceTheme.colorPrimary) != null ? _f : base2.colorPrimary,
4898
+ colorPrimaryHover: (_h = (_g = configTheme.colorPrimaryHover) != null ? _g : workspaceTheme.colorPrimaryHover) != null ? _h : base2.colorPrimaryHover,
4899
+ borderRadius: (_j = (_i = configTheme.borderRadius) != null ? _i : workspaceTheme.borderRadius) != null ? _j : base2.borderRadius,
4900
+ canvas: { ...base2.canvas, ...workspaceTheme.canvas, ...configTheme.canvas },
4901
+ launcher: { ...base2.launcher, ...workspaceTheme.launcher, ...configTheme.launcher },
4902
+ tile: { ...base2.tile, ...workspaceTheme.tile, ...configTheme.tile },
4903
+ overlay: { ...base2.overlay, ...workspaceTheme.overlay, ...configTheme.overlay },
4904
+ notification: {
4905
+ ...base2.notification,
4906
+ ...workspaceTheme.notification,
4907
+ ...configTheme.notification
4908
+ },
4909
+ content: { ...base2.content, ...workspaceTheme.content, ...configTheme.content }
3378
4910
  };
3379
- return /* @__PURE__ */ jsxs2(
3380
- "article",
3381
- {
3382
- "data-shadow-canvas-id": `tile-${config.id}`,
3383
- style: cardStyle,
3384
- onMouseEnter,
3385
- onMouseLeave,
3386
- children: [
3387
- /* @__PURE__ */ jsxs2("div", { style: headerStyle, children: [
3388
- /* @__PURE__ */ jsx5("div", { style: iconStyle, children: "+" }),
3389
- /* @__PURE__ */ jsx5("div", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsx5(
3390
- "h3",
3391
- {
3392
- style: {
3393
- fontSize: "0.95rem",
3394
- fontWeight: 600,
3395
- color: text.primary,
3396
- margin: "0.125rem 0 0",
3397
- whiteSpace: "nowrap",
3398
- overflow: "hidden",
3399
- textOverflow: "ellipsis"
3400
- },
3401
- children: title != null ? title : widget
3402
- }
3403
- ) })
3404
- ] }),
3405
- /* @__PURE__ */ jsx5(
3406
- "div",
3407
- {
3408
- style: {
3409
- padding: "0 1rem 1rem",
3410
- borderTop: `1px solid ${border.secondary}`
3411
- },
3412
- children: /* @__PURE__ */ jsx5("div", { style: { paddingTop: "0.875rem" }, children: /* @__PURE__ */ jsx5(WidgetMount, { widgetId: widget, props }) })
3413
- }
3414
- )
3415
- ]
4911
+ }
4912
+ var ELEMENT_SECTIONS = [
4913
+ "canvas",
4914
+ "launcher",
4915
+ "tile",
4916
+ "overlay",
4917
+ "notification",
4918
+ "content"
4919
+ ];
4920
+ function flattenThemeConfig(config) {
4921
+ const vars = {};
4922
+ if (config.fontFamily) vars["--sc-font-family"] = config.fontFamily;
4923
+ if (config.colorPrimary) vars["--sc-color-primary"] = config.colorPrimary;
4924
+ if (config.colorPrimaryHover) vars["--sc-color-primary-hover"] = config.colorPrimaryHover;
4925
+ if (config.borderRadius) vars["--sc-border-radius"] = config.borderRadius;
4926
+ for (const section of ELEMENT_SECTIONS) {
4927
+ const sectionConfig = config[section];
4928
+ if (!sectionConfig) continue;
4929
+ for (const [key, value] of Object.entries(sectionConfig)) {
4930
+ if (value !== void 0) {
4931
+ vars[`--sc-${section}-${kebabCase(key)}`] = value;
4932
+ }
3416
4933
  }
4934
+ }
4935
+ return vars;
4936
+ }
4937
+
4938
+ // src/theme/ThemeProvider.tsx
4939
+ import { createContext as createContext3, useContext as useContext3, useEffect as useEffect6, useMemo as useMemo4 } from "react";
4940
+ import { jsx as jsx6 } from "react/jsx-runtime";
4941
+ var ThemeContext = createContext3(null);
4942
+ function ThemeProvider({
4943
+ children,
4944
+ themeConfig,
4945
+ workspaceTheme,
4946
+ shadowRoot
4947
+ }) {
4948
+ const merged = useMemo4(
4949
+ () => workspaceTheme ? mergeThemeWithWorkspace(workspaceTheme, themeConfig != null ? themeConfig : {}) : mergeThemeConfig(themeConfig != null ? themeConfig : {}),
4950
+ [themeConfig, workspaceTheme]
3417
4951
  );
4952
+ const cssVariables = useMemo4(() => flattenThemeConfig(merged), [merged]);
4953
+ useEffect6(() => {
4954
+ if (!shadowRoot) return;
4955
+ let styleEl = shadowRoot.querySelector("#sc-theme-vars");
4956
+ if (!styleEl) {
4957
+ styleEl = document.createElement("style");
4958
+ styleEl.id = "sc-theme-vars";
4959
+ shadowRoot.insertBefore(styleEl, shadowRoot.firstChild);
4960
+ }
4961
+ const cssRules = Object.entries(cssVariables).map(([key, value2]) => ` ${key}: ${value2};`).join("\n");
4962
+ styleEl.textContent = `:host {
4963
+ ${cssRules}
4964
+ }`;
4965
+ }, [shadowRoot, cssVariables]);
4966
+ const value = {
4967
+ config: merged,
4968
+ mode: merged.mode,
4969
+ cssVariables
4970
+ };
4971
+ return /* @__PURE__ */ jsx6(ThemeContext.Provider, { value, children });
4972
+ }
4973
+ function useTheme() {
4974
+ const context = useContext3(ThemeContext);
4975
+ if (!context) {
4976
+ throw new Error("useTheme must be used within a ThemeProvider");
4977
+ }
4978
+ return context;
3418
4979
  }
3419
4980
 
3420
4981
  // src/components/ShadowCanvasOverlay.tsx
3421
- import { useCallback as useCallback3, useEffect as useEffect6, useRef as useRef4, useState as useState5 } from "react";
3422
- import { createPortal } from "react-dom";
3423
- import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
4982
+ import { Fragment, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
3424
4983
  var LAUNCHER_STYLES_ID = "syntro-launcher-styles";
3425
- var LAUNCHER_CSS = `
3426
- @keyframes syntro-launcher-pulse {
3427
- 0%, 100% {
3428
- box-shadow: 0 20px 45px ${slateGrey[0]}59, 0 0 0 0 ${brand[3]}66;
3429
- }
3430
- 50% {
3431
- box-shadow: 0 20px 45px ${slateGrey[0]}59, 0 0 0 8px ${brand[3]}00;
3432
- }
3433
- }
3434
- @keyframes syntro-launcher-glow {
3435
- 0%, 100% {
3436
- border-color: ${slateGrey[5]};
3437
- }
3438
- 50% {
3439
- border-color: ${brand[4]}99;
3440
- }
3441
- }
3442
- .syntro-launcher-animate {
3443
- animation: syntro-launcher-pulse 2s ease-in-out infinite, syntro-launcher-glow 2s ease-in-out infinite;
3444
- }
3445
- .syntro-launcher-animate:hover {
3446
- animation: none;
3447
- }
3448
- @keyframes syntro-badge-ping {
3449
- 0% { transform: scale(1); opacity: 1; }
3450
- 75%, 100% { transform: scale(2.2); opacity: 0; }
3451
- }
3452
- @keyframes syntro-badge-glow {
3453
- 0%, 100% { box-shadow: 0 0 8px 2px ${withAlpha(brand[4], 0.3)}; }
3454
- 50% { box-shadow: 0 0 20px 6px ${withAlpha(brand[4], 0.5)}; }
3455
- }
3456
- @keyframes syntro-badge-bounce {
3457
- 0%, 100% { transform: translateY(0); }
3458
- 25% { transform: translateY(-6px); }
3459
- 50% { transform: translateY(-3px); }
3460
- }
3461
- .syntro-badge-ping { animation: syntro-badge-ping 1.5s cubic-bezier(0,0,0.2,1) infinite; }
3462
- .syntro-badge-glow { animation: syntro-badge-glow 2s ease-in-out infinite; }
3463
- .syntro-badge-bounce { animation: syntro-badge-bounce 2s ease-in-out infinite; }
3464
- `;
3465
- function ensureLauncherStyles(target) {
4984
+ function ensureLauncherStyles(target, css) {
3466
4985
  if (target.querySelector(`#${LAUNCHER_STYLES_ID}`)) return;
3467
4986
  const style = document.createElement("style");
3468
4987
  style.id = LAUNCHER_STYLES_ID;
3469
- style.textContent = LAUNCHER_CSS;
4988
+ style.textContent = css;
3470
4989
  target.appendChild(style);
3471
4990
  }
3472
- var DEFAULT_THEME = {
3473
- position: "right",
3474
- baseWidth: "clamp(320px, 16.666vw, 420px)",
3475
- expandedWidth: "40vw",
3476
- zIndex: 2147483600,
3477
- backdropBlur: "18px",
3478
- mode: "dark",
3479
- colorPrimary: brand[3],
3480
- colorBackground: slateGrey[1],
3481
- colorText: text.primary,
3482
- colorTextSecondary: text.secondary,
3483
- borderRadius: "12px",
3484
- glassBackground: withAlpha(slateGrey[1], 0.6),
3485
- glassBlur: "blur(24px)",
3486
- glassSaturate: "saturate(1.2)"
3487
- };
3488
4991
  function ShadowCanvasOverlay({
3489
4992
  isOpen,
3490
4993
  onToggle,
3491
4994
  telemetry,
3492
4995
  launcherLabel: _launcherLabel = "Adaptives",
4996
+ launcherIcon,
3493
4997
  launcherAnimate = false,
3494
4998
  launcherAnimationStyle: _launcherAnimationStyle = "pulse",
3495
4999
  notificationCount: _notificationCount,
@@ -3498,21 +5002,21 @@ function ShadowCanvasOverlay({
3498
5002
  isLoading,
3499
5003
  error: error2,
3500
5004
  canvasTitle,
3501
- displayMode = "standard",
3502
- theme: themeOverride
5005
+ displayMode = "standard"
3503
5006
  }) {
3504
- var _a2, _b, _c, _d, _e, _f, _g;
5007
+ var _a2, _b, _c, _d;
3505
5008
  const [mounted, setMounted] = useState5(false);
3506
5009
  const [launcherPos, setLauncherPos] = useState5(null);
3507
- const dragRef = useRef4(null);
5010
+ const dragRef = useRef5(null);
3508
5011
  const runtime3 = useRuntime();
3509
5012
  const { shadowRoot, portalRoot } = useShadowRoot();
5013
+ const { config } = useTheme();
3510
5014
  const { notifications, dismiss: dismissNotification } = useNotifications(
3511
5015
  (_a2 = runtime3 == null ? void 0 : runtime3.events) != null ? _a2 : null,
3512
5016
  tiles
3513
5017
  );
3514
5018
  useNotifyWatcher(runtime3, tiles, runtime3 == null ? void 0 : runtime3.apps);
3515
- const handleNotificationClick = useCallback3(
5019
+ const handleNotificationClick = useCallback4(
3516
5020
  (notif) => {
3517
5021
  if (runtime3) {
3518
5022
  runtime3.events.publish(StandardEvents.NOTIFICATION_CLICKED, {
@@ -3534,8 +5038,44 @@ function ShadowCanvasOverlay({
3534
5038
  },
3535
5039
  [runtime3, isOpen, onToggle, dismissNotification]
3536
5040
  );
3537
- const theme = { ...DEFAULT_THEME, ...themeOverride };
3538
- useEffect6(() => {
5041
+ const launcherCss = useMemo5(
5042
+ () => `
5043
+ @keyframes syntro-launcher-pulse {
5044
+ 0%, 100% {
5045
+ box-shadow: 0 20px 45px rgba(0,0,0,0.35), 0 0 0 0 ${config.colorPrimary}66;
5046
+ }
5047
+ 50% {
5048
+ box-shadow: 0 20px 45px rgba(0,0,0,0.35), 0 0 0 8px ${config.colorPrimary}00;
5049
+ }
5050
+ }
5051
+ @keyframes syntro-launcher-glow {
5052
+ 0%, 100% { border-color: rgba(255,255,255,0.1); }
5053
+ 50% { border-color: ${config.colorPrimaryHover}99; }
5054
+ }
5055
+ .syntro-launcher-animate {
5056
+ animation: syntro-launcher-pulse 2s ease-in-out infinite, syntro-launcher-glow 2s ease-in-out infinite;
5057
+ }
5058
+ .syntro-launcher-animate:hover { animation: none; }
5059
+ @keyframes syntro-badge-ping {
5060
+ 0% { transform: scale(1); opacity: 1; }
5061
+ 75%, 100% { transform: scale(2.2); opacity: 0; }
5062
+ }
5063
+ @keyframes syntro-badge-glow {
5064
+ 0%, 100% { box-shadow: 0 0 8px 2px ${config.colorPrimaryHover}4d; }
5065
+ 50% { box-shadow: 0 0 20px 6px ${config.colorPrimaryHover}80; }
5066
+ }
5067
+ @keyframes syntro-badge-bounce {
5068
+ 0%, 100% { transform: translateY(0); }
5069
+ 25% { transform: translateY(-6px); }
5070
+ 50% { transform: translateY(-3px); }
5071
+ }
5072
+ .syntro-badge-ping { animation: syntro-badge-ping 1.5s cubic-bezier(0,0,0.2,1) infinite; }
5073
+ .syntro-badge-glow { animation: syntro-badge-glow 2s ease-in-out infinite; }
5074
+ .syntro-badge-bounce { animation: syntro-badge-bounce 2s ease-in-out infinite; }
5075
+ `,
5076
+ [config.colorPrimary, config.colorPrimaryHover]
5077
+ );
5078
+ useEffect7(() => {
3539
5079
  if (!isOpen) return;
3540
5080
  tiles.forEach((tile) => {
3541
5081
  telemetry == null ? void 0 : telemetry.trackRectangleViewed(tile.id, "overlay");
@@ -3545,11 +5085,11 @@ function ShadowCanvasOverlay({
3545
5085
  }
3546
5086
  });
3547
5087
  }, [telemetry, runtime3, isOpen, tiles]);
3548
- useEffect6(() => {
5088
+ useEffect7(() => {
3549
5089
  setMounted(true);
3550
- ensureLauncherStyles(shadowRoot);
3551
- }, [shadowRoot]);
3552
- const toggle2 = useCallback3(() => {
5090
+ ensureLauncherStyles(shadowRoot, launcherCss);
5091
+ }, [shadowRoot, launcherCss]);
5092
+ const toggle2 = useCallback4(() => {
3553
5093
  const next = !isOpen;
3554
5094
  if (next) {
3555
5095
  telemetry == null ? void 0 : telemetry.trackCanvasOpened("overlay");
@@ -3562,7 +5102,7 @@ function ShadowCanvasOverlay({
3562
5102
  }
3563
5103
  onToggle();
3564
5104
  }, [isOpen, telemetry, runtime3, onToggle]);
3565
- const onLauncherPointerDown = useCallback3((e) => {
5105
+ const onLauncherPointerDown = useCallback4((e) => {
3566
5106
  const rect = e.currentTarget.getBoundingClientRect();
3567
5107
  dragRef.current = {
3568
5108
  startX: e.clientX,
@@ -3573,7 +5113,7 @@ function ShadowCanvasOverlay({
3573
5113
  };
3574
5114
  e.currentTarget.setPointerCapture(e.pointerId);
3575
5115
  }, []);
3576
- const onLauncherPointerMove = useCallback3((e) => {
5116
+ const onLauncherPointerMove = useCallback4((e) => {
3577
5117
  const drag = dragRef.current;
3578
5118
  if (!drag) return;
3579
5119
  const dx = e.clientX - drag.startX;
@@ -3585,8 +5125,8 @@ function ShadowCanvasOverlay({
3585
5125
  setLauncherPos({ x: drag.startElX + dx, y: drag.startElY + dy });
3586
5126
  }
3587
5127
  }, []);
3588
- const onLauncherPointerUp = useCallback3(
3589
- (_e2) => {
5128
+ const onLauncherPointerUp = useCallback4(
5129
+ (_e) => {
3590
5130
  const drag = dragRef.current;
3591
5131
  dragRef.current = null;
3592
5132
  if (drag && !drag.dragged) {
@@ -3596,30 +5136,77 @@ function ShadowCanvasOverlay({
3596
5136
  [toggle2]
3597
5137
  );
3598
5138
  const isFocused = displayMode === "focused";
3599
- const isRight = theme.position === "right";
5139
+ const isRight = config.canvas.position === "right";
5140
+ const isPush = config.canvas.layout === "push";
5141
+ const canvasBorder = (_b = config.canvas.border) != null ? _b : "none";
5142
+ const containerRef = useRef5(null);
5143
+ const zIndex = 2147483600;
5144
+ useEffect7(() => {
5145
+ var _a3, _b2, _c2, _d2;
5146
+ if (!isPush) return;
5147
+ const root = document.documentElement;
5148
+ const prop = isRight ? "marginRight" : "marginLeft";
5149
+ const duration = (_a3 = config.canvas.transitionDuration) != null ? _a3 : "300ms";
5150
+ const easing = (_b2 = config.canvas.transitionEasing) != null ? _b2 : "cubic-bezier(0.16, 1, 0.3, 1)";
5151
+ const prevTransition = root.style.transition;
5152
+ root.style.transition = `${prop} ${duration} ${easing}`;
5153
+ if (isOpen) {
5154
+ const width = (_d2 = (_c2 = containerRef.current) == null ? void 0 : _c2.offsetWidth) != null ? _d2 : 380;
5155
+ root.style[prop] = `${width}px`;
5156
+ } else {
5157
+ root.style[prop] = "0px";
5158
+ }
5159
+ return () => {
5160
+ root.style[prop] = "";
5161
+ root.style.transition = prevTransition;
5162
+ };
5163
+ }, [isPush, isOpen, isRight, config.canvas.transitionDuration, config.canvas.transitionEasing]);
5164
+ useEffect7(() => {
5165
+ if (!isPush || !isOpen) return;
5166
+ const container = containerRef.current;
5167
+ if (!container) return;
5168
+ const prop = isRight ? "marginRight" : "marginLeft";
5169
+ const observer = new ResizeObserver(([entry]) => {
5170
+ document.documentElement.style[prop] = `${entry.contentRect.width}px`;
5171
+ });
5172
+ observer.observe(container);
5173
+ return () => observer.disconnect();
5174
+ }, [isPush, isOpen, isRight]);
3600
5175
  const containerStyle = {
3601
5176
  display: "flex",
3602
5177
  flexDirection: "column",
3603
- width: isFocused ? theme.expandedWidth : theme.baseWidth,
5178
+ width: isFocused ? "40vw" : config.canvas.width,
3604
5179
  minWidth: "320px",
3605
5180
  height: "100%",
3606
5181
  maxHeight: "100%",
3607
5182
  pointerEvents: "auto",
3608
5183
  opacity: isOpen ? 1 : 0,
3609
- transition: isOpen ? "transform 340ms cubic-bezier(0.16, 1, 0.3, 1), opacity 280ms ease-out" : "transform 280ms cubic-bezier(0.4, 0, 0.2, 1), opacity 200ms ease-in",
3610
- color: theme.colorText || text.primary,
3611
- // Standard mode: no tint, just blur with a tight fade mask at the leading edge
3612
- // Focused mode: full frosted glass
3613
- background: isFocused ? (_b = theme.glassBackground) != null ? _b : withAlpha(slateGrey[1], 0.6) : "transparent",
3614
- backdropFilter: isFocused ? `${(_c = theme.glassBlur) != null ? _c : "blur(24px)"} ${(_d = theme.glassSaturate) != null ? _d : "saturate(1.2)"}` : "blur(6px)",
3615
- WebkitBackdropFilter: isFocused ? `${(_e = theme.glassBlur) != null ? _e : "blur(24px)"} ${(_f = theme.glassSaturate) != null ? _f : "saturate(1.2)"}` : "blur(6px)",
3616
- // Tight fade mask so blur cuts off sharply at the leading edge
3617
- ...!isFocused ? {
3618
- maskImage: isRight ? "linear-gradient(to right, transparent, black 6%)" : "linear-gradient(to left, transparent, black 6%)",
3619
- WebkitMaskImage: isRight ? "linear-gradient(to right, transparent, black 6%)" : "linear-gradient(to left, transparent, black 6%)"
3620
- } : {},
3621
- border: "none",
3622
- borderRadius: "0",
5184
+ transition: (() => {
5185
+ var _a3, _b2;
5186
+ const dur = (_a3 = config.canvas.transitionDuration) != null ? _a3 : "300ms";
5187
+ const ease = (_b2 = config.canvas.transitionEasing) != null ? _b2 : "cubic-bezier(0.16, 1, 0.3, 1)";
5188
+ return isOpen ? `transform ${dur} ${ease}, opacity ${dur} ease-out` : `transform ${dur} ${ease}, opacity ${dur} ease-in`;
5189
+ })(),
5190
+ color: "var(--sc-overlay-text-color)",
5191
+ background: "var(--sc-canvas-background)",
5192
+ // Standard mode: config blur only. Focused mode: config blur + saturate boost.
5193
+ ...(() => {
5194
+ var _a3;
5195
+ const blur = (_a3 = config.canvas.blur) != null ? _a3 : "blur(24px)";
5196
+ const filter = isFocused && blur !== "none" ? `${blur} saturate(1.2)` : blur;
5197
+ return { backdropFilter: filter, WebkitBackdropFilter: filter };
5198
+ })(),
5199
+ // Tight fade mask so blur cuts off sharply at the leading edge.
5200
+ // Disabled when a visible border is configured (mask would fade the border).
5201
+ ...(() => {
5202
+ var _a3;
5203
+ if (isFocused || canvasBorder && canvasBorder !== "none") return {};
5204
+ const fade = (_a3 = config.canvas.transitionFade) != null ? _a3 : "6%";
5205
+ const mask = isRight ? `linear-gradient(to right, transparent, black ${fade})` : `linear-gradient(to left, transparent, black ${fade})`;
5206
+ return { maskImage: mask, WebkitMaskImage: mask };
5207
+ })(),
5208
+ border: canvasBorder,
5209
+ borderRadius: "var(--sc-overlay-border-radius, 0)",
3623
5210
  boxShadow: "none",
3624
5211
  // Transform logic
3625
5212
  transform: isOpen ? "translateX(0)" : isRight ? "translateX(120%)" : "translateX(-120%)"
@@ -3633,7 +5220,7 @@ function ShadowCanvasOverlay({
3633
5220
  pointerEvents: "none",
3634
5221
  padding: "0"
3635
5222
  };
3636
- const content = /* @__PURE__ */ jsx6(
5223
+ const content = /* @__PURE__ */ jsx7(
3637
5224
  "div",
3638
5225
  {
3639
5226
  "data-shadow-canvas-id": "overlay-root",
@@ -3641,11 +5228,11 @@ function ShadowCanvasOverlay({
3641
5228
  position: "fixed",
3642
5229
  inset: 0,
3643
5230
  pointerEvents: isOpen ? "auto" : "none",
3644
- zIndex: theme.zIndex
5231
+ zIndex
3645
5232
  },
3646
5233
  children: /* @__PURE__ */ jsxs3("div", { style: wrapperStyle, children: [
3647
- /* @__PURE__ */ jsxs3("div", { "data-shadow-canvas-id": "overlay-container", style: containerStyle, children: [
3648
- isFocused && canvasTitle && /* @__PURE__ */ jsx6("header", { style: { color: "white", padding: "1.5rem 1.5rem 0" }, children: /* @__PURE__ */ jsx6(
5234
+ /* @__PURE__ */ jsxs3("div", { ref: containerRef, "data-shadow-canvas-id": "overlay-container", style: containerStyle, children: [
5235
+ isFocused && canvasTitle && /* @__PURE__ */ jsx7("header", { style: { color: "white", padding: "1.5rem 1.5rem 0" }, children: /* @__PURE__ */ jsx7(
3649
5236
  "p",
3650
5237
  {
3651
5238
  style: {
@@ -3653,17 +5240,32 @@ function ShadowCanvasOverlay({
3653
5240
  fontSize: "0.7rem",
3654
5241
  textTransform: "uppercase",
3655
5242
  letterSpacing: "0.5em",
3656
- color: text.tertiary
5243
+ color: "var(--sc-notification-text-secondary-color)"
3657
5244
  },
3658
5245
  children: canvasTitle
3659
5246
  }
3660
5247
  ) }),
3661
- /* @__PURE__ */ jsx6("div", { style: { flex: 1, overflowY: "auto", padding: isFocused ? "0" : "1rem" }, children: isLoading ? /* @__PURE__ */ jsx6("div", { style: { color: text.primary, padding: isFocused ? "1rem" : "0" }, children: "Loading..." }) : error2 ? /* @__PURE__ */ jsxs3("div", { style: { color: red[8], padding: isFocused ? "1rem" : "0" }, children: [
3662
- "Failed to load: ",
3663
- error2
3664
- ] }) : isFocused ? (
5248
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, overflowY: "auto", padding: isFocused ? "0" : "1rem" }, children: isLoading ? /* @__PURE__ */ jsx7(
5249
+ "div",
5250
+ {
5251
+ style: { color: "var(--sc-overlay-text-color)", padding: isFocused ? "1rem" : "0" },
5252
+ children: "Loading..."
5253
+ }
5254
+ ) : error2 ? /* @__PURE__ */ jsxs3(
5255
+ "div",
5256
+ {
5257
+ style: {
5258
+ color: "var(--sc-notification-error-color)",
5259
+ padding: isFocused ? "1rem" : "0"
5260
+ },
5261
+ children: [
5262
+ "Failed to load: ",
5263
+ error2
5264
+ ]
5265
+ }
5266
+ ) : isFocused ? (
3665
5267
  /* Focused Mode: Render first tile full size */
3666
- tiles.length > 0 ? /* @__PURE__ */ jsx6(
5268
+ tiles.length > 0 ? /* @__PURE__ */ jsx7(
3667
5269
  TileCard,
3668
5270
  {
3669
5271
  config: tiles[0],
@@ -3674,7 +5276,7 @@ function ShadowCanvasOverlay({
3674
5276
  ) : null
3675
5277
  ) : (
3676
5278
  /* Standard Mode: Stacked cards — widgets always visible */
3677
- /* @__PURE__ */ jsx6(
5279
+ /* @__PURE__ */ jsx7(
3678
5280
  "div",
3679
5281
  {
3680
5282
  style: {
@@ -3683,7 +5285,7 @@ function ShadowCanvasOverlay({
3683
5285
  gap: "0.75rem",
3684
5286
  width: "100%"
3685
5287
  },
3686
- children: tiles.map((tile) => /* @__PURE__ */ jsx6(
5288
+ children: tiles.map((tile) => /* @__PURE__ */ jsx7(
3687
5289
  TileCard,
3688
5290
  {
3689
5291
  config: tile,
@@ -3698,7 +5300,7 @@ function ShadowCanvasOverlay({
3698
5300
  ) }),
3699
5301
  footerSlot
3700
5302
  ] }),
3701
- /* @__PURE__ */ jsx6(
5303
+ /* @__PURE__ */ jsx7(
3702
5304
  "div",
3703
5305
  {
3704
5306
  onClick: toggle2,
@@ -3723,16 +5325,16 @@ function ShadowCanvasOverlay({
3723
5325
  position: "fixed",
3724
5326
  inset: 0,
3725
5327
  pointerEvents: "none",
3726
- zIndex: theme.zIndex + 47
5328
+ zIndex: zIndex + 47
3727
5329
  },
3728
5330
  children: [
3729
- /* @__PURE__ */ jsx6(
5331
+ /* @__PURE__ */ jsx7(
3730
5332
  NotificationToastStack,
3731
5333
  {
3732
5334
  notifications,
3733
5335
  onDismiss: dismissNotification,
3734
5336
  onClickNotification: handleNotificationClick,
3735
- position: theme.position
5337
+ position: (_c = config.canvas.position) != null ? _c : "right"
3736
5338
  }
3737
5339
  ),
3738
5340
  /* @__PURE__ */ jsxs3(
@@ -3752,36 +5354,36 @@ function ShadowCanvasOverlay({
3752
5354
  display: "flex",
3753
5355
  alignItems: "center",
3754
5356
  justifyContent: "center",
3755
- width: "56px",
3756
- height: "56px",
3757
- borderRadius: "9999px",
3758
- color: button.primary.icon,
3759
- boxShadow: `0 8px 32px ${withAlpha(slateGrey[0], 0.6)}`,
5357
+ width: "var(--sc-launcher-size, 56px)",
5358
+ height: "var(--sc-launcher-size, 56px)",
5359
+ borderRadius: "var(--sc-launcher-border-radius, 9999px)",
5360
+ color: "var(--sc-launcher-color)",
5361
+ boxShadow: "var(--sc-launcher-shadow)",
3760
5362
  transition: dragRef.current ? "none" : "transform 0.2s ease, opacity 0.3s ease, background-color 0.2s ease",
3761
5363
  outline: "none",
3762
5364
  padding: 0,
3763
5365
  border: "none",
3764
- backgroundColor: button.primary.backgroundDefault,
3765
- cursor: ((_g = dragRef.current) == null ? void 0 : _g.dragged) ? "grabbing" : "pointer",
5366
+ backgroundColor: "var(--sc-launcher-background)",
5367
+ cursor: ((_d = dragRef.current) == null ? void 0 : _d.dragged) ? "grabbing" : "pointer",
3766
5368
  touchAction: "none"
3767
5369
  },
3768
5370
  onPointerDown: onLauncherPointerDown,
3769
5371
  onPointerMove: onLauncherPointerMove,
3770
5372
  onPointerUp: onLauncherPointerUp,
3771
5373
  onMouseEnter: (e) => {
3772
- var _a3;
5374
+ var _a3, _b2;
3773
5375
  if (!((_a3 = dragRef.current) == null ? void 0 : _a3.dragged)) {
3774
- e.currentTarget.style.backgroundColor = button.primary.backgroundHover;
5376
+ e.currentTarget.style.backgroundColor = (_b2 = config.launcher.backgroundHover) != null ? _b2 : "var(--sc-launcher-background-hover)";
3775
5377
  }
3776
5378
  },
3777
5379
  onMouseLeave: (e) => {
3778
- var _a3;
5380
+ var _a3, _b2;
3779
5381
  if (!((_a3 = dragRef.current) == null ? void 0 : _a3.dragged)) {
3780
- e.currentTarget.style.backgroundColor = button.primary.backgroundDefault;
5382
+ e.currentTarget.style.backgroundColor = (_b2 = config.launcher.background) != null ? _b2 : "var(--sc-launcher-background)";
3781
5383
  }
3782
5384
  },
3783
5385
  children: [
3784
- /* @__PURE__ */ jsx6(
5386
+ isOpen ? /* @__PURE__ */ jsxs3(
3785
5387
  "svg",
3786
5388
  {
3787
5389
  width: "24",
@@ -3795,20 +5397,49 @@ function ShadowCanvasOverlay({
3795
5397
  "aria-hidden": "true",
3796
5398
  focusable: "false",
3797
5399
  style: { transition: "transform 200ms ease" },
3798
- children: isOpen ? /* @__PURE__ */ jsxs3(Fragment, { children: [
3799
- /* @__PURE__ */ jsx6("path", { d: "M18 6L6 18" }),
3800
- /* @__PURE__ */ jsx6("path", { d: "M6 6l12 12" })
3801
- ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
3802
- /* @__PURE__ */ jsx6("path", { d: "M12 3l1.912 5.813a2 2 0 0 0 1.275 1.275L21 12l-5.813 1.912a2 2 0 0 0-1.275 1.275L12 21l-1.912-5.813a2 2 0 0 0-1.275-1.275L3 12l5.813-1.912a2 2 0 0 0 1.275-1.275L12 3Z" }),
3803
- /* @__PURE__ */ jsx6("path", { d: "M5 3v4" }),
3804
- /* @__PURE__ */ jsx6("path", { d: "M3 5h4" }),
3805
- /* @__PURE__ */ jsx6("path", { d: "M19 17v4" }),
3806
- /* @__PURE__ */ jsx6("path", { d: "M17 19h4" })
3807
- ] })
5400
+ children: [
5401
+ /* @__PURE__ */ jsx7("path", { d: "M18 6L6 18" }),
5402
+ /* @__PURE__ */ jsx7("path", { d: "M6 6l12 12" })
5403
+ ]
5404
+ }
5405
+ ) : launcherIcon ? /* @__PURE__ */ jsx7(
5406
+ "img",
5407
+ {
5408
+ src: launcherIcon,
5409
+ alt: "",
5410
+ "aria-hidden": "true",
5411
+ style: {
5412
+ width: "60%",
5413
+ height: "60%",
5414
+ objectFit: "contain",
5415
+ pointerEvents: "none"
5416
+ }
5417
+ }
5418
+ ) : /* @__PURE__ */ jsxs3(
5419
+ "svg",
5420
+ {
5421
+ width: "24",
5422
+ height: "24",
5423
+ viewBox: "0 0 24 24",
5424
+ fill: "none",
5425
+ stroke: "currentColor",
5426
+ strokeWidth: "2",
5427
+ strokeLinecap: "round",
5428
+ strokeLinejoin: "round",
5429
+ "aria-hidden": "true",
5430
+ focusable: "false",
5431
+ style: { transition: "transform 200ms ease" },
5432
+ children: [
5433
+ /* @__PURE__ */ jsx7("path", { d: "M12 3l1.912 5.813a2 2 0 0 0 1.275 1.275L21 12l-5.813 1.912a2 2 0 0 0-1.275 1.275L12 21l-1.912-5.813a2 2 0 0 0-1.275-1.275L3 12l5.813-1.912a2 2 0 0 0 1.275-1.275L12 3Z" }),
5434
+ /* @__PURE__ */ jsx7("path", { d: "M5 3v4" }),
5435
+ /* @__PURE__ */ jsx7("path", { d: "M3 5h4" }),
5436
+ /* @__PURE__ */ jsx7("path", { d: "M19 17v4" }),
5437
+ /* @__PURE__ */ jsx7("path", { d: "M17 19h4" })
5438
+ ]
3808
5439
  }
3809
5440
  ),
3810
5441
  !isOpen && notifications.length > 0 && /* @__PURE__ */ jsxs3("div", { style: { position: "absolute", top: -2, right: -2, pointerEvents: "none" }, children: [
3811
- /* @__PURE__ */ jsx6(
5442
+ /* @__PURE__ */ jsx7(
3812
5443
  "span",
3813
5444
  {
3814
5445
  className: "syntro-badge-ping",
@@ -3816,11 +5447,11 @@ function ShadowCanvasOverlay({
3816
5447
  position: "absolute",
3817
5448
  inset: 0,
3818
5449
  borderRadius: "9999px",
3819
- background: withAlpha(brand[4], 0.4)
5450
+ background: `${config.colorPrimaryHover}66`
3820
5451
  }
3821
5452
  }
3822
5453
  ),
3823
- /* @__PURE__ */ jsx6(
5454
+ /* @__PURE__ */ jsx7(
3824
5455
  "span",
3825
5456
  {
3826
5457
  className: "syntro-badge-glow",
@@ -3831,7 +5462,7 @@ function ShadowCanvasOverlay({
3831
5462
  }
3832
5463
  }
3833
5464
  ),
3834
- /* @__PURE__ */ jsx6(
5465
+ /* @__PURE__ */ jsx7(
3835
5466
  "span",
3836
5467
  {
3837
5468
  className: "syntro-badge-bounce",
@@ -3843,8 +5474,8 @@ function ShadowCanvasOverlay({
3843
5474
  width: 20,
3844
5475
  height: 20,
3845
5476
  borderRadius: "9999px",
3846
- background: brand[4],
3847
- color: base.white,
5477
+ background: config.colorPrimaryHover,
5478
+ color: "#ffffff",
3848
5479
  fontSize: 11,
3849
5480
  fontWeight: 700
3850
5481
  },
@@ -3860,33 +5491,46 @@ function ShadowCanvasOverlay({
3860
5491
  ),
3861
5492
  portalRoot
3862
5493
  ),
3863
- isOpen ? createPortal(content, portalRoot) : null
5494
+ createPortal(content, portalRoot)
3864
5495
  ] });
3865
5496
  }
3866
5497
 
3867
5498
  // src/hooks/useShadowCanvasConfig.ts
3868
- import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMemo3, useState as useState6 } from "react";
5499
+ import { useCallback as useCallback5, useEffect as useEffect8, useMemo as useMemo6, useRef as useRef6, useState as useState6 } from "react";
3869
5500
  var sortTiles = (tiles) => [...tiles].sort((a, b) => {
3870
5501
  var _a2, _b;
3871
5502
  return ((_a2 = b.priority) != null ? _a2 : 0) - ((_b = a.priority) != null ? _b : 0);
3872
5503
  });
3873
5504
  function useShadowCanvasConfig({
3874
5505
  fetcher,
3875
- pollIntervalMs = 3e4,
3876
5506
  experiments,
3877
5507
  runtime: runtime3,
3878
- pathname
5508
+ pageUrl,
5509
+ pollIntervalMs
3879
5510
  }) {
3880
5511
  const [state, setState] = useState6({
3881
5512
  tiles: [],
3882
5513
  actions: [],
3883
5514
  isLoading: true
3884
5515
  });
3885
- const load = useCallback4(async () => {
5516
+ const prevActionsJsonRef = useRef6("[]");
5517
+ const rawConfigRef = useRef6(null);
5518
+ const refilter = useCallback5(async () => {
5519
+ const raw = rawConfigRef.current;
5520
+ if (!raw || !runtime3) return;
5521
+ let tiles = raw.tiles || [];
5522
+ tiles = await runtime3.filterTiles(tiles);
5523
+ if (experiments) {
5524
+ tiles = tiles.filter((tile) => experiments.shouldRenderRectangle(tile));
5525
+ }
5526
+ setState((prev) => ({ ...prev, tiles: sortTiles(tiles) }));
5527
+ }, [runtime3, experiments]);
5528
+ const load = useCallback5(async () => {
3886
5529
  try {
3887
5530
  setState((prev) => ({ ...prev, isLoading: true, error: void 0 }));
3888
5531
  const response = await fetcher();
3889
5532
  debug("SmartCanvas Config", "Raw config response", response);
5533
+ rawConfigRef.current = response;
3890
5534
  let tiles = response.tiles || [];
3891
5535
  if (runtime3) {
3892
5536
  tiles = await runtime3.filterTiles(tiles);
@@ -3897,16 +5541,22 @@ function useShadowCanvasConfig({
3897
5541
  tiles = tiles.filter((tile) => experiments.shouldRenderRectangle(tile));
3898
5542
  }
3899
5543
  debug("SmartCanvas Config", `Tile count after filtering: ${tiles.length}`);
3900
- setState({
5544
+ const newActions = response.actions || [];
5545
+ const newActionsJson = JSON.stringify(newActions);
5546
+ const actionsChanged = newActionsJson !== prevActionsJsonRef.current;
5547
+ if (actionsChanged) {
5548
+ prevActionsJsonRef.current = newActionsJson;
5549
+ }
5550
+ setState((prev) => ({
3901
5551
  tiles: sortTiles(tiles),
3902
- actions: response.actions || [],
5552
+ actions: actionsChanged ? newActions : prev.actions,
3903
5553
  isLoading: false,
3904
5554
  fetchedAt: response.fetchedAt,
3905
5555
  canvasTitle: response.canvasTitle,
3906
5556
  configVersion: response.configVersion,
3907
5557
  theme: response.theme,
3908
5558
  launcher: response.launcher
3909
- });
5559
+ }));
3910
5560
  } catch (err) {
3911
5561
  const message = err instanceof Error ? err.message : "Unknown error";
3912
5562
  console.error("[SmartCanvas Config] Failed to fetch/filter config:", message, err);
@@ -3917,18 +5567,37 @@ function useShadowCanvasConfig({
3917
5567
  }));
3918
5568
  }
3919
5569
  }, [experiments, fetcher, runtime3]);
3920
- useEffect7(() => {
5570
+ useEffect8(() => {
3921
5571
  load();
5572
+ if (experiments == null ? void 0 : experiments.onFeaturesChanged) {
5573
+ return experiments.onFeaturesChanged(() => load());
5574
+ }
5575
+ }, [load, experiments, pageUrl]);
5576
+ useEffect8(() => {
5577
+ if (!(runtime3 == null ? void 0 : runtime3.accumulator)) return;
5578
+ return runtime3.accumulator.subscribe(() => {
5579
+ refilter();
5580
+ });
5581
+ }, [runtime3, refilter]);
5582
+ useEffect8(() => {
5583
+ if (!(runtime3 == null ? void 0 : runtime3.context)) return;
5584
+ return runtime3.context.subscribe(() => {
5585
+ refilter();
5586
+ });
5587
+ }, [runtime3, refilter]);
5588
+ useEffect8(() => {
3922
5589
  if (!pollIntervalMs) return;
3923
- const id = setInterval(load, pollIntervalMs);
5590
+ const id = setInterval(() => {
5591
+ load();
5592
+ }, pollIntervalMs);
3924
5593
  return () => clearInterval(id);
3925
- }, [load, pollIntervalMs, pathname]);
3926
- return useMemo3(() => state, [state]);
5594
+ }, [load, pollIntervalMs]);
5595
+ return useMemo6(() => state, [state]);
3927
5596
  }
3928
5597
 
3929
5598
  // src/SmartCanvasApp.tsx
3930
- import { useEffect as useEffect8, useMemo as useMemo4, useRef as useRef5, useState as useState7 } from "react";
3931
- import { jsx as jsx7 } from "react/jsx-runtime";
5599
+ import { useEffect as useEffect9, useMemo as useMemo7, useRef as useRef7, useState as useState7 } from "react";
5600
+ import { jsx as jsx8 } from "react/jsx-runtime";
3932
5601
  function SmartCanvasApp({
3933
5602
  controller,
3934
5603
  fetcher,
@@ -3947,11 +5616,11 @@ function SmartCanvasApp({
3947
5616
  footerSlot,
3948
5617
  launcherLabel,
3949
5618
  canvasHost,
3950
- theme,
3951
- initialBatchHandle
5619
+ initialBatchHandle,
5620
+ workspaceTheme
3952
5621
  }) {
3953
5622
  if (runtime3) {
3954
- return /* @__PURE__ */ jsx7(RuntimeProvider, { runtime: runtime3, children: /* @__PURE__ */ jsx7(
5623
+ return /* @__PURE__ */ jsx8(RuntimeProvider, { runtime: runtime3, children: /* @__PURE__ */ jsx8(
3955
5624
  SmartCanvasAppInner,
3956
5625
  {
3957
5626
  controller,
@@ -3971,12 +5640,12 @@ function SmartCanvasApp({
3971
5640
  footerSlot,
3972
5641
  launcherLabel,
3973
5642
  canvasHost,
3974
- theme,
3975
- initialBatchHandle
5643
+ initialBatchHandle,
5644
+ workspaceTheme
3976
5645
  }
3977
5646
  ) });
3978
5647
  }
3979
- return /* @__PURE__ */ jsx7(
5648
+ return /* @__PURE__ */ jsx8(
3980
5649
  SmartCanvasAppInner,
3981
5650
  {
3982
5651
  controller,
@@ -3995,8 +5664,8 @@ function SmartCanvasApp({
3995
5664
  footerSlot,
3996
5665
  launcherLabel,
3997
5666
  canvasHost,
3998
- theme,
3999
- initialBatchHandle
5667
+ initialBatchHandle,
5668
+ workspaceTheme
4000
5669
  }
4001
5670
  );
4002
5671
  }
@@ -4018,38 +5687,40 @@ function SmartCanvasAppInner({
4018
5687
  footerSlot,
4019
5688
  launcherLabel,
4020
5689
  canvasHost: _canvasHost,
4021
- theme,
4022
- initialBatchHandle
5690
+ initialBatchHandle,
5691
+ workspaceTheme
4023
5692
  }) {
4024
- var _a2, _b, _c, _d, _e;
5693
+ var _a2, _b, _c, _d, _e, _f, _g;
4025
5694
  const [open, setOpen] = useState7(controller.getState().open);
4026
5695
  const pageContext = usePageContext();
4027
- const [localPathname, setLocalPathname] = useState7(
4028
- () => typeof window !== "undefined" ? window.location.pathname : "/"
5696
+ const [localUrl, setLocalUrl] = useState7(
5697
+ () => typeof window !== "undefined" ? window.location.href : "/"
4029
5698
  );
4030
- const pathname = (pageContext == null ? void 0 : pageContext.url) ? new URL(pageContext.url).pathname : localPathname;
4031
- useEffect8(() => {
5699
+ const pageUrl = (_a2 = pageContext == null ? void 0 : pageContext.url) != null ? _a2 : localUrl;
5700
+ useEffect9(() => {
4032
5701
  if (runtime3) return;
4033
5702
  if (typeof window === "undefined") return;
4034
- const updatePathname = () => setLocalPathname(window.location.pathname);
4035
- window.addEventListener("popstate", updatePathname);
5703
+ const updateUrl = () => setLocalUrl(window.location.href);
5704
+ window.addEventListener("popstate", updateUrl);
5705
+ window.addEventListener("hashchange", updateUrl);
4036
5706
  const originalPushState = history.pushState;
4037
5707
  const originalReplaceState = history.replaceState;
4038
5708
  history.pushState = function(...args) {
4039
5709
  originalPushState.apply(this, args);
4040
- queueMicrotask(updatePathname);
5710
+ queueMicrotask(updateUrl);
4041
5711
  };
4042
5712
  history.replaceState = function(...args) {
4043
5713
  originalReplaceState.apply(this, args);
4044
- queueMicrotask(updatePathname);
5714
+ queueMicrotask(updateUrl);
4045
5715
  };
4046
5716
  return () => {
4047
- window.removeEventListener("popstate", updatePathname);
5717
+ window.removeEventListener("popstate", updateUrl);
5718
+ window.removeEventListener("hashchange", updateUrl);
4048
5719
  history.pushState = originalPushState;
4049
5720
  history.replaceState = originalReplaceState;
4050
5721
  };
4051
5722
  }, [runtime3]);
4052
- const derivedFetcher = useMemo4(() => {
5723
+ const derivedFetcher = useMemo7(() => {
4053
5724
  if (fetcher) return fetcher;
4054
5725
  return createCanvasConfigFetcher({
4055
5726
  configUri,
@@ -4061,21 +5732,21 @@ function SmartCanvasAppInner({
4061
5732
  }, [fetcher, configUri, experiments, configUriFeatureKey, configFeatureKey, fetchCredentials]);
4062
5733
  const configState = useShadowCanvasConfig({
4063
5734
  fetcher: derivedFetcher,
4064
- pollIntervalMs,
4065
5735
  experiments,
4066
5736
  runtime: runtime3,
4067
- pathname
5737
+ pageUrl
4068
5738
  });
4069
5739
  const hasContent = configState.tiles.length > 0 && !configState.error;
4070
- useEffect8(() => controller.subscribe((state) => setOpen(state.open)), [controller]);
4071
- useEffect8(() => {
5740
+ useEffect9(() => controller.subscribe((state) => setOpen(state.open)), [controller]);
5741
+ useEffect9(() => {
4072
5742
  if (!configState.isLoading && !hasContent && controller.getState().open) {
4073
5743
  controller.setOpen(false);
4074
5744
  }
4075
5745
  }, [controller, hasContent, configState.isLoading]);
4076
- const batchHandleRef = useRef5(initialBatchHandle != null ? initialBatchHandle : null);
4077
- const adoptedInitialRef = useRef5(!!initialBatchHandle);
4078
- useEffect8(() => {
5746
+ const batchHandleRef = useRef7(initialBatchHandle != null ? initialBatchHandle : null);
5747
+ const adoptedInitialRef = useRef7(!!initialBatchHandle);
5748
+ const runVersionRef = useRef7(0);
5749
+ useEffect9(() => {
4079
5750
  if (!(runtime3 == null ? void 0 : runtime3.actions)) return;
4080
5751
  if (adoptedInitialRef.current) {
4081
5752
  if (configState.actions.length > 0) {
@@ -4083,10 +5754,10 @@ function SmartCanvasAppInner({
4083
5754
  }
4084
5755
  return;
4085
5756
  }
4086
- let cancelled = false;
5757
+ const version = ++runVersionRef.current;
5758
+ const stale = () => version !== runVersionRef.current;
4087
5759
  const run = async () => {
4088
5760
  if (batchHandleRef.current) {
4089
- console.log("[SmartCanvasApp] Reverting previous action batch before re-apply");
4090
5761
  try {
4091
5762
  await batchHandleRef.current.revertAll();
4092
5763
  } catch (err) {
@@ -4094,92 +5765,76 @@ function SmartCanvasAppInner({
4094
5765
  }
4095
5766
  batchHandleRef.current = null;
4096
5767
  }
4097
- if (cancelled) return;
5768
+ if (stale()) return;
4098
5769
  if (configState.actions.length > 0) {
4099
- console.log(
4100
- `[SmartCanvasApp] Applying ${configState.actions.length} action(s):`,
4101
- configState.actions.map(
4102
- (a, i) => `[${i}] ${a.kind}${a.anchorId ? ` anchor="${a.anchorId}"` : ""}${a.label ? ` "${a.label}"` : ""}`
4103
- ).join(", ")
4104
- );
4105
5770
  try {
4106
5771
  const handle = await runtime3.actions.applyBatch(configState.actions);
4107
- if (cancelled) {
5772
+ if (stale()) {
4108
5773
  await handle.revertAll();
4109
5774
  } else {
4110
5775
  batchHandleRef.current = handle;
4111
- console.log("[SmartCanvasApp] Action batch applied successfully");
4112
5776
  }
4113
5777
  } catch (err) {
4114
- if (!cancelled) {
4115
- console.error(
4116
- "[SmartCanvasApp] Failed to apply actions:",
4117
- err,
4118
- "\nActions that failed:",
4119
- JSON.stringify(configState.actions, null, 2)
4120
- );
5778
+ if (!stale()) {
5779
+ console.error("[SmartCanvasApp] Failed to apply actions:", err);
4121
5780
  }
4122
5781
  }
4123
5782
  }
4124
5783
  };
4125
5784
  run();
4126
5785
  return () => {
4127
- cancelled = true;
4128
5786
  if (batchHandleRef.current) {
4129
- console.log(
4130
- "[SmartCanvasApp] Cleanup: reverting action batch (actions changed or unmount)"
4131
- );
4132
5787
  batchHandleRef.current.revertAll().catch((err) => {
4133
5788
  console.error("[SmartCanvasApp] Failed to revert actions on cleanup:", err);
4134
5789
  });
4135
5790
  batchHandleRef.current = null;
4136
5791
  }
4137
5792
  };
4138
- }, [runtime3, configState.actions]);
4139
- useEffect8(() => {
5793
+ }, [runtime3, configState.actions, pageUrl]);
5794
+ useEffect9(() => {
4140
5795
  if (!runtime3) return;
4141
5796
  return runtime3.events.subscribe(
4142
5797
  { names: ["canvas.requestOpen"] },
4143
5798
  () => controller.setOpen(true)
4144
5799
  );
4145
5800
  }, [runtime3, controller]);
4146
- const mergedTheme = useMemo4(() => {
4147
- const configTheme = configState.theme;
4148
- if (!configTheme) return theme;
4149
- return {
4150
- ...configTheme,
4151
- // position, mode, colors, borderRadius from config
4152
- ...theme
4153
- // any direct overrides from theme prop
4154
- };
4155
- }, [configState.theme, theme]);
5801
+ const { shadowRoot } = useShadowRoot();
5802
+ const themeConfig = configState.theme;
4156
5803
  if (!configState.isLoading && !hasContent) {
4157
5804
  return null;
4158
5805
  }
4159
- return /* @__PURE__ */ jsx7(
4160
- ShadowCanvasOverlay,
5806
+ return /* @__PURE__ */ jsx8(
5807
+ ThemeProvider,
4161
5808
  {
4162
- tiles: configState.tiles,
4163
- isLoading: configState.isLoading,
4164
- error: configState.error,
4165
- canvasTitle: configState.canvasTitle,
4166
- telemetry,
4167
- launcherLabel: launcherLabel != null ? launcherLabel : (_a2 = configState.launcher) == null ? void 0 : _a2.label,
4168
- launcherAnimate: (_b = configState.launcher) == null ? void 0 : _b.animate,
4169
- launcherAnimationStyle: (_c = configState.launcher) == null ? void 0 : _c.animationStyle,
4170
- notificationCount: (_e = (_d = configState.launcher) == null ? void 0 : _d.notificationCount) != null ? _e : configState.tiles.length,
4171
- footerSlot,
4172
- isOpen: open,
4173
- onToggle: () => controller.toggle(),
4174
- displayMode: configState.displayMode,
4175
- theme: mergedTheme
5809
+ themeConfig,
5810
+ workspaceTheme,
5811
+ shadowRoot,
5812
+ children: /* @__PURE__ */ jsx8(
5813
+ ShadowCanvasOverlay,
5814
+ {
5815
+ tiles: configState.tiles,
5816
+ isLoading: configState.isLoading,
5817
+ error: configState.error,
5818
+ canvasTitle: configState.canvasTitle,
5819
+ telemetry,
5820
+ launcherLabel: launcherLabel != null ? launcherLabel : (_b = configState.launcher) == null ? void 0 : _b.label,
5821
+ launcherIcon: (_c = configState.launcher) == null ? void 0 : _c.icon,
5822
+ launcherAnimate: (_d = configState.launcher) == null ? void 0 : _d.animate,
5823
+ launcherAnimationStyle: (_e = configState.launcher) == null ? void 0 : _e.animationStyle,
5824
+ notificationCount: (_g = (_f = configState.launcher) == null ? void 0 : _f.notificationCount) != null ? _g : configState.tiles.length,
5825
+ footerSlot,
5826
+ isOpen: open,
5827
+ onToggle: () => controller.toggle(),
5828
+ displayMode: configState.displayMode
5829
+ }
5830
+ )
4176
5831
  }
4177
5832
  );
4178
5833
  }
4179
5834
 
4180
5835
  // src/SmartCanvasElement.tsx
4181
- import { createRoot } from "react-dom/client";
4182
- import { jsx as jsx8 } from "react/jsx-runtime";
5836
+ import { createRoot as createRoot2 } from "react-dom/client";
5837
+ import { jsx as jsx9 } from "react/jsx-runtime";
4183
5838
  var TAG_NAME = "smart-canvas";
4184
5839
  var BASE_CSS = `
4185
5840
  :host {
@@ -4219,7 +5874,7 @@ var SmartCanvasElement = class extends HTMLElement {
4219
5874
  Object.assign(__privateGet(this, _overlayContainer).style, {
4220
5875
  position: "fixed",
4221
5876
  inset: "0",
4222
- zIndex: "2147483647",
5877
+ zIndex: "2147483645",
4223
5878
  pointerEvents: "none"
4224
5879
  });
4225
5880
  __privateGet(this, _shadow).appendChild(__privateGet(this, _overlayContainer));
@@ -4277,16 +5932,16 @@ var SmartCanvasElement = class extends HTMLElement {
4277
5932
  render() {
4278
5933
  if (!this.isConnected || !__privateGet(this, _lastAppProps)) return;
4279
5934
  if (!__privateGet(this, _root)) {
4280
- __privateSet(this, _root, createRoot(__privateGet(this, _mount)));
5935
+ __privateSet(this, _root, createRoot2(__privateGet(this, _mount)));
4281
5936
  }
4282
5937
  __privateGet(this, _root).render(
4283
- /* @__PURE__ */ jsx8(
5938
+ /* @__PURE__ */ jsx9(
4284
5939
  ShadowRootProvider,
4285
5940
  {
4286
5941
  shadowRoot: __privateGet(this, _shadow),
4287
5942
  portalRoot: __privateGet(this, _portalRoot),
4288
5943
  overlayContainer: __privateGet(this, _overlayContainer),
4289
- children: /* @__PURE__ */ jsx8(SmartCanvasApp, { ...__privateGet(this, _lastAppProps), controller: __privateGet(this, _controller), canvasHost: this })
5944
+ children: /* @__PURE__ */ jsx9(SmartCanvasApp, { ...__privateGet(this, _lastAppProps), controller: __privateGet(this, _controller), canvasHost: this })
4290
5945
  }
4291
5946
  )
4292
5947
  );
@@ -4530,7 +6185,7 @@ function ensureOverlayRoot() {
4530
6185
  Object.assign(overlayRootEl.style, {
4531
6186
  position: "fixed",
4532
6187
  inset: "0",
4533
- zIndex: "2147483647",
6188
+ zIndex: "2147483645",
4534
6189
  pointerEvents: "none"
4535
6190
  });
4536
6191
  document.body.appendChild(overlayRootEl);
@@ -4542,20 +6197,23 @@ function injectBaseStyles(target) {
4542
6197
  if (stylesInjected) return;
4543
6198
  const css = `
4544
6199
  :host {
4545
- --syntro-surface: #0f1318;
4546
- --syntro-fg: #cbd0d7;
4547
- --syntro-accent: #b72e2a;
4548
- --syntro-accent-hover: #d44844;
4549
- --syntro-radius: 12px;
6200
+ --syntro-surface: var(--sc-overlay-background, #0f1318);
6201
+ --syntro-fg: var(--sc-overlay-text-color, #cbd0d7);
6202
+ --syntro-accent: var(--sc-color-primary, #b72e2a);
6203
+ --syntro-accent-hover: var(--sc-color-primary-hover, #d44844);
6204
+ --syntro-radius: var(--sc-border-radius, 12px);
4550
6205
  --syntro-shadow: 0 25px 50px -12px rgba(16,24,40,0.25);
4551
- --syntro-ring: #d44844;
4552
- --syntro-border: #2b333f;
6206
+ --syntro-ring: var(--sc-overlay-highlight-ring, #d44844);
6207
+ --syntro-border: var(--sc-overlay-border, #2b333f);
4553
6208
  --syntro-tooltip-bg: var(--syntro-surface);
4554
6209
  --syntro-tooltip-fg: var(--syntro-fg);
6210
+ --syntro-tooltip-title-color: var(--sc-overlay-title-color, var(--syntro-fg));
6211
+ --syntro-tooltip-arrow-bg: var(--sc-overlay-arrow-color, var(--syntro-tooltip-bg));
6212
+ --syntro-tooltip-arrow-size: var(--sc-overlay-arrow-size, 8px);
4555
6213
  --syntro-tooltip-radius: var(--syntro-radius);
4556
6214
  --syntro-tooltip-padding: 12px 16px;
4557
6215
  --syntro-tooltip-shadow: var(--syntro-shadow);
4558
- --syntro-spotlight-backdrop: rgba(0,0,0,0.70);
6216
+ --syntro-spotlight-backdrop: rgba(0,0,0, var(--sc-overlay-scrim-opacity, 0.70));
4559
6217
  }
4560
6218
 
4561
6219
  /* Tooltip container */
@@ -4578,14 +6236,13 @@ function injectBaseStyles(target) {
4578
6236
  transform 200ms cubic-bezier(0.16, 1, 0.3, 1);
4579
6237
  }
4580
6238
 
4581
- /* Tooltip arrow */
6239
+ /* Tooltip arrow \u2014 triangle via clip-path (square box so rotation is symmetric) */
4582
6240
  .syntro-tooltip-arrow {
4583
6241
  position: absolute;
4584
- width: 8px;
4585
- height: 8px;
4586
- background: inherit;
4587
- transform: rotate(45deg);
4588
- z-index: -1;
6242
+ width: var(--syntro-tooltip-arrow-size);
6243
+ height: var(--syntro-tooltip-arrow-size);
6244
+ background: var(--syntro-tooltip-arrow-bg);
6245
+ clip-path: polygon(0 0, 100% 0, 50% 100%);
4589
6246
  }
4590
6247
 
4591
6248
  /* Tooltip content */
@@ -4593,6 +6250,7 @@ function injectBaseStyles(target) {
4593
6250
  font-weight: 600;
4594
6251
  font-size: 15px;
4595
6252
  margin-bottom: 6px;
6253
+ color: var(--syntro-tooltip-title-color);
4596
6254
  }
4597
6255
 
4598
6256
  .syntro-tt-body {
@@ -4821,7 +6479,6 @@ var createSmartCanvas = async (config = {}) => {
4821
6479
  let currentConfig = null;
4822
6480
  let currentFetcher = null;
4823
6481
  let currentBatchHandle = null;
4824
- let isEnabled = true;
4825
6482
  const { runtime: runtime3 } = config;
4826
6483
  async function applyActions(actions) {
4827
6484
  if (!(runtime3 == null ? void 0 : runtime3.actions) || actions.length === 0) {
@@ -4848,13 +6505,13 @@ var createSmartCanvas = async (config = {}) => {
4848
6505
  "[SmartCanvas] Config fetched:",
4849
6506
  `tiles=${(_b = (_a2 = canvasConfig.tiles) == null ? void 0 : _a2.length) != null ? _b : 0},`,
4850
6507
  `actions=${(_d = (_c = canvasConfig.actions) == null ? void 0 : _c.length) != null ? _d : 0},`,
4851
- `theme=${(_f = (_e = canvasConfig.theme) == null ? void 0 : _e.name) != null ? _f : "none"}`
6508
+ `theme=${(_f = (_e = canvasConfig.theme) == null ? void 0 : _e.mode) != null ? _f : "default"}`
4852
6509
  );
4853
6510
  if ((_g = canvasConfig.actions) == null ? void 0 : _g.length) {
4854
6511
  console.log(
4855
6512
  "[SmartCanvas] Actions to apply:",
4856
6513
  canvasConfig.actions.map(
4857
- (a, i) => `[${i}] ${a.kind}${a.anchorId ? ` anchor="${a.anchorId}"` : ""}${a.label ? ` "${a.label}"` : ""}`
6514
+ (a, i) => `[${i}] ${a.kind}${a.anchorId ? ` anchor="${typeof a.anchorId === "string" ? a.anchorId : a.anchorId.selector}"` : ""}${a.label ? ` "${a.label}"` : ""}`
4858
6515
  ).join(", ")
4859
6516
  );
4860
6517
  }
@@ -4863,7 +6520,7 @@ var createSmartCanvas = async (config = {}) => {
4863
6520
  console.log("[SmartCanvas] Initial actions applied successfully");
4864
6521
  }
4865
6522
  if (canvasConfig.tiles && (runtime3 == null ? void 0 : runtime3.accumulator)) {
4866
- registerConfigPredicates(canvasConfig.tiles, runtime3.accumulator);
6523
+ registerConfigPredicates(canvasConfig.tiles, runtime3.accumulator, canvasConfig.actions);
4867
6524
  }
4868
6525
  } catch (error2) {
4869
6526
  console.error("[SmartCanvas] Error fetching/applying initial config:", error2);
@@ -4921,8 +6578,8 @@ var createSmartCanvas = async (config = {}) => {
4921
6578
  configUri: config.configUri,
4922
6579
  configUriFeatureKey: config.configUriFeatureKey,
4923
6580
  fetchCredentials: config.fetchCredentials,
4924
- theme: config.theme,
4925
- initialBatchHandle: currentBatchHandle
6581
+ initialBatchHandle: currentBatchHandle,
6582
+ workspaceTheme: config.workspaceTheme
4926
6583
  };
4927
6584
  host.mountReactApp(appProps);
4928
6585
  if (config.defaultOpen) {
@@ -4953,30 +6610,7 @@ var createSmartCanvas = async (config = {}) => {
4953
6610
  }
4954
6611
  return currentConfig != null ? currentConfig : { tiles: [], actions: [], fetchedAt: "" };
4955
6612
  },
4956
- updateConfig: async (newConfig) => {
4957
- var _a3, _b2, _c2, _d2;
4958
- console.log(
4959
- "[SmartCanvas] updateConfig:",
4960
- `tiles=${(_b2 = (_a3 = newConfig.tiles) == null ? void 0 : _a3.length) != null ? _b2 : 0},`,
4961
- `actions=${(_d2 = (_c2 = newConfig.actions) == null ? void 0 : _c2.length) != null ? _d2 : 0},`,
4962
- `enabled=${isEnabled}`
4963
- );
4964
- currentConfig = newConfig;
4965
- if (newConfig.tiles && (runtime3 == null ? void 0 : runtime3.accumulator)) {
4966
- registerConfigPredicates(newConfig.tiles, runtime3.accumulator);
4967
- }
4968
- if (isEnabled) {
4969
- try {
4970
- await applyActions(newConfig.actions);
4971
- console.log("[SmartCanvas] updateConfig actions applied successfully");
4972
- } catch (error2) {
4973
- console.error("[SmartCanvas] updateConfig failed to apply actions:", error2);
4974
- }
4975
- }
4976
- host.setOverrideFetcher(() => Promise.resolve(newConfig));
4977
- },
4978
6613
  setEnabled: async (enabled) => {
4979
- isEnabled = enabled;
4980
6614
  if (enabled) {
4981
6615
  if (currentConfig) {
4982
6616
  await applyActions(currentConfig.actions);
@@ -4991,7 +6625,7 @@ var createSmartCanvas = async (config = {}) => {
4991
6625
  if (runtime3 == null ? void 0 : runtime3.accumulator) {
4992
6626
  newFetcher().then((config2) => {
4993
6627
  if (config2 == null ? void 0 : config2.tiles) {
4994
- registerConfigPredicates(config2.tiles, runtime3.accumulator);
6628
+ registerConfigPredicates(config2.tiles, runtime3.accumulator, config2.actions);
4995
6629
  }
4996
6630
  }).catch(() => {
4997
6631
  });
@@ -5038,6 +6672,7 @@ var DEFAULT_EXPERIMENT_API_HOST = "https://experiment.syntrologie.com";
5038
6672
  var GrowthBookAdapter = class {
5039
6673
  constructor(options = {}) {
5040
6674
  __publicField(this, "gb");
6675
+ __publicField(this, "featureListeners", /* @__PURE__ */ new Set());
5041
6676
  var _a2, _b;
5042
6677
  if (options.client) {
5043
6678
  this.gb = options.client;
@@ -5093,6 +6728,20 @@ var GrowthBookAdapter = class {
5093
6728
  shouldRenderRectangle(_tile) {
5094
6729
  return true;
5095
6730
  }
6731
+ onFeaturesChanged(callback) {
6732
+ this.featureListeners.add(callback);
6733
+ if (this.featureListeners.size === 1) {
6734
+ this.gb.setRenderer(() => {
6735
+ for (const cb of this.featureListeners) cb();
6736
+ });
6737
+ }
6738
+ return () => {
6739
+ this.featureListeners.delete(callback);
6740
+ if (this.featureListeners.size === 0) {
6741
+ this.gb.setRenderer(null);
6742
+ }
6743
+ };
6744
+ }
5096
6745
  };
5097
6746
  function createGrowthBookClient(options = {}) {
5098
6747
  return new GrowthBookAdapter(options);
@@ -5510,315 +7159,67 @@ var executeSequence = async (action, context) => {
5510
7159
  }
5511
7160
  }
5512
7161
  }
5513
- };
5514
- };
5515
- var executeParallel = async (action, context) => {
5516
- const handles = [];
5517
- if (context.applyAction) {
5518
- const promises = action.actions.map((childAction) => context.applyAction(childAction));
5519
- if (action.waitFor === "any") {
5520
- const firstHandle = await Promise.race(promises);
5521
- handles.push(firstHandle);
5522
- const remaining = await Promise.allSettled(promises);
5523
- for (const result of remaining) {
5524
- if (result.status === "fulfilled" && result.value !== firstHandle) {
5525
- handles.push(result.value);
5526
- }
5527
- }
5528
- } else {
5529
- const allHandles = await Promise.all(promises);
5530
- handles.push(...allHandles);
5531
- }
5532
- }
5533
- return {
5534
- cleanup: async () => {
5535
- for (const handle of handles) {
5536
- try {
5537
- await handle.revert();
5538
- } catch {
5539
- }
5540
- }
5541
- }
5542
- };
5543
- };
5544
- var executeWait = async (action, context) => {
5545
- let unsubscribe;
5546
- let timeoutId;
5547
- let cancelled = false;
5548
- const result = {
5549
- cleanup: () => {
5550
- cancelled = true;
5551
- if (timeoutId) clearTimeout(timeoutId);
5552
- if (unsubscribe) unsubscribe();
5553
- }
5554
- };
5555
- if (action.event && context.subscribeEvent) {
5556
- await new Promise((resolve) => {
5557
- unsubscribe = context.subscribeEvent(action.event, () => {
5558
- if (!cancelled) resolve();
5559
- });
5560
- if (action.durationMs !== void 0) {
5561
- timeoutId = setTimeout(() => {
5562
- if (!cancelled) resolve();
5563
- }, action.durationMs);
5564
- }
5565
- });
5566
- } else if (action.durationMs !== void 0) {
5567
- await new Promise((resolve) => {
5568
- timeoutId = setTimeout(() => {
5569
- if (!cancelled) resolve();
5570
- }, action.durationMs);
5571
- });
5572
- }
5573
- return result;
5574
- };
5575
-
5576
- // src/actions/executors/tour.ts
5577
- var ACTIVE_TOUR_KEY = "syntro_active_tour";
5578
- var activeTours = /* @__PURE__ */ new Map();
5579
- function getTourState(tourId) {
5580
- try {
5581
- const data = localStorage.getItem(ACTIVE_TOUR_KEY);
5582
- if (!data) return null;
5583
- const state = JSON.parse(data);
5584
- if (state.tourId !== tourId) return null;
5585
- return state;
5586
- } catch {
5587
- return null;
5588
- }
5589
- }
5590
- function saveTourState(state) {
5591
- try {
5592
- localStorage.setItem(ACTIVE_TOUR_KEY, JSON.stringify(state));
5593
- } catch {
5594
- }
5595
- }
5596
- function clearTourState() {
5597
- try {
5598
- localStorage.removeItem(ACTIVE_TOUR_KEY);
5599
- } catch {
5600
- }
5601
- }
5602
- function getCurrentRoute() {
5603
- return window.location.pathname;
5604
- }
5605
- function stepMatchesRoute(step) {
5606
- if (!step.route) return true;
5607
- const currentRoute = getCurrentRoute();
5608
- if (step.route.includes("*")) {
5609
- const pattern = new RegExp(`^${step.route.replace(/\*/g, ".*")}$`);
5610
- return pattern.test(currentRoute);
5611
- }
5612
- return currentRoute === step.route;
5613
- }
5614
- var executeTour = async (action, context) => {
5615
- const { tourId, steps, startStep, autoStart = true } = action;
5616
- if (steps.length === 0) {
5617
- throw new Error(`Tour "${tourId}" has no steps`);
5618
- }
5619
- if (activeTours.has(tourId)) {
5620
- return {
5621
- cleanup: async () => {
5622
- const existing = activeTours.get(tourId);
5623
- if (existing) {
5624
- await existing.cleanup();
5625
- }
5626
- }
5627
- };
5628
- }
5629
- if (!context.applyAction) {
5630
- throw new Error("Tour executor requires applyAction in context");
5631
- }
5632
- let state = getTourState(tourId);
5633
- const isResumingTour = !!state;
5634
- if (!isResumingTour && !autoStart) {
5635
- return {
5636
- cleanup: () => {
5637
- }
5638
- };
5639
- }
5640
- if (!state) {
5641
- state = {
5642
- tourId,
5643
- currentStepId: startStep || steps[0].id,
5644
- startedAt: Date.now()
5645
- };
5646
- saveTourState(state);
5647
- }
5648
- let currentStepIndex = steps.findIndex((s) => s.id === state.currentStepId);
5649
- if (currentStepIndex === -1) {
5650
- const initialStepId = startStep || steps[0].id;
5651
- currentStepIndex = steps.findIndex((s) => s.id === initialStepId);
5652
- if (currentStepIndex === -1) currentStepIndex = 0;
5653
- state.currentStepId = steps[currentStepIndex].id;
5654
- saveTourState(state);
5655
- }
5656
- const currentStep = steps[currentStepIndex];
5657
- if (!stepMatchesRoute(currentStep)) {
5658
- context.publishEvent("tour.waiting_for_route", {
5659
- tourId,
5660
- stepId: currentStep.id,
5661
- expectedRoute: currentStep.route,
5662
- currentRoute: getCurrentRoute()
5663
- });
5664
- return {
5665
- cleanup: () => {
5666
- }
5667
- };
5668
- }
5669
- let isDestroyed = false;
5670
- let currentActionHandle = null;
5671
- let eventUnsubscribe = null;
5672
- let routeWatcher = null;
5673
- const cleanupCurrentStep = async () => {
5674
- if (eventUnsubscribe) {
5675
- eventUnsubscribe();
5676
- eventUnsubscribe = null;
5677
- }
5678
- if (currentActionHandle == null ? void 0 : currentActionHandle.isApplied()) {
5679
- await currentActionHandle.revert();
5680
- currentActionHandle = null;
5681
- }
5682
- };
5683
- const advanceToStep = async (nextStepId) => {
5684
- if (isDestroyed) return;
5685
- await cleanupCurrentStep();
5686
- if (nextStepId === "end") {
5687
- clearTourState();
5688
- context.publishEvent("tour.completed", {
5689
- tourId,
5690
- totalSteps: steps.length
5691
- });
5692
- isDestroyed = true;
5693
- return;
5694
- }
5695
- const nextStep = steps.find((s) => s.id === nextStepId);
5696
- if (!nextStep) {
5697
- console.error(`[Tour] Step "${nextStepId}" not found`);
5698
- return;
5699
- }
5700
- state.currentStepId = nextStepId;
5701
- saveTourState(state);
5702
- context.publishEvent("tour.step_changed", {
5703
- tourId,
5704
- previousStepId: currentStep.id,
5705
- nextStepId
5706
- });
5707
- if (nextStep.route && nextStep.route !== getCurrentRoute()) {
5708
- context.publishEvent("tour.awaiting_navigation", {
5709
- tourId,
5710
- stepId: nextStepId,
5711
- targetRoute: nextStep.route
5712
- });
5713
- return;
5714
- }
5715
- await executeStep(nextStep);
5716
- };
5717
- const executeStep = async (step) => {
5718
- if (isDestroyed) return;
5719
- context.publishEvent("tour.step_started", {
5720
- tourId,
5721
- stepId: step.id,
5722
- stepIndex: steps.findIndex((s) => s.id === step.id),
5723
- totalSteps: steps.length
5724
- });
5725
- try {
5726
- currentActionHandle = await context.applyAction(step.action);
5727
- } catch (error2) {
5728
- console.error(`[Tour] Failed to execute step "${step.id}":`, error2);
5729
- context.publishEvent("tour.step_failed", {
5730
- tourId,
5731
- stepId: step.id,
5732
- error: String(error2)
5733
- });
5734
- return;
5735
- }
5736
- if (step.onAction && context.subscribeEvent) {
5737
- eventUnsubscribe = context.subscribeEvent("action.modal_cta_clicked", (props) => {
5738
- const actionId = props == null ? void 0 : props.actionId;
5739
- if (actionId && step.onAction) {
5740
- const nextStepId = step.onAction[actionId];
5741
- if (nextStepId) {
5742
- advanceToStep(nextStepId);
5743
- }
7162
+ };
7163
+ };
7164
+ var executeParallel = async (action, context) => {
7165
+ const handles = [];
7166
+ if (context.applyAction) {
7167
+ const promises = action.actions.map((childAction) => context.applyAction(childAction));
7168
+ if (action.waitFor === "any") {
7169
+ const firstHandle = await Promise.race(promises);
7170
+ handles.push(firstHandle);
7171
+ const remaining = await Promise.allSettled(promises);
7172
+ for (const result of remaining) {
7173
+ if (result.status === "fulfilled" && result.value !== firstHandle) {
7174
+ handles.push(result.value);
5744
7175
  }
5745
- });
5746
- const tooltipUnsubscribe = context.subscribeEvent("action.tooltip_cta_clicked", (props) => {
5747
- const actionId = props == null ? void 0 : props.actionId;
5748
- if (actionId && step.onAction) {
5749
- const nextStepId = step.onAction[actionId];
5750
- if (nextStepId) {
5751
- advanceToStep(nextStepId);
5752
- }
7176
+ }
7177
+ } else {
7178
+ const allHandles = await Promise.all(promises);
7179
+ handles.push(...allHandles);
7180
+ }
7181
+ }
7182
+ return {
7183
+ cleanup: async () => {
7184
+ for (const handle of handles) {
7185
+ try {
7186
+ await handle.revert();
7187
+ } catch {
5753
7188
  }
5754
- });
5755
- const originalUnsubscribe = eventUnsubscribe;
5756
- eventUnsubscribe = () => {
5757
- originalUnsubscribe();
5758
- tooltipUnsubscribe();
5759
- };
7189
+ }
5760
7190
  }
5761
7191
  };
5762
- const setupRouteWatcher = () => {
5763
- let lastPath = getCurrentRoute();
5764
- const checkRoute = () => {
5765
- const currentPath = getCurrentRoute();
5766
- if (currentPath !== lastPath) {
5767
- lastPath = currentPath;
5768
- context.publishEvent("tour.route_changed", {
5769
- tourId,
5770
- newRoute: currentPath
5771
- });
5772
- }
5773
- };
5774
- if (context.subscribeNavigation) {
5775
- return context.subscribeNavigation(() => checkRoute());
7192
+ };
7193
+ var executeWait = async (action, context) => {
7194
+ let unsubscribe;
7195
+ let timeoutId;
7196
+ let cancelled = false;
7197
+ const result = {
7198
+ cleanup: () => {
7199
+ cancelled = true;
7200
+ if (timeoutId) clearTimeout(timeoutId);
7201
+ if (unsubscribe) unsubscribe();
5776
7202
  }
5777
- window.addEventListener("popstate", checkRoute);
5778
- const origPushState = history.pushState.bind(history);
5779
- const origReplaceState = history.replaceState.bind(history);
5780
- history.pushState = (...args) => {
5781
- origPushState(...args);
5782
- queueMicrotask(checkRoute);
5783
- };
5784
- history.replaceState = (...args) => {
5785
- origReplaceState(...args);
5786
- queueMicrotask(checkRoute);
5787
- };
5788
- return () => {
5789
- window.removeEventListener("popstate", checkRoute);
5790
- history.pushState = origPushState;
5791
- history.replaceState = origReplaceState;
5792
- };
5793
7203
  };
5794
- routeWatcher = setupRouteWatcher();
5795
- if (!isResumingTour) {
5796
- context.publishEvent("tour.started", {
5797
- tourId,
5798
- totalSteps: steps.length,
5799
- startStepId: state.currentStepId
7204
+ if (action.event && context.subscribeEvent) {
7205
+ await new Promise((resolve) => {
7206
+ unsubscribe = context.subscribeEvent(action.event, () => {
7207
+ if (!cancelled) resolve();
7208
+ });
7209
+ if (action.durationMs !== void 0) {
7210
+ timeoutId = setTimeout(() => {
7211
+ if (!cancelled) resolve();
7212
+ }, action.durationMs);
7213
+ }
5800
7214
  });
5801
- } else {
5802
- context.publishEvent("tour.resumed", {
5803
- tourId,
5804
- stepId: state.currentStepId
7215
+ } else if (action.durationMs !== void 0) {
7216
+ await new Promise((resolve) => {
7217
+ timeoutId = setTimeout(() => {
7218
+ if (!cancelled) resolve();
7219
+ }, action.durationMs);
5805
7220
  });
5806
7221
  }
5807
- await executeStep(currentStep);
5808
- const cleanup = async () => {
5809
- isDestroyed = true;
5810
- activeTours.delete(tourId);
5811
- await cleanupCurrentStep();
5812
- if (routeWatcher) {
5813
- routeWatcher();
5814
- }
5815
- context.publishEvent("tour.paused", {
5816
- tourId,
5817
- stepId: state.currentStepId
5818
- });
5819
- };
5820
- activeTours.set(tourId, { cleanup });
5821
- return { cleanup };
7222
+ return result;
5822
7223
  };
5823
7224
 
5824
7225
  // src/actions/executors/index.ts
@@ -5842,7 +7243,6 @@ var ExecutorRegistry = class {
5842
7243
  this.registerCore("core:sequence", executeSequence);
5843
7244
  this.registerCore("core:parallel", executeParallel);
5844
7245
  this.registerCore("core:wait", executeWait);
5845
- this.registerCore("core:tour", executeTour);
5846
7246
  }
5847
7247
  /**
5848
7248
  * Register a core executor (synchronous, built-in, cannot be overridden).
@@ -6121,18 +7521,46 @@ function validateAction(action) {
6121
7521
  };
6122
7522
  }
6123
7523
  function validateAnchorAction(action, errors, warnings) {
6124
- if (!action.anchorId || typeof action.anchorId !== "string") {
7524
+ const anchorId = action.anchorId;
7525
+ if (!anchorId || typeof anchorId !== "object") {
6125
7526
  errors.push({
6126
7527
  code: "MISSING_ANCHOR_ID",
6127
- message: "Action requires an 'anchorId' property",
7528
+ message: "Action requires an 'anchorId' object with a 'selector' string",
6128
7529
  field: "anchorId"
6129
7530
  });
6130
- } else if (action.anchorId.length > 200) {
7531
+ return;
7532
+ }
7533
+ if (!anchorId.selector || typeof anchorId.selector !== "string") {
7534
+ errors.push({
7535
+ code: "MISSING_ANCHOR_SELECTOR",
7536
+ message: "anchorId requires a 'selector' string",
7537
+ field: "anchorId.selector"
7538
+ });
7539
+ } else if (anchorId.selector.length > 200) {
6131
7540
  warnings.push({
6132
7541
  code: "LONG_ANCHOR_ID",
6133
- message: "Anchor ID is unusually long",
6134
- suggestion: "Consider using a shorter, more descriptive ID"
7542
+ message: "Anchor selector is unusually long",
7543
+ suggestion: "Consider using a shorter, more descriptive selector"
7544
+ });
7545
+ }
7546
+ if (anchorId.route === void 0 || anchorId.route === null) {
7547
+ errors.push({
7548
+ code: "MISSING_ANCHOR_ROUTE",
7549
+ message: `anchorId requires a 'route' (string or array of strings). Use "**" for all routes.`,
7550
+ field: "anchorId.route"
6135
7551
  });
7552
+ } else {
7553
+ const routes = Array.isArray(anchorId.route) ? anchorId.route : [anchorId.route];
7554
+ for (const route of routes) {
7555
+ if (typeof route !== "string") {
7556
+ errors.push({
7557
+ code: "INVALID_ANCHOR_ROUTE",
7558
+ message: "anchorId.route must be a string or array of strings",
7559
+ field: "anchorId.route"
7560
+ });
7561
+ break;
7562
+ }
7563
+ }
6136
7564
  }
6137
7565
  }
6138
7566
  function validateBadgeAction(action, errors, warnings) {
@@ -6581,11 +8009,14 @@ function createActionEngine(options) {
6581
8009
  eventBus,
6582
8010
  surfaces,
6583
8011
  anchorResolver,
8012
+ waitForAnchor,
6584
8013
  adaptiveId,
6585
8014
  executorRegistry: executorRegistry2 = executorRegistry,
6586
- subscribeNavigation
8015
+ subscribeNavigation,
8016
+ runtime: runtime3
6587
8017
  } = options;
6588
8018
  const activeActions = /* @__PURE__ */ new Map();
8019
+ const conditionalUnsubs = /* @__PURE__ */ new Map();
6589
8020
  function generateId() {
6590
8021
  return `action-${++actionIdCounter}`;
6591
8022
  }
@@ -6598,6 +8029,7 @@ function createActionEngine(options) {
6598
8029
  return {
6599
8030
  overlayRoot,
6600
8031
  resolveAnchor: anchorResolver,
8032
+ waitForAnchor,
6601
8033
  generateId,
6602
8034
  publishEvent,
6603
8035
  adaptiveId,
@@ -6648,6 +8080,67 @@ function createActionEngine(options) {
6648
8080
  }
6649
8081
  };
6650
8082
  }
8083
+ async function executeAction(action, context) {
8084
+ if (action.kind === "core:mountWidget") {
8085
+ return executeMountWidget(action, context);
8086
+ }
8087
+ const executor = executorRegistry2.get(action.kind);
8088
+ if (!executor) {
8089
+ const registered = "list" in executorRegistry2 ? executorRegistry2.list() : "(list unavailable)";
8090
+ console.error(
8091
+ `[ActionEngine] No executor for kind="${action.kind}".`,
8092
+ `Registered: [${registered}]`
8093
+ );
8094
+ throw new Error(`No executor for action kind: ${action.kind}`);
8095
+ }
8096
+ return executor(action, context);
8097
+ }
8098
+ function subscribeForReeval(id, action, triggerWhen, handle) {
8099
+ if (!runtime3) return;
8100
+ const unsubs = [];
8101
+ const onReeval = async () => {
8102
+ const entry = activeActions.get(id);
8103
+ if (!entry) return;
8104
+ const result = runtime3.evaluateSync(triggerWhen);
8105
+ const shouldApply = result.value;
8106
+ if (entry.state === "applied" && !shouldApply) {
8107
+ try {
8108
+ await entry.cleanup();
8109
+ } catch (error2) {
8110
+ console.error(`[ActionEngine] Error reverting conditional action ${id}:`, error2);
8111
+ }
8112
+ entry.state = "deferred";
8113
+ entry.cleanup = () => {
8114
+ };
8115
+ entry.updateFn = void 0;
8116
+ publishEvent("action.deferred", { id, kind: action.kind });
8117
+ } else if (entry.state === "deferred" && shouldApply) {
8118
+ try {
8119
+ const context = createExecutorContext();
8120
+ const execResult = await executeAction(action, context);
8121
+ entry.state = "applied";
8122
+ entry.cleanup = execResult.cleanup;
8123
+ entry.updateFn = execResult.updateFn;
8124
+ entry.appliedTs = Date.now();
8125
+ publishEvent("action.applied", {
8126
+ id,
8127
+ kind: action.kind,
8128
+ anchorId: "anchorId" in action ? action.anchorId : void 0
8129
+ });
8130
+ } catch (error2) {
8131
+ console.error(`[ActionEngine] Error applying deferred action ${id}:`, error2);
8132
+ entry.state = "failed";
8133
+ }
8134
+ }
8135
+ };
8136
+ if (runtime3.context) {
8137
+ unsubs.push(runtime3.context.subscribe(onReeval));
8138
+ }
8139
+ if (runtime3.accumulator) {
8140
+ unsubs.push(runtime3.accumulator.subscribe(onReeval));
8141
+ }
8142
+ conditionalUnsubs.set(id, unsubs);
8143
+ }
6651
8144
  async function apply(action) {
6652
8145
  const validation = validateAction(action);
6653
8146
  if (!validation.valid) {
@@ -6660,22 +8153,61 @@ function createActionEngine(options) {
6660
8153
  throw new Error(`Action validation failed: ${errorMessages}`);
6661
8154
  }
6662
8155
  const id = generateId();
6663
- const context = createExecutorContext();
6664
- let result;
6665
- if (action.kind === "core:mountWidget") {
6666
- result = await executeMountWidget(action, context);
6667
- } else {
6668
- const executor = executorRegistry2.get(action.kind);
6669
- if (!executor) {
6670
- const registered = "list" in executorRegistry2 ? executorRegistry2.list() : "(list unavailable)";
6671
- console.error(
6672
- `[ActionEngine] No executor for kind="${action.kind}".`,
6673
- `Registered: [${registered}]`
6674
- );
6675
- throw new Error(`No executor for action kind: ${action.kind}`);
8156
+ const triggerWhen = action.triggerWhen;
8157
+ if (triggerWhen && runtime3) {
8158
+ const evalResult = runtime3.evaluateSync(triggerWhen);
8159
+ if (!evalResult.value) {
8160
+ const entry2 = {
8161
+ id,
8162
+ action,
8163
+ adaptiveId,
8164
+ appliedTs: Date.now(),
8165
+ state: "deferred",
8166
+ cleanup: () => {
8167
+ }
8168
+ };
8169
+ activeActions.set(id, entry2);
8170
+ const handle2 = {
8171
+ id,
8172
+ action,
8173
+ async revert() {
8174
+ const entry3 = activeActions.get(id);
8175
+ if (!entry3) return;
8176
+ const unsubs = conditionalUnsubs.get(id);
8177
+ if (unsubs) {
8178
+ for (const unsub of unsubs) unsub();
8179
+ conditionalUnsubs.delete(id);
8180
+ }
8181
+ entry3.state = "reverted";
8182
+ activeActions.delete(id);
8183
+ },
8184
+ isApplied() {
8185
+ const entry3 = activeActions.get(id);
8186
+ return (entry3 == null ? void 0 : entry3.state) === "applied";
8187
+ },
8188
+ async update(changes) {
8189
+ const entry3 = activeActions.get(id);
8190
+ if (!entry3 || entry3.state !== "applied") {
8191
+ throw new Error("Cannot update action that is not applied");
8192
+ }
8193
+ if (entry3.updateFn) {
8194
+ await entry3.updateFn(changes);
8195
+ } else {
8196
+ throw new Error(`Action kind ${action.kind} does not support updates`);
8197
+ }
8198
+ },
8199
+ getState() {
8200
+ var _a2;
8201
+ const entry3 = activeActions.get(id);
8202
+ return (_a2 = entry3 == null ? void 0 : entry3.state) != null ? _a2 : "reverted";
8203
+ }
8204
+ };
8205
+ subscribeForReeval(id, action, triggerWhen, handle2);
8206
+ return handle2;
6676
8207
  }
6677
- result = await executor(action, context);
6678
8208
  }
8209
+ const context = createExecutorContext();
8210
+ const result = await executeAction(action, context);
6679
8211
  const entry = {
6680
8212
  id,
6681
8213
  action,
@@ -6697,6 +8229,11 @@ function createActionEngine(options) {
6697
8229
  async revert() {
6698
8230
  const entry2 = activeActions.get(id);
6699
8231
  if (!entry2 || entry2.state !== "applied") return;
8232
+ const unsubs = conditionalUnsubs.get(id);
8233
+ if (unsubs) {
8234
+ for (const unsub of unsubs) unsub();
8235
+ conditionalUnsubs.delete(id);
8236
+ }
6700
8237
  try {
6701
8238
  await entry2.cleanup();
6702
8239
  entry2.state = "reverted";
@@ -6734,6 +8271,9 @@ function createActionEngine(options) {
6734
8271
  return (_a2 = entry2 == null ? void 0 : entry2.state) != null ? _a2 : "reverted";
6735
8272
  }
6736
8273
  };
8274
+ if (triggerWhen && runtime3) {
8275
+ subscribeForReeval(id, action, triggerWhen, handle);
8276
+ }
6737
8277
  return handle;
6738
8278
  }
6739
8279
  async function applyBatch(actions) {
@@ -6749,7 +8289,7 @@ function createActionEngine(options) {
6749
8289
  errorMessages,
6750
8290
  "\nActions:",
6751
8291
  actions.map(
6752
- (a, i) => ` [${i}] ${a.kind} ${a.anchorId ? `anchor="${a.anchorId}"` : ""} ${a.label ? `label="${a.label}"` : ""}`
8292
+ (a, i) => ` [${i}] ${a.kind} ${a.anchorId ? `anchor="${typeof a.anchorId === "string" ? a.anchorId : a.anchorId.selector}"` : ""} ${a.label ? `label="${a.label}"` : ""}`
6753
8293
  ).join("\n")
6754
8294
  );
6755
8295
  throw new Error(`Batch validation failed: ${errorMessages}`);
@@ -6808,6 +8348,15 @@ function createActionEngine(options) {
6808
8348
  }));
6809
8349
  }
6810
8350
  function destroy() {
8351
+ for (const unsubs of conditionalUnsubs.values()) {
8352
+ for (const unsub of unsubs) {
8353
+ try {
8354
+ unsub();
8355
+ } catch {
8356
+ }
8357
+ }
8358
+ }
8359
+ conditionalUnsubs.clear();
6811
8360
  for (const entry of activeActions.values()) {
6812
8361
  if (entry.state === "applied") {
6813
8362
  try {
@@ -6828,6 +8377,154 @@ function createActionEngine(options) {
6828
8377
  };
6829
8378
  }
6830
8379
 
8380
+ // src/anchor/AnchorResolver.ts
8381
+ function createAnchorResolver(opts) {
8382
+ var _a2, _b;
8383
+ const root = (_a2 = opts == null ? void 0 : opts.root) != null ? _a2 : typeof document !== "undefined" ? document.body : null;
8384
+ const defaultTimeoutMs = (_b = opts == null ? void 0 : opts.defaultTimeoutMs) != null ? _b : 5e3;
8385
+ const waiters = /* @__PURE__ */ new Map();
8386
+ const appearWatchers = /* @__PURE__ */ new Map();
8387
+ let observer = null;
8388
+ let destroyed = false;
8389
+ function resolve(selector) {
8390
+ if (!root) return null;
8391
+ try {
8392
+ return root.querySelector(selector);
8393
+ } catch {
8394
+ return null;
8395
+ }
8396
+ }
8397
+ function checkPending() {
8398
+ for (const [selector, set] of waiters) {
8399
+ const el = resolve(selector);
8400
+ if (el) {
8401
+ for (const w of set) {
8402
+ clearTimeout(w.timer);
8403
+ w.resolve(el);
8404
+ }
8405
+ waiters.delete(selector);
8406
+ }
8407
+ }
8408
+ for (const [selector, set] of appearWatchers) {
8409
+ const el = resolve(selector);
8410
+ if (el) {
8411
+ for (const watcher of set) {
8412
+ if (!watcher.cancelled) {
8413
+ watcher.cb(el);
8414
+ }
8415
+ }
8416
+ appearWatchers.delete(selector);
8417
+ }
8418
+ }
8419
+ if (waiters.size === 0 && appearWatchers.size === 0) {
8420
+ disconnectObserver();
8421
+ }
8422
+ }
8423
+ function ensureObserver() {
8424
+ if (observer || destroyed) return;
8425
+ if (typeof MutationObserver === "undefined" || !root) return;
8426
+ observer = new MutationObserver(() => {
8427
+ checkPending();
8428
+ });
8429
+ observer.observe(root, { childList: true, subtree: true });
8430
+ }
8431
+ function disconnectObserver() {
8432
+ if (observer) {
8433
+ observer.disconnect();
8434
+ observer = null;
8435
+ }
8436
+ }
8437
+ function removeWaiter(selector, waiter) {
8438
+ const set = waiters.get(selector);
8439
+ if (set) {
8440
+ set.delete(waiter);
8441
+ if (set.size === 0) {
8442
+ waiters.delete(selector);
8443
+ }
8444
+ }
8445
+ if (waiters.size === 0 && appearWatchers.size === 0) {
8446
+ disconnectObserver();
8447
+ }
8448
+ }
8449
+ function waitFor(selector, timeoutMs) {
8450
+ if (destroyed) {
8451
+ return Promise.reject(new Error("AnchorResolver destroyed"));
8452
+ }
8453
+ const existing = resolve(selector);
8454
+ if (existing) return Promise.resolve(existing);
8455
+ const timeout = timeoutMs != null ? timeoutMs : defaultTimeoutMs;
8456
+ return new Promise((res, rej) => {
8457
+ const waiter = {
8458
+ resolve: res,
8459
+ reject: rej,
8460
+ timer: setTimeout(() => {
8461
+ removeWaiter(selector, waiter);
8462
+ rej(new Error(`waitFor("${selector}") timed out after ${timeout}ms`));
8463
+ }, timeout)
8464
+ };
8465
+ let set = waiters.get(selector);
8466
+ if (!set) {
8467
+ set = /* @__PURE__ */ new Set();
8468
+ waiters.set(selector, set);
8469
+ }
8470
+ set.add(waiter);
8471
+ ensureObserver();
8472
+ });
8473
+ }
8474
+ function onAppear(selector, cb) {
8475
+ if (destroyed) return () => {
8476
+ };
8477
+ const existing = resolve(selector);
8478
+ if (existing) {
8479
+ cb(existing);
8480
+ return () => {
8481
+ };
8482
+ }
8483
+ const watcher = { cb, cancelled: false };
8484
+ let set = appearWatchers.get(selector);
8485
+ if (!set) {
8486
+ set = /* @__PURE__ */ new Set();
8487
+ appearWatchers.set(selector, set);
8488
+ }
8489
+ set.add(watcher);
8490
+ ensureObserver();
8491
+ return () => {
8492
+ watcher.cancelled = true;
8493
+ const s = appearWatchers.get(selector);
8494
+ if (s) {
8495
+ s.delete(watcher);
8496
+ if (s.size === 0) {
8497
+ appearWatchers.delete(selector);
8498
+ }
8499
+ }
8500
+ if (waiters.size === 0 && appearWatchers.size === 0) {
8501
+ disconnectObserver();
8502
+ }
8503
+ };
8504
+ }
8505
+ function pendingCount() {
8506
+ let count = 0;
8507
+ for (const set of waiters.values()) {
8508
+ count += set.size;
8509
+ }
8510
+ return count;
8511
+ }
8512
+ function destroy() {
8513
+ destroyed = true;
8514
+ disconnectObserver();
8515
+ const err = new Error("AnchorResolver destroyed");
8516
+ for (const set of waiters.values()) {
8517
+ for (const w of set) {
8518
+ clearTimeout(w.timer);
8519
+ w.reject(err);
8520
+ }
8521
+ }
8522
+ waiters.clear();
8523
+ appearWatchers.clear();
8524
+ }
8525
+ return { resolve, waitFor, onAppear, pendingCount, destroy };
8526
+ }
8527
+
6831
8528
  // src/context/ContextManager.ts
6832
8529
  function createDefaultContext() {
6833
8530
  const now = Date.now();
@@ -7059,13 +8756,13 @@ function createContextManager(options = {}) {
7059
8756
 
7060
8757
  // src/decisions/strategies/rules.ts
7061
8758
  function evaluateCondition(condition, evalContext) {
7062
- var _a2, _b, _c, _d, _e;
8759
+ var _a2, _b, _c, _d, _e, _f, _g;
7063
8760
  const { context, state, events } = evalContext;
7064
8761
  switch (condition.type) {
7065
8762
  case "page_url": {
7066
8763
  const { url } = condition;
7067
- const currentUrl = context.page.url;
7068
- const pattern = url.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
8764
+ const currentUrl = context.page.url.split("?")[0].split("#")[0];
8765
+ const pattern = url.replace(/\*\*/g, "\0GLOBSTAR\0").replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\0GLOBSTAR\0/g, ".*");
7069
8766
  const regex = new RegExp(`^${pattern}$`);
7070
8767
  return regex.test(currentUrl);
7071
8768
  }
@@ -7073,7 +8770,8 @@ function evaluateCondition(condition, evalContext) {
7073
8770
  return context.page.routeId === condition.routeId;
7074
8771
  }
7075
8772
  case "anchor_visible": {
7076
- const anchor = (_a2 = context.anchors) == null ? void 0 : _a2.find((a) => a.anchorId === condition.anchorId);
8773
+ const condSelector = typeof condition.anchorId === "string" ? condition.anchorId : (_b = (_a2 = condition.anchorId) == null ? void 0 : _a2.selector) != null ? _b : "";
8774
+ const anchor = (_c = context.anchors) == null ? void 0 : _c.find((a) => a.anchorId === condSelector);
7077
8775
  switch (condition.state) {
7078
8776
  case "visible":
7079
8777
  return (anchor == null ? void 0 : anchor.visible) === true;
@@ -7087,7 +8785,7 @@ function evaluateCondition(condition, evalContext) {
7087
8785
  }
7088
8786
  case "event_occurred": {
7089
8787
  if (!events) return false;
7090
- const withinMs = (_b = condition.withinMs) != null ? _b : 6e4;
8788
+ const withinMs = (_d = condition.withinMs) != null ? _d : 6e4;
7091
8789
  return events.hasRecentEvent(condition.eventName, withinMs);
7092
8790
  }
7093
8791
  case "state_equals": {
@@ -7122,17 +8820,17 @@ function evaluateCondition(condition, evalContext) {
7122
8820
  }
7123
8821
  }
7124
8822
  case "dismissed": {
7125
- if (!state) return (_c = condition.inverted) != null ? _c : false;
8823
+ if (!state) return (_e = condition.inverted) != null ? _e : false;
7126
8824
  const isDismissed = state.isDismissed(condition.key);
7127
8825
  return condition.inverted ? !isDismissed : isDismissed;
7128
8826
  }
7129
8827
  case "cooldown_active": {
7130
- if (!state) return (_d = condition.inverted) != null ? _d : false;
8828
+ if (!state) return (_f = condition.inverted) != null ? _f : false;
7131
8829
  const isActive = state.isCooldownActive(condition.key);
7132
8830
  return condition.inverted ? !isActive : isActive;
7133
8831
  }
7134
8832
  case "frequency_limit": {
7135
- if (!state) return (_e = condition.inverted) != null ? _e : false;
8833
+ if (!state) return (_g = condition.inverted) != null ? _g : false;
7136
8834
  const count = state.getFrequencyCount(condition.key);
7137
8835
  const limitReached = count >= condition.limit;
7138
8836
  return condition.inverted ? !limitReached : limitReached;
@@ -8323,7 +10021,7 @@ function createSurfaces(options) {
8323
10021
  if (!anchorResolver) {
8324
10022
  throw new Error("Anchor resolver required for inline slots");
8325
10023
  }
8326
- const anchorEl = anchorResolver(anchorId);
10024
+ const anchorEl = anchorResolver({ selector: anchorId, route: "**" });
8327
10025
  if (!anchorEl) {
8328
10026
  throw new Error(`Anchor not found for inline slot: ${anchorId}`);
8329
10027
  }
@@ -8336,7 +10034,7 @@ function createSurfaces(options) {
8336
10034
  if (!anchorResolver) {
8337
10035
  throw new Error("Anchor resolver required for adjacent slots");
8338
10036
  }
8339
- const anchorEl = anchorResolver(anchorId);
10037
+ const anchorEl = anchorResolver({ selector: anchorId, route: "**" });
8340
10038
  if (!anchorEl) {
8341
10039
  throw new Error(`Anchor not found for adjacent slot: ${anchorId}`);
8342
10040
  }
@@ -8739,16 +10437,15 @@ function matchesRouteFilter(url, filter) {
8739
10437
  function matchRoutePattern(pathname, pattern) {
8740
10438
  const normalizedPattern = pattern.replace(/\/$/, "") || "/";
8741
10439
  if (pathname === normalizedPattern) return true;
8742
- const regexPattern = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/:[^/]+/g, "[^/]+");
10440
+ const regexPattern = normalizedPattern.replace(/\*\*/g, "\0GLOBSTAR\0").replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\0GLOBSTAR\0/g, ".*").replace(/:[^/]+/g, "[^/]+");
8743
10441
  const regex = new RegExp(`^${regexPattern}$`);
8744
10442
  return regex.test(pathname);
8745
10443
  }
8746
- function resolveAnchorById(anchorId) {
8747
- try {
8748
- return document.querySelector(anchorId);
8749
- } catch {
8750
- return null;
8751
- }
10444
+ function matchesAnchorRoute(anchorId) {
10445
+ const pathname = typeof window !== "undefined" ? window.location.pathname : "/";
10446
+ const normalizedPath = pathname.replace(/\/$/, "") || "/";
10447
+ const routes = Array.isArray(anchorId.route) ? anchorId.route : [anchorId.route];
10448
+ return routes.some((pattern) => matchRoutePattern(normalizedPath, pattern));
8752
10449
  }
8753
10450
  function createSmartCanvasRuntime(options = {}) {
8754
10451
  var _a2, _b, _c, _d;
@@ -8777,7 +10474,19 @@ function createSmartCanvasRuntime(options = {}) {
8777
10474
  accumulator
8778
10475
  });
8779
10476
  const overlayRoot = ensureOverlayRoot();
8780
- const anchorResolver = (anchorId) => resolveAnchorById(anchorId);
10477
+ const anchorResolverService = createAnchorResolver();
10478
+ const anchorResolver = (anchorId) => {
10479
+ if (!matchesAnchorRoute(anchorId)) return null;
10480
+ return anchorResolverService.resolve(anchorId.selector);
10481
+ };
10482
+ const waitForAnchor = async (anchorId, timeoutMs) => {
10483
+ if (!matchesAnchorRoute(anchorId)) return null;
10484
+ try {
10485
+ return await anchorResolverService.waitFor(anchorId.selector, timeoutMs);
10486
+ } catch {
10487
+ return null;
10488
+ }
10489
+ };
8781
10490
  const surfaces = createSurfaces({
8782
10491
  overlayRoot,
8783
10492
  eventBus: events,
@@ -8789,8 +10498,14 @@ function createSmartCanvasRuntime(options = {}) {
8789
10498
  eventBus: events,
8790
10499
  surfaces,
8791
10500
  anchorResolver,
10501
+ waitForAnchor,
8792
10502
  executorRegistry: executors3,
8793
- subscribeNavigation: (callback) => navigation.subscribe(callback)
10503
+ subscribeNavigation: (callback) => navigation.subscribe(callback),
10504
+ runtime: {
10505
+ evaluateSync: (strategy) => decisionEngine.evaluateSync(strategy, context.get()),
10506
+ context,
10507
+ accumulator
10508
+ }
8794
10509
  });
8795
10510
  const runtime3 = {
8796
10511
  telemetry,
@@ -8804,6 +10519,7 @@ function createSmartCanvasRuntime(options = {}) {
8804
10519
  executors: executors3,
8805
10520
  apps,
8806
10521
  accumulator,
10522
+ anchorResolver: anchorResolverService,
8807
10523
  navigation,
8808
10524
  version: RUNTIME_VERSION,
8809
10525
  mode,
@@ -8814,6 +10530,7 @@ function createSmartCanvasRuntime(options = {}) {
8814
10530
  return decisionEngine.evaluateSync(strategy, context.get());
8815
10531
  },
8816
10532
  async filterTiles(tiles) {
10533
+ var _a3, _b2;
8817
10534
  const currentUrl = context.get().page.url;
8818
10535
  const results = [];
8819
10536
  for (const tile of tiles) {
@@ -8825,14 +10542,20 @@ function createSmartCanvasRuntime(options = {}) {
8825
10542
  if (!matchesRouteFilter(currentUrl, activation.routes)) {
8826
10543
  continue;
8827
10544
  }
8828
- if (!activation.strategy) {
8829
- results.push(tile);
8830
- continue;
8831
- }
8832
- const result = await this.evaluate(activation.strategy);
8833
- if (result.value) {
8834
- results.push(tile);
10545
+ if (activation.onlyIfPopulated) {
10546
+ const actions2 = (_b2 = (_a3 = tile.props) == null ? void 0 : _a3.actions) != null ? _b2 : [];
10547
+ if (actions2.length > 0) {
10548
+ const hasVisible = actions2.some((a) => {
10549
+ if (!a.triggerWhen) return true;
10550
+ return decisionEngine.evaluateSync(
10551
+ a.triggerWhen,
10552
+ context.get()
10553
+ ).value;
10554
+ });
10555
+ if (!hasVisible) continue;
10556
+ }
8835
10557
  }
10558
+ results.push(tile);
8836
10559
  }
8837
10560
  return results;
8838
10561
  },
@@ -8843,6 +10566,7 @@ function createSmartCanvasRuntime(options = {}) {
8843
10566
  apps.unbind().catch((err) => {
8844
10567
  console.error("[Runtime] Error unbinding apps registry:", err);
8845
10568
  });
10569
+ anchorResolverService.destroy();
8846
10570
  accumulator.destroy();
8847
10571
  navigation.destroy();
8848
10572
  context.destroy();
@@ -9433,7 +11157,7 @@ async function init(options) {
9433
11157
  console.log(
9434
11158
  "[Syntro Bootstrap] Actions in config:",
9435
11159
  config.actions.map(
9436
- (a, i) => `[${i}] ${a.kind}${a.anchorId ? ` anchor="${a.anchorId}"` : ""}${a.label ? ` "${a.label}"` : ""}`
11160
+ (a, i) => `[${i}] ${a.kind}${a.anchorId ? ` anchor="${typeof a.anchorId === "string" ? a.anchorId : a.anchorId.selector}"` : ""}${a.label ? ` "${a.label}"` : ""}`
9437
11161
  ).join(", ")
9438
11162
  );
9439
11163
  }
@@ -9504,11 +11228,9 @@ if (typeof window !== "undefined") {
9504
11228
  export {
9505
11229
  runtime,
9506
11230
  base,
9507
- brand,
9508
11231
  slateGrey,
9509
11232
  red,
9510
11233
  purple,
9511
- border,
9512
11234
  runtime2,
9513
11235
  createAppContext,
9514
11236
  cleanupAppContext,
@@ -9567,6 +11289,7 @@ export {
9567
11289
  validateAction,
9568
11290
  validateActions,
9569
11291
  createActionEngine,
11292
+ createAnchorResolver,
9570
11293
  ContextManager,
9571
11294
  createContextManager,
9572
11295
  evaluateCondition,
@@ -9605,4 +11328,4 @@ export {
9605
11328
  encodeToken,
9606
11329
  Syntro
9607
11330
  };
9608
- //# sourceMappingURL=chunk-QGWATS3Z.js.map
11331
+ //# sourceMappingURL=chunk-Q4WGXNKC.js.map