@almadar/ui 4.50.9 → 4.50.11

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/dist/avl/index.js CHANGED
@@ -24901,6 +24901,7 @@ function useDataDnd(args) {
24901
24901
  const unregisterZone = React93__default.useCallback((zoneId2) => {
24902
24902
  zonesRef.current.delete(zoneId2);
24903
24903
  }, []);
24904
+ const [activeDrag, setActiveDrag] = React93__default.useState(null);
24904
24905
  const zoneId = React93__default.useId();
24905
24906
  const ownGroup = dragGroup ?? accepts ?? zoneId;
24906
24907
  const meta = React93__default.useMemo(
@@ -25081,30 +25082,49 @@ function useDataDnd(args) {
25081
25082
  id: droppableId,
25082
25083
  data: sortableData
25083
25084
  });
25085
+ const ctx = React93__default.useContext(RootCtx);
25086
+ const activeDrag2 = ctx?.activeDrag ?? null;
25087
+ const showForeignPlaceholder = isOver && activeDrag2 != null && activeDrag2.sourceGroup !== ownGroup;
25084
25088
  React93__default.useEffect(() => {
25085
- dndLog.debug("dropzone:isOver:change", { droppableId, group: ownGroup, isOver });
25086
- }, [droppableId, isOver]);
25087
- return /* @__PURE__ */ jsx(
25089
+ dndLog.debug("dropzone:isOver:change", { droppableId, group: ownGroup, isOver, showForeignPlaceholder });
25090
+ }, [droppableId, isOver, showForeignPlaceholder]);
25091
+ return /* @__PURE__ */ jsxs(
25088
25092
  Box,
25089
25093
  {
25090
25094
  ref: setNodeRef,
25091
25095
  "data-dnd-zone": ownGroup,
25092
25096
  "data-dnd-is-over": isOver ? "true" : "false",
25093
25097
  className: isOver ? "ring-2 ring-primary ring-offset-2 rounded-lg transition-all min-h-[3rem]" : "min-h-[3rem] rounded-lg transition-all",
25094
- children
25098
+ children: [
25099
+ children,
25100
+ showForeignPlaceholder ? /* @__PURE__ */ jsx(
25101
+ Box,
25102
+ {
25103
+ "data-dnd-placeholder": true,
25104
+ style: { height: activeDrag2.height },
25105
+ className: "border-2 border-dashed border-primary/60 bg-primary/5 rounded-md my-1 transition-all"
25106
+ }
25107
+ ) : null
25108
+ ]
25095
25109
  }
25096
25110
  );
25097
25111
  };
25098
25112
  const rootContextValue = React93__default.useMemo(
25099
- () => ({ registerZone, unregisterZone }),
25100
- [registerZone, unregisterZone]
25113
+ () => ({ registerZone, unregisterZone, activeDrag }),
25114
+ [registerZone, unregisterZone, activeDrag]
25101
25115
  );
25102
25116
  const handleDragStart = React93__default.useCallback((event) => {
25103
25117
  const sourceZone = findZoneByItem(event.active.id);
25118
+ const rect = event.active.rect.current.initial;
25119
+ const height = rect?.height && rect.height > 0 ? rect.height : 64;
25120
+ if (sourceZone) {
25121
+ setActiveDrag({ sourceGroup: sourceZone.group, height });
25122
+ }
25104
25123
  dndLog.info("dragStart", {
25105
25124
  activeId: event.active.id,
25106
25125
  activeData: event.active.data?.current,
25107
25126
  sourceGroup: sourceZone?.group,
25127
+ height,
25108
25128
  zoneCount: zonesRef.current.size
25109
25129
  });
25110
25130
  }, [findZoneByItem]);
@@ -25116,11 +25136,16 @@ function useDataDnd(args) {
25116
25136
  });
25117
25137
  }, []);
25118
25138
  const handleDragCancel = React93__default.useCallback((event) => {
25139
+ setActiveDrag(null);
25119
25140
  dndLog.warn("dragCancel", {
25120
25141
  activeId: event.active.id,
25121
25142
  reason: "dnd-kit cancelled the drag (escape key, pointer interrupted, or external)"
25122
25143
  });
25123
25144
  }, []);
