@kubuild/editor 0.1.0 → 0.1.2

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/index.cjs CHANGED
@@ -88,12 +88,13 @@ module.exports = __toCommonJS(index_exports);
88
88
 
89
89
  // src/editor.tsx
90
90
  var import_react19 = __toESM(require("react"), 1);
91
- var import_components6 = require("@kubuild/components");
91
+ var import_components7 = require("@kubuild/components");
92
92
  var import_core12 = require("@kubuild/core");
93
93
 
94
94
  // src/store.ts
95
95
  var import_zustand = require("zustand");
96
96
  var import_core = require("@kubuild/core");
97
+ var import_components = require("@kubuild/components");
97
98
  function formatCommandError(err) {
98
99
  if (err && typeof err === "object" && Array.isArray(err.issues)) {
99
100
  const issues = err.issues;
@@ -118,7 +119,11 @@ function buildDefaultChildren(specs, existingIds, registry) {
118
119
  const def = registry.get(spec.type);
119
120
  const nodeProps = (0, import_core.deepClone)(spec.props ?? def?.defaultProps ?? {});
120
121
  const nodeStyles = spec.styles ?? def?.defaultStyles;
121
- const childNodes = buildDefaultChildren(spec.children ?? def?.defaultChildren, existingIds, registry);
122
+ const childNodes = buildDefaultChildren(
123
+ spec.children ?? def?.defaultChildren,
124
+ existingIds,
125
+ registry
126
+ );
122
127
  const node = {
123
128
  id,
124
129
  type: spec.type,
@@ -138,6 +143,7 @@ var useEditorStore = (0, import_zustand.create)((set, get) => ({
138
143
  document: historyManager.document,
139
144
  selectedNodeId: null,
140
145
  hoveredNodeId: null,
146
+ dragPayload: null,
141
147
  viewport: "desktop",
142
148
  isDirty: false,
143
149
  canUndo: false,
@@ -147,6 +153,7 @@ var useEditorStore = (0, import_zustand.create)((set, get) => ({
147
153
  variableCatalog: [],
148
154
  navigatorMode: "floating",
149
155
  tableSpreadsheetMode: "floating",
156
+ setDragPayload: (payload) => set({ dragPayload: payload }),
150
157
  setVariableCatalog: (catalog) => set({ variableCatalog: catalog }),
151
158
  setNavigatorMode: (mode) => set({ navigatorMode: mode }),
152
159
  toggleNavigator: () => set((state) => ({
@@ -163,6 +170,7 @@ var useEditorStore = (0, import_zustand.create)((set, get) => ({
163
170
  isDirty: false,
164
171
  selectedNodeId: null,
165
172
  hoveredNodeId: null,
173
+ dragPayload: null,
166
174
  clipboard: null,
167
175
  canUndo: false,
168
176
  canRedo: false
@@ -181,12 +189,15 @@ var useEditorStore = (0, import_zustand.create)((set, get) => ({
181
189
  });
182
190
  onChangeHandler?.(result.document);
183
191
  },
184
- insertComponent: (type, registry, parentId) => {
192
+ insertComponent: (type, registry, parentId, index) => {
185
193
  const state = get();
186
194
  const targetParentId = parentId ?? state.selectedNodeId ?? state.document.document.id;
187
195
  const parentNode = (0, import_core.findNodeById)(state.document.document, targetParentId);
188
196
  if (!parentNode) {
189
- return { success: false, error: `Insertion target "${targetParentId}" was not found in the document.` };
197
+ return {
198
+ success: false,
199
+ error: `Insertion target "${targetParentId}" was not found in the document.`
200
+ };
190
201
  }
191
202
  const definition = registry.get(type);
192
203
  if (!definition) {
@@ -203,7 +214,10 @@ var useEditorStore = (0, import_zustand.create)((set, get) => ({
203
214
  return { success: false, error: propResult.join(" ") };
204
215
  }
205
216
  if (propResult === false) {
206
- return { success: false, error: `Default props for "${definition.label}" failed validation.` };
217
+ return {
218
+ success: false,
219
+ error: `Default props for "${definition.label}" failed validation.`
220
+ };
207
221
  }
208
222
  }
209
223
  const existingIds = (0, import_core.collectNodeIdSet)(state.document.document);
@@ -217,10 +231,49 @@ var useEditorStore = (0, import_zustand.create)((set, get) => ({
217
231
  ...definition.defaultStyles ? { styles: (0, import_core.deepClone)(definition.defaultStyles) } : {},
218
232
  ...children ? { children } : {}
219
233
  };
220
- get().dispatch((doc) => (0, import_core.insertNode)(doc, { parentId: targetParentId, node }));
234
+ get().dispatch((doc) => (0, import_core.insertNode)(doc, { parentId: targetParentId, node, index }));
221
235
  get().selectNode(nodeId);
222
236
  return { success: true, nodeId };
223
237
  },
238
+ insertBlock: (blockOrId, parentId, index) => {
239
+ const state = get();
240
+ const targetParentId = parentId ?? state.selectedNodeId ?? state.document.document.id;
241
+ let blockDef;
242
+ if (typeof blockOrId === "string") {
243
+ blockDef = import_components.STARTER_BLOCKS.find((b) => b.id === blockOrId);
244
+ } else {
245
+ blockDef = blockOrId;
246
+ }
247
+ if (!blockDef) {
248
+ return { success: false, error: "Block definition not found." };
249
+ }
250
+ const existingIds = (0, import_core.collectNodeIdSet)(state.document.document);
251
+ let counter = 1;
252
+ const generateId = (prefix = "node") => {
253
+ let id = `${prefix}-${Date.now().toString(36)}-${counter++}`;
254
+ while (existingIds.has(id)) {
255
+ id = `${prefix}-${Date.now().toString(36)}-${counter++}`;
256
+ }
257
+ existingIds.add(id);
258
+ return id;
259
+ };
260
+ const nodeTree = blockDef.createNodeTree(generateId);
261
+ try {
262
+ get().dispatch((doc) => (0, import_core.insertNode)(doc, { parentId: targetParentId, node: nodeTree, index }));
263
+ get().selectNode(nodeTree.id);
264
+ return { success: true, nodeId: nodeTree.id };
265
+ } catch {
266
+ try {
267
+ get().dispatch(
268
+ (doc) => (0, import_core.insertNode)(doc, { parentId: state.document.document.id, node: nodeTree })
269
+ );
270
+ get().selectNode(nodeTree.id);
271
+ return { success: true, nodeId: nodeTree.id };
272
+ } catch (err) {
273
+ return { success: false, error: formatCommandError(err) };
274
+ }
275
+ }
276
+ },
224
277
  moveComponent: (nodeId, targetParentId, registry, index) => {
225
278
  const state = get();
226
279
  if (nodeId === state.document.document.id) {
@@ -232,10 +285,16 @@ var useEditorStore = (0, import_zustand.create)((set, get) => ({
232
285
  }
233
286
  const targetParent = (0, import_core.findNodeById)(state.document.document, targetParentId);
234
287
  if (!targetParent) {
235
- return { success: false, error: `Move target "${targetParentId}" was not found in the document.` };
288
+ return {
289
+ success: false,
290
+ error: `Move target "${targetParentId}" was not found in the document.`
291
+ };
236
292
  }
237
293
  if ((0, import_core.isDescendantOf)(sourceLocation.node, targetParentId)) {
238
- return { success: false, error: "Cannot move a node into itself or one of its own descendants." };
294
+ return {
295
+ success: false,
296
+ error: "Cannot move a node into itself or one of its own descendants."
297
+ };
239
298
  }
240
299
  const policy = registry.canInsertChild(targetParent.type, sourceLocation.node.type);
241
300
  if (!policy.valid) {
@@ -372,10 +431,16 @@ var useEditorStore = (0, import_zustand.create)((set, get) => ({
372
431
  }
373
432
  const targetParent = (0, import_core.findNodeById)(document2.document, targetParentId);
374
433
  if (!targetParent) {
375
- return { success: false, error: `Paste target "${targetParentId}" was not found in the document.` };
434
+ return {
435
+ success: false,
436
+ error: `Paste target "${targetParentId}" was not found in the document.`
437
+ };
376
438
  }
377
439
  if ((0, import_core.isDescendantOf)(clipboard, targetParentId)) {
378
- return { success: false, error: "Cannot paste a node into itself or one of its own descendants." };
440
+ return {
441
+ success: false,
442
+ error: "Cannot paste a node into itself or one of its own descendants."
443
+ };
379
444
  }
380
445
  const policy = registry.canInsertChild(targetParent.type, clipboard.type);
381
446
  if (!policy.valid) {
@@ -384,7 +449,9 @@ var useEditorStore = (0, import_zustand.create)((set, get) => ({
384
449
  const existingIds = (0, import_core.collectNodeIdSet)(document2.document);
385
450
  const { clonedNode } = (0, import_core.cloneTreeWithNewIds)(clipboard, void 0, existingIds);
386
451
  try {
387
- get().dispatch((doc) => (0, import_core.insertNode)(doc, { parentId: targetParentId, node: clonedNode, index }));
452
+ get().dispatch(
453
+ (doc) => (0, import_core.insertNode)(doc, { parentId: targetParentId, node: clonedNode, index })
454
+ );
388
455
  } catch (err) {
389
456
  return { success: false, error: formatCommandError(err) };
390
457
  }
@@ -549,7 +616,19 @@ var EditorCanvas = ({
549
616
  onDiagnostic,
550
617
  config
551
618
  }) => {
552
- const { document: storeDoc, selectedNodeId, hoveredNodeId, selectNode, hoverNode, updateNodeProps } = useEditorStore();
619
+ const {
620
+ document: storeDoc,
621
+ selectedNodeId,
622
+ hoveredNodeId,
623
+ dragPayload,
624
+ selectNode,
625
+ hoverNode,
626
+ updateNodeProps,
627
+ setDragPayload,
628
+ insertComponent,
629
+ insertBlock,
630
+ moveComponent
631
+ } = useEditorStore();
553
632
  const document2 = propDoc ?? storeDoc;
554
633
  const showFloatingBadges = config?.showFloatingBadges !== false;
555
634
  const containerRef = (0, import_react.useRef)(null);
@@ -626,7 +705,11 @@ var EditorCanvas = ({
626
705
  const direction = ARROW_DIRECTIONS[e.key];
627
706
  if (direction) {
628
707
  e.preventDefault();
629
- const target = (0, import_core3.getNavigationTarget)(state.document.document, state.selectedNodeId, direction);
708
+ const target = (0, import_core3.getNavigationTarget)(
709
+ state.document.document,
710
+ state.selectedNodeId,
711
+ direction
712
+ );
630
713
  if (target) state.selectNode(target);
631
714
  }
632
715
  };
@@ -653,24 +736,71 @@ var EditorCanvas = ({
653
736
  e.stopPropagation();
654
737
  e.dataTransfer.effectAllowed = "move";
655
738
  e.dataTransfer.setData("text/plain", nodeId);
739
+ e.dataTransfer.setData("application/kubuild-drag-type", "node");
740
+ e.dataTransfer.setData("application/kubuild-node-id", nodeId);
656
741
  setDraggingId(nodeId);
742
+ setDragPayload({ type: "node", nodeId });
657
743
  selectNode(nodeId);
658
744
  };
659
745
  const handleDragOver = (e) => {
660
- if (!draggingId) return;
746
+ const activePayload = dragPayload ?? (draggingId ? { type: "node", nodeId: draggingId } : null);
747
+ if (!activePayload) return;
661
748
  const container = containerRef.current;
662
- const hoveredEl = e.target.closest("[data-kubuild-node]");
663
- if (!container || !hoveredEl) return;
664
- const hoveredId = hoveredEl.getAttribute("data-kubuild-node");
665
- const draggedNode = hoveredId ? (0, import_core3.findNodeById)(document2.document, draggingId) : null;
666
- const hoveredNode = hoveredId ? (0, import_core3.findNodeById)(document2.document, hoveredId) : null;
667
- if (!hoveredId || !draggedNode || !hoveredNode || (0, import_core3.isDescendantOf)(draggedNode, hoveredId)) {
749
+ if (!container) return;
750
+ const containerRect = container.getBoundingClientRect();
751
+ const hoveredEl = e.target.closest(
752
+ "[data-kubuild-node]"
753
+ );
754
+ let incomingType = null;
755
+ if (activePayload.type === "node") {
756
+ const draggedNode = (0, import_core3.findNodeById)(document2.document, activePayload.nodeId);
757
+ incomingType = draggedNode?.type ?? null;
758
+ } else if (activePayload.type === "component") {
759
+ incomingType = activePayload.componentType;
760
+ } else if (activePayload.type === "block") {
761
+ incomingType = "section";
762
+ }
763
+ if (!incomingType) {
668
764
  setDropTarget(null);
669
765
  e.dataTransfer.dropEffect = "none";
670
766
  return;
671
767
  }
768
+ if (!hoveredEl) {
769
+ const rootNode = document2.document;
770
+ const policy2 = registry.canInsertChild(rootNode.type, incomingType);
771
+ if (!policy2.valid) {
772
+ setDropTarget(null);
773
+ e.dataTransfer.dropEffect = "none";
774
+ return;
775
+ }
776
+ e.preventDefault();
777
+ e.dataTransfer.dropEffect = activePayload.type === "node" ? "move" : "copy";
778
+ setDropTarget({
779
+ parentId: rootNode.id,
780
+ index: rootNode.children?.length ?? 0,
781
+ position: "inside",
782
+ rect: {
783
+ top: 0,
784
+ left: 0,
785
+ width: containerRect.width,
786
+ height: Math.max(containerRect.height, 200)
787
+ }
788
+ });
789
+ return;
790
+ }
791
+ const hoveredId = hoveredEl.getAttribute("data-kubuild-node");
792
+ if (!hoveredId) return;
793
+ if (activePayload.type === "node") {
794
+ const draggedNode = (0, import_core3.findNodeById)(document2.document, activePayload.nodeId);
795
+ if (!draggedNode || (0, import_core3.isDescendantOf)(draggedNode, hoveredId)) {
796
+ setDropTarget(null);
797
+ e.dataTransfer.dropEffect = "none";
798
+ return;
799
+ }
800
+ }
801
+ const hoveredNode = (0, import_core3.findNodeById)(document2.document, hoveredId);
802
+ if (!hoveredNode) return;
672
803
  const elRect = hoveredEl.getBoundingClientRect();
673
- const containerRect = container.getBoundingClientRect();
674
804
  const ratio = elRect.height > 0 ? (e.clientY - elRect.top) / elRect.height : 0.5;
675
805
  const hoveredDef = registry.get(hoveredNode.type);
676
806
  const canGoInside = !!hoveredDef?.acceptsChildren;
@@ -691,18 +821,21 @@ var EditorCanvas = ({
691
821
  targetType: location.parent.type
692
822
  };
693
823
  } else {
694
- setDropTarget(null);
695
- e.dataTransfer.dropEffect = "none";
696
- return;
824
+ candidate = {
825
+ parentId: document2.document.id,
826
+ index: document2.document.children?.length ?? 0,
827
+ position: "inside",
828
+ targetType: document2.document.type
829
+ };
697
830
  }
698
- const policy = registry.canInsertChild(candidate.targetType, draggedNode.type);
831
+ const policy = registry.canInsertChild(candidate.targetType, incomingType);
699
832
  if (!policy.valid) {
700
833
  setDropTarget(null);
701
834
  e.dataTransfer.dropEffect = "none";
702
835
  return;
703
836
  }
704
837
  e.preventDefault();
705
- e.dataTransfer.dropEffect = "move";
838
+ e.dataTransfer.dropEffect = activePayload.type === "node" ? "move" : "copy";
706
839
  setDropTarget({
707
840
  parentId: candidate.parentId,
708
841
  index: candidate.index,
@@ -717,15 +850,29 @@ var EditorCanvas = ({
717
850
  };
718
851
  const handleDrop = (e) => {
719
852
  e.preventDefault();
720
- if (draggingId && dropTarget) {
721
- useEditorStore.getState().moveComponent(draggingId, dropTarget.parentId, registry, dropTarget.index);
853
+ const activePayload = dragPayload ?? (draggingId ? { type: "node", nodeId: draggingId } : null);
854
+ if (activePayload && dropTarget) {
855
+ if (activePayload.type === "node") {
856
+ moveComponent(activePayload.nodeId, dropTarget.parentId, registry, dropTarget.index);
857
+ } else if (activePayload.type === "component") {
858
+ insertComponent(
859
+ activePayload.componentType,
860
+ registry,
861
+ dropTarget.parentId,
862
+ dropTarget.index
863
+ );
864
+ } else if (activePayload.type === "block") {
865
+ insertBlock(activePayload.blockId, dropTarget.parentId, dropTarget.index);
866
+ }
722
867
  }
723
868
  setDraggingId(null);
724
869
  setDropTarget(null);
870
+ setDragPayload(null);
725
871
  };
726
872
  const handleDragEnd = () => {
727
873
  setDraggingId(null);
728
874
  setDropTarget(null);
875
+ setDragPayload(null);
729
876
  };
730
877
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
731
878
  "div",
@@ -2077,21 +2224,32 @@ var EditorToolbar = ({
2077
2224
  type: "button",
2078
2225
  title: `Navigator / Element Tree (${navigatorMode !== "hidden" ? "Open" : "Hidden"})`,
2079
2226
  onClick: toggleNavigator,
2080
- className: `flex items-center gap-1 text-xs px-2.5 py-1 rounded border transition font-medium cursor-pointer ${navigatorMode !== "hidden" ? "border-blue-500 bg-blue-50 text-blue-700 shadow-xs" : "border-slate-200 bg-white hover:border-slate-300 text-slate-700"}`,
2227
+ className: `hidden sm:flex items-center gap-1 text-xs px-2.5 py-1 rounded border transition font-medium cursor-pointer ${navigatorMode !== "hidden" ? "border-blue-500 bg-blue-50 text-blue-700 shadow-xs" : "border-slate-200 bg-white hover:border-slate-300 text-slate-700"}`,
2081
2228
  children: [
2082
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2083
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "8", y1: "6", x2: "21", y2: "6" }),
2084
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "8", y1: "12", x2: "21", y2: "12" }),
2085
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "8", y1: "18", x2: "21", y2: "18" }),
2086
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "3", y1: "6", x2: "3.01", y2: "6" }),
2087
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "3", y1: "12", x2: "3.01", y2: "12" }),
2088
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "3", y1: "18", x2: "3.01", y2: "18" })
2089
- ] }),
2229
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2230
+ "svg",
2231
+ {
2232
+ width: "13",
2233
+ height: "13",
2234
+ viewBox: "0 0 24 24",
2235
+ fill: "none",
2236
+ stroke: "currentColor",
2237
+ strokeWidth: "2",
2238
+ children: [
2239
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "8", y1: "6", x2: "21", y2: "6" }),
2240
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "8", y1: "12", x2: "21", y2: "12" }),
2241
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "8", y1: "18", x2: "21", y2: "18" }),
2242
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "3", y1: "6", x2: "3.01", y2: "6" }),
2243
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "3", y1: "12", x2: "3.01", y2: "12" }),
2244
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "3", y1: "18", x2: "3.01", y2: "18" })
2245
+ ]
2246
+ }
2247
+ ),
2090
2248
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Navigator" })
2091
2249
  ]
2092
2250
  }
2093
2251
  ),
2094
- showNavigatorToggle && (showClipboard || showHistory || showCodeViewer || showExportImport) && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "h-4 w-px bg-slate-200 mx-1" }),
2252
+ showNavigatorToggle && (showClipboard || showHistory || showCodeViewer || showExportImport) && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hidden sm:block h-4 w-px bg-slate-200 mx-1" }),
2095
2253
  (showClipboard || showHistory) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-1", children: [
2096
2254
  showClipboard && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2097
2255
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -2166,21 +2324,21 @@ var EditorToolbar = ({
2166
2324
  )
2167
2325
  ] })
2168
2326
  ] }),
2169
- (showClipboard || showHistory) && (showCodeViewer || showExportImport) && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "h-4 w-px bg-slate-200 mx-1" }),
2327
+ (showClipboard || showHistory) && (showCodeViewer || showExportImport) && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hidden sm:block h-4 w-px bg-slate-200 mx-1" }),
2170
2328
  showCodeViewer && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2171
2329
  "button",
2172
2330
  {
2173
2331
  type: "button",
2174
2332
  title: "View Semantic HTML & CSS (< >)",
2175
2333
  onClick: () => setIsCodeViewerModalOpen(true),
2176
- className: "flex items-center gap-1.5 text-xs px-2.5 py-1 rounded border border-slate-200 bg-white hover:border-blue-500 hover:text-blue-600 font-medium text-slate-700 transition",
2334
+ className: "flex items-center gap-1.5 text-xs px-2 sm:px-2.5 py-1 rounded border border-slate-200 bg-white hover:border-blue-500 hover:text-blue-600 font-medium text-slate-700 transition",
2177
2335
  children: [
2178
2336
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "font-mono text-[11px] font-bold text-blue-600", children: "<>" }),
2179
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "View Code" })
2337
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "hidden sm:inline", children: "View Code" })
2180
2338
  ]
