@decido/shell 4.0.3 → 4.0.4

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 (70) hide show
  1. package/dist/CenterComposite-V3LL23PG.mjs +64 -0
  2. package/dist/DebugPanel-NFXQOZQ6.mjs +1 -0
  3. package/dist/MorphShell-4JGDAN46.mjs +1 -0
  4. package/dist/PlaygroundAppSidebar-QI73PNRI.mjs +1 -0
  5. package/dist/PlaygroundChat-CBIIQMWU.mjs +1 -0
  6. package/dist/PlaygroundTerminal-MVZKXTYW.mjs +1 -0
  7. package/dist/PluginSandbox-MUNBFWGH.mjs +1 -0
  8. package/dist/ReactFlowEditor-2ALQWCHQ.mjs +6 -0
  9. package/dist/ReactFlowEditor-ROZ6RF3K.css +1 -0
  10. package/dist/TimelineEditor-UPRRXXVM.mjs +3 -0
  11. package/dist/WidgetSlotPanel-YX2VBQAP.mjs +1 -0
  12. package/dist/chunk-3YEFIKFH.mjs +1 -0
  13. package/dist/chunk-53FADIUX.mjs +1 -0
  14. package/dist/chunk-5FWJFDER.mjs +285 -0
  15. package/dist/chunk-6K3AFDP5.mjs +1 -0
  16. package/dist/chunk-AGAAZ2MU.mjs +1 -0
  17. package/dist/chunk-BCP43BRV.mjs +2 -0
  18. package/dist/chunk-BSJBW64M.mjs +1 -0
  19. package/dist/chunk-C5FD536O.mjs +1 -0
  20. package/dist/chunk-DTH34ILQ.mjs +1 -0
  21. package/dist/chunk-DWP77YZU.mjs +1 -0
  22. package/dist/chunk-O3OWL3Q2.mjs +1 -0
  23. package/dist/chunk-POBZMHZ5.mjs +7 -0
  24. package/dist/chunk-S3AQ4QDD.mjs +42 -0
  25. package/dist/chunk-XDA6ZRIQ.mjs +1 -0
  26. package/dist/chunk-XTDMHHMW.mjs +5 -0
  27. package/dist/chunk-YAYNNUEX.mjs +12 -0
  28. package/dist/chunk-YEKQJ4YC.mjs +1 -0
  29. package/dist/chunk-YHFKY5MB.mjs +1 -0
  30. package/dist/chunk-ZKD74U4X.mjs +1 -0
  31. package/dist/index.css +1 -561
  32. package/dist/index.js +462 -65086
  33. package/dist/index.mjs +50 -40209
  34. package/dist/useIntentLens-OCAUBNOQ.mjs +1 -0
  35. package/dist/useSuggestionsStore-Q4KVPU7L.mjs +1 -0
  36. package/dist/wasm-A4BTVJWV.mjs +1 -0
  37. package/package.json +15 -15
  38. package/dist/CenterComposite-RPEGBKQU.mjs +0 -1697
  39. package/dist/DebugPanel-KEKDMHDN.mjs +0 -14
  40. package/dist/MorphShell-FKDBB7E5.mjs +0 -14
  41. package/dist/PlaygroundAppSidebar-ALYJJGAO.mjs +0 -9
  42. package/dist/PlaygroundChat-MI2KXMK6.mjs +0 -15
  43. package/dist/PlaygroundTerminal-5AV4BJAI.mjs +0 -7
  44. package/dist/PluginSandbox-WMNAUQOJ.mjs +0 -188
  45. package/dist/ReactFlowEditor-RTF2652X.mjs +0 -3574
  46. package/dist/ReactFlowEditor-ZW5MCN5Y.css +0 -561
  47. package/dist/TimelineEditor-N4HRMHTB.mjs +0 -226
  48. package/dist/WidgetSlotPanel-KJI4CHHD.mjs +0 -11
  49. package/dist/chunk-2YMI4N5I.mjs +0 -2004
  50. package/dist/chunk-3BZX7LF2.mjs +0 -139
  51. package/dist/chunk-3P4P3M54.mjs +0 -136
  52. package/dist/chunk-F3OTFHNO.mjs +0 -40
  53. package/dist/chunk-IMHORBTL.mjs +0 -48
  54. package/dist/chunk-JF5QSJYT.mjs +0 -295
  55. package/dist/chunk-LWMMFTJC.mjs +0 -382
  56. package/dist/chunk-MSVEFEXE.mjs +0 -179
  57. package/dist/chunk-OCHGY2MN.mjs +0 -1662
  58. package/dist/chunk-PMYAM764.mjs +0 -813
  59. package/dist/chunk-Q64KZXPK.mjs +0 -43
  60. package/dist/chunk-QHQW2HMU.mjs +0 -155
  61. package/dist/chunk-RWZ4BOIN.mjs +0 -385
  62. package/dist/chunk-UHT6FIYF.mjs +0 -195
  63. package/dist/chunk-UJCSKKID.mjs +0 -30
  64. package/dist/chunk-V3CYNPGL.mjs +0 -8758
  65. package/dist/chunk-VBPGEFNM.mjs +0 -2381
  66. package/dist/chunk-XMSU6UWD.mjs +0 -158
  67. package/dist/chunk-ZCCCBHE6.mjs +0 -55
  68. package/dist/useIntentLens-LEQCAXCK.mjs +0 -13
  69. package/dist/useSuggestionsStore-4L2AIZ2D.mjs +0 -7
  70. package/dist/wasm-QFXGEYGP.mjs +0 -81
