@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.
Files changed (208) hide show
  1. package/README.md +31 -0
  2. package/package.json +17 -14
  3. package/.turbo/turbo-build.log +0 -13
  4. package/src/AgentPlayer.tsx +0 -105
  5. package/src/DecidoPlayer.tsx +0 -117
  6. package/src/bridge/BridgeAgent.ts +0 -443
  7. package/src/components/DecidoIcon.tsx +0 -56
  8. package/src/components/JsonTreeEditor.tsx +0 -117
  9. package/src/components/PanelSplitter.tsx +0 -71
  10. package/src/components/PluginErrorBoundary.tsx +0 -69
  11. package/src/components/SafeLiquidUI.tsx +0 -114
  12. package/src/components/TransientLayer.tsx +0 -92
  13. package/src/components/agent/AgentChat.tsx +0 -134
  14. package/src/components/chat-extensions/IntentCatalogPanel.tsx +0 -81
  15. package/src/components/chat-extensions/chatSlashCommands.ts +0 -101
  16. package/src/components/controls/CreatorInputBar.tsx +0 -144
  17. package/src/components/controls/OSToolbar.tsx +0 -90
  18. package/src/components/controls/TimelineTape.tsx +0 -43
  19. package/src/components/debug/ActionTimelineTab.tsx +0 -111
  20. package/src/components/debug/CSSInspectorTab.tsx +0 -436
  21. package/src/components/debug/ExportTab.tsx +0 -192
  22. package/src/components/debug/FlowHealthTab.tsx +0 -86
  23. package/src/components/debug/LogsTab.tsx +0 -110
  24. package/src/components/debug/MorphStackTab.tsx +0 -241
  25. package/src/components/debug/NetworkTab.tsx +0 -173
  26. package/src/components/debug/PerformanceTab.tsx +0 -171
  27. package/src/components/debug/ProfilesTab.tsx +0 -238
  28. package/src/components/debug/ReplayTab.tsx +0 -70
  29. package/src/components/debug/StoresTab.tsx +0 -255
  30. package/src/components/debug/TopologyTab.tsx +0 -59
  31. package/src/components/debug/debugConfig.tsx +0 -66
  32. package/src/components/playground/DebugPanel.tsx +0 -112
  33. package/src/components/playground/HeaderCenterControls.tsx +0 -92
  34. package/src/components/playground/KeyframeListItem.tsx +0 -70
  35. package/src/components/playground/PlaygroundAppSidebar.tsx +0 -171
  36. package/src/components/playground/PlaygroundBottomControls.tsx +0 -132
  37. package/src/components/playground/PlaygroundCanvas.tsx +0 -87
  38. package/src/components/playground/PlaygroundChat.tsx +0 -236
  39. package/src/components/playground/PlaygroundErrorBoundary.tsx +0 -63
  40. package/src/components/playground/PlaygroundFloatingInput.tsx +0 -352
  41. package/src/components/playground/PlaygroundHeader.tsx +0 -222
  42. package/src/components/playground/PlaygroundSidebar.tsx +0 -136
  43. package/src/components/playground/PlaygroundTerminal.tsx +0 -44
  44. package/src/components/playground/SuggestionCards.tsx +0 -29
  45. package/src/components/playground/demos/ClinicaAINode.tsx +0 -221
  46. package/src/components/playground/demos/FinanceAINode.tsx +0 -226
  47. package/src/components/playground/demos/KiaAcademyNode.tsx +0 -250
  48. package/src/components/playground/demos/KiaBotNode.tsx +0 -207
  49. package/src/components/playground/demos/KiaCampaignNode.tsx +0 -191
  50. package/src/components/playground/demos/KiaComplianceNode.tsx +0 -140
  51. package/src/components/playground/demos/KiaCustomerJourneyNode.tsx +0 -220
  52. package/src/components/playground/demos/KiaCyberNode.tsx +0 -203
  53. package/src/components/playground/demos/KiaDashboardNode.tsx +0 -399
  54. package/src/components/playground/demos/KiaEmbudoOverviewNode.tsx +0 -168
  55. package/src/components/playground/demos/KiaExecutiveNode.tsx +0 -169
  56. package/src/components/playground/demos/KiaGamificationNode.tsx +0 -229
  57. package/src/components/playground/demos/KiaIntelligenceHubNode.tsx +0 -165
  58. package/src/components/playground/demos/KiaInventoryNode.tsx +0 -183
  59. package/src/components/playground/demos/KiaLeadScoringNode.tsx +0 -226
  60. package/src/components/playground/demos/KiaLiveSimulationNode.tsx +0 -177
  61. package/src/components/playground/demos/KiaMultiDealerNode.tsx +0 -223
  62. package/src/components/playground/demos/KiaNPSVoiceNode.tsx +0 -214
  63. package/src/components/playground/demos/KiaOmnichannelNode.tsx +0 -162
  64. package/src/components/playground/demos/KiaPBIBudgetNode.tsx +0 -152
  65. package/src/components/playground/demos/KiaPBIConversionNode.tsx +0 -206
  66. package/src/components/playground/demos/KiaPBIFunnelNode.tsx +0 -184
  67. package/src/components/playground/demos/KiaPBIOwnershipNode.tsx +0 -113
  68. package/src/components/playground/demos/KiaPBIPartnerNode.tsx +0 -143
  69. package/src/components/playground/demos/KiaPBIPreciosNode.tsx +0 -120
  70. package/src/components/playground/demos/KiaPBIRuntNode.tsx +0 -205
  71. package/src/components/playground/demos/KiaPartnerScoreNode.tsx +0 -206
  72. package/src/components/playground/demos/KiaPredictiveNode.tsx +0 -226
  73. package/src/components/playground/demos/KiaShowroomNode.tsx +0 -194
  74. package/src/components/playground/demos/KiaStoreNode.tsx +0 -215
  75. package/src/components/playground/demos/KiaSustainabilityNode.tsx +0 -173
  76. package/src/components/playground/demos/KiaUsedVehiclesNode.tsx +0 -163
  77. package/src/components/playground/demos/KiaWorkshopNode.tsx +0 -221
  78. package/src/components/playground/demos/SmartCityNode.tsx +0 -205
  79. package/src/components/playground/demos/kia_campaign_manifest.json +0 -112
  80. package/src/components/playground/input-parts/AIModelSelector.tsx +0 -156
  81. package/src/components/playground/input-parts/InputActions.tsx +0 -80
  82. package/src/components/playground/input-parts/InputToolbar.tsx +0 -245
  83. package/src/components/playground/input-parts/ResourceLibraryPanel.tsx +0 -287
  84. package/src/components/playground/sidebarDsdIO.ts +0 -82
  85. package/src/components/settings/SettingsPanel.tsx +0 -267
  86. package/src/components/shell/AppHeader.tsx +0 -9
  87. package/src/components/shell/AppShell.tsx +0 -139
  88. package/src/components/shell/ArtifactBar.tsx +0 -97
  89. package/src/components/shell/BootScreen.tsx +0 -19
  90. package/src/components/shell/CenterComposite.tsx +0 -87
  91. package/src/components/shell/CodeEditorPanel.tsx +0 -88
  92. package/src/components/shell/GlobalOverlays.tsx +0 -228
  93. package/src/components/shell/LayoutConfigurator.tsx +0 -209
  94. package/src/components/shell/LayoutGrid.tsx +0 -178
  95. package/src/components/shell/MorphShell.tsx +0 -368
  96. package/src/components/shell/PluginViewer.tsx +0 -147
  97. package/src/components/shell/ShellNexusPreview.tsx +0 -458
  98. package/src/components/shell/SlotRenderer.tsx +0 -115
  99. package/src/components/shell/TabBar.tsx +0 -94
  100. package/src/components/shell/TemplateLibrary.tsx +0 -195
  101. package/src/components/shell/layoutConstants.ts +0 -35
  102. package/src/components/shell/morphStageMeta.ts +0 -15
  103. package/src/components/shell/shells/BuiltInShells.tsx +0 -443
  104. package/src/components/shell/shells/DatawayChatShell.tsx +0 -42
  105. package/src/components/shell/shells/TokenPreview.tsx +0 -339
  106. package/src/components/shell/shells/bootShells.ts +0 -31
  107. package/src/components/shells/CreatorShell.tsx +0 -37
  108. package/src/components/shells/DecidoShell.tsx +0 -447
  109. package/src/components/shells/ExperimentalChatShell.tsx +0 -245
  110. package/src/components/shells/UserCanvas.tsx +0 -44
  111. package/src/components/studio/BlueprintManagerPanel.tsx +0 -137
  112. package/src/components/studio/DependencyTreePanel.tsx +0 -192
  113. package/src/components/studio/NodePalette.tsx +0 -92
  114. package/src/components/studio/NodePropertiesPanel.tsx +0 -81
  115. package/src/components/studio/ReactFlowEditor.tsx +0 -242
  116. package/src/components/studio/TimelineEditor.tsx +0 -122
  117. package/src/components/studio/TimelineKeyframeCard.tsx +0 -99
  118. package/src/components/studio/VariablePanel.tsx +0 -181
  119. package/src/components/studio/blueprint/BlueprintCard.tsx +0 -82
  120. package/src/components/studio/editor/CanvasContextMenu.tsx +0 -107
  121. package/src/components/studio/editor/EditorToolbar.tsx +0 -80
  122. package/src/components/studio/editor/StageContentRenderer.tsx +0 -134
  123. package/src/components/studio/editor/TrackPropertyEditors.tsx +0 -133
  124. package/src/components/studio/editor/TreeNodeItem.tsx +0 -91
  125. package/src/components/studio/editor/edgeStyles.ts +0 -43
  126. package/src/components/studio/editor/editorKeyHandler.ts +0 -95
  127. package/src/components/studio/editor/nodeTypeRegistry.ts +0 -137
  128. package/src/components/studio/editor/paletteCatalog.tsx +0 -84
  129. package/src/components/studio/nodes/shell/InteractionNodes.tsx +0 -82
  130. package/src/components/studio/nodes/shell/LayoutControlNodes.tsx +0 -69
  131. package/src/components/studio/nodes/shell/RegisterActionNode.tsx +0 -20
  132. package/src/components/studio/nodes/shell/RegisterButtonNode.tsx +0 -22
  133. package/src/components/studio/nodes/shell/RegisterPanelNode.tsx +0 -19
  134. package/src/components/studio/nodes/shell/RegisterSidebarNode.tsx +0 -19
  135. package/src/components/studio/nodes/shell/RegisterStatusBarNode.tsx +0 -22
  136. package/src/components/studio/nodes/shell/RegisterTabNode.tsx +0 -21
  137. package/src/components/studio/nodes/shell/RegisterTopBarNode.tsx +0 -22
  138. package/src/components/studio/nodes/shell/ShellConfigNode.tsx +0 -51
  139. package/src/components/studio/nodes/shell/ShellNodeBase.tsx +0 -100
  140. package/src/components/studio/nodes/shell/ThemeNodes.tsx +0 -51
  141. package/src/components/studio/nodes/shell/index.ts +0 -12
  142. package/src/components/widgets/BroadcastWidget.tsx +0 -93
  143. package/src/components/widgets/MarketplaceWidget.tsx +0 -298
  144. package/src/components/widgets/McpToolsWidget.tsx +0 -231
  145. package/src/components/widgets/OpsDashboard.tsx +0 -59
  146. package/src/components/widgets/QuickActionsWidget.tsx +0 -60
  147. package/src/components/widgets/UsageWidget.tsx +0 -112
  148. package/src/components/widgets/WidgetRenderer.tsx +0 -892
  149. package/src/components/widgets/WidgetSlotPanel.tsx +0 -213
  150. package/src/config/IconRegistry.ts +0 -126
  151. package/src/contexts/NetworkProvider.tsx +0 -162
  152. package/src/core/AIDirector.ts +0 -71
  153. package/src/core/EventBus.ts +0 -37
  154. package/src/core/PluginContext.tsx +0 -141
  155. package/src/hooks/listeners/useUIStateListener.ts +0 -59
  156. package/src/hooks/listeners/useWhatsAppListener.ts +0 -110
  157. package/src/hooks/morphBridge.ts +0 -82
  158. package/src/hooks/useAIModelSelector.ts +0 -144
  159. package/src/hooks/useAgentStream.ts +0 -220
  160. package/src/hooks/useAutoUpdater.ts +0 -89
  161. package/src/hooks/useBootSequence.ts +0 -20
  162. package/src/hooks/useExportDSD.ts +0 -53
  163. package/src/hooks/useFullscreen.ts +0 -35
  164. package/src/hooks/useGeminiStream.ts +0 -282
  165. package/src/hooks/useIntentLens.ts +0 -224
  166. package/src/hooks/useKeyboardShortcuts.ts +0 -69
  167. package/src/hooks/useLoggerBridge.ts +0 -32
  168. package/src/hooks/useMcpClient.ts +0 -112
  169. package/src/hooks/useNexusaiDeploy.ts +0 -118
  170. package/src/hooks/usePlaybackEngine.ts +0 -21
  171. package/src/hooks/usePlaygroundCommander.ts +0 -475
  172. package/src/hooks/usePluginEngine.ts +0 -165
  173. package/src/hooks/useScreenRecorder.ts +0 -73
  174. package/src/hooks/useShellKeyboard.ts +0 -40
  175. package/src/hooks/useShellShortcuts.ts +0 -118
  176. package/src/hooks/useSoundEffects.ts +0 -35
  177. package/src/hooks/useStudioConfig.ts +0 -72
  178. package/src/hooks/useSystemBoot.ts +0 -84
  179. package/src/hooks/useSystemTelemetry.ts +0 -62
  180. package/src/lib/debugLogger.ts +0 -80
  181. package/src/lib/networkInterceptor.ts +0 -100
  182. package/src/mocks/decido.tsx +0 -41
  183. package/src/plugins/pluginAPI.ts +0 -190
  184. package/src/store/McpStore.ts +0 -69
  185. package/src/store/UpdaterStore.ts +0 -60
  186. package/src/store/engine.ts +0 -392
  187. package/src/store/index.ts +0 -4
  188. package/src/store/layoutPresets.ts +0 -66
  189. package/src/store/playgroundTypes.ts +0 -98
  190. package/src/store/useActionTimelineStore.ts +0 -48
  191. package/src/store/useDebugPanelStore.ts +0 -98
  192. package/src/store/useDebugProfileStore.ts +0 -130
  193. package/src/store/useLayoutStore.ts +0 -205
  194. package/src/store/useMorphInstanceStore.ts +0 -289
  195. package/src/store/useMorphologyStore.ts +0 -103
  196. package/src/store/usePlaygroundStore.ts +0 -236
  197. package/src/store/useShellRegistry.ts +0 -123
  198. package/src/store/useSuggestionsStore.ts +0 -57
  199. package/src/store/useThemeStore.ts +0 -399
  200. package/src/store/useUIComponentStore.ts +0 -179
  201. package/src/types/DecidoStoryDefinition.ts +0 -43
  202. package/src/utils/ai/ai-architect.ts +0 -92
  203. package/src/utils/ai/ai-code.ts +0 -187
  204. package/src/utils/ai/ai-core.ts +0 -50
  205. package/src/utils/ai/ai-media.ts +0 -292
  206. package/src/utils/layoutGraph.ts +0 -67
  207. package/tsconfig.json +0 -17
  208. package/tsconfig.tsbuildinfo +0 -1
