@decido/shell 1.0.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +31 -0
  2. package/package.json +27 -18
  3. package/.turbo/turbo-build.log +0 -13
  4. package/src/AgentPlayer.tsx +0 -105
  5. package/src/DecidoPlayer.tsx +0 -117
  6. package/src/bridge/BridgeAgent.ts +0 -443
  7. package/src/components/DecidoIcon.tsx +0 -56
  8. package/src/components/JsonTreeEditor.tsx +0 -117
  9. package/src/components/PanelSplitter.tsx +0 -71
  10. package/src/components/PluginErrorBoundary.tsx +0 -69
  11. package/src/components/SafeLiquidUI.tsx +0 -114
  12. package/src/components/TransientLayer.tsx +0 -92
  13. package/src/components/agent/AgentChat.tsx +0 -134
  14. package/src/components/chat-extensions/IntentCatalogPanel.tsx +0 -81
  15. package/src/components/chat-extensions/chatSlashCommands.ts +0 -101
  16. package/src/components/controls/CreatorInputBar.tsx +0 -144
  17. package/src/components/controls/OSToolbar.tsx +0 -90
  18. package/src/components/controls/TimelineTape.tsx +0 -43
  19. package/src/components/debug/ActionTimelineTab.tsx +0 -111
  20. package/src/components/debug/CSSInspectorTab.tsx +0 -436
  21. package/src/components/debug/ExportTab.tsx +0 -192
  22. package/src/components/debug/FlowHealthTab.tsx +0 -86
  23. package/src/components/debug/LogsTab.tsx +0 -110
  24. package/src/components/debug/MorphStackTab.tsx +0 -241
  25. package/src/components/debug/NetworkTab.tsx +0 -173
  26. package/src/components/debug/PerformanceTab.tsx +0 -171
  27. package/src/components/debug/ProfilesTab.tsx +0 -238
  28. package/src/components/debug/ReplayTab.tsx +0 -70
  29. package/src/components/debug/StoresTab.tsx +0 -255
  30. package/src/components/debug/TopologyTab.tsx +0 -59
  31. package/src/components/debug/debugConfig.tsx +0 -66
  32. package/src/components/playground/DebugPanel.tsx +0 -112
  33. package/src/components/playground/HeaderCenterControls.tsx +0 -92
  34. package/src/components/playground/KeyframeListItem.tsx +0 -70
  35. package/src/components/playground/PlaygroundAppSidebar.tsx +0 -171
  36. package/src/components/playground/PlaygroundBottomControls.tsx +0 -132
  37. package/src/components/playground/PlaygroundCanvas.tsx +0 -87
  38. package/src/components/playground/PlaygroundChat.tsx +0 -236
  39. package/src/components/playground/PlaygroundErrorBoundary.tsx +0 -63
  40. package/src/components/playground/PlaygroundFloatingInput.tsx +0 -352
  41. package/src/components/playground/PlaygroundHeader.tsx +0 -222
  42. package/src/components/playground/PlaygroundSidebar.tsx +0 -136
  43. package/src/components/playground/PlaygroundTerminal.tsx +0 -44
  44. package/src/components/playground/SuggestionCards.tsx +0 -29
  45. package/src/components/playground/demos/ClinicaAINode.tsx +0 -221
  46. package/src/components/playground/demos/FinanceAINode.tsx +0 -226
  47. package/src/components/playground/demos/KiaAcademyNode.tsx +0 -250
  48. package/src/components/playground/demos/KiaBotNode.tsx +0 -207
  49. package/src/components/playground/demos/KiaCampaignNode.tsx +0 -191
  50. package/src/components/playground/demos/KiaComplianceNode.tsx +0 -140
  51. package/src/components/playground/demos/KiaCustomerJourneyNode.tsx +0 -220
  52. package/src/components/playground/demos/KiaCyberNode.tsx +0 -203
  53. package/src/components/playground/demos/KiaDashboardNode.tsx +0 -399
  54. package/src/components/playground/demos/KiaEmbudoOverviewNode.tsx +0 -168
  55. package/src/components/playground/demos/KiaExecutiveNode.tsx +0 -169
  56. package/src/components/playground/demos/KiaGamificationNode.tsx +0 -229
  57. package/src/components/playground/demos/KiaIntelligenceHubNode.tsx +0 -165
  58. package/src/components/playground/demos/KiaInventoryNode.tsx +0 -183
  59. package/src/components/playground/demos/KiaLeadScoringNode.tsx +0 -226
  60. package/src/components/playground/demos/KiaLiveSimulationNode.tsx +0 -177
  61. package/src/components/playground/demos/KiaMultiDealerNode.tsx +0 -223
  62. package/src/components/playground/demos/KiaNPSVoiceNode.tsx +0 -214
  63. package/src/components/playground/demos/KiaOmnichannelNode.tsx +0 -162
  64. package/src/components/playground/demos/KiaPBIBudgetNode.tsx +0 -152
  65. package/src/components/playground/demos/KiaPBIConversionNode.tsx +0 -206
  66. package/src/components/playground/demos/KiaPBIFunnelNode.tsx +0 -184
  67. package/src/components/playground/demos/KiaPBIOwnershipNode.tsx +0 -113
  68. package/src/components/playground/demos/KiaPBIPartnerNode.tsx +0 -143
  69. package/src/components/playground/demos/KiaPBIPreciosNode.tsx +0 -120
  70. package/src/components/playground/demos/KiaPBIRuntNode.tsx +0 -205
  71. package/src/components/playground/demos/KiaPartnerScoreNode.tsx +0 -206
  72. package/src/components/playground/demos/KiaPredictiveNode.tsx +0 -226
  73. package/src/components/playground/demos/KiaShowroomNode.tsx +0 -194
  74. package/src/components/playground/demos/KiaStoreNode.tsx +0 -215
  75. package/src/components/playground/demos/KiaSustainabilityNode.tsx +0 -173
  76. package/src/components/playground/demos/KiaUsedVehiclesNode.tsx +0 -163
  77. package/src/components/playground/demos/KiaWorkshopNode.tsx +0 -221
  78. package/src/components/playground/demos/SmartCityNode.tsx +0 -205
  79. package/src/components/playground/demos/kia_campaign_manifest.json +0 -112
  80. package/src/components/playground/input-parts/AIModelSelector.tsx +0 -156
  81. package/src/components/playground/input-parts/InputActions.tsx +0 -80
  82. package/src/components/playground/input-parts/InputToolbar.tsx +0 -245
  83. package/src/components/playground/input-parts/ResourceLibraryPanel.tsx +0 -287
  84. package/src/components/playground/sidebarDsdIO.ts +0 -82
  85. package/src/components/settings/SettingsPanel.tsx +0 -267
  86. package/src/components/shell/AppHeader.tsx +0 -9
  87. package/src/components/shell/AppShell.tsx +0 -139
  88. package/src/components/shell/ArtifactBar.tsx +0 -97
  89. package/src/components/shell/BootScreen.tsx +0 -19
  90. package/src/components/shell/CenterComposite.tsx +0 -87
  91. package/src/components/shell/CodeEditorPanel.tsx +0 -88
  92. package/src/components/shell/GlobalOverlays.tsx +0 -228
  93. package/src/components/shell/LayoutConfigurator.tsx +0 -209
  94. package/src/components/shell/LayoutGrid.tsx +0 -178
  95. package/src/components/shell/MorphShell.tsx +0 -368
  96. package/src/components/shell/PluginViewer.tsx +0 -147
  97. package/src/components/shell/ShellNexusPreview.tsx +0 -458
  98. package/src/components/shell/SlotRenderer.tsx +0 -115
  99. package/src/components/shell/TabBar.tsx +0 -94
  100. package/src/components/shell/TemplateLibrary.tsx +0 -195
  101. package/src/components/shell/layoutConstants.ts +0 -35
  102. package/src/components/shell/morphStageMeta.ts +0 -15
  103. package/src/components/shell/shells/BuiltInShells.tsx +0 -443
  104. package/src/components/shell/shells/DatawayChatShell.tsx +0 -42
  105. package/src/components/shell/shells/TokenPreview.tsx +0 -339
  106. package/src/components/shell/shells/bootShells.ts +0 -31
  107. package/src/components/shells/CreatorShell.tsx +0 -37
  108. package/src/components/shells/DecidoShell.tsx +0 -447
  109. package/src/components/shells/ExperimentalChatShell.tsx +0 -245
  110. package/src/components/shells/UserCanvas.tsx +0 -44
  111. package/src/components/studio/BlueprintManagerPanel.tsx +0 -137
  112. package/src/components/studio/DependencyTreePanel.tsx +0 -192
  113. package/src/components/studio/NodePalette.tsx +0 -92
  114. package/src/components/studio/NodePropertiesPanel.tsx +0 -81
  115. package/src/components/studio/ReactFlowEditor.tsx +0 -242
  116. package/src/components/studio/TimelineEditor.tsx +0 -122
  117. package/src/components/studio/TimelineKeyframeCard.tsx +0 -99
  118. package/src/components/studio/VariablePanel.tsx +0 -181
  119. package/src/components/studio/blueprint/BlueprintCard.tsx +0 -82
  120. package/src/components/studio/editor/CanvasContextMenu.tsx +0 -107
  121. package/src/components/studio/editor/EditorToolbar.tsx +0 -80
  122. package/src/components/studio/editor/StageContentRenderer.tsx +0 -134
  123. package/src/components/studio/editor/TrackPropertyEditors.tsx +0 -133
  124. package/src/components/studio/editor/TreeNodeItem.tsx +0 -91
  125. package/src/components/studio/editor/edgeStyles.ts +0 -43
  126. package/src/components/studio/editor/editorKeyHandler.ts +0 -95
  127. package/src/components/studio/editor/nodeTypeRegistry.ts +0 -137
  128. package/src/components/studio/editor/paletteCatalog.tsx +0 -84
  129. package/src/components/studio/nodes/shell/InteractionNodes.tsx +0 -82
  130. package/src/components/studio/nodes/shell/LayoutControlNodes.tsx +0 -69
  131. package/src/components/studio/nodes/shell/RegisterActionNode.tsx +0 -20
  132. package/src/components/studio/nodes/shell/RegisterButtonNode.tsx +0 -22
  133. package/src/components/studio/nodes/shell/RegisterPanelNode.tsx +0 -19
  134. package/src/components/studio/nodes/shell/RegisterSidebarNode.tsx +0 -19
  135. package/src/components/studio/nodes/shell/RegisterStatusBarNode.tsx +0 -22
  136. package/src/components/studio/nodes/shell/RegisterTabNode.tsx +0 -21
  137. package/src/components/studio/nodes/shell/RegisterTopBarNode.tsx +0 -22
  138. package/src/components/studio/nodes/shell/ShellConfigNode.tsx +0 -51
  139. package/src/components/studio/nodes/shell/ShellNodeBase.tsx +0 -100
  140. package/src/components/studio/nodes/shell/ThemeNodes.tsx +0 -51
  141. package/src/components/studio/nodes/shell/index.ts +0 -12
  142. package/src/components/widgets/BroadcastWidget.tsx +0 -93
  143. package/src/components/widgets/MarketplaceWidget.tsx +0 -298
  144. package/src/components/widgets/McpToolsWidget.tsx +0 -231
  145. package/src/components/widgets/OpsDashboard.tsx +0 -59
  146. package/src/components/widgets/QuickActionsWidget.tsx +0 -60
  147. package/src/components/widgets/UsageWidget.tsx +0 -112
  148. package/src/components/widgets/WidgetRenderer.tsx +0 -892
  149. package/src/components/widgets/WidgetSlotPanel.tsx +0 -213
  150. package/src/config/IconRegistry.ts +0 -126
  151. package/src/contexts/NetworkProvider.tsx +0 -162
  152. package/src/core/AIDirector.ts +0 -71
  153. package/src/core/EventBus.ts +0 -37
  154. package/src/core/PluginContext.tsx +0 -141
  155. package/src/hooks/listeners/useUIStateListener.ts +0 -59
  156. package/src/hooks/listeners/useWhatsAppListener.ts +0 -110
  157. package/src/hooks/morphBridge.ts +0 -82
  158. package/src/hooks/useAIModelSelector.ts +0 -144
  159. package/src/hooks/useAgentStream.ts +0 -220
  160. package/src/hooks/useAutoUpdater.ts +0 -89
  161. package/src/hooks/useBootSequence.ts +0 -20
  162. package/src/hooks/useExportDSD.ts +0 -53
  163. package/src/hooks/useFullscreen.ts +0 -35
  164. package/src/hooks/useGeminiStream.ts +0 -282
  165. package/src/hooks/useIntentLens.ts +0 -224
  166. package/src/hooks/useKeyboardShortcuts.ts +0 -69
  167. package/src/hooks/useLoggerBridge.ts +0 -32
  168. package/src/hooks/useMcpClient.ts +0 -112
  169. package/src/hooks/useNexusaiDeploy.ts +0 -118
  170. package/src/hooks/usePlaybackEngine.ts +0 -21
  171. package/src/hooks/usePlaygroundCommander.ts +0 -475
  172. package/src/hooks/usePluginEngine.ts +0 -165
  173. package/src/hooks/useScreenRecorder.ts +0 -73
  174. package/src/hooks/useShellKeyboard.ts +0 -40
  175. package/src/hooks/useShellShortcuts.ts +0 -118
  176. package/src/hooks/useSoundEffects.ts +0 -35
  177. package/src/hooks/useStudioConfig.ts +0 -72
  178. package/src/hooks/useSystemBoot.ts +0 -84
  179. package/src/hooks/useSystemTelemetry.ts +0 -62
  180. package/src/index.ts +0 -97
  181. package/src/lib/debugLogger.ts +0 -80
  182. package/src/lib/networkInterceptor.ts +0 -100
  183. package/src/mocks/decido.tsx +0 -41
  184. package/src/plugins/pluginAPI.ts +0 -190
  185. package/src/store/McpStore.ts +0 -69
  186. package/src/store/UpdaterStore.ts +0 -60
  187. package/src/store/engine.ts +0 -392
  188. package/src/store/index.ts +0 -4
  189. package/src/store/layoutPresets.ts +0 -66
  190. package/src/store/playgroundTypes.ts +0 -98
  191. package/src/store/useActionTimelineStore.ts +0 -48
  192. package/src/store/useDebugPanelStore.ts +0 -98
  193. package/src/store/useDebugProfileStore.ts +0 -130
  194. package/src/store/useLayoutStore.ts +0 -205
  195. package/src/store/useMorphInstanceStore.ts +0 -289
  196. package/src/store/useMorphologyStore.ts +0 -103
  197. package/src/store/usePlaygroundStore.ts +0 -236
  198. package/src/store/useShellRegistry.ts +0 -123
  199. package/src/store/useSuggestionsStore.ts +0 -57
  200. package/src/store/useThemeStore.ts +0 -399
  201. package/src/store/useUIComponentStore.ts +0 -179
  202. package/src/types/DecidoStoryDefinition.ts +0 -43
  203. package/src/utils/ai/ai-architect.ts +0 -92
  204. package/src/utils/ai/ai-code.ts +0 -187
  205. package/src/utils/ai/ai-core.ts +0 -50
  206. package/src/utils/ai/ai-media.ts +0 -292
  207. package/src/utils/layoutGraph.ts +0 -67
  208. package/tsconfig.json +0 -17
  209. package/tsconfig.tsbuildinfo +0 -1
