@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,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* networkInterceptor — Monkey-patches window.fetch to capture all network requests.
|
|
3
|
+
* Sprint AP: All captured requests are stored in a global array accessible by NetworkTab.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface NetworkEntry {
|
|
7
|
+
id: number;
|
|
8
|
+
timestamp: string;
|
|
9
|
+
method: string;
|
|
10
|
+
url: string;
|
|
11
|
+
status: number;
|
|
12
|
+
duration: number; // ms
|
|
13
|
+
requestBody?: string;
|
|
14
|
+
responseBody?: string;
|
|
15
|
+
size: number; // bytes approx
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let _networkId = 0;
|
|
20
|
+
const MAX_ENTRIES = 200;
|
|
21
|
+
const _entries: NetworkEntry[] = [];
|
|
22
|
+
const _listeners = new Set<() => void>();
|
|
23
|
+
|
|
24
|
+
function _getTimestamp(): string {
|
|
25
|
+
const now = new Date();
|
|
26
|
+
return `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function _addEntry(entry: NetworkEntry) {
|
|
30
|
+
_entries.push(entry);
|
|
31
|
+
if (_entries.length > MAX_ENTRIES) _entries.shift();
|
|
32
|
+
_listeners.forEach(fn => fn());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getNetworkEntries(): NetworkEntry[] {
|
|
36
|
+
return _entries;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function clearNetworkEntries() {
|
|
40
|
+
_entries.length = 0;
|
|
41
|
+
_listeners.forEach(fn => fn());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function subscribeNetwork(cb: () => void): () => void {
|
|
45
|
+
_listeners.add(cb);
|
|
46
|
+
return () => _listeners.delete(cb);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let _installed = false;
|
|
50
|
+
|
|
51
|
+
export function installNetworkInterceptor() {
|
|
52
|
+
if (_installed) return;
|
|
53
|
+
_installed = true;
|
|
54
|
+
|
|
55
|
+
const _origFetch = window.fetch;
|
|
56
|
+
|
|
57
|
+
window.fetch = async function (input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
|
58
|
+
const method = init?.method?.toUpperCase() || 'GET';
|
|
59
|
+
const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : (input as Request).url;
|
|
60
|
+
const id = ++_networkId;
|
|
61
|
+
const t0 = performance.now();
|
|
62
|
+
|
|
63
|
+
let requestBody: string | undefined;
|
|
64
|
+
try {
|
|
65
|
+
if (init?.body) {
|
|
66
|
+
requestBody = typeof init.body === 'string' ? init.body.slice(0, 2048) : '[binary]';
|
|
67
|
+
}
|
|
68
|
+
} catch { /* ignore */ }
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const res = await _origFetch.call(window, input, init);
|
|
72
|
+
const duration = Math.round(performance.now() - t0);
|
|
73
|
+
|
|
74
|
+
// Clone response to read body without consuming
|
|
75
|
+
let responseBody: string | undefined;
|
|
76
|
+
let size = 0;
|
|
77
|
+
try {
|
|
78
|
+
const clone = res.clone();
|
|
79
|
+
const text = await clone.text();
|
|
80
|
+
size = text.length;
|
|
81
|
+
responseBody = text.slice(0, 2048);
|
|
82
|
+
} catch { /* ignore */ }
|
|
83
|
+
|
|
84
|
+
_addEntry({
|
|
85
|
+
id, timestamp: _getTimestamp(), method, url,
|
|
86
|
+
status: res.status, duration, requestBody, responseBody, size,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return res;
|
|
90
|
+
} catch (err: any) {
|
|
91
|
+
const duration = Math.round(performance.now() - t0);
|
|
92
|
+
_addEntry({
|
|
93
|
+
id, timestamp: _getTimestamp(), method, url,
|
|
94
|
+
status: 0, duration, requestBody, size: 0,
|
|
95
|
+
error: err.message,
|
|
96
|
+
});
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const kernel = {
|
|
2
|
+
askAI: async (prompt: string, type: string, mode: string) => {
|
|
3
|
+
// Stub, logic moved
|
|
4
|
+
return JSON.stringify({
|
|
5
|
+
duration: 20,
|
|
6
|
+
keyframes: [
|
|
7
|
+
{ t: 0, state: 'idle', canvas: false, speech: 'Modo sin servidor activo.' }
|
|
8
|
+
]
|
|
9
|
+
});
|
|
10
|
+
},
|
|
11
|
+
onEvent: (cb: any) => {
|
|
12
|
+
return () => { };
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type UserRole = 'user' | 'admin' | 'developer';
|
|
17
|
+
export type LicenseTarget = { id: string, name: string, type: string };
|
|
18
|
+
|
|
19
|
+
export const useAuthStore = Object.assign(
|
|
20
|
+
(selector: any) => {
|
|
21
|
+
const state = {
|
|
22
|
+
availableLicenses: [
|
|
23
|
+
{ id: '1', name: 'Decido Personal', type: 'personal' },
|
|
24
|
+
{ id: '2', name: 'Kúspide Corp', type: 'enterprise' },
|
|
25
|
+
{ id: '3', name: 'Madefront Ind', type: 'enterprise' }
|
|
26
|
+
]
|
|
27
|
+
};
|
|
28
|
+
return selector ? selector(state) : state;
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
getState: () => ({
|
|
32
|
+
availableLicenses: [
|
|
33
|
+
{ id: '1', name: 'Decido Personal', type: 'personal' },
|
|
34
|
+
{ id: '2', name: 'Kúspide Corp', type: 'enterprise' },
|
|
35
|
+
{ id: '3', name: 'Madefront Ind', type: 'enterprise' }
|
|
36
|
+
],
|
|
37
|
+
login: (role?: string, context?: string, licenses?: any) => { },
|
|
38
|
+
setActiveContext: (id?: string) => { }
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
);
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin API — Public API for plugins to register shells, nodes, and extend Decido OS.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { registerNodeType, registerShell, getNodeTypes } from '@decido/studio';
|
|
6
|
+
*
|
|
7
|
+
* registerNodeType({ type: 'my-node', component: MyNode, category: 'custom' });
|
|
8
|
+
* registerShell('my-canvas', MyCanvasComponent);
|
|
9
|
+
*/
|
|
10
|
+
import type { ComponentType } from 'react';
|
|
11
|
+
|
|
12
|
+
// ═══════════════════════════════════════════
|
|
13
|
+
// Node Type Registry
|
|
14
|
+
// ═══════════════════════════════════════════
|
|
15
|
+
|
|
16
|
+
export interface NodeTypeRegistration {
|
|
17
|
+
/** Unique node type identifier */
|
|
18
|
+
type: string;
|
|
19
|
+
/** Display label */
|
|
20
|
+
label: string;
|
|
21
|
+
/** Category for grouping in context menu */
|
|
22
|
+
category: 'automation' | 'logic' | 'ui' | 'data' | 'integration' | 'custom';
|
|
23
|
+
/** React component to render the node */
|
|
24
|
+
component: ComponentType<any>;
|
|
25
|
+
/** Which canvas types this node is compatible with */
|
|
26
|
+
compatibleWith?: ('flow-canvas' | 'dsd-editor' | 'all')[];
|
|
27
|
+
/** Default node data */
|
|
28
|
+
defaultData?: Record<string, any>;
|
|
29
|
+
/** Handle definitions */
|
|
30
|
+
handles?: {
|
|
31
|
+
inputs?: string[];
|
|
32
|
+
outputs?: string[];
|
|
33
|
+
};
|
|
34
|
+
/** Plugin ID that registered this node */
|
|
35
|
+
pluginId?: string;
|
|
36
|
+
/** Icon component or emoji */
|
|
37
|
+
icon?: ComponentType<{ size?: number }> | string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// In-memory registry
|
|
41
|
+
const nodeTypeRegistry = new Map<string, NodeTypeRegistration>();
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Register a custom node type that can be used in any compatible canvas.
|
|
45
|
+
*/
|
|
46
|
+
export function registerNodeType(registration: NodeTypeRegistration): void {
|
|
47
|
+
if (nodeTypeRegistry.has(registration.type)) {
|
|
48
|
+
console.warn(`[PluginAPI] Node type "${registration.type}" already registered, overwriting.`);
|
|
49
|
+
}
|
|
50
|
+
nodeTypeRegistry.set(registration.type, {
|
|
51
|
+
compatibleWith: ['all'],
|
|
52
|
+
defaultData: {},
|
|
53
|
+
handles: { inputs: ['default'], outputs: ['success'] },
|
|
54
|
+
...registration,
|
|
55
|
+
});
|
|
56
|
+
console.log(`[PluginAPI] 🧩 Node type registered: ${registration.type} (${registration.category})`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get all registered node types, optionally filtered by canvas compatibility.
|
|
61
|
+
*/
|
|
62
|
+
export function getRegisteredNodeTypes(
|
|
63
|
+
canvasFilter?: 'flow-canvas' | 'dsd-editor' | 'all'
|
|
64
|
+
): NodeTypeRegistration[] {
|
|
65
|
+
const all = Array.from(nodeTypeRegistry.values());
|
|
66
|
+
if (!canvasFilter) return all;
|
|
67
|
+
return all.filter(
|
|
68
|
+
(n) => n.compatibleWith?.includes('all') || n.compatibleWith?.includes(canvasFilter)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get React Flow nodeTypes map for a specific canvas type.
|
|
74
|
+
* Merges built-in types with plugin-registered types.
|
|
75
|
+
*/
|
|
76
|
+
export function getNodeTypesMap(
|
|
77
|
+
canvasFilter?: 'flow-canvas' | 'dsd-editor' | 'all'
|
|
78
|
+
): Record<string, ComponentType<any>> {
|
|
79
|
+
const filtered = getRegisteredNodeTypes(canvasFilter);
|
|
80
|
+
const map: Record<string, ComponentType<any>> = {};
|
|
81
|
+
for (const reg of filtered) {
|
|
82
|
+
map[reg.type] = reg.component;
|
|
83
|
+
}
|
|
84
|
+
return map;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get context menu entries for registered node types.
|
|
89
|
+
*/
|
|
90
|
+
export function getNodeTypeMenuEntries(
|
|
91
|
+
canvasFilter?: 'flow-canvas' | 'dsd-editor' | 'all'
|
|
92
|
+
): Array<{ type: string; label: string; category: string; icon?: any }> {
|
|
93
|
+
return getRegisteredNodeTypes(canvasFilter).map((reg) => ({
|
|
94
|
+
type: reg.type,
|
|
95
|
+
label: reg.label,
|
|
96
|
+
category: reg.category,
|
|
97
|
+
icon: reg.icon,
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Remove a registered node type.
|
|
103
|
+
*/
|
|
104
|
+
export function unregisterNodeType(type: string): boolean {
|
|
105
|
+
return nodeTypeRegistry.delete(type);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ═══════════════════════════════════════════
|
|
109
|
+
// Plugin Manifest
|
|
110
|
+
// ═══════════════════════════════════════════
|
|
111
|
+
|
|
112
|
+
export interface PluginManifest {
|
|
113
|
+
/** Unique plugin ID */
|
|
114
|
+
id: string;
|
|
115
|
+
/** Display name */
|
|
116
|
+
name: string;
|
|
117
|
+
/** Version */
|
|
118
|
+
version: string;
|
|
119
|
+
/** Shells to register */
|
|
120
|
+
shells?: Array<{
|
|
121
|
+
type: string;
|
|
122
|
+
component: ComponentType<any>;
|
|
123
|
+
}>;
|
|
124
|
+
/** Node types to register */
|
|
125
|
+
nodeTypes?: NodeTypeRegistration[];
|
|
126
|
+
/** Called when plugin is loaded */
|
|
127
|
+
onLoad?: () => void;
|
|
128
|
+
/** Called when plugin is unloaded */
|
|
129
|
+
onUnload?: () => void;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Plugin registry
|
|
133
|
+
const pluginRegistry = new Map<string, PluginManifest>();
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Load a plugin manifest — registers all shells and node types.
|
|
137
|
+
*/
|
|
138
|
+
export async function loadPlugin(manifest: PluginManifest): Promise<void> {
|
|
139
|
+
// Avoid re-loading
|
|
140
|
+
if (pluginRegistry.has(manifest.id)) {
|
|
141
|
+
console.warn(`[PluginAPI] Plugin "${manifest.id}" already loaded.`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Register shells
|
|
146
|
+
if (manifest.shells) {
|
|
147
|
+
const { registerShell } = await import('../store/useShellRegistry');
|
|
148
|
+
for (const shell of manifest.shells) {
|
|
149
|
+
registerShell(shell.type, shell.component);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Register node types
|
|
154
|
+
if (manifest.nodeTypes) {
|
|
155
|
+
for (const nodeType of manifest.nodeTypes) {
|
|
156
|
+
registerNodeType({ ...nodeType, pluginId: manifest.id });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Lifecycle
|
|
161
|
+
manifest.onLoad?.();
|
|
162
|
+
pluginRegistry.set(manifest.id, manifest);
|
|
163
|
+
console.log(`[PluginAPI] 📦 Plugin loaded: ${manifest.name} v${manifest.version}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Unload a plugin — removes its shells and node types.
|
|
168
|
+
*/
|
|
169
|
+
export function unloadPlugin(pluginId: string): void {
|
|
170
|
+
const manifest = pluginRegistry.get(pluginId);
|
|
171
|
+
if (!manifest) return;
|
|
172
|
+
|
|
173
|
+
// Remove node types
|
|
174
|
+
if (manifest.nodeTypes) {
|
|
175
|
+
for (const nt of manifest.nodeTypes) {
|
|
176
|
+
unregisterNodeType(nt.type);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
manifest.onUnload?.();
|
|
181
|
+
pluginRegistry.delete(pluginId);
|
|
182
|
+
console.log(`[PluginAPI] 📦 Plugin unloaded: ${manifest.name}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get all loaded plugins.
|
|
187
|
+
*/
|
|
188
|
+
export function getLoadedPlugins(): PluginManifest[] {
|
|
189
|
+
return Array.from(pluginRegistry.values());
|
|
190
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
|
|
3
|
+
// ─── MCP Server Types ────────────────────────────────────────
|
|
4
|
+
export interface McpServerConfig {
|
|
5
|
+
name: string;
|
|
6
|
+
command: string;
|
|
7
|
+
args: string;
|
|
8
|
+
isDefault: boolean;
|
|
9
|
+
isConnected: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface McpState {
|
|
13
|
+
mcpServers: McpServerConfig[];
|
|
14
|
+
addMcpServer: (server: Omit<McpServerConfig, 'isConnected'>) => void;
|
|
15
|
+
removeMcpServer: (name: string) => void;
|
|
16
|
+
setMcpServerStatus: (name: string, isConnected: boolean) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const STORAGE_KEY = 'macia_mcp_servers';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Load MCP server configs from localStorage.
|
|
23
|
+
* No hardcoded server entries — defaults are injected by the host app
|
|
24
|
+
* via `useMcpStore.getState().addMcpServer(...)` during boot.
|
|
25
|
+
*/
|
|
26
|
+
const loadPersistedServers = (): McpServerConfig[] => {
|
|
27
|
+
try {
|
|
28
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
29
|
+
if (stored) {
|
|
30
|
+
const parsed = JSON.parse(stored);
|
|
31
|
+
if (Array.isArray(parsed)) return parsed;
|
|
32
|
+
}
|
|
33
|
+
} catch (e) {
|
|
34
|
+
console.error('[McpStore] Failed to parse persisted MCP servers', e);
|
|
35
|
+
}
|
|
36
|
+
return [];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const persistServers = (servers: McpServerConfig[]) => {
|
|
40
|
+
try {
|
|
41
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(servers));
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.error('[McpStore] Failed to persist MCP servers', e);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// ─── Store ───────────────────────────────────────────────────
|
|
48
|
+
export const useMcpStore = create<McpState>()((set) => ({
|
|
49
|
+
mcpServers: loadPersistedServers(),
|
|
50
|
+
|
|
51
|
+
addMcpServer: (server) => set((state) => {
|
|
52
|
+
const next = [
|
|
53
|
+
...state.mcpServers.filter(s => s.name !== server.name),
|
|
54
|
+
{ ...server, isConnected: false },
|
|
55
|
+
];
|
|
56
|
+
persistServers(next);
|
|
57
|
+
return { mcpServers: next };
|
|
58
|
+
}),
|
|
59
|
+
|
|
60
|
+
removeMcpServer: (name) => set((state) => {
|
|
61
|
+
const next = state.mcpServers.filter(s => s.name !== name || s.isDefault);
|
|
62
|
+
persistServers(next);
|
|
63
|
+
return { mcpServers: next };
|
|
64
|
+
}),
|
|
65
|
+
|
|
66
|
+
setMcpServerStatus: (name, isConnected) => set((state) => ({
|
|
67
|
+
mcpServers: state.mcpServers.map(s => s.name === name ? { ...s, isConnected } : s),
|
|
68
|
+
})),
|
|
69
|
+
}));
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
|
|
3
|
+
export type UpdaterStatus = 'idle' | 'checking' | 'downloading' | 'installing' | 'rebooting' | 'error';
|
|
4
|
+
|
|
5
|
+
interface UpdaterState {
|
|
6
|
+
isUpdating: boolean;
|
|
7
|
+
status: UpdaterStatus;
|
|
8
|
+
progress: number; // 0 to 100
|
|
9
|
+
downloadedBytes: number;
|
|
10
|
+
totalBytes: number;
|
|
11
|
+
version: string | null;
|
|
12
|
+
error: string | null;
|
|
13
|
+
|
|
14
|
+
// Actions
|
|
15
|
+
setChecking: () => void;
|
|
16
|
+
setDownloading: (version: string, totalBytes: number) => void;
|
|
17
|
+
updateProgress: (downloadedBytes: number) => void;
|
|
18
|
+
setInstalling: () => void;
|
|
19
|
+
setRebooting: () => void;
|
|
20
|
+
setError: (msg: string) => void;
|
|
21
|
+
reset: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const useUpdaterStore = create<UpdaterState>((set, get) => ({
|
|
25
|
+
isUpdating: false,
|
|
26
|
+
status: 'idle',
|
|
27
|
+
progress: 0,
|
|
28
|
+
downloadedBytes: 0,
|
|
29
|
+
totalBytes: 0,
|
|
30
|
+
version: null,
|
|
31
|
+
error: null,
|
|
32
|
+
|
|
33
|
+
setChecking: () => set({ status: 'checking', error: null }),
|
|
34
|
+
setDownloading: (version, totalBytes) => set({
|
|
35
|
+
isUpdating: true,
|
|
36
|
+
status: 'downloading',
|
|
37
|
+
version,
|
|
38
|
+
totalBytes,
|
|
39
|
+
downloadedBytes: 0,
|
|
40
|
+
progress: 0
|
|
41
|
+
}),
|
|
42
|
+
updateProgress: (downloadedBytes) => {
|
|
43
|
+
const total = get().totalBytes;
|
|
44
|
+
const currentBytes = get().downloadedBytes + downloadedBytes;
|
|
45
|
+
const progress = total > 0 ? Math.min(Math.round((currentBytes / total) * 100), 100) : 0;
|
|
46
|
+
set({ downloadedBytes: currentBytes, progress });
|
|
47
|
+
},
|
|
48
|
+
setInstalling: () => set({ status: 'installing', progress: 100 }),
|
|
49
|
+
setRebooting: () => set({ status: 'rebooting' }),
|
|
50
|
+
setError: (msg) => set({ status: 'error', error: msg, isUpdating: false }),
|
|
51
|
+
reset: () => set({
|
|
52
|
+
isUpdating: false,
|
|
53
|
+
status: 'idle',
|
|
54
|
+
progress: 0,
|
|
55
|
+
downloadedBytes: 0,
|
|
56
|
+
totalBytes: 0,
|
|
57
|
+
version: null,
|
|
58
|
+
error: null
|
|
59
|
+
})
|
|
60
|
+
}));
|