@agent-native/core 0.114.2 → 0.114.4

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 (132) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +30 -0
  3. package/corpus/core/package.json +2 -1
  4. package/corpus/core/src/a2a/artifact-response.ts +42 -16
  5. package/corpus/core/src/a2a/client.ts +4 -0
  6. package/corpus/core/src/a2a/handlers.ts +133 -2
  7. package/corpus/core/src/a2a/index.ts +1 -0
  8. package/corpus/core/src/a2a/types.ts +14 -0
  9. package/corpus/core/src/db/request-telemetry.ts +32 -8
  10. package/corpus/core/src/integrations/index.ts +6 -0
  11. package/corpus/core/src/integrations/pending-tasks-store.ts +89 -0
  12. package/corpus/core/src/integrations/webhook-handler.ts +17 -7
  13. package/corpus/core/src/scripts/call-agent.ts +54 -10
  14. package/corpus/core/src/server/agent-discovery.ts +22 -0
  15. package/corpus/core/src/settings/store.ts +15 -6
  16. package/corpus/core/src/shared/optional-node-builtins.ts +66 -0
  17. package/corpus/core/src/templates/default/.agents/skills/feature-flags/SKILL.md +1 -1
  18. package/corpus/core/src/templates/headless/.agents/skills/feature-flags/SKILL.md +1 -1
  19. package/corpus/core/src/templates/workspace-core/.agents/skills/feature-flags/SKILL.md +1 -1
  20. package/corpus/core/src/vite/client.ts +5 -0
  21. package/corpus/templates/analytics/.agents/skills/feature-flags/SKILL.md +1 -1
  22. package/corpus/templates/assets/.agents/skills/feature-flags/SKILL.md +1 -1
  23. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +2 -2
  24. package/corpus/templates/brain/.agents/skills/feature-flags/SKILL.md +1 -1
  25. package/corpus/templates/calendar/.agents/skills/feature-flags/SKILL.md +1 -1
  26. package/corpus/templates/chat/.agents/skills/feature-flags/SKILL.md +1 -1
  27. package/corpus/templates/clips/.agents/skills/feature-flags/SKILL.md +1 -1
  28. package/corpus/templates/clips/shared/feature-flags.ts +1 -1
  29. package/corpus/templates/content/.agents/skills/content/SKILL.md +19 -5
  30. package/corpus/templates/content/.agents/skills/feature-flags/SKILL.md +1 -1
  31. package/corpus/templates/content/changelog/2026-07-17-slack-created-database-entries-now-record-slack-as-their-sub.md +6 -0
  32. package/corpus/templates/design/.agents/skills/feature-flags/SKILL.md +1 -1
  33. package/corpus/templates/design/actions/update-file.ts +10 -1
  34. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +30 -1
  35. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +14 -1
  36. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +547 -50
  37. package/corpus/templates/design/app/components/design/dnd-debug.ts +27 -0
  38. package/corpus/templates/design/app/components/design/multi-screen/draft-primitives.ts +19 -2
  39. package/corpus/templates/design/app/i18n-data.ts +19 -0
  40. package/corpus/templates/design/app/lib/design-save-outbox.ts +46 -2
  41. package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +4 -9
  42. package/corpus/templates/design/app/pages/design-editor/history.ts +14 -1
  43. package/corpus/templates/design/changelog/2026-07-17-drawn-shapes-stay-free-positioned-when-dragged.md +6 -0
  44. package/corpus/templates/design/changelog/2026-07-17-shapes-show-a-live-outline-while-you-draw.md +6 -0
  45. package/corpus/templates/design/shared/drag-reflow.ts +375 -0
  46. package/corpus/templates/design/shared/full-app.ts +1 -1
  47. package/corpus/templates/dispatch/.agents/skills/feature-flags/SKILL.md +1 -1
  48. package/corpus/templates/forms/.agents/skills/feature-flags/SKILL.md +1 -1
  49. package/corpus/templates/macros/.agents/skills/feature-flags/SKILL.md +1 -1
  50. package/corpus/templates/mail/.agents/skills/feature-flags/SKILL.md +1 -1
  51. package/corpus/templates/plan/.agents/skills/feature-flags/SKILL.md +1 -1
  52. package/corpus/templates/slides/.agents/skills/feature-flags/SKILL.md +1 -1
  53. package/corpus/templates/tasks/.agents/skills/feature-flags/SKILL.md +1 -1
  54. package/dist/a2a/artifact-response.d.ts +5 -0
  55. package/dist/a2a/artifact-response.d.ts.map +1 -1
  56. package/dist/a2a/artifact-response.js +20 -5
  57. package/dist/a2a/artifact-response.js.map +1 -1
  58. package/dist/a2a/client.d.ts +3 -1
  59. package/dist/a2a/client.d.ts.map +1 -1
  60. package/dist/a2a/client.js +2 -0
  61. package/dist/a2a/client.js.map +1 -1
  62. package/dist/a2a/handlers.d.ts.map +1 -1
  63. package/dist/a2a/handlers.js +100 -8
  64. package/dist/a2a/handlers.js.map +1 -1
  65. package/dist/a2a/index.d.ts +1 -1
  66. package/dist/a2a/index.d.ts.map +1 -1
  67. package/dist/a2a/index.js.map +1 -1
  68. package/dist/a2a/types.d.ts +12 -0
  69. package/dist/a2a/types.d.ts.map +1 -1
  70. package/dist/a2a/types.js.map +1 -1
  71. package/dist/collab/routes.d.ts +1 -1
  72. package/dist/collab/struct-routes.d.ts +1 -1
  73. package/dist/db/request-telemetry.d.ts.map +1 -1
  74. package/dist/db/request-telemetry.js +24 -6
  75. package/dist/db/request-telemetry.js.map +1 -1
  76. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  77. package/dist/integrations/index.d.ts +1 -0
  78. package/dist/integrations/index.d.ts.map +1 -1
  79. package/dist/integrations/index.js +1 -0
  80. package/dist/integrations/index.js.map +1 -1
  81. package/dist/integrations/pending-tasks-store.d.ts +9 -0
  82. package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
  83. package/dist/integrations/pending-tasks-store.js +60 -0
  84. package/dist/integrations/pending-tasks-store.js.map +1 -1
  85. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  86. package/dist/integrations/webhook-handler.js +13 -7
  87. package/dist/integrations/webhook-handler.js.map +1 -1
  88. package/dist/notifications/routes.d.ts +2 -2
  89. package/dist/observability/routes.d.ts +3 -3
  90. package/dist/scripts/call-agent.d.ts.map +1 -1
  91. package/dist/scripts/call-agent.js +39 -10
  92. package/dist/scripts/call-agent.js.map +1 -1
  93. package/dist/secrets/routes.d.ts +9 -9
  94. package/dist/server/agent-discovery.d.ts +2 -0
  95. package/dist/server/agent-discovery.d.ts.map +1 -1
  96. package/dist/server/agent-discovery.js +19 -0
  97. package/dist/server/agent-discovery.js.map +1 -1
  98. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  99. package/dist/settings/store.d.ts +1 -1
  100. package/dist/settings/store.d.ts.map +1 -1
  101. package/dist/settings/store.js +14 -6
  102. package/dist/settings/store.js.map +1 -1
  103. package/dist/shared/optional-node-builtins.d.ts +23 -0
  104. package/dist/shared/optional-node-builtins.d.ts.map +1 -0
  105. package/dist/shared/optional-node-builtins.js +35 -0
  106. package/dist/shared/optional-node-builtins.js.map +1 -0
  107. package/dist/templates/chat/.agents/skills/feature-flags/SKILL.md +1 -1
  108. package/dist/templates/default/.agents/skills/feature-flags/SKILL.md +1 -1
  109. package/dist/templates/headless/.agents/skills/feature-flags/SKILL.md +1 -1
  110. package/dist/templates/workspace-core/.agents/skills/feature-flags/SKILL.md +1 -1
  111. package/dist/vite/client.d.ts.map +1 -1
  112. package/dist/vite/client.js +2 -0
  113. package/dist/vite/client.js.map +1 -1
  114. package/package.json +2 -1
  115. package/src/a2a/artifact-response.ts +42 -16
  116. package/src/a2a/client.ts +4 -0
  117. package/src/a2a/handlers.ts +133 -2
  118. package/src/a2a/index.ts +1 -0
  119. package/src/a2a/types.ts +14 -0
  120. package/src/db/request-telemetry.ts +32 -8
  121. package/src/integrations/index.ts +6 -0
  122. package/src/integrations/pending-tasks-store.ts +89 -0
  123. package/src/integrations/webhook-handler.ts +17 -7
  124. package/src/scripts/call-agent.ts +54 -10
  125. package/src/server/agent-discovery.ts +22 -0
  126. package/src/settings/store.ts +15 -6
  127. package/src/shared/optional-node-builtins.ts +66 -0
  128. package/src/templates/chat/.agents/skills/feature-flags/SKILL.md +1 -1
  129. package/src/templates/default/.agents/skills/feature-flags/SKILL.md +1 -1
  130. package/src/templates/headless/.agents/skills/feature-flags/SKILL.md +1 -1
  131. package/src/templates/workspace-core/.agents/skills/feature-flags/SKILL.md +1 -1
  132. package/src/vite/client.ts +5 -0