25145
+ const handleDragEndWithCleanup = React93__default.useCallback((event) => {
25146
+ handleDragEnd(event);
25147
+ setActiveDrag(null);
25148
+ }, [handleDragEnd]);
25124
25149
  const wrapContainer = React93__default.useCallback(
25125
25150
  (children) => {
25126
25151
  if (!enabled) return children;
@@ -25134,7 +25159,7 @@ function useDataDnd(args) {
25134
25159
  collisionDetection,
25135
25160
  onDragStart: handleDragStart,
25136
25161
  onDragOver: handleDragOver,
25137
- onDragEnd: handleDragEnd,
25162
+ onDragEnd: handleDragEndWithCleanup,
25138
25163
  onDragCancel: handleDragCancel,
25139
25164
  children
25140
25165
  }
@@ -25149,7 +25174,7 @@ function useDataDnd(args) {
25149
25174
  collisionDetection,
25150
25175
  onDragStart: handleDragStart,
25151
25176
  onDragOver: handleDragOver,
25152
- onDragEnd: handleDragEnd,
25177
+ onDragEnd: handleDragEndWithCleanup,
25153
25178
  onDragCancel: handleDragCancel,
25154
25179
  children: inner
25155
25180
  }
@@ -25157,7 +25182,7 @@ function useDataDnd(args) {
25157
25182
  }
25158
25183
  return inner;
25159
25184
  },
25160
- [enabled, isZone, layout, sensors, collisionDetection, handleDragStart, handleDragOver, handleDragEnd, handleDragCancel, itemIds, isRoot, rootContextValue]
25185
+ [enabled, isZone, layout, sensors, collisionDetection, handleDragStart, handleDragOver, handleDragEndWithCleanup, handleDragCancel, itemIds, isRoot, rootContextValue]
25161
25186
  );
25162
25187
  return {
25163
25188
  enabled,
@@ -59780,7 +59805,7 @@ function findPatternInTree(root, path) {
59780
59805
  }
59781
59806
  var FIELD_TYPE_OPTIONS = FieldTypeSchema.options.map((v) => ({ value: v, label: v }));
59782
59807
  var EFFECT_TYPE_OPTIONS = Object.keys(EFFECT_TYPE_TO_CATEGORY).map((v) => ({ value: v, label: v }));
59783
- function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose }) {
59808
+ function OrbInspector({ node, schema, editable = false, userType = "builder", onSchemaChange, onClose }) {
59784
59809
  const { selected: selectedPattern } = useContext(PatternSelectionContext);
59785
59810
  const [activeTab, setActiveTab] = useState("inspector");
59786
59811
  const eventBus = useEventBus();
@@ -59886,27 +59911,18 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
59886
59911
  }
59887
59912
  )
59888
59913
  ] }),
59889
- /* @__PURE__ */ jsxs(Box, { className: "flex px-4 gap-4", children: [
59890
- /* @__PURE__ */ jsx(
59891
- "button",
59892
- {
59893
- onClick: () => setActiveTab("inspector"),
59894
- className: `pb-2 text-[12px] font-medium border-b-2 cursor-pointer bg-transparent border-x-0 border-t-0 px-0 ${activeTab === "inspector" ? "border-[var(--color-primary)] text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"}`,
59895
- children: "Inspector"
59896
- }
59897
- ),
59898
- /* @__PURE__ */ jsx(
59899
- "button",
59900
- {
59901
- onClick: () => setActiveTab("code"),
59902
- className: `pb-2 text-[12px] font-medium border-b-2 cursor-pointer bg-transparent border-x-0 border-t-0 px-0 ${activeTab === "code" ? "border-[var(--color-primary)] text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"}`,
59903
- children: "Code"
59904
- }
59905
- )
59906
- ] })
59914
+ /* @__PURE__ */ jsx(Box, { className: "flex px-4 gap-4", children: ["inspector", "styles", "code"].filter((tab) => tab !== "code" || userType === "architect").map((tab) => /* @__PURE__ */ jsx(
59915
+ "button",
59916
+ {
59917
+ onClick: () => setActiveTab(tab),
59918
+ className: `pb-2 text-[12px] font-medium border-b-2 cursor-pointer bg-transparent border-x-0 border-t-0 px-0 capitalize ${activeTab === tab ? "border-[var(--color-primary)] text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"}`,
59919
+ children: tab
59920
+ },
59921
+ tab
59922
+ )) })
59907
59923
  ] }),
