@decido/shell 1.0.0

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/.turbo/turbo-build.log +13 -0
  2. package/package.json +65 -0
  3. package/src/AgentPlayer.tsx +105 -0
  4. package/src/DecidoPlayer.tsx +117 -0
  5. package/src/bridge/BridgeAgent.ts +443 -0
  6. package/src/components/DecidoIcon.tsx +56 -0
  7. package/src/components/JsonTreeEditor.tsx +117 -0
  8. package/src/components/PanelSplitter.tsx +71 -0
  9. package/src/components/PluginErrorBoundary.tsx +69 -0
  10. package/src/components/SafeLiquidUI.tsx +114 -0
  11. package/src/components/TransientLayer.tsx +92 -0
  12. package/src/components/agent/AgentChat.tsx +134 -0
  13. package/src/components/chat-extensions/IntentCatalogPanel.tsx +81 -0
  14. package/src/components/chat-extensions/chatSlashCommands.ts +101 -0
  15. package/src/components/controls/CreatorInputBar.tsx +144 -0
  16. package/src/components/controls/OSToolbar.tsx +90 -0
  17. package/src/components/controls/TimelineTape.tsx +43 -0
  18. package/src/components/debug/ActionTimelineTab.tsx +111 -0
  19. package/src/components/debug/CSSInspectorTab.tsx +436 -0
  20. package/src/components/debug/ExportTab.tsx +192 -0
  21. package/src/components/debug/FlowHealthTab.tsx +86 -0
  22. package/src/components/debug/LogsTab.tsx +110 -0
  23. package/src/components/debug/MorphStackTab.tsx +241 -0
  24. package/src/components/debug/NetworkTab.tsx +173 -0
  25. package/src/components/debug/PerformanceTab.tsx +171 -0
  26. package/src/components/debug/ProfilesTab.tsx +238 -0
  27. package/src/components/debug/ReplayTab.tsx +70 -0
  28. package/src/components/debug/StoresTab.tsx +255 -0
  29. package/src/components/debug/TopologyTab.tsx +59 -0
  30. package/src/components/debug/debugConfig.tsx +66 -0
  31. package/src/components/playground/DebugPanel.tsx +112 -0
  32. package/src/components/playground/HeaderCenterControls.tsx +92 -0
  33. package/src/components/playground/KeyframeListItem.tsx +70 -0
  34. package/src/components/playground/PlaygroundAppSidebar.tsx +171 -0
  35. package/src/components/playground/PlaygroundBottomControls.tsx +132 -0
  36. package/src/components/playground/PlaygroundCanvas.tsx +87 -0
  37. package/src/components/playground/PlaygroundChat.tsx +236 -0
  38. package/src/components/playground/PlaygroundErrorBoundary.tsx +63 -0
  39. package/src/components/playground/PlaygroundFloatingInput.tsx +352 -0
  40. package/src/components/playground/PlaygroundHeader.tsx +222 -0
  41. package/src/components/playground/PlaygroundSidebar.tsx +136 -0
  42. package/src/components/playground/PlaygroundTerminal.tsx +44 -0
  43. package/src/components/playground/SuggestionCards.tsx +29 -0
  44. package/src/components/playground/demos/ClinicaAINode.tsx +221 -0
  45. package/src/components/playground/demos/FinanceAINode.tsx +226 -0
  46. package/src/components/playground/demos/KiaAcademyNode.tsx +250 -0
  47. package/src/components/playground/demos/KiaBotNode.tsx +207 -0
  48. package/src/components/playground/demos/KiaCampaignNode.tsx +191 -0
  49. package/src/components/playground/demos/KiaComplianceNode.tsx +140 -0
  50. package/src/components/playground/demos/KiaCustomerJourneyNode.tsx +220 -0
  51. package/src/components/playground/demos/KiaCyberNode.tsx +203 -0
  52. package/src/components/playground/demos/KiaDashboardNode.tsx +399 -0
  53. package/src/components/playground/demos/KiaEmbudoOverviewNode.tsx +168 -0
  54. package/src/components/playground/demos/KiaExecutiveNode.tsx +169 -0
  55. package/src/components/playground/demos/KiaGamificationNode.tsx +229 -0
  56. package/src/components/playground/demos/KiaIntelligenceHubNode.tsx +165 -0
  57. package/src/components/playground/demos/KiaInventoryNode.tsx +183 -0
  58. package/src/components/playground/demos/KiaLeadScoringNode.tsx +226 -0
  59. package/src/components/playground/demos/KiaLiveSimulationNode.tsx +177 -0
  60. package/src/components/playground/demos/KiaMultiDealerNode.tsx +223 -0
  61. package/src/components/playground/demos/KiaNPSVoiceNode.tsx +214 -0
  62. package/src/components/playground/demos/KiaOmnichannelNode.tsx +162 -0
  63. package/src/components/playground/demos/KiaPBIBudgetNode.tsx +152 -0
  64. package/src/components/playground/demos/KiaPBIConversionNode.tsx +206 -0
  65. package/src/components/playground/demos/KiaPBIFunnelNode.tsx +184 -0
  66. package/src/components/playground/demos/KiaPBIOwnershipNode.tsx +113 -0
  67. package/src/components/playground/demos/KiaPBIPartnerNode.tsx +143 -0
  68. package/src/components/playground/demos/KiaPBIPreciosNode.tsx +120 -0
  69. package/src/components/playground/demos/KiaPBIRuntNode.tsx +205 -0
  70. package/src/components/playground/demos/KiaPartnerScoreNode.tsx +206 -0
  71. package/src/components/playground/demos/KiaPredictiveNode.tsx +226 -0
  72. package/src/components/playground/demos/KiaShowroomNode.tsx +194 -0
  73. package/src/components/playground/demos/KiaStoreNode.tsx +215 -0
  74. package/src/components/playground/demos/KiaSustainabilityNode.tsx +173 -0
  75. package/src/components/playground/demos/KiaUsedVehiclesNode.tsx +163 -0
  76. package/src/components/playground/demos/KiaWorkshopNode.tsx +221 -0
  77. package/src/components/playground/demos/SmartCityNode.tsx +205 -0
  78. package/src/components/playground/demos/kia_campaign_manifest.json +112 -0
  79. package/src/components/playground/input-parts/AIModelSelector.tsx +156 -0
  80. package/src/components/playground/input-parts/InputActions.tsx +80 -0
  81. package/src/components/playground/input-parts/InputToolbar.tsx +245 -0
  82. package/src/components/playground/input-parts/ResourceLibraryPanel.tsx +287 -0
  83. package/src/components/playground/sidebarDsdIO.ts +82 -0
  84. package/src/components/settings/SettingsPanel.tsx +267 -0
  85. package/src/components/shell/AppHeader.tsx +9 -0
  86. package/src/components/shell/AppShell.tsx +139 -0
  87. package/src/components/shell/ArtifactBar.tsx +97 -0
  88. package/src/components/shell/BootScreen.tsx +19 -0
  89. package/src/components/shell/CenterComposite.tsx +87 -0
  90. package/src/components/shell/CodeEditorPanel.tsx +88 -0
  91. package/src/components/shell/GlobalOverlays.tsx +228 -0
  92. package/src/components/shell/LayoutConfigurator.tsx +209 -0
  93. package/src/components/shell/LayoutGrid.tsx +178 -0
  94. package/src/components/shell/MorphShell.tsx +368 -0
  95. package/src/components/shell/PluginViewer.tsx +147 -0
  96. package/src/components/shell/ShellNexusPreview.tsx +458 -0
  97. package/src/components/shell/SlotRenderer.tsx +115 -0
  98. package/src/components/shell/TabBar.tsx +94 -0
  99. package/src/components/shell/TemplateLibrary.tsx +195 -0
  100. package/src/components/shell/layoutConstants.ts +35 -0
  101. package/src/components/shell/morphStageMeta.ts +15 -0
  102. package/src/components/shell/shells/BuiltInShells.tsx +443 -0
  103. package/src/components/shell/shells/DatawayChatShell.tsx +42 -0
  104. package/src/components/shell/shells/TokenPreview.tsx +339 -0
  105. package/src/components/shell/shells/bootShells.ts +31 -0
  106. package/src/components/shells/CreatorShell.tsx +37 -0
  107. package/src/components/shells/DecidoShell.tsx +447 -0
  108. package/src/components/shells/ExperimentalChatShell.tsx +245 -0
  109. package/src/components/shells/UserCanvas.tsx +44 -0
  110. package/src/components/studio/BlueprintManagerPanel.tsx +137 -0
  111. package/src/components/studio/DependencyTreePanel.tsx +192 -0
  112. package/src/components/studio/NodePalette.tsx +92 -0
  113. package/src/components/studio/NodePropertiesPanel.tsx +81 -0
  114. package/src/components/studio/ReactFlowEditor.tsx +242 -0
  115. package/src/components/studio/TimelineEditor.tsx +122 -0
  116. package/src/components/studio/TimelineKeyframeCard.tsx +99 -0
  117. package/src/components/studio/VariablePanel.tsx +181 -0
  118. package/src/components/studio/blueprint/BlueprintCard.tsx +82 -0
  119. package/src/components/studio/editor/CanvasContextMenu.tsx +107 -0
  120. package/src/components/studio/editor/EditorToolbar.tsx +80 -0
  121. package/src/components/studio/editor/StageContentRenderer.tsx +134 -0
  122. package/src/components/studio/editor/TrackPropertyEditors.tsx +133 -0
  123. package/src/components/studio/editor/TreeNodeItem.tsx +91 -0
  124. package/src/components/studio/editor/edgeStyles.ts +43 -0
  125. package/src/components/studio/editor/editorKeyHandler.ts +95 -0
  126. package/src/components/studio/editor/nodeTypeRegistry.ts +137 -0
  127. package/src/components/studio/editor/paletteCatalog.tsx +84 -0
  128. package/src/components/studio/nodes/shell/InteractionNodes.tsx +82 -0
  129. package/src/components/studio/nodes/shell/LayoutControlNodes.tsx +69 -0
  130. package/src/components/studio/nodes/shell/RegisterActionNode.tsx +20 -0
  131. package/src/components/studio/nodes/shell/RegisterButtonNode.tsx +22 -0
  132. package/src/components/studio/nodes/shell/RegisterPanelNode.tsx +19 -0
  133. package/src/components/studio/nodes/shell/RegisterSidebarNode.tsx +19 -0
  134. package/src/components/studio/nodes/shell/RegisterStatusBarNode.tsx +22 -0
  135. package/src/components/studio/nodes/shell/RegisterTabNode.tsx +21 -0
  136. package/src/components/studio/nodes/shell/RegisterTopBarNode.tsx +22 -0
  137. package/src/components/studio/nodes/shell/ShellConfigNode.tsx +51 -0
  138. package/src/components/studio/nodes/shell/ShellNodeBase.tsx +100 -0
  139. package/src/components/studio/nodes/shell/ThemeNodes.tsx +51 -0
  140. package/src/components/studio/nodes/shell/index.ts +12 -0
  141. package/src/components/widgets/BroadcastWidget.tsx +93 -0
  142. package/src/components/widgets/MarketplaceWidget.tsx +298 -0
  143. package/src/components/widgets/McpToolsWidget.tsx +231 -0
  144. package/src/components/widgets/OpsDashboard.tsx +59 -0
  145. package/src/components/widgets/QuickActionsWidget.tsx +60 -0
  146. package/src/components/widgets/UsageWidget.tsx +112 -0
  147. package/src/components/widgets/WidgetRenderer.tsx +892 -0
  148. package/src/components/widgets/WidgetSlotPanel.tsx +213 -0
  149. package/src/config/IconRegistry.ts +126 -0
  150. package/src/contexts/NetworkProvider.tsx +162 -0
  151. package/src/core/AIDirector.ts +71 -0
  152. package/src/core/EventBus.ts +37 -0
  153. package/src/core/PluginContext.tsx +141 -0
  154. package/src/hooks/listeners/useUIStateListener.ts +59 -0
  155. package/src/hooks/listeners/useWhatsAppListener.ts +110 -0
  156. package/src/hooks/morphBridge.ts +82 -0
  157. package/src/hooks/useAIModelSelector.ts +144 -0
  158. package/src/hooks/useAgentStream.ts +220 -0
  159. package/src/hooks/useAutoUpdater.ts +89 -0
  160. package/src/hooks/useBootSequence.ts +20 -0
  161. package/src/hooks/useExportDSD.ts +53 -0
  162. package/src/hooks/useFullscreen.ts +35 -0
  163. package/src/hooks/useGeminiStream.ts +282 -0
  164. package/src/hooks/useIntentLens.ts +224 -0
  165. package/src/hooks/useKeyboardShortcuts.ts +69 -0
  166. package/src/hooks/useLoggerBridge.ts +32 -0
  167. package/src/hooks/useMcpClient.ts +112 -0
  168. package/src/hooks/useNexusaiDeploy.ts +118 -0
  169. package/src/hooks/usePlaybackEngine.ts +21 -0
  170. package/src/hooks/usePlaygroundCommander.ts +475 -0
  171. package/src/hooks/usePluginEngine.ts +165 -0
  172. package/src/hooks/useScreenRecorder.ts +73 -0
  173. package/src/hooks/useShellKeyboard.ts +40 -0
  174. package/src/hooks/useShellShortcuts.ts +118 -0
  175. package/src/hooks/useSoundEffects.ts +35 -0
  176. package/src/hooks/useStudioConfig.ts +72 -0
  177. package/src/hooks/useSystemBoot.ts +84 -0
  178. package/src/hooks/useSystemTelemetry.ts +62 -0
  179. package/src/index.ts +97 -0
  180. package/src/lib/debugLogger.ts +80 -0
  181. package/src/lib/networkInterceptor.ts +100 -0
  182. package/src/mocks/decido.tsx +41 -0
  183. package/src/plugins/pluginAPI.ts +190 -0
  184. package/src/store/McpStore.ts +69 -0
  185. package/src/store/UpdaterStore.ts +60 -0
  186. package/src/store/engine.ts +392 -0
  187. package/src/store/index.ts +4 -0
  188. package/src/store/layoutPresets.ts +66 -0
  189. package/src/store/playgroundTypes.ts +98 -0
  190. package/src/store/useActionTimelineStore.ts +48 -0
  191. package/src/store/useDebugPanelStore.ts +98 -0
  192. package/src/store/useDebugProfileStore.ts +130 -0
  193. package/src/store/useLayoutStore.ts +205 -0
  194. package/src/store/useMorphInstanceStore.ts +289 -0
  195. package/src/store/useMorphologyStore.ts +103 -0
  196. package/src/store/usePlaygroundStore.ts +236 -0
  197. package/src/store/useShellRegistry.ts +123 -0
  198. package/src/store/useSuggestionsStore.ts +57 -0
  199. package/src/store/useThemeStore.ts +399 -0
  200. package/src/store/useUIComponentStore.ts +179 -0
  201. package/src/types/DecidoStoryDefinition.ts +43 -0
  202. package/src/utils/ai/ai-architect.ts +92 -0
  203. package/src/utils/ai/ai-code.ts +187 -0
  204. package/src/utils/ai/ai-core.ts +50 -0
  205. package/src/utils/ai/ai-media.ts +292 -0
  206. package/src/utils/layoutGraph.ts +67 -0
  207. package/tsconfig.json +17 -0
  208. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,73 @@