@@ -34,6 +34,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
34
34
  declare var __DESIGN_CANVAS_CONTENT_OFFSET_X__: number;
35
35
  declare var __DESIGN_CANVAS_CONTENT_OFFSET_Y__: number;
36
36
  declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
37
+ declare var __LIVE_REFLOW_ENABLED__: boolean;
37
38
 
38
39
  (function () {
39
40
  // Idempotency guard: replace-document-content / srcdoc rebuilds can end up
@@ -62,8 +63,58 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
62
63
  var designCanvasContentOffsetY =
63
64
  Number(__DESIGN_CANVAS_CONTENT_OFFSET_Y__) || 0;
64
65
  var runtimeLayerSnapshotEnabled = !!__RUNTIME_LAYER_SNAPSHOT_ENABLED__;
66
+ // Figma-parity live-reflow drag (Phase 0 + 1: hysteresis-stabilized target
67
+ // resolution, size guard, transform lift/follow, live sibling reflow,
68
+ // exact absolute commit). Gated so it can be flipped off without a revert.
69
+ // The placeholder is referenced EXACTLY ONCE so the host's single
70
+ // String.replace fully substitutes it; the try/catch keeps it crash-safe if
71
+ // an injection site forgets to replace it (an un-replaced identifier read
72
+ // throws ReferenceError, which we treat as "off") — the behavior is additive
73
+ // and rolling out incrementally.
74
+ var liveReflowEnabled = (function () {
75
+ try {
76
+ return !!__LIVE_REFLOW_ENABLED__;
77
+ } catch (_e) {
78
+ return false;
79
+ }
80
+ })();
65
81
  var scaleToolEnabled = false;
66
82
 
83
+ // ── Drag-and-drop debug logging ────────────────────────────────────
84
+ // Every DnD seam logs under the "[dnd]" prefix with a phase tag, so the
85
+ // console reads as a narrated timeline of ONE gesture:
86
+ // start:* → target → lift/reflow → commit:* → post:* → ack:*
87
+ // The iframe posts these to its own console; the host mirrors its side
88
+ // under the same prefix. Defaults OFF; opt in at runtime from the iframe
89
+ // console with `window.__DND_DEBUG = true` — no rebuild needed.
90
+ function dndLog(phase: string, data?: unknown): void {
91
+ if (!(window as any).__DND_DEBUG) return;
92
+ try {
93
+ var tag = "%c[dnd:" + phase + "]";
94
+ var style = "color:#8b5cf6;font-weight:bold";
95
+ if (data === undefined) console.log(tag, style);
96
+ else console.log(tag, style, data);
97
+ } catch (_e) {}
98
+ }
99
+ // Describe a resolved drop target compactly for the log timeline. Reads
100
+ // getSelector/dropContainerForTarget (declared later, hoisted) at call time.
101
+ function dndTarget(t): unknown {
102
+ if (!t) return null;
103
+ try {
104
+ var container = dropContainerForTarget(t);
105
+ return {
106
+ anchor: t.anchor ? getSelector(t.anchor) : null,
107
+ placement: t.placement,
108
+ dropMode: t.dropMode,
109
+ axis: t.axis,
110
+ container: container ? getSelector(container) : null,
111
+ needsConversion: !!t.needsAutoLayoutConversion,
112
+ };
113
+ } catch (_e) {
114
+ return { placement: t.placement, dropMode: t.dropMode };
115
+ }
116
+ }
117
+
67
118
  // Interaction-state forced preview (phase 2 — see shared/interaction-states.ts's
68
119
  // "Forced-preview mechanism" doc comment). Keep the actual element rather
69
120
  // than only its node id: localhost React layers can resolve through runtime
@@ -1961,18 +2012,30 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
1961
2012
 
1962
2013
  var transformBadge = document.createElement("div");
1963
2014
  transformBadge.setAttribute("data-agent-native-transform-badge", "");
2015
+ // Classify as edit-overlay so the content-stamp pass (isEditorChrome) does
2016
+ // not strip it and replaceRuntimeDocument preserves it — otherwise the badge
2017
+ // is detached from the DOM and its styling targets a dead node (invisible).
2018
+ transformBadge.setAttribute(
2019
+ "data-agent-native-edit-overlay",
2020
+ "transform-badge",
2021
+ );
1964
2022
  transformBadge.style.cssText =
1965
2023
  "position:fixed;z-index:100000;display:none;pointer-events:none;border:1px solid rgba(255,255,255,0.16);border-radius:4px;background:rgba(24,24,27,0.96);color:rgba(255,255,255,0.96);font:11px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;padding:3px 5px;box-shadow:0 8px 20px rgba(0,0,0,0.28);";
1966
2024
  document.body.appendChild(transformBadge);
1967
2025
 
1968
2026
  var spacingBadge = document.createElement("div");
1969
2027
  spacingBadge.setAttribute("data-agent-native-spacing-badge", "");
2028
+ spacingBadge.setAttribute("data-agent-native-edit-overlay", "spacing-badge");
1970
2029
  spacingBadge.style.cssText =
1971
2030
  "position:fixed;z-index:100000;display:none;pointer-events:none;border-radius:3px;color:white;font:10px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700;padding:2px 4px;box-shadow:0 4px 14px rgba(0,0,0,0.18);";
1972
2031
  document.body.appendChild(spacingBadge);
1973
2032
 
1974
2033
  var insertionGuide = document.createElement("div");
1975
2034
  insertionGuide.setAttribute("data-agent-native-insertion-guide", "");
2035
+ insertionGuide.setAttribute(
2036
+ "data-agent-native-edit-overlay",
2037
+ "insertion-guide",
2038
+ );
1976
2039
  insertionGuide.style.cssText =
1977
2040
  "position:fixed;z-index:100000;display:none;pointer-events:none;background:var(--design-editor-accent-color);border-radius:999px;box-shadow:0 0 0 1px var(--design-editor-accent-color);";
1978
2041
  document.body.appendChild(insertionGuide);
@@ -6786,6 +6849,7 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
6786
6849
  el?: Element | null,
6787
6850
  ev?: { clientX?: number; clientY?: number } | null,
6788
6851
  ): void {
6852
+ dndLog("post:cross-screen", { phase: phase, el: getSelector(el ?? null) });
6789
6853
  if (phase === "cancel") {
6790
6854
  activeCrossScreenStyleSnapshot = undefined;
6791
6855
  (window.parent as Window).postMessage(
@@ -6921,6 +6985,17 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
6921
6985
  return true;
6922
6986
  }
6923
6987
 
6988
+ // Figma R11: an element whose entire content is text / inline leaves is a
6989
+ // text node, not a frame — it must never swallow a flow-reordered sibling as
6990
+ // a child. Used to keep the flow-reorder resolver on before/after for text
6991
+ // cards (§1.1/§1.2 nest-into-text + silent flex-conversion bug). A truly
6992
+ // empty element (no text, no children) is NOT a text leaf — it stays a valid
6993
+ // empty frame you can drop into, and the absolute-drag nest path is
6994
+ // unaffected (it targets frames, not reordered flow siblings).
6995
+ function isTextBearingLeaf(el: Element): boolean {
6996
+ return hasOnlyLeafContent(el) && (el.textContent || "").trim().length > 0;
6997
+ }
6998
+
6924
6999
  function isContainerDropTarget(el: Element | null): boolean {
6925
7000
  if (!el || el === document.documentElement) return false;
6926
7001
  if (isOverlayElement(el) || isLayerInteractionBlocked(el)) return false;
@@ -7113,7 +7188,7 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
7113
7188
  !isOverlayElement(hit) &&
7114
7189
  !isTemplateCloneElement(hit)
7115
7190
  ) {
7116
- if (isContainerDropTarget(hit)) {
7191
+ if (isContainerDropTarget(hit) && !isTextBearingLeaf(hit)) {
7117
7192
  var containerRect = hit.getBoundingClientRect();
7118
7193
  var edgeAxis = hit.parentElement
7119
7194
  ? parentFlowAxis(hit.parentElement)
@@ -7294,8 +7369,9 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
7294
7369
  };
7295
7370
  }
7296
7371
 
7297
- // Flow child dropped into a plain container: match the absolute-drag path
7298
- // by converting that container to auto layout before the structural move.
7372
+ // Flow child (a real flow-reorder gesture) dropped into a plain container:
7373
+ // convert it to auto layout before the structural move. This is the flow
7374
+ // path only; the absolute/free drag keeps shapes free (no conversion).
7299
7375
  if (
7300
7376
  target &&
7301
7377
  target.dropMode === "flow-insert" &&
@@ -7471,11 +7547,21 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
7471
7547
  isContainerDropTarget(cursor) &&
7472
7548
  !(parent && parent !== document.body && isAutoLayoutElement(parent))
7473
7549
  ) {
7474
- // B5-4 (absolute path): pointer over the container's own
7475
- // background its padding or a gap between children. Prefer the
7476
- // nearest child slot (insertion line at the pointer index) over
7477
- // plain "inside" (which appends after the last child); fall back
7478
- // to "inside" only for containers with no visible children.
7550
+ // Free (absolute) element into a non-auto-layout container stays free:
7551
+ // nest as an absolute child at the drop point, never convert to flex.
7552
+ // Same-parent drop is a pure reposition (null onUp writes left/top).
7553
+ if (!isAutoLayoutElement(cursor)) {
7554
+ if (cursor === el.parentElement) return null;
7555
+ return {
7556
+ anchor: cursor,
7557
+ placement: "inside",
7558
+ axis: "y",
7559
+ dropMode: "absolute-container",
7560
+ };
7561
+ }
7562
+ // B5-4: pointer over an auto-layout container's background (padding or a
7563
+ // gap). Prefer the nearest child slot over plain "inside" (append last);
7564
+ // fall back to "inside" for an empty container.
7479
7565
  var betweenContainerChildren = nearestChildInsertionTarget(
7480
7566
  cursor,
7481
7567
  clientX,
@@ -7488,8 +7574,6 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
7488
7574
  placement: betweenContainerChildren.placement,
7489
7575
  axis: betweenContainerChildren.axis,
7490
7576
  dropMode: "flow-insert",
7491
- needsAutoLayoutConversion: !isAutoLayoutElement(cursor),
7492
- conversionTarget: cursor,
7493
7577
  };
7494
7578
  }
7495
7579
  return {
@@ -7497,11 +7581,21 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
7497
7581
  placement: "inside",
7498
7582
  axis: parentFlowAxis(cursor),
7499
7583
  dropMode: "flow-insert",
7500
- needsAutoLayoutConversion: !isAutoLayoutElement(cursor),
7501
- conversionTarget: cursor,
7502
7584
  };
7503
7585
  }
7504
7586
  if (parent && parent !== document.body && isContainerDropTarget(parent)) {
7587
+ // Free element over a sibling in a non-auto-layout parent stays free:
7588
+ // absolute child at the drop point (same-parent → null reposition).
7589
+ if (!isAutoLayoutElement(parent)) {
7590
+ if (parent === el.parentElement) return null;
7591
+ return {
7592
+ anchor: parent,
7593
+ placement: "inside",
7594
+ axis: "y",
7595
+ dropMode: "absolute-container",
7596
+ };
7597
+ }
7598
+ // parent is an established auto-layout list: reorder within its flow.
7505
7599
  // Anchor-candidate gate: cursor is a plain sibling under `parent`
7506
7600
  // being used as a before/after anchor — but if it's a template
7507
7601
  // clone (no counterpart in source HTML), fall back to the nearest
@@ -7521,8 +7615,6 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
7521
7615
  placement: cloneFallback.placement,
7522
7616
  axis: cloneFallback.axis,
7523
7617
  dropMode: "flow-insert",
7524
- needsAutoLayoutConversion: !isAutoLayoutElement(parent),
7525
- conversionTarget: parent,
7526
7618
  };
7527
7619
  }
7528
7620
  return {
@@ -7530,8 +7622,6 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
7530
7622
  placement: "inside",
7531
7623
  axis: parentFlowAxis(parent),
7532
7624
  dropMode: "flow-insert",
7533
- needsAutoLayoutConversion: !isAutoLayoutElement(parent),
7534
- conversionTarget: parent,
7535
7625
  };
7536
7626
  }
7537
7627
  var parentAxis = parentFlowAxis(parent);
@@ -7546,8 +7636,6 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
7546
7636
  placement: childPointer < childCenter ? "before" : "after",
7547
7637
  axis: parentAxis,
7548
7638
  dropMode: "flow-insert",
7549
- needsAutoLayoutConversion: !isAutoLayoutElement(parent),
7550
- conversionTarget: parent,
7551
7639
  };