59908
- /* @__PURE__ */ jsx(Box, { className: "flex-1 overflow-y-auto", children: activeTab === "code" ? (
59909
- /* ── Code Tab ── */
59924
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 overflow-y-auto", children: activeTab === "code" && userType === "architect" ? (
59925
+ /* ── Code Tab (architect only) ── */
59910
59926
  /* GAP-51: when editable, the CodeBlock molecule renders the existing
59911
59927
  Textarea atom internally and forwards keystrokes via UI:CODE_CHANGE
59912
59928
  on the EventBus. The consumer (builder workspace) listens, debounces,
@@ -59924,6 +59940,21 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
59924
59940
  onChange: editable ? (code) => eventBus.emit("UI:CODE_CHANGE", { code }) : void 0
59925
59941
  }
59926
59942
  ) })
59943
+ ) : activeTab === "styles" ? (
59944
+ /* ── Styles Tab (read-only in Phase 2) ──
59945
+ Phase 2 ships a token-only viewer with no editing. The proper
59946
+ tokenContract on PatternDefinition lands in Phase 6; until then
59947
+ we fall back to a static map of well-known atoms keyed by
59948
+ pattern type. Patterns missing from the map render a placeholder
59949
+ so the gap is visible rather than silently empty. */
59950
+ /* @__PURE__ */ jsx(
59951
+ StylesTabReadOnly,
59952
+ {
59953
+ patternType,
59954
+ patternDef,
59955
+ patternConfig
59956
+ }
59957
+ )
59927
59958
  ) : (
59928
59959
  /* ── Inspector Tab ── */
59929
59960
  /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -59950,7 +59981,7 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
59950
59981
  ] }, propName);
59951
59982
  }) })
59952
59983
  ] }),
59953
- (selectedPattern && isEntityPattern || !selectedPattern && !isExpanded) && entity && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
59984
+ userType === "architect" && (selectedPattern && isEntityPattern || !selectedPattern && !isExpanded) && entity && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
59954
59985
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: t("Entity") }),
59955
59986
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2 mb-2", children: [
59956
59987
  /* @__PURE__ */ jsx("svg", { width: 14, height: 14, children: /* @__PURE__ */ jsx("circle", { cx: 7, cy: 7, r: 5, fill: "var(--color-primary)" }) }),
@@ -60074,7 +60105,7 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
60074
60105
  /* @__PURE__ */ jsx("svg", { width: 16, height: 16, children: /* @__PURE__ */ jsx(AvlEvent, { x: 8, y: 8, size: 12 }) }),
60075
60106
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "font-semibold text-[12px]", children: transitionEvent })
60076
60107
  ] }) }),
60077
- (transition?.guard ?? guard ?? editable) && isExpanded && /* @__PURE__ */ jsx(Box, { className: "px-4 py-2 border-b border-border/40", children: /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
60108
+ userType === "architect" && (transition?.guard ?? guard ?? editable) && isExpanded && /* @__PURE__ */ jsx(Box, { className: "px-4 py-2 border-b border-border/40", children: /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
60078
60109
  /* @__PURE__ */ jsx("svg", { width: 16, height: 16, children: /* @__PURE__ */ jsx(AvlGuard, { x: 8, y: 8, size: 12 }) }),
60079
60110
  editable ? /* @__PURE__ */ jsx(
60080
60111
  Input,
@@ -60086,7 +60117,7 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
60086
60117
  }
60087
60118
  ) : /* @__PURE__ */ jsx(Typography, { variant: "small", className: "font-mono text-[11px] text-muted-foreground", children: formatExpression(transition?.guard ?? guard) })
60088
60119
  ] }) }),
