@decido/shell 4.0.1 → 4.0.3

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.
Files changed (38) hide show
  1. package/dist/CenterComposite-RPEGBKQU.mjs +1697 -0
  2. package/dist/DebugPanel-KEKDMHDN.mjs +14 -0
  3. package/dist/MorphShell-FKDBB7E5.mjs +14 -0
  4. package/dist/PlaygroundAppSidebar-ALYJJGAO.mjs +9 -0
  5. package/dist/PlaygroundChat-MI2KXMK6.mjs +15 -0
  6. package/dist/PlaygroundTerminal-5AV4BJAI.mjs +7 -0
  7. package/dist/PluginSandbox-WMNAUQOJ.mjs +188 -0
  8. package/dist/ReactFlowEditor-RTF2652X.mjs +3574 -0
  9. package/dist/ReactFlowEditor-ZW5MCN5Y.css +561 -0
  10. package/dist/TimelineEditor-N4HRMHTB.mjs +226 -0
  11. package/dist/WidgetSlotPanel-KJI4CHHD.mjs +11 -0
  12. package/dist/chunk-2YMI4N5I.mjs +2004 -0
  13. package/dist/chunk-3BZX7LF2.mjs +139 -0
  14. package/dist/chunk-3P4P3M54.mjs +136 -0
  15. package/dist/chunk-F3OTFHNO.mjs +40 -0
  16. package/dist/chunk-IMHORBTL.mjs +48 -0
  17. package/dist/chunk-JF5QSJYT.mjs +295 -0
  18. package/dist/chunk-LWMMFTJC.mjs +382 -0
  19. package/dist/chunk-MSVEFEXE.mjs +179 -0
  20. package/dist/chunk-OCHGY2MN.mjs +1662 -0
  21. package/dist/chunk-PMYAM764.mjs +813 -0
  22. package/dist/chunk-Q64KZXPK.mjs +43 -0
  23. package/dist/chunk-QHQW2HMU.mjs +155 -0
  24. package/dist/chunk-RWZ4BOIN.mjs +385 -0
  25. package/dist/chunk-UHT6FIYF.mjs +195 -0
  26. package/dist/chunk-UJCSKKID.mjs +30 -0
  27. package/dist/chunk-V3CYNPGL.mjs +8758 -0
  28. package/dist/chunk-VBPGEFNM.mjs +2381 -0
  29. package/dist/chunk-XMSU6UWD.mjs +158 -0
  30. package/dist/chunk-ZCCCBHE6.mjs +55 -0
  31. package/dist/index.css +561 -0
  32. package/dist/index.js +65130 -0
  33. package/dist/index.mjs +40248 -0
  34. package/dist/useIntentLens-LEQCAXCK.mjs +13 -0
  35. package/dist/useSuggestionsStore-4L2AIZ2D.mjs +7 -0
  36. package/dist/wasm-QFXGEYGP.mjs +81 -0
  37. package/package.json +27 -22
  38. package/src/index.ts +0 -97