2181
2339
  }
2182
2340
  ),
2183
- showExportImport && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2341
+ showExportImport && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-1", children: [
2184
2342
  showCodeViewer && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "h-4 w-px bg-slate-200 mx-1" }),
2185
2343
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2186
2344
  "button",
@@ -2188,7 +2346,7 @@ var EditorToolbar = ({
2188
2346
  type: "button",
2189
2347
  title: "Import .stora Package",
2190
2348
  onClick: () => setIsImportModalOpen(true),
2191
- className: "text-xs px-2.5 py-1 rounded border border-slate-200 bg-white hover:border-blue-500 hover:text-blue-600 font-medium text-slate-700 transition",
2349
+ className: "text-xs px-2 sm:px-2.5 py-1 rounded border border-slate-200 bg-white hover:border-blue-500 hover:text-blue-600 font-medium text-slate-700 transition",
2192
2350
  children: "Import"
2193
2351
  }
2194
2352
  ),
@@ -2199,8 +2357,8 @@ var EditorToolbar = ({
2199
2357
  title: "Export .stora Archive",
2200
2358
  disabled: isExporting,
2201
2359
  onClick: handleExportStora,
2202
- className: "text-xs px-2.5 py-1 rounded border border-blue-600 bg-blue-600 hover:bg-blue-500 text-white font-medium disabled:opacity-50 transition shadow-sm",
2203
- children: isExporting ? "Exporting..." : "Export .stora"
2360
+ className: "text-xs px-2 sm:px-2.5 py-1 rounded border border-blue-600 bg-blue-600 hover:bg-blue-500 text-white font-medium disabled:opacity-50 transition shadow-xs",
2361
+ children: isExporting ? "..." : "Export"
2204
2362
  }
2205
2363
  ),
