@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.
- package/.turbo/turbo-build.log +13 -0
- package/package.json +65 -0
- package/src/AgentPlayer.tsx +105 -0
- package/src/DecidoPlayer.tsx +117 -0
- package/src/bridge/BridgeAgent.ts +443 -0
- package/src/components/DecidoIcon.tsx +56 -0
- package/src/components/JsonTreeEditor.tsx +117 -0
- package/src/components/PanelSplitter.tsx +71 -0
- package/src/components/PluginErrorBoundary.tsx +69 -0
- package/src/components/SafeLiquidUI.tsx +114 -0
- package/src/components/TransientLayer.tsx +92 -0
- package/src/components/agent/AgentChat.tsx +134 -0
- package/src/components/chat-extensions/IntentCatalogPanel.tsx +81 -0
- package/src/components/chat-extensions/chatSlashCommands.ts +101 -0
- package/src/components/controls/CreatorInputBar.tsx +144 -0
- package/src/components/controls/OSToolbar.tsx +90 -0
- package/src/components/controls/TimelineTape.tsx +43 -0
- package/src/components/debug/ActionTimelineTab.tsx +111 -0
- package/src/components/debug/CSSInspectorTab.tsx +436 -0
- package/src/components/debug/ExportTab.tsx +192 -0
- package/src/components/debug/FlowHealthTab.tsx +86 -0
- package/src/components/debug/LogsTab.tsx +110 -0
- package/src/components/debug/MorphStackTab.tsx +241 -0
- package/src/components/debug/NetworkTab.tsx +173 -0
- package/src/components/debug/PerformanceTab.tsx +171 -0
- package/src/components/debug/ProfilesTab.tsx +238 -0
- package/src/components/debug/ReplayTab.tsx +70 -0
- package/src/components/debug/StoresTab.tsx +255 -0
- package/src/components/debug/TopologyTab.tsx +59 -0
- package/src/components/debug/debugConfig.tsx +66 -0
- package/src/components/playground/DebugPanel.tsx +112 -0
- package/src/components/playground/HeaderCenterControls.tsx +92 -0
- package/src/components/playground/KeyframeListItem.tsx +70 -0
- package/src/components/playground/PlaygroundAppSidebar.tsx +171 -0
- package/src/components/playground/PlaygroundBottomControls.tsx +132 -0
- package/src/components/playground/PlaygroundCanvas.tsx +87 -0
- package/src/components/playground/PlaygroundChat.tsx +236 -0
- package/src/components/playground/PlaygroundErrorBoundary.tsx +63 -0
- package/src/components/playground/PlaygroundFloatingInput.tsx +352 -0
- package/src/components/playground/PlaygroundHeader.tsx +222 -0
- package/src/components/playground/PlaygroundSidebar.tsx +136 -0
- package/src/components/playground/PlaygroundTerminal.tsx +44 -0
- package/src/components/playground/SuggestionCards.tsx +29 -0
- package/src/components/playground/demos/ClinicaAINode.tsx +221 -0
- package/src/components/playground/demos/FinanceAINode.tsx +226 -0
- package/src/components/playground/demos/KiaAcademyNode.tsx +250 -0
- package/src/components/playground/demos/KiaBotNode.tsx +207 -0
- package/src/components/playground/demos/KiaCampaignNode.tsx +191 -0
- package/src/components/playground/demos/KiaComplianceNode.tsx +140 -0
- package/src/components/playground/demos/KiaCustomerJourneyNode.tsx +220 -0
- package/src/components/playground/demos/KiaCyberNode.tsx +203 -0
- package/src/components/playground/demos/KiaDashboardNode.tsx +399 -0
- package/src/components/playground/demos/KiaEmbudoOverviewNode.tsx +168 -0
- package/src/components/playground/demos/KiaExecutiveNode.tsx +169 -0
- package/src/components/playground/demos/KiaGamificationNode.tsx +229 -0
- package/src/components/playground/demos/KiaIntelligenceHubNode.tsx +165 -0
- package/src/components/playground/demos/KiaInventoryNode.tsx +183 -0
- package/src/components/playground/demos/KiaLeadScoringNode.tsx +226 -0
- package/src/components/playground/demos/KiaLiveSimulationNode.tsx +177 -0
- package/src/components/playground/demos/KiaMultiDealerNode.tsx +223 -0
- package/src/components/playground/demos/KiaNPSVoiceNode.tsx +214 -0
- package/src/components/playground/demos/KiaOmnichannelNode.tsx +162 -0
- package/src/components/playground/demos/KiaPBIBudgetNode.tsx +152 -0
- package/src/components/playground/demos/KiaPBIConversionNode.tsx +206 -0
- package/src/components/playground/demos/KiaPBIFunnelNode.tsx +184 -0
- package/src/components/playground/demos/KiaPBIOwnershipNode.tsx +113 -0
- package/src/components/playground/demos/KiaPBIPartnerNode.tsx +143 -0
- package/src/components/playground/demos/KiaPBIPreciosNode.tsx +120 -0
- package/src/components/playground/demos/KiaPBIRuntNode.tsx +205 -0
- package/src/components/playground/demos/KiaPartnerScoreNode.tsx +206 -0
- package/src/components/playground/demos/KiaPredictiveNode.tsx +226 -0
- package/src/components/playground/demos/KiaShowroomNode.tsx +194 -0
- package/src/components/playground/demos/KiaStoreNode.tsx +215 -0
- package/src/components/playground/demos/KiaSustainabilityNode.tsx +173 -0
- package/src/components/playground/demos/KiaUsedVehiclesNode.tsx +163 -0
- package/src/components/playground/demos/KiaWorkshopNode.tsx +221 -0
- package/src/components/playground/demos/SmartCityNode.tsx +205 -0
- package/src/components/playground/demos/kia_campaign_manifest.json +112 -0
- package/src/components/playground/input-parts/AIModelSelector.tsx +156 -0
- package/src/components/playground/input-parts/InputActions.tsx +80 -0
- package/src/components/playground/input-parts/InputToolbar.tsx +245 -0
- package/src/components/playground/input-parts/ResourceLibraryPanel.tsx +287 -0
- package/src/components/playground/sidebarDsdIO.ts +82 -0
- package/src/components/settings/SettingsPanel.tsx +267 -0
- package/src/components/shell/AppHeader.tsx +9 -0
- package/src/components/shell/AppShell.tsx +139 -0
- package/src/components/shell/ArtifactBar.tsx +97 -0
- package/src/components/shell/BootScreen.tsx +19 -0
- package/src/components/shell/CenterComposite.tsx +87 -0
- package/src/components/shell/CodeEditorPanel.tsx +88 -0
- package/src/components/shell/GlobalOverlays.tsx +228 -0
- package/src/components/shell/LayoutConfigurator.tsx +209 -0
- package/src/components/shell/LayoutGrid.tsx +178 -0
- package/src/components/shell/MorphShell.tsx +368 -0
- package/src/components/shell/PluginViewer.tsx +147 -0
- package/src/components/shell/ShellNexusPreview.tsx +458 -0
- package/src/components/shell/SlotRenderer.tsx +115 -0
- package/src/components/shell/TabBar.tsx +94 -0
- package/src/components/shell/TemplateLibrary.tsx +195 -0
- package/src/components/shell/layoutConstants.ts +35 -0
- package/src/components/shell/morphStageMeta.ts +15 -0
- package/src/components/shell/shells/BuiltInShells.tsx +443 -0
- package/src/components/shell/shells/DatawayChatShell.tsx +42 -0
- package/src/components/shell/shells/TokenPreview.tsx +339 -0
- package/src/components/shell/shells/bootShells.ts +31 -0
- package/src/components/shells/CreatorShell.tsx +37 -0
- package/src/components/shells/DecidoShell.tsx +447 -0
- package/src/components/shells/ExperimentalChatShell.tsx +245 -0
- package/src/components/shells/UserCanvas.tsx +44 -0
- package/src/components/studio/BlueprintManagerPanel.tsx +137 -0
- package/src/components/studio/DependencyTreePanel.tsx +192 -0
- package/src/components/studio/NodePalette.tsx +92 -0
- package/src/components/studio/NodePropertiesPanel.tsx +81 -0
- package/src/components/studio/ReactFlowEditor.tsx +242 -0
- package/src/components/studio/TimelineEditor.tsx +122 -0
- package/src/components/studio/TimelineKeyframeCard.tsx +99 -0
- package/src/components/studio/VariablePanel.tsx +181 -0
- package/src/components/studio/blueprint/BlueprintCard.tsx +82 -0
- package/src/components/studio/editor/CanvasContextMenu.tsx +107 -0
- package/src/components/studio/editor/EditorToolbar.tsx +80 -0
- package/src/components/studio/editor/StageContentRenderer.tsx +134 -0
- package/src/components/studio/editor/TrackPropertyEditors.tsx +133 -0
- package/src/components/studio/editor/TreeNodeItem.tsx +91 -0
- package/src/components/studio/editor/edgeStyles.ts +43 -0
- package/src/components/studio/editor/editorKeyHandler.ts +95 -0
- package/src/components/studio/editor/nodeTypeRegistry.ts +137 -0
- package/src/components/studio/editor/paletteCatalog.tsx +84 -0
- package/src/components/studio/nodes/shell/InteractionNodes.tsx +82 -0
- package/src/components/studio/nodes/shell/LayoutControlNodes.tsx +69 -0
- package/src/components/studio/nodes/shell/RegisterActionNode.tsx +20 -0
- package/src/components/studio/nodes/shell/RegisterButtonNode.tsx +22 -0
- package/src/components/studio/nodes/shell/RegisterPanelNode.tsx +19 -0
- package/src/components/studio/nodes/shell/RegisterSidebarNode.tsx +19 -0
- package/src/components/studio/nodes/shell/RegisterStatusBarNode.tsx +22 -0
- package/src/components/studio/nodes/shell/RegisterTabNode.tsx +21 -0
- package/src/components/studio/nodes/shell/RegisterTopBarNode.tsx +22 -0
- package/src/components/studio/nodes/shell/ShellConfigNode.tsx +51 -0
- package/src/components/studio/nodes/shell/ShellNodeBase.tsx +100 -0
- package/src/components/studio/nodes/shell/ThemeNodes.tsx +51 -0
- package/src/components/studio/nodes/shell/index.ts +12 -0
- package/src/components/widgets/BroadcastWidget.tsx +93 -0
- package/src/components/widgets/MarketplaceWidget.tsx +298 -0
- package/src/components/widgets/McpToolsWidget.tsx +231 -0
- package/src/components/widgets/OpsDashboard.tsx +59 -0
- package/src/components/widgets/QuickActionsWidget.tsx +60 -0
- package/src/components/widgets/UsageWidget.tsx +112 -0
- package/src/components/widgets/WidgetRenderer.tsx +892 -0
- package/src/components/widgets/WidgetSlotPanel.tsx +213 -0
- package/src/config/IconRegistry.ts +126 -0
- package/src/contexts/NetworkProvider.tsx +162 -0
- package/src/core/AIDirector.ts +71 -0
- package/src/core/EventBus.ts +37 -0
- package/src/core/PluginContext.tsx +141 -0
- package/src/hooks/listeners/useUIStateListener.ts +59 -0
- package/src/hooks/listeners/useWhatsAppListener.ts +110 -0
- package/src/hooks/morphBridge.ts +82 -0
- package/src/hooks/useAIModelSelector.ts +144 -0
- package/src/hooks/useAgentStream.ts +220 -0
- package/src/hooks/useAutoUpdater.ts +89 -0
- package/src/hooks/useBootSequence.ts +20 -0
- package/src/hooks/useExportDSD.ts +53 -0
- package/src/hooks/useFullscreen.ts +35 -0
- package/src/hooks/useGeminiStream.ts +282 -0
- package/src/hooks/useIntentLens.ts +224 -0
- package/src/hooks/useKeyboardShortcuts.ts +69 -0
- package/src/hooks/useLoggerBridge.ts +32 -0
- package/src/hooks/useMcpClient.ts +112 -0
- package/src/hooks/useNexusaiDeploy.ts +118 -0
- package/src/hooks/usePlaybackEngine.ts +21 -0
- package/src/hooks/usePlaygroundCommander.ts +475 -0
- package/src/hooks/usePluginEngine.ts +165 -0
- package/src/hooks/useScreenRecorder.ts +73 -0
- package/src/hooks/useShellKeyboard.ts +40 -0
- package/src/hooks/useShellShortcuts.ts +118 -0
- package/src/hooks/useSoundEffects.ts +35 -0
- package/src/hooks/useStudioConfig.ts +72 -0
- package/src/hooks/useSystemBoot.ts +84 -0
- package/src/hooks/useSystemTelemetry.ts +62 -0
- package/src/index.ts +97 -0
- package/src/lib/debugLogger.ts +80 -0
- package/src/lib/networkInterceptor.ts +100 -0
- package/src/mocks/decido.tsx +41 -0
- package/src/plugins/pluginAPI.ts +190 -0
- package/src/store/McpStore.ts +69 -0
- package/src/store/UpdaterStore.ts +60 -0
- package/src/store/engine.ts +392 -0
- package/src/store/index.ts +4 -0
- package/src/store/layoutPresets.ts +66 -0
- package/src/store/playgroundTypes.ts +98 -0
- package/src/store/useActionTimelineStore.ts +48 -0
- package/src/store/useDebugPanelStore.ts +98 -0
- package/src/store/useDebugProfileStore.ts +130 -0
- package/src/store/useLayoutStore.ts +205 -0
- package/src/store/useMorphInstanceStore.ts +289 -0
- package/src/store/useMorphologyStore.ts +103 -0
- package/src/store/usePlaygroundStore.ts +236 -0
- package/src/store/useShellRegistry.ts +123 -0
- package/src/store/useSuggestionsStore.ts +57 -0
- package/src/store/useThemeStore.ts +399 -0
- package/src/store/useUIComponentStore.ts +179 -0
- package/src/types/DecidoStoryDefinition.ts +43 -0
- package/src/utils/ai/ai-architect.ts +92 -0
- package/src/utils/ai/ai-code.ts +187 -0
- package/src/utils/ai/ai-core.ts +50 -0
- package/src/utils/ai/ai-media.ts +292 -0
- package/src/utils/layoutGraph.ts +67 -0
- package/tsconfig.json +17 -0
- 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
|
+
}
|