@archie/devtools 0.0.21 → 0.0.23

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.
@@ -95,6 +95,28 @@
95
95
  var FIBER_KEY_PREFIX = "__reactFiber$"
96
96
  var PROPS_KEY_PREFIX = "__reactProps$"
97
97
 
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.
104
+ function resolveEditorMetaValue(value) {
105
+ 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).
111
+ return value()
112
+ }
113
+ // Twin parity with fiber-bridge.ts's readEditorMetaValue: only
114
+ // undefined/null is a miss, not "any falsy value" — __editorMeta is
115
+ // always an object or a function in practice, so this is a no-op
116
+ // today, but keeps the two implementations provably equivalent.
117
+ return value === undefined || value === null ? null : value
118
+ }
119
+
98
120
  function hasEditorMetaInFiberChain(fiber) {
99
121
  var current = fiber
100
122
  while (current) {
@@ -195,7 +217,7 @@
195
217
  var fiber = getFiberFromElement(el)
196
218
  while (fiber) {
197
219
  if (fiber.memoizedProps && fiber.memoizedProps.__editorMeta) {
198
- return fiber.memoizedProps.__editorMeta
220
+ return resolveEditorMetaValue(fiber.memoizedProps.__editorMeta)
199
221
  }
200
222
  fiber = fiber["return"]
201
223
  }
@@ -205,7 +227,7 @@
205
227
  function getDirectEditorMetaFromElement(el) {
206
228
  var fiber = getFiberFromElement(el)
207
229
  if (fiber && fiber.memoizedProps && fiber.memoizedProps.__editorMeta) {
208
- return fiber.memoizedProps.__editorMeta
230
+ return resolveEditorMetaValue(fiber.memoizedProps.__editorMeta)
209
231
  }
210
232
  return null
211
233
  }
@@ -231,7 +253,7 @@
231
253
  break
232
254
  }
233
255
  if (current.memoizedProps && current.memoizedProps.__editorMeta) {
234
- return current.memoizedProps.__editorMeta
256
+ return resolveEditorMetaValue(current.memoizedProps.__editorMeta)
235
257
  }
236
258
  current = current["return"]
237
259
  }
@@ -400,6 +422,12 @@
400
422
  return normalizedTarget
401
423
  }
402
424
 
425
+ // Palette-inserted elements have no React fiber and no source ref, so
426
+ // the source-signature walk below would skip past them and select the
427
+ // nearest ancestor with a source ref (the original <section>/<main>).
428
+ // We tag them with `data-dnd-force-allow` precisely to claim their
429
+ // own identity for selection/arrange; honor that tag here so a click
430
+ // on an inserted Card / Hero / Table targets the insert itself.
403
431
  var current = normalizedTarget
404
432
  while (
405
433
  current &&
@@ -407,6 +435,9 @@
407
435
  current !== document.documentElement &&
408
436
  !isInspectorElement(current)
409
437
  ) {
438
+ if (current.hasAttribute && current.hasAttribute("data-dnd-force-allow")) {
439
+ return current
440
+ }
410
441
  if (getExactSourceSignatureFromElement(current)) {
411
442
  return current
412
443
  }
@@ -855,9 +886,14 @@
855
886
  postMessageToParent({
856
887
  type: "INSPECTOR_SCRIPT_LOADED",
857
888
  timestamp: Date.now(),
858
- version: "2.0.3",
889
+ version: "2.1.1",
859
890
  protocolVersion: 1,
860
891
  capabilities: {
892
+ // 2.1.1: function-valued __editorMeta props are invoked before any
893
+ // postMessage, so meta payloads stay structured-cloneable. Hosts can
894
+ // gate on this bit to confirm the deployed CDN artifact carries the
895
+ // fix (the 2.1.0 URL is overwritten in place on publish).
896
+ functionValuedEditorMeta: true,
861
897
  inspect: true,
862
898
  dndMove: true,
863
899
  dndResize: true,
@@ -865,6 +901,11 @@
865
901
  hostControlledHistory: true,
866
902
  arrangeStructuredHistory: true,
867
903
  arrangeStrongNodeIdentity: true,
904
+ // Unified mode: SET_EDITOR_MODE "inspect" also enables the arrange
905
+ // (design) runtime — selection and drag/resize coexist. Hosts MUST
906
+ // feature-detect on this flag: a frozen 2.0.x inspector disables
907
+ // design mode on "inspect" and needs the legacy "design" mode.
908
+ unifiedInspectArrange: true,
868
909
  },
869
910
  })
870
911
 
@@ -902,6 +943,35 @@
902
943
  var inspectorCleanupMap = new WeakMap()
903
944
  var BADGE_UPDATE_DELAY_MS = 50
904
945
 
946
+ /**
947
+ * Bridge to design-mode store: publish current inspector selection as
948
+ * DND nodeIds so arrange overlay knows when to show resize handles +
949
+ * enable drag (single-select only). If the design-mode bundle hasn't
950
+ * mounted yet, buffer the latest selection for it to drain on mount.
951
+ */
952
+ function publishSelectionToDesignMode() {
953
+ var ids = []
954
+ for (var i = 0; i < selectedElements.length; i += 1) {
955
+ var el = selectedElements[i]
956
+ if (!el || typeof el.getAttribute !== "function") continue
957
+ var nodeId = el.getAttribute("data-dnd-node-id")
958
+ if (nodeId) ids.push(nodeId)
959
+ }
960
+ var bridge = window.__ARCHIE_DESIGN_MODE__
961
+ if (bridge && typeof bridge.setInspectorSelection === "function") {
962
+ try {
963
+ bridge.setInspectorSelection(ids)
964
+ } catch (err) {
965
+ // Defensive: never let a bridge error break inspector selection.
966
+ if (window._inspectorDebug) {
967
+ console.warn("[archie-inspector] design-mode bridge error", err)
968
+ }
969
+ }
970
+ } else {
971
+ window.__ARCHIE_DESIGN_MODE_PENDING__ = ids
972
+ }
973
+ }
974
+
905
975
  if (!window._inspectorSelectedElement) {
906
976
  window._inspectorSelectedElement = null
907
977
  }
@@ -925,7 +995,12 @@
925
995
  element === overlay ||
926
996
  element === selectedElementBadge ||
927
997
  element.id === "inspector-ping-effect" ||
928
- (element.closest && element.closest("[data-inspector-badge]"))
998
+ (element.closest && element.closest("[data-inspector-badge]")) ||
999
+ // Design-mode (arrange) overlay elements: HitArea, resize handles,
1000
+ // drop-line, etc. These belong to the unified visual editor and must
1001
+ // pass mousedown/click events through to React (dnd-kit) instead of
1002
+ // being treated as inspect targets.
1003
+ (element.closest && element.closest("[data-design-mode-ui]"))
929
1004
  )