2206
2364
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -2209,7 +2367,7 @@ var EditorToolbar = ({
2209
2367
  type: "button",
2210
2368
  title: "Download page.json",
2211
2369
  onClick: handleExportJson,
2212
- className: "text-xs px-2 py-1 rounded border border-slate-200 bg-white hover:border-slate-400 text-slate-600 transition",
2370
+ className: "hidden md:inline-block text-xs px-2 py-1 rounded border border-slate-200 bg-white hover:border-slate-400 text-slate-600 transition",
2213
2371
  children: "JSON"
2214
2372
  }
2215
2373
  )
@@ -2242,16 +2400,16 @@ var EditorToolbar = ({
2242
2400
 
2243
2401
  // src/inspector-panel.tsx
2244
2402
  var import_react13 = require("react");
2245
- var import_components3 = require("@kubuild/components");
2403
+ var import_components4 = require("@kubuild/components");
2246
2404
  var import_core8 = require("@kubuild/core");
2247
2405
  var import_schema5 = require("@kubuild/schema");
2248
2406
 
2249
2407
  // src/variable-picker.tsx
2250
2408
  var import_schema2 = require("@kubuild/schema");
2251
- var import_components = require("@kubuild/components");
2409
+ var import_components2 = require("@kubuild/components");
2252
2410
  var import_jsx_runtime7 = require("react/jsx-runtime");
2253
2411
  function getCompatibleCatalogEntries(field, catalog) {
2254
- const expectedType = (0, import_components.primitiveTypeForField)(field);
2412
+ const expectedType = (0, import_components2.primitiveTypeForField)(field);
2255
2413
  if (!expectedType || !catalog) {
2256
2414
  return [];
2257
2415
  }
@@ -5456,7 +5614,7 @@ var StyleManagerAccordion = ({
5456
5614
 
5457
5615
  // src/traits-panel.tsx
5458
5616
  var import_react12 = require("react");
5459
- var import_components2 = require("@kubuild/components");
5617
+ var import_components3 = require("@kubuild/components");
5460
5618
  var import_core7 = require("@kubuild/core");
5461
5619
  var import_lucide_react5 = require("lucide-react");
5462
5620
  var import_jsx_runtime16 = require("react/jsx-runtime");
@@ -5779,7 +5937,7 @@ var TraitsPanel = ({
5779
5937
  grouped.set(key, list);
5780
5938
  }
5781
5939
  const orderedGroups = [
5782
- ...import_components2.TRAIT_GROUP_ORDER.filter((g) => grouped.has(g)),
5940
+ ...import_components3.TRAIT_GROUP_ORDER.filter((g) => grouped.has(g)),
5783
5941
  ...grouped.has(void 0) ? [void 0] : []
5784
5942
  ];
5785
5943
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: `flex flex-col gap-4 ${className ?? "p-3"} text-sm text-slate-900`, children: orderedGroups.map((group) => {
@@ -5787,7 +5945,7 @@ var TraitsPanel = ({
5787
5945
  const traitNames = new Set(groupTraits.map((t) => t.name));
5788
5946
  const isLinkGroup = group === "link" && traitNames.has("href");
5789
5947
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
5790
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2", children: group ? import_components2.TRAIT_GROUP_LABELS[group] : "Other" }),
5948
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2", children: group ? import_components3.TRAIT_GROUP_LABELS[group] : "Other" }),
5791
5949
  isLinkGroup ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5792
5950
  LinkTraitControl,
5793
5951
  {
@@ -6601,7 +6759,7 @@ var InspectorPanel = ({
6601
6759
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
6602
6760
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "block text-xs font-medium text-slate-600 mb-1", children: field.label }),
6603
6761
  !bound && renderPropControl(field),
6604
- (0, import_components3.isBindableField)(field) && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6762
+ (0, import_components4.isBindableField)(field) && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6605
6763
  VariableBindingControl,
6606
6764
  {
6607
6765
  field,
@@ -6949,11 +7107,11 @@ var LayersPanel = ({ registry, className }) => {
6949
7107
 
6950
7108
  // src/breadcrumbs.tsx
6951
7109
  var import_react15 = __toESM(require("react"), 1);
6952
- var import_components4 = require("@kubuild/components");
7110
+ var import_components5 = require("@kubuild/components");
6953
7111
  var import_core10 = require("@kubuild/core");
6954
7112
  var import_jsx_runtime19 = require("react/jsx-runtime");
6955
7113
  var HierarchyBreadcrumbs = ({
6956
- registry = (0, import_components4.createDefaultComponentRegistry)(),
7114
+ registry = (0, import_components5.createDefaultComponentRegistry)(),
6957
7115
  document: propDoc,
6958
7116
  selectedNodeId: propSelectedNodeId,
6959
7117
  className
@@ -7019,7 +7177,15 @@ var import_lucide_react9 = require("lucide-react");
7019
7177
  // src/component-panel.tsx
7020
7178
  var import_react16 = require("react");
7021
7179
  var import_jsx_runtime20 = require("react/jsx-runtime");
7022
- var CATEGORY_ORDER = ["layout", "typography", "media", "form", "interactive", "data", "custom"];
7180
+ var CATEGORY_ORDER = [
7181
+ "layout",
7182
+ "typography",
7183
+ "media",
7184
+ "form",
7185
+ "interactive",
7186
+ "data",
7187
+ "custom"
7188
+ ];
7023
7189
  var CATEGORY_LABELS = {
7024
7190
  layout: "Layout",
7025
7191
  typography: "Typography",
@@ -7031,6 +7197,7 @@ var CATEGORY_LABELS = {
7031
7197
  };
7032
7198
  var ComponentPanel = ({ registry, className }) => {
7033
7199
  const insertComponent = useEditorStore((s) => s.insertComponent);
7200
+ const setDragPayload = useEditorStore((s) => s.setDragPayload);
7034
7201
  const [error, setError] = (0, import_react16.useState)(null);
7035
7202
  const groups = CATEGORY_ORDER.map((category) => ({
7036
7203
  category,
@@ -7040,6 +7207,16 @@ var ComponentPanel = ({ registry, className }) => {
7040
7207
  const result = insertComponent(definition.type, registry);
7041
7208
  setError(result.success ? null : result.error ?? `Could not insert "${definition.label}".`);
7042
7209
  };
7210
+ const handleDragStart = (e, definition) => {
7211
+ e.dataTransfer.effectAllowed = "copy";
7212
+ e.dataTransfer.setData("text/plain", `component:${definition.type}`);
7213
+ e.dataTransfer.setData("application/kubuild-drag-type", "component");
7214
+ e.dataTransfer.setData("application/kubuild-component-type", definition.type);
7215
+ setDragPayload({ type: "component", componentType: definition.type });
7216
+ };
7217
+ const handleDragEnd = () => {
7218
+ setDragPayload(null);
7219
+ };
7043
7220
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: `flex flex-col gap-4 p-3 overflow-y-auto h-full min-h-0 ${className || ""}`, children: [
7044
7221
  error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
7045
7222
  "div",
@@ -7055,12 +7232,16 @@ var ComponentPanel = ({ registry, className }) => {
7055
7232
  "button",
7056
7233
  {
7057
7234
  type: "button",
7235
+ draggable: true,
7236
+ onDragStart: (e) => handleDragStart(e, definition),
7237
+ onDragEnd: handleDragEnd,
7058
7238
  onClick: () => handleInsert(definition),
7059
- title: definition.description || definition.label,
7060
- className: "flex flex-col items-center justify-center p-2.5 min-h-[74px] rounded-lg border border-slate-200 bg-white hover:border-blue-400 hover:bg-blue-50/50 hover:shadow text-slate-700 transition group cursor-pointer text-center",
7239
+ "data-testid": `component-item-${definition.type}`,
7240
+ title: definition.description || `Click to insert or drag to canvas: ${definition.label}`,
7241
+ className: "flex flex-col items-center justify-center p-2.5 min-h-[74px] rounded-lg border border-slate-200 bg-white hover:border-blue-400 hover:bg-blue-50/50 hover:shadow text-slate-700 transition group cursor-grab active:cursor-grabbing text-center select-none",
7061
7242
  children: [
7062
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "mb-1.5 text-slate-500 group-hover:text-blue-600 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ComponentIcon, { iconOrType: definition.icon ?? definition.type, size: 22 }) }),
7063
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[11px] font-medium text-slate-700 group-hover:text-blue-600 text-center leading-tight break-words w-full select-none", children: definition.label })
7243
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "mb-1.5 text-slate-500 group-hover:text-blue-600 transition-colors pointer-events-none", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ComponentIcon, { iconOrType: definition.icon ?? definition.type, size: 22 }) }),
7244
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[11px] font-medium text-slate-700 group-hover:text-blue-600 text-center leading-tight break-words w-full pointer-events-none", children: definition.label })
7064
7245
  ]
7065
7246
  },
7066
7247
  definition.type
@@ -7071,7 +7252,7 @@ var ComponentPanel = ({ registry, className }) => {
7071
7252
 
7072
7253
  // src/blocks-panel.tsx
7073
7254
  var import_react17 = require("react");
7074
- var import_components5 = require("@kubuild/components");
7255
+ var import_components6 = require("@kubuild/components");
7075
7256
  var import_core11 = require("@kubuild/core");
7076
7257
  var import_lucide_react8 = require("lucide-react");
7077
7258
  var import_jsx_runtime21 = require("react/jsx-runtime");
@@ -7137,7 +7318,7 @@ var BlockThumbnail = ({ block }) => {
7137
7318
  }
7138
7319
  };
7139
7320
  var BlocksPanel = ({
7140
- blocks = import_components5.STARTER_BLOCKS,
7321
+ blocks = import_components6.STARTER_BLOCKS,
7141
7322
  className,
7142
7323
  onInsertBlock
7143
7324
  }) => {
@@ -7181,6 +7362,17 @@ var BlocksPanel = ({
7181
7362
  selectNode(nodeTree.id);
7182
7363
  }
7183
7364
  };
7365
+ const setDragPayload = useEditorStore((s) => s.setDragPayload);
7366
+ const handleDragStart = (e, block) => {
7367
+ e.dataTransfer.effectAllowed = "copy";
7368
+ e.dataTransfer.setData("text/plain", `block:${block.id}`);
7369
+ e.dataTransfer.setData("application/kubuild-drag-type", "block");
7370
+ e.dataTransfer.setData("application/kubuild-block-id", block.id);
7371
+ setDragPayload({ type: "block", blockId: block.id });
7372
+ };
7373
+ const handleDragEnd = () => {
7374
+ setDragPayload(null);
7375
+ };
7184
7376
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
7185
7377
  "div",
7186
7378
  {
@@ -7211,13 +7403,17 @@ var BlocksPanel = ({
7211
7403
  "button",
7212
7404
  {
7213
7405
  type: "button",
7406
+ draggable: true,
7407
+ onDragStart: (e) => handleDragStart(e, block),
7408
+ onDragEnd: handleDragEnd,
7214
7409
  "data-testid": "block-card",
7215
7410
  "data-block-id": block.id,
7216
7411
  onClick: () => handleInsert(block),
7217
- className: "flex flex-col justify-between p-2 rounded-lg border border-slate-200 bg-white hover:border-blue-400 hover:shadow-md hover:bg-blue-50/20 transition-all text-left group cursor-pointer",
7412
+ title: block.description || `Click to insert or drag to canvas: ${block.name}`,
7413
+ className: "flex flex-col justify-between p-2 rounded-lg border border-slate-200 bg-white hover:border-blue-400 hover:shadow-md hover:bg-blue-50/20 transition-all text-left group cursor-grab active:cursor-grabbing select-none",
7218
7414
  children: [
7219
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mb-2 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(BlockThumbnail, { block }) }),
7220
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "w-full", children: [
7415
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mb-2 w-full pointer-events-none", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(BlockThumbnail, { block }) }),
7416
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "w-full pointer-events-none", children: [
7221
7417
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex items-center justify-between gap-1 mb-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[11px] font-semibold text-slate-800 group-hover:text-blue-600 truncate", children: block.name }) }),
7222
7418
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-block text-[9px] uppercase tracking-wider font-semibold text-slate-400 bg-slate-100 px-1 py-0.2 rounded", children: block.category })
7223
7419
  ] })
@@ -7367,10 +7563,11 @@ function resolveEditorConfig(config) {
7367
7563
  }
7368
7564
 
7369
7565
  // src/editor.tsx
7566
+ var import_lucide_react10 = require("lucide-react");
7370
7567
  var import_jsx_runtime23 = require("react/jsx-runtime");
7371
7568
  var KubuildEditor = ({
7372
7569
  initialDocument,
7373
- registry = (0, import_components6.createDefaultComponentRegistry)(),
7570
+ registry = (0, import_components7.createDefaultComponentRegistry)(),
7374
7571
  context,
7375
7572
  variableCatalog,
7376
7573
  onChange,
@@ -7388,9 +7585,16 @@ var KubuildEditor = ({
7388
7585
  selectedNodeId,
7389
7586
  navigatorMode,
7390
7587
  tableSpreadsheetMode,
7391
- setTableSpreadsheetMode
7588
+ setTableSpreadsheetMode,
7589
+ undo,
7590
+ redo,
7591
+ canUndo,
7592
+ canRedo
7392
7593
  } = useEditorStore();
7393
7594
  const lastLoadedDocRef = import_react19.default.useRef(void 0);
7595
+ const [isMobileSidebarOpen, setIsMobileSidebarOpen] = (0, import_react19.useState)(false);
7596
+ const [isMobileInspectorOpen, setIsMobileInspectorOpen] = (0, import_react19.useState)(false);
7597
+ const [isMobileLayersOpen, setIsMobileLayersOpen] = (0, import_react19.useState)(false);
7394
7598
  const activeTable = (0, import_react19.useMemo)(
7395
7599
  () => findActiveTableNode(document2.document, selectedNodeId),
7396
7600
  [document2.document, selectedNodeId]
@@ -7419,65 +7623,245 @@ var KubuildEditor = ({
7419
7623
  }, [context, sampleVariables]);
7420
7624
  const viewportWidthMap = {
7421
7625
  desktop: "w-full max-w-6xl",
7422
- tablet: "w-[768px]",
7423
- mobile: "w-[375px]"
7626
+ tablet: "w-full max-w-[768px] sm:w-[768px]",
7627
+ mobile: "w-full max-w-[375px] sm:w-[375px]"
7628
+ };
7629
+ const viewportIcons = {
7630
+ desktop: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Monitor, { className: "w-3.5 h-3.5" }),
7631
+ tablet: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Tablet, { className: "w-3.5 h-3.5" }),
7632
+ mobile: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Smartphone, { className: "w-3.5 h-3.5" })
7424
7633
  };
7425
7634
  const resolvedConfig = (0, import_react19.useMemo)(() => resolveEditorConfig(config), [config]);
7426
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: `flex flex-col h-full bg-slate-100 text-slate-900 relative ${className || ""}`, children: [
7427
- navigatorMode === "floating" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(LayersPanel, { registry }),
7428
- activeTable && tableSpreadsheetMode === "floating" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7429
- TableSpreadsheetEditor,
7430
- {
7431
- registry,
7432
- tableNode: activeTable,
7433
- mode: "floating",
7434
- onToggleMode: () => setTableSpreadsheetMode("docked"),
7435
- onClose: () => setTableSpreadsheetMode("hidden")
7436
- }
7437
- ),
7438
- resolvedConfig.toolbar.enabled && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center justify-between px-4 py-2 bg-white border-b border-slate-200", children: [
7439
- resolvedConfig.toolbar.showTitle ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2", children: [
7440
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "font-semibold text-slate-800 text-sm", children: "KUBUILD Editor" }),
7441
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-xs bg-slate-100 text-slate-600 px-2 py-0.5 rounded border", children: document2.metadata?.title || "Untitled" })
7442
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", {}),
7443
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(EditorToolbar, { registry, config: resolvedConfig.toolbar }),
7444
- resolvedConfig.toolbar.showViewportSwitcher && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex items-center gap-1 bg-slate-100 p-1 rounded-md text-xs", children: ["desktop", "tablet", "mobile"].map((vp) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7445
- "button",
7446
- {
7447
- type: "button",
7448
- onClick: () => setViewport(vp),
7449
- className: `px-3 py-1 rounded capitalize font-medium transition ${viewport === vp ? "bg-white text-blue-600 shadow-sm" : "text-slate-600 hover:text-slate-900"}`,
7450
- children: vp
7451
- },
7452
- vp
7453
- )) }),
7454
- resolvedConfig.toolbar.showSelectionStatus && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "text-xs text-slate-500", children: selectedNodeId ? `Selected: #${selectedNodeId}` : "No element selected" })
7455
- ] }),
7456
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-1 overflow-hidden min-h-0", children: [
7457
- resolvedConfig.sidebar.enabled && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "w-80 shrink-0 bg-white border-r border-slate-200 overflow-hidden flex flex-col min-h-0 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(LeftSidebar, { registry, config: resolvedConfig.sidebar }) }),
7458
- navigatorMode === "docked" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "w-60 shrink-0 bg-white border-r border-slate-200 overflow-hidden flex flex-col min-h-0 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(LayersPanel, { registry }) }),
7459
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex-1 overflow-hidden flex flex-col min-h-0 h-full", children: [
7460
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 overflow-auto p-8 flex justify-center items-start min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7461
- "div",
7635
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
7636
+ "div",
7637
+ {
7638
+ className: `flex flex-col h-full bg-slate-100 text-slate-900 relative overflow-hidden ${className || ""}`,
7639
+ children: [
7640
+ navigatorMode === "floating" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(LayersPanel, { registry }),
7641
+ activeTable && tableSpreadsheetMode === "floating" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7642
+ TableSpreadsheetEditor,
7462
7643
  {
7463
- className: `${viewportWidthMap[viewport]} bg-white shadow-md rounded-lg overflow-hidden transition-all duration-200 min-h-[600px] border border-slate-200`,
7464
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7465
- EditorCanvas,
7644
+ registry,
7645
+ tableNode: activeTable,
7646
+ mode: "floating",
7647
+ onToggleMode: () => setTableSpreadsheetMode("docked"),
7648
+ onClose: () => setTableSpreadsheetMode("hidden")
7649
+ }
7650
+ ),
7651
+ isMobileSidebarOpen && resolvedConfig.sidebar.enabled && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "fixed inset-0 z-50 flex lg:hidden animate-in fade-in duration-200", children: [
7652
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7653
+ "div",
7654
+ {
7655
+ className: "fixed inset-0 bg-slate-950/50 backdrop-blur-xs transition-opacity",
7656
+ onClick: () => setIsMobileSidebarOpen(false)
7657
+ }
7658
+ ),
7659
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "relative w-80 max-w-[85vw] bg-white h-full shadow-2xl flex flex-col z-10 animate-in slide-in-from-left duration-200", children: [
7660
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center justify-between px-3.5 py-2.5 border-b border-slate-200 bg-slate-50", children: [
7661
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2", children: [
7662
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Boxes, { className: "w-4 h-4 text-blue-600" }),
7663
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "font-bold text-xs text-slate-800", children: "Add Components & Blocks" })
7664
+ ] }),
7665
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7666
+ "button",
7667
+ {
7668
+ type: "button",
7669
+ onClick: () => setIsMobileSidebarOpen(false),
7670
+ className: "p-1 rounded-md text-slate-400 hover:text-slate-700 hover:bg-slate-200 transition",
7671
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.X, { className: "w-4 h-4" })
7672
+ }
7673
+ )
7674
+ ] }),
7675
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 overflow-hidden min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(LeftSidebar, { registry, config: resolvedConfig.sidebar }) })
7676
+ ] })
7677
+ ] }),
7678
+ isMobileInspectorOpen && resolvedConfig.inspector.enabled && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "fixed inset-0 z-50 flex justify-end lg:hidden animate-in fade-in duration-200", children: [
7679
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7680
+ "div",
7681
+ {
7682
+ className: "fixed inset-0 bg-slate-950/50 backdrop-blur-xs transition-opacity",
7683
+ onClick: () => setIsMobileInspectorOpen(false)
7684
+ }
7685
+ ),
7686
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "relative w-84 max-w-[90vw] sm:max-w-md bg-white h-full shadow-2xl flex flex-col z-10 animate-in slide-in-from-right duration-200", children: [
7687
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center justify-between px-3.5 py-2.5 border-b border-slate-200 bg-slate-50", children: [
7688
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2", children: [
7689
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Sliders, { className: "w-4 h-4 text-blue-600" }),
7690
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "font-bold text-xs text-slate-800", children: selectedNodeId ? `Inspector (#${selectedNodeId})` : "Inspector & Properties" })
7691
+ ] }),
7692
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7693
+ "button",
7694
+ {
7695
+ type: "button",
7696
+ onClick: () => setIsMobileInspectorOpen(false),
7697
+ className: "p-1 rounded-md text-slate-400 hover:text-slate-700 hover:bg-slate-200 transition",
7698
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.X, { className: "w-4 h-4" })
7699
+ }
7700
+ )
7701
+ ] }),
7702
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 overflow-hidden min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(InspectorPanel, { registry, config: resolvedConfig.inspector }) })
7703
+ ] })
7704
+ ] }),
7705
+ isMobileLayersOpen && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "fixed inset-0 z-50 flex lg:hidden animate-in fade-in duration-200", children: [
7706
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7707
+ "div",
7708
+ {
7709
+ className: "fixed inset-0 bg-slate-950/50 backdrop-blur-xs transition-opacity",
7710
+ onClick: () => setIsMobileLayersOpen(false)
7711
+ }
7712
+ ),
7713
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "relative w-72 max-w-[85vw] bg-white h-full shadow-2xl flex flex-col z-10 animate-in slide-in-from-left duration-200", children: [
7714
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center justify-between px-3.5 py-2.5 border-b border-slate-200 bg-slate-50", children: [
7715
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2", children: [
7716
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Layers, { className: "w-4 h-4 text-blue-600" }),
7717
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "font-bold text-xs text-slate-800", children: "Element Tree / Layers" })
7718
+ ] }),
7719
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7720
+ "button",
7721
+ {
7722
+ type: "button",
7723
+ onClick: () => setIsMobileLayersOpen(false),
7724
+ className: "p-1 rounded-md text-slate-400 hover:text-slate-700 hover:bg-slate-200 transition",
7725
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.X, { className: "w-4 h-4" })
7726
+ }
7727
+ )
7728
+ ] }),
7729
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 overflow-hidden min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(LayersPanel, { registry }) })
7730
+ ] })
7731
+ ] }),
7732
+ resolvedConfig.toolbar.enabled && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center justify-between px-3 py-1.5 sm:px-4 sm:py-2 bg-white border-b border-slate-200 gap-2 overflow-x-auto min-h-[44px]", children: [
7733
+ resolvedConfig.toolbar.showTitle ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2 shrink-0", children: [
7734
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "font-bold text-slate-800 text-xs sm:text-sm tracking-tight", children: "KUBUILD Editor" }),
7735
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-[11px] bg-slate-100 text-slate-600 px-1.5 py-0.5 rounded border max-w-[120px] sm:max-w-[200px] truncate font-medium", children: document2.metadata?.title || "Untitled" })
7736
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", {}),
7737
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2 shrink-0", children: [
7738
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(EditorToolbar, { registry, config: resolvedConfig.toolbar }),
7739
+ resolvedConfig.toolbar.showViewportSwitcher && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex items-center gap-0.5 bg-slate-100 p-0.5 sm:p-1 rounded-md text-xs border border-slate-200/80", children: ["desktop", "tablet", "mobile"].map((vp) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
7740
+ "button",
7741
+ {
7742
+ type: "button",
7743
+ onClick: () => setViewport(vp),
7744
+ title: `Switch to ${vp} preview`,
7745
+ className: `px-2 py-1 sm:px-2.5 sm:py-1 rounded capitalize font-medium transition flex items-center gap-1 text-[11px] sm:text-xs ${viewport === vp ? "bg-white text-blue-600 shadow-xs font-semibold" : "text-slate-600 hover:text-slate-900"}`,
7746
+ children: [
7747
+ viewportIcons[vp],
7748
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "hidden md:inline", children: vp })
7749
+ ]
7750
+ },
7751
+ vp
7752
+ )) })
7753
+ ] }),
7754
+ resolvedConfig.toolbar.showSelectionStatus && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "hidden xl:block text-xs text-slate-500 shrink-0 font-mono", children: selectedNodeId ? `Selected: #${selectedNodeId}` : "No element selected" })
7755
+ ] }),
7756
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-1 overflow-hidden min-h-0 relative", children: [
7757
+ resolvedConfig.sidebar.enabled && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "hidden lg:flex w-80 shrink-0 bg-white border-r border-slate-200 overflow-hidden flex-col min-h-0 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(LeftSidebar, { registry, config: resolvedConfig.sidebar }) }),
7758
+ navigatorMode === "docked" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "hidden lg:flex w-60 shrink-0 bg-white border-r border-slate-200 overflow-hidden flex-col min-h-0 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(LayersPanel, { registry }) }),
7759
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex-1 overflow-hidden flex flex-col min-h-0 h-full bg-slate-100/90 relative", children: [
7760
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 overflow-auto p-2 sm:p-4 md:p-8 flex justify-center items-start min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7761
+ "div",
7762
+ {
7763
+ className: `${viewportWidthMap[viewport]} bg-white shadow-md rounded-lg overflow-hidden transition-all duration-200 min-h-[500px] border border-slate-200`,
7764
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7765
+ EditorCanvas,
7766
+ {
7767
+ registry,
7768
+ context: previewContext,
7769
+ viewport,
7770
+ onDiagnostic,
7771
+ config: resolvedConfig.canvas
7772
+ }
7773
+ )
7774
+ }
7775
+ ) }),
7776
+ selectedNodeId && !isMobileInspectorOpen && resolvedConfig.inspector.enabled && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "lg:hidden fixed bottom-14 left-1/2 -translate-x-1/2 z-30 animate-in fade-in slide-in-from-bottom-2", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
7777
+ "button",
7778
+ {
7779
+ type: "button",
7780
+ onClick: () => setIsMobileInspectorOpen(true),
7781
+ className: "flex items-center gap-2 bg-blue-600 hover:bg-blue-500 text-white text-xs font-semibold px-4 py-2 rounded-full shadow-xl border border-blue-400/40 active:scale-95 transition",
7782
+ children: [
7783
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Sliders, { className: "w-3.5 h-3.5" }),
7784
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { children: [
7785
+ "Edit Element (#",
7786
+ selectedNodeId,
7787
+ ")"
7788
+ ] })
7789
+ ]
7790
+ }
7791
+ ) }),
7792
+ resolvedConfig.canvas.showBreadcrumbs && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(HierarchyBreadcrumbs, { registry })
7793
+ ] }),
7794
+ resolvedConfig.inspector.enabled && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "hidden lg:flex w-72 shrink-0 bg-white border-l border-slate-200 overflow-hidden flex-col min-h-0 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(InspectorPanel, { registry, config: resolvedConfig.inspector }) })
7795
+ ] }),
7796
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex lg:hidden items-center justify-around bg-white border-t border-slate-200 px-2 py-1.5 z-20 shrink-0 shadow-lg select-none min-h-[48px]", children: [
7797
+ resolvedConfig.sidebar.enabled && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
7798
+ "button",
7799
+ {
7800
+ type: "button",
7801
+ onClick: () => setIsMobileSidebarOpen(true),
7802
+ className: "flex flex-col items-center justify-center gap-0.5 px-3 py-1 rounded-md text-slate-600 hover:text-blue-600 hover:bg-slate-50 active:bg-slate-100 transition",
7803
+ children: [
7804
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Plus, { className: "w-4 h-4 text-blue-600" }),
7805
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-[10px] font-medium", children: "Add" })
7806
+ ]
7807
+ }
7808
+ ),
7809
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
7810
+ "button",
7811
+ {
7812
+ type: "button",
7813
+ onClick: () => setIsMobileLayersOpen(true),
7814
+ className: "flex flex-col items-center justify-center gap-0.5 px-3 py-1 rounded-md text-slate-600 hover:text-blue-600 hover:bg-slate-50 active:bg-slate-100 transition",
7815
+ children: [
7816
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Layers, { className: "w-4 h-4" }),
7817
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-[10px] font-medium", children: "Layers" })
7818
+ ]
7819
+ }
7820
+ ),
7821
+ resolvedConfig.inspector.enabled && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
7822
+ "button",
7823
+ {
7824
+ type: "button",
7825
+ onClick: () => setIsMobileInspectorOpen(true),
7826
+ className: `flex flex-col items-center justify-center gap-0.5 px-3 py-1 rounded-md transition relative ${selectedNodeId ? "text-blue-600 font-semibold" : "text-slate-600 hover:text-blue-600 hover:bg-slate-50"}`,
7827
+ children: [
7828
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "relative", children: [
7829
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Sliders, { className: "w-4 h-4" }),
7830
+ selectedNodeId && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "absolute -top-1 -right-1 w-2 h-2 rounded-full bg-blue-600 ring-2 ring-white" })
7831
+ ] }),
7832
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-[10px]", children: selectedNodeId ? "Inspect *" : "Inspect" })
7833
+ ]
7834
+ }
7835
+ ),
7836
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "h-5 w-px bg-slate-200 mx-0.5" }),
7837
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-1", children: [
7838
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7839
+ "button",
7840
+ {
7841
+ type: "button",
7842
+ disabled: !canUndo,
7843
+ onClick: undo,
7844
+ title: "Undo",
7845
+ className: "p-2 rounded text-slate-600 hover:text-blue-600 disabled:opacity-30 disabled:hover:text-slate-600 transition",
7846
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Undo2, { className: "w-4 h-4" })
7847
+ }
7848
+ ),
7849
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7850
+ "button",
7466
7851
  {
7467
- registry,
7468
- context: previewContext,
7469
- viewport,
7470
- onDiagnostic,
7471
- config: resolvedConfig.canvas
7852
+ type: "button",
7853
+ disabled: !canRedo,
7854
+ onClick: redo,
7855
+ title: "Redo",
7856
+ className: "p-2 rounded text-slate-600 hover:text-blue-600 disabled:opacity-30 disabled:hover:text-slate-600 transition",
7857
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Redo2, { className: "w-4 h-4" })
7472
7858
  }
7473
7859
  )
7474
- }
7475
- ) }),
7476
- resolvedConfig.canvas.showBreadcrumbs && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(HierarchyBreadcrumbs, { registry })
7477
- ] }),
7478
- resolvedConfig.inspector.enabled && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "w-72 shrink-0 bg-white border-l border-slate-200 overflow-hidden flex flex-col min-h-0 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(InspectorPanel, { registry, config: resolvedConfig.inspector }) })
7479
- ] })
7480
- ] });
7860
+ ] })
7861
+ ] })
7862
+ ]
7863
+ }
7864
+ );
7481
7865
  };
7482
7866
  // Annotate the CommonJS export names for ESM import in node:
7483
7867
  0 && (module.exports = {