7552
7640
  }
7553
7641
  cursor = parent;
@@ -7986,6 +8074,12 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
7986
8074
 
7987
8075
  function postVisualStructureChange(el, target, origin) {
7988
8076
  if (!el || !target || !target.anchor) return;
8077
+ dndLog("post:structure-change", {
8078
+ el: getSelector(el),
8079
+ anchor: getSelector(target.anchor),
8080
+ placement: target.placement,
8081
+ dropMode: target.dropMode || "flow-insert",
8082
+ });
7989
8083
  var requestId =
7990
8084
  "move-" + Date.now() + "-" + Math.random().toString(16).slice(2);
7991
8085
  pendingStructureMoves[requestId] = {
@@ -8411,6 +8505,7 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
8411
8505
  };
8412
8506
  });
8413
8507
  var reorderGestureStartRect = reorderEl.getBoundingClientRect();
8508
+ var reorderLastTargetKey = null;
8414
8509
  var keepCurrentFlowParent = bridgeSpaceKeyPressed;
8415
8510
  var currentTarget = flowMoveTargetForPoint(
8416
8511
  reorderEl,
@@ -8421,6 +8516,12 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
8421
8516
  Boolean(e.ctrlKey),
8422
8517
  );
8423
8518
  showInsertionGuideFor(currentTarget);
