@archie/devtools 0.1.7 → 0.1.8

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,41 @@ Tests follow a colocated-by-feature convention:
156
156
 
157
157
  ## Release notes
158
158
 
159
+ ### 0.1.8
160
+
161
+ Fixes two related cases where a palette drop or an in-canvas move could
162
+ land somewhere the host would later discard, instead of refusing the
163
+ gesture up front:
164
+
165
+ - **Screen-root translation.** A drop/insert that lands inside the app
166
+ shell's children-slot container (kit-internal/chrome,
167
+ `containerKind: 'expression'` — the element that renders `{children}`)
168
+ between the screen's own root nodes now translates to a screen-root
169
+ target instead of an ordinary container insert. The wire message
170
+ (`ELEMENT_INSERTED`/`ELEMENT_MOVED`, and `arrange-session-changed`'s
171
+ per-change entries) carries `screenRootFile` (the screen's file) and an
172
+ `index` remapped to the position among the screen's own root children —
173
+ `containerVid` is unchanged, still the observed DOM container.
174
+ - **Text/expression containers, and every other chrome/kit-internal
175
+ container.** Any OTHER drop/insert into app-shell/kit chrome, or into a
176
+ non-plain (`containerKind !== 'plain'`) element such as a text-bearing
177
+ `<p>`/`<h3>`/`<span>`, now refuses the gesture instead of completing —
178
+ the engine falls back to the nearest enclosing PLAIN container under the
179
+ pointer when one exists (e.g. the card `<div>` wrapping a paragraph), or
180
+ refuses the gesture entirely when none does. This also fixes a gap where
181
+ the external-palette-insert path (`useExternalDragLoop.ts`) never applied
182
+ the `NodeMeta`-derived chrome/kit-origin/non-plain blocking at all, unlike
183
+ the in-canvas move path — every palette drop onto text or app-shell chrome
184
+ used to be accepted and silently discarded by the host afterward.
185
+
186
+ The `screenRootFile` wire field is additive: a host whose own
187
+ `@8base-archie/editor-protocol` copy has not yet added
188
+ `DropMessage.screenRootFile` simply ignores it. This package does not
189
+ construct `InsertOp`/`MoveOp` itself, and does not import any
190
+ `@8base-archie/editor-protocol` schema at runtime (only two plain constants,
191
+ `VID_ATTR`/`VID_PATTERN` — everything else is `import type`), so it carries
192
+ no dependency-version coupling to this change.
193
+
159
194
  ### 0.1.5
160
195
 
161
196
  Treats a JSX comment (`{/* ... */}`) as a plain container, not an expression
@@ -4119,7 +4119,8 @@ function summarizeRuntimeEvent(detail) {
4119
4119
  diagnostics: detail.diagnostics ?? null,
4120
4120
  targetVid: detail.targetVid ?? null,
4121
4121
  containerVid: detail.containerVid ?? null,
4122
- index: detail.index
4122
+ index: detail.index,
4123
+ screenRootFile: detail.screenRootFile ?? null
4123
4124
  };
4124
4125
  case "drag-started":
4125
4126
  return { targetVid: detail.targetVid, droppableCount: detail.droppable.length };
@@ -4150,6 +4151,7 @@ function summarizeRuntimeEvent(detail) {
4150
4151
  containerVid: detail.containerVid ?? null,
4151
4152
  insertedVid: detail.insertedVid ?? null,
4152
4153
  index: detail.index,
4154
+ screenRootFile: detail.screenRootFile ?? null,
4153
4155
  beforeSibling: summarizeIdentity(detail.beforeSibling),
4154
4156
  hasTsxCode: !!detail.tsxCode,
4155
4157
  htmlLength: detail.html?.length ?? 0
@@ -4245,6 +4247,54 @@ function isNonDraggableNodeMeta(meta) {
4245
4247
  if (meta.isKitOrigin) return true;
4246
4248
  return false;
4247
4249
  }
4250
+ var SCREEN_FILE_RE = /(?:^|\/)screens\//;
4251
+ function isScreenRootMeta(meta) {
4252
+ return !!meta && !!meta.file && SCREEN_FILE_RE.test(meta.file) && meta.parentNodeId === null;
4253
+ }
4254
+ function isUnsavedScreenRootCandidate(nodeId) {
4255
+ return nodeId.startsWith("insert:");
4256
+ }
4257
+ function isScreenRootChild(nodeId, nodeMap) {
4258
+ const meta = nodeMap.get(nodeId)?.editorMeta;
4259
+ if (meta) return isScreenRootMeta(meta);
4260
+ return isUnsavedScreenRootCandidate(nodeId);
4261
+ }
4262
+ function isChildrenSlotContainer(meta) {
4263
+ if (!meta) return false;
4264
+ if (meta.containerKind !== "expression") return false;
4265
+ return !!(meta.isChrome || meta.isKitOrigin || meta.isKitContainer);
4266
+ }
4267
+ function findScreenRootHostFile(containerId, projection, nodeMap) {
4268
+ if (!isChildrenSlotContainer(nodeMap.get(containerId)?.editorMeta)) return null;
4269
+ const container = projection.containerIndex.get(containerId);
4270
+ if (!container) return null;
4271
+ for (const childId of container.children) {
4272
+ const childMeta = nodeMap.get(childId)?.editorMeta;
4273
+ if (isScreenRootMeta(childMeta)) return childMeta.file;
4274
+ }
4275
+ return null;
4276
+ }
4277
+ function resolveScreenRootDropTarget(containerId, domIndex, projection, nodeMap) {
4278
+ const screenRootFile = findScreenRootHostFile(containerId, projection, nodeMap);
4279
+ if (!screenRootFile) return null;
4280
+ const container = projection.containerIndex.get(containerId);
4281
+ if (!container) return null;
4282
+ const children = container.children;
4283
+ const before = domIndex > 0 ? children[domIndex - 1] : null;
4284
+ const after = domIndex < children.length ? children[domIndex] : null;
4285
+ if (before && !isScreenRootChild(before, nodeMap)) return null;
4286
+ if (after && !isScreenRootChild(after, nodeMap)) return null;
4287
+ let index = 0;
4288
+ for (let i = 0; i < domIndex && i < children.length; i += 1) {
4289
+ if (isScreenRootChild(children[i], nodeMap)) index += 1;
4290
+ }
4291
+ return { screenRootFile, index };
4292
+ }
4293
+ function isBlockedDropContainer(containerId, projection, nodeMap) {
4294
+ const meta = nodeMap.get(containerId)?.editorMeta;
4295
+ if (!isNonDroppableNodeMeta(meta)) return false;
4296
+ return findScreenRootHostFile(containerId, projection, nodeMap) === null;
4297
+ }
4248
4298
  function collectSubtreeIds(rootId, projection) {
4249
4299
  const ids2 = /* @__PURE__ */ new Set([rootId]);
4250
4300
  const stack = [rootId];
@@ -4267,8 +4317,9 @@ function computeDroppableSet(sourceId, projection, nodeMap) {
4267
4317
  for (const containerId of projection.containerIndex.keys()) {
4268
4318
  if (excluded.has(containerId)) continue;
4269
4319
  if (isStructuralContainer(containerId, nodeMap)) continue;
4270
- if (isNonDroppableNodeMeta(nodeMap.get(containerId)?.editorMeta)) continue;
4271
- const containerFile = nodeMap.get(containerId)?.editorMeta?.file;
4320
+ if (isBlockedDropContainer(containerId, projection, nodeMap)) continue;
4321
+ const screenRootFile = findScreenRootHostFile(containerId, projection, nodeMap);
4322
+ const containerFile = screenRootFile ?? nodeMap.get(containerId)?.editorMeta?.file;
4272
4323
  if (sourceFile && containerFile && containerFile !== sourceFile) continue;
4273
4324
  droppable.add(containerId);
4274
4325
  }
@@ -4278,11 +4329,12 @@ function buildBlockedNodeMetaContainerIds(projection, nodeMap, sourceId) {
4278
4329
  const sourceFile = sourceId ? nodeMap.get(sourceId)?.editorMeta?.file : void 0;
4279
4330
  const blocked = /* @__PURE__ */ new Set();
4280
4331
  for (const containerId of projection.containerIndex.keys()) {
4281
- if (isNonDroppableNodeMeta(nodeMap.get(containerId)?.editorMeta)) {
4332
+ if (isBlockedDropContainer(containerId, projection, nodeMap)) {
4282
4333
  blocked.add(containerId);
4283
4334
  continue;
4284
4335
  }
4285
- const containerFile = nodeMap.get(containerId)?.editorMeta?.file;
4336
+ const screenRootFile = findScreenRootHostFile(containerId, projection, nodeMap);
4337
+ const containerFile = screenRootFile ?? nodeMap.get(containerId)?.editorMeta?.file;
4286
4338
  if (sourceFile && containerFile && containerFile !== sourceFile) {
4287
4339
  blocked.add(containerId);
4288
4340
  }
@@ -4935,6 +4987,7 @@ var useDesignModeStore = create((set, get) => ({
4935
4987
  binding,
4936
4988
  targetVid,
4937
4989
  containerVid,
4990
+ screenRootFile,
4938
4991
  componentName,
4939
4992
  before,
4940
4993
  after
@@ -4964,6 +5017,7 @@ var useDesignModeStore = create((set, get) => ({
4964
5017
  binding: binding ?? null,
4965
5018
  targetVid: targetVid ?? null,
4966
5019
  containerVid: containerVid ?? null,
5020
+ screenRootFile: screenRootFile ?? null,
4967
5021
  payload: { before, after },
4968
5022
  at: now
4969
5023
  };
@@ -5040,6 +5094,7 @@ var useDesignModeStore = create((set, get) => ({
5040
5094
  containerNodeKey,
5041
5095
  containerVid,
5042
5096
  insertedVid,
5097
+ screenRootFile,
5043
5098
  index,
5044
5099
  beforeSiblingInspectorRef,
5045
5100
  beforeSiblingNodeKey
@@ -5060,6 +5115,7 @@ var useDesignModeStore = create((set, get) => ({
5060
5115
  containerNodeKey,
5061
5116
  containerVid: containerVid ?? null,
5062
5117
  insertedVid: insertedVid ?? null,
5118
+ screenRootFile: screenRootFile ?? null,
5063
5119
  index,
5064
5120
  beforeSiblingInspectorRef: beforeSiblingInspectorRef ?? null,
5065
5121
  beforeSiblingNodeKey: beforeSiblingNodeKey ?? null,
@@ -6839,6 +6895,11 @@ function serializeArrangeChange(event, index) {
6839
6895
  // the drop container's vid on an `insert` change.
6840
6896
  targetVid: event.kind === "move" || event.kind === "resize" ? event.targetVid : void 0,
6841
6897
  containerVid: event.kind === "move" || event.kind === "insert" ? event.containerVid : void 0,
6898
+ // ARCH — arrange screen-root: `screenRootFile` names the screen the
6899
+ // destination resolved to, on both a move (kind: 'move') and an insert
6900
+ // (kind: 'insert') that landed on the screen's own root — see
6901
+ // `RuntimeMoveDetail.screenRootFile` (`host-bridge.ts`).
6902
+ screenRootFile: event.kind === "move" || event.kind === "insert" ? event.screenRootFile : void 0,
6842
6903
  index: event.kind === "move" ? event.payload.after.index : void 0,
6843
6904
  // Insert-specific fields
6844
6905
  displayName: event.kind === "insert" ? event.displayName : void 0,
@@ -8380,6 +8441,9 @@ function resolveDragEndRefusal(params) {
8380
8441
  function resolveDragEndBranch(params) {
8381
8442
  if (!params.dropTargetExistsInMap) return { kind: "refuse", reason: "drop-target-missing" };
8382
8443
  if (!params.dropTargetIsDroppable) return { kind: "refuse", reason: "drop-target-not-droppable" };
8444
+ if (!params.hasNestTarget && params.dropTargetIsScreenRootHost && !params.dropTargetScreenRootSlotValid) {
8445
+ return { kind: "refuse", reason: "drop-target-not-droppable" };
8446
+ }
8383
8447
  if (params.hasNestTarget) {
8384
8448
  if (params.nestIsSelfOrDescendant) return { kind: "refuse", reason: "nest-self-or-descendant" };
8385
8449
  if (params.nestIsAncestor) return { kind: "refuse", reason: "nest-ancestor" };
@@ -8454,7 +8518,8 @@ function recordCommittedMove(args) {
8454
8518
  targetLaneSnapshot,
8455
8519
  resolveNodeElement,
8456
8520
  recordMoveChange,
8457
- nodeMap
8521
+ nodeMap,
8522
+ screenRootTarget
8458
8523
  } = args;
8459
8524
  const sourceLaneIdentitySnapshot = captureCommitLaneIdentitySnapshot(
8460
8525
  sourceLaneSnapshot,
@@ -8536,6 +8601,7 @@ function recordCommittedMove(args) {
8536
8601
  binding,
8537
8602
  targetVid,
8538
8603
  containerVid,
8604
+ screenRootFile: screenRootTarget?.screenRootFile ?? null,
8539
8605
  container: containerIdentity,
8540
8606
  beforeSibling: beforeSiblingIdentity,
8541
8607
  afterSibling: afterSiblingIdentity,
@@ -8549,7 +8615,8 @@ function recordCommittedMove(args) {
8549
8615
  commandType: command.type,
8550
8616
  targetVid,
8551
8617
  containerVid,
8552
- index: command.to.index,
8618
+ index: screenRootTarget?.index ?? command.to.index,
8619
+ screenRootFile: screenRootTarget?.screenRootFile ?? null,
8553
8620
  selected: {
8554
8621
  nodeId: selectedNodeId,
8555
8622
  nodeKey: selectedNodeKey,
@@ -8790,9 +8857,13 @@ function useDragLifecycle({
8790
8857
  const pos = sourcePos;
8791
8858
  const dropTargetId = nestTarget?.nodeId ?? intent?.containerId;
8792
8859
  const resolvedReorder = refs.resolvedReorderRef.current;
8860
+ const screenRootTarget = !nestTarget && intent && intent.mode === "reorder" ? resolveScreenRootDropTarget(intent.containerId, intent.index, refs.projectionRef.current, refs.nodeMapRef.current) : null;
8861
+ const dropTargetIsScreenRootHost = !nestTarget && intent && intent.mode === "reorder" ? findScreenRootHostFile(intent.containerId, refs.projectionRef.current, refs.nodeMapRef.current) !== null : false;
8793
8862
  const branch = resolveDragEndBranch({
8794
8863
  dropTargetExistsInMap: refs.nodeMapRef.current.has(dropTargetId),
8795
8864
  dropTargetIsDroppable: !!refs.droppableContainerIdsRef.current?.has(dropTargetId),
8865
+ dropTargetIsScreenRootHost,
8866
+ dropTargetScreenRootSlotValid: !!screenRootTarget,
8796
8867
  hasNestTarget: !!nestTarget,
8797
8868
  nestIsSelfOrDescendant: !!nestTarget && el.contains(nestTarget.element),
8798
8869
  nestIsAncestor: !!nestTarget && nestTarget.element.contains(el),
@@ -8887,7 +8958,8 @@ function useDragLifecycle({
8887
8958
  targetLaneSnapshot,
8888
8959
  resolveNodeElement,
8889
8960
  recordMoveChange,
8890
- nodeMap: refs.nodeMapRef.current
8961
+ nodeMap: refs.nodeMapRef.current,
8962
+ screenRootTarget
8891
8963
  });
8892
8964
  } else {
8893
8965
  emitRefused(GATE_REJECT, "The resolved move could not be applied.", anchorVid());
@@ -9154,6 +9226,9 @@ function useExternalDragLoop({
9154
9226
  const nodeMap = /* @__PURE__ */ new Map();
9155
9227
  for (const el of elements) nodeMap.set(el.nodeId, el);
9156
9228
  const blockedContainerIds = buildBlockedStructuralContainerIds(projection, nodeMap, null);
9229
+ for (const id of buildBlockedNodeMetaContainerIds(projection, nodeMap, null)) {
9230
+ blockedContainerIds.add(id);
9231
+ }
9157
9232
  const engine = new DragIntentEngine(projection, EXTERNAL_DRAG_ACTIVE_ID, {
9158
9233
  blockedContainerIds
9159
9234
  });
@@ -9173,7 +9248,8 @@ function useExternalDragLoop({
9173
9248
  resolveNodeElement,
9174
9249
  setDropLine,
9175
9250
  scrollX,
9176
- scrollY
9251
+ scrollY,
9252
+ projection
9177
9253
  );
9178
9254
  return;
9179
9255
  }
@@ -9206,13 +9282,35 @@ function useExternalDragLoop({
9206
9282
  return () => cancelAnimationFrame(rafId);
9207
9283
  }, [externalDrag, elements, refs, resolveNodeElement, setDropLine]);
9208
9284
  }
9209
- function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, setDropLine, startScrollX, startScrollY) {
9285
+ function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, setDropLine, startScrollX, startScrollY, projection) {
9210
9286
  const intent = engine.resolve(x, y, window.scrollX, window.scrollY, startScrollX, startScrollY);
9211
9287
  if (!intent || intent.mode !== "reorder") {
9212
9288
  useDesignModeStore.getState().cancelExternalDrag();
9213
9289
  setDropLine(null);
9214
9290
  return;
9215
9291
  }
9292
+ let screenRootFile = null;
9293
+ let screenRootIndex = null;
9294
+ if (projection) {
9295
+ const isChildrenSlot = findScreenRootHostFile(intent.containerId, projection, refs.nodeMapRef.current) !== null;
9296
+ if (isChildrenSlot) {
9297
+ const target = resolveScreenRootDropTarget(intent.containerId, intent.index, projection, refs.nodeMapRef.current);
9298
+ if (!target) {
9299
+ const containerMeta = refs.nodeMapRef.current.get(intent.containerId)?.editorMeta;
9300
+ dispatchRuntimeEvent({
9301
+ type: "drag-refused",
9302
+ code: resolveRefusalCode(containerMeta, "CONTAINER_NOT_PLAIN"),
9303
+ cause: "Drop position is not a valid screen-root slot inside app-shell/kit chrome.",
9304
+ vid: resolveVidForScannedNodeStrict(intent.containerId, refs.nodeMapRef.current)
9305
+ });
9306
+ useDesignModeStore.getState().cancelExternalDrag();
9307
+ setDropLine(null);
9308
+ return;
9309
+ }
9310
+ screenRootFile = target.screenRootFile;
9311
+ screenRootIndex = target.index;
9312
+ }
9313
+ }
9216
9314
  const containerEl = resolveNodeElement(intent.containerId);
9217
9315
  if (!containerEl) {
9218
9316
  useDesignModeStore.getState().cancelExternalDrag();
@@ -9303,7 +9401,8 @@ function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, s
9303
9401
  containerNodeKey,
9304
9402
  containerVid,
9305
9403
  insertedVid,
9306
- index: intent.index,
9404
+ index: screenRootIndex ?? intent.index,
9405
+ screenRootFile,
9307
9406
  beforeSiblingInspectorRef: beforeSiblingIdentity?.inspectorRef ?? null,
9308
9407
  beforeSiblingNodeKey: beforeSiblingIdentity?.nodeKey ?? null
9309
9408
  });
@@ -9319,7 +9418,8 @@ function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, s
9319
9418
  container: containerIdentity,
9320
9419
  containerVid,
9321
9420
  insertedVid,
9322
- index: intent.index,
9421
+ index: screenRootIndex ?? intent.index,
9422
+ screenRootFile,
9323
9423
  beforeSibling: beforeSiblingIdentity
9324
9424
  });
9325
9425
  }
@@ -10130,7 +10230,7 @@ function isPreviewOrigin(origin) {
10130
10230
  var DEFAULT_SCRIPT_ID = "archie-inspector-script";
10131
10231
  var R2_INSPECTOR_BASE_URL = "https://pub-bf238c278d9644b39ec34ecaca1d315c.r2.dev";
10132
10232
  function readBuildVersion() {
10133
- return "0.1.7" ? "0.1.7" : null;
10233
+ return "0.1.8" ? "0.1.8" : null;
10134
10234
  }
10135
10235
  var REMOTE_INSPECTOR_VERSION = readBuildVersion();
10136
10236
  var isRemoteInspectorUrlVersioned = REMOTE_INSPECTOR_VERSION !== null;
@@ -4084,7 +4084,8 @@ function summarizeRuntimeEvent(detail) {
4084
4084
  diagnostics: detail.diagnostics ?? null,
4085
4085
  targetVid: detail.targetVid ?? null,
4086
4086
  containerVid: detail.containerVid ?? null,
4087
- index: detail.index
4087
+ index: detail.index,
4088
+ screenRootFile: detail.screenRootFile ?? null
4088
4089
  };
4089
4090
  case "drag-started":
4090
4091
  return { targetVid: detail.targetVid, droppableCount: detail.droppable.length };
@@ -4115,6 +4116,7 @@ function summarizeRuntimeEvent(detail) {
4115
4116
  containerVid: detail.containerVid ?? null,
4116
4117
  insertedVid: detail.insertedVid ?? null,
4117
4118
  index: detail.index,
4119
+ screenRootFile: detail.screenRootFile ?? null,
4118
4120
  beforeSibling: summarizeIdentity(detail.beforeSibling),
4119
4121
  hasTsxCode: !!detail.tsxCode,
4120
4122
  htmlLength: detail.html?.length ?? 0
@@ -4210,6 +4212,54 @@ function isNonDraggableNodeMeta(meta) {
4210
4212
  if (meta.isKitOrigin) return true;
4211
4213
  return false;
4212
4214
  }
4215
+ var SCREEN_FILE_RE = /(?:^|\/)screens\//;
4216
+ function isScreenRootMeta(meta) {
4217
+ return !!meta && !!meta.file && SCREEN_FILE_RE.test(meta.file) && meta.parentNodeId === null;
4218
+ }
4219
+ function isUnsavedScreenRootCandidate(nodeId) {
4220
+ return nodeId.startsWith("insert:");
4221
+ }
4222
+ function isScreenRootChild(nodeId, nodeMap) {
4223
+ const meta = nodeMap.get(nodeId)?.editorMeta;
4224
+ if (meta) return isScreenRootMeta(meta);
4225
+ return isUnsavedScreenRootCandidate(nodeId);
4226
+ }
4227
+ function isChildrenSlotContainer(meta) {
4228
+ if (!meta) return false;
4229
+ if (meta.containerKind !== "expression") return false;
4230
+ return !!(meta.isChrome || meta.isKitOrigin || meta.isKitContainer);
4231
+ }
4232
+ function findScreenRootHostFile(containerId, projection, nodeMap) {
4233
+ if (!isChildrenSlotContainer(nodeMap.get(containerId)?.editorMeta)) return null;
4234
+ const container = projection.containerIndex.get(containerId);
4235
+ if (!container) return null;
4236
+ for (const childId of container.children) {
4237
+ const childMeta = nodeMap.get(childId)?.editorMeta;
4238
+ if (isScreenRootMeta(childMeta)) return childMeta.file;
4239
+ }
4240
+ return null;
4241
+ }
4242
+ function resolveScreenRootDropTarget(containerId, domIndex, projection, nodeMap) {
4243
+ const screenRootFile = findScreenRootHostFile(containerId, projection, nodeMap);
4244
+ if (!screenRootFile) return null;
4245
+ const container = projection.containerIndex.get(containerId);
4246
+ if (!container) return null;
4247
+ const children = container.children;
4248
+ const before = domIndex > 0 ? children[domIndex - 1] : null;
4249
+ const after = domIndex < children.length ? children[domIndex] : null;
4250
+ if (before && !isScreenRootChild(before, nodeMap)) return null;
4251
+ if (after && !isScreenRootChild(after, nodeMap)) return null;
4252
+ let index = 0;
4253
+ for (let i = 0; i < domIndex && i < children.length; i += 1) {
4254
+ if (isScreenRootChild(children[i], nodeMap)) index += 1;
4255
+ }
4256
+ return { screenRootFile, index };
4257
+ }
4258
+ function isBlockedDropContainer(containerId, projection, nodeMap) {
4259
+ const meta = nodeMap.get(containerId)?.editorMeta;
4260
+ if (!isNonDroppableNodeMeta(meta)) return false;
4261
+ return findScreenRootHostFile(containerId, projection, nodeMap) === null;
4262
+ }
4213
4263
  function collectSubtreeIds(rootId, projection) {
4214
4264
  const ids2 = /* @__PURE__ */ new Set([rootId]);
4215
4265
  const stack = [rootId];
@@ -4232,8 +4282,9 @@ function computeDroppableSet(sourceId, projection, nodeMap) {
4232
4282
  for (const containerId of projection.containerIndex.keys()) {
4233
4283
  if (excluded.has(containerId)) continue;
4234
4284
  if (isStructuralContainer(containerId, nodeMap)) continue;
4235
- if (isNonDroppableNodeMeta(nodeMap.get(containerId)?.editorMeta)) continue;
4236
- const containerFile = nodeMap.get(containerId)?.editorMeta?.file;
4285
+ if (isBlockedDropContainer(containerId, projection, nodeMap)) continue;
4286
+ const screenRootFile = findScreenRootHostFile(containerId, projection, nodeMap);
4287
+ const containerFile = screenRootFile ?? nodeMap.get(containerId)?.editorMeta?.file;
4237
4288
  if (sourceFile && containerFile && containerFile !== sourceFile) continue;
4238
4289
  droppable.add(containerId);
4239
4290
  }
@@ -4243,11 +4294,12 @@ function buildBlockedNodeMetaContainerIds(projection, nodeMap, sourceId) {
4243
4294
  const sourceFile = sourceId ? nodeMap.get(sourceId)?.editorMeta?.file : void 0;
4244
4295
  const blocked = /* @__PURE__ */ new Set();
4245
4296
  for (const containerId of projection.containerIndex.keys()) {
4246
- if (isNonDroppableNodeMeta(nodeMap.get(containerId)?.editorMeta)) {
4297
+ if (isBlockedDropContainer(containerId, projection, nodeMap)) {
4247
4298
  blocked.add(containerId);
4248
4299
  continue;
4249
4300
  }
4250
- const containerFile = nodeMap.get(containerId)?.editorMeta?.file;
4301
+ const screenRootFile = findScreenRootHostFile(containerId, projection, nodeMap);
4302
+ const containerFile = screenRootFile ?? nodeMap.get(containerId)?.editorMeta?.file;
4251
4303
  if (sourceFile && containerFile && containerFile !== sourceFile) {
4252
4304
  blocked.add(containerId);
4253
4305
  }
@@ -4900,6 +4952,7 @@ var useDesignModeStore = create((set, get) => ({
4900
4952
  binding,
4901
4953
  targetVid,
4902
4954
  containerVid,
4955
+ screenRootFile,
4903
4956
  componentName,
4904
4957
  before,
4905
4958
  after
@@ -4929,6 +4982,7 @@ var useDesignModeStore = create((set, get) => ({
4929
4982
  binding: binding ?? null,
4930
4983
  targetVid: targetVid ?? null,
4931
4984
  containerVid: containerVid ?? null,
4985
+ screenRootFile: screenRootFile ?? null,
4932
4986
  payload: { before, after },
4933
4987
  at: now
4934
4988
  };
@@ -5005,6 +5059,7 @@ var useDesignModeStore = create((set, get) => ({
5005
5059
  containerNodeKey,
5006
5060
  containerVid,
5007
5061
  insertedVid,
5062
+ screenRootFile,
5008
5063
  index,
5009
5064
  beforeSiblingInspectorRef,
5010
5065
  beforeSiblingNodeKey
@@ -5025,6 +5080,7 @@ var useDesignModeStore = create((set, get) => ({
5025
5080
  containerNodeKey,
5026
5081
  containerVid: containerVid ?? null,
5027
5082
  insertedVid: insertedVid ?? null,
5083
+ screenRootFile: screenRootFile ?? null,
5028
5084
  index,
5029
5085
  beforeSiblingInspectorRef: beforeSiblingInspectorRef ?? null,
5030
5086
  beforeSiblingNodeKey: beforeSiblingNodeKey ?? null,
@@ -6804,6 +6860,11 @@ function serializeArrangeChange(event, index) {
6804
6860
  // the drop container's vid on an `insert` change.
6805
6861
  targetVid: event.kind === "move" || event.kind === "resize" ? event.targetVid : void 0,
6806
6862
  containerVid: event.kind === "move" || event.kind === "insert" ? event.containerVid : void 0,
6863
+ // ARCH — arrange screen-root: `screenRootFile` names the screen the
6864
+ // destination resolved to, on both a move (kind: 'move') and an insert
6865
+ // (kind: 'insert') that landed on the screen's own root — see
6866
+ // `RuntimeMoveDetail.screenRootFile` (`host-bridge.ts`).
6867
+ screenRootFile: event.kind === "move" || event.kind === "insert" ? event.screenRootFile : void 0,
6807
6868
  index: event.kind === "move" ? event.payload.after.index : void 0,
6808
6869
  // Insert-specific fields
6809
6870
  displayName: event.kind === "insert" ? event.displayName : void 0,
@@ -8345,6 +8406,9 @@ function resolveDragEndRefusal(params) {
8345
8406
  function resolveDragEndBranch(params) {
8346
8407
  if (!params.dropTargetExistsInMap) return { kind: "refuse", reason: "drop-target-missing" };
8347
8408
  if (!params.dropTargetIsDroppable) return { kind: "refuse", reason: "drop-target-not-droppable" };
8409
+ if (!params.hasNestTarget && params.dropTargetIsScreenRootHost && !params.dropTargetScreenRootSlotValid) {
8410
+ return { kind: "refuse", reason: "drop-target-not-droppable" };
8411
+ }
8348
8412
  if (params.hasNestTarget) {
8349
8413
  if (params.nestIsSelfOrDescendant) return { kind: "refuse", reason: "nest-self-or-descendant" };
8350
8414
  if (params.nestIsAncestor) return { kind: "refuse", reason: "nest-ancestor" };
@@ -8419,7 +8483,8 @@ function recordCommittedMove(args) {
8419
8483
  targetLaneSnapshot,
8420
8484
  resolveNodeElement,
8421
8485
  recordMoveChange,
8422
- nodeMap
8486
+ nodeMap,
8487
+ screenRootTarget
8423
8488
  } = args;
8424
8489
  const sourceLaneIdentitySnapshot = captureCommitLaneIdentitySnapshot(
8425
8490
  sourceLaneSnapshot,
@@ -8501,6 +8566,7 @@ function recordCommittedMove(args) {
8501
8566
  binding,
8502
8567
  targetVid,
8503
8568
  containerVid,
8569
+ screenRootFile: screenRootTarget?.screenRootFile ?? null,
8504
8570
  container: containerIdentity,
8505
8571
  beforeSibling: beforeSiblingIdentity,
8506
8572
  afterSibling: afterSiblingIdentity,
@@ -8514,7 +8580,8 @@ function recordCommittedMove(args) {
8514
8580
  commandType: command.type,
8515
8581
  targetVid,
8516
8582
  containerVid,
8517
- index: command.to.index,
8583
+ index: screenRootTarget?.index ?? command.to.index,
8584
+ screenRootFile: screenRootTarget?.screenRootFile ?? null,
8518
8585
  selected: {
8519
8586
  nodeId: selectedNodeId,
8520
8587
  nodeKey: selectedNodeKey,
@@ -8755,9 +8822,13 @@ function useDragLifecycle({
8755
8822
  const pos = sourcePos;
8756
8823
  const dropTargetId = nestTarget?.nodeId ?? intent?.containerId;
8757
8824
  const resolvedReorder = refs.resolvedReorderRef.current;
8825
+ const screenRootTarget = !nestTarget && intent && intent.mode === "reorder" ? resolveScreenRootDropTarget(intent.containerId, intent.index, refs.projectionRef.current, refs.nodeMapRef.current) : null;
8826
+ const dropTargetIsScreenRootHost = !nestTarget && intent && intent.mode === "reorder" ? findScreenRootHostFile(intent.containerId, refs.projectionRef.current, refs.nodeMapRef.current) !== null : false;
8758
8827
  const branch = resolveDragEndBranch({
8759
8828
  dropTargetExistsInMap: refs.nodeMapRef.current.has(dropTargetId),
8760
8829
  dropTargetIsDroppable: !!refs.droppableContainerIdsRef.current?.has(dropTargetId),
8830
+ dropTargetIsScreenRootHost,
8831
+ dropTargetScreenRootSlotValid: !!screenRootTarget,
8761
8832
  hasNestTarget: !!nestTarget,
8762
8833
  nestIsSelfOrDescendant: !!nestTarget && el.contains(nestTarget.element),
8763
8834
  nestIsAncestor: !!nestTarget && nestTarget.element.contains(el),
@@ -8852,7 +8923,8 @@ function useDragLifecycle({
8852
8923
  targetLaneSnapshot,
8853
8924
  resolveNodeElement,
8854
8925
  recordMoveChange,
8855
- nodeMap: refs.nodeMapRef.current
8926
+ nodeMap: refs.nodeMapRef.current,
8927
+ screenRootTarget
8856
8928
  });
8857
8929
  } else {
8858
8930
  emitRefused(GATE_REJECT, "The resolved move could not be applied.", anchorVid());
@@ -9119,6 +9191,9 @@ function useExternalDragLoop({
9119
9191
  const nodeMap = /* @__PURE__ */ new Map();
9120
9192
  for (const el of elements) nodeMap.set(el.nodeId, el);
9121
9193
  const blockedContainerIds = buildBlockedStructuralContainerIds(projection, nodeMap, null);
9194
+ for (const id of buildBlockedNodeMetaContainerIds(projection, nodeMap, null)) {
9195
+ blockedContainerIds.add(id);
9196
+ }
9122
9197
  const engine = new DragIntentEngine(projection, EXTERNAL_DRAG_ACTIVE_ID, {
9123
9198
  blockedContainerIds
9124
9199
  });
@@ -9138,7 +9213,8 @@ function useExternalDragLoop({
9138
9213
  resolveNodeElement,
9139
9214
  setDropLine,
9140
9215
  scrollX,
9141
- scrollY
9216
+ scrollY,
9217
+ projection
9142
9218
  );
9143
9219
  return;
9144
9220
  }
@@ -9171,13 +9247,35 @@ function useExternalDragLoop({
9171
9247
  return () => cancelAnimationFrame(rafId);
9172
9248
  }, [externalDrag, elements, refs, resolveNodeElement, setDropLine]);
9173
9249
  }
9174
- function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, setDropLine, startScrollX, startScrollY) {
9250
+ function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, setDropLine, startScrollX, startScrollY, projection) {
9175
9251
  const intent = engine.resolve(x, y, window.scrollX, window.scrollY, startScrollX, startScrollY);
9176
9252
  if (!intent || intent.mode !== "reorder") {
9177
9253
  useDesignModeStore.getState().cancelExternalDrag();
9178
9254
  setDropLine(null);
9179
9255
  return;
9180
9256
  }
9257
+ let screenRootFile = null;
9258
+ let screenRootIndex = null;
9259
+ if (projection) {
9260
+ const isChildrenSlot = findScreenRootHostFile(intent.containerId, projection, refs.nodeMapRef.current) !== null;
9261
+ if (isChildrenSlot) {
9262
+ const target = resolveScreenRootDropTarget(intent.containerId, intent.index, projection, refs.nodeMapRef.current);
9263
+ if (!target) {
9264
+ const containerMeta = refs.nodeMapRef.current.get(intent.containerId)?.editorMeta;
9265
+ dispatchRuntimeEvent({
9266
+ type: "drag-refused",
9267
+ code: resolveRefusalCode(containerMeta, "CONTAINER_NOT_PLAIN"),
9268
+ cause: "Drop position is not a valid screen-root slot inside app-shell/kit chrome.",
9269
+ vid: resolveVidForScannedNodeStrict(intent.containerId, refs.nodeMapRef.current)
9270
+ });
9271
+ useDesignModeStore.getState().cancelExternalDrag();
9272
+ setDropLine(null);
9273
+ return;
9274
+ }
9275
+ screenRootFile = target.screenRootFile;
9276
+ screenRootIndex = target.index;
9277
+ }
9278
+ }
9181
9279
  const containerEl = resolveNodeElement(intent.containerId);
9182
9280
  if (!containerEl) {
9183
9281
  useDesignModeStore.getState().cancelExternalDrag();
@@ -9268,7 +9366,8 @@ function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, s
9268
9366
  containerNodeKey,
9269
9367
  containerVid,
9270
9368
  insertedVid,
9271
- index: intent.index,
9369
+ index: screenRootIndex ?? intent.index,
9370
+ screenRootFile,
9272
9371
  beforeSiblingInspectorRef: beforeSiblingIdentity?.inspectorRef ?? null,
9273
9372
  beforeSiblingNodeKey: beforeSiblingIdentity?.nodeKey ?? null
9274
9373
  });
@@ -9284,7 +9383,8 @@ function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, s
9284
9383
  container: containerIdentity,
9285
9384
  containerVid,
9286
9385
  insertedVid,
9287
- index: intent.index,
9386
+ index: screenRootIndex ?? intent.index,
9387
+ screenRootFile,
9288
9388
  beforeSibling: beforeSiblingIdentity
9289
9389
  });
9290
9390
  }
@@ -10095,7 +10195,7 @@ function isPreviewOrigin(origin) {
10095
10195
  var DEFAULT_SCRIPT_ID = "archie-inspector-script";
10096
10196
  var R2_INSPECTOR_BASE_URL = "https://pub-bf238c278d9644b39ec34ecaca1d315c.r2.dev";
10097
10197
  function readBuildVersion() {
10098
- return "0.1.7" ? "0.1.7" : null;
10198
+ return "0.1.8" ? "0.1.8" : null;
10099
10199
  }
10100
10200
  var REMOTE_INSPECTOR_VERSION = readBuildVersion();
10101
10201
  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.7",
3
+ "version": "0.1.8",
4
4
  "description": "DevTools for Archie generated applications - Route synchronization and editor communication",
5
5
  "sideEffects": false,
6
6
  "repository": {