@archie/devtools 0.0.31 → 0.0.33

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.
@@ -534,6 +534,11 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
534
534
  parentNodeId ? t.stringLiteral(parentNodeId) : t.nullLiteral()
535
535
  )
536
536
  ]);
537
+ if (isKitOrigin) {
538
+ metaObj.properties.push(
539
+ t.objectProperty(t.identifier("origin"), t.stringLiteral("kit"))
540
+ );
541
+ }
537
542
  if (slot !== null) {
538
543
  metaObj.properties.push(
539
544
  t.objectProperty(t.identifier("slot"), t.stringLiteral(slot))
@@ -4160,6 +4160,7 @@ var FIBER_KEY_PREFIX = "__reactFiber$";
4160
4160
  var PROPS_KEY_PREFIX = "__reactProps$";
4161
4161
  var metaCache = /* @__PURE__ */ new WeakMap();
4162
4162
  var directMetaCache = /* @__PURE__ */ new WeakMap();
4163
+ var usageMetaCache = /* @__PURE__ */ new WeakMap();
4163
4164
  function readEditorMetaValue(value) {
4164
4165
  if (typeof value === "function") {
4165
4166
  return value();
@@ -4324,6 +4325,34 @@ function getSingleHostElement(owner) {
4324
4325
  }
4325
4326
  return hosts.length === 1 ? hosts[0] : null;
4326
4327
  }
4328
+ function getUsageMetaOwnerFiber(el) {
4329
+ let fiber = getFiberSelectionFromElement(el).fiber;
4330
+ while (fiber) {
4331
+ const meta = readEditorMetaValue(fiber.memoizedProps?.__editorMeta);
4332
+ if (meta && meta.origin !== "kit") return fiber;
4333
+ fiber = fiber.return;
4334
+ }
4335
+ return null;
4336
+ }
4337
+ function projectFromOwner(owner, el) {
4338
+ if (!owner) return null;
4339
+ if (getSingleHostElement(owner) !== el) return null;
4340
+ return readEditorMetaValue(owner.memoizedProps?.__editorMeta);
4341
+ }
4342
+ function projectUsageMeta(el) {
4343
+ const selection = getFiberSelectionFromElement(el);
4344
+ const cached = readFiberAwareCache(usageMetaCache, el, selection);
4345
+ if (cached !== void 0) return cached;
4346
+ return writeFiberAwareCache(usageMetaCache, el, selection, computeUsageMeta(el));
4347
+ }
4348
+ function computeUsageMeta(el) {
4349
+ const direct = getDirectEditorMeta(el);
4350
+ if (direct && direct.origin !== "kit") return null;
4351
+ const usage = projectFromOwner(getUsageMetaOwnerFiber(el), el);
4352
+ if (usage) return usage;
4353
+ if (direct) return null;
4354
+ return projectFromOwner(getEditorMetaOwnerFiber(el), el);
4355
+ }
4327
4356
  function findMetaOwnerElement(el) {
4328
4357
  let current = el;
4329
4358
  while (current) {
@@ -4391,10 +4420,13 @@ function getRuntimeKey(el) {
4391
4420
  _runtimeKeyMap.set(el, key2);
4392
4421
  return key2;
4393
4422
  }
4423
+ function identityMeta(host) {
4424
+ return projectUsageMeta(host) ?? getEditorMeta(host);
4425
+ }
4394
4426
  function getNodeKeyFromElement(el) {
4395
4427
  const resolved = resolveDragSurface(el);
4396
4428
  const host = resolved.host ?? el;
4397
- const meta = getEditorMeta(host);
4429
+ const meta = identityMeta(host);
4398
4430
  if (meta) return meta.nodeId;
4399
4431
  const persisted = host.getAttribute("data-dnd-node-id");
4400
4432
  if (persisted) return persisted;
@@ -4403,7 +4435,7 @@ function getNodeKeyFromElement(el) {
4403
4435
  function getInspectorRefFromElement(el) {
4404
4436
  const resolved = resolveDragSurface(el);
4405
4437
  const host = resolved.host ?? el;
4406
- const meta = getEditorMeta(host);
4438
+ const meta = identityMeta(host);
4407
4439
  if (!meta) return null;
4408
4440
  return { relativePath: meta.file, line: meta.line, column: meta.col };
4409
4441
  }
@@ -5081,13 +5113,6 @@ function isInspectorEligible(el) {
5081
5113
  if (el.hasAttribute("data-dnd-force-allow")) return "force-allow";
5082
5114
  return null;
5083
5115
  }
5084
- function projectUsageMeta(el) {
5085
- if (getDirectEditorMeta(el)) return null;
5086
- const owner = getEditorMetaOwnerFiber(el);
5087
- if (!owner) return null;
5088
- if (getSingleHostElement(owner) !== el) return null;
5089
- return readEditorMetaValue(owner.memoizedProps?.__editorMeta);
5090
- }
5091
5116
  function isRuntimeVisualInternal(el) {
5092
5117
  if (shouldIgnoreAsVirtualRuntime(el)) return true;
5093
5118
  const tag = el.tagName.toLowerCase();
@@ -5117,10 +5142,10 @@ function ensureUniqueNodeId(base, used) {
5117
5142
  function resolveStableNodeId(el, used) {
5118
5143
  const persisted = el.getAttribute(DND_NODE_ID_ATTR);
5119
5144
  if (persisted && !used.has(persisted)) return persisted;
5120
- const metaId = getEditorMetaNodeId(el);
5121
- if (metaId) return ensureUniqueNodeId(metaId, used);
5122
5145
  const projected = projectUsageMeta(el);
5123
5146
  if (projected) return ensureUniqueNodeId(projected.nodeId, used);
5147
+ const metaId = getEditorMetaNodeId(el);
5148
+ if (metaId) return ensureUniqueNodeId(metaId, used);
5124
5149
  return ensureUniqueNodeId(getRuntimeNodeId(el), used);
5125
5150
  }
5126
5151
  function getUniqueSelector(el) {
@@ -5205,6 +5230,36 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5205
5230
  const minW = isExplicit ? INTERACTIVE_MIN : MIN_WIDTH;
5206
5231
  const minH = isExplicit ? INTERACTIVE_MIN : MIN_HEIGHT;
5207
5232
  if (rect.width < minW || rect.height < minH || rect.width === 0 || rect.height === 0) {
5233
+ const isZeroRect = rect.width === 0 || rect.height === 0;
5234
+ const smallEligibility = isInspectorEligible(el) ?? (projectUsageMeta(el) ? "projected-usage" : null);
5235
+ if (smallEligibility !== null && !seenResolvedHosts.has(el)) {
5236
+ const nodeId2 = resolveStableNodeId(el, usedNodeIds);
5237
+ usedNodeIds.add(nodeId2);
5238
+ el.setAttribute(DND_NODE_ID_ATTR, nodeId2);
5239
+ if (!isZeroRect) {
5240
+ seenResolvedHosts.add(el);
5241
+ const surfaceMeta2 = el instanceof HTMLElement ? getDirectEditorMeta(el) ?? void 0 : void 0;
5242
+ const identityMeta3 = projectUsageMeta(el) ?? surfaceMeta2;
5243
+ const selector = getUniqueSelector(el);
5244
+ const parentEl2 = el.parentElement;
5245
+ results.push({
5246
+ nodeId: nodeId2,
5247
+ element: el,
5248
+ selector,
5249
+ sourceMetaNodeId: identityMeta3?.nodeId ?? null,
5250
+ resolvedHostNodeId: nodeId2,
5251
+ resolvedHostSelector: selector,
5252
+ resolutionKind: "direct-host",
5253
+ label: getLabel(el),
5254
+ rect,
5255
+ containerStrategy: parentEl2 && isFlexOrGridParent(parentEl2) ? resolveContainerStrategy(parentEl2) : null,
5256
+ eligibilitySource: smallEligibility,
5257
+ draggable: false,
5258
+ identityOrigin: identityMeta3?.origin ?? null,
5259
+ editorMeta: surfaceMeta2
5260
+ });
5261
+ }
5262
+ }
5208
5263
  if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
5209
5264
  continue;
5210
5265
  }
@@ -5259,13 +5314,15 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5259
5314
  const surfaceMeta = hostEl instanceof HTMLElement ? getDirectEditorMeta(hostEl) ?? void 0 : void 0;
5260
5315
  const ownerMeta = dragSurface.metaOwner && dragSurface.metaOwner instanceof HTMLElement ? getEditorMeta(dragSurface.metaOwner) : null;
5261
5316
  const resolvedSelector = getUniqueSelector(hostEl);
5262
- const draggable = inferredEligibility === "force-allow" || !nodeId.startsWith("runtime:");
5317
+ const usageMeta = hostEl instanceof HTMLElement ? projectUsageMeta(hostEl) : null;
5318
+ const identityMeta2 = usageMeta ?? ownerMeta ?? surfaceMeta;
5319
+ const draggable = inferredEligibility === "force-allow" || !nodeId.startsWith("runtime:") && (identityMeta2?.origin !== "kit" || resolveRowBinding(hostEl) !== null);
5263
5320
  results.push({
5264
5321
  nodeId,
5265
5322
  element: hostEl,
5266
5323
  selector: resolvedSelector,
5267
5324
  draggable,
5268
- sourceMetaNodeId: ownerMeta?.nodeId ?? surfaceMeta?.nodeId ?? null,
5325
+ sourceMetaNodeId: identityMeta2?.nodeId ?? null,
5269
5326
  resolvedHostNodeId: nodeId,
5270
5327
  resolvedHostSelector: resolvedSelector,
5271
5328
  resolutionKind: dragSurface.kind,
@@ -5273,6 +5330,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5273
5330
  rect: hostRect,
5274
5331
  containerStrategy,
5275
5332
  eligibilitySource: inferredEligibility,
5333
+ identityOrigin: identityMeta2?.origin ?? null,
5276
5334
  editorMeta: surfaceMeta
5277
5335
  });
5278
5336
  if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
@@ -5282,6 +5340,45 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5282
5340
  }
5283
5341
  return results;
5284
5342
  }
5343
+ function buildMovedElementEntry(el, usedNodeIds) {
5344
+ const rect = el.getBoundingClientRect();
5345
+ if (rect.width === 0 || rect.height === 0) {
5346
+ if (isInspectorEligible(el) !== null || projectUsageMeta(el) !== null) {
5347
+ const nodeId2 = resolveStableNodeId(el, usedNodeIds);
5348
+ usedNodeIds.add(nodeId2);
5349
+ el.setAttribute(DND_NODE_ID_ATTR, nodeId2);
5350
+ }
5351
+ return null;
5352
+ }
5353
+ const nodeId = resolveStableNodeId(el, usedNodeIds);
5354
+ el.setAttribute(DND_NODE_ID_ATTR, nodeId);
5355
+ const parentEl = el.parentElement;
5356
+ const surfaceMeta = getDirectEditorMeta(el) ?? void 0;
5357
+ const ownerMeta = getEditorMeta(el);
5358
+ const identityMeta2 = projectUsageMeta(el) ?? ownerMeta ?? surfaceMeta;
5359
+ const selector = getUniqueSelector(el);
5360
+ const eligibility = isInspectorEligible(el);
5361
+ return {
5362
+ nodeId,
5363
+ element: el,
5364
+ selector,
5365
+ // Same draggable rule as the main scan, INCLUDING the force-allow
5366
+ // escape — an element that was draggable when the user moved it must
5367
+ // not silently lose draggability for having landed below MAX_DEPTH.
5368
+ // Same kit-origin bound-row exemption as the main scan (see there).
5369
+ draggable: eligibility === "force-allow" || !nodeId.startsWith("runtime:") && (identityMeta2?.origin !== "kit" || resolveRowBinding(el) !== null),
5370
+ sourceMetaNodeId: identityMeta2?.nodeId ?? null,
5371
+ resolvedHostNodeId: nodeId,
5372
+ resolvedHostSelector: selector,
5373
+ resolutionKind: ownerMeta && ownerMeta.componentName[0] !== ownerMeta.componentName[0].toLowerCase() ? "wrapper-to-host" : "direct-host",
5374
+ label: getLabel(el),
5375
+ rect,
5376
+ containerStrategy: parentEl && isFlexOrGridParent(parentEl) ? resolveContainerStrategy(parentEl) : null,
5377
+ eligibilitySource: eligibility ?? "force-allow",
5378
+ identityOrigin: identityMeta2?.origin ?? null,
5379
+ editorMeta: surfaceMeta
5380
+ };
5381
+ }
5285
5382
  function useElementScanner(enabled) {
5286
5383
  const [elements, setElements] = (0, import_react6.useState)([]);
5287
5384
  const rafRef = (0, import_react6.useRef)(0);
@@ -5298,30 +5395,10 @@ function useElementScanner(enabled) {
5298
5395
  const seenEls = new Set(scanned.map((s) => s.element));
5299
5396
  for (const el of Array.from(document.querySelectorAll("[data-dnd-moved]"))) {
5300
5397
  if (seenEls.has(el)) continue;
5301
- const rect = el.getBoundingClientRect();
5302
- if (rect.width === 0 || rect.height === 0) continue;
5303
- const nodeId = resolveStableNodeId(el, usedNodeIds);
5304
- usedNodeIds.add(nodeId);
5305
- el.setAttribute(DND_NODE_ID_ATTR, nodeId);
5306
- const parentEl = el.parentElement;
5307
- const surfaceMeta = getDirectEditorMeta(el) ?? void 0;
5308
- const ownerMeta = getEditorMeta(el);
5309
- const selector = getUniqueSelector(el);
5310
- scanned.push({
5311
- nodeId,
5312
- element: el,
5313
- selector,
5314
- draggable: !nodeId.startsWith("runtime:"),
5315
- sourceMetaNodeId: ownerMeta?.nodeId ?? surfaceMeta?.nodeId ?? null,
5316
- resolvedHostNodeId: nodeId,
5317
- resolvedHostSelector: selector,
5318
- resolutionKind: ownerMeta && ownerMeta.componentName[0] !== ownerMeta.componentName[0].toLowerCase() ? "wrapper-to-host" : "direct-host",
5319
- label: getLabel(el),
5320
- rect,
5321
- containerStrategy: parentEl && isFlexOrGridParent(parentEl) ? resolveContainerStrategy(parentEl) : null,
5322
- eligibilitySource: isInspectorEligible(el) ?? "force-allow",
5323
- editorMeta: surfaceMeta
5324
- });
5398
+ const entry = buildMovedElementEntry(el, usedNodeIds);
5399
+ if (!entry) continue;
5400
+ usedNodeIds.add(entry.nodeId);
5401
+ scanned.push(entry);
5325
5402
  }
5326
5403
  const seen = /* @__PURE__ */ new Set();
5327
5404
  setElements(scanned.filter((el) => {
@@ -8526,7 +8603,9 @@ function useExternalDragLoop({
8526
8603
  externalDrag,
8527
8604
  refs,
8528
8605
  resolveNodeElement,
8529
- setDropLine
8606
+ setDropLine,
8607
+ scrollX,
8608
+ scrollY
8530
8609
  );
8531
8610
  return;
8532
8611
  }
@@ -8559,10 +8638,8 @@ function useExternalDragLoop({
8559
8638
  return () => cancelAnimationFrame(rafId);
8560
8639
  }, [externalDrag, elements, refs, resolveNodeElement, setDropLine]);
8561
8640
  }
8562
- function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, setDropLine) {
8563
- const scrollX = window.scrollX;
8564
- const scrollY = window.scrollY;
8565
- const intent = engine.resolve(x, y, scrollX, scrollY, scrollX, scrollY);
8641
+ function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, setDropLine, startScrollX, startScrollY) {
8642
+ const intent = engine.resolve(x, y, window.scrollX, window.scrollY, startScrollX, startScrollY);
8566
8643
  if (!intent || intent.mode !== "reorder") {
8567
8644
  useDesignModeStore.getState().cancelExternalDrag();
8568
8645
  setDropLine(null);
@@ -4125,6 +4125,7 @@ var FIBER_KEY_PREFIX = "__reactFiber$";
4125
4125
  var PROPS_KEY_PREFIX = "__reactProps$";
4126
4126
  var metaCache = /* @__PURE__ */ new WeakMap();
4127
4127
  var directMetaCache = /* @__PURE__ */ new WeakMap();
4128
+ var usageMetaCache = /* @__PURE__ */ new WeakMap();
4128
4129
  function readEditorMetaValue(value) {
4129
4130
  if (typeof value === "function") {
4130
4131
  return value();
@@ -4289,6 +4290,34 @@ function getSingleHostElement(owner) {
4289
4290
  }
4290
4291
  return hosts.length === 1 ? hosts[0] : null;
4291
4292
  }
4293
+ function getUsageMetaOwnerFiber(el) {
4294
+ let fiber = getFiberSelectionFromElement(el).fiber;
4295
+ while (fiber) {
4296
+ const meta = readEditorMetaValue(fiber.memoizedProps?.__editorMeta);
4297
+ if (meta && meta.origin !== "kit") return fiber;
4298
+ fiber = fiber.return;
4299
+ }
4300
+ return null;
4301
+ }
4302
+ function projectFromOwner(owner, el) {
4303
+ if (!owner) return null;
4304
+ if (getSingleHostElement(owner) !== el) return null;
4305
+ return readEditorMetaValue(owner.memoizedProps?.__editorMeta);
4306
+ }
4307
+ function projectUsageMeta(el) {
4308
+ const selection = getFiberSelectionFromElement(el);
4309
+ const cached = readFiberAwareCache(usageMetaCache, el, selection);
4310
+ if (cached !== void 0) return cached;
4311
+ return writeFiberAwareCache(usageMetaCache, el, selection, computeUsageMeta(el));
4312
+ }
4313
+ function computeUsageMeta(el) {
4314
+ const direct = getDirectEditorMeta(el);
4315
+ if (direct && direct.origin !== "kit") return null;
4316
+ const usage = projectFromOwner(getUsageMetaOwnerFiber(el), el);
4317
+ if (usage) return usage;
4318
+ if (direct) return null;
4319
+ return projectFromOwner(getEditorMetaOwnerFiber(el), el);
4320
+ }
4292
4321
  function findMetaOwnerElement(el) {
4293
4322
  let current = el;
4294
4323
  while (current) {
@@ -4356,10 +4385,13 @@ function getRuntimeKey(el) {
4356
4385
  _runtimeKeyMap.set(el, key2);
4357
4386
  return key2;
4358
4387
  }
4388
+ function identityMeta(host) {
4389
+ return projectUsageMeta(host) ?? getEditorMeta(host);
4390
+ }
4359
4391
  function getNodeKeyFromElement(el) {
4360
4392
  const resolved = resolveDragSurface(el);
4361
4393
  const host = resolved.host ?? el;
4362
- const meta = getEditorMeta(host);
4394
+ const meta = identityMeta(host);
4363
4395
  if (meta) return meta.nodeId;
4364
4396
  const persisted = host.getAttribute("data-dnd-node-id");
4365
4397
  if (persisted) return persisted;
@@ -4368,7 +4400,7 @@ function getNodeKeyFromElement(el) {
4368
4400
  function getInspectorRefFromElement(el) {
4369
4401
  const resolved = resolveDragSurface(el);
4370
4402
  const host = resolved.host ?? el;
4371
- const meta = getEditorMeta(host);
4403
+ const meta = identityMeta(host);
4372
4404
  if (!meta) return null;
4373
4405
  return { relativePath: meta.file, line: meta.line, column: meta.col };
4374
4406
  }
@@ -5046,13 +5078,6 @@ function isInspectorEligible(el) {
5046
5078
  if (el.hasAttribute("data-dnd-force-allow")) return "force-allow";
5047
5079
  return null;
5048
5080
  }
5049
- function projectUsageMeta(el) {
5050
- if (getDirectEditorMeta(el)) return null;
5051
- const owner = getEditorMetaOwnerFiber(el);
5052
- if (!owner) return null;
5053
- if (getSingleHostElement(owner) !== el) return null;
5054
- return readEditorMetaValue(owner.memoizedProps?.__editorMeta);
5055
- }
5056
5081
  function isRuntimeVisualInternal(el) {
5057
5082
  if (shouldIgnoreAsVirtualRuntime(el)) return true;
5058
5083
  const tag = el.tagName.toLowerCase();
@@ -5082,10 +5107,10 @@ function ensureUniqueNodeId(base, used) {
5082
5107
  function resolveStableNodeId(el, used) {
5083
5108
  const persisted = el.getAttribute(DND_NODE_ID_ATTR);
5084
5109
  if (persisted && !used.has(persisted)) return persisted;
5085
- const metaId = getEditorMetaNodeId(el);
5086
- if (metaId) return ensureUniqueNodeId(metaId, used);
5087
5110
  const projected = projectUsageMeta(el);
5088
5111
  if (projected) return ensureUniqueNodeId(projected.nodeId, used);
5112
+ const metaId = getEditorMetaNodeId(el);
5113
+ if (metaId) return ensureUniqueNodeId(metaId, used);
5089
5114
  return ensureUniqueNodeId(getRuntimeNodeId(el), used);
5090
5115
  }
5091
5116
  function getUniqueSelector(el) {
@@ -5170,6 +5195,36 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5170
5195
  const minW = isExplicit ? INTERACTIVE_MIN : MIN_WIDTH;
5171
5196
  const minH = isExplicit ? INTERACTIVE_MIN : MIN_HEIGHT;
5172
5197
  if (rect.width < minW || rect.height < minH || rect.width === 0 || rect.height === 0) {
5198
+ const isZeroRect = rect.width === 0 || rect.height === 0;
5199
+ const smallEligibility = isInspectorEligible(el) ?? (projectUsageMeta(el) ? "projected-usage" : null);
5200
+ if (smallEligibility !== null && !seenResolvedHosts.has(el)) {
5201
+ const nodeId2 = resolveStableNodeId(el, usedNodeIds);
5202
+ usedNodeIds.add(nodeId2);
5203
+ el.setAttribute(DND_NODE_ID_ATTR, nodeId2);
5204
+ if (!isZeroRect) {
5205
+ seenResolvedHosts.add(el);
5206
+ const surfaceMeta2 = el instanceof HTMLElement ? getDirectEditorMeta(el) ?? void 0 : void 0;
5207
+ const identityMeta3 = projectUsageMeta(el) ?? surfaceMeta2;
5208
+ const selector = getUniqueSelector(el);
5209
+ const parentEl2 = el.parentElement;
5210
+ results.push({
5211
+ nodeId: nodeId2,
5212
+ element: el,
5213
+ selector,
5214
+ sourceMetaNodeId: identityMeta3?.nodeId ?? null,
5215
+ resolvedHostNodeId: nodeId2,
5216
+ resolvedHostSelector: selector,
5217
+ resolutionKind: "direct-host",
5218
+ label: getLabel(el),
5219
+ rect,
5220
+ containerStrategy: parentEl2 && isFlexOrGridParent(parentEl2) ? resolveContainerStrategy(parentEl2) : null,
5221
+ eligibilitySource: smallEligibility,
5222
+ draggable: false,
5223
+ identityOrigin: identityMeta3?.origin ?? null,
5224
+ editorMeta: surfaceMeta2
5225
+ });
5226
+ }
5227
+ }
5173
5228
  if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
5174
5229
  continue;
5175
5230
  }
@@ -5224,13 +5279,15 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5224
5279
  const surfaceMeta = hostEl instanceof HTMLElement ? getDirectEditorMeta(hostEl) ?? void 0 : void 0;
5225
5280
  const ownerMeta = dragSurface.metaOwner && dragSurface.metaOwner instanceof HTMLElement ? getEditorMeta(dragSurface.metaOwner) : null;
5226
5281
  const resolvedSelector = getUniqueSelector(hostEl);
5227
- const draggable = inferredEligibility === "force-allow" || !nodeId.startsWith("runtime:");
5282
+ const usageMeta = hostEl instanceof HTMLElement ? projectUsageMeta(hostEl) : null;
5283
+ const identityMeta2 = usageMeta ?? ownerMeta ?? surfaceMeta;
5284
+ const draggable = inferredEligibility === "force-allow" || !nodeId.startsWith("runtime:") && (identityMeta2?.origin !== "kit" || resolveRowBinding(hostEl) !== null);
5228
5285
  results.push({
5229
5286
  nodeId,
5230
5287
  element: hostEl,
5231
5288
  selector: resolvedSelector,
5232
5289
  draggable,
5233
- sourceMetaNodeId: ownerMeta?.nodeId ?? surfaceMeta?.nodeId ?? null,
5290
+ sourceMetaNodeId: identityMeta2?.nodeId ?? null,
5234
5291
  resolvedHostNodeId: nodeId,
5235
5292
  resolvedHostSelector: resolvedSelector,
5236
5293
  resolutionKind: dragSurface.kind,
@@ -5238,6 +5295,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5238
5295
  rect: hostRect,
5239
5296
  containerStrategy,
5240
5297
  eligibilitySource: inferredEligibility,
5298
+ identityOrigin: identityMeta2?.origin ?? null,
5241
5299
  editorMeta: surfaceMeta
5242
5300
  });
5243
5301
  if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
@@ -5247,6 +5305,45 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5247
5305
  }
5248
5306
  return results;
5249
5307
  }
5308
+ function buildMovedElementEntry(el, usedNodeIds) {
5309
+ const rect = el.getBoundingClientRect();
5310
+ if (rect.width === 0 || rect.height === 0) {
5311
+ if (isInspectorEligible(el) !== null || projectUsageMeta(el) !== null) {
5312
+ const nodeId2 = resolveStableNodeId(el, usedNodeIds);
5313
+ usedNodeIds.add(nodeId2);
5314
+ el.setAttribute(DND_NODE_ID_ATTR, nodeId2);
5315
+ }
5316
+ return null;
5317
+ }
5318
+ const nodeId = resolveStableNodeId(el, usedNodeIds);
5319
+ el.setAttribute(DND_NODE_ID_ATTR, nodeId);
5320
+ const parentEl = el.parentElement;
5321
+ const surfaceMeta = getDirectEditorMeta(el) ?? void 0;
5322
+ const ownerMeta = getEditorMeta(el);
5323
+ const identityMeta2 = projectUsageMeta(el) ?? ownerMeta ?? surfaceMeta;
5324
+ const selector = getUniqueSelector(el);
5325
+ const eligibility = isInspectorEligible(el);
5326
+ return {
5327
+ nodeId,
5328
+ element: el,
5329
+ selector,
5330
+ // Same draggable rule as the main scan, INCLUDING the force-allow
5331
+ // escape — an element that was draggable when the user moved it must
5332
+ // not silently lose draggability for having landed below MAX_DEPTH.
5333
+ // Same kit-origin bound-row exemption as the main scan (see there).
5334
+ draggable: eligibility === "force-allow" || !nodeId.startsWith("runtime:") && (identityMeta2?.origin !== "kit" || resolveRowBinding(el) !== null),
5335
+ sourceMetaNodeId: identityMeta2?.nodeId ?? null,
5336
+ resolvedHostNodeId: nodeId,
5337
+ resolvedHostSelector: selector,
5338
+ resolutionKind: ownerMeta && ownerMeta.componentName[0] !== ownerMeta.componentName[0].toLowerCase() ? "wrapper-to-host" : "direct-host",
5339
+ label: getLabel(el),
5340
+ rect,
5341
+ containerStrategy: parentEl && isFlexOrGridParent(parentEl) ? resolveContainerStrategy(parentEl) : null,
5342
+ eligibilitySource: eligibility ?? "force-allow",
5343
+ identityOrigin: identityMeta2?.origin ?? null,
5344
+ editorMeta: surfaceMeta
5345
+ };
5346
+ }
5250
5347
  function useElementScanner(enabled) {
5251
5348
  const [elements, setElements] = useState4([]);
5252
5349
  const rafRef = useRef4(0);
@@ -5263,30 +5360,10 @@ function useElementScanner(enabled) {
5263
5360
  const seenEls = new Set(scanned.map((s) => s.element));
5264
5361
  for (const el of Array.from(document.querySelectorAll("[data-dnd-moved]"))) {
5265
5362
  if (seenEls.has(el)) continue;
5266
- const rect = el.getBoundingClientRect();
5267
- if (rect.width === 0 || rect.height === 0) continue;
5268
- const nodeId = resolveStableNodeId(el, usedNodeIds);
5269
- usedNodeIds.add(nodeId);
5270
- el.setAttribute(DND_NODE_ID_ATTR, nodeId);
5271
- const parentEl = el.parentElement;
5272
- const surfaceMeta = getDirectEditorMeta(el) ?? void 0;
5273
- const ownerMeta = getEditorMeta(el);
5274
- const selector = getUniqueSelector(el);
5275
- scanned.push({
5276
- nodeId,
5277
- element: el,
5278
- selector,
5279
- draggable: !nodeId.startsWith("runtime:"),
5280
- sourceMetaNodeId: ownerMeta?.nodeId ?? surfaceMeta?.nodeId ?? null,
5281
- resolvedHostNodeId: nodeId,
5282
- resolvedHostSelector: selector,
5283
- resolutionKind: ownerMeta && ownerMeta.componentName[0] !== ownerMeta.componentName[0].toLowerCase() ? "wrapper-to-host" : "direct-host",
5284
- label: getLabel(el),
5285
- rect,
5286
- containerStrategy: parentEl && isFlexOrGridParent(parentEl) ? resolveContainerStrategy(parentEl) : null,
5287
- eligibilitySource: isInspectorEligible(el) ?? "force-allow",
5288
- editorMeta: surfaceMeta
5289
- });
5363
+ const entry = buildMovedElementEntry(el, usedNodeIds);
5364
+ if (!entry) continue;
5365
+ usedNodeIds.add(entry.nodeId);
5366
+ scanned.push(entry);
5290
5367
  }
5291
5368
  const seen = /* @__PURE__ */ new Set();
5292
5369
  setElements(scanned.filter((el) => {
@@ -8491,7 +8568,9 @@ function useExternalDragLoop({
8491
8568
  externalDrag,
8492
8569
  refs,
8493
8570
  resolveNodeElement,
8494
- setDropLine
8571
+ setDropLine,
8572
+ scrollX,
8573
+ scrollY
8495
8574
  );
8496
8575
  return;
8497
8576
  }
@@ -8524,10 +8603,8 @@ function useExternalDragLoop({
8524
8603
  return () => cancelAnimationFrame(rafId);
8525
8604
  }, [externalDrag, elements, refs, resolveNodeElement, setDropLine]);
8526
8605
  }
8527
- function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, setDropLine) {
8528
- const scrollX = window.scrollX;
8529
- const scrollY = window.scrollY;
8530
- const intent = engine.resolve(x, y, scrollX, scrollY, scrollX, scrollY);
8606
+ function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, setDropLine, startScrollX, startScrollY) {
8607
+ const intent = engine.resolve(x, y, window.scrollX, window.scrollY, startScrollX, startScrollY);
8531
8608
  if (!intent || intent.mode !== "reorder") {
8532
8609
  useDesignModeStore.getState().cancelExternalDrag();
8533
8610
  setDropLine(null);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  meaningfulChildren
3
- } from "./chunk-4VGQJ3PW.mjs";
3
+ } from "./chunk-CO6SWEYJ.mjs";
4
4
 
5
5
  // src/babel-plugin-collection-binding.ts
6
6
  import { VISITOR_KEYS } from "@babel/types";
package/dist/index.d.mts CHANGED
@@ -11,6 +11,7 @@ import { types, PluginObj } from '@babel/core';
11
11
  * The injected prop contains:
12
12
  * nodeId, file, line, col, componentName, staticProps, movable, parentNodeId
13
13
  * + (ARCH-883 F1) slot, propBindings, propOwner
14
+ * + (ARCH-883-KOI) origin
14
15
  *
15
16
  * At runtime the Fiber bridge reads `memoizedProps.__editorMeta` to resolve
16
17
  * element identity without any DOM attributes.
@@ -30,6 +31,10 @@ import { types, PluginObj } from '@babel/core';
30
31
  * 3. `movable: false` for kit-origin nodes — reordering inside a kit template
31
32
  * affects N usages, a permanent non-objective (PRD §1.3).
32
33
  *
34
+ * ARCH-883-KOI (kit-origin-identity, CREW round) adds a FOURTH: `origin: 'kit'`
35
+ * on every kit-origin node (see `types/editor-meta.ts`'s docstring on that
36
+ * field for why it exists separately from `movable`).
37
+ *
33
38
  * `parentNodeId` stays INTRA-FILE by construction (it only ever walks JSX
34
39
  * ancestors within the file being transformed), so it never links a kit template
35
40
  * node to a screen usage; per SDD §2.3 it must not be used across that seam.
@@ -174,6 +179,52 @@ interface EditorMeta {
174
179
  * DOM. The root node of a kit template emits `null` here.
175
180
  */
176
181
  parentNodeId: string | null;
182
+ /**
183
+ * `'kit'` when the node comes from a kit template — emitted with
184
+ * `origin: 'kit'`, or carrying a slot (which makes it kit-origin by
185
+ * definition). Absent means screen-origin.
186
+ *
187
+ * WHY THIS EXISTS SEPARATELY FROM `movable`. Both are `false`/`'kit'` for a
188
+ * kit node, so it looks redundant, and it is not: `movable` is ALSO `false`
189
+ * for map-callback roots, fragment children and expression-container siblings
190
+ * in ordinary SCREENS (RFC E2.4 §2.7). A consumer that wants "may this node
191
+ * be reordered at all, by any write path" must not read `movable` — it would
192
+ * over-block exactly the list instances that reorder through their backing
193
+ * data. `origin` answers the narrower, structural question: this node is one
194
+ * template behind N usages, so no write path can ever reorder it (PRD §1.3).
195
+ *
196
+ * ABSENCE IS AMBIGUOUS, NOT SAFE, on today's fleet — this is NOT the same
197
+ * claim as "reads the same as screen-origin, the pre-existing meaning of
198
+ * every meta" (an earlier version of this doc said exactly that, and it is
199
+ * false for the current deployed fleet; kept here as the record of the
200
+ * CREW correction, ARCH-883-KOI F4). Two axes move independently:
201
+ *
202
+ * 1. Whether kit COMPONENTS render instrumented (kit-runtime's late-
203
+ * binding fix — a separate repo/change from this one).
204
+ * 2. Whether the `@archie/devtools` version that built a given bundle
205
+ * emits `origin` at all (this field).
206
+ *
207
+ * A page can be on axis-1-yes / axis-2-no TODAY: kit-runtime already
208
+ * instruments kit components on pages whose screen (or kit) build used a
209
+ * pre-`origin` `@archie/devtools`. On such a page a kit-internal host's
210
+ * template meta is present (per the P0-1 thunk) but carries no `origin`
211
+ * key — indistinguishable from a genuine screen-origin meta by this field
212
+ * alone. A consumer that trusts absence-means-screen-origin on that page
213
+ * reproduces the exact F1 bug this change fixes, silently.
214
+ *
215
+ * There is no per-node way to close this gap (a missing field cannot prove
216
+ * its own absence is meaningful). The mitigation is PAGE-LEVEL: the
217
+ * standalone inspector script's `INSPECTOR_SCRIPT_LOADED` handshake reports
218
+ * a `kitOriginIdentity` capability bit (see `inspector.standalone.js`) that
219
+ * tells the host "this SCRIPT understands `origin` and projects across the
220
+ * kit boundary" — it does NOT and cannot tell the host whether the PAGE's
221
+ * own plugin build emitted the field, which is the acg/kitgen build's
222
+ * `@archie/devtools` version, an axis this package has no visibility into.
223
+ * Hosts that need a hard guarantee must track that build version
224
+ * themselves; this capability bit only rules out "the runtime code is too
225
+ * old to know what `origin` means" as a failure mode.
226
+ */
227
+ origin?: 'kit';
177
228
  /**
178
229
  * Curated kit slot label, e.g. `"AuthFrame/aside/headline"` (SDD §2.6).
179
230
  *
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ import { types, PluginObj } from '@babel/core';
11
11
  * The injected prop contains:
12
12
  * nodeId, file, line, col, componentName, staticProps, movable, parentNodeId
13
13
  * + (ARCH-883 F1) slot, propBindings, propOwner
14
+ * + (ARCH-883-KOI) origin
14
15
  *
15
16
  * At runtime the Fiber bridge reads `memoizedProps.__editorMeta` to resolve
16
17
  * element identity without any DOM attributes.
@@ -30,6 +31,10 @@ import { types, PluginObj } from '@babel/core';
30
31
  * 3. `movable: false` for kit-origin nodes — reordering inside a kit template
31
32
  * affects N usages, a permanent non-objective (PRD §1.3).
32
33
  *
34
+ * ARCH-883-KOI (kit-origin-identity, CREW round) adds a FOURTH: `origin: 'kit'`
35
+ * on every kit-origin node (see `types/editor-meta.ts`'s docstring on that
36
+ * field for why it exists separately from `movable`).
37
+ *
33
38
  * `parentNodeId` stays INTRA-FILE by construction (it only ever walks JSX
34
39
  * ancestors within the file being transformed), so it never links a kit template
35
40
  * node to a screen usage; per SDD §2.3 it must not be used across that seam.
@@ -174,6 +179,52 @@ interface EditorMeta {
174
179
  * DOM. The root node of a kit template emits `null` here.
175
180
  */
176
181
  parentNodeId: string | null;
182
+ /**
183
+ * `'kit'` when the node comes from a kit template — emitted with
184
+ * `origin: 'kit'`, or carrying a slot (which makes it kit-origin by
185
+ * definition). Absent means screen-origin.
186
+ *
187
+ * WHY THIS EXISTS SEPARATELY FROM `movable`. Both are `false`/`'kit'` for a
188
+ * kit node, so it looks redundant, and it is not: `movable` is ALSO `false`
189
+ * for map-callback roots, fragment children and expression-container siblings
190
+ * in ordinary SCREENS (RFC E2.4 §2.7). A consumer that wants "may this node
191
+ * be reordered at all, by any write path" must not read `movable` — it would
192
+ * over-block exactly the list instances that reorder through their backing
193
+ * data. `origin` answers the narrower, structural question: this node is one
194
+ * template behind N usages, so no write path can ever reorder it (PRD §1.3).
195
+ *
196
+ * ABSENCE IS AMBIGUOUS, NOT SAFE, on today's fleet — this is NOT the same
197
+ * claim as "reads the same as screen-origin, the pre-existing meaning of
198
+ * every meta" (an earlier version of this doc said exactly that, and it is
199
+ * false for the current deployed fleet; kept here as the record of the
200
+ * CREW correction, ARCH-883-KOI F4). Two axes move independently:
201
+ *
202
+ * 1. Whether kit COMPONENTS render instrumented (kit-runtime's late-
203
+ * binding fix — a separate repo/change from this one).
204
+ * 2. Whether the `@archie/devtools` version that built a given bundle
205
+ * emits `origin` at all (this field).
206
+ *
207
+ * A page can be on axis-1-yes / axis-2-no TODAY: kit-runtime already
208
+ * instruments kit components on pages whose screen (or kit) build used a
209
+ * pre-`origin` `@archie/devtools`. On such a page a kit-internal host's
210
+ * template meta is present (per the P0-1 thunk) but carries no `origin`
211
+ * key — indistinguishable from a genuine screen-origin meta by this field
212
+ * alone. A consumer that trusts absence-means-screen-origin on that page
213
+ * reproduces the exact F1 bug this change fixes, silently.
214
+ *
215
+ * There is no per-node way to close this gap (a missing field cannot prove
216
+ * its own absence is meaningful). The mitigation is PAGE-LEVEL: the
217
+ * standalone inspector script's `INSPECTOR_SCRIPT_LOADED` handshake reports
218
+ * a `kitOriginIdentity` capability bit (see `inspector.standalone.js`) that
219
+ * tells the host "this SCRIPT understands `origin` and projects across the
220
+ * kit boundary" — it does NOT and cannot tell the host whether the PAGE's
221
+ * own plugin build emitted the field, which is the acg/kitgen build's
222
+ * `@archie/devtools` version, an axis this package has no visibility into.
223
+ * Hosts that need a hard guarantee must track that build version
224
+ * themselves; this capability bit only rules out "the runtime code is too
225
+ * old to know what `origin` means" as a failure mode.
226
+ */
227
+ origin?: 'kit';
177
228
  /**
178
229
  * Curated kit slot label, e.g. `"AuthFrame/aside/headline"` (SDD §2.6).
179
230
  *
package/dist/index.js CHANGED
@@ -733,6 +733,11 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
733
733
  parentNodeId ? t.stringLiteral(parentNodeId) : t.nullLiteral()
734
734
  )
735
735
  ]);
736
+ if (isKitOrigin) {
737
+ metaObj.properties.push(
738
+ t.objectProperty(t.identifier("origin"), t.stringLiteral("kit"))
739
+ );
740
+ }
736
741
  if (slot !== null) {
737
742
  metaObj.properties.push(
738
743
  t.objectProperty(t.identifier("slot"), t.stringLiteral(slot))
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  __dirname,
3
3
  babelPluginEditorMeta
4
- } from "./chunk-4VGQJ3PW.mjs";
4
+ } from "./chunk-CO6SWEYJ.mjs";
5
5
 
6
6
  // src/index.ts
7
7
  import { readFileSync } from "fs";
@@ -1,10 +1,4 @@
1
- /**
2
- * Archie Inspector - Standalone script for CDN injection.
3
- * Set before loading: window.__ARCHIE_INSPECTOR_TARGET_ORIGIN__, window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__
4
- * Then load: <script src="https://your-cdn/inspector.standalone.js"></script>
5
- * Auto-runs if configured; or call window.__ARCHIE_INSPECTOR_RUN__().
6
- * Security: No eval(), no "*" origins; style values sanitized.
7
- */
1
+ /** Archie Inspector standalone: safe DOM inspector for CDN use. */
8
2
  ;(function () {
9
3
  "use strict"
10
4
  // Capture originals at module load time so the patch below is never recursive
@@ -95,19 +89,10 @@
95
89
  var FIBER_KEY_PREFIX = "__reactFiber$"
96
90
  var PROPS_KEY_PREFIX = "__reactProps$"
97
91
 
98
- // ARCH-883 P0-1: babel-plugin-editor-meta.ts (TypeScript context) now
99
- // emits __editorMeta as a zero-arg function, not the object literal
100
- // directly, so React never writes it into the DOM. Every extraction
101
- // site below must invoke the value when it is a function. Mirrors
102
- // readEditorMetaValue in fiber-bridge.ts — if this changes, update
103
- // that copy to maintain parity.
92
+ // __editorMeta is emitted as a thunk; invoke it (parity with fiber-bridge).
104
93
  function resolveEditorMetaValue(value) {
105
94
  if (typeof value === "function") {
106
- // Each call re-evaluates the plugin-emitted `() => ({...})`
107
- // closure and returns a FRESH object literal — never rely on
108
- // meta object identity (`===`) across two separate reads, even
109
- // within the same render (mirrors readEditorMetaValue in
110
- // fiber-bridge.ts).
95
+ // Invoke thunk and return a fresh meta object (do not rely on identity).
111
96
  return value()
112
97
  }
113
98
  // Twin parity with fiber-bridge.ts's readEditorMetaValue: only
@@ -338,19 +323,56 @@
338
323
  return hosts.length === 1 ? hosts[0] : null
339
324
  }
340
325
 
341
- // Vanilla twin of projectUsageMeta (useElementScanner.ts): the usage meta
342
- // when `el` is the SINGLE host root a component usage renders and that
343
- // component destructured its props, so the host fiber carries no meta.
344
- function projectUsageMetaFromElement(el) {
345
- if (getDirectEditorMetaFromElement(el)) return null
346
- var owner = getEditorMetaOwnerFiberFromElement(el)
326
+ // The fiber owning the nearest NON-kit __editorMeta for `el` — the usage
327
+ // written in a screen, reached across however many kit template frames
328
+ // sit in between. Vanilla twin of getUsageMetaOwnerFiber
329
+ // (fiber-bridge.ts); see that docstring for why the nearest meta is the
330
+ // wrong one once the kit itself is instrumented.
331
+ function getUsageMetaOwnerFiberFromElement(el) {
332
+ var fiber = getFiberFromElement(el)
333
+ while (fiber) {
334
+ if (fiber.memoizedProps && fiber.memoizedProps.__editorMeta) {
335
+ var meta = resolveEditorMetaValue(fiber.memoizedProps.__editorMeta)
336
+ if (meta && meta.origin !== "kit") return fiber
337
+ }
338
+ fiber = fiber["return"]
339
+ }
340
+ return null
341
+ }
342
+
343
+ // The meta `owner` carries, but only if `el` is the single host it renders.
344
+ function projectFromOwnerFiber(owner, el) {
347
345
  if (!owner) return null
348
346
  if (getSingleHostElementFromFiber(owner) !== el) return null
349
347
  return resolveEditorMetaValue(owner.memoizedProps.__editorMeta)
350
348
  }
351
349
 
352
- // The nodeId this element carries AS ITS OWN — direct fiber meta, or the
353
- // single-host projection. NEVER an inherited ancestor nodeId: walking the
350
+ // Vanilla twin of projectUsageMeta (fiber-bridge.ts): the usage meta when
351
+ // `el` is the SINGLE host root a component usage renders and that usage's
352
+ // meta never reached the host fiber — because the component destructured
353
+ // its props, or because the plugin sanitized it out of the spread (which
354
+ // is every kit component, once the kit is instrumented).
355
+ function projectUsageMetaFromElement(el) {
356
+ var direct = getDirectEditorMetaFromElement(el)
357
+ if (direct && direct.origin !== "kit") return null
358
+
359
+ var usage = projectFromOwnerFiber(
360
+ getUsageMetaOwnerFiberFromElement(el),
361
+ el
362
+ )
363
+ if (usage) return usage
364
+
365
+ // A kit-internal host already has its own identity; nothing to project.
366
+ if (direct) return null
367
+
368
+ return projectFromOwnerFiber(
369
+ getEditorMetaOwnerFiberFromElement(el),
370
+ el
371
+ )
372
+ }
373
+
374
+ // The nodeId this element carries AS ITS OWN — the single-host projection,
375
+ // then direct fiber meta. NEVER an inherited ancestor nodeId: walking the
354
376
  // fiber chain here made every UNSTAMPED subtree (any two siblings
355
377
  // rendered by a component the plugin never transformed — anything from
356
378
  // node_modules, and here SVG internals too, since this twin accepts any
@@ -358,13 +380,39 @@
358
380
  // direct-meta-ONLY rule would make every kit component that destructures
359
381
  // its props look like a singleton — which is the fail-OPEN mistake, and
360
382
  // would write a literal into a mapped usage, changing every row.
361
- // See instance-identity.ts for the full rationale.
383
+ // The projection is checked FIRST because an instrumented kit gives every
384
+ // <Card/> host the same template nodeId; two unrelated Cards under one
385
+ // parent would otherwise read as one repeated source position.
386
+ //
387
+ // ARCH-883-KOI F1 (CREW round): a direct KIT meta is the TEMPLATE's
388
+ // position, shared by every usage. When a screen USAGE exists above (the
389
+ // projection bailed only because `el` is a template-internal node BELOW
390
+ // the usage's single host root — a <Card/>'s inner <span>, not the outer
391
+ // <div> the projection already caught) the id CANNOT be the bare usage
392
+ // nodeId: every other template-internal node under that SAME usage (the
393
+ // sibling <p>) would walk to the identical usage fiber and report the
394
+ // identical id, collapsing two distinct template positions into one and
395
+ // making `findCollectionWindow` read them as a false repeated collection
396
+ // with no real key — refusing a legitimate kit-internal edit. The id is
397
+ // instead a COMPOSITE of the usage's nodeId and this node's OWN template
398
+ // position: stable across N repeats of the SAME position (the
399
+ // DataTable-row case — same usage, same template row id, correctly still
400
+ // a collection) while distinct across different positions under one
401
+ // usage (span vs p). Mirrors ownNodeId in instance-identity.ts — if this
402
+ // changes, update that copy to maintain parity.
362
403
  function ownMetaNodeId(el) {
363
404
  if (!el || el.nodeType !== 1) return null
364
- var direct = getDirectEditorMetaFromElement(el)
365
- if (direct && direct.nodeId) return direct.nodeId
366
405
  var projected = projectUsageMetaFromElement(el)
367
- return projected && projected.nodeId ? projected.nodeId : null
406
+ if (projected && projected.nodeId) return projected.nodeId
407
+ var direct = getDirectEditorMetaFromElement(el)
408
+ if (direct && direct.origin === "kit") {
409
+ var usageOwner = getUsageMetaOwnerFiberFromElement(el)
410
+ var usage =
411
+ usageOwner &&
412
+ resolveEditorMetaValue(usageOwner.memoizedProps.__editorMeta)
413
+ if (usage && usage.nodeId) return usage.nodeId + " " + direct.nodeId
414
+ }
415
+ return direct && direct.nodeId ? direct.nodeId : null
368
416
  }
369
417
 
370
418
  // The repeated-row boundary above `el`, or a search outcome explaining
@@ -786,6 +834,17 @@
786
834
  function getExactSourceSignatureFromElement(element) {
787
835
  if (!element) return ""
788
836
 
837
+ // The projected USAGE outranks the element's own meta. On an
838
+ // instrumented kit the host of a `<Card/>` carries the kit TEMPLATE's
839
+ // meta, so reading the direct one first signs the selection as
840
+ // `card.jsx` — a file the per-screen save path cannot write, which
841
+ // surfaces to the user as "no editable source for these changes".
842
+ var usageMeta = projectUsageMetaFromElement(element)
843
+ var usageSignature = usageMeta
844
+ ? getSharedSignatureFromMeta(usageMeta)
845
+ : ""
846
+ if (usageSignature) return usageSignature
847
+
789
848
  var directMeta = getDirectEditorMetaFromElement(element)
790
849
  var directSignature = directMeta
791
850
  ? getSharedSignatureFromMeta(directMeta)
@@ -1099,6 +1158,15 @@
1099
1158
  function getInspectorInfoFromElement(element) {
1100
1159
  if (!element) return null
1101
1160
 
1161
+ // Same precedence as getExactSourceSignatureFromElement: the usage the
1162
+ // screen wrote outranks the kit template that rendered the host. This
1163
+ // is the value that becomes `inspectorSource` in ELEMENT_SELECTED, so
1164
+ // getting it wrong is what makes the host app refuse the save.
1165
+ var usageMeta = projectUsageMetaFromElement(element)
1166
+ if (usageMeta) {
1167
+ return buildInspectorInfoFromMeta(usageMeta)
1168
+ }
1169
+
1102
1170
  var directMeta = getDirectEditorMetaFromElement(element)
1103
1171
  if (directMeta) {
1104
1172
  return buildInspectorInfoFromMeta(directMeta)
@@ -1350,7 +1418,11 @@
1350
1418
  // 2.3.0 (ARCH-1065 A17-support): additive only — `QUERY_COLLECTION_ORDER`
1351
1419
  // in the message protocol and `capabilities.collectionOrderQuery`
1352
1420
  // below. No existing field changed shape, so `protocolVersion` stays 1.
1353
- version: "2.3.0",
1421
+ // 2.4.0 (ARCH-883-KOI, CREW round): additive only — `origin` on
1422
+ // `EditorMeta`, the usage-projected `ownMetaNodeId`/`inspectorSource`/
1423
+ // `editorInstance` resolution, and `capabilities.kitOriginIdentity`
1424
+ // below. No existing field changed shape, so `protocolVersion` stays 1.
1425
+ version: "2.4.0",
1354
1426
  protocolVersion: 1,
1355
1427
  capabilities: {
1356
1428
  // 2.1.1: function-valued __editorMeta props are invoked before any
@@ -1391,6 +1463,24 @@
1391
1463
  // concept, singular mechanism — are treated as opaque strings
1392
1464
  // elsewhere in this file).
1393
1465
  bindingContract: readBindingContractMeta(),
1466
+ // ARCH-883-KOI F4 (v2.4.0) — this SCRIPT resolves `EditorMeta.origin`
1467
+ // and projects identity across an instrumented kit boundary
1468
+ // (`ownMetaNodeId`, `projectUsageMetaFromElement`,
1469
+ // `getInspectorInfoFromElement`, `resolveInstanceIdentity`). Hosts
1470
+ // MUST feature-detect on this flag before trusting `origin`
1471
+ // (absent or `editorMeta.origin === 'kit'`) as meaningful: a frozen
1472
+ // pre-2.4.0 inspector never reads the field at all, so its
1473
+ // `inspectorSource`/`editorInstance` on an instrumented-kit page
1474
+ // reproduce the exact F1 bug this version fixes.
1475
+ //
1476
+ // What this bit CANNOT tell the host: whether the PAGE's own build
1477
+ // emitted `origin` on its metas in the first place — that is the
1478
+ // `@archie/devtools` version the acg/kitgen esbuild path had
1479
+ // installed when the screen (or the kit) was built, a completely
1480
+ // separate axis from this CDN script's version (see the `origin`
1481
+ // field's docstring in `types/editor-meta.ts` for the full
1482
+ // reasoning, and why NO per-node signal can close that gap).
1483
+ kitOriginIdentity: true,
1394
1484
  },
1395
1485
  })
1396
1486
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archie/devtools",
3
- "version": "0.0.31",
3
+ "version": "0.0.33",
4
4
  "description": "DevTools for Archie generated applications - Route synchronization and editor communication",
5
5
  "repository": {
6
6
  "type": "git",