8519
+ dndLog("start:reorder", {
8520
+ el: getSelector(reorderEl),
8521
+ isGroup: isGroupDrag,
8522
+ ctrl: Boolean(e.ctrlKey),
8523
+ target: dndTarget(currentTarget),
8524
+ });
8424
8525
  // Cross-screen drag state: true when the pointer is outside this iframe's
8425
8526
  // viewport bounds. The host frame renders the ghost + highlight and owns
8426
8527
  // the drop when this is true; the bridge suppresses its in-iframe reorder.
@@ -8434,11 +8535,355 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
8434
8535
  x: reorderPointerStart.clientX - reorderRect.left,
8435
8536
  y: reorderPointerStart.clientY - reorderRect.top,
8436
8537
  };
8538
+ // Transform-only follow: must be cleared before any pointer-up commit
8539
+ // reads getBoundingClientRect, or the drag delta corrupts the result.
8540
+ function authoredTransformOf(el: HTMLElement): string {
8541
+ // The element's own transform to compose the drag translate WITH: inline
8542
+ // if set, else the class/stylesheet value so a drag never wipes an
8543
+ // authored rotate/scale. "none" → "" so we don't emit an identity.
8544
+ if (el.style.transform) return el.style.transform;
8545
+ var computed = window.getComputedStyle(el).transform;
8546
+ return computed && computed !== "none" ? computed : "";
8547
+ }
8548
+ var reorderLiftedMembers: {
8549
+ el: HTMLElement;
8550
+ prevTransform: string;
8551
+ authoredTransform: string;
8552
+ prevTransition: string;
8553
+ prevZIndex: string;
8554
+ prevBoxShadow: string;
8555
+ prevWillChange: string;
8556
+ prevPointerEvents: string;
8557
+ }[] = [];
8558
+ function applyReorderLift(dx: number, dy: number): void {
8559
+ if (!liveReflowEnabled) return;
8560
+ groupEls.forEach(function (member) {
8561
+ var el = member as HTMLElement;
8562
+ var snap = reorderLiftedMembers.filter(function (s) {
8563
+ return s.el === el;
8564
+ })[0];
8565
+ if (!snap) {
8566
+ snap = {
8567
+ el: el,
8568
+ prevTransform: el.style.transform,
8569
+ authoredTransform: authoredTransformOf(el),
8570
+ prevTransition: el.style.transition,
8571
+ prevZIndex: el.style.zIndex,
8572
+ prevBoxShadow: el.style.boxShadow,
8573
+ prevWillChange: el.style.willChange,
8574
+ prevPointerEvents: el.style.pointerEvents,
8575
+ };
8576
+ reorderLiftedMembers.push(snap);
8577
+ el.style.transition = "none";
8578
+ el.style.willChange = "transform";
8579
+ el.style.zIndex = "2147483646";
8580
+ el.style.boxShadow = "0 8px 24px rgba(0, 0, 0, 0.18)";
8581
+ // Hit-test through the lifted element so it never resolves as its
8582
+ // own drop target while following the cursor.
8583
+ el.style.pointerEvents = "none";
8584
+ }
8585
+ // Translate FIRST so movement is in screen space (an authored rotate
8586
+ // would otherwise send the drag off-axis), composed with the element's
8587
+ // own transform (inline OR class/stylesheet) so the drag never wipes
8588
+ // it.
8589
+ el.style.transform =
8590
+ "translate(" +
8591
+ dx +
8592
+ "px, " +
8593
+ dy +
8594
+ "px)" +
8595
+ (snap.authoredTransform ? " " + snap.authoredTransform : "");
8596
+ });
8597
+ }
8598
+ function clearReorderLift(): void {
8599
+ reorderLiftedMembers.forEach(function (snap) {
8600
+ var el = snap.el;
8601
+ el.style.transform = snap.prevTransform;
8602
+ el.style.transition = snap.prevTransition;
8603
+ el.style.zIndex = snap.prevZIndex;
8604
+ el.style.boxShadow = snap.prevBoxShadow;
8605
+ el.style.willChange = snap.prevWillChange;
8606
+ el.style.pointerEvents = snap.prevPointerEvents;
8607
+ });
8608
+ reorderLiftedMembers = [];
8609
+ }
8610
+ // Live sibling reflow, restricted to same-container simple-packed flex so
8611
+ // a constant per-sibling shift always matches the real drop; ported from
8612
+ // shared/drag-reflow.ts.
8613
+ var reorderCommittedTarget: any = null;
8614
+ var reorderCommittedSlot: number | null = null;
8615
+ var reorderCommittedAt = 0;
8616
+ var reorderCommittedPointer = { x: 0, y: 0 };
8617
+ var reorderPendingSlot: number | null = null;
8618
+ var reorderPendingAt = 0;
8619
+ var reflowSiblings: {
8620
+ el: HTMLElement;
8621
+ prevTransform: string;
8622
+ authoredTransform: string;
8623
+ prevTransition: string;
8624
+ }[] = [];
8625
+ var reflowKey: string | null = null;
8626
+ var packedCacheContainer: Element | null = null;
8627
+ var packedCacheResult = false;
8628
+ function reorderMainAxis(target): "x" | "y" {
8629
+ return target && target.axis === "y" ? "y" : "x";
8630
+ }
8631
+ function reorderRealChildren(container: Element): Element[] {
8632
+ var out: Element[] = [];
8633
+ var kids = container.children;
8634
+ for (var i = 0; i < kids.length; i += 1) {
8635
+ var k = kids[i];
8636
+ if (k.nodeType === 1 && !isOverlayElement(k)) out.push(k);
8637
+ }
8638
+ return out;
8639
+ }
8640
+ function reorderSlotForTarget(target, real: Element[]) {
8641
+ if (target.placement === "inside") return { slot: real.length };
8642
+ var ai = real.indexOf(target.anchor);
8643
+ if (ai < 0) return null;
8644
+ return { slot: target.placement === "before" ? ai : ai + 1 };
8645
+ }
8646
+ function containerIsSimplePacked(container: Element): boolean {
8647
+ if (packedCacheContainer === container) return packedCacheResult;
8648
+ packedCacheContainer = container;
8649
+ packedCacheResult = false;
8650
+ var cs = window.getComputedStyle(container);
8651
+ if (cs.display !== "flex" && cs.display !== "inline-flex") return false;
8652
+ if (cs.flexDirection !== "row" && cs.flexDirection !== "column") {
8653
+ return false;
8654
+ }
8655
+ if (cs.flexWrap !== "nowrap") return false;
8656
+ var jc = cs.justifyContent;
8657
+ if (
8658
+ jc !== "flex-start" &&
8659
+ jc !== "start" &&
8660
+ jc !== "normal" &&
8661
+ jc !== "left" &&
8662
+ jc !== ""
8663
+ ) {
8664
+ return false;
8665
+ }
8666
+ var kids = container.children;
8667
+ for (var i = 0; i < kids.length; i += 1) {
8668
+ if (kids[i].nodeType !== 1) continue;
8669
+ if (parseFloat(window.getComputedStyle(kids[i]).flexGrow) > 0) {
8670
+ return false;
8671
+ }
8672
+ }
8673
+ packedCacheResult = true;
8674
+ return true;
8675
+ }
8676
+ function reorderMainGap(container: Element, axis: "x" | "y"): number {
8677
+ var cs = window.getComputedStyle(container);
8678
+ var raw = axis === "x" ? cs.columnGap || cs.gap : cs.rowGap || cs.gap;
8679
+ var n = readPx(raw);
8680
+ return Number.isFinite(n) && n > 0 ? n : 0;
8681
+ }
8682
+ function clearReorderReflow(): void {
8683
+ reflowSiblings.forEach(function (s) {
8684
+ s.el.style.transform = s.prevTransform;
8685
+ s.el.style.transition = s.prevTransition;
8686
+ });
8687
+ reflowSiblings = [];
8688
+ reflowKey = null;
8689
+ }
8690
+ // Auto-layout children reorder into a slot on plain drag — they have no
8691
+ // free x/y without leaving the layout, which would collapse it. Ctrl
8692
+ // "ignore auto layout" is the explicit free-place escape.
8693
+ function resolveReorderOrFreeTarget(cx, cy, ctrlKey) {
8694
+ return flowMoveTargetForPoint(
8695
+ reorderEl,
8696
+ cx,
8697
+ cy,
8698
+ groupOthers,
8699
+ keepCurrentFlowParent,
8700
+ ctrlKey,
8701
+ );
8702
+ }
8703
+ // Figma's "don't nest into a smaller container" guard (⌘/Ctrl overrides).
8704
+ // Instead of nesting into a too-small container, fall back to placing
8705
+ // beside it in its parent so the drop is never silently discarded.
8706
+ function applyReorderSizeGuard(target, ev) {
8707
+ if (!liveReflowEnabled || !target || target.placement !== "inside") {
8708
+ return target;
8709
+ }
8710
+ if (ev && (ev.metaKey || ev.ctrlKey)) return target;
8711
+ var container = dropContainerForTarget(target);
8712
+ // Never treat the screen root (body/html) as a too-small container: the
8713
+ // before/after fallback would reparent to documentElement and insert a
8714
+ // full-size layer as a sibling of <body>. Guard nested frames only.
8715
+ if (
8716
+ !container ||
8717
+ container === reorderEl ||
8718
+ container === document.body ||
8719
+ container === document.documentElement
8720
+ ) {
8721
+ return target;
8722
+ }
8723
+ var crect = container.getBoundingClientRect();
8724
+ var drect = (reorderEl as HTMLElement).getBoundingClientRect();
8725
+ if (crect.width >= drect.width && crect.height >= drect.height) {
8726
+ return target;
8727
+ }
8728
+ var parent = container.parentElement;
8729
+ if (!parent) return null;
8730
+ var pcs = window.getComputedStyle(parent);
8731
+ var pAxis =
8732
+ pcs.flexDirection === "column" ||
8733
+ pcs.flexDirection === "column-reverse"
8734
+ ? "y"
8735
+ : "x";
8736
+ var center =
8737
+ pAxis === "x"
8738
+ ? crect.left + crect.width / 2
8739
+ : crect.top + crect.height / 2;
8740
+ var ptr =
8741
+ pAxis === "x" ? (ev ? ev.clientX : center) : ev ? ev.clientY : center;
8742
+ return {
8743
+ anchor: container,
8744
+ placement: ptr < center ? "before" : "after",
8745
+ axis: pAxis,
8746
+ dropMode: "flow-insert",
8747
+ };
8748
+ }
8749
+ // Stabilize a freshly-resolved target against the committed one so the
8750
+ // preview only moves on a deliberate ≥8px pointer move from the last
8751
+ // commit or after the NEW candidate persists ≥60ms. Mirrors
8752
+ // shared/drag-reflow.ts resolveTargetHysteresis; same-container only.
8753
+ function stabilizeReorderTarget(rawTarget, cx, cy, now) {
8754
+ var reset = function () {
8755
+ reorderCommittedTarget = null;
8756
+ reorderCommittedSlot = null;
8757
+ reorderPendingSlot = null;
8758
+ };
8759
+ if (
8760
+ !liveReflowEnabled ||
8761
+ !rawTarget ||
8762
+ rawTarget.dropMode !== "flow-insert"
8763
+ ) {
8764
+ reset();
8765
+ return rawTarget;
8766
+ }
8767
+ var container = dropContainerForTarget(rawTarget);
8768
+ if (
8769
+ !container ||
8770
+ (reorderEl as HTMLElement).parentElement !== container
8771
+ ) {
8772
+ reset();
8773
+ return rawTarget;
8774
+ }
8775
+ var slotInfo = reorderSlotForTarget(
8776
+ rawTarget,
8777
+ reorderRealChildren(container),
8778
+ );
8779
+ if (!slotInfo) {
8780
+ reset();
8781
+ return rawTarget;
8782
+ }
8783
+ var slot = slotInfo.slot;
8784
+ var commit = function () {
8785
+ reorderCommittedSlot = slot;
8786
+ reorderCommittedTarget = rawTarget;
8787
+ reorderCommittedPointer = { x: cx, y: cy };
8788
+ reorderCommittedAt = now;
8789
+ reorderPendingSlot = null;
8790
+ return rawTarget;
8791
+ };
8792
+ if (reorderCommittedSlot === null) return commit();
8793
+ if (slot === reorderCommittedSlot) {
8794
+ reorderCommittedTarget = rawTarget;
8795
+ reorderPendingSlot = null;
8796
+ return rawTarget;
8797
+ }
8798
+ if (reorderPendingSlot !== slot) {
8799
+ reorderPendingSlot = slot;
8800
+ reorderPendingAt = now;
8801
+ }
8802
+ var movedPx = Math.hypot(
8803
+ cx - reorderCommittedPointer.x,
8804
+ cy - reorderCommittedPointer.y,
8805
+ );
8806
+ if (movedPx >= 8 || now - reorderPendingAt >= 60) return commit();
8807
+ return reorderCommittedTarget || rawTarget;
8808
+ }
8809
+ function applyReorderReflow(target, cx, cy): void {
8810
+ if (!liveReflowEnabled) return;
8811
+ // Group members are each lifted and follow the cursor; also reflowing
8812
+ // them would double-transform and strand a residual on teardown, so
8813
+ // group drags stay indicator-only.
8814
+ if (isGroupDrag || !target || target.dropMode !== "flow-insert") {
8815
+ clearReorderReflow();
8816
+ return;
8817
+ }
8818
+ var container = dropContainerForTarget(target);
8819
+ if (
8820
+ !container ||
8821
+ (reorderEl as HTMLElement).parentElement !== container ||
8822
+ !containerIsSimplePacked(container)
8823
+ ) {
8824
+ clearReorderReflow();
8825
+ return;
8826
+ }
8827
+ var real = reorderRealChildren(container);
8828
+ var originIndex = real.indexOf(reorderEl);
8829
+ var slotInfo = reorderSlotForTarget(target, real);
8830
+ if (originIndex < 0 || !slotInfo) {
8831
+ clearReorderReflow();
8832
+ return;
8833
+ }
8834
+ var axis = reorderMainAxis(target);
8835
+ var key = axis + ":" + slotInfo.slot;
8836
+ if (key === reflowKey) return;
8837
+ clearReorderReflow();
8838
+ reflowKey = key;
8839
+ var drect = (reorderEl as HTMLElement).getBoundingClientRect();
8840
+ var slotMain =
8841
+ (axis === "x" ? drect.width : drect.height) +
8842
+ reorderMainGap(container, axis);
8843
+ // Only siblings between origin and target shift, by ±slotMain — exact
8844
+ // for a packed container regardless of each sibling's own size.
8845
+ var offsets: number[] = new Array(real.length).fill(0);
8846
+ if (slotInfo.slot > originIndex + 1) {
8847
+ for (var a = originIndex + 1; a <= slotInfo.slot - 1; a += 1) {
8848
+ offsets[a] = -slotMain;
8849
+ }
8850
+ } else if (slotInfo.slot < originIndex) {
8851
+ for (var b = slotInfo.slot; b <= originIndex - 1; b += 1) {
8852
+ offsets[b] = slotMain;
8853
+ }
8854
+ }
8855
+ for (var i = 0; i < real.length; i += 1) {
8856
+ if (i === originIndex) continue;
8857
+ var el = real[i] as HTMLElement;
8858
+ var prevTransform = el.style.transform;
8859
+ var authoredTransform = authoredTransformOf(el);
8860
+ reflowSiblings.push({
8861
+ el: el,
8862
+ prevTransform: prevTransform,
8863
+ authoredTransform: authoredTransform,
8864
+ prevTransition: el.style.transition,
8865
+ });
8866
+ el.style.transition = "transform 140ms cubic-bezier(0.2, 0, 0, 1)";
8867
+ var tx = axis === "x" ? offsets[i] : 0;
8868
+ var ty = axis === "y" ? offsets[i] : 0;
8869
+ // Translate FIRST (screen space) composed with the sibling's own
8870
+ // transform so an authored rotate/scale survives the reflow shift.
8871
+ el.style.transform =
8872
+ "translate(" +
8873
+ tx +
8874
+ "px, " +
8875
+ ty +
8876
+ "px)" +
8877
+ (authoredTransform ? " " + authoredTransform : "");
8878
+ }
8879
+ }
8437
8880
  function onReorderMove(ev) {
8438
8881
  var vw = window.innerWidth;
8439
8882
  var vh = window.innerHeight;
8440
8883
  var cx = ev.clientX;
8441
8884
  var cy = ev.clientY;
8885
+ var dx = cx - reorderPointerStart.clientX;
8886
+ var dy = cy - reorderPointerStart.clientY;
8442
8887
  var outside = cx < 0 || cy < 0 || cx > vw || cy > vh;
8443
8888
  pointerOutsideIframe = outside;
8444
8889
  // Always notify the host frame so it can track the cursor position,
@@ -8465,28 +8910,58 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
8465
8910
  if (outside && !isGroupDrag) {
8466
8911
  // Cursor left this iframe — hide the in-iframe insertion guide so
8467
8912
  // it does not render while the host shows a cross-screen drop target.
8913
+ // Drop the lift so the host-rendered ghost is the only moving visual
8914
+ // (re-applied automatically if the cursor comes back inside).
8468
8915
  hideInsertionGuide();
8916
+ clearReorderLift();
8917
+ clearReorderReflow();
8469
8918
  showTransformBadge("Move layer", cx, cy);
8470
8919
  } else {
8471
- // Cursor is inside this iframe — use existing in-iframe behavior.
8472
- currentTarget = flowMoveTargetForPoint(
8473
- reorderEl,
8920
+ // Cursor is inside this iframe — use existing in-iframe behavior,
8921
+ // stabilized (hysteresis) and previewed with live sibling reflow when
8922
+ // liveReflowEnabled. stabilizeReorderTarget / applyReorderReflow are
8923
+ // no-ops (pass-through) when the flag is off.
8924
+ var rawTarget = resolveReorderOrFreeTarget(
8474
8925
  cx,
8475
8926
  cy,
8476
- groupOthers,
8477
- keepCurrentFlowParent,
8478
8927
  Boolean(ev.ctrlKey),
8479
8928
  );
8929
+ rawTarget = applyReorderSizeGuard(rawTarget, ev);
8930
+ currentTarget = stabilizeReorderTarget(
8931
+ rawTarget,
8932
+ cx,
8933
+ cy,
8934
+ ev.timeStamp,
8935
+ );
8480
8936
  showInsertionGuideFor(currentTarget);
8937
+ var _dndKey = currentTarget
8938
+ ? getSelector(currentTarget.anchor) +
8939
+ "|" +
8940
+ currentTarget.placement +
8941
+ "|" +
8942
+ currentTarget.dropMode
8943
+ : "none";
8944
+ if (_dndKey !== reorderLastTargetKey) {
8945
+ reorderLastTargetKey = _dndKey;
8946
+ dndLog("target", dndTarget(currentTarget));
8947
+ }
8948
+ applyReorderLift(dx, dy);
8949
+ applyReorderReflow(currentTarget, cx, cy);
8481
8950
  showTransformBadge(currentTarget ? "Move layer" : "Move", cx, cy);
8482
8951
  }
8483
8952
  }
8484
8953
  function cleanupReorderDrag() {
8485
8954
  document.removeEventListener(events.move, onReorderMove, true);
8486
8955
  document.removeEventListener(events.up, onReorderUp, true);
8956
+ document.removeEventListener("pointercancel", onReorderEscape, true);
8487
8957
  document.removeEventListener("keydown", onReorderKeyDown, true);
8488
8958
  document.removeEventListener("keyup", onReorderKeyUp, true);
8489
8959
  clearActiveDragCancel(onReorderEscape);
8960
+ // onReorderUp calls this before resolving/reordering, so the commit
8961
+ // reads un-transformed rects and — one synchronous task — paints once
8962
+ // in the final slot with no back-to-origin flicker.
8963
+ clearReorderLift();
8964
+ clearReorderReflow();
8490
8965
  }
8491
8966
  function onReorderEscape() {
8492
8967
  cleanupReorderDrag();
@@ -8566,14 +9041,26 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
8566
9041
  // already clears cross-screen state on re-entry so checking the
8567
9042
  // momentary excursion flag here would wrongly drop the element nowhere.
8568
9043
  if (outsideOnDrop) return;
8569
- currentTarget = flowMoveTargetForPoint(
8570
- reorderEl,
8571
- cx,
8572
- cy,
8573
- groupOthers,
8574
- keepCurrentFlowParent,
8575
- Boolean(ev?.ctrlKey),
8576
- );
9044
+ // Resolve from the RELEASE point + release-time modifiers so a Ctrl or
9045
+ // Space held only at release still takes effect; live reflow then runs
9046
+ // one final stabilize tick so the drop still lands on the previewed
9047
+ // slot rather than jumping.
9048
+ var finalRaw = resolveReorderOrFreeTarget(cx, cy, Boolean(ev?.ctrlKey));
9049
+ currentTarget = liveReflowEnabled
9050
+ ? stabilizeReorderTarget(
9051
+ applyReorderSizeGuard(finalRaw, ev),
9052
+ cx,
9053
+ cy,
9054
+ ev && typeof ev.timeStamp === "number"
9055
+ ? ev.timeStamp
9056
+ : reorderCommittedAt,
9057
+ )
9058
+ : finalRaw;
9059
+ dndLog("commit:resolve", {
9060
+ raw: dndTarget(finalRaw),
9061
+ final: dndTarget(currentTarget),
9062
+ ctrl: Boolean(ev && ev.ctrlKey),
9063
+ });
8577
9064
  if (!currentTarget) {
8578
9065
  // No valid drop target — clean up the clone if one was inserted so
8579
9066
  // no ghost element is left in the DOM.
@@ -8657,6 +9144,12 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
8657
9144
  // visual feedback; the visual-structure-ack handler will confirm
8658
9145
  // or revert once the parent processes the change.
8659
9146
  applyRuntimeReorder(reorderEl, currentTarget);
9147
+ dndLog("commit:done", {
9148
+ el: getSelector(reorderEl),
9149
+ parent: reorderEl.parentElement
9150
+ ? getSelector(reorderEl.parentElement)
9151
+ : null,
9152
+ });
8660
9153
  postVisualStructureChange(reorderEl, currentTarget, {
8661
9154
  prevParent: prevParent,
8662
9155
  prevNextSibling: prevNextSibling,
@@ -8666,6 +9159,7 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
8666
9159
  }
8667
9160
  document.addEventListener(events.move, onReorderMove, true);
8668
9161
  document.addEventListener(events.up, onReorderUp, true);
9162
+ document.addEventListener("pointercancel", onReorderEscape, true);
8669
9163
  document.addEventListener("keydown", onReorderKeyDown, true);
8670
9164
  document.addEventListener("keyup", onReorderKeyUp, true);
8671
9165
  setActiveDragCancel(onReorderEscape);
@@ -8711,6 +9205,7 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
8711
9205
  var dragEl = gestureEl;
8712
9206
  var moved = false;
8713
9207
  var DRAG_THRESHOLD = 3;
9208
+ dndLog("start:free", { el: getSelector(gestureEl), isGroup: isGroupDrag });
8714
9209
  var currentAutoLayoutTarget: {
8715
9210
  anchor: Element;
8716
9211
  placement: string;
@@ -8973,25 +9468,10 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
8973
9468
  postVisualDuplicateChange(originalSelectedEl, dragEl);
8974
9469
  } else if (currentAutoLayoutTarget) {
8975
9470
  setMembersOpacity(null);
8976
- // Figma-parity nest-on-drop: the target container may be a plain
8977
- // rect/div that isn't auto-layout yet (see
8978
- // autoLayoutInsertionTargetForPoint's needsAutoLayoutConversion).
8979
- // Convert it to flex BEFORE reparenting/posting the move so the
8980
- // host applies the two edits in the right order against its
8981
- // synchronous same-tick content refs (container becomes flex, then
8982
- // the child moves into it and loses absolute positioning via the
8983
- // existing "flow-insert" dropMode handling). For group drags the
8984
- // conversion fires ONCE for the container; every member then nests
8985
- // consecutively via applyGroupStructureDrop.
8986
- if (
8987
- currentAutoLayoutTarget.needsAutoLayoutConversion &&
8988
- currentAutoLayoutTarget.conversionTarget
8989
- ) {
8990
- applyAutoLayoutConversionForDrop(
8991
- currentAutoLayoutTarget.conversionTarget,
8992
- groupEls,
8993
- );
8994
- }
9471
+ // Nest-on-drop: a free element nests as an absolute child of a plain
9472
+ // container ("absolute-container", keeps left/top) or flow-inserts into
9473
+ // an existing auto-layout frame. The resolver never requests an implicit
9474
+ // flex conversion, so there is none to apply here.
8995
9475
  if (isGroupDrag) {
8996
9476
  applyGroupStructureDrop(
8997
9477
  groupEls,
@@ -9023,6 +9503,10 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
9023
9503
  dropContainerForTarget(currentAutoLayoutTarget),
9024
9504
  );
9025
9505
  applyRuntimeReorder(dragEl, currentAutoLayoutTarget);
9506
+ dndLog("commit:free-nest", {
9507
+ el: getSelector(dragEl),
9508
+ target: dndTarget(currentAutoLayoutTarget),
9509
+ });
9026
9510
  postVisualStructureChange(dragEl, currentAutoLayoutTarget, {
9027
9511
  prevParent: prevParent,
9028
9512
  prevNextSibling: prevNextSibling,
@@ -9031,6 +9515,7 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
9031
9515
  }
9032
9516
  } else {
9033
9517
  setMembersOpacity(null);
9518
+ dndLog("commit:free-absolute", { count: memberStates.length });
9034
9519
  // Free absolute placement: one style-change message per member, in
9035
9520
  // order — the host composes them against its synchronous same-tick
9036
9521
  // content refs exactly like multi-property style commits.
@@ -9505,6 +9990,14 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
9505
9990
  var previousSelectedEl = selectedEl;
9506
9991
  selectedEl = target;
9507
9992
  positionOverlay(selectionOverlay, selectedEl);
9993
+ // A plain (non-shift) select on a fresh target collapses any prior
9994
+ // multi-selection, so a following drag can never inherit stale passive
9995
+ // members from an earlier gesture (phantom-passenger fix, §3.5). Shift
9996
+ // keeps + extends the set via the helper below. Only reached for
9997
+ // single-element drags — an intentional group drag returns earlier.
9998
+ if (!ev?.shiftKey && passiveSelectionEls.length) {
9999
+ setPassiveSelectionElements([]);
10000
+ }
9508
10001
  preservePreviousSelectedElementForShiftClick(
9509
10002
  previousSelectedEl,
9510
10003
  selectedEl,
@@ -11210,6 +11703,10 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
11210
11703
  return;
11211
11704
  }
11212
11705
  if (e.data.type === "visual-structure-ack") {
11706
+ dndLog("ack", {
11707
+ requestId: e.data.requestId,
11708
+ applied: Boolean(e.data.applied),
11709
+ });
11213
11710
  var move = pendingStructureMoves[e.data.requestId];
11214
11711
  if (!move) return;
11215
11712
  delete pendingStructureMoves[e.data.requestId];