@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,86 +0,0 @@
1
- import React, { useMemo } from 'react';
2
- import { AlertTriangle, XCircle, Info, CheckCircle, Circle } from 'lucide-react';
3
- import { usePlaygroundStore } from '../../store/usePlaygroundStore';
4
- import { useTimelineStore } from '@decido/engine';
5
- import { TRACK_CONFIG } from './debugConfig';
6
-
7
- export function FlowHealthTab() {
8
- const prototypeBrand = usePlaygroundStore(s => s.prototypeBrand);
9
- const timelines = useTimelineStore(s => s.timelines);
10
- const flow = timelines[prototypeBrand];
11
-
12
- const analysis = useMemo(() => {
13
- if (!flow) return null;
14
- const kfs = flow.keyframes || [];
15
- const edges = flow.edges || [];
16
- const nodeIds = new Set(kfs.map((k: any) => k.id));
17
- const trackCounts: Record<string, number> = {};
18
- kfs.forEach((k: any) => { const t = k.track || 'unknown'; trackCounts[t] = (trackCounts[t] || 0) + 1; });
19
- const hasIncoming = new Set(edges.map((e: any) => e.target));
20
- const hasOutgoing = new Set(edges.map((e: any) => e.source));
21
- const orphans = kfs.filter((k: any) => { if (k.track === 'trigger' || k.track === 'entry') return false; return !hasIncoming.has(k.id) && edges.length > 0; });
22
- const terminals = kfs.filter((k: any) => hasIncoming.has(k.id) && !hasOutgoing.has(k.id) && k.track !== 'return');
23
- const deadEdges = edges.filter((e: any) => !nodeIds.has(e.target) || !nodeIds.has(e.source));
24
- const unlabeled = kfs.filter((k: any) => !k.label && !k.state && k.track !== 'trigger');
25
- const issues = orphans.length + deadEdges.length + unlabeled.length;
26
- const score = Math.max(0, Math.min(100, 100 - issues * 10));
27
- return { kfs, edges, trackCounts, orphans, terminals, deadEdges, unlabeled, score, issues };
28
- }, [flow]);
29
-
30
- if (!flow || !analysis) return <div className="flex items-center justify-center h-full text-text-muted text-xs font-mono">Sin blueprint activo</div>;
31
-
32
- const scoreColor = analysis.score >= 80 ? 'text-green-400' : analysis.score >= 50 ? 'text-amber-400' : 'text-red-400';
33
- const scoreBg = analysis.score >= 80 ? 'bg-green-500/10' : analysis.score >= 50 ? 'bg-amber-500/10' : 'bg-red-500/10';
34
-
35
- return (
36
- <div className="h-full overflow-y-auto custom-scrollbar p-4 space-y-4">
37
- <div className={`flex items-center gap-4 p-4 rounded-xl ${scoreBg} border border-border-subtle`}>
38
- <div className={`text-4xl font-black ${scoreColor} font-mono`}>{analysis.score}</div>
39
- <div>
40
- <div className={`text-sm font-bold ${scoreColor}`}>{analysis.score >= 80 ? '✅ Saludable' : analysis.score >= 50 ? '⚠️ Revisar' : '🔴 Problemas detectados'}</div>
41
- <div className="text-[11px] text-text-muted">{analysis.kfs.length} nodos · {analysis.edges.length} conexiones · {analysis.issues} problemas</div>
42
- </div>
43
- </div>
44
- <div className="space-y-2">
45
- <div className="text-[11px] font-bold text-text-secondary uppercase tracking-widest">Distribución por Track</div>
46
- <div className="grid grid-cols-2 lg:grid-cols-3 gap-2">
47
- {Object.entries(analysis.trackCounts).map(([track, count]) => {
48
- const cfg = TRACK_CONFIG[track] || { icon: Circle, color: 'text-text-secondary', label: track };
49
- const Icon = cfg.icon;
50
- return (
51
- <div key={track} className="flex items-center gap-2 px-3 py-2 rounded-lg bg-surface-glass border border-border-subtle">
52
- <Icon size={14} className={cfg.color} />
53
- <span className="text-[11px] text-text-primary font-semibold">{cfg.label}</span>
54
- <span className="ml-auto text-[11px] font-mono text-text-muted">{count}</span>
55
- </div>
56
- );
57
- })}
58
- </div>
59
- </div>
60
- {analysis.orphans.length > 0 && (
61
- <div className="space-y-1">
62
- <div className="text-[11px] font-bold text-amber-400 flex items-center gap-1"><AlertTriangle size={12} /> Nodos Huérfanos ({analysis.orphans.length})</div>
63
- {analysis.orphans.map((k: any) => <div key={k.id} className="pl-5 text-[11px] text-text-muted font-mono">· {k.label || k.id} <span className="text-text-muted">({k.track})</span></div>)}
64
- </div>
65
- )}
66
- {analysis.deadEdges.length > 0 && (
67
- <div className="space-y-1">
68
- <div className="text-[11px] font-bold text-red-400 flex items-center gap-1"><XCircle size={12} /> Conexiones Rotas ({analysis.deadEdges.length})</div>
69
- {analysis.deadEdges.map((e: any, i: number) => <div key={i} className="pl-5 text-[11px] text-text-muted font-mono">· {e.source} → {e.target}</div>)}
70
- </div>
71
- )}
72
- {analysis.unlabeled.length > 0 && (
73
- <div className="space-y-1">
74
- <div className="text-[11px] font-bold text-text-muted flex items-center gap-1"><Info size={12} /> Sin Etiqueta ({analysis.unlabeled.length})</div>
75
- {analysis.unlabeled.slice(0, 5).map((k: any) => <div key={k.id} className="pl-5 text-[11px] text-text-muted font-mono">· {k.id} <span className="text-text-muted">({k.track})</span></div>)}
76
- </div>
77
- )}
78
- {analysis.terminals.length > 0 && (
79
- <div className="space-y-1">
80
- <div className="text-[11px] font-bold text-text-secondary flex items-center gap-1"><CheckCircle size={12} /> Nodos Terminales ({analysis.terminals.length})</div>
81
- {analysis.terminals.map((k: any) => <div key={k.id} className="pl-5 text-[11px] text-text-muted font-mono">· {k.label || k.id} <span className="text-text-muted">({k.track})</span></div>)}
82
- </div>
83
- )}
84
- </div>
85
- );
86
- }
@@ -1,110 +0,0 @@
1
- import React, { useEffect, useRef, useMemo, useState } from 'react';
2
- import { AnimatePresence, motion } from 'motion/react';
3
- import { Filter, Trash2, Copy, Check } from 'lucide-react';
4
- import { useDebugPanelStore, LogLevel } from '../../store/useDebugPanelStore';
5
- import { LEVEL_CONFIG, LogRow } from './debugConfig';
6
-
7
- export function LogsTab() {
8
- const logEntries = useDebugPanelStore(s => s.logEntries);
9
- const logFilters = useDebugPanelStore(s => s.logFilters);
10
- const allCategories = useDebugPanelStore(s => s.allCategories);
11
- const clearLogs = useDebugPanelStore(s => s.clearLogs);
12
- const toggleLevel = useDebugPanelStore(s => s.toggleLevel);
13
- const toggleCategory = useDebugPanelStore(s => s.toggleCategory);
14
- const [showFilters, setShowFilters] = useState(false);
15
- const [copied, setCopied] = useState(false);
16
- const scrollRef = useRef<HTMLDivElement>(null);
17
-
18
- useEffect(() => {
19
- if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
20
- }, [logEntries.length]);
21
-
22
- const filteredEntries = useMemo(() => {
23
- return logEntries.filter(e => {
24
- if (!logFilters.levels.has(e.level)) return false;
25
- if (logFilters.categories.size > 0 && !e.categories.some(c => logFilters.categories.has(c))) return false;
26
- return true;
27
- });
28
- }, [logEntries, logFilters]);
29
-
30
- const catArray = useMemo(() => Array.from(allCategories).sort(), [allCategories]);
31
-
32
- return (
33
- <div className="flex flex-col h-full">
34
- <div className="flex items-center gap-2 px-3 py-1.5 border-b border-border-subtle shrink-0 bg-surface-primary/50">
35
- <div className="flex gap-1">
36
- {(Object.keys(LEVEL_CONFIG) as LogLevel[]).map(lvl => {
37
- const cfg = LEVEL_CONFIG[lvl];
38
- const active = logFilters.levels.has(lvl);
39
- const count = logEntries.filter(e => e.level === lvl).length;
40
- return (
41
- <button
42
- key={lvl}
43
- onClick={() => toggleLevel(lvl)}
44
- className={`flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-bold transition-all border ${
45
- active ? `${cfg.bg} ${cfg.color} border-current/20` : 'bg-transparent text-text-muted border-transparent hover:text-text-secondary'
46
- }`}
47
- >
48
- {cfg.label}
49
- {count > 0 && <span className="text-[9px] opacity-60">{count}</span>}
50
- </button>
51
- );
52
- })}
53
- </div>
54
- <div className="flex-1" />
55
- {catArray.length > 0 && (
56
- <button
57
- onClick={() => setShowFilters(!showFilters)}
58
- className={`p-1 rounded transition-colors ${showFilters ? 'bg-cyan-500/20 text-cyan-400' : 'text-text-muted hover:text-text-primary hover:bg-surface-glass'}`}
59
- title="Filtrar categorías"
60
- >
61
- <Filter size={12} />
62
- </button>
63
- )}
64
- <span className="text-[10px] text-text-muted font-mono">{filteredEntries.length}/{logEntries.length}</span>
65
- <button
66
- onClick={() => {
67
- const text = filteredEntries.map(e => {
68
- const cats = e.categories.length > 0 ? ` [${e.categories.join(',')}]` : '';
69
- const data = e.data !== undefined ? ` | ${typeof e.data === 'object' ? JSON.stringify(e.data) : String(e.data)}` : '';
70
- return `[${e.timestamp}] ${LEVEL_CONFIG[e.level].label}${cats} ${e.message}${data}`;
71
- }).join('\n');
72
- navigator.clipboard.writeText(text).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1500); });
73
- }}
74
- className={`p-1 rounded transition-colors ${copied ? 'text-green-400 bg-green-500/10' : 'text-text-muted hover:text-cyan-400 hover:bg-cyan-500/10'}`}
75
- title="Copiar logs visibles"
76
- >
77
- {copied ? <Check size={12} /> : <Copy size={12} />}
78
- </button>
79
- <button onClick={clearLogs} className="p-1 rounded text-text-muted hover:text-red-400 hover:bg-red-500/10 transition-colors" title="Limpiar logs">
80
- <Trash2 size={12} />
81
- </button>
82
- </div>
83
-
84
- <AnimatePresence>
85
- {showFilters && catArray.length > 0 && (
86
- <motion.div initial={{ height: 0, opacity: 0 }} animate={{ height: 'auto', opacity: 1 }} exit={{ height: 0, opacity: 0 }} className="overflow-hidden border-b border-border-subtle">
87
- <div className="flex flex-wrap gap-1 px-3 py-2 bg-surface-primary/80">
88
- {catArray.map(cat => {
89
- const active = logFilters.categories.size === 0 || logFilters.categories.has(cat);
90
- return (
91
- <button key={cat} onClick={() => toggleCategory(cat)} className={`px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider transition-all border ${active ? 'bg-cyan-500/10 text-cyan-400 border-cyan-500/20' : 'bg-transparent text-text-muted border-transparent hover:text-text-secondary'}`}>
92
- {cat}
93
- </button>
94
- );
95
- })}
96
- </div>
97
- </motion.div>
98
- )}
99
- </AnimatePresence>
100
-
101
- <div ref={scrollRef} className="flex-1 overflow-y-auto custom-scrollbar">
102
- {filteredEntries.length === 0 ? (
103
- <div className="flex items-center justify-center h-full text-text-muted text-xs font-mono">Sin entradas de log</div>
104
- ) : (
105
- filteredEntries.map(entry => <LogRow key={entry.id} entry={entry} />)
106
- )}
107
- </div>
108
- </div>
109
- );
110
- }
@@ -1,241 +0,0 @@
1
- import React, { useMemo, useEffect, useState, useRef } from 'react';
2
- import { useMorphologyStore } from '../../store/useMorphologyStore';
3
- import { useLayoutStore } from '../../store/useLayoutStore';
4
- import { Layers, AlertTriangle, ArrowDown, ArrowUp, Clock, RotateCcw } from 'lucide-react';
5
-
6
- interface MorphEvent {
7
- id: number;
8
- timestamp: string;
9
- action: string;
10
- stageType: string | null;
11
- label: string;
12
- data?: any;
13
- level: 'info' | 'warn' | 'error';
14
- }
15
-
16
- let _eventId = 0;
17
- const MAX_EVENTS = 100;
18
-
19
- const STAGE_ICONS: Record<string, string> = {
20
- '2d': '📊',
21
- '3d': '🧊',
22
- workbench: '🔧',
23
- liquid: '💧',
24
- artifact: '📄',
25
- custom: '⚙️',
26
- };
27
-
28
- const STAGE_COLORS: Record<string, string> = {
29
- '2d': 'border-cyan-500 bg-cyan-500/10',
30
- '3d': 'border-violet-500 bg-violet-500/10',
31
- workbench: 'border-amber-500 bg-amber-500/10',
32
- liquid: 'border-blue-500 bg-blue-500/10',
33
- artifact: 'border-emerald-500 bg-emerald-500/10',
34
- custom: 'border-border-default bg-surface-glass',
35
- };
36
-
37
- function getTimestamp(): string {
38
- const now = new Date();
39
- return `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
40
- }
41
-
42
- export function MorphStackTab() {
43
- const morphActiveStage = useMorphologyStore(s => s.activeStage);
44
- const morphHistory = useMorphologyStore(s => s.stageHistory);
45
- const layoutActiveStage = useLayoutStore(s => s.activeStage);
46
- const layoutHistory = useLayoutStore(s => s.stageHistory);
47
-
48
- const [events, setEvents] = useState<MorphEvent[]>([]);
49
- const eventLogRef = useRef<HTMLDivElement>(null);
50
-
51
- // Detect desync between morphology and layout stores
52
- const isDesync = useMemo(() => {
53
- const morphType = morphActiveStage?.type || null;
54
- const layoutType = layoutActiveStage?.type || null;
55
- return morphType !== layoutType;
56
- }, [morphActiveStage, layoutActiveStage]);
57
-
58
- // Stack depth warning
59
- const stackDepth = morphHistory.length;
60
- const isDeepStack = stackDepth > 5;
61
-
62
- // Subscribe to morphology store changes to build event log
63
- useEffect(() => {
64
- let prevStage = useMorphologyStore.getState().activeStage;
65
- let prevDepth = useMorphologyStore.getState().stageHistory.length;
66
-
67
- const unsub = useMorphologyStore.subscribe((state) => {
68
- const newStage = state.activeStage;
69
- const newDepth = state.stageHistory.length;
70
-
71
- if (newStage !== prevStage || newDepth !== prevDepth) {
72
- let action = 'change';
73
- let level: MorphEvent['level'] = 'info';
74
-
75
- if (newDepth > prevDepth) {
76
- action = '⬇️ pushStage';
77
- } else if (newDepth < prevDepth && newStage) {
78
- action = '⬆️ popStage';
79
- } else if (!newStage && prevStage) {
80
- action = '🗑️ clearStages';
81
- level = 'warn';
82
- } else if (newStage && !prevStage) {
83
- action = '📌 setStage';
84
- } else {
85
- action = '🔄 setStage';
86
- }
87
-
88
- setEvents(prev => {
89
- const updated = [...prev, {
90
- id: ++_eventId,
91
- timestamp: getTimestamp(),
92
- action,
93
- stageType: newStage?.type || null,
94
- label: newStage?.label || newStage?.type || 'null',
95
- data: { depth: newDepth, sourceNodeId: newStage?.sourceNodeId },
96
- level,
97
- }];
98
- return updated.length > MAX_EVENTS ? updated.slice(-MAX_EVENTS) : updated;
99
- });
100
-
101
- prevStage = newStage;
102
- prevDepth = newDepth;
103
- }
104
- });
105
- return unsub;
106
- }, []);
107
-
108
- // Auto-scroll event log
109
- useEffect(() => {
110
- if (eventLogRef.current) {
111
- eventLogRef.current.scrollTop = eventLogRef.current.scrollHeight;
112
- }
113
- }, [events]);
114
-
115
- return (
116
- <div className="flex h-full text-[10px]">
117
- {/* Left: Stack Visualization */}
118
- <div className="w-48 shrink-0 border-r border-border-subtle p-3 space-y-2 overflow-y-auto">
119
- <div className="text-[9px] font-bold text-text-muted uppercase tracking-widest mb-2 flex items-center gap-1">
120
- <Layers size={10} /> Stack ({stackDepth + (morphActiveStage ? 1 : 0)})
121
- </div>
122
-
123
- {/* Warnings */}
124
- {isDesync && (
125
- <div className="px-2 py-1.5 rounded-lg bg-red-500/10 border border-red-500/30 text-red-400 text-[9px] font-bold flex items-center gap-1 animate-pulse">
126
- <AlertTriangle size={10} /> Stores desincronizados
127
- </div>
128
- )}
129
- {isDeepStack && (
130
- <div className="px-2 py-1.5 rounded-lg bg-amber-500/10 border border-amber-500/30 text-amber-400 text-[9px] font-bold flex items-center gap-1">
131
- <AlertTriangle size={10} /> Stack depth: {stackDepth} (posible leak)
132
- </div>
133
- )}
134
-
135
- {/* Active Stage */}
136
- {morphActiveStage ? (
137
- <div className={`px-3 py-2 rounded-xl border-2 ${STAGE_COLORS[morphActiveStage.type] || 'border-border-default bg-surface-glass'} relative`}>
138
- <div className="absolute -top-2 right-2 px-1.5 py-0.5 rounded text-[7px] font-bold bg-cyan-500 text-text-inverse uppercase">Activo</div>
139
- <div className="flex items-center gap-1.5">
140
- <span className="text-lg">{STAGE_ICONS[morphActiveStage.type] || '❓'}</span>
141
- <div>
142
- <div className="font-bold text-text-primary text-[11px]">{morphActiveStage.type.toUpperCase()}</div>
143
- {morphActiveStage.label && <div className="text-text-secondary text-[9px]">{morphActiveStage.label}</div>}
144
- </div>
145
- </div>
146
- {morphActiveStage.sourceNodeId && (
147
- <div className="mt-1 text-[8px] text-text-muted font-mono truncate">src: {morphActiveStage.sourceNodeId}</div>
148
- )}
149
- </div>
150
- ) : (
151
- <div className="px-3 py-2 rounded-xl border border-dashed border-border-default text-text-muted text-center italic">
152
- Sin stage activo
153
- </div>
154
- )}
155
-
156
- {/* Arrow */}
157
- {morphHistory.length > 0 && (
158
- <div className="flex justify-center text-text-muted"><ArrowUp size={12} /></div>
159
- )}
160
-
161
- {/* History Stack (reversed: most recent on top) */}
162
- {[...morphHistory].reverse().map((stage, i) => (
163
- <div
164
- key={i}
165
- className={`px-3 py-1.5 rounded-lg border ${STAGE_COLORS[stage.type] || 'border-border-default bg-surface-tertiary/50'} opacity-50 hover:opacity-80 cursor-pointer transition-all`}
166
- onClick={() => {
167
- // Pop back to this level
168
- const popsNeeded = i + 1;
169
- const ms = useMorphologyStore.getState();
170
- const ls = useLayoutStore.getState();
171
- for (let j = 0; j < popsNeeded; j++) { ms.popStage(); ls.popStage(); }
172
- }}
173
- title={`Click para volver a: ${stage.label || stage.type}`}
174
- >
175
- <div className="flex items-center gap-1.5">
176
- <span>{STAGE_ICONS[stage.type] || '❓'}</span>
177
- <span className="font-semibold text-text-primary">{stage.type}</span>
178
- {stage.label && <span className="text-text-muted truncate max-w-[80px]"> · {stage.label}</span>}
179
- </div>
180
- </div>
181
- ))}
182
- </div>
183
-
184
- {/* Right: Event Log */}
185
- <div className="flex-1 flex flex-col">
186
- <div className="px-3 py-1.5 border-b border-border-subtle flex items-center justify-between">
187
- <div className="text-[9px] font-bold text-text-muted uppercase tracking-widest flex items-center gap-1">
188
- <Clock size={10} /> Eventos ({events.length})
189
- </div>
190
- <button onClick={() => setEvents([])} className="p-0.5 rounded hover:bg-surface-glass text-text-muted hover:text-text-secondary" title="Limpiar eventos">
191
- <RotateCcw size={10} />
192
- </button>
193
- </div>
194
- <div ref={eventLogRef} className="flex-1 overflow-y-auto custom-scrollbar">
195
- {events.length === 0 ? (
196
- <div className="flex items-center justify-center h-full text-text-muted italic">
197
- Esperando eventos morph...
198
- </div>
199
- ) : (
200
- events.map((evt) => (
201
- <div
202
- key={evt.id}
203
- className={`flex items-start gap-2 px-3 py-1 border-b border-border-subtle hover:bg-surface-glass ${
204
- evt.level === 'warn' ? 'bg-amber-500/5' : evt.level === 'error' ? 'bg-red-500/5' : ''
205
- }`}
206
- >
207
- <span className="text-text-muted shrink-0 font-mono w-[50px]">{evt.timestamp}</span>
208
- <span className="shrink-0 w-[90px] font-semibold text-text-secondary">{evt.action}</span>
209
- <span className={`font-mono ${evt.stageType ? 'text-cyan-400' : 'text-text-muted'}`}>
210
- {evt.label}
211
- </span>
212
- {evt.data?.depth !== undefined && (
213
- <span className="text-text-muted ml-auto shrink-0">depth: {evt.data.depth}</span>
214
- )}
215
- </div>
216
- ))
217
- )}
218
- </div>
219
-
220
- {/* Desync Detail */}
221
- {isDesync && (
222
- <div className="px-3 py-2 border-t border-red-500/20 bg-red-500/5">
223
- <div className="text-[9px] font-bold text-red-400 mb-1">⚠️ Store Desync Detectado</div>
224
- <div className="grid grid-cols-2 gap-2 text-[9px] font-mono">
225
- <div>
226
- <span className="text-text-muted">morph:</span>{' '}
227
- <span className="text-violet-400">{morphActiveStage?.type || 'null'}</span>
228
- <span className="text-text-muted"> (depth: {morphHistory.length})</span>
229
- </div>
230
- <div>
231
- <span className="text-text-muted">layout:</span>{' '}
232
- <span className="text-emerald-400">{layoutActiveStage?.type || 'null'}</span>
233
- <span className="text-text-muted"> (depth: {layoutHistory.length})</span>
234
- </div>
235
- </div>
236
- </div>
237
- )}
238
- </div>
239
- </div>
240
- );
241
- }
@@ -1,173 +0,0 @@
1
- import React, { useState, useSyncExternalStore, useCallback, useMemo } from 'react';
2
- import { Globe, ChevronDown, ChevronRight, RotateCcw, Filter } from 'lucide-react';
3
- import {
4
- getNetworkEntries, clearNetworkEntries, subscribeNetwork, installNetworkInterceptor,
5
- type NetworkEntry,
6
- } from '../../lib/networkInterceptor';
7
-
8
- // Install interceptor on import
9
- installNetworkInterceptor();
10
-
11
- const STATUS_COLORS: Record<string, string> = {
12
- '2': 'text-emerald-400',
13
- '3': 'text-amber-400',
14
- '4': 'text-red-400',
15
- '5': 'text-red-500',
16
- '0': 'text-text-muted',
17
- };
18
-
19
- function getStatusColor(status: number): string {
20
- return STATUS_COLORS[String(status).charAt(0)] || 'text-text-secondary';
21
- }
22
-
23
- function truncateUrl(url: string): string {
24
- try {
25
- const u = new URL(url);
26
- const path = u.pathname + u.search;
27
- return path.length > 60 ? path.slice(0, 57) + '...' : path;
28
- } catch {
29
- return url.length > 60 ? url.slice(0, 57) + '...' : url;
30
- }
31
- }
32
-
33
- function getDomain(url: string): string {
34
- try { return new URL(url).hostname; } catch { return ''; }
35
- }
36
-
37
- function formatSize(bytes: number): string {
38
- if (bytes < 1024) return `${bytes}B`;
39
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
40
- return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
41
- }
42
-
43
- function NetworkRow({ entry, isExpanded, onToggle }: { entry: NetworkEntry; isExpanded: boolean; onToggle: () => void }) {
44
- return (
45
- <>
46
- <div
47
- onClick={onToggle}
48
- className={`flex items-center gap-2 px-3 py-1 text-[10px] font-mono cursor-pointer hover:bg-surface-glass border-b border-border-subtle ${
49
- entry.error ? 'bg-red-500/5' : ''
50
- }`}
51
- >
52
- {isExpanded ? <ChevronDown size={10} className="text-text-muted" /> : <ChevronRight size={10} className="text-text-muted" />}
53
- <span className="text-text-muted w-[50px] shrink-0">{entry.timestamp}</span>
54
- <span className={`w-[40px] shrink-0 font-bold ${entry.method === 'POST' ? 'text-amber-400' : 'text-cyan-400'}`}>{entry.method}</span>
55
- <span className={`w-[30px] shrink-0 font-bold ${getStatusColor(entry.status)}`}>{entry.status || 'ERR'}</span>
56
- <span className="flex-1 text-text-primary truncate" title={entry.url}>{truncateUrl(entry.url)}</span>
57
- <span className="text-text-muted w-[50px] shrink-0 text-right">{entry.duration}ms</span>
58
- <span className="text-text-muted w-[40px] shrink-0 text-right">{formatSize(entry.size)}</span>
59
- </div>
60
- {isExpanded && (
61
- <div className="px-6 py-2 bg-surface-primary/80 border-b border-border-subtle text-[9px] font-mono space-y-2">
62
- <div><span className="text-text-muted">URL:</span> <span className="text-text-primary break-all">{entry.url}</span></div>
63
- {entry.error && <div className="text-red-400">Error: {entry.error}</div>}
64
- {entry.requestBody && (
65
- <div>
66
- <div className="text-text-muted mb-0.5">Request Body:</div>
67
- <pre className="bg-surface-secondary rounded p-2 text-text-secondary overflow-x-auto max-h-[150px] overflow-y-auto whitespace-pre-wrap">{
68
- (() => { try { return JSON.stringify(JSON.parse(entry.requestBody), null, 2); } catch { return entry.requestBody; } })()
69
- }</pre>
70
- </div>
71
- )}
72
- {entry.responseBody && (
73
- <div>
74
- <div className="text-text-muted mb-0.5">Response Body:</div>
75
- <pre className="bg-surface-secondary rounded p-2 text-text-secondary overflow-x-auto max-h-[150px] overflow-y-auto whitespace-pre-wrap">{
76
- (() => { try { return JSON.stringify(JSON.parse(entry.responseBody), null, 2); } catch { return entry.responseBody; } })()
77
- }</pre>
78
- </div>
79
- )}
80
- </div>
81
- )}
82
- </>
83
- );
84
- }
85
-
86
- export function NetworkTab() {
87
- const entries = useSyncExternalStore(
88
- useCallback((cb) => subscribeNetwork(cb), []),
89
- useCallback(() => getNetworkEntries(), [])
90
- );
91
-
92
- const [expandedIds, setExpandedIds] = useState<Set<number>>(new Set());
93
- const [domainFilter, setDomainFilter] = useState<string>('');
94
-
95
- // Unique domains for filter
96
- const domains = useMemo(() => {
97
- const set = new Set<string>();
98
- entries.forEach(e => { const d = getDomain(e.url); if (d) set.add(d); });
99
- return Array.from(set).sort();
100
- }, [entries]);
101
-
102
- const filtered = useMemo(() => {
103
- if (!domainFilter) return entries;
104
- return entries.filter(e => getDomain(e.url).includes(domainFilter));
105
- }, [entries, domainFilter]);
106
-
107
- const toggleExpand = useCallback((id: number) => {
108
- setExpandedIds(prev => {
109
- const next = new Set(prev);
110
- if (next.has(id)) next.delete(id); else next.add(id);
111
- return next;
112
- });
113
- }, []);
114
-
115
- const totalErrors = useMemo(() => entries.filter(e => e.status >= 400 || e.error).length, [entries]);
116
-
117
- return (
118
- <div className="flex flex-col h-full">
119
- {/* Toolbar */}
120
- <div className="flex items-center gap-2 px-3 py-1.5 border-b border-border-subtle shrink-0">
121
- <Globe size={12} className="text-cyan-400" />
122
- <span className="text-[10px] font-bold text-text-secondary uppercase tracking-widest">Network</span>
123
- <span className="text-[9px] text-text-muted font-mono">{filtered.length}/{entries.length}</span>
124
- {totalErrors > 0 && (
125
- <span className="px-1.5 py-0.5 rounded-full text-[8px] font-bold bg-red-500/20 text-red-400">{totalErrors} err</span>
126
- )}
127
- <div className="flex-1" />
128
- {domains.length > 0 && (
129
- <select
130
- value={domainFilter}
131
- onChange={e => setDomainFilter(e.target.value)}
132
- className="bg-surface-secondary border border-border-default rounded px-1.5 py-0.5 text-[9px] text-text-secondary outline-hidden"
133
- >
134
- <option value="">All domains</option>
135
- {domains.map(d => <option key={d} value={d}>{d}</option>)}
136
- </select>
137
- )}
138
- <button onClick={clearNetworkEntries} className="p-0.5 rounded hover:bg-surface-glass text-text-muted hover:text-text-secondary" title="Limpiar">
139
- <RotateCcw size={10} />
140
- </button>
141
- </div>
142
-
143
- {/* Header Row */}
144
- <div className="flex items-center gap-2 px-3 py-0.5 text-[8px] font-bold text-text-muted uppercase tracking-widest border-b border-border-subtle shrink-0">
145
- <span className="w-[14px]" />
146
- <span className="w-[50px]">Time</span>
147
- <span className="w-[40px]">Method</span>
148
- <span className="w-[30px]">Status</span>
149
- <span className="flex-1">URL</span>
150
- <span className="w-[50px] text-right">Duration</span>
151
- <span className="w-[40px] text-right">Size</span>
152
- </div>
153
-
154
- {/* Entries */}
155
- <div className="flex-1 overflow-y-auto custom-scrollbar">
156
- {filtered.length === 0 ? (
157
- <div className="flex items-center justify-center h-full text-text-muted text-[10px] italic">
158
- Sin peticiones de red capturadas
159
- </div>
160
- ) : (
161
- filtered.map(entry => (
162
- <NetworkRow
163
- key={entry.id}
164
- entry={entry}
165
- isExpanded={expandedIds.has(entry.id)}
166
- onToggle={() => toggleExpand(entry.id)}
167
- />
168
- ))
169
- )}
170
- </div>
171
- </div>
172
- );
173
- }