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,322 @@
1
+ import { memo, useState, useEffect, useRef } from 'react';
2
+ import { NodeProps, Handle, Position } from '@xyflow/react';
3
+ import { AgentNodeData } from '../../types/graph';
4
+ import { useUIStore } from '../../store/uiStore';
5
+ import { useSettingsStore } from '../../store/settingsStore';
6
+
7
+ const AgentNode = memo(({ data, selected }: NodeProps) => {
8
+ const nodeData = data as AgentNodeData;
9
+ const name = nodeData.name;
10
+ const status = nodeData.status;
11
+ const sentCount = nodeData.sentCount;
12
+ const recvCount = nodeData.recvCount;
13
+ const subscriptions = nodeData.subscriptions || [];
14
+ const outputTypes = nodeData.outputTypes || [];
15
+ const receivedByType = nodeData.receivedByType || {};
16
+ const sentByType = nodeData.sentByType || {};
17
+ const streamingTokens = nodeData.streamingTokens || [];
18
+
19
+ // Merge known types with actual counts - show all types even with 0 count
20
+ // Start with actual counts, then add known types that haven't happened yet
21
+ const displayReceivedByType: Record<string, number> = { ...receivedByType };
22
+ subscriptions.forEach(type => {
23
+ if (!(type in displayReceivedByType)) {
24
+ displayReceivedByType[type] = 0;
25
+ }
26
+ });
27
+
28
+ const displaySentByType: Record<string, number> = { ...sentByType };
29
+ outputTypes.forEach(type => {
30
+ if (!(type in displaySentByType)) {
31
+ displaySentByType[type] = 0;
32
+ }
33
+ });
34
+
35
+ // Track which types just changed for flash animation
36
+ const [flashingKeys, setFlashingKeys] = useState<Set<string>>(new Set());
37
+ const prevCounts = useRef<Record<string, number>>({});
38
+
39
+ // Detect changes and trigger flash
40
+ useEffect(() => {
41
+ // Check only actual received/sent counts for flashing (not predicted types)
42
+ const allCounts = { ...receivedByType, ...sentByType };
43
+ const changedKeys = new Set<string>();
44
+
45
+ Object.entries(allCounts).forEach(([key, count]) => {
46
+ if (prevCounts.current[key] !== undefined && prevCounts.current[key] !== count) {
47
+ changedKeys.add(key);
48
+ }
49
+ prevCounts.current[key] = count;
50
+ });
51
+
52
+ if (changedKeys.size > 0) {
53
+ setFlashingKeys(changedKeys);
54
+ const timer = setTimeout(() => setFlashingKeys(new Set()), 500);
55
+ return () => clearTimeout(timer);
56
+ }
57
+ }, [receivedByType, sentByType]);
58
+
59
+ const handleDoubleClick = () => {
60
+ useUIStore.getState().openDetailWindow(name);
61
+ };
62
+
63
+ // Appearance settings from settings store
64
+ const agentIdleColor = useSettingsStore((state) => state.appearance.agentIdleColor);
65
+ const agentActiveColor = useSettingsStore((state) => state.appearance.agentActiveColor);
66
+ const agentErrorColor = useSettingsStore((state) => state.appearance.agentErrorColor);
67
+ const nodeShadow = useSettingsStore((state) => state.appearance.nodeShadow);
68
+ const showStatusPulse = useSettingsStore((state) => state.appearance.showStatusPulse);
69
+ const compactNodeView = useSettingsStore((state) => state.appearance.compactNodeView);
70
+
71
+ // Status styling: customizable colors from settings
72
+ const getStatusStyle = () => {
73
+ if (status === 'running') {
74
+ return {
75
+ indicatorColor: agentActiveColor,
76
+ borderColor: agentActiveColor,
77
+ borderWidth: '3px', // Thick border for active
78
+ };
79
+ } else if (status === 'error') {
80
+ return {
81
+ indicatorColor: agentErrorColor,
82
+ borderColor: agentErrorColor,
83
+ borderWidth: '2px',
84
+ };
85
+ } else {
86
+ // idle
87
+ return {
88
+ indicatorColor: agentIdleColor,
89
+ borderColor: agentIdleColor,
90
+ borderWidth: '2px',
91
+ };
92
+ }
93
+ };
94
+
95
+ const statusStyle = getStatusStyle();
96
+
97
+ // Shadow mapping
98
+ const shadowMap = {
99
+ none: 'none',
100
+ small: 'var(--shadow-sm)',
101
+ medium: 'var(--shadow-md)',
102
+ large: 'var(--shadow-lg)',
103
+ };
104
+
105
+ const baseShadow = shadowMap[nodeShadow];
106
+ const selectedShadow = selected ? 'var(--shadow-lg), var(--shadow-glow-primary)' : baseShadow;
107
+
108
+ return (
109
+ <div
110
+ className={`agent-node ${selected ? 'selected' : ''}`}
111
+ onDoubleClick={handleDoubleClick}
112
+ style={{
113
+ padding: compactNodeView ? 'var(--space-component-sm)' : 'var(--space-component-md)',
114
+ border: `${statusStyle.borderWidth} solid ${statusStyle.borderColor}`,
115
+ borderRadius: 'var(--radius-lg)',
116
+ backgroundColor: 'var(--color-bg-surface)',
117
+ minWidth: compactNodeView ? '160px' : '200px',
118
+ maxWidth: '300px',
119
+ boxShadow: selectedShadow,
120
+ cursor: 'pointer',
121
+ transition: 'var(--transition-all)',
122
+ }}
123
+ >
124
+ <Handle type="target" position={Position.Left} />
125
+ <Handle type="source" position={Position.Right} />
126
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 'var(--spacing-2)' }}>
127
+ <span style={{
128
+ fontWeight: 600,
129
+ fontSize: compactNodeView ? '14px' : '16px',
130
+ color: 'var(--color-text-primary)',
131
+ lineHeight: 1.4,
132
+ }}>{name}</span>
133
+ <span
134
+ style={{
135
+ width: compactNodeView ? '12px' : '14px',
136
+ height: compactNodeView ? '12px' : '14px',
137
+ borderRadius: '50%',
138
+ backgroundColor: statusStyle.indicatorColor,
139
+ display: 'inline-block',
140
+ flexShrink: 0,
141
+ marginLeft: 'var(--spacing-2)',
142
+ animation: (status === 'running' && showStatusPulse) ? 'pulse 2s infinite' : 'none',
143
+ }}
144
+ title={status}
145
+ />
146
+ </div>
147
+ {!compactNodeView && (
148
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
149
+ {/* Per-type input breakdown */}
150
+ {Object.entries(displayReceivedByType).map(([type, count]) => {
151
+ const isFlashing = flashingKeys.has(type);
152
+ return (
153
+ <div
154
+ key={`in-${type}`}
155
+ style={{
156
+ display: 'flex',
157
+ alignItems: 'center',
158
+ gap: 'var(--spacing-2)',
159
+ padding: '6px 10px',
160
+ background: isFlashing ? 'var(--color-info-bg)' : 'rgba(59, 130, 246, 0.08)',
161
+ borderLeft: '3px solid var(--color-info)',
162
+ borderRadius: 'var(--radius-md)',
163
+ transition: 'var(--transition-all)',
164
+ boxShadow: isFlashing ? 'var(--shadow-glow-primary)' : 'var(--shadow-xs)',
165
+ }}
166
+ >
167
+ <div style={{
168
+ display: 'flex',
169
+ alignItems: 'center',
170
+ justifyContent: 'center',
171
+ width: '20px',
172
+ height: '20px',
173
+ borderRadius: 'var(--radius-sm)',
174
+ background: 'var(--color-info-bg)',
175
+ color: 'var(--color-info-light)',
176
+ fontSize: '12px',
177
+ fontWeight: 700,
178
+ }}>
179
+
180
+ </div>
181
+ <div style={{
182
+ minWidth: '24px',
183
+ height: '24px',
184
+ display: 'flex',
185
+ alignItems: 'center',
186
+ justifyContent: 'center',
187
+ borderRadius: 'var(--radius-full)',
188
+ background: isFlashing ? 'var(--color-info)' : 'var(--color-info-bg)',
189
+ color: isFlashing ? 'var(--color-text-on-primary)' : 'var(--color-info-light)',
190
+ fontSize: '12px',
191
+ fontWeight: 700,
192
+ padding: '0 var(--spacing-2)',
193
+ transition: 'var(--transition-all)',
194
+ }}>
195
+ {count}
196
+ </div>
197
+ <div style={{
198
+ flex: 1,
199
+ fontSize: '11px',
200
+ fontFamily: 'var(--font-family-mono)',
201
+ color: 'var(--color-text-secondary)',
202
+ fontWeight: 500,
203
+ overflow: 'hidden',
204
+ textOverflow: 'ellipsis',
205
+ whiteSpace: 'nowrap',
206
+ }}>
207
+ {type}
208
+ </div>
209
+ </div>
210
+ );
211
+ })}
212
+ {/* Per-type output breakdown */}
213
+ {Object.entries(displaySentByType).map(([type, count]) => {
214
+ const isFlashing = flashingKeys.has(type);
215
+ return (
216
+ <div
217
+ key={`out-${type}`}
218
+ style={{
219
+ display: 'flex',
220
+ alignItems: 'center',
221
+ gap: 'var(--spacing-2)',
222
+ padding: '6px 10px',
223
+ background: isFlashing ? 'var(--color-success-bg)' : 'rgba(16, 185, 129, 0.08)',
224
+ borderLeft: '3px solid var(--color-success)',
225
+ borderRadius: 'var(--radius-md)',
226
+ transition: 'var(--transition-all)',
227
+ boxShadow: isFlashing ? 'var(--shadow-glow-success)' : 'var(--shadow-xs)',
228
+ }}
229
+ >
230
+ <div style={{
231
+ display: 'flex',
232
+ alignItems: 'center',
233
+ justifyContent: 'center',
234
+ width: '20px',
235
+ height: '20px',
236
+ borderRadius: 'var(--radius-sm)',
237
+ background: 'var(--color-success-bg)',
238
+ color: 'var(--color-success-light)',
239
+ fontSize: '12px',
240
+ fontWeight: 700,
241
+ }}>
242
+
243
+ </div>
244
+ <div style={{
245
+ minWidth: '24px',
246
+ height: '24px',
247
+ display: 'flex',
248
+ alignItems: 'center',
249
+ justifyContent: 'center',
250
+ borderRadius: 'var(--radius-full)',
251
+ background: isFlashing ? 'var(--color-success)' : 'var(--color-success-bg)',
252
+ color: isFlashing ? 'var(--color-text-on-primary)' : 'var(--color-success-light)',
253
+ fontSize: '12px',
254
+ fontWeight: 700,
255
+ padding: '0 var(--spacing-2)',
256
+ transition: 'var(--transition-all)',
257
+ }}>
258
+ {count}
259
+ </div>
260
+ <div style={{
261
+ flex: 1,
262
+ fontSize: '11px',
263
+ fontFamily: 'var(--font-family-mono)',
264
+ color: 'var(--color-text-secondary)',
265
+ fontWeight: 500,
266
+ overflow: 'hidden',
267
+ textOverflow: 'ellipsis',
268
+ whiteSpace: 'nowrap',
269
+ }}>
270
+ {type}
271
+ </div>
272
+ </div>
273
+ );
274
+ })}
275
+ {/* Fallback to totals if no per-type data yet */}
276
+ {Object.keys(displayReceivedByType).length === 0 && Object.keys(displaySentByType).length === 0 && (
277
+ <div style={{ fontSize: '11px', color: 'var(--color-text-tertiary)', opacity: 0.6 }}>
278
+ <span>↓ {recvCount} in</span>
279
+ <span style={{ marginLeft: 'var(--spacing-2)' }}>↑ {sentCount} out</span>
280
+ </div>
281
+ )}
282
+ {/* News ticker for streaming tokens */}
283
+ {streamingTokens.length > 0 && (
284
+ <div style={{
285
+ padding: '4px 8px',
286
+ background: 'rgba(250, 204, 21, 0.08)',
287
+ borderLeft: '2px solid var(--color-warning)',
288
+ borderRadius: 'var(--radius-sm)',
289
+ overflow: 'hidden',
290
+ minWidth: 0,
291
+ display: 'flex',
292
+ justifyContent: 'center',
293
+ }}>
294
+ <div style={{
295
+ width: '200px',
296
+ fontSize: '10px',
297
+ fontFamily: 'var(--font-family-mono)',
298
+ color: 'var(--color-warning-light)',
299
+ fontWeight: 500,
300
+ whiteSpace: 'nowrap',
301
+ overflow: 'hidden',
302
+ textOverflow: 'ellipsis',
303
+ }}>
304
+ {streamingTokens.join('')}
305
+ </div>
306
+ </div>
307
+ )}
308
+ </div>
309
+ )}
310
+ {compactNodeView && (
311
+ <div style={{ display: 'flex', gap: 'var(--spacing-4)', fontSize: '11px', color: 'var(--color-text-tertiary)' }}>
312
+ <span title="Received">↓ {recvCount} <span style={{ fontSize: '10px', opacity: 0.7 }}>in</span></span>
313
+ <span title="Sent">↑ {sentCount} <span style={{ fontSize: '10px', opacity: 0.7 }}>out</span></span>
314
+ </div>
315
+ )}
316
+ </div>
317
+ );
318
+ });
319
+
320
+ AgentNode.displayName = 'AgentNode';
321
+
322
+ export default AgentNode;