flock-core 0.5.0b50__py3-none-any.whl → 0.5.0b52__py3-none-any.whl

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.

Potentially problematic release.


This version of flock-core might be problematic. Click here for more details.

Files changed (117) hide show
  1. flock/dashboard/launcher.py +1 -1
  2. flock/frontend/README.md +678 -0
  3. flock/frontend/docs/DESIGN_SYSTEM.md +1980 -0
  4. flock/frontend/index.html +12 -0
  5. flock/frontend/package-lock.json +4347 -0
  6. flock/frontend/package.json +48 -0
  7. flock/frontend/src/App.tsx +79 -0
  8. flock/frontend/src/__tests__/e2e/critical-scenarios.test.tsx +587 -0
  9. flock/frontend/src/__tests__/integration/filtering-e2e.test.tsx +387 -0
  10. flock/frontend/src/__tests__/integration/graph-rendering.test.tsx +640 -0
  11. flock/frontend/src/__tests__/integration/indexeddb-persistence.test.tsx +699 -0
  12. flock/frontend/src/components/common/BuildInfo.tsx +39 -0
  13. flock/frontend/src/components/common/EmptyState.module.css +115 -0
  14. flock/frontend/src/components/common/EmptyState.tsx +128 -0
  15. flock/frontend/src/components/common/ErrorBoundary.module.css +169 -0
  16. flock/frontend/src/components/common/ErrorBoundary.tsx +118 -0
  17. flock/frontend/src/components/common/KeyboardShortcutsDialog.css +251 -0
  18. flock/frontend/src/components/common/KeyboardShortcutsDialog.tsx +151 -0
  19. flock/frontend/src/components/common/LoadingSpinner.module.css +97 -0
  20. flock/frontend/src/components/common/LoadingSpinner.tsx +29 -0
  21. flock/frontend/src/components/controls/PublishControl.css +547 -0
  22. flock/frontend/src/components/controls/PublishControl.test.tsx +543 -0
  23. flock/frontend/src/components/controls/PublishControl.tsx +432 -0
  24. flock/frontend/src/components/details/DetailWindowContainer.tsx +62 -0
  25. flock/frontend/src/components/details/LiveOutputTab.test.tsx +792 -0
  26. flock/frontend/src/components/details/LiveOutputTab.tsx +220 -0
  27. flock/frontend/src/components/details/MessageHistoryTab.tsx +299 -0
  28. flock/frontend/src/components/details/NodeDetailWindow.test.tsx +501 -0
  29. flock/frontend/src/components/details/NodeDetailWindow.tsx +218 -0
  30. flock/frontend/src/components/details/RunStatusTab.tsx +307 -0
  31. flock/frontend/src/components/details/tabs.test.tsx +1015 -0
  32. flock/frontend/src/components/filters/CorrelationIDFilter.module.css +102 -0
  33. flock/frontend/src/components/filters/CorrelationIDFilter.test.tsx +197 -0
  34. flock/frontend/src/components/filters/CorrelationIDFilter.tsx +121 -0
  35. flock/frontend/src/components/filters/FilterBar.module.css +29 -0
  36. flock/frontend/src/components/filters/FilterBar.test.tsx +133 -0
  37. flock/frontend/src/components/filters/FilterBar.tsx +33 -0
  38. flock/frontend/src/components/filters/FilterPills.module.css +79 -0
  39. flock/frontend/src/components/filters/FilterPills.test.tsx +173 -0
  40. flock/frontend/src/components/filters/FilterPills.tsx +67 -0
  41. flock/frontend/src/components/filters/TimeRangeFilter.module.css +91 -0
  42. flock/frontend/src/components/filters/TimeRangeFilter.test.tsx +154 -0
  43. flock/frontend/src/components/filters/TimeRangeFilter.tsx +105 -0
  44. flock/frontend/src/components/graph/AgentNode.test.tsx +75 -0
  45. flock/frontend/src/components/graph/AgentNode.tsx +322 -0
  46. flock/frontend/src/components/graph/GraphCanvas.tsx +406 -0
  47. flock/frontend/src/components/graph/MessageFlowEdge.tsx +128 -0
  48. flock/frontend/src/components/graph/MessageNode.test.tsx +62 -0
  49. flock/frontend/src/components/graph/MessageNode.tsx +116 -0
  50. flock/frontend/src/components/graph/MiniMap.tsx +47 -0
  51. flock/frontend/src/components/graph/TransformEdge.tsx +123 -0
  52. flock/frontend/src/components/layout/DashboardLayout.css +407 -0
  53. flock/frontend/src/components/layout/DashboardLayout.tsx +300 -0
  54. flock/frontend/src/components/layout/Header.module.css +88 -0
  55. flock/frontend/src/components/layout/Header.tsx +52 -0
  56. flock/frontend/src/components/modules/EventLogModule.test.tsx +401 -0
  57. flock/frontend/src/components/modules/EventLogModule.tsx +396 -0
  58. flock/frontend/src/components/modules/EventLogModuleWrapper.tsx +17 -0
  59. flock/frontend/src/components/modules/ModuleRegistry.test.ts +333 -0
  60. flock/frontend/src/components/modules/ModuleRegistry.ts +85 -0
  61. flock/frontend/src/components/modules/ModuleWindow.tsx +155 -0
  62. flock/frontend/src/components/modules/registerModules.ts +20 -0
  63. flock/frontend/src/components/settings/AdvancedSettings.tsx +175 -0
  64. flock/frontend/src/components/settings/AppearanceSettings.tsx +185 -0
  65. flock/frontend/src/components/settings/GraphSettings.tsx +110 -0
  66. flock/frontend/src/components/settings/SettingsPanel.css +327 -0
  67. flock/frontend/src/components/settings/SettingsPanel.tsx +131 -0
  68. flock/frontend/src/components/settings/ThemeSelector.tsx +298 -0
  69. flock/frontend/src/hooks/useKeyboardShortcuts.ts +148 -0
  70. flock/frontend/src/hooks/useModulePersistence.test.ts +442 -0
  71. flock/frontend/src/hooks/useModulePersistence.ts +154 -0
  72. flock/frontend/src/hooks/useModules.ts +139 -0
  73. flock/frontend/src/hooks/usePersistence.ts +139 -0
  74. flock/frontend/src/main.tsx +13 -0
  75. flock/frontend/src/services/api.ts +213 -0
  76. flock/frontend/src/services/indexeddb.test.ts +793 -0
  77. flock/frontend/src/services/indexeddb.ts +794 -0
  78. flock/frontend/src/services/layout.test.ts +437 -0
  79. flock/frontend/src/services/layout.ts +146 -0
  80. flock/frontend/src/services/themeApplicator.ts +140 -0
  81. flock/frontend/src/services/themeService.ts +77 -0
  82. flock/frontend/src/services/websocket.test.ts +595 -0
  83. flock/frontend/src/services/websocket.ts +685 -0
  84. flock/frontend/src/store/filterStore.test.ts +242 -0
  85. flock/frontend/src/store/filterStore.ts +103 -0
  86. flock/frontend/src/store/graphStore.test.ts +186 -0
  87. flock/frontend/src/store/graphStore.ts +414 -0
  88. flock/frontend/src/store/moduleStore.test.ts +253 -0
  89. flock/frontend/src/store/moduleStore.ts +57 -0
  90. flock/frontend/src/store/settingsStore.ts +188 -0
  91. flock/frontend/src/store/streamStore.ts +68 -0
  92. flock/frontend/src/store/uiStore.test.ts +54 -0
  93. flock/frontend/src/store/uiStore.ts +110 -0
  94. flock/frontend/src/store/wsStore.ts +34 -0
  95. flock/frontend/src/styles/index.css +15 -0
  96. flock/frontend/src/styles/scrollbar.css +47 -0
  97. flock/frontend/src/styles/variables.css +488 -0
  98. flock/frontend/src/test/setup.ts +1 -0
  99. flock/frontend/src/types/filters.ts +14 -0
  100. flock/frontend/src/types/graph.ts +55 -0
  101. flock/frontend/src/types/modules.ts +7 -0
  102. flock/frontend/src/types/theme.ts +55 -0
  103. flock/frontend/src/utils/mockData.ts +85 -0
  104. flock/frontend/src/utils/performance.ts +16 -0
  105. flock/frontend/src/utils/transforms.test.ts +860 -0
  106. flock/frontend/src/utils/transforms.ts +323 -0
  107. flock/frontend/src/vite-env.d.ts +17 -0
  108. flock/frontend/tsconfig.json +27 -0
  109. flock/frontend/tsconfig.node.json +11 -0
  110. flock/frontend/vite.config.ts +25 -0
  111. flock/frontend/vitest.config.ts +11 -0
  112. flock/helper/cli_helper.py +1 -1
  113. {flock_core-0.5.0b50.dist-info → flock_core-0.5.0b52.dist-info}/METADATA +1 -1
  114. {flock_core-0.5.0b50.dist-info → flock_core-0.5.0b52.dist-info}/RECORD +117 -7
  115. {flock_core-0.5.0b50.dist-info → flock_core-0.5.0b52.dist-info}/WHEEL +0 -0
  116. {flock_core-0.5.0b50.dist-info → flock_core-0.5.0b52.dist-info}/entry_points.txt +0 -0
  117. {flock_core-0.5.0b50.dist-info → flock_core-0.5.0b52.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,57 @@
1
+ import { create } from 'zustand';
2
+ import { devtools } from 'zustand/middleware';
3
+ import type { ModuleInstance } from '../types/modules';
4
+
5
+ interface ModuleState {
6
+ // Module instances
7
+ instances: Map<string, ModuleInstance>;
8
+
9
+ // Actions
10
+ addModule: (module: ModuleInstance) => void;
11
+ updateModule: (id: string, updates: Partial<Omit<ModuleInstance, 'id' | 'type'>>) => void;
12
+ removeModule: (id: string) => void;
13
+ toggleVisibility: (id: string) => void;
14
+ }
15
+
16
+ export const useModuleStore = create<ModuleState>()(
17
+ devtools(
18
+ (set) => ({
19
+ instances: new Map(),
20
+
21
+ addModule: (module) =>
22
+ set((state) => {
23
+ const instances = new Map(state.instances);
24
+ instances.set(module.id, module);
25
+ return { instances };
26
+ }),
27
+
28
+ updateModule: (id, updates) =>
29
+ set((state) => {
30
+ const instances = new Map(state.instances);
31
+ const existing = instances.get(id);
32
+ if (existing) {
33
+ instances.set(id, { ...existing, ...updates });
34
+ }
35
+ return { instances };
36
+ }),
37
+
38
+ removeModule: (id) =>
39
+ set((state) => {
40
+ const instances = new Map(state.instances);
41
+ instances.delete(id);
42
+ return { instances };
43
+ }),
44
+
45
+ toggleVisibility: (id) =>
46
+ set((state) => {
47
+ const instances = new Map(state.instances);
48
+ const existing = instances.get(id);
49
+ if (existing) {
50
+ instances.set(id, { ...existing, visible: !existing.visible });
51
+ }
52
+ return { instances };
53
+ }),
54
+ }),
55
+ { name: 'moduleStore' }
56
+ )
57
+ );
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Settings Store
3
+ *
4
+ * Manages all user preferences and customization settings.
5
+ * Persisted to localStorage for cross-session persistence.
6
+ */
7
+
8
+ import { create } from 'zustand';
9
+ import { persist } from 'zustand/middleware';
10
+
11
+ export interface SettingsState {
12
+ version: string; // For future migrations
13
+
14
+ ui: {
15
+ showFilters: boolean;
16
+ showControls: boolean;
17
+ showSettings: boolean;
18
+ };
19
+
20
+ graph: {
21
+ edgeType: 'smoothstep' | 'bezier' | 'straight' | 'simplebezier';
22
+ edgeStrokeWidth: number;
23
+ edgeAnimation: boolean;
24
+ showEdgeLabels: boolean;
25
+ };
26
+
27
+ appearance: {
28
+ agentIdleColor: string;
29
+ agentActiveColor: string;
30
+ agentErrorColor: string;
31
+ nodeShadow: 'none' | 'small' | 'medium' | 'large';
32
+ showStatusPulse: boolean;
33
+ compactNodeView: boolean;
34
+ theme: string;
35
+ };
36
+
37
+ advanced: {
38
+ layoutDirection: 'TB' | 'LR';
39
+ nodeSpacing: number;
40
+ rankSpacing: number;
41
+ debugMode: boolean;
42
+ performanceMode: boolean;
43
+ };
44
+
45
+ // UI Actions
46
+ setShowFilters: (show: boolean) => void;
47
+ setShowControls: (show: boolean) => void;
48
+ setShowSettings: (show: boolean) => void;
49
+
50
+ // Graph Actions
51
+ setEdgeType: (edgeType: SettingsState['graph']['edgeType']) => void;
52
+ setEdgeStrokeWidth: (width: number) => void;
53
+ setEdgeAnimation: (enabled: boolean) => void;
54
+ setShowEdgeLabels: (show: boolean) => void;
55
+
56
+ // Appearance Actions
57
+ setTheme: (theme: string) => void;
58
+ setAgentIdleColor: (color: string) => void;
59
+ setAgentActiveColor: (color: string) => void;
60
+ setAgentErrorColor: (color: string) => void;
61
+ setNodeShadow: (shadow: SettingsState['appearance']['nodeShadow']) => void;
62
+ setShowStatusPulse: (show: boolean) => void;
63
+ setCompactNodeView: (compact: boolean) => void;
64
+
65
+ // Advanced Actions
66
+ setLayoutDirection: (direction: SettingsState['advanced']['layoutDirection']) => void;
67
+ setNodeSpacing: (spacing: number) => void;
68
+ setRankSpacing: (spacing: number) => void;
69
+ setDebugMode: (enabled: boolean) => void;
70
+ setPerformanceMode: (enabled: boolean) => void;
71
+
72
+ // Utility Actions
73
+ resetToDefaults: () => void;
74
+ }
75
+
76
+ const DEFAULT_SETTINGS: Omit<SettingsState, keyof ReturnType<typeof createActions>> = {
77
+ version: '1.0.0',
78
+
79
+ ui: {
80
+ showFilters: false, // Hidden by default
81
+ showControls: true, // Shown by default
82
+ showSettings: false, // Hidden by default
83
+ },
84
+
85
+ graph: {
86
+ edgeType: 'bezier',
87
+ edgeStrokeWidth: 3,
88
+ edgeAnimation: true,
89
+ showEdgeLabels: true,
90
+ },
91
+
92
+ appearance: {
93
+ agentIdleColor: '#60a5fa', // Blue
94
+ agentActiveColor: '#10b981', // Green
95
+ agentErrorColor: '#ef4444', // Red
96
+ nodeShadow: 'medium',
97
+ showStatusPulse: true,
98
+ compactNodeView: false,
99
+ theme: 'default',
100
+ },
101
+
102
+ advanced: {
103
+ layoutDirection: 'LR',
104
+ nodeSpacing: 75, // Horizontal spacing between nodes
105
+ rankSpacing: 150, // Vertical spacing between ranks
106
+ debugMode: false,
107
+ performanceMode: false,
108
+ },
109
+ };
110
+
111
+ const createActions = (set: any) => ({
112
+ // UI Actions
113
+ setShowFilters: (show: boolean) =>
114
+ set((state: SettingsState) => ({ ui: { ...state.ui, showFilters: show } })),
115
+
116
+ setShowControls: (show: boolean) =>
117
+ set((state: SettingsState) => ({ ui: { ...state.ui, showControls: show } })),
118
+
119
+ setShowSettings: (show: boolean) =>
120
+ set((state: SettingsState) => ({ ui: { ...state.ui, showSettings: show } })),
121
+
122
+ // Graph Actions
123
+ setEdgeType: (edgeType: SettingsState['graph']['edgeType']) =>
124
+ set((state: SettingsState) => ({ graph: { ...state.graph, edgeType } })),
125
+
126
+ setEdgeStrokeWidth: (width: number) =>
127
+ set((state: SettingsState) => ({ graph: { ...state.graph, edgeStrokeWidth: width } })),
128
+
129
+ setEdgeAnimation: (enabled: boolean) =>
130
+ set((state: SettingsState) => ({ graph: { ...state.graph, edgeAnimation: enabled } })),
131
+
132
+ setShowEdgeLabels: (show: boolean) =>
133
+ set((state: SettingsState) => ({ graph: { ...state.graph, showEdgeLabels: show } })),
134
+
135
+ // Appearance Actions
136
+ setTheme: (theme: string) =>
137
+ set((state: SettingsState) => ({ appearance: { ...state.appearance, theme } })),
138
+
139
+ setAgentIdleColor: (color: string) =>
140
+ set((state: SettingsState) => ({ appearance: { ...state.appearance, agentIdleColor: color } })),
141
+
142
+ setAgentActiveColor: (color: string) =>
143
+ set((state: SettingsState) => ({ appearance: { ...state.appearance, agentActiveColor: color } })),
144
+
145
+ setAgentErrorColor: (color: string) =>
146
+ set((state: SettingsState) => ({ appearance: { ...state.appearance, agentErrorColor: color } })),
147
+
148
+ setNodeShadow: (shadow: SettingsState['appearance']['nodeShadow']) =>
149
+ set((state: SettingsState) => ({ appearance: { ...state.appearance, nodeShadow: shadow } })),
150
+
151
+ setShowStatusPulse: (show: boolean) =>
152
+ set((state: SettingsState) => ({ appearance: { ...state.appearance, showStatusPulse: show } })),
153
+
154
+ setCompactNodeView: (compact: boolean) =>
155
+ set((state: SettingsState) => ({ appearance: { ...state.appearance, compactNodeView: compact } })),
156
+
157
+ // Advanced Actions
158
+ setLayoutDirection: (direction: SettingsState['advanced']['layoutDirection']) =>
159
+ set((state: SettingsState) => ({ advanced: { ...state.advanced, layoutDirection: direction } })),
160
+
161
+ setNodeSpacing: (spacing: number) =>
162
+ set((state: SettingsState) => ({ advanced: { ...state.advanced, nodeSpacing: spacing } })),
163
+
164
+ setRankSpacing: (spacing: number) =>
165
+ set((state: SettingsState) => ({ advanced: { ...state.advanced, rankSpacing: spacing } })),
166
+
167
+ setDebugMode: (enabled: boolean) =>
168
+ set((state: SettingsState) => ({ advanced: { ...state.advanced, debugMode: enabled } })),
169
+
170
+ setPerformanceMode: (enabled: boolean) =>
171
+ set((state: SettingsState) => ({ advanced: { ...state.advanced, performanceMode: enabled } })),
172
+
173
+ // Utility Actions
174
+ resetToDefaults: () => set(DEFAULT_SETTINGS),
175
+ });
176
+
177
+ export const useSettingsStore = create<SettingsState>()(
178
+ persist(
179
+ (set) => ({
180
+ ...DEFAULT_SETTINGS,
181
+ ...createActions(set),
182
+ }),
183
+ {
184
+ name: 'flock-flow-settings',
185
+ version: 1,
186
+ }
187
+ )
188
+ );
@@ -0,0 +1,68 @@
1
+ import { create } from 'zustand';
2
+ import { devtools } from 'zustand/middleware';
3
+
4
+ export interface StreamingOutputData {
5
+ agent_name: string;
6
+ run_id: string;
7
+ output_type: 'llm_token' | 'log' | 'stdout' | 'stderr';
8
+ content: string;
9
+ sequence: number;
10
+ is_final: boolean;
11
+ timestamp?: string;
12
+ }
13
+
14
+ interface StreamState {
15
+ // Map<agent_name, StreamingOutputData[]>
16
+ outputs: Map<string, StreamingOutputData[]>;
17
+
18
+ // Actions
19
+ addOutput: (agentName: string, output: StreamingOutputData) => void;
20
+ clearOutputs: (agentName: string) => void;
21
+ getOutputs: (agentName: string) => StreamingOutputData[];
22
+ }
23
+
24
+ export const useStreamStore = create<StreamState>()(
25
+ devtools(
26
+ (set, get) => ({
27
+ outputs: new Map(),
28
+
29
+ addOutput: (agentName, output) =>
30
+ set((state) => {
31
+ const outputs = new Map(state.outputs);
32
+ const existing = outputs.get(agentName) || [];
33
+
34
+ // Check if this exact event already exists (by sequence number and run_id)
35
+ // to prevent duplicates when loading history
36
+ // const isDuplicate = existing.some(
37
+ // (e) => e.run_id === output.run_id && e.sequence === output.sequence
38
+ // );
39
+
40
+ // if (isDuplicate) {
41
+ // return state; // Skip duplicate
42
+ // }
43
+
44
+ // Just append - display events exactly as they arrive from backend
45
+ const updated = [...existing, output];
46
+
47
+ // Keep only last 1000 outputs per agent to prevent memory issues
48
+ // const trimmed = updated.slice(-1000);
49
+
50
+ outputs.set(agentName, updated);
51
+ return { outputs };
52
+ }),
53
+
54
+ clearOutputs: (agentName) =>
55
+ set((state) => {
56
+ const outputs = new Map(state.outputs);
57
+ outputs.delete(agentName);
58
+ return { outputs };
59
+ }),
60
+
61
+ getOutputs: (agentName) => {
62
+ const outputs = get().outputs.get(agentName) || [];
63
+ return outputs;
64
+ },
65
+ }),
66
+ { name: 'streamStore' }
67
+ )
68
+ );
@@ -0,0 +1,54 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { useUIStore } from './uiStore';
3
+
4
+ describe('uiStore', () => {
5
+ beforeEach(() => {
6
+ // Reset store before each test
7
+ useUIStore.setState({
8
+ mode: 'agent',
9
+ selectedNodeIds: new Set(),
10
+ detailWindows: new Map(),
11
+ defaultTab: 'liveOutput',
12
+ });
13
+ });
14
+
15
+ it('should set mode to blackboard', () => {
16
+ useUIStore.getState().setMode('blackboard');
17
+ expect(useUIStore.getState().mode).toBe('blackboard');
18
+ });
19
+
20
+ it('should select and deselect nodes', () => {
21
+ useUIStore.getState().selectNode('node-1');
22
+ useUIStore.getState().selectNode('node-2');
23
+
24
+ let selectedIds = useUIStore.getState().selectedNodeIds;
25
+ expect(selectedIds.size).toBe(2);
26
+ expect(selectedIds.has('node-1')).toBe(true);
27
+ expect(selectedIds.has('node-2')).toBe(true);
28
+
29
+ useUIStore.getState().deselectNode('node-1');
30
+ selectedIds = useUIStore.getState().selectedNodeIds;
31
+ expect(selectedIds.size).toBe(1);
32
+ expect(selectedIds.has('node-1')).toBe(false);
33
+ });
34
+
35
+ it('should clear selection', () => {
36
+ useUIStore.getState().selectNode('node-1');
37
+ useUIStore.getState().selectNode('node-2');
38
+ useUIStore.getState().clearSelection();
39
+
40
+ expect(useUIStore.getState().selectedNodeIds.size).toBe(0);
41
+ });
42
+
43
+ it('should open and close detail windows', () => {
44
+ useUIStore.getState().openDetailWindow('node-1');
45
+
46
+ let windows = useUIStore.getState().detailWindows;
47
+ expect(windows.size).toBe(1);
48
+ expect(windows.get('node-1')?.nodeId).toBe('node-1');
49
+
50
+ useUIStore.getState().closeDetailWindow('node-1');
51
+ windows = useUIStore.getState().detailWindows;
52
+ expect(windows.size).toBe(0);
53
+ });
54
+ });
@@ -0,0 +1,110 @@
1
+ import { create } from 'zustand';
2
+ import { devtools, persist } from 'zustand/middleware';
3
+
4
+ export type VisualizationMode = 'agent' | 'blackboard';
5
+
6
+ interface DetailWindow {
7
+ nodeId: string;
8
+ position: { x: number; y: number };
9
+ size: { width: number; height: number };
10
+ activeTab: 'liveOutput' | 'messageHistory' | 'runStatus';
11
+ }
12
+
13
+ interface UIState {
14
+ // Visualization mode
15
+ mode: VisualizationMode;
16
+ setMode: (mode: VisualizationMode) => void;
17
+
18
+ // Node selection
19
+ selectedNodeIds: Set<string>;
20
+ selectNode: (nodeId: string) => void;
21
+ deselectNode: (nodeId: string) => void;
22
+ clearSelection: () => void;
23
+
24
+ // Detail windows
25
+ detailWindows: Map<string, DetailWindow>;
26
+ openDetailWindow: (nodeId: string) => void;
27
+ closeDetailWindow: (nodeId: string) => void;
28
+ updateDetailWindow: (nodeId: string, updates: Partial<DetailWindow>) => void;
29
+
30
+ // Layout preferences
31
+ layoutDirection: 'TB' | 'LR';
32
+ setLayoutDirection: (direction: 'TB' | 'LR') => void;
33
+ autoLayoutEnabled: boolean;
34
+ setAutoLayoutEnabled: (enabled: boolean) => void;
35
+
36
+ // Preferences
37
+ defaultTab: 'liveOutput' | 'messageHistory' | 'runStatus';
38
+ setDefaultTab: (tab: 'liveOutput' | 'messageHistory' | 'runStatus') => void;
39
+ }
40
+
41
+ export const useUIStore = create<UIState>()(
42
+ devtools(
43
+ persist(
44
+ (set) => ({
45
+ mode: 'agent',
46
+ setMode: (mode) => set({ mode }),
47
+
48
+ selectedNodeIds: new Set(),
49
+ selectNode: (nodeId) =>
50
+ set((state) => ({
51
+ selectedNodeIds: new Set(state.selectedNodeIds).add(nodeId),
52
+ })),
53
+ deselectNode: (nodeId) =>
54
+ set((state) => {
55
+ const ids = new Set(state.selectedNodeIds);
56
+ ids.delete(nodeId);
57
+ return { selectedNodeIds: ids };
58
+ }),
59
+ clearSelection: () => set({ selectedNodeIds: new Set() }),
60
+
61
+ detailWindows: new Map(),
62
+ openDetailWindow: (nodeId) =>
63
+ set((state) => {
64
+ const windows = new Map(state.detailWindows);
65
+ if (!windows.has(nodeId)) {
66
+ windows.set(nodeId, {
67
+ nodeId,
68
+ position: { x: 100 + windows.size * 20, y: 100 + windows.size * 20 },
69
+ size: { width: 600, height: 400 },
70
+ activeTab: state.defaultTab,
71
+ });
72
+ }
73
+ return { detailWindows: windows };
74
+ }),
75
+ closeDetailWindow: (nodeId) =>
76
+ set((state) => {
77
+ const windows = new Map(state.detailWindows);
78
+ windows.delete(nodeId);
79
+ return { detailWindows: windows };
80
+ }),
81
+ updateDetailWindow: (nodeId, updates) =>
82
+ set((state) => {
83
+ const windows = new Map(state.detailWindows);
84
+ const existing = windows.get(nodeId);
85
+ if (existing) {
86
+ windows.set(nodeId, { ...existing, ...updates });
87
+ }
88
+ return { detailWindows: windows };
89
+ }),
90
+
91
+ layoutDirection: 'TB',
92
+ setLayoutDirection: (direction) => set({ layoutDirection: direction }),
93
+ autoLayoutEnabled: true,
94
+ setAutoLayoutEnabled: (enabled) => set({ autoLayoutEnabled: enabled }),
95
+
96
+ defaultTab: 'liveOutput',
97
+ setDefaultTab: (tab) => set({ defaultTab: tab }),
98
+ }),
99
+ {
100
+ name: 'ui-storage',
101
+ partialize: (state) => ({
102
+ defaultTab: state.defaultTab,
103
+ layoutDirection: state.layoutDirection,
104
+ autoLayoutEnabled: state.autoLayoutEnabled,
105
+ }),
106
+ }
107
+ ),
108
+ { name: 'uiStore' }
109
+ )
110
+ );
@@ -0,0 +1,34 @@
1
+ import { create } from 'zustand';
2
+ import { devtools } from 'zustand/middleware';
3
+
4
+ export type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
5
+
6
+ interface WSState {
7
+ // Connection state
8
+ status: ConnectionStatus;
9
+ lastError: string | null;
10
+ reconnectAttempts: number;
11
+
12
+ // Actions
13
+ setStatus: (status: ConnectionStatus) => void;
14
+ setError: (error: string | null) => void;
15
+ incrementAttempts: () => void;
16
+ resetAttempts: () => void;
17
+ }
18
+
19
+ export const useWSStore = create<WSState>()(
20
+ devtools(
21
+ (set) => ({
22
+ status: 'disconnected',
23
+ lastError: null,
24
+ reconnectAttempts: 0,
25
+
26
+ setStatus: (status) => set({ status }),
27
+ setError: (error) => set({ lastError: error }),
28
+ incrementAttempts: () =>
29
+ set((state) => ({ reconnectAttempts: state.reconnectAttempts + 1 })),
30
+ resetAttempts: () => set({ reconnectAttempts: 0 }),
31
+ }),
32
+ { name: 'wsStore' }
33
+ )
34
+ );
@@ -0,0 +1,15 @@
1
+ /* Import design system variables */
2
+ @import './variables.css';
3
+
4
+ /* Import themed scrollbar styles */
5
+ @import './scrollbar.css';
6
+
7
+ /* Animations */
8
+ @keyframes pulse {
9
+ 0%, 100% {
10
+ opacity: 1;
11
+ }
12
+ 50% {
13
+ opacity: 0.5;
14
+ }
15
+ }
@@ -0,0 +1,47 @@
1
+ /* ========================================
2
+ Themed Scrollbars
3
+ ========================================
4
+ Global scrollbar styling that uses theme CSS variables
5
+ for consistent appearance across all scrollable areas
6
+ ======================================== */
7
+
8
+ /* Webkit browsers (Chrome, Safari, Edge) */
9
+ *::-webkit-scrollbar {
10
+ width: 10px;
11
+ height: 10px;
12
+ }
13
+
14
+ *::-webkit-scrollbar-track {
15
+ background: var(--color-bg-base);
16
+ border-radius: var(--radius-sm, 4px);
17
+ }
18
+
19
+ *::-webkit-scrollbar-thumb {
20
+ background: var(--color-bg-overlay);
21
+ border-radius: var(--radius-sm, 4px);
22
+ border: 2px solid var(--color-bg-base);
23
+ }
24
+
25
+ *::-webkit-scrollbar-thumb:hover {
26
+ background: var(--color-border-default);
27
+ }
28
+
29
+ *::-webkit-scrollbar-thumb:active {
30
+ background: var(--color-border-strong);
31
+ }
32
+
33
+ /* Firefox */
34
+ * {
35
+ scrollbar-width: thin;
36
+ scrollbar-color: var(--color-bg-overlay) var(--color-bg-base);
37
+ }
38
+
39
+ /* Ensure scrollbars are visible in detail windows and other components */
40
+ div[style*="overflow: auto"],
41
+ div[style*="overflow-y: auto"],
42
+ div[style*="overflow-x: auto"],
43
+ .settings-content,
44
+ .publish-content {
45
+ scrollbar-width: thin;
46
+ scrollbar-color: var(--color-bg-overlay) var(--color-bg-base);
47
+ }