60089
- (effectTypes.length > 0 || editable) && isExpanded && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
60120
+ userType === "architect" && (effectTypes.length > 0 || editable) && isExpanded && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
60090
60121
  /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: [
60091
60122
  t("Effects"),
60092
60123
  " (",
@@ -60118,7 +60149,7 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
60118
60149
  }) }),
60119
60150
  editable && /* @__PURE__ */ jsx(AddEffectButton, { onAdd: handleAddEffect })
60120
60151
  ] }),
60121
- patterns.length > 0 && !selectedPattern && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3", children: [
60152
+ userType === "architect" && patterns.length > 0 && !selectedPattern && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3", children: [
60122
60153
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: "render-ui" }),
60123
60154
  /* @__PURE__ */ jsx(Box, { className: "bg-muted/20 rounded-md p-3 font-mono text-[11px] leading-relaxed overflow-x-auto", children: patterns.map((entry, i) => /* @__PURE__ */ jsxs(Box, { children: [
60124
60155
  /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "text-muted-foreground text-[10px]", children: [
@@ -60191,6 +60222,83 @@ function OrbPatternTree({ config, depth }) {
60191
60222
  ] });
60192
60223
  }
60193
60224
  OrbInspector.displayName = "OrbInspector";
60225
+ var PHASE_2_TOKEN_FALLBACK = {
60226
+ button: ["--color-primary", "--color-primary-foreground", "--radius-md", "--shadow-sm"],
60227
+ badge: ["--color-primary", "--radius-full"],
60228
+ card: ["--color-card", "--color-border", "--radius-lg", "--shadow-main"],
60229
+ input: ["--color-input", "--color-border", "--radius-md"],
60230
+ typography: ["--color-foreground", "--color-muted-foreground"],
60231
+ divider: ["--color-border"],
60232
+ avatar: ["--radius-full", "--color-muted"],
60233
+ alert: ["--color-warning", "--color-success", "--color-danger", "--radius-md"],
60234
+ modal: ["--color-card", "--shadow-lg", "--radius-lg"],
60235
+ toast: ["--color-card", "--shadow-lg", "--radius-md"]
60236
+ };
60237
+ function StylesTabReadOnly({ patternType, patternDef, patternConfig }) {
60238
+ const { t } = useTranslate();
60239
+ if (!patternType) {
60240
+ return /* @__PURE__ */ jsx(Box, { className: "p-4", children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[11px]", children: t("Select a pattern to view its style tokens.") }) });
60241
+ }
60242
+ const tier = patternDef?.category ?? "Pattern";
60243
+ const tokens = PHASE_2_TOKEN_FALLBACK[patternType] ?? [];
60244
+ const variantEnum = patternDef?.propsSchema?.variant?.enumValues;
60245
+ const sizeEnum = patternDef?.propsSchema?.size?.enumValues;
60246
+ const currentVariant = patternConfig && typeof patternConfig.variant === "string" ? patternConfig.variant : void 0;
60247
+ const currentSize = patternConfig && typeof patternConfig.size === "string" ? patternConfig.size : void 0;
60248
+ return /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 flex flex-col gap-4", children: [
60249
+ /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2", children: [
60250
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "font-semibold text-[12px]", children: patternType }),
60251
+ /* @__PURE__ */ jsx(
60252
+ Box,
60253
+ {
60254
+ className: "rounded px-1.5 py-0.5 text-[9px] font-mono uppercase tracking-wider",
60255
+ style: { backgroundColor: "var(--color-muted)", color: "var(--color-muted-foreground)" },
60256
+ children: tier
60257
+ }
60258
+ )
60259
+ ] }),
60260
+ /* @__PURE__ */ jsxs(Box, { children: [
60261
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: t("Tokens") }),
60262
+ tokens.length === 0 ? /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[11px] italic", children: t("No token contract declared for this pattern.") }) : /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-1", children: tokens.map((token) => /* @__PURE__ */ jsx(Box, { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "font-mono text-[11px]", children: token }) }, token)) })
60263
+ ] }),
60264
+ variantEnum && variantEnum.length > 0 && /* @__PURE__ */ jsxs(Box, { children: [
60265
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: t("Variant") }),
60266
+ /* @__PURE__ */ jsx(Box, { className: "flex flex-wrap gap-1", children: variantEnum.map((variant) => {
60267
+ const isActive = variant === currentVariant || !currentVariant && variant === "default";
60268
+ return /* @__PURE__ */ jsx(
60269
+ Box,
60270
+ {
60271
+ className: "rounded px-2 py-0.5 text-[11px] font-mono",
60272
+ style: {
60273
+ backgroundColor: isActive ? "var(--color-primary)" : "var(--color-muted)",
60274
+ color: isActive ? "var(--color-primary-foreground)" : "var(--color-muted-foreground)"
60275
+ },
60276
+ children: variant
60277
+ },
60278
+ variant
60279
+ );
60280
+ }) })
60281
+ ] }),
60282
+ sizeEnum && sizeEnum.length > 0 && /* @__PURE__ */ jsxs(Box, { children: [
60283
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: t("Size") }),
60284
+ /* @__PURE__ */ jsx(Box, { className: "flex flex-wrap gap-1", children: sizeEnum.map((size) => {
60285
+ const isActive = size === currentSize || !currentSize && size === "md";
60286
+ return /* @__PURE__ */ jsx(
60287
+ Box,
60288
+ {
60289
+ className: "rounded px-2 py-0.5 text-[11px] font-mono",
60290
+ style: {
60291
+ backgroundColor: isActive ? "var(--color-primary)" : "var(--color-muted)",
60292
+ color: isActive ? "var(--color-primary-foreground)" : "var(--color-muted-foreground)"
60293
+ },
60294
+ children: size
60295
+ },
60296
+ size
60297
+ );
60298
+ }) })
60299
+ ] })
60300
+ ] });
60301
+ }
60194
60302
 
