@archie/devtools 0.1.8 → 0.1.10

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.
package/README.md CHANGED
@@ -156,6 +156,24 @@ Tests follow a colocated-by-feature convention:
156
156
 
157
157
  ## Release notes
158
158
 
159
+ ### 0.1.9
160
+
161
+ Fixes the `arrange-session-changed` index for a move onto a screen's own
162
+ root (0.1.8's screen-root translation) so it matches the direct
163
+ `ELEMENT_MOVED` event.
164
+
165
+ - **Screen-root move index.** `recordCommittedMove`'s `after` payload (the
166
+ one `serializeArrangeChange` reads as `event.payload.after.index` for the
167
+ `arrange-session-changed` change entry) carried the raw DOM-child index
168
+ (`command.to.index`) even when the drop resolved to a screen-root target,
169
+ instead of `screenRootTarget.index` — the position remapped to count only
170
+ the screen's own root children. The direct `element-moved` runtime event
171
+ already used the remapped index; only the session-change payload lagged.
172
+ In the common case (no runtime-injected chrome interleaved among a
173
+ screen's root children) the two indices coincide and nothing broke; the
174
+ mismatch only surfaced when chrome sat between screen-root children and
175
+ the drop slot was valid but not adjacent to the chrome node.
176
+
159
177
  ### 0.1.8
160
178
 
161
179
  Fixes two related cases where a palette drop or an in-canvas move could
@@ -5335,6 +5335,17 @@ var MIN_WIDTH = 40;
5335
5335
  var MIN_HEIGHT = 20;
5336
5336
  var INTERACTIVE_MIN = 8;
5337
5337
  var MAX_DEPTH = 12;
5338
+ var COLLAPSED_CONTAINER_HIT_MIN = 24;
5339
+ function collapsedContainerHitRect(el, rect) {
5340
+ if (el.children.length > 0 || el.textContent?.trim()) return null;
5341
+ if (getDirectEditorMeta(el)?.containerKind !== "plain") return null;
5342
+ if (el.getClientRects().length === 0) return null;
5343
+ const width = Math.max(rect.width, COLLAPSED_CONTAINER_HIT_MIN);
5344
+ const height = Math.max(rect.height, COLLAPSED_CONTAINER_HIT_MIN);
5345
+ const left = rect.left + rect.width / 2 - width / 2;
5346
+ const top = rect.top + rect.height / 2 - height / 2;
5347
+ return new DOMRect(left, top, width, height);
5348
+ }
5338
5349
  var _runtimeNodeIdMap = /* @__PURE__ */ new WeakMap();
5339
5350
  var _runtimeNodeIdCounter = 0;
5340
5351
  function getRuntimeNodeId(el) {
@@ -5486,7 +5497,8 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5486
5497
  const nodeId2 = resolveStableNodeId(el, usedNodeIds);
5487
5498
  usedNodeIds.add(nodeId2);
5488
5499
  el.setAttribute(DND_NODE_ID_ATTR, nodeId2);
5489
- if (isZeroRect) {
5500
+ const collapsedHitRect = isZeroRect && !isSvgRoot ? collapsedContainerHitRect(el, rect) : null;
5501
+ if (isZeroRect && !collapsedHitRect) {
5490
5502
  resolvedUnreported.add(el);
5491
5503
  } else {
5492
5504
  seenResolvedHosts.add(el);
@@ -5503,7 +5515,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5503
5515
  resolvedHostSelector: selector,
5504
5516
  resolutionKind: "direct-host",
5505
5517
  label: getLabel(el),
5506
- rect,
5518
+ rect: collapsedHitRect ?? rect,
5507
5519
  containerStrategy: parentEl2 && isFlexOrGridParent(parentEl2) ? resolveContainerStrategy(parentEl2) : null,
5508
5520
  eligibilitySource: smallEligibility,
5509
5521
  draggable: false,
@@ -7407,7 +7419,7 @@ function computeSlotBoundaryPoint(slot, container, projectionTree) {
7407
7419
  {
7408
7420
  let lineY;
7409
7421
  if (n === 0) {
7410
- lineY = container.rect.top;
7422
+ lineY = container.rect.top + container.rect.height / 2;
7411
7423
  } else if (i === 0) {
7412
7424
  lineY = nodeRect(children[0])?.top ?? container.rect.top;
7413
7425
  } else if (i >= n) {
@@ -7912,6 +7924,7 @@ function createDragIntentEngine(projection, activeId, options) {
7912
7924
  // src/client/dnd/design-mode/drop-line.ts
7913
7925
  var DROP_LINE_THICKNESS_PX = 2;
7914
7926
  var DROP_LINE_MIN_LENGTH_PX = 40;
7927
+ var EMPTY_CONTAINER_INSET_PX = 8;
7915
7928
  function slotDropLine(slot, container, projectionTree, sdx, sdy) {
7916
7929
  const T = DROP_LINE_THICKNESS_PX;
7917
7930
  const MIN = DROP_LINE_MIN_LENGTH_PX;
@@ -7945,6 +7958,14 @@ function slotDropLine(slot, container, projectionTree, sdx, sdy) {
7945
7958
  };
7946
7959
  }
7947
7960
  }
7961
+ if (container.children.length === 0 && container.rect.width > 2 * EMPTY_CONTAINER_INSET_PX + MIN) {
7962
+ return {
7963
+ x: container.rect.left + EMPTY_CONTAINER_INSET_PX - sdx,
7964
+ y: bp.y - sdy - T / 2,
7965
+ width: container.rect.width - 2 * EMPTY_CONTAINER_INSET_PX,
7966
+ height: T
7967
+ };
7968
+ }
7948
7969
  return {
7949
7970
  x: container.rect.left - sdx,
7950
7971
  y: bp.y - sdy - T / 2,
@@ -8154,6 +8175,27 @@ function computeAllowNest(nestPreview, isLargeDraggedNode, rowLocked) {
8154
8175
  const childId = nestPreview.slotId.split("::nest::")[1] ?? "";
8155
8176
  return !!childId && !isLargeDraggedNode;
8156
8177
  }
8178
+ function resolveContainerHold(params) {
8179
+ const { intent, previousIntent, sourceContainerId, projection, x, y } = params;
8180
+ if (intent.mode !== "reorder" || previousIntent?.mode !== "reorder") return intent;
8181
+ if (previousIntent.containerId === intent.containerId) return intent;
8182
+ if (intent.containerId === sourceContainerId) return intent;
8183
+ if (isProjectedDescendant(intent.containerId, previousIntent.containerId, projection.projectionTree)) {
8184
+ return intent;
8185
+ }
8186
+ const prev = projection.containerIndex.get(previousIntent.containerId);
8187
+ if (!prev) return intent;
8188
+ const inPrevExpanded = x >= prev.rect.left - CONTAINER_SWITCH_HYSTERESIS && x <= prev.rect.right + CONTAINER_SWITCH_HYSTERESIS && y >= prev.rect.top - CONTAINER_SWITCH_HYSTERESIS && y <= prev.rect.bottom + CONTAINER_SWITCH_HYSTERESIS;
8189
+ return inPrevExpanded ? previousIntent : intent;
8190
+ }
8191
+ function isProjectedDescendant(id, ancestorId, tree) {
8192
+ let parentId = tree.get(id)?.parentId ?? null;
8193
+ while (parentId) {
8194
+ if (parentId === ancestorId) return true;
8195
+ parentId = tree.get(parentId)?.parentId ?? null;
8196
+ }
8197
+ return false;
8198
+ }
8157
8199
  function useDragVisualLoop({
8158
8200
  activeDrag,
8159
8201
  refs,
@@ -8245,15 +8287,14 @@ function useDragVisualLoop({
8245
8287
  rafId = requestAnimationFrame(tick);
8246
8288
  return;
8247
8289
  }
8248
- const previousIntent = refs.lastIntentRef.current;
8249
- const isReturningToSource = intent.mode === "reorder" && intent.containerId === refs.sourceContainerIdRef.current;
8250
- if (!isReturningToSource && previousIntent?.mode === "reorder" && intent.mode === "reorder" && previousIntent.containerId !== intent.containerId) {
8251
- const prevContainer = refs.projectionRef.current.containerIndex.get(previousIntent.containerId);
8252
- if (prevContainer) {
8253
- const inPrevExpanded = adjustedPx >= prevContainer.rect.left - CONTAINER_SWITCH_HYSTERESIS && adjustedPx <= prevContainer.rect.right + CONTAINER_SWITCH_HYSTERESIS && adjustedPy >= prevContainer.rect.top - CONTAINER_SWITCH_HYSTERESIS && adjustedPy <= prevContainer.rect.bottom + CONTAINER_SWITCH_HYSTERESIS;
8254
- if (inPrevExpanded) intent = previousIntent;
8255
- }
8256
- }
8290
+ intent = resolveContainerHold({
8291
+ intent,
8292
+ previousIntent: refs.lastIntentRef.current,
8293
+ sourceContainerId: refs.sourceContainerIdRef.current,
8294
+ projection: refs.projectionRef.current,
8295
+ x: adjustedPx,
8296
+ y: adjustedPy
8297
+ });
8257
8298
  if (intent.mode === "nest") {
8258
8299
  setTargetPlane("nest");
8259
8300
  const targetId = intent.slotId.split("::nest::")[1] ?? "";
@@ -8577,7 +8618,7 @@ function recordCommittedMove(args) {
8577
8618
  commitIdentity: sourceCommitIdentity
8578
8619
  });
8579
8620
  const afterPayload = buildMovePayloadWithEffectiveIdentities({
8580
- placement: command.to,
8621
+ placement: screenRootTarget ? { ...command.to, index: screenRootTarget.index } : command.to,
8581
8622
  containerIdentity,
8582
8623
  beforeSiblingIdentity,
8583
8624
  afterSiblingIdentity,
@@ -10230,7 +10271,7 @@ function isPreviewOrigin(origin) {
10230
10271
  var DEFAULT_SCRIPT_ID = "archie-inspector-script";
10231
10272
  var R2_INSPECTOR_BASE_URL = "https://pub-bf238c278d9644b39ec34ecaca1d315c.r2.dev";
10232
10273
  function readBuildVersion() {
10233
- return "0.1.8" ? "0.1.8" : null;
10274
+ return "0.1.10" ? "0.1.10" : null;
10234
10275
  }
10235
10276
  var REMOTE_INSPECTOR_VERSION = readBuildVersion();
10236
10277
  var isRemoteInspectorUrlVersioned = REMOTE_INSPECTOR_VERSION !== null;
@@ -5300,6 +5300,17 @@ var MIN_WIDTH = 40;
5300
5300
  var MIN_HEIGHT = 20;
5301
5301
  var INTERACTIVE_MIN = 8;
5302
5302
  var MAX_DEPTH = 12;
5303
+ var COLLAPSED_CONTAINER_HIT_MIN = 24;
5304
+ function collapsedContainerHitRect(el, rect) {
5305
+ if (el.children.length > 0 || el.textContent?.trim()) return null;
5306
+ if (getDirectEditorMeta(el)?.containerKind !== "plain") return null;
5307
+ if (el.getClientRects().length === 0) return null;
5308
+ const width = Math.max(rect.width, COLLAPSED_CONTAINER_HIT_MIN);
5309
+ const height = Math.max(rect.height, COLLAPSED_CONTAINER_HIT_MIN);
5310
+ const left = rect.left + rect.width / 2 - width / 2;
5311
+ const top = rect.top + rect.height / 2 - height / 2;
5312
+ return new DOMRect(left, top, width, height);
5313
+ }
5303
5314
  var _runtimeNodeIdMap = /* @__PURE__ */ new WeakMap();
5304
5315
  var _runtimeNodeIdCounter = 0;
5305
5316
  function getRuntimeNodeId(el) {
@@ -5451,7 +5462,8 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5451
5462
  const nodeId2 = resolveStableNodeId(el, usedNodeIds);
5452
5463
  usedNodeIds.add(nodeId2);
5453
5464
  el.setAttribute(DND_NODE_ID_ATTR, nodeId2);
5454
- if (isZeroRect) {
5465
+ const collapsedHitRect = isZeroRect && !isSvgRoot ? collapsedContainerHitRect(el, rect) : null;
5466
+ if (isZeroRect && !collapsedHitRect) {
5455
5467
  resolvedUnreported.add(el);
5456
5468
  } else {
5457
5469
  seenResolvedHosts.add(el);
@@ -5468,7 +5480,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5468
5480
  resolvedHostSelector: selector,
5469
5481
  resolutionKind: "direct-host",
5470
5482
  label: getLabel(el),
5471
- rect,
5483
+ rect: collapsedHitRect ?? rect,
5472
5484
  containerStrategy: parentEl2 && isFlexOrGridParent(parentEl2) ? resolveContainerStrategy(parentEl2) : null,
5473
5485
  eligibilitySource: smallEligibility,
5474
5486
  draggable: false,
@@ -7372,7 +7384,7 @@ function computeSlotBoundaryPoint(slot, container, projectionTree) {
7372
7384
  {
7373
7385
  let lineY;
7374
7386
  if (n === 0) {
7375
- lineY = container.rect.top;
7387
+ lineY = container.rect.top + container.rect.height / 2;
7376
7388
  } else if (i === 0) {
7377
7389
  lineY = nodeRect(children[0])?.top ?? container.rect.top;
7378
7390
  } else if (i >= n) {
@@ -7877,6 +7889,7 @@ function createDragIntentEngine(projection, activeId, options) {
7877
7889
  // src/client/dnd/design-mode/drop-line.ts
7878
7890
  var DROP_LINE_THICKNESS_PX = 2;
7879
7891
  var DROP_LINE_MIN_LENGTH_PX = 40;
7892
+ var EMPTY_CONTAINER_INSET_PX = 8;
7880
7893
  function slotDropLine(slot, container, projectionTree, sdx, sdy) {
7881
7894
  const T = DROP_LINE_THICKNESS_PX;
7882
7895
  const MIN = DROP_LINE_MIN_LENGTH_PX;
@@ -7910,6 +7923,14 @@ function slotDropLine(slot, container, projectionTree, sdx, sdy) {
7910
7923
  };
7911
7924
  }
7912
7925
  }
7926
+ if (container.children.length === 0 && container.rect.width > 2 * EMPTY_CONTAINER_INSET_PX + MIN) {
7927
+ return {
7928
+ x: container.rect.left + EMPTY_CONTAINER_INSET_PX - sdx,
7929
+ y: bp.y - sdy - T / 2,
7930
+ width: container.rect.width - 2 * EMPTY_CONTAINER_INSET_PX,
7931
+ height: T
7932
+ };
7933
+ }
7913
7934
  return {
7914
7935
  x: container.rect.left - sdx,
7915
7936
  y: bp.y - sdy - T / 2,
@@ -8119,6 +8140,27 @@ function computeAllowNest(nestPreview, isLargeDraggedNode, rowLocked) {
8119
8140
  const childId = nestPreview.slotId.split("::nest::")[1] ?? "";
8120
8141
  return !!childId && !isLargeDraggedNode;
8121
8142
  }
8143
+ function resolveContainerHold(params) {
8144
+ const { intent, previousIntent, sourceContainerId, projection, x, y } = params;
8145
+ if (intent.mode !== "reorder" || previousIntent?.mode !== "reorder") return intent;
8146
+ if (previousIntent.containerId === intent.containerId) return intent;
8147
+ if (intent.containerId === sourceContainerId) return intent;
8148
+ if (isProjectedDescendant(intent.containerId, previousIntent.containerId, projection.projectionTree)) {
8149
+ return intent;
8150
+ }
8151
+ const prev = projection.containerIndex.get(previousIntent.containerId);
8152
+ if (!prev) return intent;
8153
+ const inPrevExpanded = x >= prev.rect.left - CONTAINER_SWITCH_HYSTERESIS && x <= prev.rect.right + CONTAINER_SWITCH_HYSTERESIS && y >= prev.rect.top - CONTAINER_SWITCH_HYSTERESIS && y <= prev.rect.bottom + CONTAINER_SWITCH_HYSTERESIS;
8154
+ return inPrevExpanded ? previousIntent : intent;
8155
+ }
8156
+ function isProjectedDescendant(id, ancestorId, tree) {
8157
+ let parentId = tree.get(id)?.parentId ?? null;
8158
+ while (parentId) {
8159
+ if (parentId === ancestorId) return true;
8160
+ parentId = tree.get(parentId)?.parentId ?? null;
8161
+ }
8162
+ return false;
8163
+ }
8122
8164
  function useDragVisualLoop({
8123
8165
  activeDrag,
8124
8166
  refs,
@@ -8210,15 +8252,14 @@ function useDragVisualLoop({
8210
8252
  rafId = requestAnimationFrame(tick);
8211
8253
  return;
8212
8254
  }
8213
- const previousIntent = refs.lastIntentRef.current;
8214
- const isReturningToSource = intent.mode === "reorder" && intent.containerId === refs.sourceContainerIdRef.current;
8215
- if (!isReturningToSource && previousIntent?.mode === "reorder" && intent.mode === "reorder" && previousIntent.containerId !== intent.containerId) {
8216
- const prevContainer = refs.projectionRef.current.containerIndex.get(previousIntent.containerId);
8217
- if (prevContainer) {
8218
- const inPrevExpanded = adjustedPx >= prevContainer.rect.left - CONTAINER_SWITCH_HYSTERESIS && adjustedPx <= prevContainer.rect.right + CONTAINER_SWITCH_HYSTERESIS && adjustedPy >= prevContainer.rect.top - CONTAINER_SWITCH_HYSTERESIS && adjustedPy <= prevContainer.rect.bottom + CONTAINER_SWITCH_HYSTERESIS;
8219
- if (inPrevExpanded) intent = previousIntent;
8220
- }
8221
- }
8255
+ intent = resolveContainerHold({
8256
+ intent,
8257
+ previousIntent: refs.lastIntentRef.current,
8258
+ sourceContainerId: refs.sourceContainerIdRef.current,
8259
+ projection: refs.projectionRef.current,
8260
+ x: adjustedPx,
8261
+ y: adjustedPy
8262
+ });
8222
8263
  if (intent.mode === "nest") {
8223
8264
  setTargetPlane("nest");
8224
8265
  const targetId = intent.slotId.split("::nest::")[1] ?? "";
@@ -8542,7 +8583,7 @@ function recordCommittedMove(args) {
8542
8583
  commitIdentity: sourceCommitIdentity
8543
8584
  });
8544
8585
  const afterPayload = buildMovePayloadWithEffectiveIdentities({
8545
- placement: command.to,
8586
+ placement: screenRootTarget ? { ...command.to, index: screenRootTarget.index } : command.to,
8546
8587
  containerIdentity,
8547
8588
  beforeSiblingIdentity,
8548
8589
  afterSiblingIdentity,
@@ -10195,7 +10236,7 @@ function isPreviewOrigin(origin) {
10195
10236
  var DEFAULT_SCRIPT_ID = "archie-inspector-script";
10196
10237
  var R2_INSPECTOR_BASE_URL = "https://pub-bf238c278d9644b39ec34ecaca1d315c.r2.dev";
10197
10238
  function readBuildVersion() {
10198
- return "0.1.8" ? "0.1.8" : null;
10239
+ return "0.1.10" ? "0.1.10" : null;
10199
10240
  }
10200
10241
  var REMOTE_INSPECTOR_VERSION = readBuildVersion();
10201
10242
  var isRemoteInspectorUrlVersioned = REMOTE_INSPECTOR_VERSION !== null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archie/devtools",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "DevTools for Archie generated applications - Route synchronization and editor communication",
5
5
  "sideEffects": false,
6
6
  "repository": {