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