60195
60303
  // components/organisms/avl/FlowCanvas.tsx
60196
60304
  init_Box();
@@ -60234,7 +60342,8 @@ function FlowCanvasInner({
60234
60342
  onNodeSelect,
60235
60343
  composeLevel,
60236
60344
  behaviorEntries,
60237
- behaviorWires
60345
+ behaviorWires,
60346
+ userType = "builder"
60238
60347
  }) {
60239
60348
  const NODE_TYPES2 = useMemo(() => ({
60240
60349
  preview: OrbPreviewNode,
@@ -60529,6 +60638,7 @@ function FlowCanvasInner({
60529
60638
  node: selectedNode,
60530
60639
  schema: parsedSchema,
60531
60640
  editable,
60641
+ userType,
60532
60642
  onSchemaChange,
60533
60643
  onClose: handleClosePanel
60534
60644
  }
@@ -20114,6 +20114,7 @@ function useDataDnd(args) {
20114
20114
  const unregisterZone = React75__namespace.default.useCallback((zoneId2) => {
20115
20115
  zonesRef.current.delete(zoneId2);
20116
20116
  }, []);
20117
+ const [activeDrag, setActiveDrag] = React75__namespace.default.useState(null);
20117
20118
  const zoneId = React75__namespace.default.useId();
20118
20119
  const ownGroup = dragGroup ?? accepts ?? zoneId;
20119
20120
  const meta = React75__namespace.default.useMemo(
@@ -20294,30 +20295,49 @@ function useDataDnd(args) {
20294
20295
  id: droppableId,
20295
20296
  data: sortableData
20296
20297
  });
20298
+ const ctx = React75__namespace.default.useContext(RootCtx);
20299
+ const activeDrag2 = ctx?.activeDrag ?? null;
20300
+ const showForeignPlaceholder = isOver && activeDrag2 != null && activeDrag2.sourceGroup !== ownGroup;
20297
20301
  React75__namespace.default.useEffect(() => {
20298
- dndLog.debug("dropzone:isOver:change", { droppableId, group: ownGroup, isOver });
20299
- }, [droppableId, isOver]);
20300
- return /* @__PURE__ */ jsxRuntime.jsx(
20302
+ dndLog.debug("dropzone:isOver:change", { droppableId, group: ownGroup, isOver, showForeignPlaceholder });
20303
+ }, [droppableId, isOver, showForeignPlaceholder]);
20304
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20301
20305
  exports.Box,
20302
20306
  {
20303
20307
  ref: setNodeRef,
20304
20308
  "data-dnd-zone": ownGroup,
20305
20309
  "data-dnd-is-over": isOver ? "true" : "false",
20306
20310
  className: isOver ? "ring-2 ring-primary ring-offset-2 rounded-lg transition-all min-h-[3rem]" : "min-h-[3rem] rounded-lg transition-all",
20307
- children
20311
+ children: [
20312
+ children,
20313
+ showForeignPlaceholder ? /* @__PURE__ */ jsxRuntime.jsx(
20314
+ exports.Box,
20315
+ {
20316
+ "data-dnd-placeholder": true,
20317
+ style: { height: activeDrag2.height },
20318
+ className: "border-2 border-dashed border-primary/60 bg-primary/5 rounded-md my-1 transition-all"
20319
+ }
20320
+ ) : null
20321
+ ]
20308
20322
  }
20309
20323
  );
20310
20324
  };
20311
20325
  const rootContextValue = React75__namespace.default.useMemo(
20312
- () => ({ registerZone, unregisterZone }),
20313
- [registerZone, unregisterZone]
20326
+ () => ({ registerZone, unregisterZone, activeDrag }),
20327
+ [registerZone, unregisterZone, activeDrag]
20314
20328
  );
20315
20329
  const handleDragStart = React75__namespace.default.useCallback((event) => {
20316
20330
  const sourceZone = findZoneByItem(event.active.id);
20331
+ const rect = event.active.rect.current.initial;
20332
+ const height = rect?.height && rect.height > 0 ? rect.height : 64;
20333
+ if (sourceZone) {
20334
+ setActiveDrag({ sourceGroup: sourceZone.group, height });
20335
+ }
20317
20336
  dndLog.info("dragStart", {
20318
20337
  activeId: event.active.id,
20319
20338
  activeData: event.active.data?.current,
20320
20339
  sourceGroup: sourceZone?.group,
20340
+ height,
20321
20341
  zoneCount: zonesRef.current.size
20322
20342
  });
20323
20343
  }, [findZoneByItem]);
@@ -20329,11 +20349,16 @@ function useDataDnd(args) {
20329
20349
  });
20330
20350
  }, []);
