@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,447 @@
1
+ import React, { useEffect, useMemo, useCallback } from 'react';
2
+ import { PlaygroundHeader } from '../playground/PlaygroundHeader';
3
+ import { useTriggerRouter } from '@decido/engine';
4
+ import { McpLifecycleManager } from '../../hooks/useMcpClient';
5
+ import { PlaygroundAppSidebar, SidebarMenuSection } from '../playground/PlaygroundAppSidebar';
6
+ import { usePlaygroundStore } from '../../store/usePlaygroundStore';
7
+ import { LayoutGrid } from '../shell/LayoutGrid';
8
+ import { useLayoutStore } from '../../store/useLayoutStore';
9
+ import { DecidoStudioProps } from '@decido/engine';
10
+ import { BootScreen } from '../shell/BootScreen';
11
+ import { useLoggerBridge } from '../../hooks/useLoggerBridge';
12
+ import { useBootSequence } from '../../hooks/useBootSequence';
13
+ import { MorphShell } from '../shell/MorphShell';
14
+ import { useStudioConfig } from '../../hooks/useStudioConfig';
15
+ import { useUIStateListener } from '../../hooks/listeners/useUIStateListener';
16
+ import { useWhatsAppListener } from '../../hooks/listeners/useWhatsAppListener';
17
+ import { pluginWidgetStore } from '../widgets/WidgetSlotPanel';
18
+ import type { ServerDrivenNode } from '../widgets/WidgetRenderer';
19
+ import { useManifestRegistry, registerManifestLoader } from '@decido/plugin-engine';
20
+ import { SentinelUiManifest, SENTINEL_IFRAME_URLS } from '../../../../../plugins/engineering/sentinel-ui/manifest';
21
+ import { ChameleonPlugin } from '../../../../../plugins/official/chameleon/src';
22
+ // โ”€โ”€ Register well-known manifest loaders (dev mode) โ”€โ”€
23
+ // In production, manifests are loaded via Module Federation from the plugin's URL.
24
+ // In dev, we import them directly and register as well-known loaders.
25
+ registerManifestLoader('sentinel-ui', async () => ({
26
+ manifest: {
27
+ ...SentinelUiManifest,
28
+ baseUrl: 'http://localhost:3333',
29
+ iframeUrls: {
30
+ 'sentinel-telemetry': '/',
31
+ 'sentinel-errors': '/errors',
32
+ 'sentinel-replays': '/replays',
33
+ },
34
+ } as any,
35
+ iframeUrls: SENTINEL_IFRAME_URLS,
36
+ }));
37
+
38
+ registerManifestLoader('macia-chameleon-agent', async () => ({
39
+ manifest: ChameleonPlugin.manifest as any,
40
+ }));
41
+
42
+ /**
43
+ * PlaygroundDecidoChannel โ€” Thin Orchestrator.
44
+ *
45
+ * Post-refactor responsibilities:
46
+ * 1. Boot gate (BootScreen)
47
+ * 2. Global side-effects (logger, triggers, MCP)
48
+ * 3. Layout slot sync (creator/player mode โ†’ R3 slot)
49
+ * 4. Sidebar desktop (push layout via flex)
50
+ * 5. Header + Immersive mode controls
51
+ * 6. Delegates center content to CenterComposite
52
+ *
53
+ * NetworkProvider is provided by AppShell (parent).
54
+ */
55
+ export default function PlaygroundDecidoChannel({ aiProvider, VoiceWidget, config }: DecidoStudioProps = {}) {
56
+ // 1. State selectors
57
+ const isBooting = usePlaygroundStore((s) => s.isBooting);
58
+ const isSidebarOpen = usePlaygroundStore((s) => s.isSidebarOpen);
59
+ const setIsSidebarOpen = usePlaygroundStore((s) => s.setIsSidebarOpen);
60
+ const isCreatorMode = usePlaygroundStore((s) => s.isCreatorMode);
61
+ const creatorViewMode = usePlaygroundStore((s) => s.creatorViewMode);
62
+ const prototypeBrand = usePlaygroundStore((s) => s.prototypeBrand);
63
+ const isImmersive = useLayoutStore((s) => s.isImmersive);
64
+ const setIsImmersive = useLayoutStore((s) => s.setIsImmersive);
65
+
66
+ // 2. Global side-effects
67
+ useLoggerBridge();
68
+ useBootSequence();
69
+ useTriggerRouter();
70
+
71
+ // 3. Config (Header modelLabel only)
72
+ const { activeConfig } = useStudioConfig(config, prototypeBrand);
73
+ const { showCanvas } = useUIStateListener();
74
+ useWhatsAppListener();
75
+
76
+ // โ”€โ”€ 4b. Registry-driven multi-manifest plugin system โ”€โ”€
77
+ // PluginManifestRegistry auto-loads manifests when plugins are installed
78
+ // from the Hub and unloads them when removed. Supports Module Federation.
79
+
80
+ const allManifests = useManifestRegistry(s => s.manifests);
81
+ const manifestLoading = useManifestRegistry(s => s.loading);
82
+ const syncWithRegistry = useManifestRegistry(s => s.syncWithRegistry);
83
+ const getManifest = useManifestRegistry(s => s.getManifest);
84
+
85
+ // โ”€โ”€ Persistence: restore widgets on mount (from any loaded manifest) โ”€โ”€
86
+ React.useEffect(() => {
87
+ const manifests = Object.values(allManifests);
88
+ if (manifests.length === 0) return;
89
+
90
+ const restored = pluginWidgetStore.restoreFromPersistence((widgetId: string) => {
91
+ // Resolve SDUI schema from ANY loaded manifest
92
+ if (widgetId.startsWith('sdui-')) {
93
+ const manifestWidgetId = widgetId.replace('sdui-', '');
94
+ for (const m of manifests) {
95
+ const mw = m.widgets?.find((w: any) => w.id === manifestWidgetId);
96
+ if (mw?.schema) return mw.schema as ServerDrivenNode;
97
+ }
98
+ }
99
+ return undefined;
100
+ });
101
+ if (restored && pluginWidgetStore.size > 0) {
102
+ useLayoutStore.getState().setSlot('R3', 'widget-panel');
103
+ }
104
+ }, [allManifests]);
105
+
106
+ /** Open a widget from sidebar click โ€” registry-driven */
107
+ const openPluginView = useCallback((pluginId: string, widgetId: string, label: string) => {
108
+ const manifest = getManifest(pluginId);
109
+ if (!manifest) {
110
+ console.warn(`[Plugin] No manifest loaded for ${pluginId}`);
111
+ return;
112
+ }
113
+ console.log(`[Plugin] Opening widgets for ${pluginId}:${widgetId}`);
114
+
115
+ // Find widget schema from manifest
116
+ const manifestWidget = manifest.widgets?.find((w: any) => w.id === widgetId);
117
+
118
+ // Option A: SDUI native widget (from manifest schema)
119
+ if (manifestWidget?.schema) {
120
+ pluginWidgetStore.register({
121
+ id: `sdui-${widgetId}`,
122
+ pluginId,
123
+ title: `${label} (SDUI)`,
124
+ icon: '๐ŸŸข',
125
+ slot: 'tab',
126
+ schema: manifestWidget.schema as ServerDrivenNode,
127
+ order: 1,
128
+ });
129
+ }
130
+
131
+ // Option B: iframe widget (from manifest iframeUrls or baseUrl)
132
+ const iframeUrl = manifest.iframeUrls?.[widgetId];
133
+ if (iframeUrl !== undefined) {
134
+ const fullUrl = iframeUrl.startsWith('http')
135
+ ? iframeUrl
136
+ : `${manifest.baseUrl || ''}${iframeUrl}`;
137
+ pluginWidgetStore.register({
138
+ id: `iframe-${widgetId}`,
139
+ pluginId,
140
+ title: `${label} (iframe)`,
141
+ icon: '๐Ÿ”ต',
142
+ slot: 'tab',
143
+ url: fullUrl,
144
+ order: 2,
145
+ });
146
+ }
147
+
148
+ // Option C/D: Native React component
149
+ if (manifestWidget?.component) {
150
+ pluginWidgetStore.register({
151
+ id: `react-${widgetId}`,
152
+ pluginId,
153
+ title: `${label}`,
154
+ icon: 'โšก',
155
+ slot: 'tab',
156
+ component: manifestWidget.component,
157
+ order: 3,
158
+ });
159
+ }
160
+
161
+ if (manifestWidget?.schema) {
162
+ pluginWidgetStore.setActive(`sdui-${widgetId}`);
163
+ } else if (manifestWidget?.component) {
164
+ pluginWidgetStore.setActive(`react-${widgetId}`);
165
+ } else {
166
+ pluginWidgetStore.setActive(`iframe-${widgetId}`);
167
+ }
168
+
169
+ useLayoutStore.getState().setSlot('R3', 'widget-panel');
170
+ }, [getManifest]);
171
+
172
+ // โ”€โ”€ Global Command Listener for SDUI/Chat Intents โ”€โ”€
173
+ useEffect(() => {
174
+ const handleOpenPlugin = (e: any) => {
175
+ const pid = e.detail?.pluginId;
176
+ const view = e.detail?.view;
177
+ if (pid === 'chameleon') {
178
+ // Open the Live Studio plugin widget
179
+ // If a specific view was requested, we override the default widget
180
+ let targetViewId = e.detail?.viewId;
181
+
182
+ if (!targetViewId) {
183
+ if (view === 'discovery_admin' || view === 'whatsapp_studio') targetViewId = 'chameleon-discovery-view';
184
+ else if (view === 'enterprise_kanban') targetViewId = 'chameleon-kanban-view';
185
+ else targetViewId = 'chameleon-discovery-view'; // Fallback
186
+ }
187
+
188
+ openPluginView('macia-chameleon-agent', targetViewId, view || 'WhatsApp Studio');
189
+
190
+ // Keep backwards compatibility for inner chameleon events if needed
191
+ if (view) {
192
+ setTimeout(() => window.dispatchEvent(new CustomEvent('chameleon:set-view', { detail: { view } })), 100);
193
+ }
194
+ }
195
+ };
196
+
197
+ const handleChangeView = (e: any) => {
198
+ const view = e.detail?.view?.toLowerCase();
199
+ if (!view) return;
200
+
201
+ if (view.includes('kanban') || view.includes('producciรณn') || view.includes('produccion')) {
202
+ openPluginView('macia-chameleon-agent', 'chameleon-kanban-view', 'Kanban de Producciรณn');
203
+ } else if (view.includes('excel') || view.includes('ventas') || view.includes('datos')) {
204
+ openPluginView('macia-chameleon-agent', 'chameleon-sheet-view', 'Google Sheets');
205
+ } else if (view.includes('chat') || view.includes('whatsapp') || view.includes('studio') || view.includes('discovery')) {
206
+ openPluginView('macia-chameleon-agent', 'chameleon-discovery-view', 'WhatsApp Studio');
207
+ }
208
+ };
209
+
210
+ window.addEventListener('studio:open-plugin', handleOpenPlugin);
211
+ window.addEventListener('studio:change-view', handleChangeView);
212
+
213
+ return () => {
214
+ window.removeEventListener('studio:open-plugin', handleOpenPlugin);
215
+ window.removeEventListener('studio:change-view', handleChangeView);
216
+ };
217
+ }, [openPluginView]);
218
+
219
+ const readInstalledPlugins = React.useCallback((): { name: string; url: string }[] => {
220
+ try {
221
+ const v10 = localStorage.getItem('decido-plugin-registry-v10');
222
+ const v9 = localStorage.getItem('decido-plugin-registry-v9');
223
+ const stored = v10 || v9;
224
+ let registry = [];
225
+ if (stored) {
226
+ const parsed = JSON.parse(stored);
227
+ registry = parsed?.state?.registry || [];
228
+ }
229
+
230
+ const active = registry
231
+ .filter((e: any) => e.enabled)
232
+ .map((e: any) => ({ name: e.name, url: e.url || '' }));
233
+
234
+ // Force inject Chameleon as a pre-installed built-in
235
+ if (!active.some((a: any) => a.name === 'macia-chameleon-agent')) {
236
+ active.push({ name: 'macia-chameleon-agent', url: '' });
237
+ }
238
+
239
+ return active;
240
+ } catch (err) { console.error('[ManifestRegistry] Error reading plugin registry:', err); }
241
+ return [{ name: 'macia-chameleon-agent', url: '' }];
242
+ }, []);
243
+
244
+ const [installedEntries, setInstalledEntries] = React.useState<{ name: string; url: string }[]>(readInstalledPlugins);
245
+
246
+ // Poll for changes in installed plugins
247
+ useEffect(() => {
248
+ setInstalledEntries(readInstalledPlugins());
249
+ const onStorage = () => {
250
+ const newEntries = readInstalledPlugins();
251
+ setInstalledEntries(prev => {
252
+ // Prevent deep re-renders (800ms UI blocks) by bailing out early
253
+ // if the active installations array hasn't functionally changed.
254
+ if (JSON.stringify(prev) === JSON.stringify(newEntries)) return prev;
255
+ return newEntries;
256
+ });
257
+ };
258
+ window.addEventListener('storage', onStorage);
259
+ window.addEventListener('focus', onStorage);
260
+ const interval = setInterval(onStorage, 5000);
261
+ return () => {
262
+ window.removeEventListener('storage', onStorage);
263
+ window.removeEventListener('focus', onStorage);
264
+ clearInterval(interval);
265
+ };
266
+ }, [readInstalledPlugins]);
267
+
268
+ // Sync: when installed plugins change, load/unload manifests
269
+ useEffect(() => {
270
+ if (installedEntries.length > 0) {
271
+ syncWithRegistry(installedEntries);
272
+ }
273
+ }, [installedEntries, syncWithRegistry]);
274
+
275
+ // Build sidebar sections from ALL loaded manifests
276
+ const pluginSidebarSections: SidebarMenuSection[] = useMemo(() => {
277
+ const sections: SidebarMenuSection[] = [];
278
+ const manifests = Object.values(allManifests);
279
+
280
+ for (const manifest of manifests) {
281
+ // Check if this manifest's plugin is still installed
282
+ const isInstalled = installedEntries.some(e =>
283
+ e.name === manifest.id || e.name.includes(manifest.id) || manifest.id.includes(e.name)
284
+ );
285
+ if (!isInstalled) continue;
286
+
287
+ if (manifest.sidebarItems?.length) {
288
+ // Group by section
289
+ const sectionMap = new Map<string, { title: string; items: typeof manifest.sidebarItems }>();
290
+ for (const item of manifest.sidebarItems!) {
291
+ const key = item.section || 'default';
292
+ if (!sectionMap.has(key)) {
293
+ sectionMap.set(key, { title: item.sectionTitle || manifest.name, items: [] });
294
+ }
295
+ sectionMap.get(key)!.items.push(item);
296
+ }
297
+
298
+ for (const [sectionKey, group] of sectionMap) {
299
+ sections.push({
300
+ id: `plugin-${manifest.id}-${sectionKey}`,
301
+ title: group.title,
302
+ collapsible: true,
303
+ items: group.items.map(item => ({
304
+ id: `${manifest.id}-${item.id}`,
305
+ label: item.label,
306
+ onClick: () => {
307
+ openPluginView(
308
+ manifest.id,
309
+ item.targetWidget || '',
310
+ item.label,
311
+ );
312
+ },
313
+ })),
314
+ });
315
+ }
316
+ } else {
317
+ // Plugin with no sidebar items โ€” show a minimal entry
318
+ sections.push({
319
+ id: `plugin-${manifest.id}`,
320
+ title: `๐Ÿงฉ ${manifest.name}`,
321
+ collapsible: true,
322
+ items: manifest.widgets.map(w => ({
323
+ id: `${manifest.id}-${w.id}`,
324
+ label: w.name,
325
+ onClick: () => openPluginView(manifest.id, w.id, w.name),
326
+ })),
327
+ });
328
+ }
329
+ }
330
+
331
+ // Loading indicators
332
+ for (const loadingId of manifestLoading) {
333
+ sections.push({
334
+ id: `loading-${loadingId}`,
335
+ title: 'โณ Loading...',
336
+ collapsible: false,
337
+ items: [{ id: 'loading', label: loadingId, onClick: () => {} }],
338
+ });
339
+ }
340
+
341
+ // โ”€โ”€ WASM Plugins Section โ”€โ”€
342
+ // Detect Tauri environment and offer WASM plugin controls
343
+ if (typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window) {
344
+ sections.push({
345
+ id: 'wasm-plugins',
346
+ title: 'โšก WASM Plugins',
347
+ collapsible: true,
348
+ items: [
349
+ {
350
+ id: 'wasm-demo-math',
351
+ label: 'demo-math-plugin',
352
+ onClick: () => {
353
+ // Register a headless widget for the WASM plugin
354
+ pluginWidgetStore.register({
355
+ id: 'wasm-demo-math',
356
+ pluginId: 'wasm-native',
357
+ title: 'Demo Math (WASM)',
358
+ icon: 'โšก',
359
+ slot: 'tab',
360
+ scriptUrl: 'wasm://demo-math-plugin/run',
361
+ order: 10,
362
+ });
363
+ pluginWidgetStore.setActive('wasm-demo-math');
364
+ useLayoutStore.getState().setSlot('R3', 'widget-panel');
365
+ },
366
+ },
367
+ ],
368
+ });
369
+ }
370
+
371
+ // Fallback for installed plugins with no loaded manifest and no URL
372
+ if (sections.length === 0 && installedEntries.length > 0) {
373
+ for (const entry of installedEntries) {
374
+ if (!entry.url && !allManifests[entry.name]) {
375
+ sections.push({
376
+ id: `plugin-${entry.name}`,
377
+ title: '๐Ÿ”Œ Plugin Instalado',
378
+ collapsible: true,
379
+ items: [{ id: 'info', label: entry.name.substring(0, 24), onClick: () => {} }],
380
+ });
381
+ }
382
+ }
383
+ }
384
+
385
+ console.log('[ManifestRegistry] Sidebar sections:', sections.length, sections.map(s => s.title));
386
+ return sections;
387
+ }, [allManifests, installedEntries, openPluginView, manifestLoading]);
388
+
389
+ // 4. Slot sync: mode/canvas โ†’ LayoutGrid R3
390
+ // widget-panel is the proper slot for plugins; shell modes take precedence
391
+ useEffect(() => {
392
+ const layout = useLayoutStore.getState();
393
+ const currentSlot = layout.slots?.R3;
394
+
395
+ // If widget-panel is active and user opened a plugin, don't overwrite
396
+ if (currentSlot === 'widget-panel') return;
397
+
398
+ if (isCreatorMode) {
399
+ layout.setSlot('R3', creatorViewMode === 'timeline' ? 'timeline-editor' : 'graph-editor');
400
+ } else {
401
+ layout.setSlot('R3', null);
402
+ }
403
+ }, [isCreatorMode, creatorViewMode]);
404
+
405
+ // โ”€โ”€ Boot Gate โ”€โ”€
406
+ if (isBooting) return <BootScreen />;
407
+
408
+ return (
409
+ <>
410
+ <McpLifecycleManager />
411
+ <div className="h-full w-full bg-surface-primary flex font-sans relative overflow-hidden text-text-primary">
412
+ <div className="absolute inset-0 bg-[url('data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E')] opacity-[0.15] mix-blend-overlay pointer-events-none z-0" />
413
+
414
+ {/* Mobile Sidebar Drawer */}
415
+ <div className="md:hidden">
416
+ <PlaygroundAppSidebar isOpen={isSidebarOpen} onClose={() => setIsSidebarOpen(false)} menuSections={pluginSidebarSections} />
417
+ </div>
418
+
419
+ <div className="flex-1 flex h-full relative z-20">
420
+ {/* Desktop Sidebar (push layout) */}
421
+ <div className={`hidden md:flex shrink-0 h-full transition-all duration-300 ease-in-out overflow-hidden bg-surface-secondary ${isSidebarOpen && !isImmersive ? 'w-[280px] min-w-[280px]' : 'w-0 min-w-0'}`}>
422
+ <PlaygroundAppSidebar isOpen={isSidebarOpen && !isImmersive} onClose={() => setIsSidebarOpen(false)} menuSections={pluginSidebarSections} />
423
+ </div>
424
+
425
+ {/* Main Content */}
426
+ <div className="flex-1 flex flex-col h-full min-w-0 overflow-hidden relative">
427
+ {!isImmersive && <PlaygroundHeader modelLabel={activeConfig?.greetingText || 'yo.decido'} />}
428
+
429
+ {isImmersive && (
430
+ <button
431
+ onClick={() => setIsImmersive(false)}
432
+ className="fixed top-3 right-3 z-900 px-3 py-1.5 rounded-full bg-surface-overlay backdrop-blur-md border border-border-default text-[10px] text-text-secondary hover:text-text-primary hover:bg-surface-overlay transition-all opacity-0 hover:opacity-100 focus:opacity-100"
433
+ title="Salir de modo inmersivo (โŒ˜โ‡งF)"
434
+ >
435
+ ESC ยท Salir inmersivo
436
+ </button>
437
+ )}
438
+
439
+ <LayoutGrid slotProps={{ aiProvider, VoiceWidget, config }}>
440
+ <MorphShell />
441
+ </LayoutGrid>
442
+ </div>
443
+ </div>
444
+ </div>
445
+ </>
446
+ );
447
+ }