@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,282 +0,0 @@
1
- /**
2
- * useGeminiStream — Direct Gemini API streaming for Decido Studio chat.
3
- *
4
- * Uses the REST SSE endpoint to stream responses from Gemini,
5
- * emitting ChatMessage V2 fields (thinking, executionSteps) in real time.
6
- *
7
- * 📚 Inspired by Gemini CLI (geminiChat.ts):
8
- * - Retry with exponential backoff on 429/500/503
9
- * - Content retry on empty/invalid responses
10
- * - Abort signal propagation for clean cancellation
11
- * - Chunk validation (finishReason check)
12
- *
13
- * API key is read from:
14
- * 1. localStorage 'google_gemini_api_key'
15
- * 2. import.meta.env.VITE_GEMINI_API_KEY
16
- *
17
- * Usage:
18
- * const { sendMessage, abort, isStreaming } = useGeminiStream();
19
- * sendMessage('Explica qué es un grafo DSD');
20
- */
21
- import { useState, useCallback, useRef } from 'react';
22
- import { usePlaygroundStore } from '../store/usePlaygroundStore';
23
- import type { ExecutionStep } from '../store/playgroundTypes';
24
-
25
- interface UseGeminiStreamOptions {
26
- model?: string;
27
- systemInstruction?: string;
28
- maxRetries?: number; // Max retry attempts (default: 3)
29
- initialRetryDelay?: number; // Initial delay in ms (default: 500)
30
- }
31
-
32
- /** Retry configuration inspired by Gemini CLI's retryWithBackoff */
33
- interface RetryConfig {
34
- maxAttempts: number;
35
- initialDelay: number;
36
- maxDelay: number;
37
- retryableStatusCodes: number[];
38
- }
39
-
40
- const DEFAULT_RETRY: RetryConfig = {
41
- maxAttempts: 3,
42
- initialDelay: 500,
43
- maxDelay: 30000,
44
- retryableStatusCodes: [429, 500, 502, 503],
45
- };
46
-
47
- export function useGeminiStream(options: UseGeminiStreamOptions = {}) {
48
- const {
49
- model = 'gemini-flash-lite-latest',
50
- systemInstruction,
51
- maxRetries = DEFAULT_RETRY.maxAttempts,
52
- initialRetryDelay = DEFAULT_RETRY.initialDelay,
53
- } = options;
54
-
55
- const [isStreaming, setIsStreaming] = useState(false);
56
- const [error, setError] = useState<string | null>(null);
57
- const abortRef = useRef<AbortController | null>(null);
58
- const addChatMessage = usePlaygroundStore(s => s.addChatMessage);
59
-
60
- const getApiKey = useCallback(() => {
61
- return localStorage.getItem('google_gemini_api_key')
62
- || (import.meta as any).env?.VITE_GEMINI_API_KEY
63
- || '';
64
- }, []);
65
-
66
- const sendMessage = useCallback(async (prompt: string) => {
67
- const apiKey = getApiKey();
68
- if (!apiKey) {
69
- setError('No Gemini API key found. Set it in localStorage "google_gemini_api_key" or VITE_GEMINI_API_KEY.');
70
- addChatMessage({
71
- type: 'error', sender: 'system',
72
- text: '⚠️ No se encontró API key de Gemini. Configúrala en localStorage como "google_gemini_api_key".',
73
- });
74
- return;
75
- }
76
-
77
- setIsStreaming(true);
78
- setError(null);
79
-
80
- // Add user message
81
- addChatMessage({ type: 'text', sender: 'user', text: prompt });
82
-
83
- const executionSteps: ExecutionStep[] = [];
84
- const thinkingStart = Date.now();
85
- executionSteps.push({ type: 'thinking', label: 'Processing', startTime: thinkingStart });
86
-
87
- // Create AbortController
88
- const controller = new AbortController();
89
- abortRef.current = controller;
90
-
91
- const retryConfig: RetryConfig = {
92
- ...DEFAULT_RETRY,
93
- maxAttempts: maxRetries,
94
- initialDelay: initialRetryDelay,
95
- };
96
-
97
- try {
98
- const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:streamGenerateContent?alt=sse&key=${apiKey}`;
99
-
100
- const body: any = {
101
- contents: [{ role: 'user', parts: [{ text: prompt }] }],
102
- generationConfig: { temperature: 0.7, maxOutputTokens: 4096 },
103
- };
104
-
105
- if (systemInstruction) {
106
- body.systemInstruction = { parts: [{ text: systemInstruction }] };
107
- }
108
-
109
- // ── Retry loop with exponential backoff (from Gemini CLI) ──
110
- let response: Response | null = null;
111
- let delay = retryConfig.initialDelay;
112
-
113
- for (let attempt = 1; attempt <= retryConfig.maxAttempts; attempt++) {
114
- if (controller.signal.aborted) break;
115
-
116
- try {
117
- response = await fetch(url, {
118
- method: 'POST',
119
- headers: { 'Content-Type': 'application/json' },
120
- body: JSON.stringify(body),
121
- signal: controller.signal,
122
- });
123
-
124
- if (response.ok) break; // Success, exit retry loop
125
-
126
- // Check retryable status
127
- if (retryConfig.retryableStatusCodes.includes(response.status)) {
128
- if (attempt < retryConfig.maxAttempts) {
129
- executionSteps.push({
130
- type: 'retry' as any,
131
- label: `Retry ${attempt}/${retryConfig.maxAttempts} (HTTP ${response.status})`,
132
- startTime: Date.now(),
133
- });
134
- // Update UI with retry step
135
- const retryMsgId = `retry-${Date.now()}`;
136
- usePlaygroundStore.getState().upsertChatMessage(retryMsgId, {
137
- id: retryMsgId, type: 'alert', sender: 'system',
138
- text: `🔄 Reintentando (${attempt}/${retryConfig.maxAttempts})... HTTP ${response.status}`,
139
- timestamp: Date.now(),
140
- });
141
-
142
- await new Promise(resolve => setTimeout(resolve, delay));
143
- delay = Math.min(delay * 2, retryConfig.maxDelay);
144
- response = null; // Reset for next attempt
145
- continue;
146
- }
147
- }
148
-
149
- // Non-retryable error
150
- const errText = await response.text();
151
- throw new Error(`Gemini API error ${response.status}: ${errText.slice(0, 200)}`);
152
- } catch (fetchErr: any) {
153
- if (fetchErr.name === 'AbortError') throw fetchErr;
154
- if (attempt >= retryConfig.maxAttempts) throw fetchErr;
155
-
156
- // Network error → retry
157
- executionSteps.push({
158
- type: 'retry' as any,
159
- label: `Retry ${attempt}/${retryConfig.maxAttempts} (network)`,
160
- startTime: Date.now(),
161
- });
162
- await new Promise(resolve => setTimeout(resolve, delay));
163
- delay = Math.min(delay * 2, retryConfig.maxDelay);
164
- }
165
- }
166
-
167
- if (!response || !response.ok) {
168
- throw new Error('All retry attempts exhausted');
169
- }
170
-
171
- // Mark thinking done, start text streaming
172
- executionSteps[0].endTime = Date.now();
173
- const textStart = Date.now();
174
- executionSteps.push({ type: 'text_delta', label: 'Streaming', startTime: textStart });
175
-
176
- // Read SSE stream
177
- const reader = response.body?.getReader();
178
- if (!reader) throw new Error('No response body reader');
179
-
180
- const decoder = new TextDecoder();
181
- let fullText = '';
182
- const msgId = `gemini-${Date.now()}`;
183
-
184
- while (true) {
185
- const { done, value } = await reader.read();
186
- if (done) break;
187
-
188
- const chunk = decoder.decode(value, { stream: true });
189
- const lines = chunk.split('\n');
190
-
191
- for (const line of lines) {
192
- if (!line.startsWith('data: ')) continue;
193
- const jsonStr = line.slice(6).trim();
194
- if (!jsonStr || jsonStr === '[DONE]') continue;
195
-
196
- try {
197
- const data = JSON.parse(jsonStr);
198
-
199
- // ── Chunk validation (from Gemini CLI) ──
200
- const candidate = data?.candidates?.[0];
201
- if (!candidate) continue; // Skip empty candidates
202
-
203
- const text = candidate?.content?.parts?.[0]?.text;
204
- if (text) {
205
- fullText += text;
206
-
207
- // Upsert the streaming message
208
- usePlaygroundStore.getState().upsertChatMessage(msgId, {
209
- id: msgId,
210
- type: 'text',
211
- sender: 'agent',
212
- text: fullText,
213
- timestamp: Date.now(),
214
- executionSteps: [...executionSteps],
215
- });
216
- }
217
-
218
- // Check finishReason (Gemini CLI validates this)
219
- if (candidate.finishReason && candidate.finishReason !== 'STOP') {
220
- executionSteps.push({
221
- type: 'text_complete',
222
- label: `Finished: ${candidate.finishReason}`,
223
- startTime: Date.now(),
224
- endTime: Date.now(),
225
- });
226
- }
227
- } catch {
228
- // Skip invalid JSON lines
229
- }
230
- }
231
- }
232
-
233
- // ── Content retry: if stream ended with empty text, retry once ──
234
- if (!fullText.trim() && !controller.signal.aborted) {
235
- executionSteps.push({
236
- type: 'retry' as any,
237
- label: 'Content retry (empty response)',
238
- startTime: Date.now(),
239
- });
240
- addChatMessage({
241
- type: 'alert', sender: 'system',
242
- text: '🔄 Respuesta vacía, reintentando...',
243
- });
244
- // Recursive single retry
245
- setIsStreaming(false);
246
- abortRef.current = null;
247
- return sendMessage(prompt);
248
- }
249
-
250
- // Finalize execution steps
251
- executionSteps[executionSteps.length - 1].endTime ??= Date.now();
252
- executionSteps.push({ type: 'text_complete', label: 'Complete', startTime: Date.now(), endTime: Date.now() });
253
-
254
- // Final update
255
- usePlaygroundStore.getState().upsertChatMessage(msgId, {
256
- id: msgId,
257
- type: 'text',
258
- sender: 'agent',
259
- text: fullText,
260
- timestamp: Date.now(),
261
- executionSteps,
262
- });
263
-
264
- } catch (err: any) {
265
- if (err.name === 'AbortError') {
266
- addChatMessage({ type: 'alert', sender: 'system', text: '⏹️ Generación cancelada por el usuario.' });
267
- } else {
268
- setError(err.message);
269
- addChatMessage({ type: 'error', sender: 'system', text: `❌ Error: ${err.message}` });
270
- }
271
- } finally {
272
- setIsStreaming(false);
273
- abortRef.current = null;
274
- }
275
- }, [model, systemInstruction, maxRetries, initialRetryDelay, getApiKey, addChatMessage]);
276
-
277
- const abort = useCallback(() => {
278
- abortRef.current?.abort();
279
- }, []);
280
-
281
- return { sendMessage, abort, isStreaming, error };
282
- }
@@ -1,224 +0,0 @@
1
- /**
2
- * useIntentLens — Real-time intent detection for chat input.
3
- *
4
- * As the user types, this hook detects intent-triggering words/phrases
5
- * and returns colored tokens for visual feedback in the input field.
6
- *
7
- * Only uses regex (Level ①, 0ms). The LLM (Level ②) is called only on submit.
8
- */
9
- import { useMemo } from 'react';
10
-
11
- // ═══════════════════════════════════════════
12
- // Intent Types
13
- // ═══════════════════════════════════════════
14
-
15
- export type IntentType =
16
- | 'CREATE_GRAPH'
17
- | 'EDIT_GRAPH'
18
- | 'UI_ACTION'
19
- | 'MCP_ACTION'
20
- | 'NAVIGATE'
21
- | 'NONE';
22
-
23
- export interface IntentToken {
24
- text: string;
25
- intent: IntentType;
26
- color: string | null;
27
- isMatch: boolean;
28
- }
29
-
30
- export interface DetectedIntent {
31
- type: IntentType;
32
- label: string;
33
- color: string;
34
- icon: string;
35
- matchedPhrases: string[];
36
- }
37
-
38
- // ═══════════════════════════════════════════
39
- // Intent Patterns (mirrors usePlaygroundCommander regex)
40
- // ═══════════════════════════════════════════
41
-
42
- export const INTENT_PATTERNS: Array<{
43
- type: IntentType;
44
- pattern: RegExp;
45
- label: string;
46
- icon: string;
47
- color: string;
48
- examples: string[];
49
- }> = [
50
- {
51
- type: 'EDIT_GRAPH',
52
- pattern: /\b(agrega|suma|añade|conecta|modifica|cambia|elimina|borra|parchea|actualiza|inserta|mueve)\b/gi,
53
- label: 'Editar Grafo',
54
- icon: '✏️',
55
- color: '#f59e0b',
56
- examples: [
57
- 'Agrega un nodo de validación después de login',
58
- 'Conecta el nodo de pago con el de confirmación',
59
- 'Elimina el paso de verificación duplicado',
60
- ],
61
- },
62
- {
63
- type: 'CREATE_GRAPH',
64
- pattern: /\b(grafo|flujo|nodos|blueprint|escenario|onboarding|proceso|workflow|pipeline|trigger|subflujo|orquest)\b|\b(crea|diseña|genera|construye)\s+(un|una|el|la|los|las)\b/gi,
65
- label: 'Crear Grafo',
66
- icon: '🔗',
67
- color: '#10b981',
68
- examples: [
69
- 'Crea un flujo de onboarding para nuevos usuarios',
70
- 'Diseña un pipeline de procesamiento de datos',
71
- 'Genera un blueprint con 5 nodos de decisión',
72
- ],
73
- },
74
- {
75
- type: 'UI_ACTION',
76
- pattern: /\b(shell|tema|theme|sidebar|panel|activity\s*bar|status\s*bar|top\s*bar|notificaci[oó]n|shortcut|atajo|layout)\b|\b(configura)\s+(el|la|los|las)\b/gi,
77
- label: 'Acción UI',
78
- icon: '🎨',
79
- color: '#3b82f6',
80
- examples: [
81
- 'Abre el panel de sidebar',
82
- 'Configura el tema oscuro',
83
- 'Cambia el layout a pantalla completa',
84
- ],
85
- },
86
- {
87
- type: 'MCP_ACTION',
88
- pattern: /\b(ejecuta|invoca|llama|herramienta|tool|api|endpoint)\b/gi,
89
- label: 'Acción MCP',
90
- icon: '⚡',
91
- color: '#a855f7',
92
- examples: [
93
- 'Ejecuta la herramienta de análisis de código',
94
- 'Invoca el endpoint de validación',
95
- 'Llama al API de búsqueda semántica',
96
- ],
97
- },
98
- {
99
- type: 'NAVIGATE',
100
- pattern: /\b(landing|página\s*web|web\s*page|website|diseño\s*web|sitio|homepage|portfolio|dashboard\s*ui|ui\s*design|mockup|prototipo\s*web)\b/gi,
101
- label: 'Web Preview',
102
- icon: '🌐',
103
- color: '#06b6d4',
104
- examples: [
105
- 'Crea una landing page para un SaaS de IA',
106
- 'Diseña un portfolio moderno con tema oscuro',
107
- 'Genera un dashboard UI para métricas de ventas',
108
- 'Crea una página de pricing con 3 planes',
109
- ],
110
- },
111
- ];
112
-
113
- // Dynamic pattern registry for SDK extensibility
114
- let _extraPatterns: typeof INTENT_PATTERNS = [];
115
- export function registerIntentPattern(p: typeof INTENT_PATTERNS[0]) { _extraPatterns.push(p); }
116
- export function getIntentPatterns() { return [...INTENT_PATTERNS, ..._extraPatterns]; }
117
-
118
- // ═══════════════════════════════════════════
119
- // Tokenizer
120
- // ═══════════════════════════════════════════
121
-
122
- function tokenizeWithIntents(input: string): IntentToken[] {
123
- if (!input.trim()) return [];
124
-
125
- // Find all matches across all patterns
126
- const matches: Array<{ start: number; end: number; intent: IntentType; color: string }> = [];
127
-
128
- for (const { type, pattern, color } of INTENT_PATTERNS) {
129
- // Reset regex lastIndex
130
- const re = new RegExp(pattern.source, pattern.flags);
131
- let match;
132
- while ((match = re.exec(input)) !== null) {
133
- matches.push({
134
- start: match.index,
135
- end: match.index + match[0].length,
136
- intent: type,
137
- color,
138
- });
139
- }
140
- }
141
-
142
- // Sort by position
143
- matches.sort((a, b) => a.start - b.start);
144
-
145
- // Build tokens
146
- const tokens: IntentToken[] = [];
147
- let cursor = 0;
148
-
149
- for (const m of matches) {
150
- // Skip overlapping matches
151
- if (m.start < cursor) continue;
152
-
153
- // Non-matching text before this match
154
- if (m.start > cursor) {
155
- tokens.push({
156
- text: input.slice(cursor, m.start),
157
- intent: 'NONE',
158
- color: null,
159
- isMatch: false,
160
- });
161
- }
162
-
163
- // The matched token
164
- tokens.push({
165
- text: input.slice(m.start, m.end),
166
- intent: m.intent,
167
- color: m.color,
168
- isMatch: true,
169
- });
170
-
171
- cursor = m.end;
172
- }
173
-
174
- // Remaining text
175
- if (cursor < input.length) {
176
- tokens.push({
177
- text: input.slice(cursor),
178
- intent: 'NONE',
179
- color: null,
180
- isMatch: false,
181
- });
182
- }
183
-
184
- return tokens;
185
- }
186
-
187
- // ═══════════════════════════════════════════
188
- // Hook
189
- // ═══════════════════════════════════════════
190
-
191
- export function useIntentLens(input: string) {
192
- const result = useMemo(() => {
193
- const tokens = tokenizeWithIntents(input);
194
-
195
- // Deduplicate detected intents
196
- const intentMap = new Map<IntentType, DetectedIntent>();
197
- for (const token of tokens) {
198
- if (token.isMatch && token.intent !== 'NONE') {
199
- const existing = intentMap.get(token.intent);
200
- if (existing) {
201
- existing.matchedPhrases.push(token.text);
202
- } else {
203
- const pattern = INTENT_PATTERNS.find((p) => p.type === token.intent)!;
204
- intentMap.set(token.intent, {
205
- type: token.intent,
206
- label: pattern.label,
207
- color: pattern.color,
208
- icon: pattern.icon,
209
- matchedPhrases: [token.text],
210
- });
211
- }
212
- }
213
- }
214
-
215
- return {
216
- tokens,
217
- detectedIntents: Array.from(intentMap.values()),
218
- hasIntents: intentMap.size > 0,
219
- primaryIntent: intentMap.size > 0 ? Array.from(intentMap.values())[0] : null,
220
- };
221
- }, [input]);
222
-
223
- return result;
224
- }
@@ -1,69 +0,0 @@
1
- import { useEffect } from 'react';
2
- import { useShellStore } from '../store/engine';
3
- import { useCommandRegistry, useShortcutManager } from '@decido/commands';
4
-
5
- export const useKeyboardShortcuts = () => {
6
- const registerCommand = useCommandRegistry((state) => state.registerCommand);
7
- const registerDefaultShortcut = useShortcutManager((state) => state.registerDefaultShortcut);
8
-
9
- useEffect(() => {
10
- // Register Core Commands
11
- registerCommand({
12
- id: 'shell.toggleCommandPalette',
13
- title: 'Toggle Command Palette',
14
- category: 'Shell',
15
- handler: () => {
16
- const state = useShellStore.getState();
17
- state.setCommandPaletteOpen(!state.isCommandPaletteOpen);
18
- }
19
- });
20
- registerDefaultShortcut('shell.toggleCommandPalette', '$mod+k');
21
-
22
- registerCommand({
23
- id: 'shell.openShortcutSettings',
24
- title: 'Configurar Atajos',
25
- category: 'Shell',
26
- handler: () => {
27
- const state = useShellStore.getState();
28
- state.setShortcutSettingsOpen(!state.isShortcutSettingsOpen);
29
- }
30
- });
31
- registerDefaultShortcut('shell.openShortcutSettings', '$mod+Shift+K');
32
-
33
- registerCommand({
34
- id: 'shell.toggleForgeTerminal',
35
- title: 'Toggle Forge Terminal',
36
- category: 'Shell',
37
- handler: () => {
38
- const state = useShellStore.getState();
39
- state.setForgeTerminalOpen(!state.isForgeTerminalOpen);
40
- }
41
- });
42
- registerDefaultShortcut('shell.toggleForgeTerminal', '$mod+j');
43
-
44
- registerCommand({
45
- id: 'shell.toggleGodMode',
46
- title: 'Toggle God Mode',
47
- category: 'Shell',
48
- handler: () => {
49
- const state = useShellStore.getState();
50
- state.setGodModePanelOpen(!state.isGodModePanelOpen);
51
- }
52
- });
53
- registerDefaultShortcut('shell.toggleGodMode', '$mod+Shift+D');
54
-
55
- registerCommand({
56
- id: 'shell.closePanels',
57
- title: 'Close Active Panels',
58
- category: 'Shell',
59
- handler: () => {
60
- const state = useShellStore.getState();
61
- if (state.isCommandPaletteOpen) state.setCommandPaletteOpen(false);
62
- if (state.isForgeTerminalOpen) state.setForgeTerminalOpen(false);
63
- if (state.isGodModePanelOpen) state.setGodModePanelOpen(false);
64
- }
65
- });
66
- registerDefaultShortcut('shell.closePanels', 'Escape');
67
-
68
- }, [registerCommand, registerDefaultShortcut]);
69
- };
@@ -1,32 +0,0 @@
1
- import { useEffect } from 'react';
2
- import { logger } from '@decido/logger';
3
- import { usePlaygroundStore } from '../store/usePlaygroundStore';
4
- import { useDebugPanelStore } from '../store/useDebugPanelStore';
5
-
6
- /**
7
- * useLoggerBridge — Subscribes to @decido/logger and pipes entries to both:
8
- * 1. usePlaygroundStore.addLog (legacy terminal)
9
- * 2. useDebugPanelStore.addLogEntry (structured debug panel)
10
- *
11
- * Should be called once, at the top-level component (PDC or AppShell).
12
- */
13
- export function useLoggerBridge() {
14
- const addLog = usePlaygroundStore((s) => s.addLog);
15
- const addDebugLogEntry = useDebugPanelStore((s) => s.addLogEntry);
16
-
17
- useEffect(() => {
18
- const unsubscribe = logger.subscribe((entry) => {
19
- const catPrefix = entry.categories.length > 0 ? `[${entry.categories.join(',')}] ` : '';
20
- addLog(`${catPrefix}${entry.message}`, entry.level);
21
- addDebugLogEntry({
22
- timestamp: entry.timestamp,
23
- level: entry.level,
24
- categories: entry.categories,
25
- message: entry.message,
26
- data: entry.data,
27
- source: entry.source,
28
- });
29
- });
30
- return () => unsubscribe();
31
- }, [addLog, addDebugLogEntry]);
32
- }