20331
20351
  const handleDragCancel = React75__namespace.default.useCallback((event) => {
20352
+ setActiveDrag(null);
20332
20353
  dndLog.warn("dragCancel", {
20333
20354
  activeId: event.active.id,
20334
20355
  reason: "dnd-kit cancelled the drag (escape key, pointer interrupted, or external)"
20335
20356
  });
20336
20357
  }, []);
20358
+ const handleDragEndWithCleanup = React75__namespace.default.useCallback((event) => {
20359
+ handleDragEnd(event);
20360
+ setActiveDrag(null);
20361
+ }, [handleDragEnd]);
20337
20362
  const wrapContainer = React75__namespace.default.useCallback(
20338
20363
  (children) => {
20339
20364
  if (!enabled) return children;
@@ -20347,7 +20372,7 @@ function useDataDnd(args) {
20347
20372
  collisionDetection,
20348
20373
  onDragStart: handleDragStart,
20349
20374
  onDragOver: handleDragOver,
20350
- onDragEnd: handleDragEnd,
20375
+ onDragEnd: handleDragEndWithCleanup,
20351
20376
  onDragCancel: handleDragCancel,
20352
20377
  children
20353
20378
  }
@@ -20362,7 +20387,7 @@ function useDataDnd(args) {
20362
20387
  collisionDetection,
20363
20388
  onDragStart: handleDragStart,
20364
20389
  onDragOver: handleDragOver,
20365
- onDragEnd: handleDragEnd,
20390
+ onDragEnd: handleDragEndWithCleanup,
20366
20391
  onDragCancel: handleDragCancel,
20367
20392
  children: inner
20368
20393
  }
@@ -20370,7 +20395,7 @@ function useDataDnd(args) {
20370
20395
  }
20371
20396
  return inner;
20372
20397
  },
20373
- [enabled, isZone, layout, sensors, collisionDetection, handleDragStart, handleDragOver, handleDragEnd, handleDragCancel, itemIds, isRoot, rootContextValue]
20398
+ [enabled, isZone, layout, sensors, collisionDetection, handleDragStart, handleDragOver, handleDragEndWithCleanup, handleDragCancel, itemIds, isRoot, rootContextValue]
20374
20399
  );
