@bitovi/vybit 0.12.0 → 0.13.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.
@@ -44,11 +44,6 @@
44
44
  function sendTo(role, data) {
45
45
  send({ ...data, to: role });
46
46
  }
47
- window.addEventListener("message", (event) => {
48
- if (event.data?.type === "STORYBOOK_STORY_RENDERED") {
49
- send({ type: "RESET_SELECTION" });
50
- }
51
- });
52
47
 
53
48
  // overlay/src/context.ts
54
49
  function buildContext(target, oldClass, newClass, originalClassMap) {
@@ -686,6 +681,73 @@ ${pad}</${tag}>`;
686
681
  }
687
682
  .msg-send svg { width: 12px; height: 12px; }
688
683
 
684
+ /* \u2500\u2500 Mic button (voice messages) \u2500\u2500 */
685
+ .mic-btn {
686
+ width: 24px;
687
+ height: 24px;
688
+ border-radius: 5px;
689
+ border: none;
690
+ background: transparent;
691
+ color: #888;
692
+ display: flex;
693
+ align-items: center;
694
+ justify-content: center;
695
+ cursor: pointer;
696
+ flex-shrink: 0;
697
+ transition: all 120ms ease-out;
698
+ padding: 0;
699
+ }
700
+ .mic-btn:hover { color: #e5e5e5; background: rgba(255,255,255,0.06); }
701
+ .mic-btn svg { width: 13px; height: 13px; }
702
+ .mic-btn.listening {
703
+ background: #F5532D;
704
+ color: white;
705
+ animation: mic-pulse 1.5s ease-in-out infinite;
706
+ }
707
+ .mic-btn.error { color: #F5532D; }
708
+ @keyframes mic-pulse {
709
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(245, 83, 45, 0.4); }
710
+ 50% { box-shadow: 0 0 0 4px rgba(245, 83, 45, 0); }
711
+ }
712
+ /* \u2500\u2500 Mic-blocked banner \u2500\u2500 */
713
+ .mic-banner {
714
+ position: fixed;
715
+ top: 0;
716
+ left: 0;
717
+ right: 0;
718
+ z-index: 9999999;
719
+ display: flex;
720
+ align-items: center;
721
+ justify-content: center;
722
+ gap: 12px;
723
+ padding: 10px 16px;
724
+ background: #F5532D;
725
+ color: white;
726
+ font-family: 'Inter', system-ui, sans-serif;
727
+ font-size: 13px;
728
+ font-weight: 500;
729
+ box-shadow: 0 2px 12px rgba(0,0,0,0.3);
730
+ opacity: 0;
731
+ transform: translateY(-100%);
732
+ transition: opacity 0.25s ease-out, transform 0.25s ease-out;
733
+ }
734
+ .mic-banner.visible {
735
+ opacity: 1;
736
+ transform: translateY(0);
737
+ }
738
+ .mic-banner-dismiss {
739
+ background: none;
740
+ border: none;
741
+ color: white;
742
+ font-size: 18px;
743
+ cursor: pointer;
744
+ padding: 0 4px;
745
+ line-height: 1;
746
+ opacity: 0.8;
747
+ flex-shrink: 0;
748
+ }
749
+ .mic-banner-dismiss:hover { opacity: 1; }
750
+
689
751
  /* \u2500\u2500 Text editing action bar \u2500\u2500 */
690
752
  .text-action-bar {
691
753
  position: fixed;
@@ -1524,6 +1586,9 @@ ${pad}</${tag}>`;
1524
1586
  bottom: "top",
1525
1587
  top: "bottom"
1526
1588
  };
1589
+ function clamp(start, value, end) {
1590
+ return max(start, min(value, end));
1591
+ }
1527
1592
  function evaluate(value, param) {
1528
1593
  return typeof value === "function" ? value(param) : value;
1529
1594
  }
@@ -2011,6 +2076,79 @@ ${pad}</${tag}>`;
2011
2076
  }
2012
2077
  };
2013
2078
  };
2079
+ var shift = function(options) {
2080
+ if (options === void 0) {
2081
+ options = {};
2082
+ }
2083
+ return {
2084
+ name: "shift",
2085
+ options,
2086
+ async fn(state2) {
2087
+ const {
2088
+ x,
2089
+ y,
2090
+ placement,
2091
+ platform: platform2
2092
+ } = state2;
2093
+ const {
2094
+ mainAxis: checkMainAxis = true,
2095
+ crossAxis: checkCrossAxis = false,
2096
+ limiter = {
2097
+ fn: (_ref) => {
2098
+ let {
2099
+ x: x2,
2100
+ y: y2
2101
+ } = _ref;
2102
+ return {
2103
+ x: x2,
2104
+ y: y2
2105
+ };
2106
+ }
2107
+ },
2108
+ ...detectOverflowOptions
2109
+ } = evaluate(options, state2);
2110
+ const coords = {
2111
+ x,
2112
+ y
2113
+ };
2114
+ const overflow = await platform2.detectOverflow(state2, detectOverflowOptions);
2115
+ const crossAxis = getSideAxis(getSide(placement));
2116
+ const mainAxis = getOppositeAxis(crossAxis);
2117
+ let mainAxisCoord = coords[mainAxis];
2118
+ let crossAxisCoord = coords[crossAxis];
2119
+ if (checkMainAxis) {
2120
+ const minSide = mainAxis === "y" ? "top" : "left";
2121
+ const maxSide = mainAxis === "y" ? "bottom" : "right";
2122
+ const min2 = mainAxisCoord + overflow[minSide];
2123
+ const max2 = mainAxisCoord - overflow[maxSide];
2124
+ mainAxisCoord = clamp(min2, mainAxisCoord, max2);
2125
+ }
2126
+ if (checkCrossAxis) {
2127
+ const minSide = crossAxis === "y" ? "top" : "left";
2128
+ const maxSide = crossAxis === "y" ? "bottom" : "right";
2129
+ const min2 = crossAxisCoord + overflow[minSide];
2130
+ const max2 = crossAxisCoord - overflow[maxSide];
2131
+ crossAxisCoord = clamp(min2, crossAxisCoord, max2);
2132
+ }
2133
+ const limitedCoords = limiter.fn({
2134
+ ...state2,
2135
+ [mainAxis]: mainAxisCoord,
2136
+ [crossAxis]: crossAxisCoord
2137
+ });
2138
+ return {
2139
+ ...limitedCoords,
2140
+ data: {
2141
+ x: limitedCoords.x - x,
2142
+ y: limitedCoords.y - y,
2143
+ enabled: {
2144
+ [mainAxis]: checkMainAxis,
2145
+ [crossAxis]: checkCrossAxis
2146
+ }
2147
+ }
2148
+ };
2149
+ }
2150
+ };
2151
+ };
2014
2152
 
2015
2153
  // node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.mjs
2016
2154
  function hasWindow() {
@@ -2761,6 +2899,7 @@ ${pad}</${tag}>`;
2761
2899
  };
