@decido/shell 1.0.0 → 4.0.1

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 (208) hide show
  1. package/README.md +31 -0
  2. package/package.json +17 -14
  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/lib/debugLogger.ts +0 -80
  181. package/src/lib/networkInterceptor.ts +0 -100
  182. package/src/mocks/decido.tsx +0 -41
  183. package/src/plugins/pluginAPI.ts +0 -190
  184. package/src/store/McpStore.ts +0 -69
  185. package/src/store/UpdaterStore.ts +0 -60
  186. package/src/store/engine.ts +0 -392
  187. package/src/store/index.ts +0 -4
  188. package/src/store/layoutPresets.ts +0 -66
  189. package/src/store/playgroundTypes.ts +0 -98
  190. package/src/store/useActionTimelineStore.ts +0 -48
  191. package/src/store/useDebugPanelStore.ts +0 -98
  192. package/src/store/useDebugProfileStore.ts +0 -130
  193. package/src/store/useLayoutStore.ts +0 -205
  194. package/src/store/useMorphInstanceStore.ts +0 -289
  195. package/src/store/useMorphologyStore.ts +0 -103
  196. package/src/store/usePlaygroundStore.ts +0 -236
  197. package/src/store/useShellRegistry.ts +0 -123
  198. package/src/store/useSuggestionsStore.ts +0 -57
  199. package/src/store/useThemeStore.ts +0 -399
  200. package/src/store/useUIComponentStore.ts +0 -179
  201. package/src/types/DecidoStoryDefinition.ts +0 -43
  202. package/src/utils/ai/ai-architect.ts +0 -92
  203. package/src/utils/ai/ai-code.ts +0 -187
  204. package/src/utils/ai/ai-core.ts +0 -50
  205. package/src/utils/ai/ai-media.ts +0 -292
  206. package/src/utils/layoutGraph.ts +0 -67
  207. package/tsconfig.json +0 -17
  208. package/tsconfig.tsbuildinfo +0 -1