20375
20400
  return {
20376
20401
  enabled,
@@ -20068,6 +20068,7 @@ function useDataDnd(args) {
20068
20068
  const unregisterZone = React75__default.useCallback((zoneId2) => {
20069
20069
  zonesRef.current.delete(zoneId2);
20070
20070
  }, []);
20071
+ const [activeDrag, setActiveDrag] = React75__default.useState(null);
20071
20072
  const zoneId = React75__default.useId();
20072
20073
  const ownGroup = dragGroup ?? accepts ?? zoneId;
20073
20074
  const meta = React75__default.useMemo(
@@ -20248,30 +20249,49 @@ function useDataDnd(args) {
20248
20249
  id: droppableId,
20249
20250
  data: sortableData
20250
20251
  });
20252
+ const ctx = React75__default.useContext(RootCtx);
20253
+ const activeDrag2 = ctx?.activeDrag ?? null;
20254
+ const showForeignPlaceholder = isOver && activeDrag2 != null && activeDrag2.sourceGroup !== ownGroup;
20251
20255
  React75__default.useEffect(() => {
20252
- dndLog.debug("dropzone:isOver:change", { droppableId, group: ownGroup, isOver });
20253
- }, [droppableId, isOver]);
20254
- return /* @__PURE__ */ jsx(
20256
+ dndLog.debug("dropzone:isOver:change", { droppableId, group: ownGroup, isOver, showForeignPlaceholder });
20257
+ }, [droppableId, isOver, showForeignPlaceholder]);
20258
+ return /* @__PURE__ */ jsxs(
20255
20259
  Box,
20256
20260
  {
20257
20261
  ref: setNodeRef,
20258
20262
  "data-dnd-zone": ownGroup,
20259
20263
  "data-dnd-is-over": isOver ? "true" : "false",
20260
20264
  className: isOver ? "ring-2 ring-primary ring-offset-2 rounded-lg transition-all min-h-[3rem]" : "min-h-[3rem] rounded-lg transition-all",
20261
- children
20265
+ children: [
20266
+ children,
20267
+ showForeignPlaceholder ? /* @__PURE__ */ jsx(
20268
+ Box,
20269
+ {
20270
+ "data-dnd-placeholder": true,
20271
+ style: { height: activeDrag2.height },
20272
+ className: "border-2 border-dashed border-primary/60 bg-primary/5 rounded-md my-1 transition-all"
20273
+ }
20274
+ ) : null
20275
+ ]
20262
20276
  }
20263
20277
  );
20264
20278
  };
20265
20279
  const rootContextValue = React75__default.useMemo(
20266
- () => ({ registerZone, unregisterZone }),
20267
- [registerZone, unregisterZone]
20280
+ () => ({ registerZone, unregisterZone, activeDrag }),
20281
+ [registerZone, unregisterZone, activeDrag]
20268
20282
  );
20269
20283
  const handleDragStart = React75__default.useCallback((event) => {
20270
20284
  const sourceZone = findZoneByItem(event.active.id);
20285
+ const rect = event.active.rect.current.initial;
20286
+ const height = rect?.height && rect.height > 0 ? rect.height : 64;
20287
+ if (sourceZone) {
20288
+ setActiveDrag({ sourceGroup: sourceZone.group, height });
20289
+ }
20271
20290
  dndLog.info("dragStart", {
20272
20291
  activeId: event.active.id,
20273
20292
  activeData: event.active.data?.current,
20274
20293
  sourceGroup: sourceZone?.group,
20294
+ height,
20275
20295
  zoneCount: zonesRef.current.size
20276
20296
  });
20277
20297
  }, [findZoneByItem]);
@@ -20283,11 +20303,16 @@ function useDataDnd(args) {
20283
20303
  });
20284
20304
  }, []);