2762
2900
  }
2763
2901
  var offset2 = offset;
2902
+ var shift2 = shift;
2764
2903
  var flip2 = flip;
2765
2904
  var computePosition2 = (reference, floating, options) => {
2766
2905
  const cache2 = /* @__PURE__ */ new Map();
@@ -3239,6 +3378,117 @@ ${pad}</${tag}>`;
3239
3378
  }
3240
3379
  };
3241
3380
 
3381
+ // overlay/src/angular-detect.ts
3382
+ function getNgApi() {
3383
+ const ng = window.ng;
3384
+ if (ng?.getComponent && ng?.getOwningComponent) return ng;
3385
+ return null;
3386
+ }
3387
+ function isAngularElement(el) {
3388
+ if ("__ngContext__" in el) return true;
3389
+ return Array.from(el.attributes).some(
3390
+ (a) => a.name.startsWith("_nghost-") || a.name.startsWith("_ngcontent-")
3391
+ );
3392
+ }
3393
+ function findAngularComponentBoundary(el) {
3394
+ const ng = getNgApi();
3395
+ if (ng) {
3396
+ const component = ng.getComponent(el) ?? ng.getOwningComponent(el);
3397
+ if (component) {
3398
+ const raw = component.constructor?.name || "Unknown";
3399
+ const name = raw.replace(/^_+/, "");
3400
+ return {
3401
+ componentType: component.constructor,
3402
+ componentName: name,
3403
+ componentFiber: component
3404
+ // reuse field for the component instance
3405
+ };
3406
+ }
3407
+ }
3408
+ const hostAttr = findAttrStartingWith(el, "_nghost-");
3409
+ if (hostAttr) {
3410
+ return {
3411
+ componentType: el.tagName.toLowerCase(),
3412
+ componentName: formatTagAsComponentName(el.tagName),
3413
+ componentFiber: el
3414
+ };
3415
+ }
3416
+ const contentAttr = findAttrStartingWith(el, "_ngcontent-");
3417
+ if (contentAttr) {
3418
+ const suffix = contentAttr.name.replace("_ngcontent-", "");
3419
+ const host = el.closest(`[_nghost-${suffix}]`);
3420
+ if (host) {
3421
+ return {
3422
+ componentType: host.tagName.toLowerCase(),
3423
+ componentName: formatTagAsComponentName(host.tagName),
3424
+ componentFiber: host
3425
+ };
3426
+ }
3427
+ }
3428
+ return null;
3429
+ }
3430
+ function findAllAngularInstances(tagOrType) {
3431
+ const ng = getNgApi();
3432
+ if (ng && typeof tagOrType === "function") {
3433
+ const sampleEl = document.querySelector(tagOrType.\u0275cmp?.selectors?.[0]?.[0] ?? "");
3434
+ if (!sampleEl) {
3435
+ return findAllAngularInstancesByClass(ng, tagOrType);
3436
+ }
3437
+ return Array.from(document.querySelectorAll(sampleEl.tagName.toLowerCase()));
3438
+ }
3439
+ if (typeof tagOrType === "string") {
3440
+ return Array.from(document.querySelectorAll(tagOrType));
3441
+ }
3442
+ return [];
3443
+ }
3444
+ function findAllAngularInstancesByClass(ng, componentClass) {
3445
+ const results = [];
3446
+ const allCustomElements = document.querySelectorAll("*");
3447
+ for (const el of allCustomElements) {
3448
+ const comp = ng.getComponent(el);
3449
+ if (comp instanceof componentClass) {
3450
+ results.push(el);
3451
+ }
3452
+ }
3453
+ return results;
3454
+ }
3455
+ function collectAngularComponentDOMNodes(hostEl, tagName) {
3456
+ const hostAttr = findAttrStartingWith(hostEl, "_nghost-");
3457
+ if (!hostAttr) {
3458
+ return Array.from(hostEl.querySelectorAll(tagName.toLowerCase()));
3459
+ }
3460
+ const suffix = hostAttr.name.replace("_nghost-", "");
3461
+ return Array.from(
3462
+ document.querySelectorAll(
3463
+ `${tagName.toLowerCase()}[_ngcontent-${suffix}]`
3464
+ )
3465
+ );
3466
+ }
3467
+ function findAttrStartingWith(el, prefix) {
3468
+ return Array.from(el.attributes).find((a) => a.name.startsWith(prefix)) ?? null;
3469
+ }
3470
+ function formatTagAsComponentName(tagName) {
3471
+ const lower = tagName.toLowerCase();
3472
+ const stripped = lower.replace(/^app-/, "");
3473
+ const pascal = stripped.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
3474
+ return pascal + "Component";
3475
+ }
3476
+
3477
+ // overlay/src/framework-detect.ts
3478
+ var cachedFramework = null;
3479
+ function detectComponent(el) {
3480
+ const fiber = getFiber(el);
3481
+ if (fiber) {
3482
+ cachedFramework = "react";
3483
+ return findComponentBoundary(fiber);
3484
+ }
3485
+ if (isAngularElement(el)) {
3486
+ cachedFramework = "angular";
3487
+ return findAngularComponentBoundary(el);
3488
+ }
3489
+ return null;
3490
+ }
3491
+
3242
3492
  // overlay/src/design-canvas/vb-design-canvas.ts
3243
3493
  var VbDesignCanvas = class extends HTMLElement {
3244
3494
  static observedAttributes = ["src", "width", "height", "min-height"];
@@ -3286,6 +3536,7 @@ ${pad}</${tag}>`;
3286
3536
  this.iframe.allow = "microphone";
3287
3537
  this.iframe.style.cssText = css(DESIGN_CANVAS_IFRAME);
3288
3538
  this.iframe.addEventListener("load", () => {
3539
+ console.log("[tw-debug] vb-design-canvas iframe load event fired, src=", this.iframe.src);
3289
3540
  this.dispatchEvent(new CustomEvent("vb-canvas-ready", {
3290
3541
  bubbles: true,
3291
3542
  detail: { iframe: this.iframe }
@@ -3338,7 +3589,9 @@ ${pad}</${tag}>`;
3338
3589
  /** Sync observed attributes to internal DOM. */
3339
3590
  syncAttributes() {
3340
3591
  const src = this.getAttribute("src");
3592
+ console.log("[tw-debug] vb-design-canvas syncAttributes, src=", src, "current iframe.src=", this.iframe.src);
3341
3593
  if (src && this.iframe.src !== src) {
3594
+ console.log("[tw-debug] vb-design-canvas setting iframe.src =", src);
3342
3595
  this.iframe.src = src;
3343
3596
  }
3344
3597
  const w = this.getAttribute("width");
@@ -3379,6 +3632,7 @@ ${pad}</${tag}>`;
3379
3632
  var TEXT_SVG = `<svg viewBox="0 0 16 16" fill="currentColor"><path d="M14.895,2.553l-1-2c-.169-.339-.516-.553-.895-.553H3c-.379,0-.725,.214-.895,.553L1.105,2.553c-.247,.494-.047,1.095,.447,1.342,.496,.248,1.095,.046,1.342-.447l.724-1.447h3.382V14h-2c-.552,0-1,.448-1,1s.448,1,1,1h6c.552,0,1-.448,1-1s-.448-1-1-1h-2V2h3.382l.724,1.447c.175,.351,.528,.553,.896,.553,.15,0,.303-.034,.446-.105,.494-.247,.694-.848,.447-1.342Z"/></svg>`;
3380
3633
  var REPLACE_SVG = `<svg viewBox="0 0 16 16" fill="currentColor"><path d="M6,15H1a1,1,0,0,1-1-1V2A1,1,0,0,1,1,1H6A1,1,0,0,1,7,2V14A1,1,0,0,1,6,15Z"/><rect x="9" y="6" width="2" height="4"/><path d="M14,13H11V12H9v2a1,1,0,0,0,1,1h5a1,1,0,0,0,1-1V12H14Z"/><path d="M15,1H10A1,1,0,0,0,9,2V4h2V3h3V4h2V2A1,1,0,0,0,15,1Z"/><rect x="14" y="6" width="2" height="4"/></svg>`;
3381
3634
  var SEND_SVG = `<svg viewBox="0 0 16 16" fill="currentColor"><path d="M15.7,7.3l-14-7C1.4,0.1,1.1,0.1,0.8,0.3C0.6,0.4,0.5,0.7,0.5,1l1.8,6H9v2H2.3L0.5,15c-0.1,0.3,0,0.6,0.2,0.7C0.8,15.9,1,16,1.1,16c0.1,0,0.3,0,0.4-0.1l14-7C15.8,8.7,16,8.4,16,8S15.8,7.3,15.7,7.3z"/></svg>`;
3635
+ var MIC_SVG = `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 1a4 4 0 0 1 4 4v7a4 4 0 0 1-8 0V5a4 4 0 0 1 4-4zm-1.5 4v7a1.5 1.5 0 0 0 3 0V5a1.5 1.5 0 0 0-3 0zM6 11a1 1 0 0 1 1 1 5 5 0 0 0 10 0 1 1 0 1 1 2 0 7 7 0 0 1-6 6.93V21h2a1 1 0 1 1 0 2H9a1 1 0 1 1 0-2h2v-2.07A7 7 0 0 1 5 12a1 1 0 0 1 1-1z"/></svg>`;
3382
3636
  var VYBIT_LOGO_SVG = `<svg width="26" height="27" viewBox="0 0 210 221" fill="none" xmlns="http://www.w3.org/2000/svg">
3383
3637
  <path class="eb-fill" d="M141.54 137.71L103.87 140.38C102.98 140.44 102.2 140.97 101.8 141.77C101.41 142.57 101.47 143.51 101.96 144.25C102.27 144.72 109.46 155.39 121.96 155.39C122.3 155.39 122.65 155.39 123 155.37C138.61 154.64 143.83 141.66 144.05 141.11C144.36 140.31 144.24 139.41 143.73 138.72C143.22 138.03 142.4 137.65 141.54 137.71Z"/>
3384
3638
  <path class="eb-eye-l eb-fill" d="M80.6401 93.03C76.7801 93.22 73.8 96.5 73.99 100.36L74.7501 115.96C74.9401 119.85 78.2701 122.84 82.1501 122.61C85.9801 122.38 88.9101 119.11 88.7301 115.28L87.9701 99.68C87.7801 95.82 84.5001 92.84 80.6401 93.03Z"/>
@@ -3422,13 +3676,23 @@ ${pad}</${tag}>`;
3422
3676
  function findExactMatches(clickedEl, shadowHost) {
3423
3677
  const classes = parseClassList(typeof clickedEl.className === "string" ? clickedEl.className : "");
3424
3678
  const tag = clickedEl.tagName;
3425
- const fiber = getFiber(clickedEl);
3426
- const boundary = fiber ? findComponentBoundary(fiber) : null;
3679
+ const boundary = detectComponent(clickedEl);
3427
3680
  const componentName = boundary?.componentName ?? null;
3681
+ const fiber = getFiber(clickedEl);
3682
+ const reactBoundary = fiber ? findComponentBoundary(fiber) : null;
3428
3683
  let exactMatches;
3429
- if (boundary) {
3430
- const rootFiber = getRootFiberFrom(boundary.componentFiber) ?? getRootFiber();
3431
- const allNodes = rootFiber ? collectComponentDOMNodes(rootFiber, boundary.componentType, tag) : [];
3684
+ if (reactBoundary) {
3685
+ const rootFiber = getRootFiberFrom(reactBoundary.componentFiber) ?? getRootFiber();
3686
+ const allNodes = rootFiber ? collectComponentDOMNodes(rootFiber, reactBoundary.componentType, tag) : [];
3687
+ exactMatches = allNodes.filter(
3688
+ (n) => n.tagName === tag && n.className === clickedEl.className
3689
+ );
3690
+ } else if (boundary && isAngularElement(clickedEl)) {
3691
+ const instances = findAllAngularInstances(boundary.componentType);
3692
+ const allNodes = [];
3693
+ for (const host of instances) {
3694
+ allNodes.push(...collectAngularComponentDOMNodes(host, tag));
3695
+ }
3432
3696
  exactMatches = allNodes.filter(
3433
3697
  (n) => n.tagName === tag && n.className === clickedEl.className
3434
3698
  );
@@ -3464,14 +3728,23 @@ ${pad}</${tag}>`;
3464
3728
  const tag = clickedEl.tagName;
3465
3729
  const refSet = new Set(classes);
3466
3730
  if (classes.length === 0) return [];
3731
+ const boundary = detectComponent(clickedEl);
3467
3732
  const fiber = getFiber(clickedEl);
3468
- const boundary = fiber ? findComponentBoundary(fiber) : null;
3733
+ const reactBoundary = fiber ? findComponentBoundary(fiber) : null;
3469
3734
  let candidates;
3470
- if (boundary) {
3471
- const rootFiber = getRootFiberFrom(boundary.componentFiber) ?? getRootFiber();
3472
- candidates = rootFiber ? collectComponentDOMNodes(rootFiber, boundary.componentType, tag) : [];
3735
+ if (reactBoundary) {
3736
+ const rootFiber = getRootFiberFrom(reactBoundary.componentFiber) ?? getRootFiber();
3737
+ candidates = rootFiber ? collectComponentDOMNodes(rootFiber, reactBoundary.componentType, tag) : [];
3473
3738
  candidates = candidates.filter((n) => !exactMatchSet.has(n));
3474
- console.log("[grouping] React path \u2014 component:", boundary.componentName, "tag:", tag, "candidates:", candidates.length, candidates.map((n) => n.className.split(" ")[0]));
3739
+ console.log("[grouping] React path \u2014 component:", reactBoundary.componentName, "tag:", tag, "candidates:", candidates.length, candidates.map((n) => n.className.split(" ")[0]));
3740
+ } else if (boundary && isAngularElement(clickedEl)) {
3741
+ const instances = findAllAngularInstances(boundary.componentType);
3742
+ const allNodes = [];
3743
+ for (const host of instances) {
3744
+ allNodes.push(...collectAngularComponentDOMNodes(host, tag));
3745
+ }
3746
+ candidates = allNodes.filter((n) => !exactMatchSet.has(n));
3747
+ console.log("[grouping] Angular path \u2014 component:", boundary.componentName, "tag:", tag, "candidates:", candidates.length);
3475
3748
  } else {
3476
3749
  const seen = new Set(exactMatchSet);
3477
3750
  candidates = [];
@@ -3544,21 +3817,42 @@ ${pad}</${tag}>`;
3544
3817
  }
3545
3818
  function findSamePathElements(clickedEl) {
3546
3819
  const fiber = getFiber(clickedEl);
3547
- if (!fiber) return null;
3548
- const boundary = findComponentBoundary(fiber);
3549
- if (!boundary) return null;
3550
- const { label, path } = buildPathLabel(fiber, boundary);
3551
- const rootFiber = getRootFiberFrom(boundary.componentFiber) ?? getRootFiber();
3552
- if (!rootFiber) return null;
3553
- const instances = findAllInstances(rootFiber, boundary.componentType);
3554
- const elements = [];
3555
- for (const inst of instances) {
3556
- const node = resolvePathToDOM(inst, path);
3557
- if (node && !elements.includes(node)) {
3558
- elements.push(node);
3820
+ if (fiber) {
3821
+ const boundary = findComponentBoundary(fiber);
3822
+ if (!boundary) return null;
3823
+ const { label, path } = buildPathLabel(fiber, boundary);
3824
+ const rootFiber = getRootFiberFrom(boundary.componentFiber) ?? getRootFiber();
3825
+ if (!rootFiber) return null;
3826
+ const instances = findAllInstances(rootFiber, boundary.componentType);
3827
+ const elements = [];
3828
+ for (const inst of instances) {
3829
+ const node = resolvePathToDOM(inst, path);
3830
+ if (node && !elements.includes(node)) {
3831
+ elements.push(node);
3832
+ }
3559
3833
  }
3834
+ return elements.length > 0 ? { elements, label } : null;
3835
+ }
3836
+ if (isAngularElement(clickedEl)) {
3837
+ const boundary = detectComponent(clickedEl);
3838
+ if (!boundary) return null;
3839
+ const instances = findAllAngularInstances(boundary.componentType);
3840
+ if (instances.length < 2) return null;
3841
+ const tag = clickedEl.tagName;
3842
+ const elements = [];
3843
+ for (const host of instances) {
3844
+ const nodes = collectAngularComponentDOMNodes(host, tag);
3845
+ const match = nodes.find(
3846
+ (n) => n.tagName === clickedEl.tagName && n.className === clickedEl.className
3847
+ );
3848
+ if (match && !elements.includes(match)) {
3849
+ elements.push(match);
3850
+ }
3851
+ }
3852
+ const label = `${boundary.componentName} > ${tag.toLowerCase()}`;
3853
+ return elements.length > 0 ? { elements, label } : null;
3560
3854
  }
3561
- return elements.length > 0 ? { elements, label } : null;
3855
+ return null;
3562
3856
  }
3563
3857
 
3564
3858
  // overlay/src/patcher.ts
@@ -3811,13 +4105,52 @@ ${pad}</${tag}>`;
3811
4105
  clearHoverPreview();
3812
4106
  return;
3813
4107
  }
3814
- const fiber = getFiber(target);
3815
- const boundary = fiber ? findComponentBoundary(fiber) : null;
4108
+ const boundary = detectComponent(target);
3816
4109
  const label = boundary?.componentName ?? target.tagName.toLowerCase();
3817
4110
  showHoverPreview(target, label);
3818
4111
  }
3819
4112
 
3820
4113
  // overlay/src/element-toolbar.ts
4114
+ var SpeechRecognitionAPI = typeof window !== "undefined" ? window.SpeechRecognition ?? window.webkitSpeechRecognition ?? null : null;
4115
+ var activeBanner = null;
4116
+ var bannerTimeout = null;
4117
+ function showMicBanner(message) {
4118
+ if (activeBanner) {
4119
+ activeBanner.remove();
4120
+ activeBanner = null;
4121
+ }
4122
+ if (bannerTimeout) {
4123
+ clearTimeout(bannerTimeout);
4124
+ bannerTimeout = null;
4125
+ }
4126
+ const banner = document.createElement("div");
4127
+ banner.className = "mic-banner";
4128
+ const text = document.createElement("span");
4129
+ text.textContent = message;
4130
+ banner.appendChild(text);
4131
+ const dismiss = document.createElement("button");
4132
+ dismiss.className = "mic-banner-dismiss";
4133
+ dismiss.textContent = "\xD7";
4134
+ dismiss.addEventListener("click", () => {
4135
+ banner.classList.remove("visible");
4136
+ setTimeout(() => banner.remove(), 250);
4137
+ activeBanner = null;
4138
+ if (bannerTimeout) {
4139
+ clearTimeout(bannerTimeout);
4140
+ bannerTimeout = null;
4141
+ }
4142
+ });
4143
+ banner.appendChild(dismiss);
4144
+ state.shadowRoot.appendChild(banner);
4145
+ activeBanner = banner;
4146
+ requestAnimationFrame(() => banner.classList.add("visible"));
4147
+ bannerTimeout = setTimeout(() => {
4148
+ banner.classList.remove("visible");
4149
+ setTimeout(() => banner.remove(), 250);
4150
+ activeBanner = null;
4151
+ bannerTimeout = null;
4152
+ }, 8e3);
4153
+ }
3821
4154
  var setSelectMode;
3822
4155
  var showToast;
3823
4156
  var onBrowseLocked;
@@ -3830,10 +4163,11 @@ ${pad}</${tag}>`;
3830
4163
  rebuildSelectionFromSources = deps2.rebuildSelectionFromSources;
3831
4164
  setAddMode = deps2.setAddMode;
3832
4165
  }
3833
- async function positionWithFlip(anchor, floating, placement = "top-start") {
4166
+ async function positionWithFlip(anchor, floating, placement = "top-start", options) {
4167
+ const middleware = options?.disableFlip ? [offset2(6)] : [offset2(6), flip2()];
3834
4168
  const { x, y, placement: resolved } = await computePosition2(anchor, floating, {
3835
4169
  placement,
3836
- middleware: [offset2(6), flip2()]
4170
+ middleware
3837
4171
  });
3838
4172
  floating.style.left = `${x}px`;
3839
4173
  floating.style.top = `${y}px`;
@@ -3844,11 +4178,24 @@ ${pad}</${tag}>`;
3844
4178
  if (!msgRow) return;
3845
4179
  const msgPlacement = await positionWithFlip(targetEl, msgRow, "bottom-start");
3846
4180
  if (toolbarPlacement === "bottom-start" && msgPlacement === "bottom-start") {
3847
- await positionWithFlip(toolbar, msgRow, "bottom-start");
4181
+ await positionWithFlip(toolbar, msgRow, "bottom-start", { disableFlip: true });
3848
4182
  } else if (toolbarPlacement === "top-start" && msgPlacement === "top-start") {
3849
- await positionWithFlip(toolbar, msgRow, "top-start");
4183
+ await positionWithFlip(toolbar, msgRow, "top-start", { disableFlip: true });
3850
4184
  }
3851
4185
  }
4186
+ function clearSelection() {
4187
+ cancelInsert();
4188
+ clearLockedInsert();
4189
+ revertPreview();
4190
+ clearHighlights();
4191
+ state.currentEquivalentNodes = [];
4192
+ state.currentTargetEl = null;
4193
+ state.currentBoundary = null;
4194
+ state.cachedNearGroups = null;
4195
+ state.cachedExactMatches = null;
4196
+ state.manuallyAddedNodes = /* @__PURE__ */ new Set();
4197
+ state.addMode = false;
4198
+ }
3852
4199
  function showDrawButton(targetEl) {
3853
4200
  removeDrawButton();
3854
4201
  const instanceCount = state.currentEquivalentNodes.length;
@@ -3867,21 +4214,9 @@ ${pad}</${tag}>`;
3867
4214
  selectBtn.style.cssText = "color: #5fd4da; border-radius: 0;";
3868
4215
  selectBtn.addEventListener("click", (e) => {
3869
4216
  e.stopPropagation();
3870
- cancelInsert();
3871
- clearLockedInsert();
3872
- revertPreview();
3873
- clearHighlights();
3874
- state.currentEquivalentNodes = [];
3875
- state.currentTargetEl = null;
3876
- state.currentBoundary = null;
3877
- state.cachedNearGroups = null;
3878
- state.cachedExactMatches = null;
3879
- state.manuallyAddedNodes = /* @__PURE__ */ new Set();
3880
- state.addMode = false;
3881
- state.currentMode = "select";
3882
- state.currentTab = resolveTab();
3883
- sendTo("panel", { type: "MODE_CHANGED", mode: "select" });
4217
+ clearSelection();
3884
4218
  setSelectMode(true);
4219
+ sendTo("panel", { type: "DESELECT_ELEMENT" });
3885
4220
  });
3886
4221
  selectGroup.appendChild(selectBtn);
3887
4222
  const innerSep = document.createElement("div");
@@ -3918,17 +4253,7 @@ ${pad}</${tag}>`;
3918
4253
  selectBtn.style.cssText = "opacity: 0.4;";
3919
4254
  selectBtn.addEventListener("click", (e) => {
3920
4255
  e.stopPropagation();
3921
- cancelInsert();
3922
- clearLockedInsert();
3923
- revertPreview();
3924
- clearHighlights();
3925
- state.currentEquivalentNodes = [];
3926
- state.currentTargetEl = null;
3927
- state.currentBoundary = null;
3928
- state.cachedNearGroups = null;
3929
- state.cachedExactMatches = null;
3930
- state.manuallyAddedNodes = /* @__PURE__ */ new Set();
3931
- state.addMode = false;
4256
+ clearSelection();
3932
4257
  state.currentMode = "select";
3933
4258
  state.currentTab = resolveTab();
3934
4259
  sendTo("panel", { type: "MODE_CHANGED", mode: "select" });
@@ -3946,17 +4271,13 @@ ${pad}</${tag}>`;
3946
4271
  }
3947
4272
  insertBtn.addEventListener("click", (e) => {
3948
4273
  e.stopPropagation();
3949
- cancelInsert();
3950
- clearLockedInsert();
3951
- revertPreview();
3952
- clearHighlights();
3953
- state.currentEquivalentNodes = [];
3954
- state.currentTargetEl = null;
3955
- state.currentBoundary = null;
3956
- state.cachedNearGroups = null;
3957
- state.cachedExactMatches = null;
3958
- state.manuallyAddedNodes = /* @__PURE__ */ new Set();
3959
- state.addMode = false;
4274
+ clearSelection();
4275
+ setSelectMode(false);
4276
+ if (state.currentMode === "insert") {
4277
+ sendTo("panel", { type: "DESELECT_ELEMENT" });
4278
+ startBrowse(state.shadowHost, onBrowseLocked);
4279
+ return;
4280
+ }
3960
4281
  state.currentMode = "insert";
3961
4282
  if (state.tabPreference === "design") state.tabPreference = "component";
3962
4283
  state.currentTab = resolveTab();
@@ -4008,20 +4329,79 @@ ${pad}</${tag}>`;
4008
4329
  placeBtn.innerHTML = `Place`;
4009
4330
  toolbar.appendChild(placeBtn);
4010
4331
  }
4332
+ let msgRow;
4333
+ msgRow = createMsgRow(state.currentBoundary, () => positionBothMenus(targetEl, toolbar, msgRow));
4334
+ state.shadowRoot.appendChild(msgRow);
4335
+ state.msgRowEl = msgRow;
4336
+ positionBothMenus(targetEl, toolbar, msgRow);
4337
+ }
4338
+ function getCanvasMessageText() {
4339
+ const textarea = canvasMsgRow?.querySelector("textarea");
4340
+ return textarea?.value.trim() ?? "";
4341
+ }
4342
+ function createMsgRow(boundary, onReposition, options) {
4343
+ const showSendButton = options?.showSendButton ?? true;
4011
4344
  const msgRow = document.createElement("div");
4012
4345
  msgRow.className = "msg-row";
4013
4346
  msgRow.style.left = "0px";
4014
4347
  msgRow.style.top = "0px";
4015
- state.shadowRoot.appendChild(msgRow);
4016
- state.msgRowEl = msgRow;
4017
4348
  const msgInput = document.createElement("textarea");
4018
4349
  msgInput.rows = 1;
4019
4350
  msgInput.placeholder = "add your message";
4020
4351
  msgRow.appendChild(msgInput);
4021
- const msgSendBtn = document.createElement("button");
4022
- msgSendBtn.className = "msg-send";
4023
- msgSendBtn.innerHTML = SEND_SVG;
4024
- msgRow.appendChild(msgSendBtn);
4352
+ let recognition = null;
4353
+ let micBtn = null;
4354
+ if (SpeechRecognitionAPI) {
4355
+ micBtn = document.createElement("button");
4356
+ micBtn.className = "mic-btn";
4357
+ micBtn.title = "Record voice message";
4358
+ micBtn.innerHTML = MIC_SVG;
4359
+ msgRow.appendChild(micBtn);
4360
+ micBtn.addEventListener("click", (e) => {
4361
+ e.stopPropagation();
4362
+ if (recognition) {
4363
+ recognition.stop();
4364
+ return;
4365
+ }
4366
+ const baseText = msgInput.value;
4367
+ recognition = new SpeechRecognitionAPI();
4368
+ recognition.continuous = false;
4369
+ recognition.interimResults = true;
4370
+ recognition.lang = navigator.language || "en-US";
4371
+ recognition.onresult = (event) => {
4372
+ let transcript = "";
4373
+ for (let i = 0; i < event.results.length; i++) {
4374
+ transcript += event.results[i][0].transcript;
4375
+ }
4376
+ const separator = baseText && !baseText.endsWith("\n") ? "\n" : "";
4377
+ msgInput.value = baseText + separator + transcript;
4378
+ msgInput.style.height = "auto";
4379
+ msgInput.style.height = msgInput.scrollHeight + "px";
4380
+ onReposition();
4381
+ };
4382
+ recognition.onend = () => {
4383
+ micBtn.classList.remove("listening");
4384
+ recognition = null;
4385
+ };
4386
+ recognition.onerror = (event) => {
4387
+ micBtn.classList.remove("listening");
4388
+ if (event.error === "not-allowed" || event.error === "service-not-allowed") {
4389
+ micBtn.classList.add("error");
4390
+ showMicBanner("Microphone blocked \u2014 allow access in your browser's address bar");
4391
+ }
4392
+ recognition = null;
4393
+ };
4394
+ micBtn.classList.remove("error");
4395
+ micBtn.classList.add("listening");
4396
+ recognition.start();
4397
+ });
4398
+ }
4399
+ const msgSendBtn = showSendButton ? document.createElement("button") : null;
4400
+ if (msgSendBtn) {
4401
+ msgSendBtn.className = "msg-send";
4402
+ msgSendBtn.innerHTML = SEND_SVG;
4403
+ msgRow.appendChild(msgSendBtn);
4404
+ }
4025
4405
  function sendMessage() {
4026
4406
  const text = msgInput.value.trim();
4027
4407
  if (!text) return;
@@ -4030,15 +4410,15 @@ ${pad}</${tag}>`;
4030
4410
  type: "MESSAGE_STAGE",
4031
4411
  id,
4032
4412
  message: text,
4033
- elementKey: state.currentBoundary?.componentName ?? "",
4034
- component: state.currentBoundary ? { name: state.currentBoundary.componentName } : void 0
4413
+ elementKey: boundary?.componentName ?? "",
4414
+ component: boundary ? { name: boundary.componentName } : void 0
4035
4415
  });
4036
4416
  msgInput.value = "";
4037
4417
  msgInput.style.height = "auto";
4038
- positionBothMenus(targetEl, toolbar, msgRow);
4418
+ onReposition();
4039
4419
  showToast("Message staged");
4040
4420
  }
4041
- msgSendBtn.addEventListener("click", (e) => {
4421
+ msgSendBtn?.addEventListener("click", (e) => {
4042
4422
  e.stopPropagation();
4043
4423
  sendMessage();
4044
4424
  });
@@ -4054,10 +4434,45 @@ ${pad}</${tag}>`;
4054
4434
  msgInput.addEventListener("input", () => {
4055
4435
  msgInput.style.height = "auto";
4056
4436
  msgInput.style.height = msgInput.scrollHeight + "px";
4057
- positionBothMenus(targetEl, toolbar, msgRow);
4437
+ onReposition();
4058
4438
  });
4059
4439
  msgRow.addEventListener("click", (e) => e.stopPropagation());
4060
- positionBothMenus(targetEl, toolbar, msgRow);
4440
+ return msgRow;
4441
+ }
4442
+ var canvasMsgRow = null;
4443
+ var canvasMsgRowObserver = null;
4444
+ function showCanvasMessageRow(canvasWrapper, boundary, shadowRoot) {
4445
+ hideCanvasMessageRow();
4446
+ const msgRow = createMsgRow(boundary, () => positionCanvasMsgRow(canvasWrapper, msgRow), { showSendButton: false });
4447
+ msgRow.setAttribute("data-canvas-anchor", "true");
4448
+ shadowRoot.appendChild(msgRow);
4449
+ canvasMsgRow = msgRow;
4450
+ positionCanvasMsgRow(canvasWrapper, msgRow);
4451
+ canvasMsgRowObserver = new ResizeObserver(() => {
4452
+ positionCanvasMsgRow(canvasWrapper, msgRow);
4453
+ });
4454
+ canvasMsgRowObserver.observe(canvasWrapper);
4455
+ }
4456
+ function hideCanvasMessageRow() {
4457
+ canvasMsgRowObserver?.disconnect();
4458
+ canvasMsgRowObserver = null;
4459
+ canvasMsgRow?.remove();
4460
+ canvasMsgRow = null;
4461
+ }
4462
+ function positionCanvasMsgRow(canvasWrapper, msgRow) {
4463
+ computePosition2(canvasWrapper, msgRow, {
4464
+ placement: "bottom-start",
4465
+ middleware: [
4466
+ shift2({ padding: 8 }),
4467
+ flip2()
4468
+ ]
4469
+ }).then(({ x, y }) => {
4470
+ Object.assign(msgRow.style, {
4471
+ position: "fixed",
4472
+ left: `${x}px`,
4473
+ top: `${y}px`
4474
+ });
4475
+ });
4061
4476
  }
4062
4477
  function showGroupPicker(anchorBtn, onClose, onCountChange) {
4063
4478
  if (state.pickerCloseHandler) {
@@ -5151,6 +5566,7 @@ ${pad}</${tag}>`;
5151
5566
  showToastFn = deps2.showToast;
5152
5567
  }
5153
5568
  function injectDesignCanvas(insertMode) {
5569
+ console.log("[tw-debug] injectDesignCanvas called, insertMode=", insertMode, "currentTargetEl=", state.currentTargetEl, "currentBoundary=", state.currentBoundary);
5154
5570
  if (!state.currentTargetEl || !state.currentBoundary) {
5155
5571
  showToastFn("Select an element first");
5156
5572
  return;
@@ -5158,7 +5574,9 @@ ${pad}</${tag}>`;
5158
5574
  clearHighlights();
5159
5575
  const targetEl = state.currentTargetEl;
5160
5576
  const canvas = document.createElement("vb-design-canvas");
5161
- canvas.setAttribute("src", `${serverOrigin}/panel/?mode=design`);
5577
+ const canvasSrc = `${serverOrigin}/panel/?mode=design`;
5578
+ console.log("[tw-debug] creating vb-design-canvas, src=", canvasSrc, "targetEl=", targetEl, "insertMode=", insertMode);
5579
+ canvas.setAttribute("src", canvasSrc);
5162
5580
  const wrapper = canvas.getWrapper();
5163
5581
  let replacedNodes = null;
5164
5582
  let replacedParent = null;
@@ -5193,7 +5611,10 @@ ${pad}</${tag}>`;
5193
5611
  parent: replacedParent,
5194
5612
  anchor: replacedAnchor
5195
5613
  });
5614
+ requestAnimationFrame(() => showCanvasMessageRow(canvas.getWrapper(), state.currentBoundary, state.shadowRoot));
5615
+ console.log("[tw-debug] canvas inserted into DOM, listening for vb-canvas-ready...");
5196
5616
  canvas.addEventListener("vb-canvas-ready", () => {
5617
+ console.log("[tw-debug] vb-canvas-ready fired!");
5197
5618
  const contextMsg = {
5198
5619
  type: "ELEMENT_CONTEXT",
5199
5620
  componentName: state.currentBoundary?.componentName ?? "",
@@ -5276,6 +5697,7 @@ ${pad}</${tag}>`;
5276
5697
  parent.insertBefore(canvas, marker);
5277
5698
  marker.remove();
5278
5699
  state.designCanvasWrappers.push({ wrapper: canvas, replacedNodes, parent, anchor: canvas.nextSibling });
5700
+ requestAnimationFrame(() => showCanvasMessageRow(canvas.getWrapper(), state.currentBoundary, state.shadowRoot));
5279
5701
  canvas.addEventListener("vb-canvas-ready", () => {
5280
5702
  const contextMsg = {
5281
5703
  type: "ELEMENT_CONTEXT",
@@ -5299,6 +5721,12 @@ ${pad}</${tag}>`;
5299
5721
  setTimeout(trySend, 200);
5300
5722
  });
5301
5723
  }
5724
+ function removeAllDesignCanvases() {
5725
+ for (const entry of state.designCanvasWrappers) {
5726
+ entry.wrapper.remove();
5727
+ }
5728
+ state.designCanvasWrappers.length = 0;
5729
+ }
5302
5730
  function handleDesignSubmitted(msg) {
5303
5731
  const lastEntry = state.designCanvasWrappers[state.designCanvasWrappers.length - 1];
5304
5732
  const last = lastEntry?.wrapper;
@@ -5315,6 +5743,11 @@ ${pad}</${tag}>`;
5315
5743
  last.appendChild(img);
5316
5744
  }
5317
5745
  }
5746
+ const messageText = getCanvasMessageText();
5747
+ if (messageText && msg.patchId) {
5748
+ send({ type: "CANVAS_MESSAGE_ATTACH", patchId: msg.patchId, message: messageText });
5749
+ }
5750
+ hideCanvasMessageRow();
5318
5751
  }
5319
5752
  function handleDesignClose() {
5320
5753
  const last = state.designCanvasWrappers.pop();
@@ -5326,6 +5759,7 @@ ${pad}</${tag}>`;
5326
5759
  }
5327
5760
  last.wrapper.remove();
5328
5761
  }
5762
+ hideCanvasMessageRow();
5329
5763
  }
5330
5764
 
5331
5765
  // overlay/src/recording/console-interceptor/console-interceptor.ts
@@ -6825,8 +7259,7 @@ ${pad}</${tag}>`;
6825
7259
  function onBrowseLocked2(target) {
6826
7260
  state.currentTargetEl = target;
6827
7261
  state.currentEquivalentNodes = [target];
6828
- const fiber = getFiber(target);
6829
- const boundary = fiber ? findComponentBoundary(fiber) : null;
7262
+ const boundary = detectComponent(target);
6830
7263
  state.currentBoundary = boundary ? { componentName: boundary.componentName } : { componentName: target.tagName.toLowerCase() };
6831
7264
  state.cachedNearGroups = null;
6832
7265
  showDrawButton(target);
@@ -7088,6 +7521,24 @@ ${pad}</${tag}>`;
7088
7521
  setTimeout(() => toast.remove(), 200);
7089
7522
  }, duration);
7090
7523
  }
7524
+ function resetOnNavigation() {
7525
+ if (isTextEditing()) endTextEdit(false);
7526
+ revertPreview();
7527
+ clearHighlights();
7528
+ cancelInsert();
7529
+ clearLockedInsert();
7530
+ removeAllDesignCanvases();
7531
+ state.currentEquivalentNodes = [];
7532
+ state.currentTargetEl = null;
7533
+ state.currentBoundary = null;
7534
+ state.cachedNearGroups = null;
7535
+ state.cachedExactMatches = null;
7536
+ state.manuallyAddedNodes = /* @__PURE__ */ new Set();
7537
+ state.addMode = false;
7538
+ setSelectMode2(false);
7539
+ sendTo("panel", { type: "RESET_SELECTION" });
7540
+ sendTo("panel", { type: "COMPONENT_DISARMED" });
7541
+ }
7091
7542
  function getDefaultContainer() {
7092
7543
  try {
7093
7544
  const stored = localStorage.getItem("tw-panel-container");
@@ -7099,6 +7550,8 @@ ${pad}</${tag}>`;
7099
7550
  return "popover";
7100
7551
  }
7101
7552
  function init() {
7553
+ const params = new URLSearchParams(location.search);
7554
+ if (params.get("viewMode") === "story" || params.get("vybit-ghost") === "1") return;
7102
7555
  state.shadowHost = document.createElement("div");
7103
7556
  state.shadowHost.id = "tw-visual-editor-host";
7104
7557
  state.shadowHost.style.cssText = css(SHADOW_HOST);
@@ -7125,6 +7578,16 @@ ${pad}</${tag}>`;
7125
7578
  btn.style.display = "none";
7126
7579
  }
7127
7580
  state.shadowRoot.appendChild(btn);
7581
+ createNavigationInterceptor(() => {
7582
+ if (state.active) resetOnNavigation();
7583
+ });
7584
+ window.addEventListener("message", (event) => {
7585
+ if (event.data?.type === "STORYBOOK_STORY_RENDERED") {
7586
+ if (state.active) {
7587
+ resetOnNavigation();
7588
+ }
7589
+ }
7590
+ });
7128
7591
  document.addEventListener("keydown", (e) => {
7129
7592
  if (e.key === "Escape") {
7130
7593
  if (state.addMode) {
@@ -7132,20 +7595,15 @@ ${pad}</${tag}>`;
7132
7595
  return;
7133
7596
  }
7134
7597
  if (state.currentTargetEl) {
7135
- revertPreview();
7136
- clearHighlights();
7137
- state.currentEquivalentNodes = [];
7138
- state.currentTargetEl = null;
7139
- state.currentBoundary = null;
7140
- state.cachedNearGroups = null;
7141
- state.cachedExactMatches = null;
7142
- state.manuallyAddedNodes = /* @__PURE__ */ new Set();
7143
- state.addMode = false;
7144
- sendTo("panel", { type: "RESET_SELECTION" });
7598
+ clearSelection();
7145
7599
  if (state.currentMode === "select") {
7146
7600
  setSelectMode2(true);
7601
+ sendTo("panel", { type: "DESELECT_ELEMENT" });
7147
7602
  } else if (state.currentMode === "insert") {
7603
+ sendTo("panel", { type: "DESELECT_ELEMENT" });
7148
7604
  startBrowse(state.shadowHost, onBrowseLocked2);
7605
+ } else {
7606
+ sendTo("panel", { type: "RESET_SELECTION" });
7149
7607
  }
7150
7608
  } else if (state.selectModeOn) {
7151
7609
  setSelectMode2(false);
@@ -7162,6 +7620,8 @@ ${pad}</${tag}>`;
7162
7620
  onMessage((msg) => {
7163
7621
  if (msg.type === "TOGGLE_SELECT_MODE") {
7164
7622
  if (msg.active) {
7623
+ state.active = true;
7624
+ sessionStorage.setItem(PANEL_OPEN_KEY, "1");
7165
7625
  setSelectMode2(true);
7166
7626
  if (!insideStorybook) {
7167
7627
  const panelUrl = `${SERVER_ORIGIN}/panel`;
@@ -7171,6 +7631,10 @@ ${pad}</${tag}>`;
7171
7631
  setSelectMode2(false);
7172
7632
  }
7173
7633
  } else if (msg.type === "MODE_CHANGED") {
7634
+ if (msg.mode) {
7635
+ state.active = true;
7636
+ sessionStorage.setItem(PANEL_OPEN_KEY, "1");
7637
+ }
7174
7638
  revertPreview();
7175
7639
  clearHighlights();
7176
7640
  cancelInsert();
@@ -7181,6 +7645,7 @@ ${pad}</${tag}>`;
7181
7645
  state.cachedNearGroups = null;
7182
7646
  state.currentMode = msg.mode;
7183
7647
  if (msg.mode === "insert") {
7648
+ setSelectMode2(false);
7184
7649
  if (state.tabPreference === "design") state.tabPreference = "component";
7185
7650
  state.currentTab = resolveTab();
7186
7651
  startBrowse(state.shadowHost, onBrowseLocked2);
@@ -7295,22 +7760,36 @@ ${pad}</${tag}>`;
7295
7760
  }
7296
7761
  } else {
7297
7762
  const locked2 = getLockedInsert();
7763
+ console.log("[tw-debug] INSERT_DESIGN_CANVAS else branch, locked=", locked2, "currentTargetEl=", state.currentTargetEl);
7298
7764
  if (locked2) {
7299
7765
  state.currentTargetEl = locked2.target;
7300
- const fiber = getFiber(locked2.target);
7301
- const boundary = fiber ? findComponentBoundary(fiber) : null;
7766
+ const boundary = detectComponent(locked2.target);
7302
7767
  state.currentBoundary = boundary ? { componentName: boundary.componentName } : { componentName: locked2.target.tagName.toLowerCase() };
7303
7768
  state.currentEquivalentNodes = [locked2.target];
7304
7769
  clearLockedInsert();
7770
+ console.log("[tw-debug] locked path \u2014 calling injectDesignCanvas, position=", locked2.position, "boundary=", state.currentBoundary);
7305
7771
  injectDesignCanvas(locked2.position);
7306
7772
  } else {
7773
+ console.log("[tw-debug] arming generic insert...");
7307
7774
  armGenericInsert("Place: Canvas", state.shadowHost, (target, position) => {
7308
- state.currentTargetEl = target;
7309
- const fiber = getFiber(target);
7310
- const boundary = fiber ? findComponentBoundary(fiber) : null;
7311
- state.currentBoundary = boundary ? { componentName: boundary.componentName } : { componentName: target.tagName.toLowerCase() };
7312
- state.currentEquivalentNodes = [target];
7313
- injectDesignCanvas(position);
7775
+ console.log("[tw-debug] armGenericInsert callback fired, target=", target, "position=", position);
7776
+ try {
7777
+ console.log("[tw-debug] step 1: setting currentTargetEl");
7778
+ state.currentTargetEl = target;
7779
+ console.log("[tw-debug] step 2: calling detectComponent");
7780
+ const boundary = detectComponent(target);
7781
+ console.log("[tw-debug] step 3: boundary=", boundary);
7782
+ state.currentBoundary = boundary ? { componentName: boundary.componentName } : { componentName: target.tagName.toLowerCase() };
7783
+ state.currentEquivalentNodes = [target];
7784
+ console.log("[tw-debug] step 4: state set, currentBoundary=", state.currentBoundary, "\u2014 calling injectDesignCanvas");
7785
+ injectDesignCanvas(position);
7786
+ console.log("[tw-debug] injectDesignCanvas returned OK");
7787
+ } catch (err) {
7788
+ console.error("[tw-debug] CALLBACK THREW (this is the real error):", err);
7789
+ if (err instanceof Error) {
7790
+ console.error("[tw-debug] message:", err.message, "stack:", err.stack);
7791
+ }
7792
+ }
7314
7793
  });
7315
7794
  }
7316
7795
  }
@@ -7439,8 +7918,7 @@ ${pad}</${tag}>`;
7439
7918
  if (!target || target === state.shadowHost || e.composedPath().some((el) => el === state.shadowHost)) {
7440
7919
  return;
7441
7920
  }
7442
- const fiber = getFiber(target);
7443
- const boundary = fiber ? findComponentBoundary(fiber) : null;
7921
+ const boundary = detectComponent(target);
7444
7922
  const selectorPath = buildSelectorPath2(target);
7445
7923
  const rect = target.getBoundingClientRect();
7446
7924
  const element = {