@decido/shell 1.0.0 → 4.0.2
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 +27 -18
- 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/index.ts +0 -97
- 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,443 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ═══════════════════════════════════════════════════════
|
|
3
|
-
* BridgeAgent — postMessage controller for external tools
|
|
4
|
-
* Exposes DOM actions, store inspection, and JS eval
|
|
5
|
-
* via cross-origin JSON-RPC 2.0 protocol
|
|
6
|
-
* ═══════════════════════════════════════════════════════
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// ─── Types ───
|
|
10
|
-
interface BridgeRequest {
|
|
11
|
-
jsonrpc: '2.0';
|
|
12
|
-
method: string;
|
|
13
|
-
params?: any;
|
|
14
|
-
id?: string | number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface StoreEntry {
|
|
18
|
-
name: string;
|
|
19
|
-
store: any; // Zustand store with getState/setState
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// ─── Global store registry ───
|
|
23
|
-
const storeRegistry: StoreEntry[] = [];
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Register a Zustand store so the bridge can read/write it
|
|
27
|
-
*/
|
|
28
|
-
export function registerStore(name: string, store: any) {
|
|
29
|
-
// Avoid duplicates
|
|
30
|
-
if (!storeRegistry.find(s => s.name === name)) {
|
|
31
|
-
storeRegistry.push({ name, store });
|
|
32
|
-
}
|
|
33
|
-
// Also expose on window for compatibility
|
|
34
|
-
if (typeof window !== 'undefined') {
|
|
35
|
-
(window as any).__ZUSTAND_STORES__ = (window as any).__ZUSTAND_STORES__ || {};
|
|
36
|
-
(window as any).__ZUSTAND_STORES__[name] = store;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Initialize the bridge agent. Call once at app boot.
|
|
42
|
-
*/
|
|
43
|
-
export function initBridgeAgent() {
|
|
44
|
-
if (typeof window === 'undefined') return;
|
|
45
|
-
if ((window as any).__BRIDGE_AGENT__) return; // already initialized
|
|
46
|
-
|
|
47
|
-
console.log('[BridgeAgent] Initializing postMessage bridge v1.0...');
|
|
48
|
-
|
|
49
|
-
const reply = (source: MessageEventSource | null, id: string | number | undefined, result: any, error?: any) => {
|
|
50
|
-
if (!id || !source) return;
|
|
51
|
-
try {
|
|
52
|
-
(source as Window).postMessage({ jsonrpc: '2.0', id, result, error: error || null }, '*');
|
|
53
|
-
} catch { /* cross-origin reply failure, silently ignore */ }
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const notify = (source: MessageEventSource | null, method: string, params: any) => {
|
|
57
|
-
try {
|
|
58
|
-
(source as Window).postMessage({ jsonrpc: '2.0', method, params }, '*');
|
|
59
|
-
} catch { /* ignore */ }
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// ─── Serialize state safely ───
|
|
63
|
-
const serializeState = (state: any, depth = 0): any => {
|
|
64
|
-
if (depth > 4) return '[max depth]';
|
|
65
|
-
if (state === null || state === undefined) return state;
|
|
66
|
-
if (typeof state === 'function') return '[Function]';
|
|
67
|
-
if (typeof state !== 'object') return state;
|
|
68
|
-
if (Array.isArray(state)) return state.slice(0, 50).map(v => serializeState(v, depth + 1));
|
|
69
|
-
if (state instanceof Map) return Object.fromEntries(
|
|
70
|
-
Array.from(state.entries()).slice(0, 50).map(([k, v]) => [String(k), serializeState(v, depth + 1)])
|
|
71
|
-
);
|
|
72
|
-
if (state instanceof Set) return Array.from(state).slice(0, 50).map(v => serializeState(v, depth + 1));
|
|
73
|
-
|
|
74
|
-
const result: any = {};
|
|
75
|
-
for (const key of Object.keys(state).slice(0, 100)) {
|
|
76
|
-
result[key] = serializeState(state[key], depth + 1);
|
|
77
|
-
}
|
|
78
|
-
return result;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// ─── getCssSelector ───
|
|
82
|
-
const getCssSelector = (el: Element): string => {
|
|
83
|
-
if (el.id) return `#${el.id}`;
|
|
84
|
-
const parts: string[] = [];
|
|
85
|
-
let current: Element | null = el;
|
|
86
|
-
while (current && current !== document.body) {
|
|
87
|
-
let selector = current.tagName.toLowerCase();
|
|
88
|
-
if (current.id) { parts.unshift(`#${current.id}`); break; }
|
|
89
|
-
if (current.className && typeof current.className === 'string') {
|
|
90
|
-
const cls = current.className.trim().split(/\s+/).filter(c => !c.includes('/')).slice(0, 2).join('.');
|
|
91
|
-
if (cls) selector += `.${cls}`;
|
|
92
|
-
}
|
|
93
|
-
const parent = current.parentElement;
|
|
94
|
-
if (parent) {
|
|
95
|
-
const siblings = Array.from(parent.children).filter(c => c.tagName === current!.tagName);
|
|
96
|
-
if (siblings.length > 1) {
|
|
97
|
-
selector += `:nth-of-type(${siblings.indexOf(current) + 1})`;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
parts.unshift(selector);
|
|
101
|
-
current = current.parentElement;
|
|
102
|
-
}
|
|
103
|
-
return parts.join(' > ');
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// ─── Recorder state ───
|
|
107
|
-
let isRecording = false;
|
|
108
|
-
let recorderCleanup: (() => void) | null = null;
|
|
109
|
-
let recorderSource: MessageEventSource | null = null;
|
|
110
|
-
|
|
111
|
-
// ─── Message handler ───
|
|
112
|
-
const handleMessage = async (event: MessageEvent) => {
|
|
113
|
-
const data = event.data as BridgeRequest;
|
|
114
|
-
if (!data || data.jsonrpc !== '2.0' || !data.method) return;
|
|
115
|
-
|
|
116
|
-
const { method, params, id } = data;
|
|
117
|
-
const source = event.source;
|
|
118
|
-
|
|
119
|
-
try {
|
|
120
|
-
switch (method) {
|
|
121
|
-
// ─── Lifecycle ───
|
|
122
|
-
case 'PING':
|
|
123
|
-
case 'HANDSHAKE': {
|
|
124
|
-
reply(source, id, {
|
|
125
|
-
success: true,
|
|
126
|
-
version: '1.0.0',
|
|
127
|
-
stores: storeRegistry.map(s => s.name),
|
|
128
|
-
url: window.location.href,
|
|
129
|
-
title: document.title,
|
|
130
|
-
});
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// ─── DOM Actions ───
|
|
135
|
-
case 'CLICK': {
|
|
136
|
-
const el = document.querySelector(params.selector) as HTMLElement;
|
|
137
|
-
if (!el) throw new Error(`Element not found: ${params.selector}`);
|
|
138
|
-
el.click();
|
|
139
|
-
el.focus?.();
|
|
140
|
-
reply(source, id, { success: true });
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
case 'DBLCLICK': {
|
|
144
|
-
const el = document.querySelector(params.selector);
|
|
145
|
-
if (!el) throw new Error(`Element not found: ${params.selector}`);
|
|
146
|
-
el.dispatchEvent(new MouseEvent('dblclick', { bubbles: true }));
|
|
147
|
-
reply(source, id, { success: true });
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
case 'TYPE': {
|
|
151
|
-
const el = document.querySelector(params.selector) as HTMLInputElement;
|
|
152
|
-
if (!el) throw new Error(`Element not found: ${params.selector}`);
|
|
153
|
-
el.focus();
|
|
154
|
-
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set
|
|
155
|
-
|| Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;
|
|
156
|
-
if (setter) setter.call(el, params.value || '');
|
|
157
|
-
else el.value = params.value || '';
|
|
158
|
-
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
159
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
160
|
-
reply(source, id, { success: true });
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
case 'CLEAR': {
|
|
164
|
-
const el = document.querySelector(params.selector) as HTMLInputElement;
|
|
165
|
-
if (!el) throw new Error(`Element not found: ${params.selector}`);
|
|
166
|
-
el.value = '';
|
|
167
|
-
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
168
|
-
reply(source, id, { success: true });
|
|
169
|
-
break;
|
|
170
|
-
}
|
|
171
|
-
case 'SELECT': {
|
|
172
|
-
const el = document.querySelector(params.selector) as HTMLSelectElement;
|
|
173
|
-
if (!el) throw new Error(`Element not found: ${params.selector}`);
|
|
174
|
-
el.value = params.value || '';
|
|
175
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
176
|
-
reply(source, id, { success: true });
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
case 'HOVER': {
|
|
180
|
-
const el = document.querySelector(params.selector);
|
|
181
|
-
if (!el) throw new Error(`Element not found: ${params.selector}`);
|
|
182
|
-
el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
|
|
183
|
-
el.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
|
|
184
|
-
reply(source, id, { success: true });
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
case 'FOCUS': {
|
|
188
|
-
const el = document.querySelector(params.selector) as HTMLElement;
|
|
189
|
-
if (!el) throw new Error(`Element not found: ${params.selector}`);
|
|
190
|
-
el.focus();
|
|
191
|
-
reply(source, id, { success: true });
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
case 'SCROLL': {
|
|
195
|
-
if (params.selector) {
|
|
196
|
-
const el = document.querySelector(params.selector);
|
|
197
|
-
el?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
198
|
-
} else {
|
|
199
|
-
window.scrollTo({ top: params.y || 0, left: params.x || 0, behavior: 'smooth' });
|
|
200
|
-
}
|
|
201
|
-
reply(source, id, { success: true });
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
204
|
-
case 'KEYDOWN':
|
|
205
|
-
case 'KEYUP':
|
|
206
|
-
case 'KEYPRESS': {
|
|
207
|
-
const target = params.selector
|
|
208
|
-
? document.querySelector(params.selector)
|
|
209
|
-
: document.activeElement || document.body;
|
|
210
|
-
target?.dispatchEvent(new KeyboardEvent(method.toLowerCase(), { key: params.key, bubbles: true }));
|
|
211
|
-
reply(source, id, { success: true });
|
|
212
|
-
break;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// ─── Navigation ───
|
|
216
|
-
case 'NAVIGATE': {
|
|
217
|
-
window.location.href = params.url || params.value || '/';
|
|
218
|
-
reply(source, id, { success: true });
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
case 'RELOAD': {
|
|
222
|
-
window.location.reload();
|
|
223
|
-
reply(source, id, { success: true });
|
|
224
|
-
break;
|
|
225
|
-
}
|
|
226
|
-
case 'BACK': {
|
|
227
|
-
window.history.back();
|
|
228
|
-
reply(source, id, { success: true });
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// ─── Wait / Assert ───
|
|
233
|
-
case 'WAIT_FOR': {
|
|
234
|
-
const timeout = params.timeout || 5000;
|
|
235
|
-
const start = Date.now();
|
|
236
|
-
const poll = () => {
|
|
237
|
-
if (document.querySelector(params.selector)) {
|
|
238
|
-
reply(source, id, { success: true });
|
|
239
|
-
} else if (Date.now() - start > timeout) {
|
|
240
|
-
reply(source, id, null, { code: 408, message: `Timeout waiting for: ${params.selector}` });
|
|
241
|
-
} else {
|
|
242
|
-
setTimeout(poll, 100);
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
poll();
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
case 'ASSERT_EXISTS': {
|
|
249
|
-
const el = document.querySelector(params.selector);
|
|
250
|
-
if (!el) throw new Error(`Assert failed: ${params.selector} not found`);
|
|
251
|
-
reply(source, id, { success: true });
|
|
252
|
-
break;
|
|
253
|
-
}
|
|
254
|
-
case 'ASSERT_TEXT': {
|
|
255
|
-
const el = document.querySelector(params.selector);
|
|
256
|
-
if (!el) throw new Error(`Assert failed: ${params.selector} not found`);
|
|
257
|
-
const text = el.textContent?.trim();
|
|
258
|
-
if (text !== params.expected && text !== params.value) {
|
|
259
|
-
throw new Error(`Assert failed: expected "${params.expected || params.value}", got "${text}"`);
|
|
260
|
-
}
|
|
261
|
-
reply(source, id, { success: true, text });
|
|
262
|
-
break;
|
|
263
|
-
}
|
|
264
|
-
case 'ASSERT_VISIBLE': {
|
|
265
|
-
const el = document.querySelector(params.selector) as HTMLElement;
|
|
266
|
-
if (!el) throw new Error(`Assert failed: ${params.selector} not found`);
|
|
267
|
-
const style = window.getComputedStyle(el);
|
|
268
|
-
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
|
|
269
|
-
throw new Error(`Assert failed: ${params.selector} not visible`);
|
|
270
|
-
}
|
|
271
|
-
reply(source, id, { success: true });
|
|
272
|
-
break;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// ─── Store ───
|
|
276
|
-
case 'STORE_LIST': {
|
|
277
|
-
const stores = storeRegistry.map(s => {
|
|
278
|
-
const state = s.store.getState();
|
|
279
|
-
const methods = Object.keys(state).filter(k => typeof state[k] === 'function');
|
|
280
|
-
return { name: s.name, keys: Object.keys(state).length, methods: methods.length };
|
|
281
|
-
});
|
|
282
|
-
reply(source, id, { success: true, stores });
|
|
283
|
-
break;
|
|
284
|
-
}
|
|
285
|
-
case 'STORE_READ': {
|
|
286
|
-
const entry = storeRegistry.find(s => s.name === params.store || s.name === params.storeName);
|
|
287
|
-
if (!entry) throw new Error(`Store not found: ${params.store || params.storeName}`);
|
|
288
|
-
const state = entry.store.getState();
|
|
289
|
-
const methods = Object.keys(state).filter(k => typeof state[k] === 'function');
|
|
290
|
-
let value = state;
|
|
291
|
-
if (params.path) {
|
|
292
|
-
value = params.path.split('.').reduce((o: any, k: string) => o?.[k], state);
|
|
293
|
-
}
|
|
294
|
-
reply(source, id, {
|
|
295
|
-
success: true,
|
|
296
|
-
name: entry.name,
|
|
297
|
-
state: serializeState(state),
|
|
298
|
-
methods,
|
|
299
|
-
value: params.path ? serializeState(value) : undefined,
|
|
300
|
-
});
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
case 'STORE_WRITE': {
|
|
304
|
-
const entry = storeRegistry.find(s => s.name === params.store || s.name === params.storeName);
|
|
305
|
-
if (!entry) throw new Error(`Store not found: ${params.store || params.storeName}`);
|
|
306
|
-
const path = (params.path || '').split('.').filter(Boolean);
|
|
307
|
-
if (path.length === 0) throw new Error('Store path required');
|
|
308
|
-
if (path.length === 1) {
|
|
309
|
-
entry.store.setState({ [path[0]]: JSON.parse(params.value) });
|
|
310
|
-
} else {
|
|
311
|
-
const current = { ...entry.store.getState() };
|
|
312
|
-
let obj: any = current;
|
|
313
|
-
for (let i = 0; i < path.length - 1; i++) obj = obj[path[i]];
|
|
314
|
-
obj[path[path.length - 1]] = JSON.parse(params.value);
|
|
315
|
-
entry.store.setState(current);
|
|
316
|
-
}
|
|
317
|
-
reply(source, id, { success: true });
|
|
318
|
-
break;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// ─── Capture ───
|
|
322
|
-
case 'GET_HTML': {
|
|
323
|
-
const el = document.querySelector(params.selector);
|
|
324
|
-
reply(source, id, { success: true, html: el?.outerHTML || '' });
|
|
325
|
-
break;
|
|
326
|
-
}
|
|
327
|
-
case 'GET_COMPUTED_STYLE': {
|
|
328
|
-
const el = document.querySelector(params.selector) as Element;
|
|
329
|
-
if (!el) throw new Error(`Element not found: ${params.selector}`);
|
|
330
|
-
const style = window.getComputedStyle(el);
|
|
331
|
-
const value = params.property || params.key ? style.getPropertyValue(params.property || params.key) : undefined;
|
|
332
|
-
reply(source, id, { success: true, value });
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
case 'GET_INFO': {
|
|
336
|
-
reply(source, id, {
|
|
337
|
-
success: true,
|
|
338
|
-
title: document.title,
|
|
339
|
-
url: window.location.href,
|
|
340
|
-
buttons: document.querySelectorAll('button').length,
|
|
341
|
-
inputs: document.querySelectorAll('input').length,
|
|
342
|
-
links: document.querySelectorAll('a').length,
|
|
343
|
-
});
|
|
344
|
-
break;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// ─── JS Eval ───
|
|
348
|
-
case 'EVAL': {
|
|
349
|
-
const result = (window as any).eval(params.code);
|
|
350
|
-
reply(source, id, { success: true, result: serializeState(result) });
|
|
351
|
-
break;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// ─── Recorder ───
|
|
355
|
-
case 'RECORD_START': {
|
|
356
|
-
if (isRecording) {
|
|
357
|
-
reply(source, id, { success: false, error: 'Already recording' });
|
|
358
|
-
break;
|
|
359
|
-
}
|
|
360
|
-
isRecording = true;
|
|
361
|
-
recorderSource = source;
|
|
362
|
-
|
|
363
|
-
const clickHandler = (e: MouseEvent) => {
|
|
364
|
-
const el = e.target as HTMLElement;
|
|
365
|
-
if (!el) return;
|
|
366
|
-
notify(recorderSource, 'bridge.recorded', {
|
|
367
|
-
type: 'click',
|
|
368
|
-
selector: getCssSelector(el),
|
|
369
|
-
tagName: el.tagName,
|
|
370
|
-
id: el.id,
|
|
371
|
-
textContent: (el.textContent || '').slice(0, 50),
|
|
372
|
-
timestamp: Date.now(),
|
|
373
|
-
});
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
const inputHandler = (e: Event) => {
|
|
377
|
-
const el = e.target as HTMLInputElement;
|
|
378
|
-
if (!el || !['INPUT', 'TEXTAREA', 'SELECT'].includes(el.tagName)) return;
|
|
379
|
-
notify(recorderSource, 'bridge.recorded', {
|
|
380
|
-
type: el.tagName === 'SELECT' ? 'select' : 'type',
|
|
381
|
-
selector: getCssSelector(el),
|
|
382
|
-
value: el.value,
|
|
383
|
-
tagName: el.tagName,
|
|
384
|
-
timestamp: Date.now(),
|
|
385
|
-
});
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
let scrollTimeout: ReturnType<typeof setTimeout>;
|
|
389
|
-
const scrollHandler = () => {
|
|
390
|
-
clearTimeout(scrollTimeout);
|
|
391
|
-
scrollTimeout = setTimeout(() => {
|
|
392
|
-
notify(recorderSource, 'bridge.recorded', {
|
|
393
|
-
type: 'scroll',
|
|
394
|
-
x: window.scrollX,
|
|
395
|
-
y: window.scrollY,
|
|
396
|
-
timestamp: Date.now(),
|
|
397
|
-
});
|
|
398
|
-
}, 300);
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
document.addEventListener('click', clickHandler, true);
|
|
402
|
-
document.addEventListener('input', inputHandler, true);
|
|
403
|
-
document.addEventListener('change', inputHandler, true);
|
|
404
|
-
window.addEventListener('scroll', scrollHandler, true);
|
|
405
|
-
|
|
406
|
-
recorderCleanup = () => {
|
|
407
|
-
document.removeEventListener('click', clickHandler, true);
|
|
408
|
-
document.removeEventListener('input', inputHandler, true);
|
|
409
|
-
document.removeEventListener('change', inputHandler, true);
|
|
410
|
-
window.removeEventListener('scroll', scrollHandler, true);
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
reply(source, id, { success: true });
|
|
414
|
-
break;
|
|
415
|
-
}
|
|
416
|
-
case 'RECORD_STOP': {
|
|
417
|
-
if (recorderCleanup) recorderCleanup();
|
|
418
|
-
isRecording = false;
|
|
419
|
-
recorderCleanup = null;
|
|
420
|
-
recorderSource = null;
|
|
421
|
-
reply(source, id, { success: true });
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
default: {
|
|
426
|
-
reply(source, id, null, { code: -32601, message: `Unknown method: ${method}` });
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
} catch (err: any) {
|
|
430
|
-
reply(source, id, null, { code: 500, message: err.message || String(err) });
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
window.addEventListener('message', handleMessage);
|
|
435
|
-
(window as any).__BRIDGE_AGENT__ = {
|
|
436
|
-
version: '1.0.0',
|
|
437
|
-
storeRegistry,
|
|
438
|
-
registerStore,
|
|
439
|
-
destroy: () => window.removeEventListener('message', handleMessage),
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
console.log('[BridgeAgent] Ready — listening for postMessage commands');
|
|
443
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
export const DecidoIcon = ({ className = '', size = 24 }: { className?: string; size?: number }) => (
|
|
4
|
-
<svg
|
|
5
|
-
width={size}
|
|
6
|
-
height={size}
|
|
7
|
-
viewBox="0 0 256 256"
|
|
8
|
-
fill="none"
|
|
9
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
10
|
-
className={className}
|
|
11
|
-
>
|
|
12
|
-
<g filter="url(#glow)">
|
|
13
|
-
{/* Base D Shape Stem */}
|
|
14
|
-
<path d="M64 48V208" stroke="#06B6D4" strokeWidth="16" strokeLinecap="round" strokeLinejoin="round" />
|
|
15
|
-
|
|
16
|
-
{/* Top Curve */}
|
|
17
|
-
<path d="M64 48H128C163.346 48 192 76.6543 192 112V116" stroke="url(#cyan_purple_grad)" strokeWidth="16" strokeLinecap="round" strokeLinejoin="round" />
|
|
18
|
-
|
|
19
|
-
{/* Bottom Curve */}
|
|
20
|
-
<path d="M64 208H128C163.346 208 192 179.346 192 144V140" stroke="url(#cyan_emerald_grad)" strokeWidth="16" strokeLinecap="round" strokeLinejoin="round" />
|
|
21
|
-
|
|
22
|
-
{/* Internal Network Connections */}
|
|
23
|
-
<line x1="100" y1="96" x2="116" y2="160" stroke="#06B6D4" strokeWidth="8" strokeLinecap="round" />
|
|
24
|
-
<line x1="116" y1="160" x2="176" y2="128" stroke="#10B981" strokeWidth="8" strokeLinecap="round" />
|
|
25
|
-
<line x1="100" y1="96" x2="176" y2="128" stroke="#A855F7" strokeWidth="8" strokeLinecap="round" />
|
|
26
|
-
<line x1="64" y1="72" x2="100" y2="96" stroke="#06B6D4" strokeWidth="8" strokeLinecap="round" />
|
|
27
|
-
<line x1="64" y1="184" x2="116" y2="160" stroke="#06B6D4" strokeWidth="8" strokeLinecap="round" />
|
|
28
|
-
<line x1="176" y1="128" x2="192" y2="128" stroke="#A855F7" strokeWidth="8" strokeLinecap="round" />
|
|
29
|
-
|
|
30
|
-
{/* Nodes (Dots) */}
|
|
31
|
-
<circle cx="100" cy="96" r="14" fill="#06B6D4" />
|
|
32
|
-
<circle cx="116" cy="160" r="14" fill="#10B981" />
|
|
33
|
-
<circle cx="176" cy="128" r="14" fill="#A855F7" />
|
|
34
|
-
</g>
|
|
35
|
-
|
|
36
|
-
<defs>
|
|
37
|
-
<linearGradient id="cyan_purple_grad" x1="64" y1="48" x2="192" y2="116" gradientUnits="userSpaceOnUse">
|
|
38
|
-
<stop offset="0%" stopColor="#06B6D4" />
|
|
39
|
-
<stop offset="100%" stopColor="#A855F7" />
|
|
40
|
-
</linearGradient>
|
|
41
|
-
|
|
42
|
-
<linearGradient id="cyan_emerald_grad" x1="64" y1="208" x2="192" y2="140" gradientUnits="userSpaceOnUse">
|
|
43
|
-
<stop offset="0%" stopColor="#06B6D4" />
|
|
44
|
-
<stop offset="100%" stopColor="#10B981" />
|
|
45
|
-
</linearGradient>
|
|
46
|
-
|
|
47
|
-
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
|
|
48
|
-
<feGaussianBlur stdDeviation="6" result="blur" />
|
|
49
|
-
<feMerge>
|
|
50
|
-
<feMergeNode in="blur" />
|
|
51
|
-
<feMergeNode in="SourceGraphic" />
|
|
52
|
-
</feMerge>
|
|
53
|
-
</filter>
|
|
54
|
-
</defs>
|
|
55
|
-
</svg>
|
|
56
|
-
);
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
|
|
3
|
-
interface JsonTreeEditorProps {
|
|
4
|
-
data: any;
|
|
5
|
-
label?: string;
|
|
6
|
-
level?: number;
|
|
7
|
-
path?: string;
|
|
8
|
-
onUpdate?: (path: string, value: any) => void;
|
|
9
|
-
isRoot?: boolean;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const JsonTreeEditor: React.FC<JsonTreeEditorProps> = ({
|
|
13
|
-
data,
|
|
14
|
-
label = "Root",
|
|
15
|
-
level = 0,
|
|
16
|
-
path = "",
|
|
17
|
-
onUpdate,
|
|
18
|
-
isRoot = true
|
|
19
|
-
}) => {
|
|
20
|
-
const [isCollapsed, setIsCollapsed] = useState(isRoot ? false : true);
|
|
21
|
-
|
|
22
|
-
const handleValueChange = (newValue: any) => {
|
|
23
|
-
if (onUpdate) onUpdate(path, newValue);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const indent = level * 16;
|
|
27
|
-
const isObject = typeof data === 'object' && data !== null;
|
|
28
|
-
const isArray = Array.isArray(data);
|
|
29
|
-
|
|
30
|
-
if (!isObject) {
|
|
31
|
-
// Primitive Render
|
|
32
|
-
let renderInput = null;
|
|
33
|
-
if (typeof data === 'boolean') {
|
|
34
|
-
renderInput = (
|
|
35
|
-
<button
|
|
36
|
-
onClick={() => handleValueChange(!data)}
|
|
37
|
-
className={`w-8 h-4 rounded-full relative transition-colors ${data ? 'bg-cyan-500' : 'bg-surface-elevated'}`}
|
|
38
|
-
>
|
|
39
|
-
<div className={`absolute top-0.5 left-0.5 w-3 h-3 rounded-full bg-text-primary transition-transform ${data ? 'translate-x-4' : 'translate-x-0'}`} />
|
|
40
|
-
</button>
|
|
41
|
-
);
|
|
42
|
-
} else if (typeof data === 'number') {
|
|
43
|
-
renderInput = (
|
|
44
|
-
<input
|
|
45
|
-
type="number"
|
|
46
|
-
value={data}
|
|
47
|
-
onChange={e => handleValueChange(Number(e.target.value))}
|
|
48
|
-
className="bg-transparent border border-border-default rounded px-1 py-0.5 text-emerald-400 w-24 focus:border-cyan-500 outline-hidden"
|
|
49
|
-
readOnly={!onUpdate}
|
|
50
|
-
/>
|
|
51
|
-
);
|
|
52
|
-
} else if (typeof data === 'string') {
|
|
53
|
-
renderInput = (
|
|
54
|
-
<input
|
|
55
|
-
type="text"
|
|
56
|
-
value={data}
|
|
57
|
-
onChange={e => handleValueChange(e.target.value)}
|
|
58
|
-
className="bg-transparent border border-border-default rounded px-1 py-0.5 text-amber-300 w-48 focus:border-cyan-500 outline-hidden truncate"
|
|
59
|
-
readOnly={!onUpdate}
|
|
60
|
-
/>
|
|
61
|
-
);
|
|
62
|
-
} else {
|
|
63
|
-
renderInput = <span className="text-text-muted italic">{String(data)}</span>;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<div className="flex items-center py-1 hover:bg-surface-glass rounded px-2 transition-colors" style={{ paddingLeft: `${indent}px` }}>
|
|
68
|
-
<span className="text-blue-300 mr-2">{label}:</span>
|
|
69
|
-
{renderInput}
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Object or Array Render
|
|
75
|
-
const keys = Object.keys(data);
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<div className="flex flex-col">
|
|
79
|
-
<div
|
|
80
|
-
className="flex items-center py-1 hover:bg-surface-glass rounded px-2 cursor-pointer transition-colors"
|
|
81
|
-
style={{ paddingLeft: `${indent}px` }}
|
|
82
|
-
onClick={() => setIsCollapsed(!isCollapsed)}
|
|
83
|
-
>
|
|
84
|
-
<button className="w-4 h-4 flex items-center justify-center mr-1 text-text-secondary hover:text-text-primary transition-colors">
|
|
85
|
-
<svg
|
|
86
|
-
className={`w-3 h-3 transform transition-transform ${isCollapsed ? '-rotate-90' : 'rotate-0'}`}
|
|
87
|
-
fill="none"
|
|
88
|
-
viewBox="0 0 24 24"
|
|
89
|
-
stroke="currentColor"
|
|
90
|
-
>
|
|
91
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
92
|
-
</svg>
|
|
93
|
-
</button>
|
|
94
|
-
<span className="text-purple-300 font-bold">{label}</span>
|
|
95
|
-
<span className="ml-2 text-text-muted text-xs">
|
|
96
|
-
{isArray ? `[${keys.length}]` : `{${keys.length}}`}
|
|
97
|
-
</span>
|
|
98
|
-
</div>
|
|
99
|
-
|
|
100
|
-
{!isCollapsed && (
|
|
101
|
-
<div className="flex flex-col border-l border-border-subtle ml-3 mt-1">
|
|
102
|
-
{keys.map(key => (
|
|
103
|
-
<JsonTreeEditor
|
|
104
|
-
key={key}
|
|
105
|
-
label={key}
|
|
106
|
-
data={data[key as keyof typeof data]}
|
|
107
|
-
level={level + 1}
|
|
108
|
-
path={path ? `${path}.${key}` : key}
|
|
109
|
-
onUpdate={onUpdate}
|
|
110
|
-
isRoot={false}
|
|
111
|
-
/>
|
|
112
|
-
))}
|
|
113
|
-
</div>
|
|
114
|
-
)}
|
|
115
|
-
</div>
|
|
116
|
-
);
|
|
117
|
-
};
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { useShellStore, LayoutNode } from '../store/engine';
|
|
3
|
-
import { ShellPluginProvider } from '../core/PluginContext';
|
|
4
|
-
import { PluginErrorBoundary } from './PluginErrorBoundary';
|
|
5
|
-
|
|
6
|
-
interface PanelSplitterProps {
|
|
7
|
-
node?: LayoutNode;
|
|
8
|
-
isRoot?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const PanelSplitter: React.FC<PanelSplitterProps> = ({ node, isRoot = false }) => {
|
|
12
|
-
const rootLayout = useShellStore((state) => state.rootLayout);
|
|
13
|
-
const widgets = useShellStore((state) => state.widgets);
|
|
14
|
-
|
|
15
|
-
const currentNode = node || (isRoot ? rootLayout : undefined);
|
|
16
|
-
|
|
17
|
-
if (!currentNode) {
|
|
18
|
-
return (
|
|
19
|
-
<div className="flex-1 flex items-center justify-center text-text-primary/30 text-sm italic h-full w-full">
|
|
20
|
-
No active layout topology
|
|
21
|
-
</div>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Render Widget node directly
|
|
26
|
-
if (currentNode.type === 'widget' && currentNode.widgetId) {
|
|
27
|
-
const WidgetMeta = widgets.get(currentNode.widgetId);
|
|
28
|
-
if (!WidgetMeta) return <div className="text-red-500 p-4 bg-surface-glass">Widget '{currentNode.widgetId}' not found.</div>;
|
|
29
|
-
|
|
30
|
-
const WidgetComponent = WidgetMeta.component;
|
|
31
|
-
return (
|
|
32
|
-
<div className="w-full h-full relative overflow-hidden bg-surface-overlay backdrop-blur-md border border-border-subtle rounded-2xl shadow-xl flex flex-col">
|
|
33
|
-
{/* Standard Panel Header */}
|
|
34
|
-
<div className="flex-none px-4 py-2 border-b border-border-default bg-linear-to-r from-white/5 to-transparent">
|
|
35
|
-
<span className="text-xs font-semibold text-text-primary/70 uppercase tracking-wider">{WidgetMeta.name}</span>
|
|
36
|
-
</div>
|
|
37
|
-
{/* Widget Payload */}
|
|
38
|
-
<div className="flex-1 overflow-auto relative">
|
|
39
|
-
<ShellPluginProvider pluginId={WidgetMeta.pluginId}>
|
|
40
|
-
{WidgetComponent ? (
|
|
41
|
-
<PluginErrorBoundary pluginName={WidgetMeta.name} pluginId={WidgetMeta.pluginId}>
|
|
42
|
-
<React.Suspense fallback={<div className="text-text-primary/50 p-4 text-center">Loading Remote Plugin...</div>}>
|
|
43
|
-
<WidgetComponent {...(WidgetMeta.defaultProps || {})} />
|
|
44
|
-
</React.Suspense>
|
|
45
|
-
</PluginErrorBoundary>
|
|
46
|
-
) : (
|
|
47
|
-
<div className="text-text-primary/50 p-4 text-center">Component Not Renderable</div>
|
|
48
|
-
)}
|
|
49
|
-
</ShellPluginProvider>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Base Flex setup for splits
|
|
56
|
-
const isVertical = currentNode.type === 'split-vertical';
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<div className={`flex w-full h-full gap-4 ${isVertical ? 'flex-col' : 'flex-row'}`}>
|
|
60
|
-
{currentNode.children?.map((child, idx) => (
|
|
61
|
-
<div
|
|
62
|
-
key={child.id || idx}
|
|
63
|
-
className="shrink grow"
|
|
64
|
-
style={{ flexBasis: child.size ? `${child.size}%` : 'auto' }}
|
|
65
|
-
>
|
|
66
|
-
<PanelSplitter node={child} />
|
|
67
|
-
</div>
|
|
68
|
-
))}
|
|
69
|
-
</div>
|
|
70
|
-
);
|
|
71
|
-
};
|