20285
20305
  const handleDragCancel = React75__default.useCallback((event) => {
20306
+ setActiveDrag(null);
20286
20307
  dndLog.warn("dragCancel", {
20287
20308
  activeId: event.active.id,
20288
20309
  reason: "dnd-kit cancelled the drag (escape key, pointer interrupted, or external)"
20289
20310
  });
20290
20311
  }, []);
20312
+ const handleDragEndWithCleanup = React75__default.useCallback((event) => {
20313
+ handleDragEnd(event);
20314
+ setActiveDrag(null);
20315
+ }, [handleDragEnd]);
20291
20316
  const wrapContainer = React75__default.useCallback(
20292
20317
  (children) => {
20293
20318
  if (!enabled) return children;
@@ -20301,7 +20326,7 @@ function useDataDnd(args) {
20301
20326
  collisionDetection,
20302
20327
  onDragStart: handleDragStart,
20303
20328
  onDragOver: handleDragOver,
20304
- onDragEnd: handleDragEnd,
20329
+ onDragEnd: handleDragEndWithCleanup,
20305
20330
  onDragCancel: handleDragCancel,
20306
20331
  children
20307
20332
  }
@@ -20316,7 +20341,7 @@ function useDataDnd(args) {
20316
20341
  collisionDetection,
20317
20342
  onDragStart: handleDragStart,
20318
20343
  onDragOver: handleDragOver,
20319
- onDragEnd: handleDragEnd,
20344
+ onDragEnd: handleDragEndWithCleanup,
20320
20345
  onDragCancel: handleDragCancel,
20321
20346
  children: inner
20322
20347
  }
@@ -20324,7 +20349,7 @@ function useDataDnd(args) {
20324
20349
  }
20325
20350
  return inner;
20326
20351
  },
20327
- [enabled, isZone, layout, sensors, collisionDetection, handleDragStart, handleDragOver, handleDragEnd, handleDragCancel, itemIds, isRoot, rootContextValue]
20352
+ [enabled, isZone, layout, sensors, collisionDetection, handleDragStart, handleDragOver, handleDragEndWithCleanup, handleDragCancel, itemIds, isRoot, rootContextValue]
20328
20353
  );
20329
20354
  return {
20330
20355
  enabled,
@@ -117,5 +117,12 @@ export interface FlowCanvasProps {
117
117
  initialTrait?: string;
118
118
  /** @deprecated Not used in V3. */
119
119
  stateCoverage?: Record<string, string>;
120
+ /**
121
+ * Studio persona viewing the canvas. Drives `OrbInspector` tab/section
122
+ * visibility — designers and builders hide the raw `code` tab and the
123
+ * architect-only Entity / raw-guard / raw-effects sections; architects see
124
+ * everything. Default `'builder'` preserves pre-Phase-2 behavior.
125
+ */
126
+ userType?: 'builder' | 'designer' | 'architect';
120
127
  }
121
128
  export declare const FlowCanvas: React.FC<FlowCanvasProps>;
@@ -20,10 +20,18 @@ export interface OrbInspectorProps {
20
20
  node: PreviewNodeData;
21
21
  schema: OrbitalSchema;
22
22
  editable?: boolean;
23
+ /**
24
+ * Studio persona viewing the inspector. Controls tab visibility (Code is
25
+ * architect-only, Styles is universal) and section visibility (Entity,
26
+ * raw guard, raw effects are architect-only). Default `'builder'` matches
27
+ * pre-Phase-2 behavior except for the new Styles tab, which every persona
28
+ * gets.
29
+ */
30
+ userType?: 'builder' | 'designer' | 'architect';
23
31
  onSchemaChange?: (schema: OrbitalSchema) => void;
24
32
  onClose: () => void;
25
33
  }
26
- export declare function OrbInspector({ node, schema, editable, onSchemaChange, onClose }: OrbInspectorProps): React.ReactElement;
34
+ export declare function OrbInspector({ node, schema, editable, userType, onSchemaChange, onClose }: OrbInspectorProps): React.ReactElement;
27
35
  export declare namespace OrbInspector {
28
36
  var displayName: string;
29
37
  }