@@ -1,255 +0,0 @@
1
- import React, { useState, useMemo, useCallback, useSyncExternalStore } from 'react';
2
- import { Database, Zap, ChevronRight, ChevronDown, ToggleLeft, ToggleRight, Camera, ArrowLeftRight, Download, Upload } from 'lucide-react';
3
- import { usePlaygroundStore } from '../../store/usePlaygroundStore';
4
- import { useEngineStore, useTimelineStore, useUserStateStore } from '@decido/engine';
5
- import { useMorphologyStore } from '../../store/useMorphologyStore';
6
- import { useUIComponentStore } from '../../store/useUIComponentStore';
7
-
8
- // ─── Store Registry ───
9
- interface StoreEntry {
10
- name: string;
11
- group: 'shell' | 'studio';
12
- store: any;
13
- }
14
-
15
- let _shellStores: StoreEntry[] | null = null;
16
- function getShellStores(): StoreEntry[] {
17
- if (_shellStores) return _shellStores;
18
- // Shell stores are injected by the host app via a global registry
19
- const registry = (globalThis as any).__DECIDO_SHELL_STORES__ as StoreEntry[] | undefined;
20
- _shellStores = registry || [];
21
- return _shellStores;
22
- }
23
-
24
- const STUDIO_STORES: StoreEntry[] = [
25
- { name: 'Playground', group: 'studio', store: usePlaygroundStore },
26
- { name: 'Engine', group: 'studio', store: useEngineStore },
27
- { name: 'Timeline', group: 'studio', store: useTimelineStore },
28
- { name: 'Morphology', group: 'studio', store: useMorphologyStore },
29
- { name: 'UserState', group: 'studio', store: useUserStateStore },
30
- { name: 'UIComponents', group: 'studio', store: useUIComponentStore },
31
- ];
32
-
33
- // ─── Recursive Value Editor ───
34
- function ValueEditor({ value, path, onUpdate, depth = 0 }: { value: any; path: string; onUpdate: (path: string, newValue: any) => void; depth?: number }) {
35
- const [collapsed, setCollapsed] = useState(depth > 1);
36
- if (value === null || value === undefined) return <span className="text-text-muted italic text-[10px] font-mono">{String(value)}</span>;
37
- if (typeof value === 'function') return null;
38
- if (typeof value === 'boolean') {
39
- return (
40
- <button onClick={() => onUpdate(path, !value)} className={`flex items-center gap-1 text-[10px] font-mono transition-colors ${value ? 'text-emerald-400' : 'text-text-muted'}`}>
41
- {value ? <ToggleRight size={14} /> : <ToggleLeft size={14} />}{String(value)}
42
- </button>
43
- );
44
- }
45
- if (typeof value === 'number') return <input type="number" value={value} onChange={e => onUpdate(path, Number(e.target.value))} className="bg-surface-tertiary border border-border-default rounded px-1.5 py-0.5 text-[10px] font-mono text-cyan-300 w-24 outline-hidden focus:border-cyan-500/50" />;
46
- if (typeof value === 'string') return <input type="text" value={value} onChange={e => onUpdate(path, e.target.value)} className="bg-surface-tertiary border border-border-default rounded px-1.5 py-0.5 text-[10px] font-mono text-amber-300 flex-1 min-w-[80px] max-w-[300px] outline-hidden focus:border-amber-500/50" />;
47
-
48
- const tag = Object.prototype.toString.call(value);
49
- if (tag === '[object Map]') {
50
- const entries: [any, any][] = Array.from((value as globalThis.Map<any, any>).entries());
51
- return (
52
- <div className="pl-3 border-l border-border-subtle">
53
- <button onClick={() => setCollapsed(!collapsed)} className="flex items-center gap-1 text-[10px] text-text-muted hover:text-text-primary">{collapsed ? <ChevronRight size={10} /> : <ChevronDown size={10} />}<span className="text-violet-400">Map</span>({entries.length})</button>
54
- {!collapsed && entries.map((entry, i) => <div key={i} className="flex items-start gap-2 py-0.5 pl-2"><span className="text-[10px] text-text-muted font-mono shrink-0">{String(entry[0])}:</span><ValueEditor value={entry[1]} path={`${path}.${String(entry[0])}`} onUpdate={() => {}} depth={depth + 1} /></div>)}
55
- </div>
56
- );
57
- }
58
- if (tag === '[object Set]') {
59
- const items: any[] = Array.from(value as globalThis.Set<any>);
60
- return (
61
- <div className="pl-3 border-l border-border-subtle">
62
- <button onClick={() => setCollapsed(!collapsed)} className="flex items-center gap-1 text-[10px] text-text-muted hover:text-text-primary">{collapsed ? <ChevronRight size={10} /> : <ChevronDown size={10} />}<span className="text-violet-400">Set</span>({items.length})</button>
63
- {!collapsed && items.map((v, i) => <div key={i} className="pl-2 py-0.5"><ValueEditor value={v} path={`${path}[${i}]`} onUpdate={() => {}} depth={depth + 1} /></div>)}
64
- </div>
65
- );
66
- }
67
- if (Array.isArray(value)) {
68
- return (
69
- <div className="pl-3 border-l border-border-subtle">
70
- <button onClick={() => setCollapsed(!collapsed)} className="flex items-center gap-1 text-[10px] text-text-muted hover:text-text-primary">{collapsed ? <ChevronRight size={10} /> : <ChevronDown size={10} />}<span className="text-blue-400">Array</span><span className="text-text-muted">[{value.length}]</span></button>
71
- {!collapsed && value.slice(0, 50).map((item, i) => <div key={i} className="flex items-start gap-2 py-0.5 pl-2"><span className="text-[9px] text-text-muted font-mono shrink-0 w-4 text-right">{i}</span><ValueEditor value={item} path={`${path}[${i}]`} onUpdate={onUpdate} depth={depth + 1} /></div>)}
72
- {value.length > 50 && <span className="text-[9px] text-text-muted pl-2">... +{value.length - 50} más</span>}
73
- </div>
74
- );
75
- }
76
- if (typeof value === 'object') {
77
- const entries = Object.entries(value).filter(([, v]) => typeof v !== 'function');
78
- return (
79
- <div className="pl-3 border-l border-border-subtle">
80
- <button onClick={() => setCollapsed(!collapsed)} className="flex items-center gap-1 text-[10px] text-text-muted hover:text-text-primary">{collapsed ? <ChevronRight size={10} /> : <ChevronDown size={10} />}<span className="text-orange-400">{`{${entries.length}}`}</span></button>
81
- {!collapsed && entries.map(([k, v]) => <div key={k} className="flex items-start gap-2 py-0.5 pl-2"><span className="text-[10px] text-text-secondary font-mono shrink-0">{k}:</span><ValueEditor value={v} path={`${path}.${k}`} onUpdate={onUpdate} depth={depth + 1} /></div>)}
82
- </div>
83
- );
84
- }
85
- return <span className="text-text-muted text-[10px] font-mono">{String(value)}</span>;
86
- }
87
-
88
- // ─── Stores Tab ───
89
- export function StoresTab() {
90
- const allStores = useMemo(() => [...getShellStores(), ...STUDIO_STORES], []);
91
- const [activeStore, setActiveStore] = useState(allStores[0]?.name || '');
92
- const entry = useMemo(() => allStores.find(s => s.name === activeStore), [allStores, activeStore]);
93
- const storeSnapshot = useSyncExternalStore(
94
- useCallback((cb: () => void) => entry?.store?.subscribe?.(cb) || (() => {}), [entry]),
95
- useCallback(() => entry?.store?.getState?.() ?? {}, [entry])
96
- );
97
- const { stateEntries, actionEntries } = useMemo(() => {
98
- const stateE: [string, any][] = []; const actionE: [string, Function][] = [];
99
- for (const [k, v] of Object.entries(storeSnapshot)) { if (typeof v === 'function') actionE.push([k, v]); else stateE.push([k, v]); }
100
- return { stateEntries: stateE, actionEntries: actionE };
101
- }, [storeSnapshot]);
102
- const handleUpdate = useCallback((path: string, newValue: any) => {
103
- if (!entry) return;
104
- const parts = path.split('.'); const topKey = parts[1]; if (!topKey) return;
105
- if (parts.length === 2) { entry.store.setState({ [topKey]: newValue }); } else {
106
- const current = entry.store.getState()[topKey];
107
- try { const clone = JSON.parse(JSON.stringify(current)); let target = clone; for (let i = 2; i < parts.length - 1; i++) target = target[parts[i].replace(/\[(\d+)\]/, '.$1')]; const lastKey = parts[parts.length - 1].replace(/\[(\d+)\]/, '$1'); target[lastKey] = newValue; entry.store.setState({ [topKey]: clone }); } catch { entry.store.setState({ [topKey]: newValue }); }
108
- }
109
- }, [entry]);
110
- const [actionFeedback, setActionFeedback] = useState<string | null>(null);
111
-
112
- // Sprint AU: Snapshot/Diff
113
- interface Snapshot { id: number; label: string; data: Record<string, any> }
114
- const [snapshots, setSnapshots] = useState<Snapshot[]>([]);
115
- const [showDiff, setShowDiff] = useState<number | null>(null);
116
-
117
- return (
118
- <div className="flex h-full">
119
- <div className="w-32 shrink-0 border-r border-border-subtle overflow-y-auto bg-surface-primary/50">
120
- {['shell', 'studio'].map(group => {
121
- const groupStores = allStores.filter(s => s.group === group);
122
- if (groupStores.length === 0) return null;
123
- return (
124
- <div key={group}>
125
- <div className="px-2 py-1 text-[8px] font-bold text-text-muted uppercase tracking-widest">{group}</div>
126
- {groupStores.map(s => (
127
- <button key={s.name} onClick={() => setActiveStore(s.name)} className={`w-full text-left px-2 py-1.5 text-[10px] font-semibold transition-all ${activeStore === s.name ? 'bg-emerald-500/10 text-emerald-400 border-r-2 border-emerald-400' : 'text-text-muted hover:text-text-primary hover:bg-surface-glass'}`}>{s.name}</button>
128
- ))}
129
- </div>
130
- );
131
- })}
132
- </div>
133
- <div className="flex-1 overflow-y-auto custom-scrollbar p-3 space-y-3">
134
- <div>
135
- <div className="text-[10px] font-bold text-text-secondary uppercase tracking-widest mb-2 flex items-center gap-1"><Database size={10} /> Estado ({stateEntries.length})</div>
136
- <div className="space-y-1">
137
- {stateEntries.map(([key, value]) => (
138
- <div key={key} className="flex items-start gap-2 py-0.5">
139
- <span className="text-[10px] text-text-primary font-mono font-semibold shrink-0 min-w-[100px]">{key}</span>
140
- <ValueEditor value={value} path={`root.${key}`} onUpdate={handleUpdate} depth={0} />
141
- </div>
142
- ))}
143
- </div>
144
- </div>
145
- {actionEntries.length > 0 && (
146
- <div>
147
- <div className="text-[10px] font-bold text-text-secondary uppercase tracking-widest mb-2 flex items-center gap-1"><Zap size={10} /> Acciones ({actionEntries.length})</div>
148
- <div className="flex flex-wrap gap-1">
149
- {actionEntries.map(([name, fn]) => (
150
- <button key={name} onClick={() => { try { const r = fn(); if (r instanceof Promise) r.catch((e: any) => setActionFeedback(`❌ ${name}: ${e.message}`)); setActionFeedback(`✅ ${name}()`); } catch (e: any) { setActionFeedback(`❌ ${name}: ${e.message}`); } setTimeout(() => setActionFeedback(null), 2000); }} className="px-2 py-1 rounded text-[9px] font-mono font-bold bg-surface-glass text-text-secondary hover:text-emerald-400 hover:bg-emerald-500/10 border border-border-subtle hover:border-emerald-500/20 transition-all" title={`Ejecutar ${name}()`}>▶ {name}</button>
151
- ))}
152
- </div>
153
- {actionFeedback && <div className="mt-2 text-[10px] font-mono text-text-secondary animate-pulse">{actionFeedback}</div>}
154
- </div>
155
- )}
156
-
157
- {/* Sprint AU: Snapshot/Diff */}
158
- <div className="border-t border-border-subtle pt-3 mt-3">
159
- <div className="text-[10px] font-bold text-text-secondary uppercase tracking-widest mb-2 flex items-center gap-1">
160
- <Camera size={10} /> Snapshots
161
- </div>
162
- <div className="flex flex-wrap gap-1 mb-2">
163
- <button
164
- onClick={() => {
165
- if (!entry) return;
166
- const state = entry.store.getState();
167
- const snap: Record<string, any> = {};
168
- for (const [k, v] of Object.entries(state)) {
169
- if (typeof v !== 'function') snap[k] = v;
170
- }
171
- setSnapshots(prev => [...prev, {
172
- id: Date.now(),
173
- label: `${activeStore} @ ${new Date().toLocaleTimeString()}`,
174
- data: JSON.parse(JSON.stringify(snap, (_, v) => {
175
- if (v instanceof Map) return Object.fromEntries(v);
176
- if (v instanceof Set) return Array.from(v);
177
- return v;
178
- })),
179
- }]);
180
- }}
181
- className="px-2 py-1 rounded text-[9px] font-mono font-bold bg-violet-500/10 text-violet-400 hover:bg-violet-500/20 border border-violet-500/20"
182
- >
183
- 📸 Snapshot
184
- </button>
185
- {snapshots.length > 0 && showDiff === null && (
186
- <button
187
- onClick={() => setShowDiff(snapshots[snapshots.length - 1].id)}
188
- className="px-2 py-1 rounded text-[9px] font-mono font-bold bg-cyan-500/10 text-cyan-400 hover:bg-cyan-500/20 border border-cyan-500/20"
189
- >
190
- <ArrowLeftRight size={10} className="inline mr-1" />Diff vs último
191
- </button>
192
- )}
193
- {showDiff !== null && (
194
- <button onClick={() => setShowDiff(null)} className="px-2 py-1 rounded text-[9px] font-mono bg-surface-tertiary text-text-secondary hover:bg-surface-tertiary">
195
- Cerrar diff
196
- </button>
197
- )}
198
- {snapshots.length > 0 && (
199
- <button
200
- onClick={() => {
201
- const blob = new Blob([JSON.stringify(snapshots, null, 2)], { type: 'application/json' });
202
- const url = URL.createObjectURL(blob);
203
- const a = document.createElement('a'); a.href = url; a.download = `snapshots-${activeStore}.json`; a.click();
204
- URL.revokeObjectURL(url);
205
- }}
206
- className="px-2 py-1 rounded text-[9px] font-mono text-text-muted hover:text-text-primary hover:bg-surface-glass"
207
- >
208
- <Download size={10} className="inline mr-1" />Export
209
- </button>
210
- )}
211
- </div>
212
- {/* Snapshot list */}
213
- {snapshots.map((snap, i) => (
214
- <div key={snap.id} className="flex items-center gap-2 py-0.5 text-[9px]">
215
- <span className="text-text-muted font-mono">{snap.label}</span>
216
- <button onClick={() => setShowDiff(showDiff === snap.id ? null : snap.id)} className={`px-1.5 py-0.5 rounded font-bold ${showDiff === snap.id ? 'bg-cyan-500/20 text-cyan-400' : 'text-text-muted hover:text-cyan-400'}`}>
217
- diff
218
- </button>
219
- <button onClick={() => setSnapshots(prev => prev.filter(s => s.id !== snap.id))} className="text-text-muted hover:text-red-400">×</button>
220
- </div>
221
- ))}
222
- {/* Diff view */}
223
- {showDiff !== null && (() => {
224
- const snap = snapshots.find(s => s.id === showDiff);
225
- if (!snap) return null;
226
- const current: Record<string, any> = {};
227
- for (const [k, v] of stateEntries) current[k] = v;
228
- const allKeys = new Set([...Object.keys(snap.data), ...Object.keys(current)]);
229
- return (
230
- <div className="mt-2 bg-surface-primary rounded border border-border-subtle p-2 max-h-[200px] overflow-y-auto custom-scrollbar">
231
- <div className="text-[8px] text-text-muted mb-1">Diff: {snap.label} → actual</div>
232
- {Array.from(allKeys).map(key => {
233
- const oldVal = JSON.stringify(snap.data[key]);
234
- const newVal = JSON.stringify(current[key], (_, v) => {
235
- if (v instanceof Map) return Object.fromEntries(v);
236
- if (v instanceof Set) return Array.from(v);
237
- return v;
238
- });
239
- if (oldVal === newVal) return null;
240
- return (
241
- <div key={key} className="py-0.5 font-mono text-[9px]">
242
- <span className="text-text-secondary font-semibold">{key}:</span>
243
- {oldVal !== undefined && <div className="text-red-400/70 pl-3">- {oldVal?.slice(0, 100)}</div>}
244
- {newVal !== undefined && <div className="text-emerald-400/70 pl-3">+ {newVal?.slice(0, 100)}</div>}
245
- </div>
246
- );
247
- })}
248
- </div>
249
- );
250
- })()}
251
- </div>
252
- </div>
253
- </div>
254
- );
255
- }
@@ -1,59 +0,0 @@
1
- import React from 'react';
2
- import { ArrowRight, Circle } from 'lucide-react';
3
- import { usePlaygroundStore } from '../../store/usePlaygroundStore';
4
- import { useTimelineStore } from '@decido/engine';
5
- import { useEngineStore } from '@decido/engine';
6
- import { TRACK_CONFIG } from './debugConfig';
7
-
8
- export function TopologyTab() {
9
- const prototypeBrand = usePlaygroundStore(s => s.prototypeBrand);
10
- const timelines = useTimelineStore(s => s.timelines);
11
- const activeNodeIds = useEngineStore(s => s.activeNodeIds);
12
- const flow = timelines[prototypeBrand];
13
-
14
- if (!flow) return <div className="flex items-center justify-center h-full text-text-muted text-xs font-mono">Sin blueprint activo</div>;
15
-
16
- const kfs = flow.keyframes || [];
17
- const edges = flow.edges || [];
18
- const activeSet = new Set(activeNodeIds);
19
-
20
- return (
21
- <div className="h-full overflow-y-auto custom-scrollbar p-3">
22
- <div className="text-[10px] font-bold text-text-muted uppercase tracking-widest mb-3">
23
- Grafo: {(flow as any).name || prototypeBrand} · {kfs.length} nodos · {edges.length} aristas
24
- </div>
25
- <div className="space-y-1">
26
- {kfs.map((kf: any) => {
27
- const cfg = TRACK_CONFIG[kf.track] || { icon: Circle, color: 'text-text-secondary', label: kf.track };
28
- const Icon = cfg.icon;
29
- const isActive = activeSet.has(kf.id);
30
- const outEdges = edges.filter((e: any) => e.source === kf.id);
31
- return (
32
- <div key={kf.id} className={`group rounded-lg border transition-all ${isActive ? 'bg-cyan-500/10 border-cyan-500/30 ring-1 ring-cyan-500/20' : 'bg-surface-glass border-border-subtle hover:bg-surface-glass'}`}>
33
- <div className="flex items-center gap-2 px-3 py-2">
34
- <Icon size={13} className={cfg.color} />
35
- <span className={`text-[11px] font-semibold flex-1 ${isActive ? 'text-cyan-300' : 'text-text-primary'}`}>{kf.label || kf.state || kf.id}</span>
36
- <span className="text-[9px] text-text-muted font-mono">{kf.track}</span>
37
- {isActive && <span className="w-2 h-2 rounded-full bg-cyan-400 animate-pulse" />}
38
- </div>
39
- {outEdges.length > 0 && (
40
- <div className="px-3 pb-2 flex flex-wrap gap-1">
41
- {outEdges.map((e: any, i: number) => {
42
- const target = kfs.find((k: any) => k.id === e.target);
43
- return (
44
- <span key={i} className="inline-flex items-center gap-1 text-[9px] text-text-muted bg-surface-glass rounded px-1.5 py-0.5">
45
- <ArrowRight size={8} className="text-text-muted" />
46
- {target?.label || target?.state || e.target}
47
- {e.sourceHandle && e.sourceHandle !== 'success' && <span className="text-amber-500/70">({e.sourceHandle})</span>}
48
- </span>
49
- );
50
- })}
51
- </div>
52
- )}
53
- </div>
54
- );
55
- })}
56
- </div>
57
- </div>
58
- );
59
- }
@@ -1,66 +0,0 @@
1
- import React from 'react';
2
- import {
3
- Info, AlertTriangle, AlertCircle, Bug,
4
- Zap, MessageSquare, Settings, GitBranch,
5
- ArrowRight, Circle, Map
6
- } from 'lucide-react';
7
- import { DebugTab, LogLevel, DebugLogEntry } from '../../store/useDebugPanelStore';
8
-
9
- // ─── Tab Config ───
10
- export const TABS: { id: DebugTab; label: string; icon: React.ComponentType<any>; color: string }[] = [
11
- { id: 'logs', label: 'Logs', icon: Bug, color: 'text-green-400' },
12
- { id: 'flow-health', label: 'Health', icon: () => <span>❤</span>, color: 'text-cyan-400' },
13
- { id: 'topology', label: 'Topology', icon: Map, color: 'text-purple-400' },
14
- { id: 'replay', label: 'Replay', icon: () => <span>▶</span>, color: 'text-amber-400' },
15
- { id: 'stores', label: 'Stores', icon: () => <span>🗄</span>, color: 'text-emerald-400' },
16
- { id: 'morph-stack', label: 'Morph', icon: () => <span>🔮</span>, color: 'text-violet-400' },
17
- ];
18
-
19
- // ─── Level Config ───
20
- export const LEVEL_CONFIG: Record<LogLevel, { icon: React.ComponentType<any>; color: string; bg: string; label: string }> = {
21
- info: { icon: Info, color: 'text-blue-400', bg: 'bg-blue-500/10', label: 'INFO' },
22
- warn: { icon: AlertTriangle, color: 'text-amber-400', bg: 'bg-amber-500/10', label: 'WARN' },
23
- error: { icon: AlertCircle, color: 'text-red-400', bg: 'bg-red-500/10', label: 'ERROR' },
24
- debug: { icon: Bug, color: 'text-purple-400', bg: 'bg-purple-500/10', label: 'DEBUG' },
25
- };
26
-
27
- // ─── Track visual config ───
28
- export const TRACK_CONFIG: Record<string, { icon: React.ComponentType<any>; color: string; label: string }> = {
29
- trigger: { icon: Zap, color: 'text-yellow-400', label: 'Trigger' },
30
- dialogue: { icon: MessageSquare, color: 'text-blue-400', label: 'Diálogo' },
31
- logic: { icon: Settings, color: 'text-orange-400', label: 'Lógica' },
32
- ui: { icon: Map, color: 'text-green-400', label: 'UI' },
33
- render: { icon: Map, color: 'text-pink-400', label: 'Render' },
34
- condition: { icon: GitBranch, color: 'text-amber-400', label: 'Condición' },
35
- subflow: { icon: ArrowRight, color: 'text-purple-400', label: 'Subflow' },
36
- entry: { icon: Circle, color: 'text-emerald-400', label: 'Entry' },
37
- return: { icon: ArrowRight, color: 'text-red-400', label: 'Return' },
38
- set: { icon: Settings, color: 'text-cyan-400', label: 'Variable' },
39
- morph: { icon: Map, color: 'text-violet-400', label: 'Morph' },
40
- };
41
-
42
- // ─── Log Entry Row ───
43
- export const LogRow = React.memo(function LogRow({ entry }: { entry: DebugLogEntry }) {
44
- const cfg = LEVEL_CONFIG[entry.level];
45
- const Icon = cfg.icon;
46
- return (
47
- <div className={`flex items-start gap-2 px-3 py-1 hover:bg-surface-glass font-mono text-[11px] leading-relaxed border-b border-border-subtle ${cfg.bg}`}>
48
- <span className="text-text-muted shrink-0 w-[70px] pt-0.5">{entry.timestamp}</span>
49
- <Icon size={12} className={`${cfg.color} shrink-0 mt-0.5`} />
50
- <span className={`font-bold shrink-0 w-[38px] ${cfg.color}`}>{cfg.label}</span>
51
- {entry.categories.length > 0 && (
52
- <span className="shrink-0 flex gap-1">
53
- {entry.categories.map((c, i) => (
54
- <span key={i} className="px-1.5 py-0.5 rounded text-[9px] font-bold bg-surface-glass text-text-secondary uppercase tracking-widest">{c}</span>
55
- ))}
56
- </span>
57
- )}
58
- <span className="text-text-primary flex-1 break-all">{entry.message}</span>
59
- {entry.data !== undefined && (
60
- <span className="text-text-muted shrink-0 truncate max-w-[200px]">
61
- {typeof entry.data === 'object' ? JSON.stringify(entry.data) : String(entry.data)}
62
- </span>
63
- )}
64
- </div>
65
- );
66
- });
@@ -1,112 +0,0 @@
1
- import React from 'react';
2
- import { motion, AnimatePresence } from 'motion/react';
3
- import { X, Bug, Activity, Map, PlayCircle, Database, Layers, Globe, UserCog, Clock, Gauge, Download, Palette } from 'lucide-react';
4
- import { useDebugPanelStore, DebugTab } from '../../store/useDebugPanelStore';
5
- import { LogsTab } from '../debug/LogsTab';
6
- import { FlowHealthTab } from '../debug/FlowHealthTab';
7
- import { TopologyTab } from '../debug/TopologyTab';
8
- import { ReplayTab } from '../debug/ReplayTab';
9
- import { StoresTab } from '../debug/StoresTab';
10
- import { MorphStackTab } from '../debug/MorphStackTab';
11
- import { NetworkTab } from '../debug/NetworkTab';
12
- import { ProfilesTab } from '../debug/ProfilesTab';
13
- import { ActionTimelineTab } from '../debug/ActionTimelineTab';
14
- import { PerformanceTab } from '../debug/PerformanceTab';
15
- import { ExportTab } from '../debug/ExportTab';
16
- import { CSSInspectorTab } from '../debug/CSSInspectorTab';
17
-
18
- // ─── Tab Config ───
19
- const TABS: { id: DebugTab; label: string; icon: React.ComponentType<any>; color: string }[] = [
20
- { id: 'logs', label: 'Logs', icon: Bug, color: 'text-green-400' },
21
- { id: 'flow-health', label: 'Health', icon: Activity, color: 'text-cyan-400' },
22
- { id: 'topology', label: 'Topology', icon: Map, color: 'text-purple-400' },
23
- { id: 'replay', label: 'Replay', icon: PlayCircle, color: 'text-amber-400' },
24
- { id: 'stores', label: 'Stores', icon: Database, color: 'text-emerald-400' },
25
- { id: 'morph-stack', label: 'Morph', icon: Layers, color: 'text-violet-400' },
26
- { id: 'network', label: 'Network', icon: Globe, color: 'text-cyan-400' },
27
- { id: 'profiles', label: 'Profiles', icon: UserCog, color: 'text-rose-400' },
28
- { id: 'timeline', label: 'Timeline', icon: Clock, color: 'text-amber-400' },
29
- { id: 'performance', label: 'Perf', icon: Gauge, color: 'text-lime-400' },
30
- { id: 'export', label: 'Export', icon: Download, color: 'text-text-secondary' },
31
- { id: 'css-inspector', label: 'CSS', icon: Palette, color: 'text-indigo-400' },
32
- ];
33
-
34
- // ─── Tab Content Router ───
35
- const TAB_COMPONENTS: Record<DebugTab, React.ComponentType> = {
36
- 'logs': LogsTab,
37
- 'flow-health': FlowHealthTab,
38
- 'topology': TopologyTab,
39
- 'replay': ReplayTab,
40
- 'stores': StoresTab,
41
- 'morph-stack': MorphStackTab,
42
- 'network': NetworkTab,
43
- 'profiles': ProfilesTab,
44
- 'timeline': ActionTimelineTab,
45
- 'performance': PerformanceTab,
46
- 'export': ExportTab,
47
- 'css-inspector': CSSInspectorTab,
48
- };
49
-
50
- export interface DebugPanelProps {
51
- className?: string;
52
- }
53
-
54
- export const DebugPanel = React.memo(function DebugPanel({ className }: DebugPanelProps) {
55
- const isOpen = useDebugPanelStore(s => s.isOpen);
56
- const activeTab = useDebugPanelStore(s => s.activeTab);
57
- const setActiveTab = useDebugPanelStore(s => s.setActiveTab);
58
- const togglePanel = useDebugPanelStore(s => s.togglePanel);
59
- const errorCount = useDebugPanelStore(s => s.errorCount);
60
-
61
- if (!isOpen) return null;
62
-
63
- const ActiveComponent = TAB_COMPONENTS[activeTab];
64
-
65
- return (
66
- <AnimatePresence>
67
- <motion.div
68
- initial={{ height: 0, opacity: 0 }}
69
- animate={{ height: '100%', opacity: 1 }}
70
- exit={{ height: 0, opacity: 0 }}
71
- transition={{ duration: 0.15, ease: 'easeOut' }}
72
- className={`flex flex-col bg-surface-primary border-t border-border-default overflow-hidden ${className || ''}`}
73
- >
74
- {/* Tab Bar */}
75
- <div className="flex items-center justify-between px-2 py-1 border-b border-border-subtle shrink-0 bg-surface-primary/80">
76
- <div className="flex items-center gap-0.5 overflow-x-auto flex-1 min-w-0" style={{ scrollbarWidth: 'none' }}>
77
- {TABS.map(tab => {
78
- const Icon = tab.icon;
79
- const isActive = activeTab === tab.id;
80
- return (
81
- <button
82
- key={tab.id}
83
- onClick={() => setActiveTab(tab.id)}
84
- className={`flex items-center gap-1 px-2 py-1 rounded-md text-[10px] font-semibold transition-all whitespace-nowrap shrink-0 ${
85
- isActive ? `bg-surface-glass text-text-primary shadow-xs` : 'text-text-muted hover:text-text-primary hover:bg-surface-glass'
86
- }`}
87
- >
88
- <Icon size={11} className={isActive ? tab.color : ''} />
89
- {tab.label}
90
- {tab.id === 'logs' && errorCount > 0 && (
91
- <span className="ml-0.5 px-1 py-0.5 rounded-full text-[8px] font-bold bg-red-500/20 text-red-400">{errorCount}</span>
92
- )}
93
- </button>
94
- );
95
- })}
96
- </div>
97
- <div className="flex items-center gap-1 shrink-0 ml-1">
98
- <span className="text-[9px] text-text-muted font-mono mr-1">⌃⇧D</span>
99
- <button onClick={togglePanel} className="p-1 rounded text-text-muted hover:text-text-primary hover:bg-surface-glass transition-colors" title="Cerrar panel">
100
- <X size={14} />
101
- </button>
102
- </div>
103
- </div>
104
-
105
- {/* Tab Content */}
106
- <div className="flex-1 overflow-hidden">
107
- <ActiveComponent />
108
- </div>
109
- </motion.div>
110
- </AnimatePresence>
111
- );
112
- });
@@ -1,92 +0,0 @@
1
- import React, { useState, useRef, useEffect, useCallback } from 'react';
2
- import { motion, AnimatePresence } from 'motion/react';
3
- import { Network } from 'lucide-react';
4
-
5
- interface HeaderCenterControlsProps {
6
- isSpeaking: boolean;
7
- isCreatorMode: boolean;
8
- creatorViewMode: 'timeline' | 'graph';
9
- /** @deprecated — use internal useAIModelSelector instead */
10
- modelLabel?: string;
11
- onModelClick?: () => void;
12
- onSyncCreatorMode: (val: boolean) => void;
13
- onSetCreatorViewMode: (mode: 'timeline' | 'graph') => void;
14
- }
15
-
16
- export const HeaderCenterControls = React.memo(function HeaderCenterControls({
17
- isSpeaking, isCreatorMode, creatorViewMode, modelLabel, onModelClick, onSyncCreatorMode, onSetCreatorViewMode
18
- }: HeaderCenterControlsProps) {
19
- return (
20
- <div data-tauri-drag-region className="flex-1 flex items-center justify-center gap-2 min-w-0">
21
- {/* Voice Live Indicator */}
22
- <AnimatePresence>
23
- {isSpeaking && (
24
- <motion.div
25
- initial={{ opacity: 0, width: 0 }}
26
- animate={{ opacity: 1, width: 'auto' }}
27
- exit={{ opacity: 0, width: 0 }}
28
- className="flex items-center gap-0.5 mr-2 overflow-hidden"
29
- >
30
- {[1, 2, 3, 4, 5].map((i) => (
31
- <motion.div
32
- key={i}
33
- animate={{ height: [6, Math.random() * 16 + 8, 6] }}
34
- transition={{ repeat: Infinity, duration: 0.5, delay: i * 0.08 }}
35
- className="w-1 bg-accent-cyan rounded-full"
36
- />
37
- ))}
38
- </motion.div>
39
- )}
40
- </AnimatePresence>
41
-
42
- {/* Mode Toggle — Premium Segmented Control */}
43
- <div className="flex bg-surface-tertiary border border-border-default rounded-lg p-0.5 shadow-sm">
44
- <button onClick={() => { if (isCreatorMode) onSyncCreatorMode(false); }}
45
- className={`px-3 py-1 text-[11px] font-semibold rounded-md transition-all duration-200 ${
46
- !isCreatorMode
47
- ? 'bg-surface-elevated text-text-primary shadow-sm border border-border-default'
48
- : 'text-text-muted hover:text-text-secondary border border-transparent'
49
- }`}>
50
- Player
51
- </button>
52
- <button onClick={() => { if (!isCreatorMode) onSyncCreatorMode(true); }}
53
- className={`px-3 py-1 text-[11px] font-semibold rounded-md transition-all duration-200 flex items-center gap-1 ${
54
- isCreatorMode
55
- ? 'bg-surface-elevated text-accent-purple shadow-sm border border-accent-purple'
56
- : 'text-text-muted hover:text-text-secondary border border-transparent'
57
- }`}>
58
- Creator
59
- </button>
60
- </div>
61
-
62
- {/* Creator Sub-Tabs */}
63
- <AnimatePresence>
64
- {isCreatorMode && (
65
- <motion.div
66
- initial={{ opacity: 0, scale: 0.9, width: 0 }}
67
- animate={{ opacity: 1, scale: 1, width: 'auto' }}
68
- exit={{ opacity: 0, scale: 0.9, width: 0 }}
69
- className="hidden lg:flex bg-surface-tertiary border border-border-default rounded-lg p-0.5 overflow-hidden shadow-sm"
70
- >
71
- <button onClick={() => onSetCreatorViewMode('timeline')}
72
- className={`px-2.5 py-1 text-[10px] font-bold tracking-wider rounded-md transition-all duration-200 ${
73
- creatorViewMode === 'timeline'
74
- ? 'bg-surface-elevated text-text-primary shadow-sm border border-border-default'
75
- : 'text-text-muted hover:text-text-secondary border border-transparent'
76
- }`}>
77
- TIMELINE
78
- </button>
79
- <button onClick={() => onSetCreatorViewMode('graph')}
80
- className={`px-2.5 py-1 text-[10px] font-bold tracking-wider rounded-md transition-all duration-200 flex items-center gap-1 ${
81
- creatorViewMode === 'graph'
82
- ? 'bg-surface-elevated text-text-primary shadow-sm border border-border-default'
83
- : 'text-text-muted hover:text-text-secondary border border-transparent'
84
- }`}>
85
- <Network size={10} /> GRAPH
86
- </button>
87
- </motion.div>
88
- )}
89
- </AnimatePresence>
90
- </div>
91
- );
92
- });