@@ -1,368 +0,0 @@
1
- import React, { useCallback } from 'react';
2
- import { AnimatePresence, motion } from 'motion/react';
3
- import { ArrowLeft, X, Maximize2, Minimize2 } from 'lucide-react';
4
- import { useLayoutStore, type MorphStageType } from '../../store/useLayoutStore';
5
- import { useMorphologyStore } from '../../store/useMorphologyStore';
6
- import { usePlaygroundStore } from '../../store/usePlaygroundStore';
7
- import { useMorphInstanceStore, type MorphInstance } from '../../store/useMorphInstanceStore';
8
- import { resolveShell, registerShell, getRegisteredShellTypes } from '../../store/useShellRegistry';
9
- import { PlaygroundCanvas } from '../playground/PlaygroundCanvas';
10
- import { ArtifactBar } from './ArtifactBar';
11
- import { STAGE_META } from './morphStageMeta';
12
- import { bootShells } from './shells/bootShells';
13
- import { useShellKeyboard } from '../../hooks/useShellKeyboard';
14
- import type { ExecutionStep } from '../../store/playgroundTypes';
15
-
16
- // Register built-in shells on first import
17
- bootShells();
18
-
19
- // Expose stores on window for dev console testing
20
- // ── Standalone Gemini streaming (no React hooks needed) ──
21
- let _abortCtrl: AbortController | null = null;
22
-
23
- async function geminiStreamDirect(prompt: string, model?: string) {
24
- const effectiveModel = model || localStorage.getItem('decido_gemini_model') || 'gemini-3-flash-preview';
25
- const apiKey = localStorage.getItem('google_gemini_api_key')
26
- || (window as any).__DECIDO__?._apiKey
27
- || (import.meta as any).env?.VITE_GEMINI_API_KEY || '';
28
- if (!apiKey) {
29
- console.error('❌ No Gemini API key. Set localStorage "google_gemini_api_key" or VITE_GEMINI_API_KEY');
30
- usePlaygroundStore.getState().addChatMessage({ type: 'error', sender: 'system', text: '⚠️ No se encontró API key de Gemini.' });
31
- return;
32
- }
33
-
34
- const store = usePlaygroundStore.getState();
35
- store.addChatMessage({ type: 'text', sender: 'user', text: prompt });
36
-
37
- const controller = new AbortController();
38
- _abortCtrl = controller;
39
- const steps: ExecutionStep[] = [
40
- { type: 'thinking', label: 'Processing', startTime: Date.now() },
41
- ];
42
-
43
- try {
44
- const url = `https://generativelanguage.googleapis.com/v1beta/models/${effectiveModel}:streamGenerateContent?alt=sse&key=${apiKey}`;
45
- const res = await fetch(url, {
46
- method: 'POST',
47
- headers: { 'Content-Type': 'application/json' },
48
- body: JSON.stringify({
49
- contents: [{ role: 'user', parts: [{ text: prompt }] }],
50
- systemInstruction: { parts: [{ text: 'Eres Decido AI, un asistente del sistema operativo Decido OS. Responde en español, sé conciso y útil.' }] },
51
- generationConfig: { temperature: 0.7, maxOutputTokens: 4096 },
52
- }),
53
- signal: controller.signal,
54
- });
55
-
56
- if (!res.ok) throw new Error(`Gemini API ${res.status}: ${(await res.text()).slice(0, 200)}`);
57
-
58
- steps[0].endTime = Date.now();
59
- steps.push({ type: 'text_delta', label: 'Streaming', startTime: Date.now() });
60
-
61
- const reader = res.body?.getReader();
62
- if (!reader) throw new Error('No reader');
63
- const decoder = new TextDecoder();
64
- let fullText = '';
65
- const msgId = `gemini-${Date.now()}`;
66
- let lineBuffer = ''; // Buffer for partial SSE lines
67
-
68
- while (true) {
69
- const { done, value } = await reader.read();
70
- if (done) break;
71
-
72
- lineBuffer += decoder.decode(value, { stream: true });
73
-
74
- // Process all complete lines in the buffer
75
- const lines = lineBuffer.split('\n');
76
- // Keep the last element (might be partial) in the buffer
77
- lineBuffer = lines.pop() || '';
78
-
79
- for (const line of lines) {
80
- const trimmed = line.trim();
81
- if (!trimmed.startsWith('data: ')) continue;
82
- const jsonStr = trimmed.slice(6);
83
- if (!jsonStr || jsonStr === '[DONE]') continue;
84
- try {
85
- const data = JSON.parse(jsonStr);
86
- const text = data?.candidates?.[0]?.content?.parts?.[0]?.text;
87
- if (text) {
88
- fullText += text;
89
- usePlaygroundStore.getState().upsertChatMessage(msgId, {
90
- id: msgId, type: 'text', sender: 'agent', text: fullText,
91
- timestamp: Date.now(), executionSteps: [...steps],
92
- });
93
- }
94
- } catch (parseErr) {
95
- console.warn('[SSE] JSON parse failed:', jsonStr.slice(0, 100), parseErr);
96
- }
97
- }
98
- }
99
-
100
- // Process any remaining data in the buffer
101
- if (lineBuffer.trim().startsWith('data: ')) {
102
- try {
103
- const text = JSON.parse(lineBuffer.trim().slice(6))?.candidates?.[0]?.content?.parts?.[0]?.text;
104
- if (text) fullText += text;
105
- } catch { /* final chunk */ }
106
- }
107
-
108
- steps[1].endTime = Date.now();
109
- steps.push({ type: 'text_complete', label: 'Complete', startTime: Date.now(), endTime: Date.now() });
110
- usePlaygroundStore.getState().upsertChatMessage(msgId, {
111
- id: msgId, type: 'text', sender: 'agent', text: fullText,
112
- timestamp: Date.now(), executionSteps: steps,
113
- });
114
- console.log('✅ Streaming complete:', fullText.length, 'chars');
115
- } catch (err: any) {
116
- if (err.name === 'AbortError') {
117
- usePlaygroundStore.getState().addChatMessage({ type: 'alert', sender: 'system', text: '⏹️ Generación cancelada.' });
118
- } else {
119
- console.error('❌', err.message);
120
- usePlaygroundStore.getState().addChatMessage({ type: 'error', sender: 'system', text: `❌ ${err.message}` });
121
- }
122
- } finally { _abortCtrl = null; }
123
- }
124
-
125
- if (typeof window !== 'undefined') {
126
- (window as any).__DECIDO__ = {
127
- ...(window as any).__DECIDO__,
128
- useMorphInstanceStore,
129
- useLayoutStore,
130
- useMorphologyStore,
131
- usePlaygroundStore,
132
- registerShell,
133
- resolveShell,
134
- getRegisteredShellTypes,
135
- geminiStream: geminiStreamDirect,
136
- geminiAbort: () => _abortCtrl?.abort(),
137
- // SDK: Plugin extensibility (lazy-loaded to avoid circular deps at module eval)
138
- registerSuggestions: (pluginId: string, items: string[]) => {
139
- import('../../store/useSuggestionsStore').then(m => m.useSuggestionsStore.getState().registerPluginSuggestions(pluginId, items));
140
- },
141
- registerIntent: (p: any) => {
142
- import('../../hooks/useIntentLens').then(m => m.registerIntentPattern(p));
143
- },
144
- getIntentPatterns: () => {
145
- return import('../../hooks/useIntentLens').then(m => m.getIntentPatterns());
146
- },
147
- };
148
- }
149
-
150
- /**
151
- * MorphShell — Polymorphic plugin container.
152
- *
153
- * Supports TWO rendering paths:
154
- * 1. **New API**: MorphInstance from useMorphInstanceStore (1 chat = 1 tab)
155
- * 2. **Legacy API**: MorphStage from useLayoutStore/useMorphologyStore
156
- *
157
- * Resolution: if activeInstance exists → use new path, else → legacy path.
158
- */
159
-
160
- interface MorphShellProps {
161
- showCanvas?: boolean;
162
- setShowCanvas?: (v: boolean) => void;
163
- activeConfig?: any;
164
- prototypeBrand?: string;
165
- activeState?: string;
166
- chartData?: any[];
167
- dragConstraintsRef?: React.RefObject<HTMLDivElement | null> | null;
168
- }
169
-
170
- export function MorphShell({
171
- showCanvas = true,
172
- setShowCanvas,
173
- activeConfig,
174
- prototypeBrand = '',
175
- activeState,
176
- chartData = [],
177
- dragConstraintsRef,
178
- }: MorphShellProps) {
179
- // ── Keyboard shortcuts (Cmd+W close, Cmd+1-9 switch) ──
180
- useShellKeyboard();
181
-
182
- // ── New API: MorphInstance ──
183
- const activeInstance = useMorphInstanceStore((s) => s.getActiveInstance());
184
- const removeInstance = useMorphInstanceStore((s) => s.removeInstance);
185
-
186
- // ── Legacy API: MorphStage ──
187
- const layoutStage = useLayoutStore((s) => s.activeStage);
188
- const layoutHistory = useLayoutStore((s) => s.stageHistory);
189
- const morphStage = useMorphologyStore((s) => s.activeStage);
190
- const morphHistory = useMorphologyStore((s) => s.stageHistory);
191
- const isImmersive = useLayoutStore((s) => s.isImmersive);
192
- const setIsImmersive = useLayoutStore((s) => s.setIsImmersive);
193
-
194
- const activeStage = layoutStage || morphStage;
195
- const stageHistory = layoutStage ? layoutHistory : morphHistory;
196
-
197
- const activeArtifactData = usePlaygroundStore((s) => s.activeArtifactData);
198
- const effectiveStage = activeStage || (activeArtifactData ? { type: 'artifact' as const, data: activeArtifactData } : null);
199
- const renderingType: MorphStageType = effectiveStage?.type || '3d';
200
- const stageMeta = STAGE_META[renderingType] || STAGE_META['custom'];
201
- const StageIcon = stageMeta.icon;
202
-
203
- // ── Tab management ──
204
- const addTab = useLayoutStore((s) => s.addTab);
205
-
206
- React.useEffect(() => {
207
- if (!effectiveStage && !activeInstance) return;
208
- if (activeInstance) {
209
- addTab({
210
- id: `morph-${activeInstance.id}`,
211
- label: activeInstance.label,
212
- componentId: `morph:${activeInstance.shellType}`,
213
- closeable: true,
214
- data: activeInstance,
215
- });
216
- } else if (effectiveStage) {
217
- const tabId = `morph-${renderingType}-${effectiveStage.label || 'stage'}`;
218
- addTab({
219
- id: tabId,
220
- label: effectiveStage.label || renderingType.toUpperCase(),
221
- componentId: `morph:${renderingType}`,
222
- closeable: true,
223
- data: effectiveStage,
224
- });
225
- }
226
- }, [effectiveStage?.type, effectiveStage?.label, activeInstance?.id, activeInstance?.shellType]);
227
-
228
- // ── Actions ──
229
- const handleClose = useCallback(() => {
230
- if (activeInstance) {
231
- removeInstance(activeInstance.id);
232
- }
233
- useLayoutStore.getState().clearStages();
234
- useMorphologyStore.getState().clearStages();
235
- usePlaygroundStore.getState().setActiveArtifactData(null);
236
- setShowCanvas?.(false);
237
- }, [setShowCanvas, activeInstance, removeInstance]);
238
-
239
- const handleBack = useCallback(() => {
240
- if (activeInstance) {
241
- // For MorphInstances: remove current instance and go to previous
242
- const instances = Array.from(useMorphInstanceStore.getState().instances.keys());
243
- const currentIdx = instances.indexOf(activeInstance.id);
244
-
245
- // Remove current instance's tab
246
- useLayoutStore.getState().removeTab(`morph-${activeInstance.id}`);
247
- removeInstance(activeInstance.id);
248
-
249
- // If there are remaining instances, activate the previous one
250
- if (instances.length > 1) {
251
- const prevIdx = currentIdx > 0 ? currentIdx - 1 : instances.length - 2;
252
- const prevId = instances.filter(id => id !== activeInstance.id)[Math.min(prevIdx, instances.length - 2)];
253
- if (prevId) {
254
- useMorphInstanceStore.getState().setActiveInstance(prevId);
255
- // Sync with chat
256
- const prevInstance = useMorphInstanceStore.getState().instances.get(prevId);
257
- if (prevInstance?.sourceChatId) {
258
- usePlaygroundStore.getState().setActiveChatId(prevInstance.sourceChatId);
259
- }
260
- }
261
- } else {
262
- // Last instance — close canvas
263
- handleClose();
264
- }
265
- } else if (stageHistory.length > 0) {
266
- useLayoutStore.getState().popStage();
267
- useMorphologyStore.getState().popStage();
268
- } else {
269
- handleClose();
270
- }
271
- }, [activeInstance, stageHistory.length, handleClose, removeInstance]);
272
-
273
- const toggleImmersive = useCallback(() => {
274
- setIsImmersive(!isImmersive);
275
- }, [isImmersive, setIsImmersive]);
276
-
277
- if (!showCanvas) return null;
278
-
279
- // ── Determine what to render ──
280
- const renderNewAPI = activeInstance && resolveShell(activeInstance.shellType);
281
- const displayLabel = activeInstance?.label || activeStage?.label || (activeArtifactData ? 'Artefacto' : 'Lienzo');
282
- const displayType = activeInstance?.shellType || renderingType;
283
-
284
- return (
285
- <AnimatePresence>
286
- <motion.div
287
- initial={{ opacity: 0, scale: 0.95 }}
288
- animate={{ opacity: 1, scale: 1 }}
289
- exit={{ opacity: 0, scale: 0.95 }}
290
- className="w-full h-full bg-surface-secondary flex flex-col overflow-hidden relative border-l border-border-subtle"
291
- >
292
- {/* ─── MORPH SHELL HEADER ─── */}
293
- <div className="flex justify-between items-center pl-2 z-10 p-3 pb-0 shrink-0">
294
- <div className="flex items-center gap-2">
295
- {(activeStage || activeArtifactData || activeInstance) && (
296
- <button onClick={handleBack} className="p-1.5 rounded-lg hover:bg-surface-glass text-text-muted hover:text-text-primary transition-colors" title="Volver">
297
- <ArrowLeft size={16} />
298
- </button>
299
- )}
300
- <h2 className="text-sm font-bold flex items-center gap-2 text-text-primary">
301
- <div className="w-6 h-6 rounded-lg flex items-center justify-center bg-surface-glass">
302
- <StageIcon size={12} className={stageMeta.color} />
303
- </div>
304
- <span className="truncate max-w-[180px]">{displayLabel}</span>
305
- <span className="text-text-muted font-mono text-[9px] ml-1">{String(displayType).toUpperCase()}</span>
306
- </h2>
307
- </div>
308
-
309
- <div className="flex items-center gap-2">
310
- {stageHistory.length > 0 && (
311
- <div className="hidden md:flex items-center gap-1 text-[9px] text-text-muted font-mono">
312
- {stageHistory.map((s, i) => (
313
- <React.Fragment key={i}>
314
- <span className="hover:text-text-secondary cursor-pointer"
315
- onClick={() => {
316
- const popsNeeded = stageHistory.length - i;
317
- for (let j = 0; j < popsNeeded; j++) {
318
- useLayoutStore.getState().popStage();
319
- useMorphologyStore.getState().popStage();
320
- }
321
- }}>
322
- {s.label || s.type}
323
- </span>
324
- <span>›</span>
325
- </React.Fragment>
326
- ))}
327
- <span className="text-text-secondary">{displayLabel}</span>
328
- </div>
329
- )}
330
- <button onClick={toggleImmersive} className="p-1.5 rounded-lg hover:bg-surface-glass text-text-muted hover:text-text-primary transition-colors" title={isImmersive ? 'Salir de inmersivo' : 'Modo inmersivo'}>
331
- {isImmersive ? <Minimize2 size={14} /> : <Maximize2 size={14} />}
332
- </button>
333
- <button onClick={handleClose} className="p-1.5 bg-surface-glass hover:bg-red-500/20 text-text-secondary hover:text-red-400 rounded-full transition-colors">
334
- <X size={14} />
335
- </button>
336
- </div>
337
- </div>
338
-
339
- {/* ─── ARTIFACT BAR (sub-tabs for multiple files/versions) ─── */}
340
- {activeInstance && <ArtifactBar instanceId={activeInstance.id} />}
341
-
342
- {/* ─── STAGE CONTENT ─── */}
343
- <div className="flex-1 relative overflow-hidden">
344
- {renderNewAPI ? (
345
- // New API: render shell from registry
346
- React.createElement(resolveShell(activeInstance!.shellType)!, {
347
- instanceId: activeInstance!.id,
348
- data: activeInstance!.data,
349
- artifacts: activeInstance!.artifacts,
350
- activeArtifactIndex: activeInstance!.activeArtifactIndex,
351
- })
352
- ) : (
353
- // Legacy: delegate to PlaygroundCanvas
354
- <PlaygroundCanvas
355
- showCanvas={true}
356
- setShowCanvas={setShowCanvas || (() => { })}
357
- activeConfig={activeConfig}
358
- prototypeBrand={prototypeBrand}
359
- activeState={activeState}
360
- chartData={chartData}
361
- dragConstraintsRef={dragConstraintsRef || undefined as any}
362
- />
363
- )}
364
- </div>
365
- </motion.div>
366
- </AnimatePresence>
367
- );
368
- }
@@ -1,147 +0,0 @@
1
- import React from 'react';
2
- import { ExternalLink, RefreshCw, X, Shield, Maximize2, Minimize2 } from 'lucide-react';
3
- import { AnimatePresence, motion } from 'motion/react';
4
-
5
- /**
6
- * PluginViewer — Renders a plugin's standalone UI inside an iframe.
7
- *
8
- * Architecture: OVERLAY PANEL (not a slot component).
9
- * This renders as a fixed/absolute panel that floats over the layout,
10
- * completely decoupled from the R1-R6 slot system.
11
- *
12
- * Props are passed directly by PlaygroundDecidoChannel, not read from tabs.
13
- */
14
-
15
- export interface PluginViewerProps {
16
- /** Whether the overlay is open */
17
- isOpen: boolean;
18
- /** The URL to render in the iframe */
19
- url: string;
20
- /** Display label for the toolbar */
21
- label: string;
22
- /** Plugin identifier */
23
- pluginId: string;
24
- /** Called when the user closes the overlay */
25
- onClose: () => void;
26
- }
27
-
28
- export function PluginViewer({ isOpen, url, label, pluginId, onClose }: PluginViewerProps) {
29
- const iframeRef = React.useRef<HTMLIFrameElement>(null);
30
- const [isLoading, setIsLoading] = React.useState(true);
31
- const [hasError, setHasError] = React.useState(false);
32
- const [isFullscreen, setIsFullscreen] = React.useState(false);
33
-
34
- // Reset loading state when URL changes
35
- React.useEffect(() => {
36
- if (url) {
37
- setIsLoading(true);
38
- setHasError(false);
39
- }
40
- }, [url]);
41
-
42
- const handleRefresh = () => {
43
- if (iframeRef.current) {
44
- setIsLoading(true);
45
- setHasError(false);
46
- iframeRef.current.src = iframeRef.current.src;
47
- }
48
- };
49
-
50
- const handleOpenExternal = () => {
51
- if (url) window.open(url, '_blank');
52
- };
53
-
54
- return (
55
- <AnimatePresence>
56
- {isOpen && url && (
57
- <>
58
- {/* Backdrop — click to close */}
59
- <motion.div
60
- initial={{ opacity: 0 }}
61
- animate={{ opacity: 1 }}
62
- exit={{ opacity: 0 }}
63
- transition={{ duration: 0.2 }}
64
- className="fixed inset-0 bg-black/40 backdrop-blur-sm z-[200]"
65
- onClick={onClose}
66
- />
67
-
68
- {/* Panel */}
69
- <motion.div
70
- initial={{ opacity: 0, x: 40 }}
71
- animate={{ opacity: 1, x: 0 }}
72
- exit={{ opacity: 0, x: 40 }}
73
- transition={{ type: 'spring', damping: 25, stiffness: 300 }}
74
- className={`fixed z-[201] flex flex-col bg-surface-primary border border-border-subtle rounded-xl shadow-2xl overflow-hidden ${
75
- isFullscreen
76
- ? 'inset-2'
77
- : 'top-2 right-2 bottom-2 w-[70vw] max-w-[1100px] min-w-[420px]'
78
- }`}
79
- >
80
- {/* Toolbar */}
81
- <div className="flex-none flex items-center justify-between px-3 py-2 bg-surface-secondary border-b border-border-subtle">
82
- <div className="flex items-center gap-2">
83
- <div className="w-2 h-2 rounded-full bg-emerald-400 animate-pulse" />
84
- <span className="text-xs font-semibold text-text-primary">{label}</span>
85
- <span className="text-[10px] text-text-muted font-mono bg-surface-glass px-1.5 py-0.5 rounded">{pluginId}</span>
86
- </div>
87
- <div className="flex items-center gap-1">
88
- <button onClick={handleRefresh} className="p-1.5 rounded-md text-text-muted hover:text-text-primary hover:bg-surface-glass transition-colors" title="Refresh">
89
- <RefreshCw className="w-3.5 h-3.5" />
90
- </button>
91
- <button onClick={handleOpenExternal} className="p-1.5 rounded-md text-text-muted hover:text-text-primary hover:bg-surface-glass transition-colors" title="Open in browser">
92
- <ExternalLink className="w-3.5 h-3.5" />
93
- </button>
94
- <button onClick={() => setIsFullscreen(!isFullscreen)} className="p-1.5 rounded-md text-text-muted hover:text-text-primary hover:bg-surface-glass transition-colors" title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}>
95
- {isFullscreen ? <Minimize2 className="w-3.5 h-3.5" /> : <Maximize2 className="w-3.5 h-3.5" />}
96
- </button>
97
- <button onClick={onClose} className="p-1.5 rounded-md text-text-muted hover:text-red-400 hover:bg-red-500/10 transition-colors" title="Close">
98
- <X className="w-3.5 h-3.5" />
99
- </button>
100
- </div>
101
- </div>
102
-
103
- {/* Iframe container — rr-block prevents rrweb from serializing cross-origin content */}
104
- <div className="flex-1 relative rr-block" data-rr-block>
105
- {isLoading && (
106
- <div className="absolute inset-0 flex items-center justify-center bg-surface-primary z-10">
107
- <div className="flex flex-col items-center gap-3">
108
- <div className="w-8 h-8 border-2 border-cyan-500/20 rounded-full relative">
109
- <div className="absolute inset-0 w-8 h-8 border-2 border-transparent border-t-cyan-400 rounded-full animate-spin" />
110
- </div>
111
- <p className="text-xs text-text-muted">Loading {label}...</p>
112
- </div>
113
- </div>
114
- )}
115
- {hasError && (
116
- <div className="absolute inset-0 flex items-center justify-center bg-surface-primary z-10">
117
- <div className="flex flex-col items-center gap-3 text-center">
118
- <div className="w-12 h-12 rounded-xl bg-red-500/10 flex items-center justify-center">
119
- <X className="w-6 h-6 text-red-400" />
120
- </div>
121
- <div>
122
- <p className="text-sm font-medium text-text-primary">Connection Failed</p>
123
- <p className="text-xs text-text-muted mt-1">Could not reach {url}</p>
124
- </div>
125
- <button onClick={handleRefresh} className="px-3 py-1.5 rounded-lg text-xs font-medium bg-surface-glass hover:bg-surface-elevated transition-colors">
126
- Retry
127
- </button>
128
- </div>
129
- </div>
130
- )}
131
- <iframe
132
- ref={iframeRef}
133
- src={url}
134
- className="w-full h-full border-0 rr-block"
135
- onLoad={() => { setIsLoading(false); setHasError(false); }}
136
- onError={() => { setIsLoading(false); setHasError(true); }}
137
- sandbox="allow-scripts allow-same-origin allow-popups allow-forms allow-modals"
138
- data-rr-block
139
- title={label}
140
- />
141
- </div>
142
- </motion.div>
143
- </>
144
- )}
145
- </AnimatePresence>
146
- );
147
- }