@@ -0,0 +1,2004 @@
1
+ import {
2
+ getRegisteredShellTypes,
3
+ registerShell,
4
+ resolveShell
5
+ } from "./chunk-IMHORBTL.mjs";
6
+ import {
7
+ useTheme
8
+ } from "./chunk-JF5QSJYT.mjs";
9
+ import {
10
+ getMorphComponent,
11
+ useMorphologyStore
12
+ } from "./chunk-ZCCCBHE6.mjs";
13
+ import {
14
+ useMorphInstanceStore
15
+ } from "./chunk-UHT6FIYF.mjs";
16
+ import {
17
+ useLayoutStore
18
+ } from "./chunk-MSVEFEXE.mjs";
19
+ import {
20
+ usePlaygroundStore
21
+ } from "./chunk-XMSU6UWD.mjs";
22
+
23
+ // src/components/shell/MorphShell.tsx
24
+ import React9, { useCallback as useCallback6 } from "react";
25
+ import { AnimatePresence as AnimatePresence3, motion as motion4 } from "motion/react";
26
+ import { ArrowLeft as ArrowLeft2, X as X4, Maximize2 as Maximize22, Minimize2 as Minimize22 } from "lucide-react";
27
+
28
+ // src/components/playground/PlaygroundCanvas.tsx
29
+ import React2 from "react";
30
+ import { motion, AnimatePresence } from "motion/react";
31
+ import { X, ArrowLeft, Layers, Code2 as Code22, Droplets, MonitorPlay } from "lucide-react";
32
+
33
+ // src/components/studio/editor/StageContentRenderer.tsx
34
+ import React, { Suspense } from "react";
35
+ import { Code2, Loader2 } from "lucide-react";
36
+ import { jsx, jsxs } from "react/jsx-runtime";
37
+ var LazyRecharts = React.lazy(() => import("recharts").then((m) => ({
38
+ default: ({ chartData, color }) => /* @__PURE__ */ jsx(m.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsx(m.AreaChart, { data: chartData, children: /* @__PURE__ */ jsx(m.Area, { type: "monotone", dataKey: "val", stroke: "currentColor", fill: "currentColor", strokeWidth: 2, className: color }) }) })
39
+ })));
40
+ var DataTableArtifact = ({ columns, rows }) => /* @__PURE__ */ jsx("div", { className: "w-full h-full overflow-auto bg-surface-primary border border-border-default rounded-2xl shadow-xl p-4 custom-scrollbar", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-left text-sm text-text-primary", children: [
41
+ /* @__PURE__ */ jsx("thead", { className: "text-xs text-text-muted uppercase bg-surface-glass", children: /* @__PURE__ */ jsx("tr", { children: columns?.map((col, i) => /* @__PURE__ */ jsx("th", { className: "px-6 py-3", children: col }, i)) }) }),
42
+ /* @__PURE__ */ jsx("tbody", { children: rows?.map((row, i) => /* @__PURE__ */ jsx("tr", { className: "border-b border-border-subtle hover:bg-surface-glass transition-colors", children: row?.map((cell, j) => /* @__PURE__ */ jsx("td", { className: "px-6 py-4", children: cell }, j)) }, i)) })
43
+ ] }) });
44
+ var MarkdownArtifact = ({ content }) => /* @__PURE__ */ jsx("div", { className: "w-full h-full overflow-auto bg-surface-primary border border-border-default rounded-2xl shadow-xl p-8 whitespace-pre-wrap text-text-primary font-sans leading-relaxed text-sm custom-scrollbar", children: content });
45
+ function StageContentRenderer(props) {
46
+ const { renderingType, activeStage, activeArtifactData, activeConfig, activeState, prototypeBrand, chartData, dragConstraintsRef, defaultViewMode, instanceId } = props;
47
+ const RegisteredShell = resolveShell(renderingType);
48
+ if (RegisteredShell) {
49
+ return /* @__PURE__ */ jsx(
50
+ RegisteredShell,
51
+ {
52
+ instanceId: instanceId || activeStage?.sourceNodeId || "default",
53
+ data: activeStage?.data || {},
54
+ artifacts: [],
55
+ activeArtifactIndex: 0
56
+ }
57
+ );
58
+ }
59
+ if (renderingType === "workbench") {
60
+ const WC = getMorphComponent("AdminWorkbench") || getMorphComponent("Workbench");
61
+ if (WC) return /* @__PURE__ */ jsx(WC, { ...activeStage?.data || {} });
62
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-text-muted font-mono text-sm", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
63
+ /* @__PURE__ */ jsx(Code2, { size: 48, className: "mx-auto mb-4 text-text-muted" }),
64
+ /* @__PURE__ */ jsx("p", { children: "AdminWorkbench no registrado" }),
65
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-text-muted mt-1", children: "Usa registerShell('shell-vscode', Component)" })
66
+ ] }) });
67
+ }
68
+ if (renderingType === "liquid") {
69
+ const LC = getMorphComponent("SafeLiquidUI");
70
+ if (LC && activeStage?.data) return /* @__PURE__ */ jsx("div", { className: "w-full h-full p-6 overflow-auto", children: /* @__PURE__ */ jsx(LC, { uiSchema: activeStage.data.code || activeStage.data, tenantId: "system" }) });
71
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full p-6 overflow-auto", children: /* @__PURE__ */ jsx("pre", { className: "text-xs text-text-secondary font-mono bg-surface-glass rounded-xl p-4 border border-border-subtle", children: JSON.stringify(activeStage?.data || {}, null, 2) }) });
72
+ }
73
+ if (renderingType === "custom" && activeStage?.componentId) {
74
+ const CC = getMorphComponent(activeStage.componentId);
75
+ if (CC) return /* @__PURE__ */ jsx(CC, { ...activeStage?.data || {} });
76
+ return /* @__PURE__ */ jsxs("div", { className: "w-full h-full flex items-center justify-center text-text-muted font-mono text-sm", children: [
77
+ "Componente '",
78
+ activeStage.componentId,
79
+ "' no registrado"
80
+ ] });
81
+ }
82
+ if (renderingType === "artifact" && (activeArtifactData || activeStage?.data)) {
83
+ const d = activeStage?.data || activeArtifactData;
84
+ if (d.artifact === "graph-visualizer") return /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center p-6 text-text-muted font-mono", children: "Graph Visualizer temporalmente deshabilitado por conflictos CSS." });
85
+ if (d.artifact === "data-table") return /* @__PURE__ */ jsx("div", { className: "flex-1 w-full h-full p-6 py-2", children: /* @__PURE__ */ jsx(DataTableArtifact, { columns: d.columns, rows: d.rows }) });
86
+ if (d.artifact === "markdown") return /* @__PURE__ */ jsx("div", { className: "flex-1 w-full h-full p-6 py-2", children: /* @__PURE__ */ jsx(MarkdownArtifact, { content: d.content }) });
87
+ return /* @__PURE__ */ jsxs("div", { className: "flex-1 flex items-center justify-center p-6 text-text-muted font-mono", children: [
88
+ "Componente ",
89
+ d.artifact,
90
+ " desconocido."
91
+ ] });
92
+ }
93
+ return /* @__PURE__ */ jsxs("div", { ref: dragConstraintsRef, className: "flex-1 h-full rounded-2xl bg-surface-overlay flex items-center justify-center relative overflow-hidden group border border-border-subtle m-6 mt-2", children: [
94
+ /* @__PURE__ */ jsx("svg", { className: "absolute inset-0 pointer-events-none z-0 w-full h-full opacity-30", children: /* @__PURE__ */ jsx("path", { d: "M 150 75 C 250 150, 350 50, 400 100", stroke: "cyan", strokeWidth: 2, fill: "none", strokeDasharray: "10, 10" }) }),
95
+ activeConfig?.widgets?.map((w) => {
96
+ const IC = w.icon;
97
+ return /* @__PURE__ */ jsxs("div", { className: `absolute cursor-grab rounded-2xl border ${w.bg} ${w.border} p-4 shadow-xl backdrop-blur-md flex flex-col justify-between`, style: { width: w.w, height: w.h, left: w.x, top: w.y }, children: [
98
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-start pointer-events-none z-10", children: [
99
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-text-primary/80", children: w.title }),
100
+ /* @__PURE__ */ jsx("div", { className: `p-1.5 rounded-lg bg-surface-glass ${w.color}`, children: /* @__PURE__ */ jsx(IC, { size: 16 }) })
101
+ ] }),
102
+ /* @__PURE__ */ jsx("div", { className: `text-2xl font-bold mt-2 ${w.color} pointer-events-none z-10`, children: w.value }),
103
+ w.chart && /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 w-full h-16 opacity-30 pointer-events-none rounded-b-2xl overflow-hidden", children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(LazyRecharts, { chartData, color: w.color }) }) })
104
+ ] }, `${prototypeBrand}-${w.id}`);
105
+ })
106
+ ] });
107
+ }
108
+
109
+ // src/components/playground/PlaygroundCanvas.tsx
110
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
111
+ var STAGE_META = {
112
+ "workbench": { icon: Code22, label: "Workbench", color: "text-purple-400" },
113
+ "liquid": { icon: Droplets, label: "UI Din\xE1mica", color: "text-amber-400" },
114
+ "artifact": { icon: MonitorPlay, label: "Artefacto", color: "text-emerald-400" },
115
+ "custom": { icon: Layers, label: "Custom", color: "text-violet-400" }
116
+ };
117
+ var PlaygroundCanvas = React2.memo(function PlaygroundCanvas2({
118
+ showCanvas,
119
+ setShowCanvas,
120
+ activeConfig,
121
+ prototypeBrand,
122
+ activeState,
123
+ chartData,
124
+ dragConstraintsRef
125
+ }) {
126
+ if (!showCanvas) return null;
127
+ const activeStage = useMorphologyStore((s) => s.activeStage);
128
+ const stageHistory = useMorphologyStore((s) => s.stageHistory);
129
+ const popStage = useMorphologyStore((s) => s.popStage);
130
+ const clearStages = useMorphologyStore((s) => s.clearStages);
131
+ const activeArtifactData = usePlaygroundStore((s) => s.activeArtifactData);
132
+ const [defaultViewMode, setDefaultViewMode] = React2.useState("3d");
133
+ const effectiveStage = activeStage || (activeArtifactData ? { type: "artifact", data: activeArtifactData } : null);
134
+ const renderingType = effectiveStage?.type || defaultViewMode;
135
+ const stageMeta = STAGE_META[renderingType] || STAGE_META["custom"];
136
+ const StageIcon = stageMeta.icon;
137
+ const handleClose = () => {
138
+ clearStages();
139
+ usePlaygroundStore.getState().setActiveArtifactData(null);
140
+ setShowCanvas(false);
141
+ };
142
+ const handleBack = () => {
143
+ stageHistory.length > 0 ? popStage() : clearStages();
144
+ };
145
+ return /* @__PURE__ */ jsx2(AnimatePresence, { children: /* @__PURE__ */ jsxs2(
146
+ motion.div,
147
+ {
148
+ initial: { opacity: 0, scale: 0.95 },
149
+ animate: { opacity: 1, scale: 1 },
150
+ exit: { opacity: 0, scale: 0.95 },
151
+ className: "w-full h-full bg-surface-secondary flex flex-col overflow-hidden relative border-l border-border-subtle",
152
+ children: [
153
+ /* @__PURE__ */ jsxs2("div", { className: "flex justify-between items-center pl-2 z-10 p-4 pb-0 shrink-0", children: [
154
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
155
+ (activeStage || activeArtifactData) && /* @__PURE__ */ jsx2("button", { onClick: handleBack, className: "p-1.5 rounded-lg hover:bg-surface-glass text-text-muted hover:text-text-primary transition-colors", title: "Volver", children: /* @__PURE__ */ jsx2(ArrowLeft, { size: 16 }) }),
156
+ /* @__PURE__ */ jsxs2("h2", { className: "text-lg font-bold flex items-center gap-2 text-text-primary", children: [
157
+ /* @__PURE__ */ jsx2("div", { className: "w-7 h-7 rounded-lg flex items-center justify-center bg-surface-glass", children: /* @__PURE__ */ jsx2(StageIcon, { size: 14, className: stageMeta.color }) }),
158
+ /* @__PURE__ */ jsx2("span", { className: "truncate max-w-[200px]", children: activeStage?.label || (activeArtifactData ? "Artefacto" : "Lienzo de Trabajo") }),
159
+ /* @__PURE__ */ jsx2("span", { className: "text-text-muted font-mono text-[10px] ml-1", children: renderingType.toUpperCase() })
160
+ ] })
161
+ ] }),
162
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3", children: [
163
+ stageHistory.length > 0 && /* @__PURE__ */ jsxs2("div", { className: "hidden md:flex items-center gap-1 text-[10px] text-text-muted font-mono", children: [
164
+ stageHistory.map((s, i) => /* @__PURE__ */ jsxs2(React2.Fragment, { children: [
165
+ /* @__PURE__ */ jsx2("span", { className: "hover:text-text-secondary cursor-pointer", onClick: () => {
166
+ const st = useMorphologyStore.getState();
167
+ for (let j = 0; j < stageHistory.length - i; j++) st.popStage();
168
+ }, children: s.label || s.type }),
169
+ /* @__PURE__ */ jsx2("span", { children: "\u203A" })
170
+ ] }, i)),
171
+ /* @__PURE__ */ jsx2("span", { className: "text-text-secondary", children: activeStage?.label || renderingType })
172
+ ] }),
173
+ !effectiveStage && /* @__PURE__ */ jsxs2("div", { className: "flex bg-surface-glass border border-border-default rounded-full p-1", children: [
174
+ /* @__PURE__ */ jsx2("button", { onClick: () => setDefaultViewMode("2d"), className: `px-3 py-1 text-[10px] font-bold tracking-wider rounded-full transition-all ${defaultViewMode === "2d" ? "bg-surface-glass text-text-primary shadow-xs" : "text-text-muted hover:text-text-primary"}`, children: "2D DATA" }),
175
+ /* @__PURE__ */ jsx2("button", { onClick: () => setDefaultViewMode("3d"), className: `px-3 py-1 text-[10px] font-bold tracking-wider rounded-full transition-all ${defaultViewMode === "3d" ? "bg-cyan-500/20 text-cyan-400 shadow-[0_0_10px_rgba(6,182,212,0.3)]" : "text-text-muted hover:text-text-primary"}`, children: "3D SCENE" })
176
+ ] }),
177
+ /* @__PURE__ */ jsx2("button", { onClick: handleClose, className: "p-2 bg-surface-glass hover:bg-red-500/20 text-text-secondary hover:text-red-400 rounded-full transition-colors", children: /* @__PURE__ */ jsx2(X, { size: 16 }) })
178
+ ] })
179
+ ] }),
180
+ /* @__PURE__ */ jsx2("div", { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx2(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsx2(motion.div, { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, transition: { duration: 0.2 }, className: "w-full h-full", children: /* @__PURE__ */ jsx2(
181
+ StageContentRenderer,
182
+ {
183
+ renderingType,
184
+ activeStage,
185
+ activeArtifactData,
186
+ activeConfig,
187
+ activeState,
188
+ prototypeBrand,
189
+ chartData,
190
+ dragConstraintsRef,
191
+ defaultViewMode
192
+ }
193
+ ) }, renderingType + (activeStage?.componentId || "")) }) })
194
+ ]
195
+ }
196
+ ) });
197
+ });
198
+
199
+ // src/components/shell/ArtifactBar.tsx
200
+ import { motion as motion2, AnimatePresence as AnimatePresence2 } from "motion/react";
201
+ import { X as X2, FileCode, FileText, Globe, Layers as Layers2, MessageSquare } from "lucide-react";
202
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
203
+ var SHELL_ICONS = {
204
+ "markdown": FileText,
205
+ "iframe": Globe,
206
+ "shell-vscode": FileCode,
207
+ "shell-canvas": Layers2,
208
+ "shell-omnichannel": MessageSquare
209
+ };
210
+ function getArtifactIcon(shellType) {
211
+ return SHELL_ICONS[shellType] || FileCode;
212
+ }
213
+ function ArtifactBar({ instanceId, className = "" }) {
214
+ const instance = useMorphInstanceStore((s) => s.getInstance(instanceId));
215
+ const setActiveArtifact = useMorphInstanceStore((s) => s.setActiveArtifact);
216
+ const removeArtifact = useMorphInstanceStore((s) => s.removeArtifact);
217
+ if (!instance || instance.artifacts.length <= 1) return null;
218
+ return /* @__PURE__ */ jsx3("div", { className: `flex items-center gap-0.5 px-2 py-1 bg-surface-primary border-b border-border-subtle overflow-x-auto scrollbar-hide ${className}`, children: /* @__PURE__ */ jsx3(AnimatePresence2, { mode: "popLayout", children: instance.artifacts.map((artifact, index) => {
219
+ const isActive = index === instance.activeArtifactIndex;
220
+ const Icon = getArtifactIcon(artifact.shellType);
221
+ return /* @__PURE__ */ jsxs3(
222
+ motion2.button,
223
+ {
224
+ layout: true,
225
+ initial: { opacity: 0, scale: 0.9, width: 0 },
226
+ animate: { opacity: 1, scale: 1, width: "auto" },
227
+ exit: { opacity: 0, scale: 0.9, width: 0 },
228
+ onClick: () => setActiveArtifact(instanceId, index),
229
+ className: `
230
+ flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[10px] font-medium
231
+ transition-colors whitespace-nowrap group relative shrink-0
232
+ ${isActive ? "bg-surface-glass text-text-primary border border-border-default" : "text-text-muted hover:text-text-primary hover:bg-surface-glass border border-transparent"}
233
+ `,
234
+ children: [
235
+ /* @__PURE__ */ jsx3(Icon, { size: 10, className: isActive ? "text-cyan-400" : "text-text-muted" }),
236
+ /* @__PURE__ */ jsx3("span", { className: "truncate max-w-[100px]", children: artifact.name }),
237
+ artifact.version > 1 && /* @__PURE__ */ jsxs3("span", { className: "text-[8px] text-text-muted font-mono", children: [
238
+ "v",
239
+ artifact.version
240
+ ] }),
241
+ instance.artifacts.length > 1 && /* @__PURE__ */ jsx3(
242
+ "span",
243
+ {
244
+ onClick: (e) => {
245
+ e.stopPropagation();
246
+ removeArtifact(instanceId, artifact.id);
247
+ },
248
+ className: "w-3.5 h-3.5 rounded flex items-center justify-center\n opacity-0 group-hover:opacity-100 hover:bg-surface-glass hover:text-red-400\n transition-all ml-0.5",
249
+ children: /* @__PURE__ */ jsx3(X2, { size: 8 })
250
+ }
251
+ ),
252
+ isActive && /* @__PURE__ */ jsx3(
253
+ motion2.div,
254
+ {
255
+ layoutId: "artifact-indicator",
256
+ className: "absolute bottom-0 left-1 right-1 h-[1.5px] bg-cyan-500/50 rounded-full"
257
+ }
258
+ )
259
+ ]
260
+ },
261
+ artifact.id
262
+ );
263
+ }) }) });
264
+ }
265
+
266
+ // src/components/shell/morphStageMeta.ts
267
+ import { Box, CodeXml, Droplets as Droplets2, Puzzle, FileBox, LayoutDashboard } from "lucide-react";
268
+ var STAGE_META2 = {
269
+ "3d": { icon: Box, color: "text-cyan-400" },
270
+ "2d": { icon: LayoutDashboard, color: "text-emerald-400" },
271
+ "workbench": { icon: CodeXml, color: "text-purple-400" },
272
+ "liquid": { icon: Droplets2, color: "text-blue-400" },
273
+ "custom": { icon: Puzzle, color: "text-orange-400" },
274
+ "artifact": { icon: FileBox, color: "text-yellow-400" }
275
+ };
276
+
277
+ // src/components/shell/shells/BuiltInShells.tsx
278
+ import { useMemo, useRef, useEffect, useState, useCallback } from "react";
279
+ import {
280
+ Paintbrush,
281
+ X as X3,
282
+ Plus as Plus2,
283
+ Copy,
284
+ RotateCcw,
285
+ Search,
286
+ MousePointer,
287
+ ChevronDown,
288
+ ChevronRight
289
+ } from "lucide-react";
290
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
291
+ var MarkdownShell = ({ data }) => {
292
+ const content = data?.content || data?.markdown || "";
293
+ return /* @__PURE__ */ jsx4("div", { className: "w-full h-full overflow-auto bg-surface-primary p-8 custom-scrollbar", children: /* @__PURE__ */ jsx4("div", { className: "max-w-3xl mx-auto prose prose-invert prose-sm prose-zinc", children: /* @__PURE__ */ jsx4("div", { className: "whitespace-pre-wrap text-text-primary font-sans leading-relaxed text-sm", children: content }) }) });
294
+ };
295
+ var TW_BRIDGE_SCRIPT = `
296
+ <script>
297
+ (function() {
298
+ var inspectorActive = false;
299
+ var selectedEl = null;
300
+ var hoveredEl = null;
301
+
302
+ function generateSelector(el) {
303
+ var parts = [];
304
+ var cur = el;
305
+ while (cur && cur !== document.body && cur !== document.documentElement) {
306
+ var part = cur.tagName.toLowerCase();
307
+ if (cur.id) { parts.unshift('#' + cur.id); break; }
308
+ var parent = cur.parentElement;
309
+ if (parent) {
310
+ var sibs = Array.from(parent.children).filter(function(c) { return c.tagName === cur.tagName; });
311
+ if (sibs.length > 1) {
312
+ part += ':nth-of-type(' + (sibs.indexOf(cur) + 1) + ')';
313
+ }
314
+ }
315
+ parts.unshift(part);
316
+ cur = cur.parentElement;
317
+ }
318
+ return parts.join(' > ');
319
+ }
320
+
321
+ window.addEventListener('message', function(e) {
322
+ if (e.data && e.data.type === 'tw-bridge:toggle') {
323
+ inspectorActive = !!e.data.enabled;
324
+ document.body.style.cursor = inspectorActive ? 'crosshair' : '';
325
+ if (!inspectorActive) {
326
+ if (hoveredEl) { hoveredEl.style.outline = ''; hoveredEl.style.outlineOffset = ''; hoveredEl = null; }
327
+ if (selectedEl) { selectedEl.style.outline = ''; selectedEl.style.outlineOffset = ''; selectedEl = null; }
328
+ }
329
+ }
330
+ if (e.data && e.data.type === 'tw-bridge:apply') {
331
+ var el = document.querySelector(e.data.selector);
332
+ if (el) el.className = e.data.classes.join(' ');
333
+ }
334
+ });
335
+
336
+ document.addEventListener('mouseover', function(e) {
337
+ if (!inspectorActive) return;
338
+ var t = e.target;
339
+ if (hoveredEl && hoveredEl !== selectedEl) {
340
+ hoveredEl.style.outline = '';
341
+ hoveredEl.style.outlineOffset = '';
342
+ }
343
+ t.style.outline = '2px dashed rgba(139, 92, 246, 0.6)';
344
+ t.style.outlineOffset = '2px';
345
+ hoveredEl = t;
346
+ });
347
+
348
+ document.addEventListener('mouseout', function(e) {
349
+ if (!inspectorActive) return;
350
+ if (e.target !== selectedEl) {
351
+ e.target.style.outline = '';
352
+ e.target.style.outlineOffset = '';
353
+ }
354
+ });
355
+
356
+ document.addEventListener('click', function(e) {
357
+ if (!inspectorActive) return;
358
+ e.preventDefault();
359
+ e.stopPropagation();
360
+ var t = e.target;
361
+ if (selectedEl) { selectedEl.style.outline = ''; selectedEl.style.outlineOffset = ''; }
362
+ t.style.outline = '2px solid rgba(139, 92, 246, 0.9)';
363
+ t.style.outlineOffset = '2px';
364
+ selectedEl = t;
365
+ var classes = t.className ? t.className.split(/\\s+/).filter(Boolean) : [];
366
+ var rect = t.getBoundingClientRect();
367
+ window.parent.postMessage({
368
+ type: 'tw-bridge:select',
369
+ selector: generateSelector(t),
370
+ tagName: t.tagName,
371
+ classes: classes,
372
+ originalClasses: classes,
373
+ rect: { width: rect.width, height: rect.height }
374
+ }, '*');
375
+ }, true);
376
+ })();
377
+ </script>`;
378
+ var TW_CATS = [
379
+ { id: "layout", label: "Layout", classes: ["flex", "inline-flex", "grid", "block", "hidden", "flex-row", "flex-col", "items-center", "items-start", "items-end", "justify-center", "justify-between", "justify-start", "justify-end", "relative", "absolute", "fixed", "overflow-hidden", "overflow-auto"] },
380
+ { id: "spacing", label: "Spacing", classes: ["p-0", "p-1", "p-2", "p-3", "p-4", "p-6", "p-8", "px-2", "px-4", "px-6", "py-1", "py-2", "py-3", "py-4", "m-0", "m-1", "m-2", "m-4", "mx-auto", "my-2", "my-4", "gap-1", "gap-2", "gap-3", "gap-4", "gap-6"] },
381
+ { id: "sizing", label: "Sizing", classes: ["w-full", "w-auto", "w-1/2", "w-1/3", "h-full", "h-auto", "h-screen", "min-w-0", "max-w-sm", "max-w-md", "max-w-lg"] },
382
+ { id: "typography", label: "Type", classes: ["text-xs", "text-sm", "text-base", "text-lg", "text-xl", "text-2xl", "font-light", "font-normal", "font-medium", "font-semibold", "font-bold", "text-left", "text-center", "text-right", "truncate"] },
383
+ { id: "colors", label: "Colors", classes: ["text-white", "text-black", "text-gray-500", "bg-white", "bg-black", "bg-transparent", "bg-gray-100", "bg-gray-800", "bg-gray-900", "bg-blue-500", "bg-green-500", "bg-red-500", "bg-purple-500", "bg-cyan-500"] },
384
+ { id: "borders", label: "Borders", classes: ["border", "border-0", "border-2", "rounded-none", "rounded-sm", "rounded", "rounded-md", "rounded-lg", "rounded-xl", "rounded-2xl", "rounded-full"] },
385
+ { id: "effects", label: "Effects", classes: ["shadow-sm", "shadow", "shadow-md", "shadow-lg", "shadow-xl", "opacity-0", "opacity-50", "opacity-100", "transition-all", "transition-colors", "cursor-pointer"] }
386
+ ];
387
+ function InlineTwEditor({ iframeRef, isOpen, onClose }) {
388
+ const [selected, setSelected] = useState(null);
389
+ const [inspectorOn, setInspectorOn] = useState(true);
390
+ const [addInput, setAddInput] = useState("");
391
+ const [expandedCat, setExpandedCat] = useState("layout");
392
+ const [filter, setFilter] = useState("");
393
+ useEffect(() => {
394
+ if (!isOpen) return;
395
+ const handler = (e) => {
396
+ if (e.data?.type === "tw-bridge:select") {
397
+ setSelected({
398
+ selector: e.data.selector,
399
+ tagName: e.data.tagName,
400
+ classes: e.data.classes || [],
401
+ originalClasses: e.data.originalClasses || e.data.classes || [],
402
+ rect: e.data.rect || { width: 0, height: 0 }
403
+ });
404
+ }
405
+ };
406
+ window.addEventListener("message", handler);
407
+ return () => window.removeEventListener("message", handler);
408
+ }, [isOpen]);
409
+ useEffect(() => {
410
+ if (!isOpen) return;
411
+ iframeRef.current?.contentWindow?.postMessage({ type: "tw-bridge:toggle", enabled: inspectorOn }, "*");
412
+ }, [inspectorOn, isOpen, iframeRef]);
413
+ useEffect(() => {
414
+ if (isOpen) {
415
+ setInspectorOn(true);
416
+ iframeRef.current?.contentWindow?.postMessage({ type: "tw-bridge:toggle", enabled: true }, "*");
417
+ } else {
418
+ iframeRef.current?.contentWindow?.postMessage({ type: "tw-bridge:toggle", enabled: false }, "*");
419
+ }
420
+ }, [isOpen, iframeRef]);
421
+ const applyClasses = useCallback((newClasses) => {
422
+ if (!selected) return;
423
+ setSelected((prev) => prev ? { ...prev, classes: newClasses } : null);
424
+ iframeRef.current?.contentWindow?.postMessage({
425
+ type: "tw-bridge:apply",
426
+ selector: selected.selector,
427
+ classes: newClasses
428
+ }, "*");
429
+ }, [selected, iframeRef]);
430
+ const removeClass = useCallback((cls) => {
431
+ if (!selected) return;
432
+ applyClasses(selected.classes.filter((c) => c !== cls));
433
+ }, [selected, applyClasses]);
434
+ const addClass = useCallback((cls) => {
435
+ if (!selected) return;
436
+ const t = cls.trim();
437
+ if (!t || selected.classes.includes(t)) return;
438
+ applyClasses([...selected.classes, t]);
439
+ setAddInput("");
440
+ }, [selected, applyClasses]);
441
+ const resetClasses = useCallback(() => {
442
+ if (!selected) return;
443
+ applyClasses([...selected.originalClasses]);
444
+ }, [selected, applyClasses]);
445
+ const copyClasses = useCallback(() => {
446
+ if (!selected) return;
447
+ navigator.clipboard.writeText(selected.classes.join(" "));
448
+ }, [selected]);
449
+ const hasChanges = selected && JSON.stringify(selected.classes) !== JSON.stringify(selected.originalClasses);
450
+ if (!isOpen) return null;
451
+ return /* @__PURE__ */ jsxs4("div", { className: "w-[280px] shrink-0 flex flex-col bg-surface-primary border-l border-border-subtle h-full text-text-primary", children: [
452
+ /* @__PURE__ */ jsxs4("div", { className: "px-3 py-2 border-b border-border-subtle shrink-0", children: [
453
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between mb-1.5", children: [
454
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1.5", children: [
455
+ /* @__PURE__ */ jsx4(Paintbrush, { size: 12, className: "text-accent-purple" }),
456
+ /* @__PURE__ */ jsx4("span", { className: "text-[11px] font-bold", children: "TW Editor" }),
457
+ /* @__PURE__ */ jsx4("span", { className: "text-[7px] px-1 py-0.5 rounded-full bg-accent-purple/15 text-accent-purple font-mono font-bold", children: "LIVE" })
458
+ ] }),
459
+ /* @__PURE__ */ jsx4("button", { onClick: onClose, className: "text-text-muted hover:text-text-primary transition-colors", children: /* @__PURE__ */ jsx4(X3, { size: 12 }) })
460
+ ] }),
461
+ /* @__PURE__ */ jsxs4(
462
+ "button",
463
+ {
464
+ onClick: () => setInspectorOn(!inspectorOn),
465
+ className: `w-full flex items-center gap-1.5 px-2 py-1 rounded text-[10px] font-medium transition-all cursor-pointer ${inspectorOn ? "bg-accent-purple/15 text-accent-purple border border-accent-purple/30" : "bg-surface-glass text-text-muted border border-border-subtle"}`,
466
+ children: [
467
+ /* @__PURE__ */ jsx4(MousePointer, { size: 10 }),
468
+ inspectorOn ? "Click un elemento" : "Inspector off",
469
+ /* @__PURE__ */ jsx4("span", { className: `ml-auto w-1.5 h-1.5 rounded-full ${inspectorOn ? "bg-accent-green animate-pulse" : "bg-text-muted/30"}` })
470
+ ]
471
+ }
472
+ )
473
+ ] }),
474
+ selected ? /* @__PURE__ */ jsxs4("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
475
+ /* @__PURE__ */ jsx4("div", { className: "px-3 py-1.5 border-b border-border-subtle/50 shrink-0", children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1.5", children: [
476
+ /* @__PURE__ */ jsxs4("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded bg-surface-glass text-text-muted", children: [
477
+ "<",
478
+ selected.tagName.toLowerCase(),
479
+ ">"
480
+ ] }),
481
+ /* @__PURE__ */ jsxs4("span", { className: "text-[8px] font-mono text-text-muted/40 ml-auto", children: [
482
+ Math.round(selected.rect.width),
483
+ "\xD7",
484
+ Math.round(selected.rect.height)
485
+ ] })
486
+ ] }) }),
487
+ /* @__PURE__ */ jsxs4("div", { className: "px-3 py-1.5 border-b border-border-subtle/50 shrink-0", children: [
488
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between mb-1", children: [
489
+ /* @__PURE__ */ jsxs4("span", { className: "text-[8px] font-mono text-text-muted/60 uppercase tracking-wider", children: [
490
+ "Clases (",
491
+ selected.classes.length,
492
+ ")"
493
+ ] }),
494
+ /* @__PURE__ */ jsxs4("div", { className: "flex gap-1", children: [
495
+ hasChanges && /* @__PURE__ */ jsxs4("button", { onClick: resetClasses, className: "text-[7px] text-accent-amber flex items-center gap-0.5 cursor-pointer", children: [
496
+ /* @__PURE__ */ jsx4(RotateCcw, { size: 7 }),
497
+ " Undo"
498
+ ] }),
499
+ /* @__PURE__ */ jsxs4("button", { onClick: copyClasses, className: "text-[7px] text-text-muted/40 hover:text-accent-cyan flex items-center gap-0.5 cursor-pointer", children: [
500
+ /* @__PURE__ */ jsx4(Copy, { size: 7 }),
501
+ " Copy"
502
+ ] })
503
+ ] })
504
+ ] }),
505
+ /* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-0.5 max-h-[100px] overflow-y-auto custom-scrollbar", children: selected.classes.map((cls) => {
506
+ const isNew = !selected.originalClasses.includes(cls);
507
+ return /* @__PURE__ */ jsxs4("span", { className: `group inline-flex items-center gap-0.5 px-1 py-0.5 rounded text-[8px] font-mono transition-all ${isNew ? "bg-accent-green/15 text-accent-green border border-accent-green/30" : "bg-surface-glass text-text-secondary border border-border-subtle"}`, children: [
508
+ cls,
509
+ /* @__PURE__ */ jsx4("button", { onClick: () => removeClass(cls), className: "opacity-0 group-hover:opacity-100 text-red-400 cursor-pointer", children: /* @__PURE__ */ jsx4(X3, { size: 7 }) })
510
+ ] }, cls);
511
+ }) }),
512
+ hasChanges && /* @__PURE__ */ jsx4("div", { className: "mt-1 flex flex-wrap gap-0.5", children: selected.originalClasses.filter((c) => !selected.classes.includes(c)).map((cls) => /* @__PURE__ */ jsx4("button", { onClick: () => addClass(cls), className: "px-1 py-0.5 rounded text-[8px] font-mono bg-red-500/10 text-red-400/60 border border-red-500/20 line-through hover:no-underline cursor-pointer", children: cls }, `rm-${cls}`)) })
513
+ ] }),
514
+ /* @__PURE__ */ jsxs4("div", { className: "px-3 py-1.5 border-b border-border-subtle/50 shrink-0 flex gap-1", children: [
515
+ /* @__PURE__ */ jsxs4("div", { className: "flex-1 relative", children: [
516
+ /* @__PURE__ */ jsx4(Plus2, { size: 9, className: "absolute left-1.5 top-1/2 -translate-y-1/2 text-text-muted/40" }),
517
+ /* @__PURE__ */ jsx4(
518
+ "input",
519
+ {
520
+ type: "text",
521
+ value: addInput,
522
+ onChange: (e) => setAddInput(e.target.value),
523
+ onKeyDown: (e) => {
524
+ if (e.key === "Enter" && addInput.trim()) addClass(addInput);
525
+ },
526
+ placeholder: "Agregar clase...",
527
+ className: "w-full pl-5 pr-1 py-1 text-[10px] font-mono bg-surface-glass border border-border-subtle rounded text-text-primary placeholder:text-text-muted/30 focus:outline-none focus:border-accent-purple/40"
528
+ }
529
+ )
530
+ ] }),
531
+ /* @__PURE__ */ jsx4("button", { onClick: () => addClass(addInput), disabled: !addInput.trim(), className: "px-2 py-1 rounded bg-accent-purple/15 text-accent-purple text-[9px] font-bold disabled:opacity-30 cursor-pointer", children: "+" })
532
+ ] }),
533
+ /* @__PURE__ */ jsxs4("div", { className: "flex-1 overflow-y-auto custom-scrollbar", children: [
534
+ /* @__PURE__ */ jsx4("div", { className: "px-3 py-1 border-b border-border-subtle/30 sticky top-0 bg-surface-primary z-10", children: /* @__PURE__ */ jsxs4("div", { className: "relative", children: [
535
+ /* @__PURE__ */ jsx4(Search, { size: 9, className: "absolute left-1.5 top-1/2 -translate-y-1/2 text-text-muted/30" }),
536
+ /* @__PURE__ */ jsx4(
537
+ "input",
538
+ {
539
+ type: "text",
540
+ value: filter,
541
+ onChange: (e) => setFilter(e.target.value),
542
+ placeholder: "Filtrar...",
543
+ className: "w-full pl-5 pr-1 py-0.5 text-[9px] font-mono bg-surface-glass border border-border-subtle/50 rounded text-text-primary placeholder:text-text-muted/20 focus:outline-none"
544
+ }
545
+ )
546
+ ] }) }),
547
+ TW_CATS.map((cat) => {
548
+ const filtered = filter ? cat.classes.filter((c) => c.includes(filter.toLowerCase())) : cat.classes;
549
+ if (filtered.length === 0) return null;
550
+ const isOpen2 = expandedCat === cat.id;
551
+ return /* @__PURE__ */ jsxs4("div", { className: "border-b border-border-subtle/20", children: [
552
+ /* @__PURE__ */ jsxs4("button", { onClick: () => setExpandedCat(isOpen2 ? null : cat.id), className: "w-full flex items-center gap-1.5 px-3 py-1 hover:bg-surface-glass text-left cursor-pointer", children: [
553
+ isOpen2 ? /* @__PURE__ */ jsx4(ChevronDown, { size: 8 }) : /* @__PURE__ */ jsx4(ChevronRight, { size: 8 }),
554
+ /* @__PURE__ */ jsx4("span", { className: "text-[9px] font-semibold text-text-primary flex-1", children: cat.label }),
555
+ /* @__PURE__ */ jsx4("span", { className: "text-[7px] font-mono text-text-muted/40", children: filtered.length })
556
+ ] }),
557
+ isOpen2 && /* @__PURE__ */ jsx4("div", { className: "px-3 pb-1.5 flex flex-wrap gap-0.5", children: filtered.map((cls) => {
558
+ const active = selected.classes.includes(cls);
559
+ return /* @__PURE__ */ jsxs4(
560
+ "button",
561
+ {
562
+ onClick: () => active ? removeClass(cls) : addClass(cls),
563
+ className: `px-1 py-0.5 rounded text-[8px] font-mono cursor-pointer transition-all ${active ? "bg-accent-green/15 text-accent-green border border-accent-green/30" : "bg-surface-glass text-text-muted border border-border-subtle/50 hover:border-accent-purple/30"}`,
564
+ children: [
565
+ active ? "\u2713 " : "",
566
+ cls
567
+ ]
568
+ },
569
+ cls
570
+ );
571
+ }) })
572
+ ] }, cat.id);
573
+ })
574
+ ] }),
575
+ hasChanges && /* @__PURE__ */ jsxs4("div", { className: "px-3 py-1.5 border-t border-border-subtle bg-surface-secondary/50 shrink-0", children: [
576
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between mb-0.5", children: [
577
+ /* @__PURE__ */ jsx4("span", { className: "text-[7px] font-mono text-accent-green/60 uppercase", children: "className" }),
578
+ /* @__PURE__ */ jsxs4("button", { onClick: copyClasses, className: "text-[7px] text-text-muted/40 hover:text-accent-cyan flex items-center gap-0.5 cursor-pointer", children: [
579
+ /* @__PURE__ */ jsx4(Copy, { size: 7 }),
580
+ " Copy"
581
+ ] })
582
+ ] }),
583
+ /* @__PURE__ */ jsx4("div", { className: "text-[8px] font-mono text-text-secondary bg-surface-primary p-1.5 rounded border border-border-subtle max-h-[48px] overflow-y-auto break-all", children: selected.classes.join(" ") })
584
+ ] })
585
+ ] }) : /* @__PURE__ */ jsxs4("div", { className: "flex-1 flex flex-col items-center justify-center p-4 text-center", children: [
586
+ /* @__PURE__ */ jsx4(MousePointer, { size: 20, className: "text-accent-purple/30 mb-2" }),
587
+ /* @__PURE__ */ jsx4("p", { className: "text-[10px] text-text-muted", children: "Click un elemento del preview" }),
588
+ /* @__PURE__ */ jsx4("p", { className: "text-[8px] text-text-muted/40 mt-0.5", children: "para editar sus clases Tailwind" })
589
+ ] })
590
+ ] });
591
+ }
592
+ var IframeShell = ({ data, instanceId }) => {
593
+ const iframeRef = useRef(null);
594
+ const content = data?.content || data?.html || "";
595
+ const url = data?.url || "";
596
+ const [twEditorOpen, setTwEditorOpen] = useState(false);
597
+ const blobUrl = useMemo(() => {
598
+ if (url) return url;
599
+ if (!content) return "about:blank";
600
+ const twCdn = '<script src="https://cdn.tailwindcss.com"></script>';
601
+ let enrichedContent = content;
602
+ if (enrichedContent.includes("</body>")) {
603
+ enrichedContent = enrichedContent.replace("</body>", `${twCdn}${TW_BRIDGE_SCRIPT}</body>`);
604
+ } else if (enrichedContent.includes("</html>")) {
605
+ enrichedContent = enrichedContent.replace("</html>", `${twCdn}${TW_BRIDGE_SCRIPT}</html>`);
606
+ } else {
607
+ enrichedContent += `${twCdn}${TW_BRIDGE_SCRIPT}`;
608
+ }
609
+ const blob = new Blob([enrichedContent], { type: "text/html" });
610
+ return URL.createObjectURL(blob);
611
+ }, [content, url]);
612
+ useEffect(() => {
613
+ return () => {
614
+ if (!url && blobUrl !== "about:blank") {
615
+ URL.revokeObjectURL(blobUrl);
616
+ }
617
+ };
618
+ }, [blobUrl, url]);
619
+ return /* @__PURE__ */ jsxs4("div", { className: "w-full h-full bg-surface-secondary rounded-lg overflow-hidden flex", children: [
620
+ /* @__PURE__ */ jsxs4("div", { className: "flex-1 relative", children: [
621
+ /* @__PURE__ */ jsx4(
622
+ "iframe",
623
+ {
624
+ ref: iframeRef,
625
+ src: blobUrl,
626
+ title: `sandbox-${instanceId}`,
627
+ sandbox: "allow-scripts allow-same-origin allow-forms",
628
+ className: "w-full h-full border-none"
629
+ }
630
+ ),
631
+ /* @__PURE__ */ jsxs4(
632
+ "button",
633
+ {
634
+ onClick: () => setTwEditorOpen(!twEditorOpen),
635
+ className: `absolute top-2 right-2 z-10 flex items-center gap-1 px-2 py-1 rounded-lg text-[10px] font-semibold transition-all shadow-lg cursor-pointer ${twEditorOpen ? "bg-accent-purple text-white" : "bg-black/60 text-white/80 hover:bg-accent-purple/80 hover:text-white backdrop-blur-sm"}`,
636
+ title: "Tailwind Editor",
637
+ children: [
638
+ /* @__PURE__ */ jsx4(Paintbrush, { size: 11 }),
639
+ " TW"
640
+ ]
641
+ }
642
+ )
643
+ ] }),
644
+ /* @__PURE__ */ jsx4(
645
+ InlineTwEditor,
646
+ {
647
+ iframeRef,
648
+ isOpen: twEditorOpen,
649
+ onClose: () => setTwEditorOpen(false)
650
+ }
651
+ )
652
+ ] });
653
+ };
654
+
655
+ // src/components/shell/shells/TokenPreview.tsx
656
+ import { useState as useState2, useCallback as useCallback2 } from "react";
657
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
658
+ var TOKEN_GROUPS = [
659
+ {
660
+ title: "Surfaces",
661
+ icon: "\u25FB\uFE0F",
662
+ tokens: [
663
+ { key: "surface-primary", label: "Primary" },
664
+ { key: "surface-secondary", label: "Secondary" },
665
+ { key: "surface-tertiary", label: "Tertiary" },
666
+ { key: "surface-elevated", label: "Elevated" }
667
+ ]
668
+ },
669
+ {
670
+ title: "Text",
671
+ icon: "\u270F\uFE0F",
672
+ tokens: [
673
+ { key: "text-primary", label: "Primary" },
674
+ { key: "text-secondary", label: "Secondary" },
675
+ { key: "text-muted", label: "Muted" }
676
+ ]
677
+ },
678
+ {
679
+ title: "Accents",
680
+ icon: "\u{1F48E}",
681
+ tokens: [
682
+ { key: "accent-blue", label: "Blue" },
683
+ { key: "accent-purple", label: "Purple" },
684
+ { key: "accent-green", label: "Green" },
685
+ { key: "accent-red", label: "Red" },
686
+ { key: "accent-amber", label: "Amber" },
687
+ { key: "accent-cyan", label: "Cyan" }
688
+ ]
689
+ },
690
+ {
691
+ title: "Borders",
692
+ icon: "\u25AC",
693
+ tokens: [
694
+ { key: "border-subtle", label: "Subtle" },
695
+ { key: "border-default", label: "Default" },
696
+ { key: "border-strong", label: "Strong" },
697
+ { key: "border-glow", label: "Glow" }
698
+ ]
699
+ },
700
+ {
701
+ title: "Status",
702
+ icon: "\u25CF",
703
+ tokens: [
704
+ { key: "status-ok", label: "Success" },
705
+ { key: "status-warn", label: "Warning" },
706
+ { key: "status-error", label: "Error" },
707
+ { key: "status-info", label: "Info" }
708
+ ]
709
+ }
710
+ ];
711
+ function TokenSwatch({ tokenKey, label }) {
712
+ const { getCurrentPreset, setToken } = useTheme();
713
+ const preset = getCurrentPreset();
714
+ const value = preset.tokens[tokenKey];
715
+ return /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 py-1.5 px-2 rounded-lg bg-surface-glass hover:bg-surface-elevated/50 transition-colors group", children: [
716
+ /* @__PURE__ */ jsxs5("div", { className: "relative w-6 h-6 shrink-0", children: [
717
+ /* @__PURE__ */ jsx5("div", { className: "w-6 h-6 rounded-md border border-border-default", style: { background: value } }),
718
+ /* @__PURE__ */ jsx5(
719
+ "input",
720
+ {
721
+ type: "color",
722
+ value: value.startsWith("#") ? value : "#888888",
723
+ onChange: (e) => setToken(tokenKey, e.target.value),
724
+ className: "absolute inset-0 opacity-0 w-full h-full cursor-pointer"
725
+ }
726
+ )
727
+ ] }),
728
+ /* @__PURE__ */ jsxs5("div", { className: "flex-1 min-w-0", children: [
729
+ /* @__PURE__ */ jsx5("div", { className: "text-[11px] font-semibold text-text-primary truncate", children: label }),
730
+ /* @__PURE__ */ jsxs5("div", { className: "text-[9px] text-text-muted font-mono", children: [
731
+ "--ds-",
732
+ tokenKey
733
+ ] })
734
+ ] }),
735
+ /* @__PURE__ */ jsx5("span", { className: "text-[10px] text-text-muted font-mono opacity-0 group-hover:opacity-100 transition-opacity", children: value.slice(0, 9) })
736
+ ] });
737
+ }
738
+ function PresetCard({ preset, isActive, onSelect }) {
739
+ return /* @__PURE__ */ jsxs5(
740
+ "button",
741
+ {
742
+ onClick: onSelect,
743
+ className: `flex flex-col items-center gap-2 p-3 rounded-xl border transition-all duration-200 cursor-pointer ${isActive ? "border-accent-purple bg-accent-purple/10 shadow-glow-sm" : "border-border-subtle bg-surface-glass hover:border-border-default hover:bg-surface-elevated/30"}`,
744
+ children: [
745
+ /* @__PURE__ */ jsx5("div", { className: "flex gap-1", children: [preset.preview.bg, preset.preview.accent, preset.preview.text].map((c, i) => /* @__PURE__ */ jsx5("div", { className: "w-3 h-3 rounded-full border border-border-subtle", style: { background: c } }, i)) }),
746
+ /* @__PURE__ */ jsx5("span", { className: "text-[10px] font-medium text-text-secondary", children: preset.name })
747
+ ]
748
+ }
749
+ );
750
+ }
751
+ function ComponentShowroom() {
752
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-4 p-3", children: [
753
+ /* @__PURE__ */ jsx5(ShowSection, { title: "Buttons", children: /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap gap-2", children: [
754
+ /* @__PURE__ */ jsx5("button", { className: "px-4 py-2 bg-accent-purple text-text-inverse rounded-lg text-xs font-semibold hover:opacity-90 transition-opacity", children: "Primary" }),
755
+ /* @__PURE__ */ jsx5("button", { className: "px-4 py-2 bg-surface-glass border border-border-default text-text-primary rounded-lg text-xs font-semibold hover:bg-surface-elevated transition-colors", children: "Secondary" }),
756
+ /* @__PURE__ */ jsx5("button", { className: "px-4 py-2 bg-accent-red/10 text-accent-red border border-accent-red/20 rounded-lg text-xs font-semibold hover:bg-accent-red/20 transition-colors", children: "Destructive" }),
757
+ /* @__PURE__ */ jsx5("button", { className: "px-3 py-1.5 text-text-secondary text-xs hover:text-text-primary transition-colors", children: "Ghost" })
758
+ ] }) }),
759
+ /* @__PURE__ */ jsxs5(ShowSection, { title: "Cards", children: [
760
+ /* @__PURE__ */ jsxs5("div", { className: "surface-card p-4", children: [
761
+ /* @__PURE__ */ jsx5("div", { className: "text-sm font-semibold text-text-primary mb-1", children: "Glass Card" }),
762
+ /* @__PURE__ */ jsx5("div", { className: "text-xs text-text-secondary", children: "Surface secondary con border subtle y hover." })
763
+ ] }),
764
+ /* @__PURE__ */ jsxs5("div", { className: "glass-panel rounded-xl p-4 mt-2", children: [
765
+ /* @__PURE__ */ jsx5("div", { className: "text-sm font-semibold text-text-primary mb-1", children: "Glass Panel" }),
766
+ /* @__PURE__ */ jsx5("div", { className: "text-xs text-text-secondary", children: "Overlay + backdrop blur + border default." })
767
+ ] })
768
+ ] }),
769
+ /* @__PURE__ */ jsxs5(ShowSection, { title: "Inputs", children: [
770
+ /* @__PURE__ */ jsx5(
771
+ "input",
772
+ {
773
+ type: "text",
774
+ placeholder: "Escribe algo...",
775
+ className: "w-full px-3 py-2 bg-surface-primary border border-border-default rounded-lg text-sm text-text-primary placeholder:text-text-muted focus:border-accent-purple focus:ring-1 focus:ring-accent-purple/30 outline-hidden transition-all"
776
+ }
777
+ ),
778
+ /* @__PURE__ */ jsxs5("div", { className: "flex gap-2 mt-2", children: [
779
+ /* @__PURE__ */ jsx5(
780
+ "input",
781
+ {
782
+ type: "text",
783
+ value: "Readonly",
784
+ readOnly: true,
785
+ className: "flex-1 px-3 py-2 bg-surface-glass border border-border-subtle rounded-lg text-sm text-text-secondary"
786
+ }
787
+ ),
788
+ /* @__PURE__ */ jsxs5("select", { className: "px-3 py-2 bg-surface-primary border border-border-default rounded-lg text-sm text-text-primary outline-hidden", children: [
789
+ /* @__PURE__ */ jsx5("option", { children: "Opci\xF3n A" }),
790
+ /* @__PURE__ */ jsx5("option", { children: "Opci\xF3n B" })
791
+ ] })
792
+ ] })
793
+ ] }),
794
+ /* @__PURE__ */ jsx5(ShowSection, { title: "Badges & Status", children: /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap gap-2", children: [
795
+ /* @__PURE__ */ jsx5("span", { className: "px-2 py-0.5 bg-status-ok/10 text-status-ok border border-status-ok/20 rounded-full text-[10px] font-semibold", children: "Active" }),
796
+ /* @__PURE__ */ jsx5("span", { className: "px-2 py-0.5 bg-status-warn/10 text-status-warn border border-status-warn/20 rounded-full text-[10px] font-semibold", children: "Pending" }),
797
+ /* @__PURE__ */ jsx5("span", { className: "px-2 py-0.5 bg-status-error/10 text-status-error border border-status-error/20 rounded-full text-[10px] font-semibold", children: "Error" }),
798
+ /* @__PURE__ */ jsx5("span", { className: "px-2 py-0.5 bg-status-info/10 text-status-info border border-status-info/20 rounded-full text-[10px] font-semibold", children: "Info" }),
799
+ /* @__PURE__ */ jsx5("span", { className: "px-2 py-0.5 bg-accent-purple/10 text-accent-purple border border-accent-purple/20 rounded-full text-[10px] font-semibold", children: "Plugin" })
800
+ ] }) }),
801
+ /* @__PURE__ */ jsx5(ShowSection, { title: "Chat Bubbles", children: /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
802
+ /* @__PURE__ */ jsx5("div", { className: "self-end max-w-[80%] px-3 py-2 bg-accent-purple/10 border border-accent-purple/20 rounded-2xl rounded-br-sm", children: /* @__PURE__ */ jsx5("div", { className: "text-xs text-text-primary", children: "Hola, \xBFc\xF3mo puedo ayudarte hoy?" }) }),
803
+ /* @__PURE__ */ jsx5("div", { className: "self-start max-w-[80%] px-3 py-2 bg-surface-glass border border-border-subtle rounded-2xl rounded-bl-sm", children: /* @__PURE__ */ jsx5("div", { className: "text-xs text-text-primary", children: "Muestra el dashboard de ventas" }) }),
804
+ /* @__PURE__ */ jsx5("div", { className: "self-end max-w-[80%] px-3 py-2 bg-accent-blue/10 border border-accent-blue/20 rounded-2xl rounded-br-sm", children: /* @__PURE__ */ jsx5("span", { className: "inline-flex items-center gap-1 text-xs text-accent-blue font-medium", children: "\u25CF Generando dise\xF1o web..." }) })
805
+ ] }) }),
806
+ /* @__PURE__ */ jsxs5(ShowSection, { title: "Typography", children: [
807
+ /* @__PURE__ */ jsx5("h1", { className: "text-xl font-bold text-text-primary", children: "Heading Primary" }),
808
+ /* @__PURE__ */ jsx5("h2", { className: "text-base font-semibold text-text-secondary", children: "Heading Secondary" }),
809
+ /* @__PURE__ */ jsx5("p", { className: "text-sm text-text-secondary mt-1", children: "Body text con color secundario para contenido descriptivo." }),
810
+ /* @__PURE__ */ jsx5("p", { className: "text-xs text-text-muted mt-1", children: "Caption text en muted para metadata y timestamps." }),
811
+ /* @__PURE__ */ jsx5("p", { className: "text-sm text-gradient font-bold mt-2", children: "\u2726 Gradient text premium" })
812
+ ] }),
813
+ /* @__PURE__ */ jsx5(ShowSection, { title: "Accent Palette", children: /* @__PURE__ */ jsx5("div", { className: "grid grid-cols-6 gap-1", children: ["accent-blue", "accent-purple", "accent-green", "accent-red", "accent-amber", "accent-cyan"].map((c) => /* @__PURE__ */ jsxs5("div", { className: "flex flex-col items-center gap-1", children: [
814
+ /* @__PURE__ */ jsx5("div", { className: `w-8 h-8 rounded-lg bg-${c}` }),
815
+ /* @__PURE__ */ jsx5("span", { className: "text-[8px] text-text-muted", children: c.split("-")[1] })
816
+ ] }, c)) }) }),
817
+ /* @__PURE__ */ jsx5(ShowSection, { title: "Graph Nodes", children: /* @__PURE__ */ jsx5("div", { className: "flex gap-2", children: [
818
+ { name: "Dialogue", border: "border-accent-purple/30", header: "bg-accent-purple/10" },
819
+ { name: "Logic", border: "border-accent-amber/30", header: "bg-accent-amber/10" },
820
+ { name: "State", border: "border-accent-cyan/30", header: "bg-accent-cyan/10" }
821
+ ].map((n) => /* @__PURE__ */ jsxs5("div", { className: `flex-1 bg-surface-tertiary ${n.border} border rounded-lg overflow-hidden`, children: [
822
+ /* @__PURE__ */ jsx5("div", { className: `${n.header} px-2 py-1 text-[10px] font-semibold text-text-primary`, children: n.name }),
823
+ /* @__PURE__ */ jsx5("div", { className: "px-2 py-2 text-[9px] text-text-muted", children: "Node content" })
824
+ ] }, n.name)) }) })
825
+ ] });
826
+ }
827
+ function ShowSection({ title, children }) {
828
+ return /* @__PURE__ */ jsxs5("div", { children: [
829
+ /* @__PURE__ */ jsx5("div", { className: "text-[10px] font-bold uppercase tracking-wider text-text-muted mb-2", children: title }),
830
+ children
831
+ ] });
832
+ }
833
+ function TokenPreview() {
834
+ const [tab, setTab] = useState2("themes");
835
+ const { currentThemeId, setTheme, exportCSS, getAllPresets, getCurrentPreset } = useTheme();
836
+ const handleExport = useCallback2(() => {
837
+ const css = exportCSS();
838
+ navigator.clipboard.writeText(css);
839
+ }, [exportCSS]);
840
+ const tabs = [
841
+ { id: "themes", icon: "\u{1F3A8}", label: "Temas" },
842
+ { id: "tokens", icon: "\u{1F527}", label: "Tokens" },
843
+ { id: "showroom", icon: "\u{1F3DB}\uFE0F", label: "Showroom" }
844
+ ];
845
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col h-full bg-surface-primary text-text-primary font-sans overflow-hidden", children: [
846
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 px-3 py-2.5 border-b border-border-subtle bg-surface-glass", children: [
847
+ /* @__PURE__ */ jsx5("span", { className: "text-sm", children: "\u{1F3A8}" }),
848
+ /* @__PURE__ */ jsx5("span", { className: "text-xs font-bold flex-1", children: "Design System" }),
849
+ /* @__PURE__ */ jsx5("span", { className: "text-[9px] px-1.5 py-0.5 bg-accent-purple/10 text-accent-purple rounded font-mono", children: getCurrentPreset().name }),
850
+ /* @__PURE__ */ jsx5(
851
+ "button",
852
+ {
853
+ onClick: handleExport,
854
+ className: "px-2.5 py-1 border border-border-default rounded-md bg-accent-purple hover:bg-accent-purple/80 text-[10px] text-text-primary font-medium cursor-pointer transition-colors",
855
+ children: "Export CSS"
856
+ }
857
+ )
858
+ ] }),
859
+ /* @__PURE__ */ jsx5("div", { className: "flex border-b border-border-subtle", children: tabs.map((t) => /* @__PURE__ */ jsxs5(
860
+ "button",
861
+ {
862
+ onClick: () => setTab(t.id),
863
+ className: `flex-1 flex items-center justify-center gap-1.5 py-2 text-[10px] font-semibold transition-all cursor-pointer ${tab === t.id ? "text-accent-purple border-b-2 border-accent-purple bg-accent-purple/5" : "text-text-muted hover:text-text-secondary hover:bg-surface-glass"}`,
864
+ children: [
865
+ /* @__PURE__ */ jsx5("span", { children: t.icon }),
866
+ /* @__PURE__ */ jsx5("span", { children: t.label })
867
+ ]
868
+ },
869
+ t.id
870
+ )) }),
871
+ /* @__PURE__ */ jsxs5("div", { className: "flex-1 overflow-auto custom-scrollbar", children: [
872
+ tab === "themes" && /* @__PURE__ */ jsxs5("div", { className: "p-3 flex flex-col gap-3", children: [
873
+ /* @__PURE__ */ jsx5("div", { className: "text-[10px] font-bold uppercase tracking-wider text-text-muted", children: "Presets" }),
874
+ /* @__PURE__ */ jsx5("div", { className: "grid grid-cols-2 gap-2", children: getAllPresets().map((p) => /* @__PURE__ */ jsx5(
875
+ PresetCard,
876
+ {
877
+ preset: p,
878
+ isActive: currentThemeId === p.id,
879
+ onSelect: () => setTheme(p.id)
880
+ },
881
+ p.id
882
+ )) }),
883
+ /* @__PURE__ */ jsxs5("div", { className: "mt-2", children: [
884
+ /* @__PURE__ */ jsx5("div", { className: "text-[10px] font-bold uppercase tracking-wider text-text-muted mb-2", children: "Vista R\xE1pida" }),
885
+ /* @__PURE__ */ jsxs5("div", { className: "surface-card p-3 flex flex-col gap-2", children: [
886
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
887
+ /* @__PURE__ */ jsx5("div", { className: "w-8 h-8 rounded-full bg-accent-purple flex items-center justify-center text-text-inverse text-[10px] font-bold", children: "Z" }),
888
+ /* @__PURE__ */ jsxs5("div", { children: [
889
+ /* @__PURE__ */ jsx5("div", { className: "text-xs font-semibold text-text-primary", children: "Zephyr AI" }),
890
+ /* @__PURE__ */ jsx5("div", { className: "text-[10px] text-text-muted", children: "Agente activo \u2022 ahora" })
891
+ ] })
892
+ ] }),
893
+ /* @__PURE__ */ jsx5("div", { className: "px-3 py-2 bg-surface-glass rounded-xl text-xs text-text-secondary", children: "\xBFEn qu\xE9 puedo ayudarte hoy?" }),
894
+ /* @__PURE__ */ jsxs5("div", { className: "flex gap-1.5", children: [
895
+ /* @__PURE__ */ jsx5("span", { className: "px-2 py-0.5 bg-status-ok/10 text-status-ok rounded-full text-[9px] font-medium", children: "Online" }),
896
+ /* @__PURE__ */ jsx5("span", { className: "px-2 py-0.5 bg-accent-blue/10 text-accent-blue rounded-full text-[9px] font-medium", children: "GPT-4" })
897
+ ] })
898
+ ] })
899
+ ] })
900
+ ] }),
901
+ tab === "tokens" && /* @__PURE__ */ jsx5("div", { className: "p-3 flex flex-col gap-3", children: TOKEN_GROUPS.map((g) => /* @__PURE__ */ jsxs5("div", { children: [
902
+ /* @__PURE__ */ jsxs5("div", { className: "text-[10px] font-bold uppercase tracking-wider text-text-muted mb-1.5 flex items-center gap-1", children: [
903
+ /* @__PURE__ */ jsx5("span", { children: g.icon }),
904
+ " ",
905
+ g.title
906
+ ] }),
907
+ /* @__PURE__ */ jsx5("div", { className: "flex flex-col gap-1", children: g.tokens.map((t) => /* @__PURE__ */ jsx5(TokenSwatch, { tokenKey: t.key, label: t.label }, t.key)) })
908
+ ] }, g.title)) }),
909
+ tab === "showroom" && /* @__PURE__ */ jsx5(ComponentShowroom, {})
910
+ ] })
911
+ ] });
912
+ }
913
+
914
+ // src/components/shell/shells/DatawayChatShell.tsx
915
+ import { useEffect as useEffect2 } from "react";
916
+ import { Database } from "lucide-react";
917
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
918
+ var DatawayChatShell = ({ artifacts, activeArtifactIndex }) => {
919
+ const artifact = artifacts?.[activeArtifactIndex];
920
+ useEffect2(() => {
921
+ if (!artifact) return;
922
+ let script = "";
923
+ if (typeof artifact.content === "string") {
924
+ script = artifact.content;
925
+ } else if (artifact.data && typeof artifact.data.content === "string") {
926
+ script = artifact.data.content;
927
+ }
928
+ if (script) {
929
+ script = script.replace(/```[a-zA-Z-]*\n/gi, "").replace(/```/g, "").trim();
930
+ window.dispatchEvent(new CustomEvent("intent:dataway-generate-script", { detail: { script } }));
931
+ }
932
+ }, [artifact]);
933
+ if (!artifact) return null;
934
+ let displayScript = typeof artifact.content === "string" ? artifact.content : artifact.data?.content || "";
935
+ displayScript = displayScript.replace(/```[a-zA-Z-]*\n/gi, "").replace(/```/g, "").trim();
936
+ return /* @__PURE__ */ jsxs6("div", { className: "w-full flex tracking-tight flex-col border border-teal-500/20 bg-teal-500/5 rounded-xl overflow-hidden mt-2", children: [
937
+ /* @__PURE__ */ jsxs6("div", { className: "bg-teal-500/10 px-3 py-2 flex items-center gap-2 border-b border-teal-500/10", children: [
938
+ /* @__PURE__ */ jsx6(Database, { className: "w-4 h-4 text-teal-600 dark:text-teal-400" }),
939
+ /* @__PURE__ */ jsx6("span", { className: "text-[11px] font-bold text-teal-800 dark:text-teal-300 uppercase tracking-widest", children: "M-Script Aplicado" })
940
+ ] }),
941
+ /* @__PURE__ */ jsx6("div", { className: "p-3", children: /* @__PURE__ */ jsx6("pre", { className: "text-[13px] font-mono text-teal-900/80 dark:text-teal-100/80 whitespace-pre-wrap break-all", children: displayScript }) })
942
+ ] });
943
+ };
944
+
945
+ // src/components/shell/ShellNexusPreview.tsx
946
+ import { useState as useState5, useRef as useRef3, useMemo as useMemo2, useCallback as useCallback5, useEffect as useEffect4 } from "react";
947
+ import {
948
+ Monitor as Monitor2,
949
+ Tablet,
950
+ Smartphone,
951
+ RotateCcw as RotateCcw2,
952
+ ExternalLink,
953
+ Maximize2,
954
+ Minimize2,
955
+ MousePointer2,
956
+ Rocket,
957
+ Copy as Copy2,
958
+ Check,
959
+ Loader2 as Loader22,
960
+ Code2 as Code23,
961
+ Undo2,
962
+ Redo2
963
+ } from "lucide-react";
964
+
965
+ // src/hooks/useNexusaiDeploy.ts
966
+ import { useState as useState3, useCallback as useCallback3 } from "react";
967
+ function buildStandaloneHTML(html, css, js) {
968
+ return `<!DOCTYPE html>
969
+ <html lang="es">
970
+ <head>
971
+ <meta charset="UTF-8">
972
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
973
+ <title>Decido Preview</title>
974
+ <script src="https://cdn.tailwindcss.com"></script>
975
+ <style>
976
+ *, *::before, *::after { box-sizing: border-box; }
977
+ body {
978
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
979
+ margin: 0;
980
+ background: #fff;
981
+ min-height: 100vh;
982
+ }
983
+ ${css}
984
+ </style>
985
+ </head>
986
+ <body>
987
+ ${html}
988
+ <script>try { ${js} } catch(e) { console.error(e); }</script>
989
+ </body>
990
+ </html>`;
991
+ }
992
+ function useNexusaiDeploy() {
993
+ const [state, setState] = useState3({
994
+ status: "idle",
995
+ url: null,
996
+ error: null,
997
+ progress: 0
998
+ });
999
+ const deploy = useCallback3(async () => {
1000
+ const activeInst = useMorphInstanceStore.getState().getActiveInstance();
1001
+ if (!activeInst?.data?.html) {
1002
+ setState({ status: "error", url: null, error: "No hay contenido para desplegar", progress: 0 });
1003
+ return;
1004
+ }
1005
+ const { html, css, javascript } = activeInst.data;
1006
+ setState({ status: "deploying", url: null, error: null, progress: 10 });
1007
+ const nexusKey = localStorage.getItem("nexusai_api_key");
1008
+ if (nexusKey) {
1009
+ try {
1010
+ setState((s) => ({ ...s, progress: 30 }));
1011
+ const sdkModule = "@nexusai/sdk";
1012
+ const { NexusAI } = await import(
1013
+ /* @vite-ignore */
1014
+ sdkModule
1015
+ );
1016
+ const client = new NexusAI({ apiKey: nexusKey });
1017
+ const standaloneCode = buildStandaloneHTML(html, css || "", javascript || "");
1018
+ const { jobId } = await client.pipeline.deploy({
1019
+ componentName: `preview-${Date.now()}`,
1020
+ componentCode: standaloneCode
1021
+ });
1022
+ setState((s) => ({ ...s, progress: 60 }));
1023
+ const result = await client.pipeline.waitForDeploy(jobId, {
1024
+ timeoutMs: 6e4,
1025
+ onProgress: (s) => setState((prev) => ({ ...prev, progress: 60 + Math.round((s.progress || 0) * 0.4) }))
1026
+ });
1027
+ const deployUrl = result.result?.deploymentUrl || result.result?.url || "";
1028
+ setState({ status: "success", url: deployUrl, error: null, progress: 100 });
1029
+ } catch (err) {
1030
+ const standaloneCode = buildStandaloneHTML(html, css || "", javascript || "");
1031
+ const blob = new Blob([standaloneCode], { type: "text/html" });
1032
+ const blobUrl = URL.createObjectURL(blob);
1033
+ setState({ status: "success", url: blobUrl, error: `SDK fall\xF3 (${err.message}). Preview local generado.`, progress: 100 });
1034
+ }
1035
+ } else {
1036
+ await new Promise((r) => setTimeout(r, 500));
1037
+ setState((s) => ({ ...s, progress: 50 }));
1038
+ const standaloneCode = buildStandaloneHTML(html, css || "", javascript || "");
1039
+ const blob = new Blob([standaloneCode], { type: "text/html" });
1040
+ const blobUrl = URL.createObjectURL(blob);
1041
+ setState({ status: "success", url: blobUrl, error: null, progress: 100 });
1042
+ }
1043
+ }, []);
1044
+ const reset = useCallback3(() => {
1045
+ setState({ status: "idle", url: null, error: null, progress: 0 });
1046
+ }, []);
1047
+ const copyUrl = useCallback3(() => {
1048
+ if (state.url) navigator.clipboard.writeText(state.url);
1049
+ }, [state.url]);
1050
+ return { ...state, deploy, reset, copyUrl };
1051
+ }
1052
+
1053
+ // src/components/shell/CodeEditorPanel.tsx
1054
+ import React6, { useState as useState4, useCallback as useCallback4, useEffect as useEffect3, useRef as useRef2 } from "react";
1055
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1056
+ var TAB_META = {
1057
+ html: { label: "HTML", color: "#e44d26" },
1058
+ css: { label: "CSS", color: "#2965f1" },
1059
+ js: { label: "JS", color: "#f7df1e" }
1060
+ };
1061
+ var CodeEditorPanel = React6.memo(function CodeEditorPanel2({
1062
+ code = { html: "", css: "", js: "" },
1063
+ onCodeChange
1064
+ }) {
1065
+ const [activeTab, setActiveTab] = useState4("html");
1066
+ const textareaRef = useRef2(null);
1067
+ const [localCode, setLocalCode] = useState4(code);
1068
+ useEffect3(() => {
1069
+ setLocalCode(code);
1070
+ }, [code]);
1071
+ const handleChange = useCallback4((value) => {
1072
+ const updated = { ...localCode, [activeTab]: value };
1073
+ setLocalCode(updated);
1074
+ onCodeChange(updated);
1075
+ }, [activeTab, localCode, onCodeChange]);
1076
+ const currentValue = activeTab === "html" ? localCode.html : activeTab === "css" ? localCode.css : localCode.js;
1077
+ const lineCount = (currentValue || "").split("\n").length;
1078
+ return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col h-full bg-surface-tertiary border-l border-border-subtle overflow-hidden", style: { minWidth: 280 }, children: [
1079
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-0.5 px-2 py-1.5 bg-surface-secondary border-b border-border-subtle shrink-0", children: [
1080
+ ["html", "css", "js"].map((tab) => /* @__PURE__ */ jsxs7(
1081
+ "button",
1082
+ {
1083
+ onClick: () => setActiveTab(tab),
1084
+ className: `px-3 py-1 rounded text-[10px] font-mono uppercase tracking-wider transition-colors ${activeTab === tab ? "bg-surface-glass text-text-primary" : "text-text-muted hover:text-text-primary hover:bg-surface-glass"}`,
1085
+ children: [
1086
+ /* @__PURE__ */ jsx7("span", { className: "inline-block w-2 h-2 rounded-full mr-1.5", style: { background: TAB_META[tab].color } }),
1087
+ TAB_META[tab].label
1088
+ ]
1089
+ },
1090
+ tab
1091
+ )),
1092
+ /* @__PURE__ */ jsxs7("span", { className: "ml-auto text-[9px] text-text-muted font-mono", children: [
1093
+ lineCount,
1094
+ " l\xEDneas"
1095
+ ] })
1096
+ ] }),
1097
+ /* @__PURE__ */ jsx7("div", { className: "flex-1 overflow-auto relative", style: { fontFamily: 'ui-monospace, "SF Mono", "Cascadia Code", Menlo, monospace' }, children: /* @__PURE__ */ jsxs7("div", { className: "flex min-h-full", children: [
1098
+ /* @__PURE__ */ jsx7("div", { className: "select-none text-right pr-3 pl-3 py-3 text-[11px] text-text-muted leading-normal shrink-0 bg-surface-tertiary border-r border-border-subtle", children: Array.from({ length: lineCount }, (_, i) => /* @__PURE__ */ jsx7("div", { children: i + 1 }, i)) }),
1099
+ /* @__PURE__ */ jsx7(
1100
+ "textarea",
1101
+ {
1102
+ ref: textareaRef,
1103
+ value: currentValue,
1104
+ onChange: (e) => handleChange(e.target.value),
1105
+ spellCheck: false,
1106
+ className: "flex-1 resize-none bg-transparent text-text-primary text-[11px] leading-normal p-3 outline-hidden min-h-full",
1107
+ style: { tabSize: 2 }
1108
+ }
1109
+ )
1110
+ ] }) })
1111
+ ] });
1112
+ });
1113
+
1114
+ // src/components/shell/TemplateLibrary.tsx
1115
+ import React7 from "react";
1116
+ import { motion as motion3 } from "motion/react";
1117
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1118
+ var TEMPLATES = [
1119
+ {
1120
+ id: "landing-saas",
1121
+ title: "Landing SaaS",
1122
+ category: "Marketing",
1123
+ emoji: "\u{1F680}",
1124
+ gradient: "from-violet-600 to-indigo-600",
1125
+ description: "Hero con CTA, features, testimonios",
1126
+ html: `<section class="min-h-screen bg-linear-to-br from-violet-950 via-indigo-950 to-black text-text-primary">
1127
+ <nav class="flex items-center justify-between px-8 py-6">
1128
+ <span class="text-xl font-bold bg-linear-to-r from-violet-400 to-indigo-400 bg-clip-text text-transparent">NexusAI</span>
1129
+ <div class="flex gap-6 text-sm text-text-secondary"><a href="#" class="hover:text-text-primary">Features</a><a href="#" class="hover:text-text-primary">Pricing</a><a href="#" class="hover:text-text-primary">Docs</a></div>
1130
+ <button class="px-5 py-2 bg-violet-600 hover:bg-violet-500 rounded-full text-sm font-medium transition">Get Started</button>
1131
+ </nav>
1132
+ <div class="flex flex-col items-center text-center pt-24 px-6">
1133
+ <span class="px-4 py-1.5 bg-violet-500/10 border border-violet-500/20 rounded-full text-xs text-violet-300 mb-6">\u2728 Now in Beta</span>
1134
+ <h1 class="text-5xl md:text-7xl font-bold leading-tight max-w-4xl">Build faster with <span class="bg-linear-to-r from-violet-400 to-cyan-400 bg-clip-text text-transparent">AI-powered</span> tools</h1>
1135
+ <p class="text-lg text-text-secondary mt-6 max-w-2xl">Ship products 10x faster. Our AI understands your codebase and generates production-ready code in seconds.</p>
1136
+ <div class="flex gap-4 mt-10"><button class="px-8 py-3 bg-violet-600 hover:bg-violet-500 rounded-full font-medium transition">Start Free Trial</button><button class="px-8 py-3 bg-surface-glass hover:bg-surface-glass border border-border-default rounded-full font-medium transition">Watch Demo \u2192</button></div>
1137
+ </div>
1138
+ </section>`,
1139
+ css: "",
1140
+ js: ""
1141
+ },
1142
+ {
1143
+ id: "dashboard",
1144
+ title: "Dashboard",
1145
+ category: "App",
1146
+ emoji: "\u{1F4CA}",
1147
+ gradient: "from-cyan-600 to-blue-600",
1148
+ description: "M\xE9tricas, gr\xE1ficos, sidebar",
1149
+ html: `<div class="flex h-screen bg-surface-primary text-text-primary">
1150
+ <aside class="w-56 bg-surface-secondary border-r border-border-subtle p-4 flex flex-col gap-2">
1151
+ <span class="text-lg font-bold text-cyan-400 mb-4">\u{1F4CA} Dashboard</span>
1152
+ <a class="px-3 py-2 rounded-lg bg-cyan-500/10 text-cyan-300 text-sm">Overview</a>
1153
+ <a class="px-3 py-2 rounded-lg text-text-muted hover:text-text-primary text-sm">Analytics</a>
1154
+ <a class="px-3 py-2 rounded-lg text-text-muted hover:text-text-primary text-sm">Reports</a>
1155
+ <a class="px-3 py-2 rounded-lg text-text-muted hover:text-text-primary text-sm">Settings</a>
1156
+ </aside>
1157
+ <main class="flex-1 p-8 overflow-auto">
1158
+ <h2 class="text-2xl font-bold mb-6">Good morning \u{1F44B}</h2>
1159
+ <div class="grid grid-cols-3 gap-4 mb-8">
1160
+ <div class="bg-surface-secondary border border-border-subtle rounded-2xl p-5"><p class="text-xs text-text-muted mb-1">Revenue</p><p class="text-3xl font-bold text-emerald-400">$48,352</p><p class="text-xs text-emerald-500 mt-1">\u2191 12.5%</p></div>
1161
+ <div class="bg-surface-secondary border border-border-subtle rounded-2xl p-5"><p class="text-xs text-text-muted mb-1">Users</p><p class="text-3xl font-bold text-cyan-400">2,847</p><p class="text-xs text-cyan-500 mt-1">\u2191 8.3%</p></div>
1162
+ <div class="bg-surface-secondary border border-border-subtle rounded-2xl p-5"><p class="text-xs text-text-muted mb-1">Conversion</p><p class="text-3xl font-bold text-violet-400">3.24%</p><p class="text-xs text-violet-500 mt-1">\u2191 2.1%</p></div>
1163
+ </div>
1164
+ <div class="bg-surface-secondary border border-border-subtle rounded-2xl p-6"><h3 class="text-sm font-medium text-text-secondary mb-4">Recent Activity</h3><div class="space-y-3"><div class="flex items-center gap-3 text-sm"><span class="w-8 h-8 rounded-full bg-emerald-500/10 flex items-center justify-center text-emerald-400">$</span><span class="text-text-primary">New subscription \u2014 Pro Plan</span><span class="ml-auto text-text-muted">2m ago</span></div><div class="flex items-center gap-3 text-sm"><span class="w-8 h-8 rounded-full bg-cyan-500/10 flex items-center justify-center text-cyan-400">\u{1F464}</span><span class="text-text-primary">New user registered</span><span class="ml-auto text-text-muted">15m ago</span></div></div></div>
1165
+ </main>
1166
+ </div>`,
1167
+ css: "",
1168
+ js: ""
1169
+ },
1170
+ {
1171
+ id: "portfolio",
1172
+ title: "Portfolio",
1173
+ category: "Personal",
1174
+ emoji: "\u{1F3A8}",
1175
+ gradient: "from-pink-600 to-rose-600",
1176
+ description: "Bio, proyectos, contacto",
1177
+ html: `<div class="min-h-screen bg-surface-primary text-text-primary px-6 py-16 flex flex-col items-center">
1178
+ <div class="max-w-2xl w-full">
1179
+ <div class="flex items-center gap-4 mb-8"><div class="w-16 h-16 rounded-full bg-linear-to-br from-pink-500 to-rose-500 flex items-center justify-center text-2xl">JR</div><div><h1 class="text-2xl font-bold">Maria Garcia</h1><p class="text-text-muted">Full-Stack Designer & Developer</p></div></div>
1180
+ <p class="text-text-secondary leading-relaxed mb-12">I craft beautiful digital experiences that bridge the gap between design and engineering. 5+ years building products for startups and enterprises.</p>
1181
+ <h2 class="text-sm font-bold text-text-muted uppercase tracking-wider mb-4">Selected Work</h2>
1182
+ <div class="grid grid-cols-2 gap-4 mb-12">
1183
+ <div class="group bg-surface-tertiary rounded-2xl overflow-hidden border border-border-subtle hover:border-pink-500/30 transition"><div class="h-32 bg-linear-to-br from-pink-500/20 to-purple-500/20"></div><div class="p-4"><h3 class="font-medium">E-Commerce Platform</h3><p class="text-xs text-text-muted mt-1">React, Node.js, Stripe</p></div></div>
1184
+ <div class="group bg-surface-tertiary rounded-2xl overflow-hidden border border-border-subtle hover:border-cyan-500/30 transition"><div class="h-32 bg-linear-to-br from-cyan-500/20 to-blue-500/20"></div><div class="p-4"><h3 class="font-medium">AI Dashboard</h3><p class="text-xs text-text-muted mt-1">Vue, Python, TensorFlow</p></div></div>
1185
+ </div>
1186
+ <div class="flex gap-4 text-sm text-text-muted"><a href="#" class="hover:text-text-primary">GitHub</a><a href="#" class="hover:text-text-primary">LinkedIn</a><a href="#" class="hover:text-text-primary">Twitter</a><a href="#" class="hover:text-text-primary">Email</a></div>
1187
+ </div>
1188
+ </div>`,
1189
+ css: "",
1190
+ js: ""
1191
+ },
1192
+ {
1193
+ id: "pricing",
1194
+ title: "Pricing",
1195
+ category: "Marketing",
1196
+ emoji: "\u{1F4B3}",
1197
+ gradient: "from-emerald-600 to-teal-600",
1198
+ description: "3 planes con toggle mensual/anual",
1199
+ html: `<section class="min-h-screen bg-surface-primary text-text-primary py-20 px-6">
1200
+ <div class="max-w-5xl mx-auto text-center">
1201
+ <h2 class="text-4xl font-bold mb-4">Simple, transparent pricing</h2>
1202
+ <p class="text-text-muted mb-12">No hidden fees. Cancel anytime.</p>
1203
+ <div class="grid md:grid-cols-3 gap-6">
1204
+ <div class="bg-surface-tertiary border border-border-subtle rounded-2xl p-8 text-left"><p class="text-sm text-text-muted mb-2">Starter</p><p class="text-4xl font-bold mb-1">$9<span class="text-lg text-text-muted">/mo</span></p><p class="text-xs text-text-muted mb-6">Perfect for getting started</p><ul class="space-y-2 text-sm text-text-secondary mb-8"><li>\u2713 5 projects</li><li>\u2713 Basic analytics</li><li>\u2713 Email support</li></ul><button class="w-full py-2.5 bg-surface-glass hover:bg-surface-glass border border-border-default rounded-xl text-sm transition">Get Started</button></div>
1205
+ <div class="bg-surface-tertiary border-2 border-emerald-500/50 rounded-2xl p-8 text-left relative"><span class="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-0.5 bg-emerald-500 rounded-full text-xs font-medium">Popular</span><p class="text-sm text-text-muted mb-2">Pro</p><p class="text-4xl font-bold mb-1">$29<span class="text-lg text-text-muted">/mo</span></p><p class="text-xs text-text-muted mb-6">For growing teams</p><ul class="space-y-2 text-sm text-text-secondary mb-8"><li>\u2713 Unlimited projects</li><li>\u2713 Advanced analytics</li><li>\u2713 Priority support</li><li>\u2713 Custom domains</li></ul><button class="w-full py-2.5 bg-emerald-600 hover:bg-emerald-500 rounded-xl text-sm font-medium transition">Get Started</button></div>
1206
+ <div class="bg-surface-tertiary border border-border-subtle rounded-2xl p-8 text-left"><p class="text-sm text-text-muted mb-2">Enterprise</p><p class="text-4xl font-bold mb-1">$99<span class="text-lg text-text-muted">/mo</span></p><p class="text-xs text-text-muted mb-6">For large organizations</p><ul class="space-y-2 text-sm text-text-secondary mb-8"><li>\u2713 Everything in Pro</li><li>\u2713 SSO & SAML</li><li>\u2713 Dedicated support</li><li>\u2713 SLA guarantee</li></ul><button class="w-full py-2.5 bg-surface-glass hover:bg-surface-glass border border-border-default rounded-xl text-sm transition">Contact Sales</button></div>
1207
+ </div>
1208
+ </div>
1209
+ </section>`,
1210
+ css: "",
1211
+ js: ""
1212
+ },
1213
+ {
1214
+ id: "login",
1215
+ title: "Login",
1216
+ category: "Auth",
1217
+ emoji: "\u{1F510}",
1218
+ gradient: "from-amber-600 to-orange-600",
1219
+ description: "Form con social login y glassmorphism",
1220
+ html: `<div class="min-h-screen bg-surface-primary flex items-center justify-center p-6">
1221
+ <div class="w-full max-w-sm bg-surface-glass backdrop-blur-xl border border-border-default rounded-3xl p-8 text-text-primary">
1222
+ <div class="text-center mb-8"><span class="text-4xl">\u{1F510}</span><h2 class="text-2xl font-bold mt-3">Welcome back</h2><p class="text-sm text-text-muted mt-1">Sign in to your account</p></div>
1223
+ <div class="space-y-4">
1224
+ <div><label class="text-xs text-text-muted mb-1 block">Email</label><input type="email" placeholder="you@email.com" class="w-full px-4 py-3 bg-surface-glass border border-border-default rounded-xl text-sm outline-hidden focus:border-amber-500/50 transition"/></div>
1225
+ <div><label class="text-xs text-text-muted mb-1 block">Password</label><input type="password" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" class="w-full px-4 py-3 bg-surface-glass border border-border-default rounded-xl text-sm outline-hidden focus:border-amber-500/50 transition"/></div>
1226
+ <button class="w-full py-3 bg-linear-to-r from-amber-500 to-orange-500 hover:from-amber-400 hover:to-orange-400 rounded-xl text-sm font-medium transition">Sign In</button>
1227
+ <div class="flex items-center gap-3 my-2"><div class="flex-1 h-px bg-surface-glass"></div><span class="text-xs text-text-muted">or</span><div class="flex-1 h-px bg-surface-glass"></div></div>
1228
+ <button class="w-full py-3 bg-surface-glass hover:bg-surface-glass border border-border-default rounded-xl text-sm transition flex items-center justify-center gap-2">\u{1F310} Continue with Google</button>
1229
+ </div>
1230
+ <p class="text-xs text-text-muted text-center mt-6">Don't have an account? <a href="#" class="text-amber-400 hover:underline">Sign up</a></p>
1231
+ </div>
1232
+ </div>`,
1233
+ css: "",
1234
+ js: ""
1235
+ },
1236
+ {
1237
+ id: "blog",
1238
+ title: "Blog",
1239
+ category: "Content",
1240
+ emoji: "\u{1F4DD}",
1241
+ gradient: "from-sky-600 to-blue-600",
1242
+ description: "Lista de art\xEDculos con cards",
1243
+ html: `<div class="min-h-screen bg-surface-primary text-text-primary py-16 px-6">
1244
+ <div class="max-w-3xl mx-auto">
1245
+ <h1 class="text-4xl font-bold mb-2">Blog</h1>
1246
+ <p class="text-text-muted mb-12">Thoughts on design, technology, and building products.</p>
1247
+ <div class="space-y-6">
1248
+ <article class="group bg-surface-tertiary border border-border-subtle rounded-2xl p-6 hover:border-sky-500/30 transition cursor-pointer"><div class="flex items-center gap-2 text-xs text-text-muted mb-3"><span class="px-2 py-0.5 bg-sky-500/10 text-sky-400 rounded">Design</span><span>Mar 15, 2026</span><span>\xB7 5 min read</span></div><h2 class="text-xl font-bold group-hover:text-sky-400 transition">The Future of AI-Powered Design Tools</h2><p class="text-sm text-text-muted mt-2 leading-relaxed">How generative AI is transforming the way we build user interfaces and what it means for designers.</p></article>
1249
+ <article class="group bg-surface-tertiary border border-border-subtle rounded-2xl p-6 hover:border-violet-500/30 transition cursor-pointer"><div class="flex items-center gap-2 text-xs text-text-muted mb-3"><span class="px-2 py-0.5 bg-violet-500/10 text-violet-400 rounded">Engineering</span><span>Mar 10, 2026</span><span>\xB7 8 min read</span></div><h2 class="text-xl font-bold group-hover:text-violet-400 transition">Building a Real-Time Collaboration Engine</h2><p class="text-sm text-text-muted mt-2 leading-relaxed">A deep dive into the architecture behind our multi-user editing system using CRDTs and WebSockets.</p></article>
1250
+ <article class="group bg-surface-tertiary border border-border-subtle rounded-2xl p-6 hover:border-emerald-500/30 transition cursor-pointer"><div class="flex items-center gap-2 text-xs text-text-muted mb-3"><span class="px-2 py-0.5 bg-emerald-500/10 text-emerald-400 rounded">Product</span><span>Mar 5, 2026</span><span>\xB7 3 min read</span></div><h2 class="text-xl font-bold group-hover:text-emerald-400 transition">Shipping Faster with Component Libraries</h2><p class="text-sm text-text-muted mt-2 leading-relaxed">How we reduced our time-to-market by 60% by investing in a robust design system.</p></article>
1251
+ </div>
1252
+ </div>
1253
+ </div>`,
1254
+ css: "",
1255
+ js: ""
1256
+ }
1257
+ ];
1258
+ var TemplateLibrary = React7.memo(function TemplateLibrary2({ onSelect }) {
1259
+ return /* @__PURE__ */ jsxs8("div", { className: "flex flex-col items-center justify-center h-full p-6 overflow-auto", children: [
1260
+ /* @__PURE__ */ jsxs8("div", { className: "text-center mb-8", children: [
1261
+ /* @__PURE__ */ jsx8("span", { className: "text-4xl", children: "\u{1F3A8}" }),
1262
+ /* @__PURE__ */ jsx8("h2", { className: "text-xl font-bold text-text-primary mt-3", children: "Elige un template" }),
1263
+ /* @__PURE__ */ jsx8("p", { className: "text-sm text-text-muted mt-1", children: "Click para cargar \u2014 luego refina con IA" })
1264
+ ] }),
1265
+ /* @__PURE__ */ jsx8("div", { className: "grid grid-cols-2 lg:grid-cols-3 gap-3 max-w-3xl w-full", children: TEMPLATES.map((t, i) => /* @__PURE__ */ jsxs8(
1266
+ motion3.button,
1267
+ {
1268
+ initial: { opacity: 0, y: 12 },
1269
+ animate: { opacity: 1, y: 0 },
1270
+ transition: { delay: i * 0.05 },
1271
+ onClick: () => onSelect({ html: t.html, css: t.css, js: t.js }),
1272
+ className: "group bg-surface-tertiary border border-border-subtle hover:border-border-strong rounded-2xl p-4 text-left transition-all hover:scale-[1.02] hover:shadow-xl overflow-hidden",
1273
+ children: [
1274
+ /* @__PURE__ */ jsx8("div", { className: `h-20 rounded-xl bg-linear-to-br ${t.gradient} opacity-30 group-hover:opacity-50 transition mb-3 flex items-center justify-center`, children: /* @__PURE__ */ jsx8("span", { className: "text-3xl opacity-70", children: t.emoji }) }),
1275
+ /* @__PURE__ */ jsx8("h3", { className: "text-sm font-bold text-text-primary", children: t.title }),
1276
+ /* @__PURE__ */ jsx8("p", { className: "text-[10px] text-text-muted mt-0.5", children: t.category }),
1277
+ /* @__PURE__ */ jsx8("p", { className: "text-[10px] text-text-muted mt-1", children: t.description })
1278
+ ]
1279
+ },
1280
+ t.id
1281
+ )) })
1282
+ ] });
1283
+ });
1284
+
1285
+ // src/components/shell/ShellNexusPreview.tsx
1286
+ import { Fragment, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1287
+ var DEVICE_DIMS = {
1288
+ desktop: { w: "100%", h: "100%", label: "Desktop" },
1289
+ tablet: { w: "768px", h: "1024px", label: "Tablet" },
1290
+ mobile: { w: "375px", h: "667px", label: "Mobile" }
1291
+ };
1292
+ var DEVICE_ICONS = {
1293
+ desktop: Monitor2,
1294
+ tablet: Tablet,
1295
+ mobile: Smartphone
1296
+ };
1297
+ var ShellNexusPreview = ({ artifacts, activeArtifactIndex, data }) => {
1298
+ const iframeRef = useRef3(null);
1299
+ const [device, setDevice] = useState5("desktop");
1300
+ const [iframeKey, setIframeKey] = useState5(Date.now());
1301
+ const [isFullscreen, setIsFullscreen] = useState5(false);
1302
+ const [inspectMode, setInspectMode] = useState5(false);
1303
+ const [selectedSelector, setSelectedSelector] = useState5(null);
1304
+ const [copied, setCopied] = useState5(false);
1305
+ const [showCodeEditor, setShowCodeEditor] = useState5(false);
1306
+ const deployHook = useNexusaiDeploy();
1307
+ const code = useMemo2(() => {
1308
+ const src = data || artifacts?.[activeArtifactIndex ?? 0]?.data || {};
1309
+ const html = src.html || src.content || "";
1310
+ const css = src.css || src.styles || "";
1311
+ const js = src.javascript || src.js || "";
1312
+ return { html, css, js };
1313
+ }, [data, artifacts, activeArtifactIndex]);
1314
+ useEffect4(() => {
1315
+ const handler = (e) => {
1316
+ if (e.data?.type === "DECIDO_ELEMENT_SELECTED") {
1317
+ setSelectedSelector(e.data.selector || null);
1318
+ if (window.__DECIDO__) {
1319
+ window.__DECIDO__.selectedElementSelector = e.data.selector || null;
1320
+ }
1321
+ }
1322
+ };
1323
+ window.addEventListener("message", handler);
1324
+ return () => window.removeEventListener("message", handler);
1325
+ }, []);
1326
+ const [showShortcuts, setShowShortcuts] = useState5(false);
1327
+ useEffect4(() => {
1328
+ const handler = (e) => {
1329
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
1330
+ const meta = e.metaKey || e.ctrlKey;
1331
+ if (!meta) return;
1332
+ if (e.code === "KeyI" && !e.shiftKey) {
1333
+ e.preventDefault();
1334
+ setInspectMode((v) => !v);
1335
+ return;
1336
+ }
1337
+ if (e.code === "KeyE" && !e.shiftKey) {
1338
+ e.preventDefault();
1339
+ setShowCodeEditor((v) => !v);
1340
+ return;
1341
+ }
1342
+ if (e.code === "KeyD" && !e.shiftKey) {
1343
+ e.preventDefault();
1344
+ deployHook.deploy();
1345
+ return;
1346
+ }
1347
+ if (e.key === "/") {
1348
+ e.preventDefault();
1349
+ setShowShortcuts((v) => !v);
1350
+ return;
1351
+ }
1352
+ };
1353
+ window.addEventListener("keydown", handler);
1354
+ return () => window.removeEventListener("keydown", handler);
1355
+ }, [deployHook]);
1356
+ useEffect4(() => {
1357
+ if (iframeRef.current?.contentWindow) {
1358
+ iframeRef.current.contentWindow.postMessage({ type: "DECIDO_SET_INSPECT", enabled: inspectMode }, "*");
1359
+ }
1360
+ }, [inspectMode, iframeKey]);
1361
+ const srcDoc = useMemo2(() => {
1362
+ const safeJs = (code.js || "").replace(/<\/script/gi, "<\\/script");
1363
+ return `
1364
+ <!DOCTYPE html>
1365
+ <html lang="es">
1366
+ <head>
1367
+ <meta charset="UTF-8">
1368
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1369
+ <base target="_blank">
1370
+ <script src="https://cdn.tailwindcss.com"></script>
1371
+ <style>
1372
+ *, *::before, *::after { box-sizing: border-box; }
1373
+ body {
1374
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
1375
+ margin: 0;
1376
+ background: #fff;
1377
+ min-height: 100vh;
1378
+ color: #1a1a1a;
1379
+ }
1380
+ html, body { width: 100%; height: 100%; }
1381
+ .__decido-hover { outline: 2px dashed #06b6d4 !important; outline-offset: 2px; cursor: crosshair !important; }
1382
+ .__decido-selected { outline: 2px solid #06b6d4 !important; outline-offset: 2px; box-shadow: 0 0 0 4px rgba(6,182,212,0.15) !important; }
1383
+ ${code.css}
1384
+ </style>
1385
+ </head>
1386
+ <body>
1387
+ ${code.html}
1388
+ <script>
1389
+ try { ${safeJs} } catch(e) { console.error('[Preview]', e); }
1390
+ </script>
1391
+ <script>
1392
+ // Link interceptor
1393
+ document.addEventListener('click', function(e) {
1394
+ var a = e.target.closest('a');
1395
+ if (!a) return;
1396
+ var href = a.getAttribute('href') || '';
1397
+ if (href.startsWith('#')) return;
1398
+ if (href.startsWith('javascript:')) { e.preventDefault(); return; }
1399
+ e.preventDefault();
1400
+ if (href && href !== '#') window.open(href, '_blank', 'noopener,noreferrer');
1401
+ }, true);
1402
+
1403
+ // Element Inspector
1404
+ var _inspectEnabled = false;
1405
+ var _hoveredEl = null;
1406
+ var _selectedEl = null;
1407
+
1408
+ function _getSelector(el) {
1409
+ if (el.id) return '#' + el.id;
1410
+ var path = [];
1411
+ while (el && el !== document.body && el !== document.documentElement) {
1412
+ var tag = el.tagName.toLowerCase();
1413
+ if (el.className && typeof el.className === 'string') {
1414
+ var cls = el.className.split(/s+/).filter(function(c) { return !c.startsWith('__decido'); }).slice(0, 2).join('.');
1415
+ if (cls) tag += '.' + cls;
1416
+ }
1417
+ var parent = el.parentElement;
1418
+ if (parent) {
1419
+ var siblings = Array.from(parent.children).filter(function(c) { return c.tagName === el.tagName; });
1420
+ if (siblings.length > 1) tag += ':nth-child(' + (Array.from(parent.children).indexOf(el) + 1) + ')';
1421
+ }
1422
+ path.unshift(tag);
1423
+ el = parent;
1424
+ if (path.length >= 3) break;
1425
+ }
1426
+ return path.join(' > ');
1427
+ }
1428
+
1429
+ document.addEventListener('mouseover', function(e) {
1430
+ if (!_inspectEnabled) return;
1431
+ if (_hoveredEl) _hoveredEl.classList.remove('__decido-hover');
1432
+ _hoveredEl = e.target;
1433
+ if (_hoveredEl !== _selectedEl) _hoveredEl.classList.add('__decido-hover');
1434
+ });
1435
+ document.addEventListener('mouseout', function(e) {
1436
+ if (_hoveredEl) _hoveredEl.classList.remove('__decido-hover');
1437
+ });
1438
+ document.addEventListener('click', function(e) {
1439
+ if (!_inspectEnabled) return;
1440
+ e.preventDefault();
1441
+ e.stopPropagation();
1442
+ if (_selectedEl) _selectedEl.classList.remove('__decido-selected');
1443
+ _selectedEl = e.target;
1444
+ _selectedEl.classList.add('__decido-selected');
1445
+ _hoveredEl = null;
1446
+ var sel = _getSelector(_selectedEl);
1447
+ window.parent.postMessage({ type: 'DECIDO_ELEMENT_SELECTED', selector: sel, tagName: _selectedEl.tagName }, '*');
1448
+ }, true);
1449
+
1450
+ window.addEventListener('message', function(e) {
1451
+ if (e.data && e.data.type === 'DECIDO_SET_INSPECT') {
1452
+ _inspectEnabled = e.data.enabled;
1453
+ if (!_inspectEnabled) {
1454
+ if (_hoveredEl) _hoveredEl.classList.remove('__decido-hover');
1455
+ if (_selectedEl) _selectedEl.classList.remove('__decido-selected');
1456
+ _hoveredEl = null; _selectedEl = null;
1457
+ }
1458
+ document.body.style.cursor = _inspectEnabled ? 'crosshair' : '';
1459
+ }
1460
+ });
1461
+ </script>
1462
+ </body>
1463
+ </html>`;
1464
+ }, [code]);
1465
+ const handleReload = useCallback5(() => setIframeKey(Date.now()), []);
1466
+ const handleOpenInTab = useCallback5(() => {
1467
+ const win = window.open();
1468
+ if (win) {
1469
+ win.document.write(srcDoc);
1470
+ win.document.close();
1471
+ }
1472
+ }, [srcDoc]);
1473
+ const handleToggleInspect = useCallback5(() => {
1474
+ const next = !inspectMode;
1475
+ setInspectMode(next);
1476
+ if (!next) setSelectedSelector(null);
1477
+ }, [inspectMode]);
1478
+ const dims = DEVICE_DIMS[device];
1479
+ const hasContent = !!code.html?.trim();
1480
+ return /* @__PURE__ */ jsxs9("div", { className: `w-full h-full flex flex-col bg-surface-primary overflow-hidden ${isFullscreen ? "fixed inset-0 z-9999" : ""}`, children: [
1481
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between px-2 py-1.5 bg-surface-secondary border-b border-border-subtle text-[10px] text-text-muted select-none shrink-0", children: [
1482
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1.5", children: [
1483
+ /* @__PURE__ */ jsxs9("div", { className: "flex gap-1 px-1", children: [
1484
+ /* @__PURE__ */ jsx9("span", { className: "w-2.5 h-2.5 rounded-full bg-red-500/80" }),
1485
+ /* @__PURE__ */ jsx9("span", { className: "w-2.5 h-2.5 rounded-full bg-yellow-500/80" }),
1486
+ /* @__PURE__ */ jsx9("span", { className: "w-2.5 h-2.5 rounded-full bg-green-500/80" })
1487
+ ] }),
1488
+ /* @__PURE__ */ jsx9("button", { onClick: handleReload, className: "p-1 rounded hover:bg-surface-glass transition-colors", title: "Recargar", children: /* @__PURE__ */ jsx9(RotateCcw2, { size: 12 }) }),
1489
+ /* @__PURE__ */ jsx9(
1490
+ "button",
1491
+ {
1492
+ onClick: () => useMorphInstanceStore.getState().undo(),
1493
+ disabled: !useMorphInstanceStore.getState().canUndo(),
1494
+ className: `p-1 rounded transition-colors ${useMorphInstanceStore.getState().canUndo() ? "hover:bg-surface-glass text-text-secondary" : "text-text-muted cursor-not-allowed"}`,
1495
+ title: "Deshacer",
1496
+ children: /* @__PURE__ */ jsx9(Undo2, { size: 12 })
1497
+ }
1498
+ ),
1499
+ /* @__PURE__ */ jsx9(
1500
+ "button",
1501
+ {
1502
+ onClick: () => useMorphInstanceStore.getState().redo(),
1503
+ disabled: !useMorphInstanceStore.getState().canRedo(),
1504
+ className: `p-1 rounded transition-colors ${useMorphInstanceStore.getState().canRedo() ? "hover:bg-surface-glass text-text-secondary" : "text-text-muted cursor-not-allowed"}`,
1505
+ title: "Rehacer",
1506
+ children: /* @__PURE__ */ jsx9(Redo2, { size: 12 })
1507
+ }
1508
+ ),
1509
+ /* @__PURE__ */ jsxs9("span", { className: "px-2 py-0.5 bg-surface-overlay rounded text-[9px] font-mono", children: [
1510
+ dims.w === "100%" ? "Adaptable" : dims.w,
1511
+ " \xD7 ",
1512
+ dims.h === "100%" ? "100%" : dims.h
1513
+ ] })
1514
+ ] }),
1515
+ /* @__PURE__ */ jsx9("div", { className: "flex items-center gap-0.5", children: ["desktop", "tablet", "mobile"].map((d) => {
1516
+ const Icon = DEVICE_ICONS[d];
1517
+ return /* @__PURE__ */ jsx9(
1518
+ "button",
1519
+ {
1520
+ onClick: () => setDevice(d),
1521
+ className: `p-1.5 rounded transition-colors ${device === d ? "bg-surface-glass text-text-primary" : "hover:bg-surface-glass text-text-muted hover:text-text-primary"}`,
1522
+ title: DEVICE_DIMS[d].label,
1523
+ children: /* @__PURE__ */ jsx9(Icon, { size: 13 })
1524
+ },
1525
+ d
1526
+ );
1527
+ }) }),
1528
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1", children: [
1529
+ /* @__PURE__ */ jsx9(
1530
+ "button",
1531
+ {
1532
+ onClick: handleToggleInspect,
1533
+ className: `p-1 rounded transition-colors ${inspectMode ? "bg-cyan-500/20 text-cyan-400" : "hover:bg-surface-glass text-text-muted"}`,
1534
+ title: inspectMode ? "Desactivar inspector" : "Seleccionar elemento",
1535
+ children: /* @__PURE__ */ jsx9(MousePointer2, { size: 12 })
1536
+ }
1537
+ ),
1538
+ /* @__PURE__ */ jsx9(
1539
+ "button",
1540
+ {
1541
+ onClick: () => setShowCodeEditor(!showCodeEditor),
1542
+ className: `p-1 rounded transition-colors ${showCodeEditor ? "bg-violet-500/20 text-violet-400" : "hover:bg-surface-glass text-text-muted"}`,
1543
+ title: showCodeEditor ? "Ocultar c\xF3digo" : "Ver / editar c\xF3digo",
1544
+ children: /* @__PURE__ */ jsx9(Code23, { size: 12 })
1545
+ }
1546
+ ),
1547
+ /* @__PURE__ */ jsx9(
1548
+ "button",
1549
+ {
1550
+ onClick: () => {
1551
+ deployHook.deploy();
1552
+ },
1553
+ disabled: !hasContent || deployHook.status === "deploying",
1554
+ className: `p-1 rounded transition-colors ${deployHook.status === "deploying" ? "text-amber-400 animate-pulse" : hasContent ? "hover:bg-emerald-500/10 text-text-muted hover:text-emerald-400" : "text-text-muted cursor-not-allowed"}`,
1555
+ title: "Desplegar preview",
1556
+ children: deployHook.status === "deploying" ? /* @__PURE__ */ jsx9(Loader22, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx9(Rocket, { size: 12 })
1557
+ }
1558
+ ),
1559
+ /* @__PURE__ */ jsx9("button", { onClick: () => setIsFullscreen(!isFullscreen), className: "p-1 rounded hover:bg-surface-glass transition-colors", title: isFullscreen ? "Salir fullscreen" : "Fullscreen", children: isFullscreen ? /* @__PURE__ */ jsx9(Minimize2, { size: 12 }) : /* @__PURE__ */ jsx9(Maximize2, { size: 12 }) }),
1560
+ /* @__PURE__ */ jsx9("button", { onClick: handleOpenInTab, className: "p-1 rounded hover:bg-surface-glass transition-colors", title: "Abrir en nueva pesta\xF1a", children: /* @__PURE__ */ jsx9(ExternalLink, { size: 12 }) })
1561
+ ] })
1562
+ ] }),
1563
+ deployHook.status !== "idle" && /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 px-3 py-1.5 border-b border-border-subtle text-[10px] shrink-0", style: { background: deployHook.status === "success" ? "rgba(16,185,129,0.08)" : deployHook.status === "error" ? "rgba(239,68,68,0.08)" : "rgba(245,158,11,0.08)" }, children: [
1564
+ deployHook.status === "deploying" && /* @__PURE__ */ jsxs9(Fragment, { children: [
1565
+ /* @__PURE__ */ jsx9(Loader22, { size: 10, className: "text-amber-400 animate-spin" }),
1566
+ /* @__PURE__ */ jsxs9("span", { className: "text-amber-300 font-mono", children: [
1567
+ "Desplegando... ",
1568
+ deployHook.progress,
1569
+ "%"
1570
+ ] }),
1571
+ /* @__PURE__ */ jsx9("div", { className: "flex-1 h-1 bg-surface-glass rounded-full overflow-hidden", children: /* @__PURE__ */ jsx9("div", { className: "h-full bg-amber-400 transition-all duration-300 rounded-full", style: { width: `${deployHook.progress}%` } }) })
1572
+ ] }),
1573
+ deployHook.status === "success" && deployHook.url && /* @__PURE__ */ jsxs9(Fragment, { children: [
1574
+ /* @__PURE__ */ jsx9(Check, { size: 10, className: "text-emerald-400" }),
1575
+ /* @__PURE__ */ jsx9("a", { href: deployHook.url, target: "_blank", rel: "noopener noreferrer", className: "text-emerald-300 font-mono flex-1 truncate hover:underline", children: deployHook.url.length > 60 ? deployHook.url.slice(0, 60) + "..." : deployHook.url }),
1576
+ /* @__PURE__ */ jsx9("button", { onClick: () => {
1577
+ deployHook.copyUrl();
1578
+ setCopied(true);
1579
+ setTimeout(() => setCopied(false), 1500);
1580
+ }, className: "text-text-secondary hover:text-text-primary transition-colors", title: "Copiar URL", children: copied ? /* @__PURE__ */ jsx9(Check, { size: 10, className: "text-emerald-400" }) : /* @__PURE__ */ jsx9(Copy2, { size: 10 }) }),
1581
+ /* @__PURE__ */ jsx9("button", { onClick: deployHook.reset, className: "text-text-muted hover:text-text-primary", children: "\u2715" })
1582
+ ] }),
1583
+ deployHook.status === "error" && /* @__PURE__ */ jsxs9(Fragment, { children: [
1584
+ /* @__PURE__ */ jsx9("span", { className: "text-red-400", children: "\u26A0" }),
1585
+ /* @__PURE__ */ jsx9("span", { className: "text-red-300 font-mono flex-1", children: deployHook.error }),
1586
+ /* @__PURE__ */ jsx9("button", { onClick: deployHook.reset, className: "text-text-muted hover:text-text-primary", children: "\u2715" })
1587
+ ] })
1588
+ ] }),
1589
+ selectedSelector && /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-cyan-500/10 border-b border-cyan-500/20 text-[10px] shrink-0", children: [
1590
+ /* @__PURE__ */ jsx9(MousePointer2, { size: 10, className: "text-cyan-400" }),
1591
+ /* @__PURE__ */ jsx9("code", { className: "text-cyan-300 font-mono flex-1 truncate", children: selectedSelector }),
1592
+ /* @__PURE__ */ jsx9("span", { className: "text-text-muted", children: "Escribe en el chat para modificar este elemento" }),
1593
+ /* @__PURE__ */ jsx9("button", { onClick: () => {
1594
+ setSelectedSelector(null);
1595
+ setInspectMode(false);
1596
+ }, className: "text-text-muted hover:text-text-primary", children: "\u2715" })
1597
+ ] }),
1598
+ /* @__PURE__ */ jsxs9("div", { className: "flex-1 flex overflow-hidden", children: [
1599
+ /* @__PURE__ */ jsx9("div", { className: `flex items-center justify-center overflow-auto p-2 ${showCodeEditor ? "w-1/2" : "flex-1"}`, style: { background: "repeating-conic-gradient(#1a1a1b 0% 25%, #141415 0% 50%) 0 0 / 20px 20px" }, children: hasContent ? /* @__PURE__ */ jsx9(
1600
+ "div",
1601
+ {
1602
+ className: "bg-white shadow-2xl transition-all duration-300 overflow-hidden w-full h-full",
1603
+ style: {
1604
+ maxWidth: showCodeEditor ? "100%" : dims.w,
1605
+ maxHeight: showCodeEditor ? "100%" : dims.h,
1606
+ border: device !== "desktop" ? "6px solid #333" : "1px solid #2a2a2a",
1607
+ borderRadius: device !== "desktop" ? "1.5rem" : "4px"
1608
+ },
1609
+ children: /* @__PURE__ */ jsx9(
1610
+ "iframe",
1611
+ {
1612
+ ref: iframeRef,
1613
+ srcDoc,
1614
+ title: "NexusAI Preview",
1615
+ sandbox: "allow-scripts allow-same-origin allow-forms allow-modals",
1616
+ className: "w-full h-full border-0"
1617
+ },
1618
+ iframeKey
1619
+ )
1620
+ }
1621
+ ) : /* @__PURE__ */ jsx9(TemplateLibrary, { onSelect: (tpl) => {
1622
+ const inst = useMorphInstanceStore.getState().getActiveInstance();
1623
+ if (inst) {
1624
+ useMorphInstanceStore.getState().upsertInstance({
1625
+ ...inst,
1626
+ data: { ...inst.data, html: tpl.html, css: tpl.css, javascript: tpl.js }
1627
+ });
1628
+ } else {
1629
+ const id = "tpl-" + Date.now();
1630
+ useMorphInstanceStore.getState().upsertInstance({
1631
+ id,
1632
+ shellType: "nexusai-preview",
1633
+ sourceChatId: "_default",
1634
+ label: "Template",
1635
+ data: { html: tpl.html, css: tpl.css, javascript: tpl.js }
1636
+ });
1637
+ useMorphInstanceStore.getState().setActiveInstance(id);
1638
+ }
1639
+ } }) }),
1640
+ showCodeEditor && /* @__PURE__ */ jsx9("div", { className: "w-1/2 shrink-0", children: /* @__PURE__ */ jsx9(
1641
+ CodeEditorPanel,
1642
+ {
1643
+ code,
1644
+ onCodeChange: (updated) => {
1645
+ const inst = useMorphInstanceStore.getState().getActiveInstance();
1646
+ if (inst) {
1647
+ useMorphInstanceStore.getState().upsertInstance({
1648
+ ...inst,
1649
+ data: { ...inst.data, html: updated.html, css: updated.css, javascript: updated.js }
1650
+ });
1651
+ }
1652
+ }
1653
+ }
1654
+ ) })
1655
+ ] }),
1656
+ showShortcuts && /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-surface-overlay backdrop-blur-xs", onClick: () => setShowShortcuts(false), children: /* @__PURE__ */ jsxs9("div", { className: "bg-surface-tertiary border border-border-default rounded-2xl p-6 max-w-xs w-full shadow-2xl", onClick: (e) => e.stopPropagation(), children: [
1657
+ /* @__PURE__ */ jsx9("h3", { className: "text-sm font-bold text-text-primary mb-4 flex items-center gap-2", children: "\u2328\uFE0F Atajos de Teclado" }),
1658
+ /* @__PURE__ */ jsx9("div", { className: "space-y-2 text-[11px]", children: [
1659
+ ["\u2318 I", "Inspector de elementos"],
1660
+ ["\u2318 E", "Editor de c\xF3digo"],
1661
+ ["\u2318 D", "Desplegar preview"],
1662
+ ["\u2318 Z", "Deshacer (topolog\xEDa)"],
1663
+ ["\u2318 \u21E7 Z", "Rehacer (topolog\xEDa)"],
1664
+ ["\u2318 /", "Esta ayuda"],
1665
+ ["\u2318 B", "Toggle sidebar"],
1666
+ ["\u2318 \u21E7 C", "Toggle modo Creator"],
1667
+ ["\u2318 \u21E7 V", "Toggle canvas"]
1668
+ ].map(([keys, desc]) => /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
1669
+ /* @__PURE__ */ jsx9("span", { className: "text-text-secondary", children: desc }),
1670
+ /* @__PURE__ */ jsx9("kbd", { className: "px-2 py-0.5 bg-surface-glass border border-border-default rounded text-text-primary font-mono text-[10px]", children: keys })
1671
+ ] }, keys)) }),
1672
+ /* @__PURE__ */ jsx9("button", { onClick: () => setShowShortcuts(false), className: "w-full mt-4 py-2 bg-surface-glass hover:bg-surface-glass rounded-xl text-xs text-text-secondary transition", children: "Cerrar" })
1673
+ ] }) })
1674
+ ] });
1675
+ };
1676
+
1677
+ // src/components/shell/shells/bootShells.ts
1678
+ var booted = false;
1679
+ function bootShells() {
1680
+ if (booted) return;
1681
+ booted = true;
1682
+ registerShell("markdown", MarkdownShell);
1683
+ registerShell("iframe", IframeShell);
1684
+ registerShell("shell-theme-editor", TokenPreview);
1685
+ registerShell("nexusai-preview", ShellNexusPreview, { icon: "\u{1F310}", color: "#3b82f6", label: "Web Preview" });
1686
+ registerShell("dataway-studio", DatawayChatShell, { icon: "\u0192", color: "#14b8a6", label: "Dataway M-Script Studio" });
1687
+ }
1688
+
1689
+ // src/hooks/useShellKeyboard.ts
1690
+ import { useEffect as useEffect5 } from "react";
1691
+ function useShellKeyboard() {
1692
+ useEffect5(() => {
1693
+ const handleKeyDown = (e) => {
1694
+ const isMeta = e.metaKey || e.ctrlKey;
1695
+ if (!isMeta) return;
1696
+ const store = useMorphInstanceStore.getState();
1697
+ if (e.key === "w" && store.activeInstanceId) {
1698
+ e.preventDefault();
1699
+ store.removeInstance(store.activeInstanceId);
1700
+ }
1701
+ if (e.key >= "1" && e.key <= "9") {
1702
+ const idx = parseInt(e.key) - 1;
1703
+ const instances = store.getInstances();
1704
+ if (idx < instances.length) {
1705
+ e.preventDefault();
1706
+ store.setActiveInstance(instances[idx].id);
1707
+ }
1708
+ }
1709
+ };
1710
+ window.addEventListener("keydown", handleKeyDown);
1711
+ return () => window.removeEventListener("keydown", handleKeyDown);
1712
+ }, []);
1713
+ }
1714
+
1715
+ // src/components/shell/MorphShell.tsx
1716
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1717
+ bootShells();
1718
+ var _abortCtrl = null;
1719
+ async function geminiStreamDirect(prompt, model) {
1720
+ const effectiveModel = model || localStorage.getItem("decido_gemini_model") || "gemini-3-flash-preview";
1721
+ const apiKey = localStorage.getItem("google_gemini_api_key") || window.__DECIDO__?._apiKey || import.meta.env?.VITE_GEMINI_API_KEY || "";
1722
+ if (!apiKey) {
1723
+ console.error('\u274C No Gemini API key. Set localStorage "google_gemini_api_key" or VITE_GEMINI_API_KEY');
1724
+ usePlaygroundStore.getState().addChatMessage({ type: "error", sender: "system", text: "\u26A0\uFE0F No se encontr\xF3 API key de Gemini." });
1725
+ return;
1726
+ }
1727
+ const store = usePlaygroundStore.getState();
1728
+ store.addChatMessage({ type: "text", sender: "user", text: prompt });
1729
+ const controller = new AbortController();
1730
+ _abortCtrl = controller;
1731
+ const steps = [
1732
+ { type: "thinking", label: "Processing", startTime: Date.now() }
1733
+ ];
1734
+ try {
1735
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/${effectiveModel}:streamGenerateContent?alt=sse&key=${apiKey}`;
1736
+ const res = await fetch(url, {
1737
+ method: "POST",
1738
+ headers: { "Content-Type": "application/json" },
1739
+ body: JSON.stringify({
1740
+ contents: [{ role: "user", parts: [{ text: prompt }] }],
1741
+ systemInstruction: { parts: [{ text: "Eres Decido AI, un asistente del sistema operativo Decido OS. Responde en espa\xF1ol, s\xE9 conciso y \xFAtil." }] },
1742
+ generationConfig: { temperature: 0.7, maxOutputTokens: 4096 }
1743
+ }),
1744
+ signal: controller.signal
1745
+ });
1746
+ if (!res.ok) throw new Error(`Gemini API ${res.status}: ${(await res.text()).slice(0, 200)}`);
1747
+ steps[0].endTime = Date.now();
1748
+ steps.push({ type: "text_delta", label: "Streaming", startTime: Date.now() });
1749
+ const reader = res.body?.getReader();
1750
+ if (!reader) throw new Error("No reader");
1751
+ const decoder = new TextDecoder();
1752
+ let fullText = "";
1753
+ const msgId = `gemini-${Date.now()}`;
1754
+ let lineBuffer = "";
1755
+ while (true) {
1756
+ const { done, value } = await reader.read();
1757
+ if (done) break;
1758
+ lineBuffer += decoder.decode(value, { stream: true });
1759
+ const lines = lineBuffer.split("\n");
1760
+ lineBuffer = lines.pop() || "";
1761
+ for (const line of lines) {
1762
+ const trimmed = line.trim();
1763
+ if (!trimmed.startsWith("data: ")) continue;
1764
+ const jsonStr = trimmed.slice(6);
1765
+ if (!jsonStr || jsonStr === "[DONE]") continue;
1766
+ try {
1767
+ const data = JSON.parse(jsonStr);
1768
+ const text = data?.candidates?.[0]?.content?.parts?.[0]?.text;
1769
+ if (text) {
1770
+ fullText += text;
1771
+ usePlaygroundStore.getState().upsertChatMessage(msgId, {
1772
+ id: msgId,
1773
+ type: "text",
1774
+ sender: "agent",
1775
+ text: fullText,
1776
+ timestamp: Date.now(),
1777
+ executionSteps: [...steps]
1778
+ });
1779
+ }
1780
+ } catch (parseErr) {
1781
+ console.warn("[SSE] JSON parse failed:", jsonStr.slice(0, 100), parseErr);
1782
+ }
1783
+ }
1784
+ }
1785
+ if (lineBuffer.trim().startsWith("data: ")) {
1786
+ try {
1787
+ const text = JSON.parse(lineBuffer.trim().slice(6))?.candidates?.[0]?.content?.parts?.[0]?.text;
1788
+ if (text) fullText += text;
1789
+ } catch {
1790
+ }
1791
+ }
1792
+ steps[1].endTime = Date.now();
1793
+ steps.push({ type: "text_complete", label: "Complete", startTime: Date.now(), endTime: Date.now() });
1794
+ usePlaygroundStore.getState().upsertChatMessage(msgId, {
1795
+ id: msgId,
1796
+ type: "text",
1797
+ sender: "agent",
1798
+ text: fullText,
1799
+ timestamp: Date.now(),
1800
+ executionSteps: steps
1801
+ });
1802
+ console.log("\u2705 Streaming complete:", fullText.length, "chars");
1803
+ } catch (err) {
1804
+ if (err.name === "AbortError") {
1805
+ usePlaygroundStore.getState().addChatMessage({ type: "alert", sender: "system", text: "\u23F9\uFE0F Generaci\xF3n cancelada." });
1806
+ } else {
1807
+ console.error("\u274C", err.message);
1808
+ usePlaygroundStore.getState().addChatMessage({ type: "error", sender: "system", text: `\u274C ${err.message}` });
1809
+ }
1810
+ } finally {
1811
+ _abortCtrl = null;
1812
+ }
1813
+ }
1814
+ if (typeof window !== "undefined") {
1815
+ window.__DECIDO__ = {
1816
+ ...window.__DECIDO__,
1817
+ useMorphInstanceStore,
1818
+ useLayoutStore,
1819
+ useMorphologyStore,
1820
+ usePlaygroundStore,
1821
+ registerShell,
1822
+ resolveShell,
1823
+ getRegisteredShellTypes,
1824
+ geminiStream: geminiStreamDirect,
1825
+ geminiAbort: () => _abortCtrl?.abort(),
1826
+ // SDK: Plugin extensibility (lazy-loaded to avoid circular deps at module eval)
1827
+ registerSuggestions: (pluginId, items) => {
1828
+ import("./useSuggestionsStore-4L2AIZ2D.mjs").then((m) => m.useSuggestionsStore.getState().registerPluginSuggestions(pluginId, items));
1829
+ },
1830
+ registerIntent: (p) => {
1831
+ import("./useIntentLens-LEQCAXCK.mjs").then((m) => m.registerIntentPattern(p));
1832
+ },
1833
+ getIntentPatterns: () => {
1834
+ return import("./useIntentLens-LEQCAXCK.mjs").then((m) => m.getIntentPatterns());
1835
+ }
1836
+ };
1837
+ }
1838
+ function MorphShell({
1839
+ showCanvas = true,
1840
+ setShowCanvas,
1841
+ activeConfig,
1842
+ prototypeBrand = "",
1843
+ activeState,
1844
+ chartData = [],
1845
+ dragConstraintsRef
1846
+ }) {
1847
+ useShellKeyboard();
1848
+ const activeInstance = useMorphInstanceStore((s) => s.getActiveInstance());
1849
+ const removeInstance = useMorphInstanceStore((s) => s.removeInstance);
1850
+ const layoutStage = useLayoutStore((s) => s.activeStage);
1851
+ const layoutHistory = useLayoutStore((s) => s.stageHistory);
1852
+ const morphStage = useMorphologyStore((s) => s.activeStage);
1853
+ const morphHistory = useMorphologyStore((s) => s.stageHistory);
1854
+ const isImmersive = useLayoutStore((s) => s.isImmersive);
1855
+ const setIsImmersive = useLayoutStore((s) => s.setIsImmersive);
1856
+ const activeStage = layoutStage || morphStage;
1857
+ const stageHistory = layoutStage ? layoutHistory : morphHistory;
1858
+ const activeArtifactData = usePlaygroundStore((s) => s.activeArtifactData);
1859
+ const effectiveStage = activeStage || (activeArtifactData ? { type: "artifact", data: activeArtifactData } : null);
1860
+ const renderingType = effectiveStage?.type || "3d";
1861
+ const stageMeta = STAGE_META2[renderingType] || STAGE_META2["custom"];
1862
+ const StageIcon = stageMeta.icon;
1863
+ const addTab = useLayoutStore((s) => s.addTab);
1864
+ React9.useEffect(() => {
1865
+ if (!effectiveStage && !activeInstance) return;
1866
+ if (activeInstance) {
1867
+ addTab({
1868
+ id: `morph-${activeInstance.id}`,
1869
+ label: activeInstance.label,
1870
+ componentId: `morph:${activeInstance.shellType}`,
1871
+ closeable: true,
1872
+ data: activeInstance
1873
+ });
1874
+ } else if (effectiveStage) {
1875
+ const tabId = `morph-${renderingType}-${effectiveStage.label || "stage"}`;
1876
+ addTab({
1877
+ id: tabId,
1878
+ label: effectiveStage.label || renderingType.toUpperCase(),
1879
+ componentId: `morph:${renderingType}`,
1880
+ closeable: true,
1881
+ data: effectiveStage
1882
+ });
1883
+ }
1884
+ }, [effectiveStage?.type, effectiveStage?.label, activeInstance?.id, activeInstance?.shellType]);
1885
+ const handleClose = useCallback6(() => {
1886
+ if (activeInstance) {
1887
+ removeInstance(activeInstance.id);
1888
+ }
1889
+ useLayoutStore.getState().clearStages();
1890
+ useMorphologyStore.getState().clearStages();
1891
+ usePlaygroundStore.getState().setActiveArtifactData(null);
1892
+ setShowCanvas?.(false);
1893
+ }, [setShowCanvas, activeInstance, removeInstance]);
1894
+ const handleBack = useCallback6(() => {
1895
+ if (activeInstance) {
1896
+ const instances = Array.from(useMorphInstanceStore.getState().instances.keys());
1897
+ const currentIdx = instances.indexOf(activeInstance.id);
1898
+ useLayoutStore.getState().removeTab(`morph-${activeInstance.id}`);
1899
+ removeInstance(activeInstance.id);
1900
+ if (instances.length > 1) {
1901
+ const prevIdx = currentIdx > 0 ? currentIdx - 1 : instances.length - 2;
1902
+ const prevId = instances.filter((id) => id !== activeInstance.id)[Math.min(prevIdx, instances.length - 2)];
1903
+ if (prevId) {
1904
+ useMorphInstanceStore.getState().setActiveInstance(prevId);
1905
+ const prevInstance = useMorphInstanceStore.getState().instances.get(prevId);
1906
+ if (prevInstance?.sourceChatId) {
1907
+ usePlaygroundStore.getState().setActiveChatId(prevInstance.sourceChatId);
1908
+ }
1909
+ }
1910
+ } else {
1911
+ handleClose();
1912
+ }
1913
+ } else if (stageHistory.length > 0) {
1914
+ useLayoutStore.getState().popStage();
1915
+ useMorphologyStore.getState().popStage();
1916
+ } else {
1917
+ handleClose();
1918
+ }
1919
+ }, [activeInstance, stageHistory.length, handleClose, removeInstance]);
1920
+ const toggleImmersive = useCallback6(() => {
1921
+ setIsImmersive(!isImmersive);
1922
+ }, [isImmersive, setIsImmersive]);
1923
+ if (!showCanvas) return null;
1924
+ const renderNewAPI = activeInstance && resolveShell(activeInstance.shellType);
1925
+ const displayLabel = activeInstance?.label || activeStage?.label || (activeArtifactData ? "Artefacto" : "Lienzo");
1926
+ const displayType = activeInstance?.shellType || renderingType;
1927
+ return /* @__PURE__ */ jsx10(AnimatePresence3, { children: /* @__PURE__ */ jsxs10(
1928
+ motion4.div,
1929
+ {
1930
+ initial: { opacity: 0, scale: 0.95 },
1931
+ animate: { opacity: 1, scale: 1 },
1932
+ exit: { opacity: 0, scale: 0.95 },
1933
+ className: "w-full h-full bg-surface-secondary flex flex-col overflow-hidden relative border-l border-border-subtle",
1934
+ children: [
1935
+ /* @__PURE__ */ jsxs10("div", { className: "flex justify-between items-center pl-2 z-10 p-3 pb-0 shrink-0", children: [
1936
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
1937
+ (activeStage || activeArtifactData || activeInstance) && /* @__PURE__ */ jsx10("button", { onClick: handleBack, className: "p-1.5 rounded-lg hover:bg-surface-glass text-text-muted hover:text-text-primary transition-colors", title: "Volver", children: /* @__PURE__ */ jsx10(ArrowLeft2, { size: 16 }) }),
1938
+ /* @__PURE__ */ jsxs10("h2", { className: "text-sm font-bold flex items-center gap-2 text-text-primary", children: [
1939
+ /* @__PURE__ */ jsx10("div", { className: "w-6 h-6 rounded-lg flex items-center justify-center bg-surface-glass", children: /* @__PURE__ */ jsx10(StageIcon, { size: 12, className: stageMeta.color }) }),
1940
+ /* @__PURE__ */ jsx10("span", { className: "truncate max-w-[180px]", children: displayLabel }),
1941
+ /* @__PURE__ */ jsx10("span", { className: "text-text-muted font-mono text-[9px] ml-1", children: String(displayType).toUpperCase() })
1942
+ ] })
1943
+ ] }),
1944
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
1945
+ stageHistory.length > 0 && /* @__PURE__ */ jsxs10("div", { className: "hidden md:flex items-center gap-1 text-[9px] text-text-muted font-mono", children: [
1946
+ stageHistory.map((s, i) => /* @__PURE__ */ jsxs10(React9.Fragment, { children: [
1947
+ /* @__PURE__ */ jsx10(
1948
+ "span",
1949
+ {
1950
+ className: "hover:text-text-secondary cursor-pointer",
1951
+ onClick: () => {
1952
+ const popsNeeded = stageHistory.length - i;
1953
+ for (let j = 0; j < popsNeeded; j++) {
1954
+ useLayoutStore.getState().popStage();
1955
+ useMorphologyStore.getState().popStage();
1956
+ }
1957
+ },
1958
+ children: s.label || s.type
1959
+ }
1960
+ ),
1961
+ /* @__PURE__ */ jsx10("span", { children: "\u203A" })
1962
+ ] }, i)),
1963
+ /* @__PURE__ */ jsx10("span", { className: "text-text-secondary", children: displayLabel })
1964
+ ] }),
1965
+ /* @__PURE__ */ jsx10("button", { onClick: toggleImmersive, className: "p-1.5 rounded-lg hover:bg-surface-glass text-text-muted hover:text-text-primary transition-colors", title: isImmersive ? "Salir de inmersivo" : "Modo inmersivo", children: isImmersive ? /* @__PURE__ */ jsx10(Minimize22, { size: 14 }) : /* @__PURE__ */ jsx10(Maximize22, { size: 14 }) }),
1966
+ /* @__PURE__ */ jsx10("button", { onClick: handleClose, className: "p-1.5 bg-surface-glass hover:bg-red-500/20 text-text-secondary hover:text-red-400 rounded-full transition-colors", children: /* @__PURE__ */ jsx10(X4, { size: 14 }) })
1967
+ ] })
1968
+ ] }),
1969
+ activeInstance && /* @__PURE__ */ jsx10(ArtifactBar, { instanceId: activeInstance.id }),
1970
+ /* @__PURE__ */ jsx10("div", { className: "flex-1 relative overflow-hidden", children: renderNewAPI ? (
1971
+ // New API: render shell from registry
1972
+ React9.createElement(resolveShell(activeInstance.shellType), {
1973
+ instanceId: activeInstance.id,
1974
+ data: activeInstance.data,
1975
+ artifacts: activeInstance.artifacts,
1976
+ activeArtifactIndex: activeInstance.activeArtifactIndex
1977
+ })
1978
+ ) : (
1979
+ // Legacy: delegate to PlaygroundCanvas
1980
+ /* @__PURE__ */ jsx10(
1981
+ PlaygroundCanvas,
1982
+ {
1983
+ showCanvas: true,
1984
+ setShowCanvas: setShowCanvas || (() => {
1985
+ }),
1986
+ activeConfig,
1987
+ prototypeBrand,
1988
+ activeState,
1989
+ chartData,
1990
+ dragConstraintsRef: dragConstraintsRef || void 0
1991
+ }
1992
+ )
1993
+ ) })
1994
+ ]
1995
+ }
1996
+ ) });
1997
+ }
1998
+
1999
+ export {
2000
+ PlaygroundCanvas,
2001
+ ArtifactBar,
2002
+ useShellKeyboard,
2003
+ MorphShell
2004
+ };