@@ -1,245 +0,0 @@
1
- import React, { useState, Suspense } from 'react';
2
- import { motion, AnimatePresence } from 'framer-motion';
3
- import { Menu, Command, Workflow, Camera, Paperclip, Mic, Sparkles, Send, X, MonitorPlay, Layers, Code2, Database } from 'lucide-react';
4
- import { Omnibar } from '../Omnibar';
5
- import { TransientLayer } from '../TransientLayer';
6
-
7
- // Lazy load: VoiceWidget comes from @decido/chat
8
- const DecidoVoiceWidget = React.lazy(() =>
9
- import('@decido/chat').then(m => ({ default: m.DecidoVoiceWidget })).catch(() => ({ default: () => null }))
10
- );
11
-
12
- interface ExperimentalChatShellProps {
13
- onExit: () => void;
14
- }
15
-
16
- export const ExperimentalChatShell: React.FC<ExperimentalChatShellProps> = ({ onExit }) => {
17
- const [isSidebarOpen, setIsSidebarOpen] = useState(false);
18
- const [isVoiceActive, setIsVoiceActive] = useState(false);
19
- const [isTranscribing, setIsTranscribing] = useState(false);
20
- const [inputValue, setInputValue] = useState('');
21
- const [suggestions, setSuggestions] = useState<any[]>([]);
22
-
23
- return (
24
- <div className="h-dvh w-full bg-surface-primary flex font-sans relative overflow-hidden text-text-primary">
25
- {/* Background Texture Drop */}
26
- <div className="absolute inset-0 bg-[url('data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E')] opacity-[0.2] mix-blend-overlay pointer-events-none z-0"></div>
27
-
28
- {/* COLLAPSIBLE LEFT SIDEBAR */}
29
- <div className={`flex flex-col bg-surface-secondary border-r border-border-subtle z-30 transition-all duration-300 ease-[cubic-bezier(0.32,_0.72,_0,_1)] shrink-0 ${isSidebarOpen ? 'w-[280px]' : 'w-0'} overflow-hidden`}>
30
- <div className="w-[280px] p-4 md:p-6 flex flex-col h-full opacity-100 min-w-[280px]">
31
- <div className="flex items-center gap-3 mb-8">
32
- <button onClick={() => setIsSidebarOpen(false)} className="p-2 -ml-2 rounded-full hover:bg-surface-glass text-text-secondary hover:text-text-primary transition-colors">
33
- <Menu size={22} />
34
- </button>
35
- <span className="font-bold text-text-primary tracking-wide">Menú Principal</span>
36
- </div>
37
-
38
- <div className="flex-1 space-y-2">
39
- <button className="flex items-center w-full gap-3 p-3 rounded-xl bg-surface-glass text-cyan-400 font-medium border border-border-subtle transition-all">
40
- <Command size={18} />
41
- <span>Centro de Comandos</span>
42
- </button>
43
- <button className="flex items-center w-full gap-3 p-3 rounded-xl text-text-secondary font-medium border border-transparent hover:bg-surface-glass transition-all">
44
- <Workflow size={18} />
45
- <span>Flujos de Trabajo</span>
46
- </button>
47
-
48
- <div className="my-6 border-t border-border-subtle" />
49
-
50
- <button onClick={onExit} className="flex items-center w-full gap-3 p-3 rounded-xl text-amber-400 font-medium border border-transparent hover:bg-amber-400/10 transition-all">
51
- <X size={18} />
52
- <span>Salir de Chat Exp.</span>
53
- </button>
54
- </div>
55
-
56
- <div className="mt-auto pb-4">
57
- <p className="text-xs text-text-muted font-mono">Kernel v1.0.0 (Chat Shell)</p>
58
- </div>
59
- </div>
60
- </div>
61
-
62
- {/* MAIN CHAT LAYER */}
63
- <div className="flex-1 flex flex-col relative z-20 bg-surface-secondary shadow-[0_0_50px_rgba(0,0,0,0.5)]">
64
-
65
- {/* HEADER */}
66
- <header className="flex-none p-4 md:p-6 z-60 flex justify-between items-center bg-linear-to-b from-surface-secondary to-transparent pointer-events-none">
67
- <div className="flex items-center gap-3 pointer-events-auto">
68
- {!isSidebarOpen && (
69
- <button onClick={() => setIsSidebarOpen(true)} className="p-2 -ml-2 rounded-full hover:bg-surface-glass text-text-secondary hover:text-text-primary transition-colors">
70
- <Menu size={22} />
71
- </button>
72
- )}
73
- <span className="text-xl md:text-2xl font-black text-text-primary tracking-tighter drop-shadow-[0_0_10px_rgba(255,255,255,0.2)]">
74
- yo.<span className="text-cyan-400">decido</span>
75
- </span>
76
- <span className="ml-2 text-[10px] font-bold tracking-widest text-amber-400 border border-amber-400/30 rounded px-2 py-0.5 opacity-80 uppercase">Experimental</span>
77
- </div>
78
- </header>
79
-
80
- {/* MAIN CHAT BODY / CANVAS */}
81
- <div className="flex-1 overflow-y-auto z-10 p-4 md:p-6 pb-48 flex flex-col justify-end">
82
- <AnimatePresence mode="wait">
83
- <motion.div
84
- key="chat-greeting"
85
- initial={{ opacity: 0, y: 20 }}
86
- animate={{ opacity: 1, y: 0 }}
87
- exit={{ opacity: 0 }}
88
- className="flex flex-col max-w-4xl mx-auto w-full gap-8"
89
- >
90
- {/* Greeting Bubble */}
91
- <div className="flex flex-col gap-2">
92
- <h1 className="text-3xl md:text-5xl font-semibold leading-tight text-text-primary/50">
93
- <span className="bg-linear-to-r from-cyan-400 via-purple-400 to-indigo-400 text-transparent bg-clip-text">Hola.</span><br />
94
- ¿En qué puedo ayudarte hoy?
95
- </h1>
96
- <p className="text-sm text-text-muted font-mono mt-2">NÚCLEO EXPERIMENTAL • ZONA DE PRUEBAS</p>
97
- </div>
98
- </motion.div>
99
- </AnimatePresence>
100
- </div>
101
-
102
- {/* BOTTOM INPUT AREA / DOCK */}
103
- <div className="absolute bottom-0 left-0 w-full p-4 pb-8 md:p-6 md:pb-10 bg-linear-to-t from-surface-secondary via-surface-secondary to-transparent z-70 flex justify-center">
104
- <motion.div
105
- layout
106
- className={`w-full max-w-4xl relative ${isVoiceActive ? 'h-64' : ''}`}
107
- >
108
- <div className={`bg-surface-tertiary border border-border-default rounded-4xl flex flex-col p-2 shadow-2xl transition-all duration-300 ${isVoiceActive ? 'h-full border-cyan-500/30' : 'shadow-[#06b6d4]/5 focus-within:border-border-strong focus-within:shadow-[#06b6d4]/10'}`}>
109
-
110
- <AnimatePresence mode="wait">
111
- {!isVoiceActive ? (
112
- <motion.div
113
- key="text-mode"
114
- initial={{ opacity: 0 }}
115
- animate={{ opacity: 1 }}
116
- exit={{ opacity: 0 }}
117
- className="flex flex-col w-full h-full cursor-text"
118
- onClick={(e) => {
119
- const input = e.currentTarget.querySelector('input');
120
- if (input && e.target === e.currentTarget) input.focus();
121
- }}
122
- >
123
- <div className="relative flex items-center px-4 py-3 min-h-[60px]">
124
- <input
125
- type="text"
126
- placeholder={isTranscribing ? "Escuchando..." : "Pregúntale a Decido..."}
127
- value={inputValue}
128
- onChange={(e) => setInputValue(e.target.value)}
129
- className="w-full bg-transparent text-text-primary placeholder-text-muted outline-hidden text-[15px] leading-relaxed resize-none h-full"
130
- onKeyDown={(e) => {
131
- if (e.key === 'Enter') {
132
- console.log("Submit:", inputValue);
133
- setInputValue('');
134
- }
135
- }}
136
- />
137
- </div>
138
-
139
- <div className="flex items-center justify-between px-2 pb-1 relative z-10 w-full">
140
- {/* Archivos / Acciones Base */}
141
- <div className="flex items-center gap-1">
142
- <button className="w-10 h-10 rounded-full text-text-secondary hover:text-text-primary hover:bg-surface-glass flex items-center justify-center transition-colors">
143
- <Camera size={20} />
144
- </button>
145
- <button className="w-10 h-10 rounded-full text-text-secondary hover:text-text-primary hover:bg-surface-glass flex items-center justify-center transition-colors">
146
- <Paperclip size={20} />
147
- </button>
148
- </div>
149
-
150
- {/* DEV CONTROL TOOLBAR (Centro) */}
151
- <div className="flex items-center gap-1 px-3 py-1.5 bg-surface-overlay border border-border-subtle rounded-2xl">
152
- <button
153
- onClick={() => console.log("[DevTools] Solicitando Workspace")}
154
- className="p-2 rounded-xl text-text-muted hover:text-cyan-400 hover:bg-surface-glass transition-all" title="Vista de Lienzo (Canvas)">
155
- <MonitorPlay size={18} />
156
- </button>
157
- <button
158
- onClick={() => window.dispatchEvent(new Event('decido-toggle-debug'))}
159
- className="p-2 rounded-xl text-text-muted hover:text-emerald-400 hover:bg-surface-glass transition-all font-bold" title="Gestor de Capas / Resaltar Componentes">
160
- <Layers size={18} />
161
- </button>
162
- <button
163
- onClick={() => alert("Editor de Nodos (Kernel) en fase de Pruebas")}
164
- className="p-2 rounded-xl text-text-muted hover:text-amber-400 hover:bg-surface-glass transition-all" title="Editor de Nodos (Kernel)">
165
- <Code2 size={18} />
166
- </button>
167
- <div className="w-[1px] h-4 bg-surface-glass mx-1"></div>
168
- <button
169
- onClick={() => console.log("[DevTools] Database Inspector activo")}
170
- className="p-2 rounded-xl text-text-muted hover:text-purple-400 hover:bg-surface-glass transition-all" title="Inspector de Estado">
171
- <Database size={18} />
172
- </button>
173
- </div>
174
-
175
- {/* Acciones de Envío / Voz */}
176
- <div className="flex items-center gap-2">
177
- {!inputValue ? (
178
- <>
179
- <button
180
- onClick={(e) => { e.stopPropagation(); setIsTranscribing(!isTranscribing); }}
181
- className={`w-10 h-10 rounded-full flex items-center justify-center transition-all ${isTranscribing ? 'text-red-400 bg-red-400/10' : 'text-text-secondary hover:text-text-primary hover:bg-surface-glass'}`}
182
- title="Reconocimiento de voz"
183
- >
184
- <Mic size={20} className={isTranscribing ? "animate-pulse" : ""} />
185
- </button>
186
- <button
187
- onClick={(e) => { e.stopPropagation(); setIsVoiceActive(true); }}
188
- className="w-12 h-12 rounded-full text-cyan-400 bg-cyan-500/10 hover:bg-cyan-500/20 flex items-center justify-center border border-cyan-500/20 transition-all shadow-[0_0_15px_rgba(6,182,212,0.15)] hover:shadow-[0_0_20px_rgba(6,182,212,0.3)] group relative overflow-hidden"
189
- title="Hablar con la IA (Live)"
190
- >
191
- <Sparkles size={22} className="relative z-10 transition-transform group-hover:scale-110" />
192
- <div className="absolute inset-0 bg-linear-to-r from-cyan-400 to-purple-500 rounded-full opacity-0 group-hover:opacity-20 transition-opacity"></div>
193
- </button>
194
- </>
195
- ) : (
196
- <button
197
- onClick={(e) => { e.stopPropagation(); console.log("Send:", inputValue); setInputValue(''); }}
198
- className="w-12 h-12 rounded-full text-text-inverse bg-cyan-400 hover:bg-cyan-300 flex items-center justify-center transition-colors shadow-[0_0_15px_rgba(34,211,238,0.3)]"
199
- >
200
- <Send size={20} className="mr-1" />
201
- </button>
202
- )}
203
- </div>
204
- </div>
205
- </motion.div>
206
- ) : (
207
- <motion.div
208
- key="voice-mode"
209
- initial={{ opacity: 0 }}
210
- animate={{ opacity: 1 }}
211
- exit={{ opacity: 0 }}
212
- className="flex flex-col w-full h-full relative p-2"
213
- >
214
- <div className="flex w-full items-center justify-between mb-2 z-10">
215
- <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">
216
- Experimental Live Activo...
217
- </span>
218
- <button
219
- onClick={(e) => { e.stopPropagation(); setIsVoiceActive(false); }}
220
- 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"
221
- >
222
- <X size={16} />
223
- </button>
224
- </div>
225
-
226
- <div className="flex-1 w-full rounded-2xl overflow-hidden relative">
227
- <DecidoVoiceWidget
228
- isCompact={true}
229
- onSuggestionsUpdate={(newSuggestions) => setSuggestions(newSuggestions)}
230
- />
231
- </div>
232
- </motion.div>
233
- )}
234
- </AnimatePresence>
235
- </div>
236
- </motion.div>
237
- </div>
238
-
239
- {/* OMNIBAR AND TRANSIENT LAYER INTEGRATION TO KEEP PLUGINS FUNCTIONAL */}
240
- <Omnibar />
241
- <TransientLayer />
242
- </div>
243
- </div>
244
- );
245
- };
@@ -1,44 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { ExperimentalChatShell } from './ExperimentalChatShell';
3
-
4
- export const UserCanvas: React.FC = () => {
5
- const [isExperimentalChat, setIsExperimentalChat] = useState(false);
6
-
7
- if (isExperimentalChat) {
8
- return <ExperimentalChatShell onExit={() => setIsExperimentalChat(false)} />;
9
- }
10
-
11
- return (
12
- <div className="relative w-full h-full flex flex-col selection:bg-cyan-500/30">
13
- {/* Efectos de fondo ambiental dinámicos */}
14
- <div className="absolute inset-x-0 top-0 h-[500px] w-full bg-linear-to-b from-cyan-900/20 via-blue-900/10 to-transparent pointer-events-none -translate-y-1/2 opacity-50 blur-[100px]" />
15
- <div className="absolute inset-0 bg-[url('data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E')] opacity-20 mix-blend-overlay pointer-events-none"></div>
16
-
17
- <div className="w-full shrink-0 flex-none bg-surface-overlay backdrop-blur-md border-b border-border-subtle" style={{ height: 'max(env(safe-area-inset-top, 0px), 1.5rem)' }} />
18
-
19
- <div className="flex-1 relative overflow-hidden w-full flex items-center justify-center border-t border-cyan-500/10 backdrop-blur-xs">
20
- <div className="absolute inset-0 pattern-grid-lg text-text-primary/2" />
21
-
22
- <div className="text-center z-10 max-w-md bg-surface-overlay p-8 rounded-4xl border border-border-default shadow-[0_0_50px_rgba(0,0,0,0.5)] backdrop-blur-xl">
23
- <div className="w-16 h-16 bg-surface-glass rounded-full flex items-center justify-center mx-auto mb-6">
24
- <i className="fas fa-project-diagram text-2xl text-cyan-400 drop-shadow-[0_0_10px_rgba(34,211,238,0.5)]"></i>
25
- </div>
26
- <h2 className="text-2xl font-light mb-2 tracking-tight">Espacio Decido</h2>
27
- <p className="text-text-secondary text-sm leading-relaxed mb-8">
28
- Bienvenido. Tu Agente IA está escuchando. Haz clic en el orbe inferior o usa <kbd className="bg-surface-glass px-1 rounded">Cmd+K</kbd> para ordenar la materialización de componentes.
29
- </p>
30
-
31
- <button
32
- onClick={() => setIsExperimentalChat(true)}
33
- className="px-6 py-3 bg-linear-to-r from-cyan-500/10 to-blue-500/10 hover:from-cyan-500/20 hover:to-blue-500/20 border border-cyan-500/30 hover:border-cyan-400 text-cyan-300 rounded-xl font-medium transition-all shadow-[0_0_15px_rgba(34,211,238,0.1)] flex items-center justify-center gap-2 mx-auto w-full"
34
- >
35
- <i className="fas fa-flask"></i>
36
- Probar Modo Inmersivo
37
- </button>
38
- </div>
39
- </div>
40
-
41
- <div className="w-full shrink-0 flex-none" style={{ height: 'max(env(safe-area-inset-bottom, 0px), 1rem)' }} />
42
- </div>
43
- );
44
- };
@@ -1,137 +0,0 @@
1
- import React, { useState, useCallback, useRef, useEffect } from 'react';
2
- import { motion, AnimatePresence } from 'motion/react';
3
- import { FolderOpen, Plus, Check, X, FileText, Network } from 'lucide-react';
4
- import { useTimelineStore } from '@decido/engine';
5
- import { usePlaygroundStore } from '../../store/usePlaygroundStore';
6
- import { useBlueprintNavigation } from '@decido/engine';
7
- import { BlueprintCard } from './blueprint/BlueprintCard';
8
-
9
- interface BlueprintManagerPanelProps { isOpen: boolean; onClose: () => void; }
10
-
11
- export const BlueprintManagerPanel: React.FC<BlueprintManagerPanelProps> = ({ isOpen, onClose }) => {
12
- const timelines = useTimelineStore(s => s.timelines);
13
- const blueprintLibrary = useTimelineStore(s => s.blueprintLibrary);
14
- const activeTimelineId = usePlaygroundStore(s => s.prototypeBrand);
15
- const setPrototypeBrand = usePlaygroundStore(s => s.setPrototypeBrand);
16
- const resetNavigation = useBlueprintNavigation(s => s.resetNavigation);
17
-
18
- const createTimeline = useTimelineStore(s => s.createTimeline);
19
- const addKeyframe = useTimelineStore(s => s.addKeyframe);
20
- const addBlueprint = useTimelineStore(s => s.addBlueprint);
21
- const duplicateTimeline = useTimelineStore(s => s.duplicateTimeline);
22
- const renameTimeline = useTimelineStore(s => s.renameTimeline);
23
- const deleteTimeline = useTimelineStore(s => s.deleteTimeline);
24
-
25
- const [isCreating, setIsCreating] = useState(false);
26
- const [createType, setCreateType] = useState<'blueprint' | 'subflow'>('blueprint');
27
- const [newName, setNewName] = useState('');
28
- const [showCreateMenu, setShowCreateMenu] = useState(false);
29
- const createMenuRef = useRef<HTMLDivElement>(null);
30
-
31
- useEffect(() => {
32
- const h = (e: MouseEvent) => { if (createMenuRef.current && !createMenuRef.current.contains(e.target as Node)) setShowCreateMenu(false); };
33
- document.addEventListener('mousedown', h);
34
- return () => document.removeEventListener('mousedown', h);
35
- }, []);
36
-
37
- const handleCreate = useCallback(() => {
38
- if (!newName.trim()) return;
39
- if (createType === 'subflow') {
40
- const subKey = `subflow_${newName.trim().toLowerCase().replace(/\s+/g, '_')}`;
41
- const id = createTimeline(newName.trim(), subKey);
42
- addKeyframe(id, { t: 0, track: 'entry', label: 'Entrada', position: { x: 300, y: 50 } } as any);
43
- addKeyframe(id, { t: 5, track: 'return', label: 'Retorno', handle: 'success', position: { x: 300, y: 300 } } as any);
44
- const bpId = `bp_${newName.trim().toLowerCase().replace(/\s+/g, '_')}`;
45
- const tl = useTimelineStore.getState().timelines[id];
46
- 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() } } as any);
47
- setPrototypeBrand(id);
48
- } else { setPrototypeBrand(createTimeline(newName.trim())); }
49
- setNewName(''); setIsCreating(false);
50
- }, [newName, createType, createTimeline, addKeyframe, addBlueprint, setPrototypeBrand]);
51
-
52
- const handleSelect = useCallback((id: string) => { resetNavigation(); setPrototypeBrand(id); }, [setPrototypeBrand, resetNavigation]);
53
- const handleDelete = useCallback((id: string) => {
54
- const del = deleteTimeline(id);
55
- if (del && activeTimelineId === id) { const rem = Object.keys(timelines).filter(k => k !== id); if (rem.length > 0) setPrototypeBrand(rem[0]); }
56
- }, [deleteTimeline, activeTimelineId, timelines, setPrototypeBrand]);
57
-
58
- const allBlueprints = Object.entries(timelines);
59
- const subflows = Object.entries(blueprintLibrary);
60
- const bpToKey: Record<string, string> = {};
61
- for (const [key] of Object.entries(timelines)) { if (key.startsWith('subflow_')) bpToKey[`bp_${key.replace('subflow_', '')}`] = key; }
62
-
63
- return (
64
- <AnimatePresence>
65
- {isOpen && (
66
- <>
67
- <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="fixed inset-0 bg-surface-overlay z-40" onClick={onClose} />
68
- <motion.div initial={{ opacity: 0, x: -20, scale: 0.95 }} animate={{ opacity: 1, x: 0, scale: 1 }} exit={{ opacity: 0, x: -20, scale: 0.95 }} transition={{ type: 'spring', damping: 25, stiffness: 300 }}
69
- 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" style={{ backdropFilter: 'blur(20px)' }} onClick={e => e.stopPropagation()}>
70
-
71
- {/* Header */}
72
- <div className="flex items-center justify-between px-4 py-3 border-b border-border-default bg-surface-glass">
73
- <div className="flex items-center gap-2">
74
- <FolderOpen size={16} className="text-cyan-400" /><span className="text-sm font-bold text-text-primary tracking-wide">Blueprints</span>
75
- <span className="text-[10px] bg-surface-glass text-text-secondary px-1.5 py-0.5 rounded-full font-mono">{allBlueprints.length}</span>
76
- </div>
77
- <div className="relative" ref={createMenuRef}>
78
- <button onClick={() => setShowCreateMenu(!showCreateMenu)} className="p-1.5 rounded-lg hover:bg-cyan-500/20 text-cyan-400 transition-colors" title="Crear nuevo"><Plus size={16} /></button>
79
- <AnimatePresence>
80
- {showCreateMenu && (
81
- <motion.div initial={{ opacity: 0, scale: 0.9, y: -5 }} animate={{ opacity: 1, scale: 1, y: 0 }} exit={{ opacity: 0, scale: 0.9, y: -5 }}
82
- 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]">
83
- <button onClick={() => { setCreateType('blueprint'); setIsCreating(true); setShowCreateMenu(false); }} className="flex items-center gap-2 w-full px-3 py-2.5 text-xs text-text-primary hover:bg-surface-glass transition-colors">
84
- <FileText size={13} className="text-cyan-400" /><div className="text-left"><div className="font-medium">Nuevo Blueprint</div><div className="text-[10px] text-text-muted">Flujo principal</div></div>
85
- </button>
86
- <button onClick={() => { setCreateType('subflow'); setIsCreating(true); setShowCreateMenu(false); }} 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">
87
- <Network size={13} className="text-violet-400" /><div className="text-left"><div className="font-medium">Nuevo SubFlow</div><div className="text-[10px] text-text-muted">Con Entry + Return</div></div>
88
- </button>
89
- </motion.div>
90
- )}
91
- </AnimatePresence>
92
- </div>
93
- </div>
94
-
95
- {/* Create Input */}
96
- <AnimatePresence>
97
- {isCreating && (
98
- <motion.div initial={{ height: 0, opacity: 0 }} animate={{ height: 'auto', opacity: 1 }} exit={{ height: 0, opacity: 0 }} className="overflow-hidden border-b border-border-default">
99
- <div className="flex items-center gap-2 px-4 py-3">
100
- {createType === 'subflow' ? <Network size={14} className="text-violet-400 shrink-0" /> : <FileText size={14} className="text-cyan-400 shrink-0" />}
101
- <input autoFocus value={newName} onChange={e => setNewName(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') handleCreate(); if (e.key === 'Escape') setIsCreating(false); }}
102
- placeholder={createType === 'subflow' ? 'Nombre del subflow...' : 'Nombre del blueprint...'} 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" />
103
- <button onClick={handleCreate} className="p-1.5 rounded-lg bg-cyan-500/20 text-cyan-400 hover:bg-cyan-500/30"><Check size={14} /></button>
104
- <button onClick={() => setIsCreating(false)} className="p-1.5 rounded-lg hover:bg-surface-glass text-text-muted"><X size={14} /></button>
105
- </div>
106
- </motion.div>
107
- )}
108
- </AnimatePresence>
109
-
110
- {/* Lists */}
111
- <div className="flex-1 overflow-y-auto custom-scrollbar">
112
- <div className="px-2 py-1">
113
- <div className="px-2 py-1.5 text-[9px] font-bold text-text-muted uppercase tracking-widest">Blueprints Principales</div>
114
- {allBlueprints.map(([id, tl]) => (
115
- <BlueprintCard key={id} id={id} name={tl.name || id} nodeCount={tl.keyframes?.length || 0} isActive={id === activeTimelineId}
116
- onSelect={() => handleSelect(id)} onRename={name => renameTimeline(id, name)} onDuplicate={() => { setPrototypeBrand(duplicateTimeline(id)); }}
117
- onDelete={allBlueprints.length > 1 ? () => handleDelete(id) : undefined} />
118
- ))}
119
- </div>
120
- {subflows.length > 0 && (
121
- <div className="px-2 py-1 border-t border-border-subtle">
122
- <div className="px-2 py-1.5 text-[9px] font-bold text-text-muted uppercase tracking-widest">Subflujos</div>
123
- {subflows.map(([id, bp]) => {
124
- const tk = bpToKey[id] || '';
125
- return <BlueprintCard key={id} id={id} name={bp.name || id} nodeCount={bp.keyframes?.length || 0} isActive={tk === activeTimelineId} isSubflow
126
- onSelect={() => tk && handleSelect(tk)} onRename={name => tk && renameTimeline(tk, name)} onDuplicate={() => tk && setPrototypeBrand(duplicateTimeline(tk))}
127
- onDelete={() => tk && handleDelete(tk)} />;
128
- })}
129
- </div>
130
- )}
131
- </div>
132
- </motion.div>
133
- </>
134
- )}
135
- </AnimatePresence>
136
- );
137
- };
@@ -1,192 +0,0 @@
1
- import React, { useMemo } from 'react';
2
- import { motion, AnimatePresence } from 'motion/react';
3
- import { AlertTriangle, X, TreePine } from 'lucide-react';
4
- import { useTimelineStore } from '@decido/engine';
5
- import { usePlaygroundStore } from '../../store/usePlaygroundStore';
6
- import { useBlueprintNavigation } from '@decido/engine';
7
- import { TreeNodeItem, type TreeNode } from './editor/TreeNodeItem';
8
-
9
- interface DependencyTreePanelProps {
10
- isOpen: boolean;
11
- onClose: () => void;
12
- }
13
-
14
- export const DependencyTreePanel: React.FC<DependencyTreePanelProps> = ({ isOpen, onClose }) => {
15
- const timelines = useTimelineStore(s => s.timelines);
16
- const blueprintLibrary = useTimelineStore(s => s.blueprintLibrary);
17
- const setPrototypeBrand = usePlaygroundStore(s => s.setPrototypeBrand);
18
- const resetNavigation = useBlueprintNavigation(s => s.resetNavigation);
19
- const activeTimelineId = usePlaygroundStore(s => s.prototypeBrand);
20
-
21
- // Build dependency tree
22
- const { roots, orphans, cycleWarnings } = useMemo(() => {
23
- // 1. Scan all timelines for subflow references
24
- const references: Record<string, string[]> = {}; // timelineKey -> [targetBpId, ...]
25
- const referencedBpIds = new Set<string>();
26
-
27
- for (const [key, timeline] of Object.entries(timelines)) {
28
- const subflowNodes = (timeline.keyframes || []).filter(
29
- (kf: any) => kf.track === 'subflow' && kf.targetBlueprintId
30
- );
31
- if (subflowNodes.length > 0) {
32
- references[key] = subflowNodes.map((kf: any) => kf.targetBlueprintId);
33
- subflowNodes.forEach((kf: any) => referencedBpIds.add(kf.targetBlueprintId));
34
- }
35
- }
36
-
37
- // 2. Identify roots: timelines that have subflow children but are NOT themselves referenced
38
- const bpToTimelineKey: Record<string, string> = {};
39
- for (const key of Object.keys(timelines)) {
40
- if (key.startsWith('subflow_')) {
41
- const bpId = `bp_${key.replace('subflow_', '')}`;
42
- bpToTimelineKey[bpId] = key;
43
- }
44
- }
45
-
46
- const referencedTimelineKeys = new Set(
47
- [...referencedBpIds].map(bpId => bpToTimelineKey[bpId]).filter(Boolean)
48
- );
49
-
50
- // 3. Build tree recursively
51
- const cycleWarnings: string[] = [];
52
-
53
- const buildNode = (tlKey: string, visited: Set<string>, depth: number): TreeNode => {
54
- const timeline = timelines[tlKey];
55
- const name = timeline?.name || tlKey;
56
- const isSubflow = tlKey.startsWith('subflow_');
57
- const hasTrigger = (timeline?.keyframes || []).some((kf: any) => kf.track === 'trigger');
58
-
59
- if (visited.has(tlKey)) {
60
- cycleWarnings.push(`Ciclo: ${[...visited].join(' → ')} → ${tlKey}`);
61
- return {
62
- id: tlKey, name, type: 'subflow',
63
- children: [], isCyclic: true, depth
64
- };
65
- }
66
-
67
- const newVisited = new Set(visited);
68
- newVisited.add(tlKey);
69
-
70
- const childBpIds = references[tlKey] || [];
71
- const children: TreeNode[] = childBpIds.map(bpId => {
72
- const childKey = bpToTimelineKey[bpId];
73
- if (childKey) {
74
- return buildNode(childKey, newVisited, depth + 1);
75
- }
76
- // Referenced but no timeline found
77
- return {
78
- id: bpId, name: bpId, type: 'orphan' as const,
79
- children: [], depth: depth + 1
80
- };
81
- });
82
-
83
- return {
84
- id: tlKey,
85
- name,
86
- type: hasTrigger ? 'trigger' : (isSubflow ? 'subflow' : 'blueprint'),
87
- children,
88
- depth
89
- };
90
- };
91
-
92
- // Roots = timelines that reference subflows but aren't themselves subflows
93
- const rootKeys = Object.keys(references).filter(key => !referencedTimelineKeys.has(key));
94
- const roots = rootKeys.map(key => buildNode(key, new Set(), 0));
95
-
96
- // Orphan subflows: in blueprintLibrary but not referenced by anyone
97
- const orphanBpIds = Object.keys(blueprintLibrary).filter(bpId => {
98
- const tlKey = bpToTimelineKey[bpId];
99
- if (!tlKey) return false;
100
- return !referencedBpIds.has(bpId) && !rootKeys.includes(tlKey);
101
- });
102
- const orphans = orphanBpIds.map(bpId => ({
103
- id: bpToTimelineKey[bpId] || bpId,
104
- name: blueprintLibrary[bpId]?.name || bpId,
105
- type: 'subflow' as const,
106
- children: [] as TreeNode[],
107
- depth: 0
108
- }));
109
-
110
- return { roots, orphans, cycleWarnings };
111
- }, [timelines, blueprintLibrary]);
112
-
113
- const handleNavigate = (nodeId: string) => {
114
- resetNavigation();
115
- setPrototypeBrand(nodeId);
116
- };
117
-
118
- return (
119
- <AnimatePresence>
120
- {isOpen && (
121
- <>
122
- <motion.div
123
- initial={{ opacity: 0 }}
124
- animate={{ opacity: 1 }}
125
- exit={{ opacity: 0 }}
126
- className="fixed inset-0 bg-surface-overlay z-40"
127
- onClick={onClose}
128
- />
129
- <motion.div
130
- initial={{ opacity: 0, x: -20, scale: 0.95 }}
131
- animate={{ opacity: 1, x: 0, scale: 1 }}
132
- exit={{ opacity: 0, x: -20, scale: 0.95 }}
133
- transition={{ type: 'spring', damping: 25, stiffness: 300 }}
134
- 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"
135
- style={{ backdropFilter: 'blur(20px)' }}
136
- onClick={(e) => e.stopPropagation()}
137
- >
138
- {/* Header */}
139
- <div className="flex items-center justify-between px-4 py-3 border-b border-border-default bg-surface-glass">
140
- <div className="flex items-center gap-2">
141
- <TreePine size={16} className="text-emerald-400" />
142
- <span className="text-sm font-bold text-text-primary tracking-wide">Jerarquía</span>
143
- {cycleWarnings.length > 0 && (
144
- <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">
145
- <AlertTriangle size={10} /> {cycleWarnings.length} ciclo(s)
146
- </span>
147
- )}
148
- </div>
149
- <button onClick={onClose} className="p-1.5 rounded-lg hover:bg-surface-glass text-text-muted transition-colors">
150
- <X size={14} />
151
- </button>
152
- </div>
153
-
154
- {/* Tree */}
155
- <div className="flex-1 overflow-y-auto custom-scrollbar p-2">
156
- {roots.length === 0 && orphans.length === 0 && (
157
- <div className="text-center text-text-muted text-xs py-8">
158
- No hay flujos con subflows referenciados
159
- </div>
160
- )}
161
-
162
- {roots.map(root => (
163
- <TreeNodeItem
164
- key={root.id}
165
- node={root}
166
- activeId={activeTimelineId}
167
- onNavigate={handleNavigate}
168
- />
169
- ))}
170
-
171
- {orphans.length > 0 && (
172
- <div className="mt-3 pt-3 border-t border-border-subtle">
173
- <div className="px-2 py-1 text-[9px] font-bold text-text-muted uppercase tracking-widest">
174
- Sin Referencia
175
- </div>
176
- {orphans.map(node => (
177
- <TreeNodeItem
178
- key={node.id}
179
- node={node}
180
- activeId={activeTimelineId}
181
- onNavigate={handleNavigate}
182
- />
183
- ))}
184
- </div>
185
- )}
186
- </div>
187
- </motion.div>
188
- </>
189
- )}
190
- </AnimatePresence>
191
- );
192
- };