930
1005
  }
931
1006
 
@@ -1366,6 +1441,7 @@
1366
1441
  window._inspectorSelectedElement = null
1367
1442
  lastMultiSelectIntent = false
1368
1443
  isInspecting = true
1444
+ publishSelectionToDesignMode()
1369
1445
 
1370
1446
  const applyCursorStyles = () => {
1371
1447
  if (document.body) {
@@ -1396,6 +1472,7 @@
1396
1472
  window.__ARCHIE_INSPECTOR__.selectedElement = null
1397
1473
  }
1398
1474
  lastMultiSelectIntent = false
1475
+ publishSelectionToDesignMode()
1399
1476
  try {
1400
1477
  safeQuerySelectorAll('[data-inspector-editing="true"]').forEach(
1401
1478
  function (el) {
@@ -1422,6 +1499,12 @@
1422
1499
  )
1423
1500
  }
1424
1501
 
1502
+ // Last mode explicitly commanded by the host via SET_EDITOR_MODE. Used
1503
+ // to (a) make repeated "inspect" commands selection-preserving no-ops
1504
+ // and (b) suppress design-mode runtime echoes that would otherwise be
1505
+ // misread by the host as a stale mode (see runtimeBridgeHandler).
1506
+ var hostCommandedEditorMode = "off"
1507
+
1425
1508
  function setEditorModeFromHost(mode, options) {
1426
1509
  var historyMode =
1427
1510
  options && options.hostControlledHistory ? "host" : "local"
@@ -1439,14 +1522,30 @@
1439
1522
  historyMode: historyMode,
1440
1523
  })
1441
1524
 
1525
+ hostCommandedEditorMode = mode === "inspect" || mode === "design" ? mode : "off"
1526
+
1442
1527
  if (mode === "inspect") {
1443
- dispatchHostCommand({ type: "set-design-mode", enabled: false })
1528
+ // Unified mode: visual editor (inspect) + arrange (design) coexist.
1529
+ // The arrange overlay stays passive until the inspector publishes a
1530
+ // single-element selection (via publishSelectionToDesignMode), at
1531
+ // which point it lights up the resize/drag controls on that element.
1532
+ dispatchHostCommand({ type: "set-design-mode", enabled: true })
1533
+ if (isInspecting) {
1534
+ // Recovery re-send (focus/visibility/online): the session is
1535
+ // already live. Activating again would wipe the user's current
1536
+ // selection and retract the arrange handles mid-session — echo
1537
+ // confirmation only.
1538
+ postMessageToParent({ type: "EDITOR_MODE_CHANGED", mode: "inspect" })
1539
+ return
1540
+ }
1444
1541
  activateInspectionMode()
1445
1542
  postMessageToParent({ type: "EDITOR_MODE_CHANGED", mode: "inspect" })
1446
1543
  return
1447
1544
  }
1448
1545
 
1449
1546
  if (mode === "design") {
1547
+ // Legacy "design-only" mode kept for backwards compatibility with
1548
+ // hosts that haven't migrated to the unified flow.
1450
1549
  deactivateInspectionMode({ notifyParent: false })
1451
1550
  dispatchHostCommand({ type: "set-design-mode", enabled: true })
1452
1551
  postMessageToParent({ type: "EDITOR_MODE_CHANGED", mode: "design" })
@@ -1948,6 +2047,19 @@
1948
2047
  : null,
1949
2048
  attributes: attributes,
1950
2049
  inspectorSource: inspectorInfo,
2050
+ // ARCH-883 F1 — FORWARD THE META WHOLE. The plugin now also emits
2051
+ // `slot`, `propBindings` and `propOwner` (SDD §2.3/§2.6) and the editor
2052
+ // host routes edits off them; field-picking here would silently strip
2053
+ // them and turn every kit-internal edit into a gap. `inspectorSource`
2054
+ // above is the deliberately narrow structure — this one is not.
2055
+ //
2056
+ // Unlike P0-1 this needs no deploy ordering: the extra keys are plain
2057
+ // data, so an already-published standalone.js forwards them unchanged
2058
+ // through `postMessage`'s structured clone. Whether a given PAGE has
2059
+ // them depends on the plugin that built it, which the host resolves via
2060
+ // `deriveEditorCapabilities(kitRuntimeVersion)` (SDD §2.5) — never from
2061
+ // this script's version. The slot string is opaque here; validating it
2062
+ // against kit-runtime's `slot-vocabulary` is the host's job.
1951
2063
  editorMeta: editorMeta ? editorMeta.meta : null,
1952
2064
  computedStyles: {
1953
2065
  display: computed.display,
@@ -2399,6 +2511,16 @@
2399
2511
  }
2400
2512
 
2401
2513
  if (detail.type === "design-mode-changed") {
2514
+ // In unified inspect the arrange overlay flips its internal
2515
+ // design-mode store as a side effect ("design" on mount, "off" on
2516
+ // teardown). Forwarding those flips while the host commanded
2517
+ // "inspect" makes the host see a stale mode and re-send
2518
+ // SET_EDITOR_MODE, which wipes the in-iframe selection. The host
2519
+ // only needs runtime-originated mode changes in the legacy
2520
+ // design-only flow.
2521
+ if (hostCommandedEditorMode === "inspect") {
2522
+ return
2523
+ }
2402
2524
  postMessageToParent({
2403
2525
  type: "EDITOR_MODE_CHANGED",
2404
2526
  mode: detail.mode || "off",
@@ -3090,6 +3212,7 @@
3090
3212
  }
3091
3213
 
3092
3214
  updateSharedHighlights()
3215
+ publishSelectionToDesignMode()
3093
3216
  postMessageToParent({
3094
3217
  type: "ELEMENT_REMOVED",
3095
3218
  requestId: event.data.requestId,
@@ -3344,6 +3467,20 @@
3344
3467
  const inspectorClickHandler = function (e) {
3345
3468
  if (!isInspecting) return
3346
3469
 
3470
+ // If the event originated on a design-mode (arrange) overlay element —
3471
+ // HitArea, resize handle, drop-line — let it propagate untouched so
3472
+ // dnd-kit can pick it up. Without this early-return, getElementFromPoint
3473
+ // below would resolve to the real element underneath the overlay and
3474
+ // the inspector would swallow the pointerdown via stopImmediatePropagation,
3475
+ // breaking arrange drag/resize on the selected element.
3476
+ if (
3477
+ e.target &&
3478
+ typeof e.target.closest === "function" &&
3479
+ e.target.closest("[data-design-mode-ui]")
3480
+ ) {
3481
+ return
3482
+ }
3483
+
3347
3484
  // Prefer e.target: browser already set it to the element under the pointer (e.g. input).
3348
3485
  // getElementFromPoint is fallback when overlay or stacking returns something else.
3349
3486
  let target = null
@@ -3469,6 +3606,7 @@
3469
3606
  type: "ELEMENT_SELECTED",
3470
3607
  metadata: metadataPayload,
3471
3608
  })
3609
+ publishSelectionToDesignMode()
3472
3610
  // Keep inspector active - don't set isInspecting = false
3473
3611
  // User can continue navigating between elements
3474
3612
  }
@@ -3515,6 +3653,7 @@
3515
3653
  if (window.__ARCHIE_INSPECTOR__)
3516
3654
  window.__ARCHIE_INSPECTOR__.selectedElement = null
3517
3655
  updateSharedHighlights()
3656
+ publishSelectionToDesignMode()
3518
3657
  postMessageToParent({ type: "INSPECTION_CANCELLED" })
3519
3658
  }
3520
3659
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archie/devtools",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "description": "DevTools for Archie generated applications - Route synchronization and editor communication",
5
5
  "repository": {
6
6
  "type": "git",
@@ -29,9 +29,8 @@
29
29
  "scripts": {
30
30
  "test": "vitest run",
31
31
  "test:watch": "vitest",
32
- "inspector:local": "node scripts/generate-inspector-local.js",
33
32
  "build": "tsup",
34
- "dev": "tsup --watch",
33
+ "build:local": "ARCHIE_INSPECTOR_INCLUDE_STANDALONE=1 tsup",
35
34
  "typecheck": "tsc --noEmit",
36
35
  "release": "node publish.js"
37
36
  },