@@ -1,1697 +0,0 @@
1
- import {
2
- PlaygroundErrorBoundary,
3
- bridgeToMorph,
4
- useNetwork,
5
- useStudioConfig,
6
- useUIStateListener
7
- } from "./chunk-LWMMFTJC.mjs";
8
- import {
9
- PlaygroundChat
10
- } from "./chunk-OCHGY2MN.mjs";
11
- import "./chunk-IMHORBTL.mjs";
12
- import "./chunk-F3OTFHNO.mjs";
13
- import "./chunk-QHQW2HMU.mjs";
14
- import {
15
- PlaygroundTerminal
16
- } from "./chunk-Q64KZXPK.mjs";
17
- import {
18
- DebugPanel
19
- } from "./chunk-VBPGEFNM.mjs";
20
- import "./chunk-JF5QSJYT.mjs";
21
- import "./chunk-ZCCCBHE6.mjs";
22
- import {
23
- useMorphInstanceStore
24
- } from "./chunk-UHT6FIYF.mjs";
25
- import {
26
- useUIComponentStore
27
- } from "./chunk-3BZX7LF2.mjs";
28
- import "./chunk-MSVEFEXE.mjs";
29
- import {
30
- dlog,
31
- useDebugPanelStore
32
- } from "./chunk-3P4P3M54.mjs";
33
- import {
34
- usePlaygroundStore
35
- } from "./chunk-XMSU6UWD.mjs";
36
- import "./chunk-UJCSKKID.mjs";
37
-
38
- // src/components/shell/CenterComposite.tsx
39
- import { useRef as useRef4, useEffect as useEffect6 } from "react";
40
- import { Panel, Group as PanelGroup, Separator as PanelResizeHandle } from "react-resizable-panels";
41
-
42
- // src/components/playground/PlaygroundBottomControls.tsx
43
- import React8, { useState as useState5 } from "react";
44
- import { motion as motion5 } from "motion/react";
45
- import { X as X3 } from "lucide-react";
46
-
47
- // src/components/studio/BlueprintManagerPanel.tsx
48
- import { useState as useState2, useCallback, useRef as useRef2, useEffect as useEffect2 } from "react";
49
- import { motion as motion2, AnimatePresence as AnimatePresence2 } from "motion/react";
50
- import { FolderOpen, Plus, Check, X, FileText as FileText2, Network as Network2 } from "lucide-react";
51
- import { useTimelineStore } from "@decido/engine";
52
- import { useBlueprintNavigation } from "@decido/engine";
53
-
54
- // src/components/studio/blueprint/BlueprintCard.tsx
55
- import { useState } from "react";
56
- import { motion, AnimatePresence } from "motion/react";
57
- import { FileText, Network, MoreVertical, Edit3, Copy, Trash2 } from "lucide-react";
58
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
59
- var BlueprintCard = ({
60
- id,
61
- name,
62
- nodeCount,
63
- isActive,
64
- isSubflow,
65
- onSelect,
66
- onRename,
67
- onDuplicate,
68
- onDelete
69
- }) => {
70
- const [isEditing, setIsEditing] = useState(false);
71
- const [editName, setEditName] = useState(name);
72
- const [menuOpen, setMenuOpen] = useState(false);
73
- const handleSubmitRename = () => {
74
- const t = editName.trim();
75
- if (t && t !== name) onRename(t);
76
- setIsEditing(false);
77
- };
78
- const accent = isSubflow ? "violet" : "cyan";
79
- const Icon = isSubflow ? Network : FileText;
80
- return /* @__PURE__ */ jsxs(
81
- "div",
82
- {
83
- className: `group relative flex items-center gap-2 px-3 py-2 rounded-xl cursor-pointer transition-all mb-0.5 ${isActive ? `bg-${accent}-500/15 border border-${accent}-500/30` : "hover:bg-surface-glass border border-transparent"}`,
84
- onClick: () => !isEditing && onSelect(),
85
- children: [
86
- /* @__PURE__ */ jsx(Icon, { size: 14, className: isActive ? `text-${accent}-400` : isSubflow ? "text-violet-400/60" : "text-text-muted" }),
87
- isEditing ? /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center gap-1", children: /* @__PURE__ */ jsx(
88
- "input",
89
- {
90
- autoFocus: true,
91
- value: editName,
92
- onChange: (e) => setEditName(e.target.value),
93
- onKeyDown: (e) => {
94
- if (e.key === "Enter") handleSubmitRename();
95
- if (e.key === "Escape") setIsEditing(false);
96
- },
97
- onBlur: handleSubmitRename,
98
- className: "flex-1 bg-surface-glass border border-cyan-500/30 rounded px-2 py-0.5 text-xs text-text-primary focus:outline-hidden font-mono",
99
- onClick: (e) => e.stopPropagation()
100
- }
101
- ) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
102
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
103
- /* @__PURE__ */ jsx("div", { className: `text-xs font-medium truncate ${isActive ? "text-text-primary" : isSubflow ? "text-text-secondary" : "text-text-primary"}`, children: name || id }),
104
- /* @__PURE__ */ jsxs("div", { className: "text-[10px] text-text-muted font-mono", children: [
105
- nodeCount,
106
- " nodos"
107
- ] })
108
- ] }),
109
- isSubflow && /* @__PURE__ */ jsx("span", { className: "text-[9px] bg-violet-500/10 text-violet-400 px-1.5 py-0.5 rounded-full font-mono", children: "sub" }),
110
- /* @__PURE__ */ jsx(
111
- "button",
112
- {
113
- onClick: (e) => {
114
- e.stopPropagation();
115
- setMenuOpen(!menuOpen);
116
- },
117
- className: "opacity-0 group-hover:opacity-100 p-1 rounded hover:bg-surface-glass text-text-muted transition-all",
118
- children: /* @__PURE__ */ jsx(MoreVertical, { size: 14 })
119
- }
120
- )
121
- ] }),
122
- /* @__PURE__ */ jsx(AnimatePresence, { children: menuOpen && /* @__PURE__ */ jsxs(
123
- motion.div,
124
- {
125
- initial: { opacity: 0, scale: 0.9 },
126
- animate: { opacity: 1, scale: 1 },
127
- exit: { opacity: 0, scale: 0.9 },
128
- className: "absolute right-0 top-full z-50 bg-surface-tertiary border border-border-default rounded-xl shadow-2xl overflow-hidden mt-1 min-w-[140px]",
129
- onClick: (e) => e.stopPropagation(),
130
- children: [
131
- /* @__PURE__ */ jsxs(
132
- "button",
133
- {
134
- onClick: () => {
135
- setEditName(name || id);
136
- setIsEditing(true);
137
- setMenuOpen(false);
138
- },
139
- className: "flex items-center gap-2 w-full px-3 py-2 text-xs text-text-primary hover:bg-surface-glass transition-colors",
140
- children: [
141
- /* @__PURE__ */ jsx(Edit3, { size: 12 }),
142
- " Renombrar"
143
- ]
144
- }
145
- ),
146
- /* @__PURE__ */ jsxs(
147
- "button",
148
- {
149
- onClick: () => {
150
- onDuplicate();
151
- setMenuOpen(false);
152
- },
153
- className: "flex items-center gap-2 w-full px-3 py-2 text-xs text-text-primary hover:bg-surface-glass transition-colors",
154
- children: [
155
- /* @__PURE__ */ jsx(Copy, { size: 12 }),
156
- " Duplicar"
157
- ]
158
- }
159
- ),
160
- onDelete && /* @__PURE__ */ jsxs(
161
- "button",
162
- {
163
- onClick: () => {
164
- onDelete();
165
- setMenuOpen(false);
166
- },
167
- className: "flex items-center gap-2 w-full px-3 py-2 text-xs text-red-400 hover:bg-red-500/10 transition-colors border-t border-border-subtle",
168
- children: [
169
- /* @__PURE__ */ jsx(Trash2, { size: 12 }),
170
- " Eliminar"
171
- ]
172
- }
173
- )
174
- ]
175
- }
176
- ) })
177
- ]
178
- }
179
- );
180
- };
181
-
182
- // src/components/studio/BlueprintManagerPanel.tsx
183
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
184
- var BlueprintManagerPanel = ({ isOpen, onClose }) => {
185
- const timelines = useTimelineStore((s) => s.timelines);
186
- const blueprintLibrary = useTimelineStore((s) => s.blueprintLibrary);
187
- const activeTimelineId = usePlaygroundStore((s) => s.prototypeBrand);
188
- const setPrototypeBrand = usePlaygroundStore((s) => s.setPrototypeBrand);
189
- const resetNavigation = useBlueprintNavigation((s) => s.resetNavigation);
190
- const createTimeline = useTimelineStore((s) => s.createTimeline);
191
- const addKeyframe = useTimelineStore((s) => s.addKeyframe);
192
- const addBlueprint = useTimelineStore((s) => s.addBlueprint);
193
- const duplicateTimeline = useTimelineStore((s) => s.duplicateTimeline);
194
- const renameTimeline = useTimelineStore((s) => s.renameTimeline);
195
- const deleteTimeline = useTimelineStore((s) => s.deleteTimeline);
196
- const [isCreating, setIsCreating] = useState2(false);
197
- const [createType, setCreateType] = useState2("blueprint");
198
- const [newName, setNewName] = useState2("");
199
- const [showCreateMenu, setShowCreateMenu] = useState2(false);
200
- const createMenuRef = useRef2(null);
201
- useEffect2(() => {
202
- const h = (e) => {
203
- if (createMenuRef.current && !createMenuRef.current.contains(e.target)) setShowCreateMenu(false);
204
- };
205
- document.addEventListener("mousedown", h);
206
- return () => document.removeEventListener("mousedown", h);
207
- }, []);
208
- const handleCreate = useCallback(() => {
209
- if (!newName.trim()) return;
210
- if (createType === "subflow") {
211
- const subKey = `subflow_${newName.trim().toLowerCase().replace(/\s+/g, "_")}`;
212
- const id = createTimeline(newName.trim(), subKey);
213
- addKeyframe(id, { t: 0, track: "entry", label: "Entrada", position: { x: 300, y: 50 } });
214
- addKeyframe(id, { t: 5, track: "return", label: "Retorno", handle: "success", position: { x: 300, y: 300 } });
215
- const bpId = `bp_${newName.trim().toLowerCase().replace(/\s+/g, "_")}`;
216
- const tl = useTimelineStore.getState().timelines[id];
217
- if (tl) addBlueprint({ ...tl, id: bpId, isSubflow: true, signature: { inputs: {}, outputs: {} }, metadata: { author: "user", version: "1.0.0", tags: ["custom"], description: newName.trim(), isPublic: false, createdAt: Date.now(), updatedAt: Date.now() } });
218
- setPrototypeBrand(id);
219
- } else {
220
- setPrototypeBrand(createTimeline(newName.trim()));
221
- }
222
- setNewName("");
223
- setIsCreating(false);
224
- }, [newName, createType, createTimeline, addKeyframe, addBlueprint, setPrototypeBrand]);
225
- const handleSelect = useCallback((id) => {
226
- resetNavigation();
227
- setPrototypeBrand(id);
228
- }, [setPrototypeBrand, resetNavigation]);
229
- const handleDelete = useCallback((id) => {
230
- const del = deleteTimeline(id);
231
- if (del && activeTimelineId === id) {
232
- const rem = Object.keys(timelines).filter((k) => k !== id);
233
- if (rem.length > 0) setPrototypeBrand(rem[0]);
234
- }
235
- }, [deleteTimeline, activeTimelineId, timelines, setPrototypeBrand]);
236
- const allBlueprints = Object.entries(timelines);
237
- const subflows = Object.entries(blueprintLibrary);
238
- const bpToKey = {};
239
- for (const [key] of Object.entries(timelines)) {
240
- if (key.startsWith("subflow_")) bpToKey[`bp_${key.replace("subflow_", "")}`] = key;
241
- }
242
- return /* @__PURE__ */ jsx2(AnimatePresence2, { children: isOpen && /* @__PURE__ */ jsxs2(Fragment2, { children: [
243
- /* @__PURE__ */ jsx2(motion2.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, className: "fixed inset-0 bg-surface-overlay z-40", onClick: onClose }),
244
- /* @__PURE__ */ jsxs2(
245
- motion2.div,
246
- {
247
- initial: { opacity: 0, x: -20, scale: 0.95 },
248
- animate: { opacity: 1, x: 0, scale: 1 },
249
- exit: { opacity: 0, x: -20, scale: 0.95 },
250
- transition: { type: "spring", damping: 25, stiffness: 300 },
251
- className: "fixed left-4 bottom-20 z-50 w-80 max-h-[60vh] bg-surface-primary border border-border-default rounded-2xl shadow-2xl overflow-hidden flex flex-col",
252
- style: { backdropFilter: "blur(20px)" },
253
- onClick: (e) => e.stopPropagation(),
254
- children: [
255
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between px-4 py-3 border-b border-border-default bg-surface-glass", children: [
256
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
257
- /* @__PURE__ */ jsx2(FolderOpen, { size: 16, className: "text-cyan-400" }),
258
- /* @__PURE__ */ jsx2("span", { className: "text-sm font-bold text-text-primary tracking-wide", children: "Blueprints" }),
259
- /* @__PURE__ */ jsx2("span", { className: "text-[10px] bg-surface-glass text-text-secondary px-1.5 py-0.5 rounded-full font-mono", children: allBlueprints.length })
260
- ] }),
261
- /* @__PURE__ */ jsxs2("div", { className: "relative", ref: createMenuRef, children: [
262
- /* @__PURE__ */ jsx2("button", { onClick: () => setShowCreateMenu(!showCreateMenu), className: "p-1.5 rounded-lg hover:bg-cyan-500/20 text-cyan-400 transition-colors", title: "Crear nuevo", children: /* @__PURE__ */ jsx2(Plus, { size: 16 }) }),
263
- /* @__PURE__ */ jsx2(AnimatePresence2, { children: showCreateMenu && /* @__PURE__ */ jsxs2(
264
- motion2.div,
265
- {
266
- initial: { opacity: 0, scale: 0.9, y: -5 },
267
- animate: { opacity: 1, scale: 1, y: 0 },
268
- exit: { opacity: 0, scale: 0.9, y: -5 },
269
- className: "absolute right-0 top-full mt-1 z-50 bg-surface-tertiary border border-border-default rounded-xl shadow-2xl overflow-hidden min-w-[180px]",
270
- children: [
271
- /* @__PURE__ */ jsxs2("button", { onClick: () => {
272
- setCreateType("blueprint");
273
- setIsCreating(true);
274
- setShowCreateMenu(false);
275
- }, className: "flex items-center gap-2 w-full px-3 py-2.5 text-xs text-text-primary hover:bg-surface-glass transition-colors", children: [
276
- /* @__PURE__ */ jsx2(FileText2, { size: 13, className: "text-cyan-400" }),
277
- /* @__PURE__ */ jsxs2("div", { className: "text-left", children: [
278
- /* @__PURE__ */ jsx2("div", { className: "font-medium", children: "Nuevo Blueprint" }),
279
- /* @__PURE__ */ jsx2("div", { className: "text-[10px] text-text-muted", children: "Flujo principal" })
280
- ] })
281
- ] }),
282
- /* @__PURE__ */ jsxs2("button", { onClick: () => {
283
- setCreateType("subflow");
284
- setIsCreating(true);
285
- setShowCreateMenu(false);
286
- }, className: "flex items-center gap-2 w-full px-3 py-2.5 text-xs text-text-primary hover:bg-surface-glass transition-colors border-t border-border-subtle", children: [
287
- /* @__PURE__ */ jsx2(Network2, { size: 13, className: "text-violet-400" }),
288
- /* @__PURE__ */ jsxs2("div", { className: "text-left", children: [
289
- /* @__PURE__ */ jsx2("div", { className: "font-medium", children: "Nuevo SubFlow" }),
290
- /* @__PURE__ */ jsx2("div", { className: "text-[10px] text-text-muted", children: "Con Entry + Return" })
291
- ] })
292
- ] })
293
- ]
294
- }
295
- ) })
296
- ] })
297
- ] }),
298
- /* @__PURE__ */ jsx2(AnimatePresence2, { children: isCreating && /* @__PURE__ */ jsx2(motion2.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, className: "overflow-hidden border-b border-border-default", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 px-4 py-3", children: [
299
- createType === "subflow" ? /* @__PURE__ */ jsx2(Network2, { size: 14, className: "text-violet-400 shrink-0" }) : /* @__PURE__ */ jsx2(FileText2, { size: 14, className: "text-cyan-400 shrink-0" }),
300
- /* @__PURE__ */ jsx2(
301
- "input",
302
- {
303
- autoFocus: true,
304
- value: newName,
305
- onChange: (e) => setNewName(e.target.value),
306
- onKeyDown: (e) => {
307
- if (e.key === "Enter") handleCreate();
308
- if (e.key === "Escape") setIsCreating(false);
309
- },
310
- placeholder: createType === "subflow" ? "Nombre del subflow..." : "Nombre del blueprint...",
311
- className: "flex-1 bg-surface-glass border border-border-default rounded-lg px-3 py-1.5 text-xs text-text-primary focus:outline-hidden focus:border-cyan-500/50 font-mono"
312
- }
313
- ),
314
- /* @__PURE__ */ jsx2("button", { onClick: handleCreate, className: "p-1.5 rounded-lg bg-cyan-500/20 text-cyan-400 hover:bg-cyan-500/30", children: /* @__PURE__ */ jsx2(Check, { size: 14 }) }),
315
- /* @__PURE__ */ jsx2("button", { onClick: () => setIsCreating(false), className: "p-1.5 rounded-lg hover:bg-surface-glass text-text-muted", children: /* @__PURE__ */ jsx2(X, { size: 14 }) })
316
- ] }) }) }),
317
- /* @__PURE__ */ jsxs2("div", { className: "flex-1 overflow-y-auto custom-scrollbar", children: [
318
- /* @__PURE__ */ jsxs2("div", { className: "px-2 py-1", children: [
319
- /* @__PURE__ */ jsx2("div", { className: "px-2 py-1.5 text-[9px] font-bold text-text-muted uppercase tracking-widest", children: "Blueprints Principales" }),
320
- allBlueprints.map(([id, tl]) => /* @__PURE__ */ jsx2(
321
- BlueprintCard,
322
- {
323
- id,
324
- name: tl.name || id,
325
- nodeCount: tl.keyframes?.length || 0,
326
- isActive: id === activeTimelineId,
327
- onSelect: () => handleSelect(id),
328
- onRename: (name) => renameTimeline(id, name),
329
- onDuplicate: () => {
330
- setPrototypeBrand(duplicateTimeline(id));
331
- },
332
- onDelete: allBlueprints.length > 1 ? () => handleDelete(id) : void 0
333
- },
334
- id
335
- ))
336
- ] }),
337
- subflows.length > 0 && /* @__PURE__ */ jsxs2("div", { className: "px-2 py-1 border-t border-border-subtle", children: [
338
- /* @__PURE__ */ jsx2("div", { className: "px-2 py-1.5 text-[9px] font-bold text-text-muted uppercase tracking-widest", children: "Subflujos" }),
339
- subflows.map(([id, bp]) => {
340
- const tk = bpToKey[id] || "";
341
- return /* @__PURE__ */ jsx2(
342
- BlueprintCard,
343
- {
344
- id,
345
- name: bp.name || id,
346
- nodeCount: bp.keyframes?.length || 0,
347
- isActive: tk === activeTimelineId,
348
- isSubflow: true,
349
- onSelect: () => tk && handleSelect(tk),
350
- onRename: (name) => tk && renameTimeline(tk, name),
351
- onDuplicate: () => tk && setPrototypeBrand(duplicateTimeline(tk)),
352
- onDelete: () => tk && handleDelete(tk)
353
- },
354
- id
355
- );
356
- })
357
- ] })
358
- ] })
359
- ]
360
- }
361
- )
362
- ] }) });
363
- };
364
-
365
- // src/components/studio/DependencyTreePanel.tsx
366
- import { useMemo } from "react";
367
- import { motion as motion3, AnimatePresence as AnimatePresence3 } from "motion/react";
368
- import { AlertTriangle as AlertTriangle2, X as X2, TreePine } from "lucide-react";
369
- import { useTimelineStore as useTimelineStore2 } from "@decido/engine";
370
- import { useBlueprintNavigation as useBlueprintNavigation2 } from "@decido/engine";
371
-
372
- // src/components/studio/editor/TreeNodeItem.tsx
373
- import { useState as useState3 } from "react";
374
- import {
375
- ChevronRight,
376
- ChevronDown,
377
- FileText as FileText3,
378
- Network as Network3,
379
- Zap,
380
- AlertTriangle
381
- } from "lucide-react";
382
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
383
- var TreeNodeItem = ({ node, activeId, onNavigate }) => {
384
- const [isExpanded, setIsExpanded] = useState3(node.depth < 2);
385
- const hasChildren = node.children.length > 0;
386
- const isActive = node.id === activeId;
387
- const icon = node.type === "trigger" ? /* @__PURE__ */ jsx3(Zap, { size: 13, className: "text-amber-400" }) : node.type === "subflow" ? /* @__PURE__ */ jsx3(Network3, { size: 13, className: node.isCyclic ? "text-red-400" : "text-violet-400" }) : node.type === "orphan" ? /* @__PURE__ */ jsx3(AlertTriangle, { size: 13, className: "text-text-muted" }) : /* @__PURE__ */ jsx3(FileText3, { size: 13, className: "text-cyan-400" });
388
- return /* @__PURE__ */ jsxs3("div", { children: [
389
- /* @__PURE__ */ jsxs3(
390
- "div",
391
- {
392
- className: `flex items-center gap-1.5 px-2 py-1.5 rounded-lg cursor-pointer transition-all mb-0.5 ${isActive ? "bg-cyan-500/15 border border-cyan-500/30" : node.isCyclic ? "bg-red-500/5 border border-red-500/20 hover:bg-red-500/10" : "hover:bg-surface-glass border border-transparent"}`,
393
- style: { paddingLeft: `${8 + node.depth * 16}px` },
394
- onClick: () => onNavigate(node.id),
395
- children: [
396
- hasChildren ? /* @__PURE__ */ jsx3(
397
- "button",
398
- {
399
- onClick: (e) => {
400
- e.stopPropagation();
401
- setIsExpanded(!isExpanded);
402
- },
403
- className: "p-0.5 rounded hover:bg-surface-glass text-text-muted shrink-0",
404
- children: isExpanded ? /* @__PURE__ */ jsx3(ChevronDown, { size: 12 }) : /* @__PURE__ */ jsx3(ChevronRight, { size: 12 })
405
- }
406
- ) : /* @__PURE__ */ jsx3("span", { className: "w-5 shrink-0" }),
407
- icon,
408
- /* @__PURE__ */ jsx3("span", { className: `text-xs truncate flex-1 ${isActive ? "text-text-primary font-medium" : node.isCyclic ? "text-red-300" : "text-text-secondary"}`, children: node.name }),
409
- node.isCyclic && /* @__PURE__ */ jsx3("span", { className: "text-[8px] bg-red-500/20 text-red-400 px-1 py-0.5 rounded font-mono", children: "CICLO" }),
410
- node.type === "trigger" && /* @__PURE__ */ jsx3("span", { className: "text-[8px] bg-amber-500/10 text-amber-400 px-1 py-0.5 rounded font-mono", children: "\u26A1" })
411
- ]
412
- }
413
- ),
414
- hasChildren && isExpanded && /* @__PURE__ */ jsx3("div", { children: node.children.map((child) => /* @__PURE__ */ jsx3(
415
- TreeNodeItem,
416
- {
417
- node: child,
418
- activeId,
419
- onNavigate
420
- },
421
- child.id
422
- )) })
423
- ] });
424
- };
425
-
426
- // src/components/studio/DependencyTreePanel.tsx
427
- import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
428
- var DependencyTreePanel = ({ isOpen, onClose }) => {
429
- const timelines = useTimelineStore2((s) => s.timelines);
430
- const blueprintLibrary = useTimelineStore2((s) => s.blueprintLibrary);
431
- const setPrototypeBrand = usePlaygroundStore((s) => s.setPrototypeBrand);
432
- const resetNavigation = useBlueprintNavigation2((s) => s.resetNavigation);
433
- const activeTimelineId = usePlaygroundStore((s) => s.prototypeBrand);
434
- const { roots, orphans, cycleWarnings } = useMemo(() => {
435
- const references = {};
436
- const referencedBpIds = /* @__PURE__ */ new Set();
437
- for (const [key, timeline] of Object.entries(timelines)) {
438
- const subflowNodes = (timeline.keyframes || []).filter(
439
- (kf) => kf.track === "subflow" && kf.targetBlueprintId
440
- );
441
- if (subflowNodes.length > 0) {
442
- references[key] = subflowNodes.map((kf) => kf.targetBlueprintId);
443
- subflowNodes.forEach((kf) => referencedBpIds.add(kf.targetBlueprintId));
444
- }
445
- }
446
- const bpToTimelineKey = {};
447
- for (const key of Object.keys(timelines)) {
448
- if (key.startsWith("subflow_")) {
449
- const bpId = `bp_${key.replace("subflow_", "")}`;
450
- bpToTimelineKey[bpId] = key;
451
- }
452
- }
453
- const referencedTimelineKeys = new Set(
454
- [...referencedBpIds].map((bpId) => bpToTimelineKey[bpId]).filter(Boolean)
455
- );
456
- const cycleWarnings2 = [];
457
- const buildNode = (tlKey, visited, depth) => {
458
- const timeline = timelines[tlKey];
459
- const name = timeline?.name || tlKey;
460
- const isSubflow = tlKey.startsWith("subflow_");
461
- const hasTrigger = (timeline?.keyframes || []).some((kf) => kf.track === "trigger");
462
- if (visited.has(tlKey)) {
463
- cycleWarnings2.push(`Ciclo: ${[...visited].join(" \u2192 ")} \u2192 ${tlKey}`);
464
- return {
465
- id: tlKey,
466
- name,
467
- type: "subflow",
468
- children: [],
469
- isCyclic: true,
470
- depth
471
- };
472
- }
473
- const newVisited = new Set(visited);
474
- newVisited.add(tlKey);
475
- const childBpIds = references[tlKey] || [];
476
- const children = childBpIds.map((bpId) => {
477
- const childKey = bpToTimelineKey[bpId];
478
- if (childKey) {
479
- return buildNode(childKey, newVisited, depth + 1);
480
- }
481
- return {
482
- id: bpId,
483
- name: bpId,
484
- type: "orphan",
485
- children: [],
486
- depth: depth + 1
487
- };
488
- });
489
- return {
490
- id: tlKey,
491
- name,
492
- type: hasTrigger ? "trigger" : isSubflow ? "subflow" : "blueprint",
493
- children,
494
- depth
495
- };
496
- };
497
- const rootKeys = Object.keys(references).filter((key) => !referencedTimelineKeys.has(key));
498
- const roots2 = rootKeys.map((key) => buildNode(key, /* @__PURE__ */ new Set(), 0));
499
- const orphanBpIds = Object.keys(blueprintLibrary).filter((bpId) => {
500
- const tlKey = bpToTimelineKey[bpId];
501
- if (!tlKey) return false;
502
- return !referencedBpIds.has(bpId) && !rootKeys.includes(tlKey);
503
- });
504
- const orphans2 = orphanBpIds.map((bpId) => ({
505
- id: bpToTimelineKey[bpId] || bpId,
506
- name: blueprintLibrary[bpId]?.name || bpId,
507
- type: "subflow",
508
- children: [],
509
- depth: 0
510
- }));
511
- return { roots: roots2, orphans: orphans2, cycleWarnings: cycleWarnings2 };
512
- }, [timelines, blueprintLibrary]);
513
- const handleNavigate = (nodeId) => {
514
- resetNavigation();
515
- setPrototypeBrand(nodeId);
516
- };
517
- return /* @__PURE__ */ jsx4(AnimatePresence3, { children: isOpen && /* @__PURE__ */ jsxs4(Fragment3, { children: [
518
- /* @__PURE__ */ jsx4(
519
- motion3.div,
520
- {
521
- initial: { opacity: 0 },
522
- animate: { opacity: 1 },
523
- exit: { opacity: 0 },
524
- className: "fixed inset-0 bg-surface-overlay z-40",
525
- onClick: onClose
526
- }
527
- ),
528
- /* @__PURE__ */ jsxs4(
529
- motion3.div,
530
- {
531
- initial: { opacity: 0, x: -20, scale: 0.95 },
532
- animate: { opacity: 1, x: 0, scale: 1 },
533
- exit: { opacity: 0, x: -20, scale: 0.95 },
534
- transition: { type: "spring", damping: 25, stiffness: 300 },
535
- className: "fixed left-4 bottom-20 z-50 w-96 max-h-[60vh] bg-surface-primary border border-border-default rounded-2xl shadow-2xl overflow-hidden flex flex-col",
536
- style: { backdropFilter: "blur(20px)" },
537
- onClick: (e) => e.stopPropagation(),
538
- children: [
539
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between px-4 py-3 border-b border-border-default bg-surface-glass", children: [
540
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
541
- /* @__PURE__ */ jsx4(TreePine, { size: 16, className: "text-emerald-400" }),
542
- /* @__PURE__ */ jsx4("span", { className: "text-sm font-bold text-text-primary tracking-wide", children: "Jerarqu\xEDa" }),
543
- cycleWarnings.length > 0 && /* @__PURE__ */ jsxs4("span", { className: "text-[10px] bg-red-500/20 text-red-400 px-1.5 py-0.5 rounded-full font-mono flex items-center gap-1", children: [
544
- /* @__PURE__ */ jsx4(AlertTriangle2, { size: 10 }),
545
- " ",
546
- cycleWarnings.length,
547
- " ciclo(s)"
548
- ] })
549
- ] }),
550
- /* @__PURE__ */ jsx4("button", { onClick: onClose, className: "p-1.5 rounded-lg hover:bg-surface-glass text-text-muted transition-colors", children: /* @__PURE__ */ jsx4(X2, { size: 14 }) })
551
- ] }),
552
- /* @__PURE__ */ jsxs4("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-2", children: [
553
- roots.length === 0 && orphans.length === 0 && /* @__PURE__ */ jsx4("div", { className: "text-center text-text-muted text-xs py-8", children: "No hay flujos con subflows referenciados" }),
554
- roots.map((root) => /* @__PURE__ */ jsx4(
555
- TreeNodeItem,
556
- {
557
- node: root,
558
- activeId: activeTimelineId,
559
- onNavigate: handleNavigate
560
- },
561
- root.id
562
- )),
563
- orphans.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "mt-3 pt-3 border-t border-border-subtle", children: [
564
- /* @__PURE__ */ jsx4("div", { className: "px-2 py-1 text-[9px] font-bold text-text-muted uppercase tracking-widest", children: "Sin Referencia" }),
565
- orphans.map((node) => /* @__PURE__ */ jsx4(
566
- TreeNodeItem,
567
- {
568
- node,
569
- activeId: activeTimelineId,
570
- onNavigate: handleNavigate
571
- },
572
- node.id
573
- ))
574
- ] })
575
- ] })
576
- ]
577
- }
578
- )
579
- ] }) });
580
- };
581
-
582
- // src/components/controls/TimelineTape.tsx
583
- import React5 from "react";
584
- import { Pause, Play, RotateCcw } from "lucide-react";
585
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
586
- var TimelineTape = React5.memo(function TimelineTape2({
587
- currentTime,
588
- seekTime,
589
- playbackSpeed,
590
- setPlaybackSpeed,
591
- isPlaying,
592
- setIsPlaying,
593
- handleResetPlayground,
594
- currentTimeline
595
- }) {
596
- return /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3 w-full bg-surface-overlay p-2 rounded-xl border border-border-subtle", children: [
597
- /* @__PURE__ */ jsxs5("span", { className: "text-xs font-mono text-text-secondary w-10 text-right", children: [
598
- currentTime.toFixed(1),
599
- "s"
600
- ] }),
601
- /* @__PURE__ */ jsxs5("div", { className: "relative flex-1 h-6 flex items-center group cursor-pointer", children: [
602
- /* @__PURE__ */ jsx5("div", { className: "absolute w-full h-1.5 bg-surface-glass rounded-full overflow-hidden group-hover:bg-surface-glass transition-colors", children: /* @__PURE__ */ jsx5("div", { className: "h-full bg-purple-500/50 group-hover:bg-purple-400 transition-colors", style: { width: `${currentTime / Math.max(currentTimeline.duration, 1) * 100}%` } }) }),
603
- /* @__PURE__ */ jsx5("input", { type: "range", min: "0", max: currentTimeline.duration, step: "0.1", value: currentTime, onChange: (e) => seekTime(parseFloat(e.target.value)), className: "absolute w-full h-full opacity-0 cursor-pointer z-20" }),
604
- currentTimeline.keyframes.map((kf, i) => /* @__PURE__ */ jsx5(
605
- "div",
606
- {
607
- className: "absolute top-1/2 -translate-y-1/2 w-2 h-2 rounded-full border border-[#1e1e1f] z-10 pointer-events-none transition-all group-hover:scale-125",
608
- style: { left: `calc(${kf.t / currentTimeline.duration * 100}% - 4px)`, backgroundColor: currentTime >= kf.t ? "#a855f7" : "#52525b" }
609
- },
610
- i
611
- ))
612
- ] }),
613
- /* @__PURE__ */ jsxs5("span", { className: "text-xs font-mono text-text-muted w-10", children: [
614
- currentTimeline.duration.toFixed(1),
615
- "s"
616
- ] }),
617
- /* @__PURE__ */ jsxs5("div", { className: "flex gap-1 border-l border-border-default pl-2 ml-1", children: [
618
- /* @__PURE__ */ jsx5("button", { onClick: () => setIsPlaying(!isPlaying), className: "w-8 h-8 rounded-lg bg-surface-glass hover:bg-purple-500/20 text-text-primary flex items-center justify-center transition-colors", children: isPlaying ? /* @__PURE__ */ jsx5(Pause, { size: 14 }) : /* @__PURE__ */ jsx5(Play, { size: 14, className: "ml-0.5" }) }),
619
- /* @__PURE__ */ jsxs5("button", { onClick: () => setPlaybackSpeed(playbackSpeed === 1 ? 2 : 1), className: "w-8 h-8 rounded-lg hover:bg-surface-glass text-text-secondary flex items-center justify-center transition-colors text-[10px] font-bold", children: [
620
- playbackSpeed,
621
- "x"
622
- ] }),
623
- /* @__PURE__ */ jsx5("button", { onClick: handleResetPlayground, className: "w-8 h-8 rounded-lg hover:bg-surface-glass text-text-secondary flex items-center justify-center transition-colors", title: "Rewind & Reset Engine", children: /* @__PURE__ */ jsx5(RotateCcw, { size: 14 }) })
624
- ] })
625
- ] });
626
- });
627
-
628
- // src/components/controls/CreatorInputBar.tsx
629
- import React6, { useState as useState4, useMemo as useMemo2, useCallback as useCallback2, useEffect as useEffect3 } from "react";
630
- import { AnimatePresence as AnimatePresence4, motion as motion4 } from "motion/react";
631
- import {
632
- Wand2,
633
- Clock,
634
- Mic,
635
- MessageCircle,
636
- Terminal,
637
- Network as Network4,
638
- Variable,
639
- Download,
640
- Layout,
641
- HelpCircle,
642
- Trash2 as Trash22
643
- } from "lucide-react";
644
- import { useTimelineStore as useTimelineStore3, useEngineStore } from "@decido/engine";
645
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
646
- var CREATOR_COMMANDS = [
647
- { cmd: "/add", args: "<type> [label]", desc: "Agregar nodo", icon: /* @__PURE__ */ jsx6(MessageCircle, { size: 14, className: "text-blue-400" }) },
648
- { cmd: "/shell", args: "<action> [label]", desc: "Nodo Shell", icon: /* @__PURE__ */ jsx6(Terminal, { size: 14, className: "text-teal-400" }) },
649
- { cmd: "/connect", args: "<nodeA> <nodeB>", desc: "Conectar nodos", icon: /* @__PURE__ */ jsx6(Network4, { size: 14, className: "text-violet-400" }) },
650
- { cmd: "/var", args: "<name> <value>", desc: "Definir variable", icon: /* @__PURE__ */ jsx6(Variable, { size: 14, className: "text-emerald-400" }) },
651
- { cmd: "/export", args: "", desc: "Exportar DSD", icon: /* @__PURE__ */ jsx6(Download, { size: 14, className: "text-cyan-400" }) },
652
- { cmd: "/ui", args: "[componentId]", desc: "UI Components", icon: /* @__PURE__ */ jsx6(Layout, { size: 14, className: "text-pink-400" }) },
653
- { cmd: "/clear", args: "", desc: "Limpiar nodos", icon: /* @__PURE__ */ jsx6(Trash22, { size: 14, className: "text-red-400" }) },
654
- { cmd: "/help", args: "", desc: "Ayuda", icon: /* @__PURE__ */ jsx6(HelpCircle, { size: 14, className: "text-text-secondary" }) }
655
- ];
656
- var CreatorInputBar = React6.memo(function CreatorInputBar2({
657
- inputValue = "",
658
- setInputValue,
659
- isGenerating = false,
660
- handleGeneratePlayground,
661
- setIsVoiceActive,
662
- prototypeBrand = "",
663
- handleExport
664
- }) {
665
- const [slashOpen, setSlashOpen] = useState4(false);
666
- const [slashIdx, setSlashIdx] = useState4(0);
667
- const filteredCmds = useMemo2(() => {
668
- if (!inputValue.startsWith("/")) return [];
669
- const q = inputValue.split(" ")[0].toLowerCase();
670
- return CREATOR_COMMANDS.filter((c) => c.cmd.startsWith(q));
671
- }, [inputValue]);
672
- useEffect3(() => {
673
- setSlashOpen(inputValue.startsWith("/") && filteredCmds.length > 0);
674
- setSlashIdx(0);
675
- }, [inputValue, filteredCmds.length]);
676
- const handleCreatorSlash = useCallback2(() => {
677
- const parts = inputValue.trim().split(/\s+/);
678
- const cmd = parts[0]?.toLowerCase();
679
- if (cmd === "/add" || cmd === "/shell") {
680
- const rawType = parts[1] || (cmd === "/shell" ? "config" : "dialogue");
681
- const label = parts.slice(2).join(" ") || rawType;
682
- const trackMap = {
683
- "dialogue": "dialogue",
684
- "logic": "logic",
685
- "ui": "ui",
686
- "render": "render",
687
- "subflow": "subflow",
688
- "entry": "entry",
689
- "return": "return",
690
- "trigger": "trigger",
691
- "condition": "condition",
692
- "set": "set",
693
- "registerButton": "shell:registerButton",
694
- "activitybar": "shell:registerButton",
695
- "registerTab": "shell:registerTab",
696
- "tab": "shell:registerTab",
697
- "registerPanel": "shell:registerPanel",
698
- "panel": "shell:registerPanel",
699
- "registerSidebar": "shell:registerSidebar",
700
- "sidebar": "shell:registerSidebar",
701
- "registerStatusBar": "shell:registerStatusBar",
702
- "statusbar": "shell:registerStatusBar",
703
- "registerTopBar": "shell:registerTopBar",
704
- "topbar": "shell:registerTopBar",
705
- "registerAction": "shell:registerAction",
706
- "command": "shell:registerAction",
707
- "toggleSidebar": "shell:toggleSidebar",
708
- "togglePanel": "shell:togglePanel",
709
- "splitPane": "shell:splitPane",
710
- "focusTab": "shell:focusTab",
711
- "closeTab": "shell:closeTab",
712
- "setTheme": "shell:setTheme",
713
- "theme": "shell:setTheme",
714
- "applyPalette": "shell:applyPalette",
715
- "palette": "shell:applyPalette",
716
- "updateToken": "shell:updateToken",
717
- "token": "shell:updateToken",
718
- "notification": "shell:notification",
719
- "notify": "shell:notification",
720
- "modal": "shell:modal",
721
- "execCommand": "shell:execCommand",
722
- "exec": "shell:execCommand",
723
- "shortcut": "shell:shortcut",
724
- "emitEvent": "shell:emitEvent",
725
- "emit": "shell:emitEvent",
726
- "config": "shell:config",
727
- "shellConfig": "shell:config"
728
- };
729
- const shellActionMap = {
730
- "shell:registerButton": "registerActivityBarItem",
731
- "shell:registerTab": "openTab",
732
- "shell:registerPanel": "registerPanelView",
733
- "shell:registerSidebar": "registerSidebarView",
734
- "shell:registerStatusBar": "registerStatusBarItem",
735
- "shell:registerTopBar": "registerTopBarItem",
736
- "shell:registerAction": "registerCommand",
737
- "shell:toggleSidebar": "toggleSidebar",
738
- "shell:togglePanel": "togglePanel",
739
- "shell:splitPane": "splitPane",
740
- "shell:focusTab": "focusTab",
741
- "shell:closeTab": "closeTab",
742
- "shell:setTheme": "setTheme",
743
- "shell:applyPalette": "applyPalette",
744
- "shell:updateToken": "updateToken",
745
- "shell:notification": "showNotification",
746
- "shell:modal": "showModal",
747
- "shell:execCommand": "executeCommand",
748
- "shell:shortcut": "registerShortcut",
749
- "shell:emitEvent": "emitEvent",
750
- "shell:config": "shellConfig"
751
- };
752
- let resolvedType = rawType;
753
- if (cmd === "/shell" && !rawType.startsWith("shell:")) resolvedType = trackMap[rawType] || `shell:${rawType}`;
754
- else resolvedType = trackMap[rawType] || rawType;
755
- const newKf = { t: Date.now(), track: resolvedType, position: { x: 200 + Math.random() * 600, y: 100 + Math.random() * 400 }, label, state: label };
756
- if (resolvedType === "dialogue") {
757
- newKf.speech = label;
758
- newKf.actorId = "assistant";
759
- } else if (resolvedType === "subflow") {
760
- newKf.targetBlueprintId = "";
761
- }
762
- if (resolvedType.startsWith("shell:")) {
763
- newKf.track = "shell";
764
- newKf.type = resolvedType;
765
- newKf.shellAction = shellActionMap[resolvedType] || resolvedType.replace("shell:", "");
766
- }
767
- useTimelineStore3.getState().addKeyframe(prototypeBrand, newKf);
768
- usePlaygroundStore.getState().addLog(`\u2705 Nodo "${label}" (${resolvedType}) agregado al canvas`, "success");
769
- } else if (cmd === "/var") {
770
- const varName = parts[1];
771
- const varValue = parts.slice(2).join(" ");
772
- if (varName) {
773
- useEngineStore.getState().setVariable(varName, varValue || "");
774
- usePlaygroundStore.getState().addLog(`\u{1F4CA} Variable "${varName}" = "${varValue}"`, "info");
775
- }
776
- } else if (cmd === "/export") {
777
- handleExport();
778
- } else if (cmd === "/ui") {
779
- const comps = Object.values(useUIComponentStore.getState().components);
780
- if (parts[1]) {
781
- const comp = comps.find((c) => c.id === parts[1] || c.name.toLowerCase().includes(parts[1].toLowerCase()));
782
- if (comp) {
783
- useTimelineStore3.getState().addKeyframe(prototypeBrand, { t: Date.now(), track: "render", position: { x: 200 + Math.random() * 600, y: 100 + Math.random() * 400 }, label: comp.name, state: comp.name, uiSchema: comp.schema, autoComplete: false });
784
- usePlaygroundStore.getState().addLog(`\u{1F5BC}\uFE0F UI Component "${comp.name}" agregado`, "success");
785
- }
786
- } else {
787
- usePlaygroundStore.getState().addLog(`\u{1F3A8} UI Components:
788
- ${comps.map((c) => `\u2022 ${c.name} (${c.source})`).join("\n") || "Ninguno disponible"}`, "info");
789
- }
790
- } else if (cmd === "/clear") {
791
- useTimelineStore3.getState().setDynamicTimelines((prev) => ({ ...prev, [prototypeBrand]: { ...prev[prototypeBrand], keyframes: [], edges: [] } }));
792
- usePlaygroundStore.getState().addLog("\u{1F5D1}\uFE0F Canvas limpiado", "warning");
793
- } else if (cmd === "/help") {
794
- usePlaygroundStore.getState().addLog(`\u{1F4CB} Comandos Creator:
795
- ${CREATOR_COMMANDS.map((c) => `${c.cmd} ${c.args} \u2014 ${c.desc}`).join("\n")}`, "info");
796
- } else {
797
- return false;
798
- }
799
- setInputValue("");
800
- return true;
801
- }, [inputValue, prototypeBrand, handleExport, setInputValue]);
802
- const handleCreatorSubmit = useCallback2(() => {
803
- inputValue.startsWith("/") ? handleCreatorSlash() : handleGeneratePlayground();
804
- }, [inputValue, handleCreatorSlash, handleGeneratePlayground]);
805
- const handleCreatorKeyDown = useCallback2((e) => {
806
- if (slashOpen) {
807
- if (e.key === "ArrowDown") {
808
- e.preventDefault();
809
- setSlashIdx((i) => Math.min(i + 1, filteredCmds.length - 1));
810
- } else if (e.key === "ArrowUp") {
811
- e.preventDefault();
812
- setSlashIdx((i) => Math.max(i - 1, 0));
813
- } else if (e.key === "Enter" && filteredCmds[slashIdx]) {
814
- e.preventDefault();
815
- setInputValue(filteredCmds[slashIdx].cmd + " ");
816
- setSlashOpen(false);
817
- return;
818
- } else if (e.key === "Escape") {
819
- setSlashOpen(false);
820
- return;
821
- }
822
- }
823
- if (e.key === "Enter" && !slashOpen && !isGenerating) handleCreatorSubmit();
824
- }, [slashOpen, slashIdx, filteredCmds, isGenerating, handleCreatorSubmit, setInputValue]);
825
- return /* @__PURE__ */ jsxs6("div", { className: "relative", children: [
826
- /* @__PURE__ */ jsx6(AnimatePresence4, { children: slashOpen && /* @__PURE__ */ jsxs6(motion4.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: 8 }, className: "absolute bottom-full left-0 mb-2 w-full bg-surface-secondary/95 backdrop-blur-xl border border-border-default rounded-xl shadow-2xl shadow-black/50 overflow-hidden z-50", children: [
827
- /* @__PURE__ */ jsx6("div", { className: "px-3 py-1.5 border-b border-border-subtle", children: /* @__PURE__ */ jsx6("span", { className: "text-[9px] font-bold text-text-muted uppercase tracking-widest", children: "/ Comandos Creator" }) }),
828
- /* @__PURE__ */ jsx6("div", { className: "max-h-[200px] overflow-y-auto py-1", children: filteredCmds.map((c, i) => /* @__PURE__ */ jsxs6("button", { onClick: () => {
829
- setInputValue(c.cmd + " ");
830
- setSlashOpen(false);
831
- }, className: `w-full flex items-center gap-2.5 px-3 py-2 text-xs transition-colors ${i === slashIdx ? "bg-purple-500/15 text-text-primary" : "text-text-secondary hover:text-text-primary hover:bg-surface-glass"}`, children: [
832
- c.icon,
833
- /* @__PURE__ */ jsx6("span", { className: "font-mono font-bold text-purple-300", children: c.cmd }),
834
- /* @__PURE__ */ jsx6("span", { className: "text-text-muted font-mono", children: c.args }),
835
- /* @__PURE__ */ jsx6("span", { className: "ml-auto text-[10px] text-text-muted truncate max-w-[150px]", children: c.desc })
836
- ] }, c.cmd)) })
837
- ] }) }),
838
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 sm:gap-3 w-full bg-surface-overlay p-1.5 sm:p-2 rounded-xl border border-border-subtle focus-within:border-cyan-500/30 transition-colors mt-2", children: [
839
- /* @__PURE__ */ jsx6(Wand2, { size: 20, className: "text-purple-400 ml-2" }),
840
- /* @__PURE__ */ jsx6("input", { type: "text", placeholder: "Escribe / para comandos, o un prompt AI...", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: handleCreatorKeyDown, className: "w-full bg-transparent text-text-primary placeholder-text-muted outline-hidden text-sm h-10" }),
841
- /* @__PURE__ */ jsx6("button", { onClick: handleCreatorSubmit, disabled: isGenerating, className: "px-3 sm:px-4 py-2 rounded-lg font-bold text-xs sm:text-sm transition-colors disabled:opacity-50 min-w-[70px] sm:min-w-[100px] flex justify-center items-center bg-purple-500/20 text-purple-400 hover:bg-purple-500/30", children: isGenerating ? /* @__PURE__ */ jsx6(Clock, { size: 16, className: "animate-spin" }) : "Enviar" }),
842
- /* @__PURE__ */ jsx6("div", { className: "w-px h-6 bg-surface-glass mx-1" }),
843
- /* @__PURE__ */ jsx6("button", { onClick: () => setIsVoiceActive(true), className: "w-10 h-10 rounded-full flex items-center justify-center border transition-all shadow-[0_0_15px_rgba(6,182,212,0.15)] text-purple-400 bg-purple-500/10 border-purple-500/20 hover:bg-purple-500/20", children: /* @__PURE__ */ jsx6(Mic, { size: 18 }) })
844
- ] })
845
- ] });
846
- });
847
-
848
- // src/components/controls/OSToolbar.tsx
849
- import React7 from "react";
850
- import {
851
- Layers,
852
- Terminal as Terminal2,
853
- Volume2,
854
- VolumeX,
855
- Minimize,
856
- Maximize,
857
- FolderOpen as FolderOpen2,
858
- TreePine as TreePine2
859
- } from "lucide-react";
860
- import { useTimelineStore as useTimelineStore4 } from "@decido/engine";
861
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
862
- var OSToolbar = React7.memo(function OSToolbar2({
863
- dynamicTimelines,
864
- prototypeBrand,
865
- showCanvas,
866
- setShowCanvas,
867
- isTerminalOpen,
868
- setIsTerminalOpen,
869
- isMuted,
870
- setIsMuted,
871
- selectedVoice,
872
- setSelectedVoice,
873
- isFullscreen,
874
- toggleFullscreen,
875
- handleExport,
876
- config,
877
- isBlueprintManagerOpen,
878
- setIsBlueprintManagerOpen,
879
- isTreeOpen,
880
- setIsTreeOpen
881
- }) {
882
- return /* @__PURE__ */ jsxs7("div", { className: "flex w-full overflow-x-auto custom-scrollbar pb-2 sm:pb-3 pt-1 mt-1 items-center justify-between gap-2 sm:gap-4", children: [
883
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1.5 sm:gap-2 min-w-max", children: [
884
- /* @__PURE__ */ jsxs7("button", { onClick: () => setIsBlueprintManagerOpen(!isBlueprintManagerOpen), className: "bg-surface-secondary hover:bg-surface-tertiary border border-border-default rounded-lg text-xs font-medium text-text-primary px-3 py-2 outline-hidden cursor-pointer hover:border-cyan-400/50 transition-colors flex items-center gap-2", children: [
885
- /* @__PURE__ */ jsx7(FolderOpen2, { size: 14, className: "text-cyan-400" }),
886
- /* @__PURE__ */ jsx7("span", { className: "max-w-[120px] truncate", children: dynamicTimelines[prototypeBrand]?.name || prototypeBrand }),
887
- /* @__PURE__ */ jsx7("span", { className: "text-[10px] text-text-muted font-mono", children: dynamicTimelines[prototypeBrand]?.keyframes?.length || 0 })
888
- ] }),
889
- /* @__PURE__ */ jsx7("button", { onClick: () => setIsTreeOpen(!isTreeOpen), className: "bg-surface-secondary hover:bg-surface-tertiary border border-border-default rounded-lg text-xs font-medium text-text-primary px-2.5 py-2 outline-hidden cursor-pointer hover:border-emerald-400/50 transition-colors flex items-center gap-1.5", title: "Jerarqu\xEDa de dependencias", children: /* @__PURE__ */ jsx7(TreePine2, { size: 14, className: "text-emerald-400" }) }),
890
- /* @__PURE__ */ jsx7("div", { className: "w-px h-6 bg-surface-glass mx-1" }),
891
- /* @__PURE__ */ jsxs7("button", { onClick: () => setShowCanvas(!showCanvas), className: `px-3 py-2 rounded-lg transition-all text-xs font-medium flex items-center gap-2 shrink-0 ${showCanvas ? "text-text-primary bg-surface-glass" : "text-text-secondary hover:text-cyan-400 hover:bg-surface-glass border border-transparent"}`, title: "Panel 3D / Diagrama", children: [
892
- /* @__PURE__ */ jsx7(Layers, { size: 14 }),
893
- " ",
894
- /* @__PURE__ */ jsx7("span", { className: "hidden sm:inline", children: "Visualizador" })
895
- ] }),
896
- /* @__PURE__ */ jsxs7("button", { onClick: () => setIsTerminalOpen(!isTerminalOpen), className: `px-3 py-2 rounded-lg transition-all text-xs font-medium flex items-center gap-2 shrink-0 ${isTerminalOpen ? "text-text-primary bg-surface-glass" : "text-text-secondary hover:text-purple-400 hover:bg-surface-glass border border-transparent"}`, title: "Panel de Logs", children: [
897
- /* @__PURE__ */ jsx7(Terminal2, { size: 14 }),
898
- " ",
899
- /* @__PURE__ */ jsx7("span", { className: "hidden sm:inline", children: "Logs OS" })
900
- ] }),
901
- /* @__PURE__ */ jsx7("div", { className: "w-px h-6 bg-surface-glass mx-1" }),
902
- /* @__PURE__ */ jsx7("button", { onClick: () => setIsMuted(!isMuted), className: `px-3 py-2 rounded-lg transition-all text-xs font-medium flex items-center justify-center shrink-0 ${!isMuted ? "text-text-primary bg-surface-glass" : "text-text-muted hover:text-red-400 hover:bg-surface-glass border border-transparent"}`, title: isMuted ? "Activar Audio" : "Silenciar", children: !isMuted ? /* @__PURE__ */ jsx7(Volume2, { size: 16 }) : /* @__PURE__ */ jsx7(VolumeX, { size: 16 }) }),
903
- /* @__PURE__ */ jsx7("select", { value: selectedVoice, onChange: (e) => setSelectedVoice(e.target.value), className: "bg-surface-secondary hover:bg-surface-tertiary border border-border-default rounded-lg text-xs font-medium text-text-primary px-3 py-2 outline-hidden cursor-pointer focus:border-cyan-400 transition-colors", children: config?.voices?.map((v) => /* @__PURE__ */ jsxs7("option", { value: v, children: [
904
- "Voz: ",
905
- v
906
- ] }, v)) }),
907
- /* @__PURE__ */ jsx7("button", { onClick: toggleFullscreen, className: "px-3 py-2 rounded-lg transition-all text-xs font-medium flex items-center justify-center shrink-0 text-text-muted hover:text-text-primary hover:bg-surface-glass border border-transparent", title: "Pantalla Completa", children: isFullscreen ? /* @__PURE__ */ jsx7(Minimize, { size: 16 }) : /* @__PURE__ */ jsx7(Maximize, { size: 16 }) })
908
- ] }),
909
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 sm:gap-3 shrink-0 pr-1", children: [
910
- /* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 shrink-0 px-4 py-2 rounded-xl font-medium text-sm transition-colors border bg-cyan-500/10 text-cyan-500 hover:bg-cyan-500/20 border-transparent cursor-pointer", title: "Importar DSD", children: [
911
- /* @__PURE__ */ jsx7(Layers, { size: 16 }),
912
- " ",
913
- /* @__PURE__ */ jsx7("span", { className: "hidden sm:inline", children: "Importar DSD" }),
914
- /* @__PURE__ */ jsx7("input", { type: "file", accept: ".dsd,.json", className: "hidden", onChange: (e) => {
915
- const file = e.target.files?.[0];
916
- if (!file) return;
917
- const reader = new FileReader();
918
- reader.onload = (ev) => {
919
- try {
920
- const json = JSON.parse(ev.target?.result);
921
- if (json.format === "decido-standard-document" && json.blueprint) {
922
- useTimelineStore4.getState().setDynamicTimelines((prev) => ({ ...prev, [json.brand || prototypeBrand]: json.blueprint }));
923
- usePlaygroundStore.getState().addLog(`Se ha importado el blueprint de ${json.brand} (${json.blueprint.keyframes.length} nodos)`, "success");
924
- } else {
925
- usePlaygroundStore.getState().addLog("El archivo no es un DSD v\xE1lido.", "error");
926
- }
927
- } catch {
928
- usePlaygroundStore.getState().addLog("Error al parsear archivo DSD.", "error");
929
- }
930
- };
931
- reader.readAsText(file);
932
- e.target.value = "";
933
- } })
934
- ] }),
935
- /* @__PURE__ */ jsxs7("button", { onClick: handleExport, className: "flex items-center gap-2 shrink-0 px-4 py-2 rounded-xl font-medium text-sm transition-colors border bg-purple-500/10 text-purple-400 hover:bg-purple-500/20 border-transparent", title: "Exportar DSD", children: [
936
- /* @__PURE__ */ jsx7(Layers, { size: 16 }),
937
- " ",
938
- /* @__PURE__ */ jsx7("span", { className: "hidden sm:inline", children: "Exportar DSD" })
939
- ] })
940
- ] })
941
- ] });
942
- });
943
-
944
- // src/components/playground/PlaygroundBottomControls.tsx
945
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
946
- var PlaygroundBottomControls = React8.memo(function PlaygroundBottomControls2({
947
- step,
948
- isVoiceActive,
949
- setIsVoiceActive,
950
- currentTime,
951
- seekTime,
952
- playbackSpeed,
953
- setPlaybackSpeed,
954
- isPlaying,
955
- setIsPlaying,
956
- handleResetPlayground,
957
- dynamicTimelines,
958
- prototypeBrand,
959
- setPrototypeBrand,
960
- inputValue,
961
- setInputValue,
962
- isGenerating,
963
- handleGeneratePlayground,
964
- showCanvas,
965
- setShowCanvas,
966
- isTerminalOpen,
967
- setIsTerminalOpen,
968
- isMuted,
969
- setIsMuted,
970
- selectedVoice,
971
- setSelectedVoice,
972
- isFullscreen,
973
- toggleFullscreen,
974
- handleExport,
975
- dynamicPersona,
976
- setSuggestions,
977
- isCreatorMode,
978
- VoiceWidget,
979
- config
980
- }) {
981
- if (step === "action" || step === "profile") return null;
982
- if (!isCreatorMode && !isVoiceActive) return null;
983
- const [isBlueprintManagerOpen, setIsBlueprintManagerOpen] = useState5(false);
984
- const [isTreeOpen, setIsTreeOpen] = useState5(false);
985
- const currentTimeline = dynamicTimelines[prototypeBrand] || { duration: 10, keyframes: [] };
986
- return /* @__PURE__ */ jsxs8(Fragment4, { children: [
987
- /* @__PURE__ */ jsx8("div", { className: "absolute bottom-0 left-0 w-full p-3 sm:p-4 md:p-6 bg-linear-to-t from-surface-primary via-surface-primary to-transparent z-40 flex flex-col items-center", children: /* @__PURE__ */ jsx8(motion5.div, { layout: true, className: `w-full max-w-4xl relative ${isVoiceActive ? "h-64" : ""}`, children: !isVoiceActive ? /* @__PURE__ */ jsxs8("div", { className: `bg-surface-tertiary/90 border border-border-default rounded-xl sm:rounded-2xl shadow-xl backdrop-blur-md p-3 sm:p-4 md:p-5 flex flex-col gap-3 sm:gap-4 transition-all ${isCreatorMode ? "border-purple-500/30" : ""}`, children: [
988
- isCreatorMode && /* @__PURE__ */ jsx8(
989
- TimelineTape,
990
- {
991
- currentTime,
992
- seekTime,
993
- playbackSpeed,
994
- setPlaybackSpeed,
995
- isPlaying,
996
- setIsPlaying,
997
- handleResetPlayground,
998
- currentTimeline
999
- }
1000
- ),
1001
- isCreatorMode && /* @__PURE__ */ jsxs8(Fragment4, { children: [
1002
- /* @__PURE__ */ jsx8(
1003
- CreatorInputBar,
1004
- {
1005
- inputValue,
1006
- setInputValue,
1007
- isGenerating,
1008
- handleGeneratePlayground,
1009
- setIsVoiceActive,
1010
- prototypeBrand,
1011
- handleExport
1012
- }
1013
- ),
1014
- /* @__PURE__ */ jsx8(
1015
- OSToolbar,
1016
- {
1017
- dynamicTimelines,
1018
- prototypeBrand,
1019
- showCanvas,
1020
- setShowCanvas,
1021
- isTerminalOpen,
1022
- setIsTerminalOpen,
1023
- isMuted,
1024
- setIsMuted,
1025
- selectedVoice,
1026
- setSelectedVoice,
1027
- isFullscreen,
1028
- toggleFullscreen,
1029
- handleExport,
1030
- config,
1031
- isBlueprintManagerOpen,
1032
- setIsBlueprintManagerOpen,
1033
- isTreeOpen,
1034
- setIsTreeOpen
1035
- }
1036
- )
1037
- ] })
1038
- ] }) : /* @__PURE__ */ jsxs8("div", { className: "flex flex-col w-full h-full relative p-2 bg-surface-tertiary border border-cyan-500/30 rounded-4xl shadow-lg", children: [
1039
- /* @__PURE__ */ jsxs8("div", { className: "flex w-full items-center justify-between mb-2", children: [
1040
- /* @__PURE__ */ jsx8("span", { className: "text-cyan-400 font-medium px-4 bg-linear-to-r from-cyan-400 via-purple-400 to-indigo-400 text-transparent bg-clip-text animate-pulse", children: "Capturando Audio de IA..." }),
1041
- /* @__PURE__ */ jsx8("button", { onClick: () => setIsVoiceActive(false), className: "w-8 h-8 rounded-full bg-surface-glass hover:bg-surface-glass flex items-center justify-center text-text-secondary hover:text-text-primary transition-colors", children: /* @__PURE__ */ jsx8(X3, { size: 16 }) })
1042
- ] }),
1043
- /* @__PURE__ */ jsx8("div", { className: "flex-1 w-full rounded-2xl overflow-hidden relative", children: VoiceWidget ? /* @__PURE__ */ jsx8(
1044
- VoiceWidget,
1045
- {
1046
- isCompact: true,
1047
- persona: dynamicPersona,
1048
- onSuggestionsUpdate: setSuggestions,
1049
- selectedVoice,
1050
- controls: { setPrototypeBrand, setShowCanvas, setIsPlaying, handleExport, setInputValue, handleGeneratePlayground }
1051
- }
1052
- ) : /* @__PURE__ */ jsx8("div", { className: "p-4 flex flex-col items-center justify-center h-full text-text-muted font-mono text-sm border border-border-subtle rounded-xl bg-surface-glass", children: "M\xF3dulo de Voz no proporcionado." }) })
1053
- ] }) }) }),
1054
- /* @__PURE__ */ jsx8(BlueprintManagerPanel, { isOpen: isBlueprintManagerOpen, onClose: () => setIsBlueprintManagerOpen(false) }),
1055
- /* @__PURE__ */ jsx8(DependencyTreePanel, { isOpen: isTreeOpen, onClose: () => setIsTreeOpen(false) })
1056
- ] });
1057
- });
1058
-
1059
- // src/components/shell/CenterComposite.tsx
1060
- import { useTimelineStore as useTimelineStore7 } from "@decido/engine";
1061
-
1062
- // src/hooks/usePlaygroundCommander.ts
1063
- import { useCallback as useCallback3, useEffect as useEffect4 } from "react";
1064
- import { useTimelineStore as useTimelineStore5 } from "@decido/engine";
1065
- import { useEngineStore as useEngineStore2 } from "@decido/engine";
1066
- import { useReactiveEngine } from "@decido/engine";
1067
- import { useAIDirector } from "@decido/engine";
1068
- import { AudioController } from "@decido/engine";
1069
- var usePlaygroundCommander = (aiProvider) => {
1070
- const prototypeBrand = usePlaygroundStore((state) => state.prototypeBrand);
1071
- const setInputValue = usePlaygroundStore((state) => state.setInputValue);
1072
- const addLog = usePlaygroundStore((state) => state.addLog);
1073
- const addChatMessage = usePlaygroundStore((state) => state.addChatMessage);
1074
- const timelines = useTimelineStore5((state) => state.timelines);
1075
- const { semanticMatch, isThinking } = useReactiveEngine(aiProvider);
1076
- const { generateKeyframeOnTheFly, generateTimeline, isGenerating } = useAIDirector(aiProvider);
1077
- const { emitEvent, isConnected } = useNetwork();
1078
- const currentBlueprint = timelines[prototypeBrand];
1079
- const lastEvent = useEngineStore2((state) => state.lastEvent);
1080
- useEffect4(() => {
1081
- const handleRenderPreview = (e) => {
1082
- const html = e.detail?.html;
1083
- if (html) {
1084
- const instanceId = `web-${Date.now()}`;
1085
- bridgeToMorph({
1086
- chatId: instanceId,
1087
- shellType: "nexusai-preview",
1088
- label: "Reporte en Vivo",
1089
- data: { html, css: "", javascript: "" }
1090
- });
1091
- usePlaygroundStore.getState().setIsSidebarOpen(false);
1092
- }
1093
- };
1094
- const handleSearchOrder = (e) => {
1095
- const query = e.detail?.query;
1096
- if (query) {
1097
- usePlaygroundStore.getState().setIsSidebarOpen(false);
1098
- }
1099
- };
1100
- window.addEventListener("studio:render-preview", handleRenderPreview);
1101
- window.addEventListener("studio:search-order", handleSearchOrder);
1102
- return () => {
1103
- window.removeEventListener("studio:render-preview", handleRenderPreview);
1104
- window.removeEventListener("studio:search-order", handleSearchOrder);
1105
- };
1106
- }, []);
1107
- const jumpToNode = useCallback3((nodeId) => {
1108
- if (!currentBlueprint) return;
1109
- const targetNode = currentBlueprint.keyframes.find((kf) => kf.id === nodeId);
1110
- if (targetNode) {
1111
- addLog(`Reaccionando a Nodo: ${targetNode.state || targetNode.id}`, "system");
1112
- useEngineStore2.getState().dispatchEvent({
1113
- type: "USER_INTENT_MATCHED",
1114
- targetNodeId: nodeId,
1115
- routeHandle: "success"
1116
- });
1117
- }
1118
- }, [currentBlueprint, addLog]);
1119
- const handleUserCommand = useCallback3(async (input) => {
1120
- if (!input || !input.trim()) return;
1121
- addLog(`Analizando intenci\xF3n: "${input}"`, "system");
1122
- AudioController.stop();
1123
- addChatMessage({ type: "text", sender: "user", text: input });
1124
- const selectedShell = localStorage.getItem("decido_selected_shell");
1125
- if (selectedShell === "nexusai-preview" || selectedShell === "iframe") {
1126
- addLog("\u{1F310} Web Preview mode \u2014 generando HTML directo...", "system");
1127
- addChatMessage({ type: "text", sender: "system", text: "\u{1F310} Generando dise\xF1o web..." });
1128
- const apiKey = localStorage.getItem("google_gemini_api_key") || window.__DECIDO__?._apiKey || import.meta.env?.VITE_GEMINI_API_KEY || "";
1129
- const model = localStorage.getItem("decido_gemini_model") || "gemini-3-flash-preview";
1130
- if (!apiKey) {
1131
- addChatMessage({ type: "error", sender: "system", text: "\u26A0\uFE0F No se encontr\xF3 API key de Gemini." });
1132
- return;
1133
- }
1134
- const activeInst = useMorphInstanceStore.getState().getActiveInstance();
1135
- const existingHtml = activeInst?.data?.html || "";
1136
- const existingCss = activeInst?.data?.css || "";
1137
- const existingJs = activeInst?.data?.javascript || "";
1138
- const hasContext = !!existingHtml.trim();
1139
- const selectedSelector = window.__DECIDO__?.selectedElementSelector || null;
1140
- const elementContext = selectedSelector ? `
1141
-
1142
- IMPORTANT: The user has SELECTED a specific element: \`${selectedSelector}\`. Focus your changes ONLY on this element and its children. Do not modify other parts of the page.` : "";
1143
- const systemPrompt = hasContext ? `You are an expert web developer. The user has an EXISTING web page and wants to MODIFY it.${elementContext}
1144
-
1145
- CURRENT HTML:
1146
- \`\`\`html
1147
- ${existingHtml}
1148
- \`\`\`
1149
-
1150
- CURRENT CSS:
1151
- \`\`\`css
1152
- ${existingCss}
1153
- \`\`\`
1154
-
1155
- CURRENT JavaScript:
1156
- \`\`\`javascript
1157
- ${existingJs}
1158
- \`\`\`
1159
-
1160
- Apply the user's requested changes to the EXISTING code above. Keep everything else intact.
1161
- IMPORTANT: DO NOT USE JSON. Instead, strictly format your response using these XML-like tags:
1162
- <decido-html>
1163
- <!-- your html here without <html> or <body> tags -->
1164
- </decido-html>
1165
- <decido-css>
1166
- /* your tailwind/custom css here */
1167
- </decido-css>
1168
- <decido-js>
1169
- // your javascript here
1170
- </decido-js>
1171
- Use Tailwind CSS classes. Do not include explanatory text outside the tags.` : `You are an expert web developer. Generate HTML, CSS, and JavaScript for the user's request.
1172
- IMPORTANT: DO NOT USE JSON. Instead, strictly format your response using these XML-like tags:
1173
- <decido-html>
1174
- <!-- your html here without <html> or <body> tags -->
1175
- </decido-html>
1176
- <decido-css>
1177
- /* your tailwind/custom css here */
1178
- </decido-css>
1179
- <decido-js>
1180
- // your javascript here
1181
- </decido-js>
1182
- Use Tailwind CSS classes for styling (it will be auto-loaded). Write modern, responsive, visually stunning code.
1183
- Use vibrant gradients, dark backgrounds (#0a0a0a, #1a1a2e), neon accent colors, smooth animations, and premium typography.
1184
- Do not include any explanatory text outside the tags.`;
1185
- try {
1186
- const chatId = usePlaygroundStore.getState().activeChatId || "_default";
1187
- const allMessages = usePlaygroundStore.getState().chatMessagesByInstance[chatId] || [];
1188
- const recentMsgs = allMessages.filter((m) => (m.sender === "user" || m.sender === "agent") && m.type === "text" && m.text?.trim()).slice(-10);
1189
- const contents = [];
1190
- for (const m of recentMsgs) {
1191
- const role = m.sender === "user" ? "user" : "model";
1192
- if (contents.length > 0 && contents[contents.length - 1].role === role) {
1193
- contents[contents.length - 1].parts[0].text += "\n" + (m.text || "");
1194
- } else {
1195
- contents.push({ role, parts: [{ text: m.text || "" }] });
1196
- }
1197
- }
1198
- if (contents.length === 0 || contents[contents.length - 1].role !== "user") {
1199
- contents.push({ role: "user", parts: [{ text: input }] });
1200
- }
1201
- const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
1202
- dlog.gemini(`POST ${model} | ${contents.length} turns | prompt: ${systemPrompt.length} chars`, { model, turnsCount: contents.length });
1203
- const _t0 = performance.now();
1204
- const res = await fetch(url, {
1205
- method: "POST",
1206
- headers: { "Content-Type": "application/json" },
1207
- body: JSON.stringify({
1208
- contents,
1209
- systemInstruction: { parts: [{ text: systemPrompt }] },
1210
- generationConfig: { temperature: 0.7, maxOutputTokens: 16384 }
1211
- })
1212
- });
1213
- const _dur = Math.round(performance.now() - _t0);
1214
- const data = await res.json();
1215
- const rawText = data?.candidates?.[0]?.content?.parts?.[0]?.text || "";
1216
- const tokenCount = data?.usageMetadata?.totalTokenCount || 0;
1217
- dlog.gemini(`Response ${res.status} | ${_dur}ms | ${tokenCount} tokens | ${rawText.length} chars`, { status: res.status, duration: _dur, tokens: tokenCount });
1218
- let code = { html: "", css: "", javascript: "" };
1219
- const htmlMatch = /<decido-html>([\s\S]*?)<\/decido-html>/i.exec(rawText);
1220
- const cssMatch = /<decido-css>([\s\S]*?)<\/decido-css>/i.exec(rawText);
1221
- const jsMatch = /<decido-js>([\s\S]*?)<\/decido-js>/i.exec(rawText);
1222
- if (htmlMatch || cssMatch || jsMatch) {
1223
- code.html = htmlMatch ? htmlMatch[1].trim() : "";
1224
- code.css = cssMatch ? cssMatch[1].trim() : "";
1225
- code.javascript = jsMatch ? jsMatch[1].trim() : "";
1226
- } else {
1227
- let jsonText = rawText.trim();
1228
- const jsonMatch = /```(?:json)?\s*([\s\S]*?)```/i.exec(rawText);
1229
- if (jsonMatch) {
1230
- jsonText = jsonMatch[1].trim();
1231
- } else {
1232
- jsonText = jsonText.replace(/^```json\s*/i, "").replace(/\s*```$/i, "").trim();
1233
- }
1234
- try {
1235
- code = JSON.parse(jsonText);
1236
- } catch (e) {
1237
- console.error("Web Preview Parse Error:", e, rawText);
1238
- code.html = `
1239
- <div class="fixed inset-0 flex flex-col items-center justify-center bg-gray-900 text-white p-6 text-center font-sans z-[9999]">
1240
- <div class="bg-red-500/10 border border-red-500 text-red-400 p-4 rounded-xl max-w-lg shadow-2xl">
1241
- <h3 class="font-bold text-lg mb-2">Error de Generaci\xF3n AI</h3>
1242
- <p class="text-sm opacity-80 mb-4">El modelo gener\xF3 c\xF3digo que no cumple con el formato XML o JSON estricto.</p>
1243
- <div class="text-xs text-left bg-black/50 p-2 rounded overflow-auto max-h-48 text-gray-300">
1244
- ${e.message}
1245
- </div>
1246
- </div>
1247
- </div>`;
1248
- }
1249
- }
1250
- let instanceId;
1251
- if (hasContext && activeInst) {
1252
- instanceId = activeInst.id;
1253
- useMorphInstanceStore.getState().upsertInstance({
1254
- id: instanceId,
1255
- sourceChatId: activeInst.sourceChatId,
1256
- shellType: "nexusai-preview",
1257
- label: activeInst.label,
1258
- data: { html: code.html, css: code.css, javascript: code.javascript }
1259
- });
1260
- } else {
1261
- instanceId = `web-${Date.now()}`;
1262
- bridgeToMorph({
1263
- chatId: instanceId,
1264
- shellType: "nexusai-preview",
1265
- label: input.slice(0, 40),
1266
- data: { html: code.html, css: code.css, javascript: code.javascript }
1267
- });
1268
- }
1269
- const verb = hasContext ? "\u{1F504} Dise\xF1o actualizado" : "\u2705 Dise\xF1o web generado";
1270
- addChatMessage({ type: "text", sender: "agent", text: `${verb}. Revisa el panel derecho.`, morphInstanceId: instanceId });
1271
- addLog(`\u{1F310} HTML ${hasContext ? "actualizado" : "generado"} en Web Preview`, "success");
1272
- } catch (err) {
1273
- dlog.gemini(`ERROR: ${err.message}`, { error: err.message }, "error");
1274
- addChatMessage({ type: "error", sender: "system", text: `Error generando HTML: ${err.message}` });
1275
- }
1276
- return;
1277
- } else if (selectedShell === "dataway-studio") {
1278
- addLog("Dataway Studio \u2014 generando M-Script...", "system");
1279
- const activeResource = localStorage.getItem("dataway_active_resource");
1280
- const activeConnector = localStorage.getItem("dataway_active_connector") || "google_sheets";
1281
- if (!activeResource) {
1282
- addChatMessage({ type: "error", sender: "system", text: "Debes seleccionar una tabla en el panel Recursos de Dataway primero." });
1283
- return;
1284
- }
1285
- const loadingId = `loading-${Date.now()}`;
1286
- addChatMessage({ id: loadingId, type: "text", sender: "system", text: "Analizando esquema y generando M-Script..." });
1287
- try {
1288
- const baseUrl = import.meta.env?.VITE_API_BASE_URL || "http://localhost:3001";
1289
- const res = await fetch(`${baseUrl}/api/dataway/copilot`, {
1290
- method: "POST",
1291
- headers: { "Content-Type": "application/json" },
1292
- body: JSON.stringify({
1293
- prompt: input,
1294
- connectorId: activeConnector,
1295
- resource: activeResource
1296
- })
1297
- });
1298
- if (!res.ok) {
1299
- const errData = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
1300
- throw new Error(errData.error || `Error del servidor HTTP ${res.status}`);
1301
- }
1302
- const data = await res.json();
1303
- if (data.script) {
1304
- const explanationText = data.explanation ? `**An\xE1lisis de la IA:** ${data.explanation}
1305
-
1306
- ` : "";
1307
- addChatMessage({
1308
- type: "text",
1309
- sender: "agent",
1310
- text: `${explanationText}Pipeline actualizado. Revisa el resultado:
1311
-
1312
- \`\`\`dataway
1313
- ${data.script}
1314
- \`\`\``
1315
- });
1316
- window.dispatchEvent(new CustomEvent("intent:dataway-generate-script", { detail: {
1317
- script: data.script,
1318
- resource: data.targetResource
1319
- } }));
1320
- } else {
1321
- addChatMessage({ type: "error", sender: "system", text: "El agente no retorn\xF3 c\xF3digo." });
1322
- }
1323
- } catch (err) {
1324
- addChatMessage({ type: "error", sender: "system", text: `Error de IA: ${err.message}` });
1325
- }
1326
- return;
1327
- }
1328
- if (!currentBlueprint) {
1329
- addLog(`No hay blueprint activo para evaluar.`, "error");
1330
- return;
1331
- }
1332
- const intentEnabled = typeof window !== "undefined" && window.__DECIDO__?.intentEnabled !== false;
1333
- const isEditRequest = intentEnabled && /agrega|suma|añade|conecta|modifica|cambia|elimina|borra|parchea|actualiza|inserta|mueve/i.test(input);
1334
- const isCreateRequest = intentEnabled && /grafo|flujo|nodos|complejo|historia|blueprint|escenario|onboarding|proceso|workflow|pipeline|crea\s+(un|una|el|la|los|las)\s|diseña|genera\s+(un|una)|construye|orquest|trigger|subflujo|set\s+variable|marque\s+user\.|shell|tema|theme|sidebar|panel|activity\s*bar|status\s*bar|top\s*bar|notificaci[oó]n|shortcut|atajo|layout|configura\s+(el|la|los|las)\s/i.test(input);
1335
- if (isCreateRequest || isEditRequest) {
1336
- addLog(`Intenci\xF3n compleja detectada. ${isEditRequest ? "Editando Grafo..." : "Construyendo Nuevo Grafo..."}`, "system");
1337
- const result = await generateTimeline(input, prototypeBrand, 15, isEditRequest, currentBlueprint);
1338
- if (result === true) {
1339
- addChatMessage({ type: "text", sender: "system", text: `Se ha modificado el grafo contextualmente seg\xFAn tus instrucciones.` });
1340
- } else if (result && result.keyframes && result.keyframes.length > 0) {
1341
- jumpToNode(result.keyframes[0].id);
1342
- addChatMessage({ type: "text", sender: "system", text: `Se ha creado un nuevo grafo.` });
1343
- } else {
1344
- addLog(`Fallo al generar el Blueprint con IA.`, "error");
1345
- }
1346
- return;
1347
- }
1348
- const matchResult = intentEnabled ? await semanticMatch(input, currentBlueprint) : null;
1349
- if (matchResult && matchResult !== "GENERATE" && typeof matchResult === "string") {
1350
- jumpToNode(matchResult);
1351
- } else if (matchResult && typeof matchResult === "object" && matchResult.intent === "UI_ACTION") {
1352
- addLog(`\u{1F3A8} UI_ACTION detectada. Montando artefacto: ${matchResult.artifact}`, "system");
1353
- usePlaygroundStore.getState().setActiveArtifactData({
1354
- artifact: matchResult.artifact,
1355
- ...matchResult.payload
1356
- });
1357
- const uiInstanceId = `ui-${matchResult.artifact}-${Date.now()}`;
1358
- bridgeToMorph({
1359
- chatId: uiInstanceId,
1360
- shellType: "nexusai-preview",
1361
- label: matchResult.artifact || "Visualizaci\xF3n",
1362
- data: { artifact: matchResult.artifact, ...matchResult.payload }
1363
- });
1364
- addChatMessage({ type: "text", sender: "agent", text: "He preparado la visualizaci\xF3n solicitada en el panel derecho.", morphInstanceId: uiInstanceId });
1365
- } else if (matchResult && typeof matchResult === "object" && matchResult.intent === "MCP_ACTION") {
1366
- addLog(`Manejando intenci\xF3n MCP_ACTION: ${matchResult.tool}`, "system");
1367
- useEngineStore2.getState().dispatchEvent({
1368
- type: "MCP_EXECUTION_REQUESTED",
1369
- payload: { tool: matchResult.tool, parameters: matchResult.parameters }
1370
- });
1371
- addChatMessage({ type: "text", sender: "system", text: `Ejecutando herramienta externa: ${matchResult.tool}` });
1372
- } else if (matchResult && typeof matchResult === "object" && matchResult.intent === "UI_COMPONENT") {
1373
- addLog(`\u{1F3A8} UI_COMPONENT generado por IA: ${matchResult.name}`, "system");
1374
- const componentId = `ai_${Date.now()}`;
1375
- useUIComponentStore.getState().addComponent({
1376
- id: componentId,
1377
- name: matchResult.name || "AI Component",
1378
- category: matchResult.category || "custom",
1379
- schema: matchResult.schema,
1380
- dataContract: { inputs: {}, outputs: {} },
1381
- tags: matchResult.tags || ["ai-generated"],
1382
- source: "ai-generated",
1383
- createdAt: Date.now(),
1384
- updatedAt: Date.now()
1385
- });
1386
- bridgeToMorph({
1387
- chatId: `comp-${componentId}`,
1388
- shellType: "nexusai-preview",
1389
- label: matchResult.name || "AI Component",
1390
- data: { html: matchResult.schema, schema: matchResult.schema },
1391
- artifact: {
1392
- id: componentId,
1393
- name: matchResult.name || "AI Component",
1394
- data: { html: matchResult.schema, schema: matchResult.schema }
1395
- }
1396
- });
1397
- addChatMessage({
1398
- type: "ui-artifact",
1399
- sender: "agent",
1400
- text: `\u{1F3A8} ${matchResult.name}`,
1401
- actorId: "system",
1402
- uiSchema: JSON.stringify(matchResult.schema),
1403
- morphInstanceId: `comp-${componentId}`
1404
- });
1405
- addChatMessage({ type: "text", sender: "system", text: `Componente "${matchResult.name}" guardado en la biblioteca UI. Accede con /ui.`, morphInstanceId: `comp-${componentId}` });
1406
- } else {
1407
- addLog(`Intenci\xF3n reactiva detectada. Inventando nodo en vivo...`, "system");
1408
- const newKf = await generateKeyframeOnTheFly(input, prototypeBrand, currentBlueprint);
1409
- if (newKf) {
1410
- jumpToNode(newKf.id);
1411
- } else {
1412
- addLog(`Fallo al generar morfolog\xEDa reactiva.`, "error");
1413
- }
1414
- }
1415
- }, [currentBlueprint, prototypeBrand, semanticMatch, generateKeyframeOnTheFly, generateTimeline, jumpToNode, addLog, addChatMessage, emitEvent, isConnected]);
1416
- const handleGeneratePlayground = useCallback3(async (eOrOverride) => {
1417
- let promptOverride = typeof eOrOverride === "string" ? eOrOverride : void 0;
1418
- if (eOrOverride && typeof eOrOverride.preventDefault === "function") {
1419
- eOrOverride.preventDefault();
1420
- }
1421
- let currentInput = usePlaygroundStore.getState().inputValue;
1422
- const promptToUse = promptOverride || currentInput;
1423
- if (!promptToUse || typeof promptToUse !== "string" || !promptToUse.trim()) return;
1424
- await handleUserCommand(promptToUse);
1425
- setInputValue("");
1426
- }, [handleUserCommand, setInputValue]);
1427
- return {
1428
- handleUserCommand,
1429
- handleGeneratePlayground,
1430
- isGenerating: isGenerating || isThinking
1431
- };
1432
- };
1433
-
1434
- // src/hooks/useScreenRecorder.ts
1435
- import { useState as useState6, useRef as useRef3, useCallback as useCallback4 } from "react";
1436
- function useScreenRecorder() {
1437
- const [isRecording, setIsRecording] = useState6(false);
1438
- const [countdown, setCountdown] = useState6(null);
1439
- const mediaRecorderRef = useRef3(null);
1440
- const startRecording = useCallback4(async () => {
1441
- try {
1442
- const stream = await navigator.mediaDevices.getDisplayMedia({
1443
- video: { displaySurface: "browser", frameRate: { ideal: 60 } },
1444
- audio: true
1445
- });
1446
- const preferredCodecs = [
1447
- { mimeType: "video/webm;codecs=vp9,opus", videoBitsPerSecond: 8e6 },
1448
- { mimeType: "video/webm;codecs=h264,opus", videoBitsPerSecond: 8e6 },
1449
- { mimeType: "video/webm;codecs=vp8,opus", videoBitsPerSecond: 5e6 },
1450
- { mimeType: "video/webm", videoBitsPerSecond: 25e5 }
1451
- ];
1452
- let mType = preferredCodecs.find((opt) => MediaRecorder.isTypeSupported(opt.mimeType));
1453
- if (!mType) mType = { mimeType: "", videoBitsPerSecond: 25e5 };
1454
- const mediaRecorder = new MediaRecorder(stream, mType);
1455
- let currentChunks = [];
1456
- mediaRecorder.ondataavailable = (e) => {
1457
- if (e.data.size > 0) currentChunks.push(e.data);
1458
- };
1459
- mediaRecorder.onstop = () => {
1460
- const blob = new Blob(currentChunks, { type: "video/webm" });
1461
- const url = URL.createObjectURL(blob);
1462
- const a = document.createElement("a");
1463
- a.style.display = "none";
1464
- a.href = url;
1465
- a.download = `decido-demo-${(/* @__PURE__ */ new Date()).getTime()}.webm`;
1466
- document.body.appendChild(a);
1467
- a.click();
1468
- URL.revokeObjectURL(url);
1469
- setIsRecording(false);
1470
- };
1471
- mediaRecorderRef.current = mediaRecorder;
1472
- return new Promise((resolve) => {
1473
- let count = 3;
1474
- setCountdown(count);
1475
- const interval = setInterval(() => {
1476
- count -= 1;
1477
- if (count > 0) {
1478
- setCountdown(count);
1479
- } else {
1480
- clearInterval(interval);
1481
- setCountdown(null);
1482
- mediaRecorder.start();
1483
- setIsRecording(true);
1484
- stream.getVideoTracks()[0].onended = () => {
1485
- if (mediaRecorder.state !== "inactive") mediaRecorder.stop();
1486
- };
1487
- resolve(true);
1488
- }
1489
- }, 1e3);
1490
- });
1491
- } catch (err) {
1492
- console.error("Error starting recording:", err);
1493
- setIsRecording(false);
1494
- setCountdown(null);
1495
- return false;
1496
- }
1497
- }, []);
1498
- const stopRecording = useCallback4(() => {
1499
- if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") mediaRecorderRef.current.stop();
1500
- }, []);
1501
- return { isRecording, countdown, startRecording, stopRecording };
1502
- }
1503
-
1504
- // src/hooks/usePlaybackEngine.ts
1505
- import { useMemo as useMemo3 } from "react";
1506
- import { usePlaybackSimulator } from "@decido/engine";
1507
- function usePlaybackEngine(prototypeBrand) {
1508
- const mockTape = useMemo3(() => ({
1509
- id: `tape-${prototypeBrand}`,
1510
- events: [
1511
- { t: 0.1, event: { type: "SYSTEM_START" }, description: "Arranque Autom\xE1tico" }
1512
- ]
1513
- }), [prototypeBrand]);
1514
- return usePlaybackSimulator(mockTape);
1515
- }
1516
-
1517
- // src/hooks/useExportDSD.ts
1518
- import { useTimelineStore as useTimelineStore6 } from "@decido/engine";
1519
- function useExportDSD() {
1520
- const { prototypeBrand, addLog } = usePlaygroundStore();
1521
- const currentBlueprint = useTimelineStore6(
1522
- (s) => s.timelines[prototypeBrand]
1523
- );
1524
- const handleExport = async () => {
1525
- if (!currentBlueprint) {
1526
- addLog("No hay un blueprint activo para exportar.", "error");
1527
- return;
1528
- }
1529
- try {
1530
- const dsdPayload = {
1531
- format: "decido-standard-document",
1532
- version: "1.0",
1533
- exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
1534
- brand: prototypeBrand,
1535
- blueprint: currentBlueprint
1536
- };
1537
- const blob = new Blob([JSON.stringify(dsdPayload, null, 2)], {
1538
- type: "application/json"
1539
- });
1540
- const url = URL.createObjectURL(blob);
1541
- const a = document.createElement("a");
1542
- a.href = url;
1543
- a.download = `${prototypeBrand}-demo.dsd`;
1544
- document.body.appendChild(a);
1545
- a.click();
1546
- document.body.removeChild(a);
1547
- URL.revokeObjectURL(url);
1548
- addLog(
1549
- `Grafo exportado exitosamente como ${prototypeBrand}-demo.dsd`,
1550
- "success"
1551
- );
1552
- } catch (e) {
1553
- addLog(`Error exportando DSD: ${e}`, "error");
1554
- console.error(e);
1555
- }
1556
- };
1557
- return { handleExport };
1558
- }
1559
-
1560
- // src/hooks/useFullscreen.ts
1561
- import { useEffect as useEffect5, useCallback as useCallback5 } from "react";
1562
- function useFullscreen() {
1563
- const isFullscreen = usePlaygroundStore((s) => s.isFullscreen);
1564
- const setIsFullscreen = usePlaygroundStore((s) => s.setIsFullscreen);
1565
- const toggleFullscreen = useCallback5(() => {
1566
- if (!document.fullscreenElement) {
1567
- document.documentElement.requestFullscreen().catch(
1568
- (err) => console.error(`Fullscreen err: ${err.message}`)
1569
- );
1570
- } else {
1571
- if (document.exitFullscreen) document.exitFullscreen();
1572
- }
1573
- }, []);
1574
- useEffect5(() => {
1575
- const handleChange = () => setIsFullscreen(!!document.fullscreenElement);
1576
- document.addEventListener("fullscreenchange", handleChange);
1577
- return () => document.removeEventListener("fullscreenchange", handleChange);
1578
- }, [setIsFullscreen]);
1579
- return { isFullscreen, toggleFullscreen };
1580
- }
1581
-
1582
- // src/components/shell/CenterComposite.tsx
1583
- import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1584
- function CenterComposite({ aiProvider, VoiceWidget, config }) {
1585
- const store = usePlaygroundStore();
1586
- const {
1587
- step,
1588
- selectedProfile,
1589
- prototypeBrand,
1590
- setPrototypeBrand,
1591
- isMuted,
1592
- setIsMuted,
1593
- selectedVoice,
1594
- setSelectedVoice,
1595
- isVoiceActive,
1596
- setIsVoiceActive,
1597
- isTerminalOpen,
1598
- setIsTerminalOpen,
1599
- isCreatorMode,
1600
- setIsCreatorMode,
1601
- inputValue,
1602
- setInputValue,
1603
- logs,
1604
- setLogs
1605
- } = store;
1606
- const logsEndRef = useRef4(null);
1607
- useEffect6(() => {
1608
- if (logsEndRef.current) logsEndRef.current.scrollIntoView({ behavior: "smooth" });
1609
- }, [logs]);
1610
- const timelines = useTimelineStore7((s) => s.timelines);
1611
- const { handleGeneratePlayground, isGenerating } = usePlaygroundCommander(aiProvider);
1612
- const { demoState, showCanvas, setShowCanvas } = useUIStateListener();
1613
- const { currentTime, isPlaying, setIsPlaying, playbackSpeed, setPlaybackSpeed, seekTime, resetSimulation } = usePlaybackEngine(prototypeBrand);
1614
- const { safeConfig, activeConfig, suggestions, setSuggestions, dynamicPersona, authAvailableLicenses, handleProfileSelect } = useStudioConfig(config, prototypeBrand);
1615
- const { handleExport } = useExportDSD();
1616
- const { isFullscreen, toggleFullscreen } = useFullscreen();
1617
- const { isRecording } = useScreenRecorder();
1618
- const debugIsOpen = useDebugPanelStore((s) => s.isOpen);
1619
- return /* @__PURE__ */ jsx9("div", { className: "flex flex-col h-full relative min-w-0 min-h-0", children: /* @__PURE__ */ jsxs9(PanelGroup, { orientation: "vertical", className: "flex-1 min-h-0 min-w-0", children: [
1620
- /* @__PURE__ */ jsxs9(Panel, { id: "chat-area", defaultSize: debugIsOpen ? 65 : 100, className: "flex flex-col relative min-h-0 min-w-0", children: [
1621
- /* @__PURE__ */ jsx9("div", { className: `flex-1 z-10 flex flex-col relative min-h-0 min-w-0 ${isCreatorMode ? "overflow-y-auto p-4 md:p-6 pb-48 justify-end" : "overflow-hidden"}`, children: /* @__PURE__ */ jsx9(PlaygroundErrorBoundary, { fallbackName: "Motor de Chat", children: /* @__PURE__ */ jsx9(
1622
- PlaygroundChat,
1623
- {
1624
- step,
1625
- selectedProfile,
1626
- authAvailableLicenses,
1627
- handleProfileSelect,
1628
- activeConfig,
1629
- demoState,
1630
- suggestions,
1631
- setInputValue,
1632
- isGenerating,
1633
- config: safeConfig,
1634
- isCreatorMode,
1635
- handleGeneratePlayground,
1636
- isVoiceActive,
1637
- setIsVoiceActive,
1638
- VoiceWidget,
1639
- dynamicPersona,
1640
- setSuggestions,
1641
- selectedVoice,
1642
- showCanvas,
1643
- setShowCanvas
1644
- }
1645
- ) }) }),
1646
- isCreatorMode && /* @__PURE__ */ jsx9(PlaygroundErrorBoundary, { fallbackName: "Terminal de Logs", children: /* @__PURE__ */ jsx9(PlaygroundTerminal, { isOpen: isTerminalOpen, setIsOpen: setIsTerminalOpen, logs, setLogs, logsEndRef }) }),
1647
- /* @__PURE__ */ jsx9(
1648
- PlaygroundBottomControls,
1649
- {
1650
- step,
1651
- isVoiceActive,
1652
- setIsVoiceActive,
1653
- currentTime,
1654
- seekTime,
1655
- playbackSpeed,
1656
- setPlaybackSpeed,
1657
- dynamicTimelines: timelines,
1658
- prototypeBrand,
1659
- setPrototypeBrand,
1660
- inputValue,
1661
- setInputValue,
1662
- isGenerating,
1663
- handleGeneratePlayground,
1664
- isPlaying,
1665
- setIsPlaying,
1666
- handleResetPlayground: resetSimulation,
1667
- showCanvas,
1668
- setShowCanvas,
1669
- isTerminalOpen,
1670
- setIsTerminalOpen,
1671
- isMuted,
1672
- setIsMuted,
1673
- selectedVoice,
1674
- setSelectedVoice,
1675
- isFullscreen,
1676
- toggleFullscreen,
1677
- demoState,
1678
- isRecording,
1679
- handleExport,
1680
- dynamicPersona,
1681
- setSuggestions,
1682
- isCreatorMode,
1683
- setIsCreatorMode,
1684
- VoiceWidget,
1685
- config: safeConfig
1686
- }
1687
- )
1688
- ] }),
1689
- debugIsOpen && /* @__PURE__ */ jsxs9(Fragment5, { children: [
1690
- /* @__PURE__ */ jsx9(PanelResizeHandle, { className: "h-2 hover:bg-cyan-500/20 active:bg-cyan-500/30 transition-colors cursor-row-resize z-40 relative group flex items-center justify-center", children: /* @__PURE__ */ jsx9("div", { className: "h-[2px] w-12 bg-surface-glass rounded-full group-hover:bg-cyan-500 transition-colors" }) }),
1691
- /* @__PURE__ */ jsx9(Panel, { id: "debug-panel", defaultSize: 35, minSize: 15, children: /* @__PURE__ */ jsx9(DebugPanel, {}) })
1692
- ] })
1693
- ] }) });
1694
- }
1695
- export {
1696
- CenterComposite
1697
- };