1
+ import { useState, useRef, useCallback } from 'react';
2
+
3
+ export function useScreenRecorder() {
4
+ const [isRecording, setIsRecording] = useState(false);
5
+ const [countdown, setCountdown] = useState<number | null>(null);
6
+ const mediaRecorderRef = useRef<MediaRecorder | null>(null);
7
+
8
+ const startRecording = useCallback(async () => {
9
+ try {
10
+ const stream = await navigator.mediaDevices.getDisplayMedia({
11
+ video: { displaySurface: "browser", frameRate: { ideal: 60 } },
12
+ audio: true
13
+ });
14
+
15
+ // 11.4: Maximizar bitrate y preferir VP9/H264 premium
16
+ const preferredCodecs = [
17
+ { mimeType: 'video/webm;codecs=vp9,opus', videoBitsPerSecond: 8000000 },
18
+ { mimeType: 'video/webm;codecs=h264,opus', videoBitsPerSecond: 8000000 },
19
+ { mimeType: 'video/webm;codecs=vp8,opus', videoBitsPerSecond: 5000000 },
20
+ { mimeType: 'video/webm', videoBitsPerSecond: 2500000 }
21
+ ];
22
+
23
+ let mType = preferredCodecs.find(opt => MediaRecorder.isTypeSupported(opt.mimeType));
24
+ if (!mType) mType = { mimeType: '', videoBitsPerSecond: 2500000 };
25
+
26
+ const mediaRecorder = new MediaRecorder(stream, mType);
27
+
28
+ let currentChunks: BlobPart[] = [];
29
+ mediaRecorder.ondataavailable = (e) => { if (e.data.size > 0) currentChunks.push(e.data); };
30
+
31
+ mediaRecorder.onstop = () => {
32
+ const blob = new Blob(currentChunks, { type: 'video/webm' });
33
+ const url = URL.createObjectURL(blob);
34
+ const a = document.createElement('a');
35
+ a.style.display = 'none'; a.href = url; a.download = `decido-demo-${new Date().getTime()}.webm`;
36
+ document.body.appendChild(a); a.click(); URL.revokeObjectURL(url);
37
+ setIsRecording(false);
38
+ };
39
+
40
+ mediaRecorderRef.current = mediaRecorder;
41
+
42
+ return new Promise((resolve) => {
43
+ let count = 3;
44
+ setCountdown(count);
45
+ const interval = setInterval(() => {
46
+ count -= 1;
47
+ if (count > 0) {
48
+ setCountdown(count);
49
+ } else {
50
+ clearInterval(interval);
51
+ setCountdown(null);
52
+ mediaRecorder.start();
53
+ setIsRecording(true);
54
+ stream.getVideoTracks()[0].onended = () => { if (mediaRecorder.state !== 'inactive') mediaRecorder.stop(); };
55
+ resolve(true);
56
+ }
57
+ }, 1000);
58
+ });
59
+
60
+ } catch (err) {
61
+ console.error("Error starting recording:", err);
62
+ setIsRecording(false);
63
+ setCountdown(null);
64
+ return false;
65
+ }
66
+ }, []);
67
+
68
+ const stopRecording = useCallback(() => {
69
+ if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') mediaRecorderRef.current.stop();
70
+ }, []);
71
+
72
+ return { isRecording, countdown, startRecording, stopRecording };
73
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * useShellKeyboard — Global keyboard shortcuts for MorphShell.
3
+ *
4
+ * Shortcuts:
5
+ * Cmd+W — Close active tab
6
+ * Cmd+T — New tab (focus chat input)
7
+ * Cmd+1-9 — Switch to tab by index
8
+ */
9
+ import { useEffect } from 'react';
10
+ import { useMorphInstanceStore } from '../store/useMorphInstanceStore';
11
+
12
+ export function useShellKeyboard() {
13
+ useEffect(() => {
14
+ const handleKeyDown = (e: KeyboardEvent) => {
15
+ const isMeta = e.metaKey || e.ctrlKey;
16
+ if (!isMeta) return;
17
+
18
+ const store = useMorphInstanceStore.getState();
19
+
20
+ // Cmd+W — Close active tab
21
+ if (e.key === 'w' && store.activeInstanceId) {
22
+ e.preventDefault();
23
+ store.removeInstance(store.activeInstanceId);
24
+ }
25
+
26
+ // Cmd+1-9 — Switch tab by index
27
+ if (e.key >= '1' && e.key <= '9') {
28
+ const idx = parseInt(e.key) - 1;
29
+ const instances = store.getInstances();
30
+ if (idx < instances.length) {
31
+ e.preventDefault();
32
+ store.setActiveInstance(instances[idx].id);
33
+ }
34
+ }
35
+ };
36
+
37
+ window.addEventListener('keydown', handleKeyDown);
38
+ return () => window.removeEventListener('keydown', handleKeyDown);
39
+ }, []);
40
+ }
@@ -0,0 +1,118 @@
1
+ import { useEffect } from 'react';
2
+ import { useLayoutStore } from '../store/useLayoutStore';
3
+ import { usePlaygroundStore } from '../store/usePlaygroundStore';
4
+ import { useDebugPanelStore } from '../store/useDebugPanelStore';
5
+ import { useTimelineStore } from '@decido/engine';
6
+
7
+ /**
8
+ * useShellShortcuts — Centralized keyboard shortcuts for AppShell layout actions.
9
+ *
10
+ * Shortcuts:
11
+ * - ⌘⇧C → Toggle Creator Mode
12
+ * - ⌘B → Toggle Sidebar
13
+ * - ⌘⇧V → Toggle Canvas
14
+ * - ⌘⇧F → Toggle Immersive Mode
15
+ * - ⌘⇧P → Reset Layout (player preset)
16
+ *
17
+ * This hook should be called once, at the AppShell level.
18
+ * It syncs both useLayoutStore and usePlaygroundStore for backward compat.
19
+ */
20
+ export function useShellShortcuts() {
21
+ useEffect(() => {
22
+ const handler = (e: KeyboardEvent) => {
23
+ // Don't interfere when typing in inputs
24
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
25
+
26
+ const meta = e.metaKey || e.ctrlKey;
27
+
28
+ // ⌘⇧C → Toggle Creator Mode
29
+ if (meta && e.shiftKey && e.code === 'KeyC') {
30
+ e.preventDefault();
31
+ const layout = useLayoutStore.getState();
32
+ const pg = usePlaygroundStore.getState();
33
+ const newVal = !layout.isCreatorMode;
34
+ layout.setIsCreatorMode(newVal);
35
+ pg.setIsCreatorMode(newVal);
36
+ return;
37
+ }
38
+
39
+ // ⌘B → Toggle Sidebar
40
+ if (meta && !e.shiftKey && e.code === 'KeyB') {
41
+ e.preventDefault();
42
+ const layout = useLayoutStore.getState();
43
+ const pg = usePlaygroundStore.getState();
44
+ const newVal = !layout.isSidebarOpen;
45
+ layout.setIsSidebarOpen(newVal);
46
+ pg.setIsSidebarOpen(newVal);
47
+ return;
48
+ }
49
+
50
+ // ⌘⇧V → Toggle Canvas
51
+ if (meta && e.shiftKey && e.code === 'KeyV') {
52
+ e.preventDefault();
53
+ const layout = useLayoutStore.getState();
54
+ const pg = usePlaygroundStore.getState();
55
+ const newVal = !layout.showCanvas;
56
+ layout.setShowCanvas(newVal);
57
+ pg.setShowCanvas(newVal);
58
+ return;
59
+ }
60
+
61
+ // ⌘⇧X → Toggle Chat
62
+ if (meta && e.shiftKey && e.code === 'KeyX') {
63
+ e.preventDefault();
64
+ const layout = useLayoutStore.getState();
65
+ const pg = usePlaygroundStore.getState();
66
+ const newVal = !layout.showChat;
67
+ layout.setShowChat(newVal);
68
+ pg.setShowChat(newVal);
69
+ return;
70
+ }
71
+
72
+ // ⌘⇧F → Toggle Immersive Mode
73
+ if (meta && e.shiftKey && e.code === 'KeyF') {
74
+ e.preventDefault();
75
+ const layout = useLayoutStore.getState();
76
+ layout.setIsImmersive(!layout.isImmersive);
77
+ return;
78
+ }
79
+
80
+ // ⌘⇧P → Reset Layout (player preset)
81
+ if (meta && e.shiftKey && e.code === 'KeyP') {
82
+ e.preventDefault();
83
+ useLayoutStore.getState().resetLayout('player');
84
+ return;
85
+ }
86
+
87
+ // ESC → Exit Immersive Mode (only if already immersive)
88
+ if (e.code === 'Escape' && useLayoutStore.getState().isImmersive) {
89
+ e.preventDefault();
90
+ useLayoutStore.getState().setIsImmersive(false);
91
+ return;
92
+ }
93
+
94
+ // ⌘⇧D → Toggle Debug Panel
95
+ if (meta && e.shiftKey && e.code === 'KeyD') {
96
+ e.preventDefault();
97
+ useDebugPanelStore.getState().togglePanel();
98
+ return;
99
+ }
100
+
101
+ // ⌘Z / ⌘⇧Z → Undo/Redo (topology, only in Creator mode)
102
+ if (meta && e.key === 'z' && usePlaygroundStore.getState().isCreatorMode) {
103
+ e.preventDefault();
104
+ if (e.shiftKey) {
105
+ useTimelineStore.getState().redo();
106
+ usePlaygroundStore.getState().addLog('Topología: Acción Rehecha', 'info');
107
+ } else {
108
+ useTimelineStore.getState().undo();
109
+ usePlaygroundStore.getState().addLog('Topología: Acción Deshecha', 'info');
110
+ }
111
+ return;
112
+ }
113
+ };
114
+
115
+ window.addEventListener('keydown', handler);
116
+ return () => window.removeEventListener('keydown', handler);
117
+ }, []);
118
+ }
@@ -0,0 +1,35 @@
1
+ export const playSound = (type: 'beep' | 'success' | 'alert', isMuted: boolean = false) => {
2
+ if (isMuted) return;
3
+ try {
4
+ const ctx = new (window.AudioContext || (window as any).webkitAudioContext)();
5
+ const osc = ctx.createOscillator();
6
+ const gain = ctx.createGain();
7
+ osc.connect(gain);
8
+ gain.connect(ctx.destination);
9
+
10
+ if (type === 'beep') {
11
+ osc.frequency.setValueAtTime(600, ctx.currentTime);
12
+ gain.gain.setValueAtTime(0.1, ctx.currentTime);
13
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.1);
14
+ osc.start(ctx.currentTime);
15
+ osc.stop(ctx.currentTime + 0.1);
16
+ } else if (type === 'success') {
17
+ osc.frequency.setValueAtTime(400, ctx.currentTime);
18
+ osc.frequency.exponentialRampToValueAtTime(800, ctx.currentTime + 0.2);
19
+ gain.gain.setValueAtTime(0.1, ctx.currentTime);
20
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.3);
21
+ osc.start(ctx.currentTime);
22
+ osc.stop(ctx.currentTime + 0.3);
23
+ } else if (type === 'alert') {
24
+ osc.type = 'square';
25
+ osc.frequency.setValueAtTime(300, ctx.currentTime);
26
+ osc.frequency.setValueAtTime(400, ctx.currentTime + 0.1);
27
+ gain.gain.setValueAtTime(0.1, ctx.currentTime);
28
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.3);
29
+ osc.start(ctx.currentTime);
30
+ osc.stop(ctx.currentTime + 0.3);
31
+ }
32
+ } catch (e) {
33
+ console.error("Audio not supported");
34
+ }
35
+ };
@@ -0,0 +1,72 @@
1
+ import { useState, useEffect, useMemo, useCallback } from 'react';
2
+ import { useAuthStore, LicenseTarget } from '../mocks/decido';
3
+ import { usePlaygroundStore } from '../store/usePlaygroundStore';
4
+
5
+ /**
6
+ * useStudioConfig — Derives active config, suggestions, persona from
7
+ * the raw DecidoStudioProps.config and the current prototypeBrand.
8
+ *
9
+ * Returns: safeConfig, activeConfig, suggestions, setSuggestions,
10
+ * dynamicPersona, authAvailableLicenses, handleProfileSelect.
11
+ */
12
+ export function useStudioConfig(config: any, prototypeBrand: string) {
13
+ const safeConfig = config || { environments: {}, voices: [] };
14
+
15
+ const activeConfig = useMemo(() => {
16
+ return (safeConfig.environments as Record<string, any>)[prototypeBrand] ||
17
+ Object.values(safeConfig.environments)[0] || {
18
+ gradient: 'from-gray-400 to-gray-600', greetingText: 'Console', subtitle: 'Sin configuración',
19
+ borderColor: 'border-border-strong', glowColor: 'shadow-none', buttonColor: 'bg-surface-glass',
20
+ buttonGlow: 'shadow-none', suggestions: [], widgets: []
21
+ };
22
+ }, [safeConfig, prototypeBrand]);
23
+
24
+ const [suggestions, setSuggestions] = useState<any[]>([]);
25
+
26
+ useEffect(() => {
27
+ setSuggestions(prev => {
28
+ const next = activeConfig.suggestions || [];
29
+ if (JSON.stringify(prev) === JSON.stringify(next)) return prev;
30
+ return next;
31
+ });
32
+ }, [activeConfig.suggestions]);
33
+
34
+ // ── Auth + Persona ──
35
+ const authAvailableLicenses = useAuthStore(
36
+ (state: { availableLicenses: LicenseTarget[] }) => state.availableLicenses
37
+ );
38
+ const enterpriseLicense = authAvailableLicenses?.find(
39
+ (l: LicenseTarget) => l.type === 'enterprise'
40
+ );
41
+ const companyName = enterpriseLicense ? enterpriseLicense.name : 'Empresa Local';
42
+
43
+ const dynamicPersona = useMemo(() => ({
44
+ id: config?.persona?.id || 'agent-core',
45
+ name: config?.persona?.name || 'Agent',
46
+ voiceName: config?.persona?.voiceName || 'Zephyr',
47
+ systemInstruction: config?.persona?.systemInstruction || "Eres un agente inteligente del Marco de Trabajo Decido OS. Estás diseñado para proporcionar asistencia rápida, ejecutar comandos del sistema de forma segura, y ayudar al usuario en el uso de la interfaz."
48
+ }), [config]);
49
+
50
+ // ── Profile Selection ──
51
+ const handleProfileSelect = useCallback((license: LicenseTarget) => {
52
+ const { setSelectedProfile, setStep, addLog } = usePlaygroundStore.getState();
53
+ setSelectedProfile(license.name);
54
+ setStep('action');
55
+ setTimeout(() => {
56
+ useAuthStore.getState().login('admin', `${license.name} Context`, authAvailableLicenses);
57
+ useAuthStore.getState().setActiveContext(license.id);
58
+ setStep('chat');
59
+ addLog(`Perfil cargado: ${license.name}`, 'success');
60
+ }, 2500);
61
+ }, [authAvailableLicenses]);
62
+
63
+ return {
64
+ safeConfig,
65
+ activeConfig,
66
+ suggestions,
67
+ setSuggestions,
68
+ dynamicPersona,
69
+ authAvailableLicenses,
70
+ handleProfileSelect,
71
+ };
72
+ }
@@ -0,0 +1,84 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { kernel, StateRehydrationManager } from '@decido/kernel-bridge';
3
+ import { initPluginSecurityManager } from '@decido/plugin-engine';
4
+ import { useSystemTelemetry } from './useSystemTelemetry';
5
+ import type { Instance } from '@decido/plugin-engine';
6
+
7
+ export interface UseSystemBootOptions {
8
+ availableInstances?: Instance[];
9
+ onAutoConnect?: (instance: Instance) => void;
10
+ }
11
+
12
+ export const useSystemBoot = ({ availableInstances = [], onAutoConnect }: UseSystemBootOptions) => {
13
+ const [isRehydrated, setIsRehydrated] = useState(false);
14
+ const [setupComplete, setSetupComplete] = useState(false);
15
+ const [selectedInstance, setSelectedInstance] = useState<Instance | null>(null);
16
+
17
+ // Activar Telemetría Activa del SO
18
+ useSystemTelemetry();
19
+
20
+ useEffect(() => {
21
+ const cleanupSecurityManager = initPluginSecurityManager();
22
+
23
+ const bootSystem = async () => {
24
+ try {
25
+ // Obtenemos los settings base primero para no enviar un objeto parcial y romper Tauri
26
+ const settings: any = await kernel.execute('get_settings').catch(() => ({}));
27
+
28
+ // Version Watcher / Cache Buster
29
+ const ENV_VERSION = (import.meta as any).env?.VITE_APP_VERSION || '1.0.0';
30
+ const cachedVersion = localStorage.getItem('decido_app_version');
31
+
32
+ if (cachedVersion !== ENV_VERSION) {
33
+ console.warn(`[Boot] Version mismatch detected (${cachedVersion} -> ${ENV_VERSION}). Purgando caché para UI Refresh.`);
34
+ // Invalidamos la seleccion del wizard preservando el resto
35
+ await kernel.execute('save_settings', {
36
+ config: {
37
+ gemini_api_key: settings?.gemini_api_key || "",
38
+ qdrant_url: settings?.qdrant_url || "http://localhost:6334",
39
+ qdrant_api_key: settings?.qdrant_api_key || "",
40
+ swarm_key: settings?.swarm_key || null,
41
+ setup_completed: false,
42
+ selected_instance_id: null
43
+ }
44
+ });
45
+ localStorage.setItem('decido_app_version', ENV_VERSION);
46
+
47
+ // Actualizamos nuestra copia en memoria para la lógica que sigue
48
+ settings.setup_completed = false;
49
+ settings.selected_instance_id = null;
50
+ }
51
+ if (settings.setup_completed && settings.swarm_key) {
52
+ await StateRehydrationManager.rehydrate(settings.swarm_key);
53
+ }
54
+ if (settings.setup_completed && settings.selected_instance_id) {
55
+ const inst = availableInstances.find(i => i.id === settings.selected_instance_id);
56
+ if (inst) {
57
+ setSelectedInstance(inst);
58
+ setSetupComplete(true);
59
+
60
+ // Auto-reconnect to the persisted swarm, delegating logic to the host
61
+ if (onAutoConnect) {
62
+ onAutoConnect(inst);
63
+ } else {
64
+ kernel.execute('connect_to_swarm', { redisUrl: inst.ip }).catch((err: any) => {
65
+ console.error('[Boot] Failed to auto-reconnect swarm:', err);
66
+ });
67
+ }
68
+ }
69
+ }
70
+ } catch (error) {
71
+ console.error("[Boot] Error during rehydration:", error);
72
+ } finally {
73
+ setIsRehydrated(true);
74
+ }
75
+ };
76
+ bootSystem();
77
+
78
+ return () => {
79
+ cleanupSecurityManager();
80
+ };
81
+ }, [availableInstances, onAutoConnect]);
82
+
83
+ return { isRehydrated, setupComplete, setSetupComplete, selectedInstance, setSelectedInstance };
84
+ };
@@ -0,0 +1,62 @@
1
+ import { useEffect } from 'react';
2
+ import { kernel } from '@decido/kernel-bridge';
3
+
4
+ export const useSystemTelemetry = () => {
5
+ useEffect(() => {
6
+ // Safe mode bypass
7
+ if ((import.meta as any).env?.VITE_DECIDO_SAFE_MODE === '1') {
8
+ console.log("🛡️ [Telemetry] Performance tracking disabled in Safe Mode");
9
+ return;
10
+ }
11
+
12
+ try {
13
+ if ('PerformanceObserver' in window) {
14
+ // Monitor for Long Tasks that freeze the UI
15
+ const observer = new PerformanceObserver((list) => {
16
+ for (const entry of list.getEntries()) {
17
+ if (entry.duration > 250) { // Threshold: 250ms Long Task
18
+ console.warn(`⚠️ [Telemetry] Long task detected! UI blocked for ${entry.duration.toFixed(0)}ms`, entry);
19
+ kernel.injectEvent({
20
+ type: 'system_alert',
21
+ source: 'telemetry_engine',
22
+ payload: {
23
+ level: 'warning',
24
+ title: 'Performance Degradation',
25
+ message: `A plugin or core component blocked the main thread for ${entry.duration.toFixed(0)}ms.`,
26
+ duration: entry.duration
27
+ }
28
+ });
29
+ }
30
+ }
31
+ });
32
+
33
+ // PerformanceObserver signature for longtasks
34
+ observer.observe({ entryTypes: ['longtask'] });
35
+
36
+ // Memory snapshot (if available in Chrome/Edge natively via experimental API)
37
+ const interval = setInterval(() => {
38
+ const mem = (performance as any).memory;
39
+ if (mem && mem.usedJSHeapSize > mem.jsHeapSizeLimit * 0.9) {
40
+ console.error("🚨 [Telemetry] Critical Memory Warning! Heap > 90%");
41
+ kernel.injectEvent({
42
+ type: 'system_alert',
43
+ source: 'telemetry_engine',
44
+ payload: {
45
+ level: 'error',
46
+ title: 'Critical Memory Exhaustion',
47
+ message: `V8 JS Heap is critically high (${(mem.usedJSHeapSize / 1024 / 1024).toFixed(0)}MB). The application might crash soon. Consider restarting plugins.`
48
+ }
49
+ });
50
+ }
51
+ }, 30000); // Check every 30s
52
+
53
+ return () => {
54
+ observer.disconnect();
55
+ clearInterval(interval);
56
+ };
57
+ }
58
+ } catch (e) {
59
+ console.warn("[Telemetry] PerformanceObserver not fully supported on this platform", e);
60
+ }
61
+ }, []);
62
+ };
package/src/index.ts ADDED
@@ -0,0 +1,97 @@
1
+ /**
2
+ * ═══════════════════════════════════════════════════════════════
3
+ * @decido/shell — Decido OS Shell
4
+ * ─────────────────────────────────────────────────────────────
5
+ * Architecture:
6
+ * @decido/sdk → contracts (IShellAdapter, WidgetRegistry)
7
+ * @decido/engine → runtime (blueprints, AI director, router)
8
+ * @decido/shell → THIS PACKAGE: OS-level composition layer
9
+ *
10
+ * Exports are split into two categories:
11
+ * 1. CORE — infrastructure that EVERY host must consume
12
+ * 2. PLUGINS — feature UI that hosts MAY opt-in to
13
+ * ═══════════════════════════════════════════════════════════════
14
+ */
15
+
16
+ // ── 1. CORE: Stores & Contexts ─────────────────────────────────────────
17
+ export * from './contexts/NetworkProvider'; // from migrated studio/lib
18
+ export * from './store/engine'; // useShellStore (layout, plugins, workspaces)
19
+ export * from "@decido/shell-auth"; // useAuthStore
20
+ export * from '@decido/plugin-engine'; // usePluginStore
21
+ export * from './store/McpStore'; // useMcpStore
22
+ export * from './store/UpdaterStore';
23
+
24
+ // ── 2. CORE: Runtime ─────────────────────────────────────────
25
+ export * from './core/PluginContext';
26
+ export * from './core/EventBus';
27
+ export * from './core/AIDirector';
28
+
29
+ // ── 4. CORE: Layout Primitives ───────────────────────────────
30
+ export * from './components/PanelSplitter';
31
+ export * from './components/TransientLayer';
32
+ export * from './components/SafeLiquidUI';
33
+ export * from './components/PluginErrorBoundary';
34
+
35
+ // ── 5. CORE: Hooks ───────────────────────────────────────────
36
+ export * from './hooks/useKeyboardShortcuts';
37
+ export * from './hooks/usePluginEngine';
38
+ export * from './hooks/useSystemBoot';
39
+ export * from './hooks/useAutoUpdater';
40
+
41
+
42
+ // ── 6. CORE: Bridge ──────────────────────────────────────────
43
+ export { initBridgeAgent, registerStore } from './bridge/BridgeAgent';
44
+
45
+ // ── 7. PLUGINS: Feature UI (Extraídos a @decido/shell-features) ─────────
46
+ // Estos componentes ahora viven en @decido/shell-features
47
+ // y son importados y montados por HostOverlays (desktop-host).
48
+ export * from './components/widgets/MarketplaceWidget';
49
+ export * from './components/widgets/OpsDashboard';
50
+ export * from './components/widgets/McpToolsWidget';
51
+
52
+ // ── 8. PLUGINS: Session Recorder (moved to @decido/shell-telemetry) ─────────
53
+
54
+ // ── Re-exports from @decido/ui-kit for backward compat ──────
55
+ // DecidoIcon and JsonTreeEditor moved to @decido/ui-kit
56
+
57
+ // ── 9. MIGRATED FROM STUDIO ─────────────────────────────────
58
+ export { default as DecidoShell } from './components/shells/DecidoShell';
59
+ export { default as DecidoPlayer } from './DecidoPlayer';
60
+ export { default as AgentPlayer } from './AgentPlayer';
61
+
62
+ // Streaming Hook
63
+ export { useGeminiStream } from './hooks/useGeminiStream';
64
+
65
+ // Store & Config
66
+ export * from './store/useMorphologyStore';
67
+ export * from './store/usePlaygroundStore';
68
+ export {
69
+ type BuiltInShellType, type ShellType, type ShellProps, type MorphArtifact, type ShellMeta,
70
+ registerShell, resolveShell, isShellRegistered, getRegisteredShellTypes, getShellMeta,
71
+ registerMorphComponentCompat, getMorphComponentCompat
72
+ } from './store/useShellRegistry';
73
+ export * from './store/useMorphInstanceStore';
74
+ export * from './store/useUIComponentStore';
75
+ export * from './store/useLayoutStore';
76
+ export * from './store/useDebugPanelStore';
77
+ export * from './store/useThemeStore';
78
+ export { resolveIcon, resolveColor, resolveColorClasses } from './config/IconRegistry';
79
+
80
+ // Hooks
81
+ export { bridgeToMorph, detectShellType } from './hooks/morphBridge';
82
+ export { useShellKeyboard } from './hooks/useShellKeyboard';
83
+ export { useIntentLens } from './hooks/useIntentLens';
84
+ export type { IntentType, IntentToken, DetectedIntent } from './hooks/useIntentLens';
85
+
86
+ // UI Components
87
+ export { AppShell } from './components/shell/AppShell';
88
+ export { MorphShell } from './components/shell/MorphShell';
89
+ export { LayoutGrid } from './components/shell/LayoutGrid';
90
+ export { AppHeader } from './components/shell/AppHeader';
91
+ export { TabBar } from './components/shell/TabBar';
92
+ export { GlobalOverlays } from './components/shell/GlobalOverlays';
93
+ export { LayoutConfigurator } from './components/shell/LayoutConfigurator';
94
+ export { SlotRenderer, registerSlotComponent, getAvailableSlotComponents } from './components/shell/SlotRenderer';
95
+ export { ArtifactBar } from './components/shell/ArtifactBar';
96
+ export { DebugPanel } from './components/playground/DebugPanel';
97
+ export * from './types/DecidoStoryDefinition';
@@ -0,0 +1,80 @@
1
+ /**
2
+ * debugLogger — Centralized instrumentation logger for Decido Studio.
3
+ *
4
+ * Sprint AO: Emits structured log entries to useDebugPanelStore.
5
+ * All subsystems should use this instead of console.log.
6
+ *
7
+ * Usage:
8
+ * import { dlog } from '../lib/debugLogger';
9
+ * dlog.morph('pushStage', { type: '3d', label: 'Lienzo' });
10
+ * dlog.gemini('POST generateContent', { model, duration: 2300 });
11
+ * dlog.chat('addMessage', { sender: 'user', text: '...' });
12
+ */
13
+
14
+ import { useDebugPanelStore, type LogLevel } from '../store/useDebugPanelStore';
15
+ import { useActionTimelineStore } from '../store/useActionTimelineStore';
16
+
17
+ type Category = 'morph' | 'chat' | 'gemini' | 'network' | 'shell' | 'engine' | 'layout' | 'store' | 'system';
18
+
19
+ // Throttle: max 50 logs/sec
20
+ let _logCount = 0;
21
+ let _logResetTimer: ReturnType<typeof setTimeout> | null = null;
22
+ const MAX_LOGS_PER_SEC = 50;
23
+
24
+ function _getTimestamp(): string {
25
+ const now = new Date();
26
+ return `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}.${String(now.getMilliseconds()).padStart(3, '0')}`;
27
+ }
28
+
29
+ function _emit(level: LogLevel, categories: Category[], message: string, data?: any) {
30
+ if (_logCount >= MAX_LOGS_PER_SEC) return;
31
+ _logCount++;
32
+ if (!_logResetTimer) {
33
+ _logResetTimer = setTimeout(() => { _logCount = 0; _logResetTimer = null; }, 1000);
34
+ }
35
+
36
+ const ts = _getTimestamp();
37
+
38
+ useDebugPanelStore.getState().addLogEntry({
39
+ timestamp: ts,
40
+ level,
41
+ categories,
42
+ message,
43
+ data,
44
+ });
45
+
46
+ // Sprint AR: Also feed the Action Timeline
47
+ const cat = categories[0] || 'system';
48
+ const actionName = message.split(' ')[0] || message;
49
+ useActionTimelineStore.getState().addEvent({
50
+ timestamp: ts,
51
+ storeName: cat,
52
+ actionName,
53
+ data,
54
+ level: level === 'debug' ? 'info' : level as any,
55
+ });
56
+ }
57
+
58
+ function _createCategoryLogger(category: Category) {
59
+ return (message: string, data?: any, level: LogLevel = 'info') => {
60
+ _emit(level, [category], message, data);
61
+ };
62
+ }
63
+
64
+ export const dlog = {
65
+ morph: _createCategoryLogger('morph'),
66
+ chat: _createCategoryLogger('chat'),
67
+ gemini: _createCategoryLogger('gemini'),
68
+ network: _createCategoryLogger('network'),
69
+ shell: _createCategoryLogger('shell'),
70
+ engine: _createCategoryLogger('engine'),
71
+ layout: _createCategoryLogger('layout'),
72
+ store: _createCategoryLogger('store'),
73
+ system: _createCategoryLogger('system'),
74
+
75
+ // Convenience: log with explicit level
76
+ info: (category: Category, message: string, data?: any) => _emit('info', [category], message, data),
77
+ warn: (category: Category, message: string, data?: any) => _emit('warn', [category], message, data),
78
+ error: (category: Category, message: string, data?: any) => _emit('error', [category], message, data),
79
+ debug: (category: Category, message: string, data?: any) => _emit('debug', [category], message, data),
80
+ };