@pennyfarthing/cyclist 9.2.0 → 9.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.d.ts +4 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +7 -0
- package/dist/main.js.map +1 -1
- package/dist/public/css/react.css +1 -1
- package/dist/public/js/react/react.js +40 -36
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +5 -4
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
- package/src/public/components/MessageView.tsx +3 -5
- package/src/public/components/ThemePalette/ThemePalette.css +2 -0
- package/src/public/components/ToolStack.tsx +23 -13
- package/src/public/components/panels/AuditLogPanel.tsx +140 -66
- package/src/public/components/panels/SettingsPanel.tsx +10 -10
- package/src/public/components/ui/switch.tsx +2 -2
- package/src/public/css/theme-system.css +25 -5
- package/src/public/styles/tailwind.css +192 -11
- package/src/public/utils/toolStackGrouper.ts +2 -2
|
@@ -19,6 +19,18 @@ import { ScrollArea } from '@/components/ui/scroll-area';
|
|
|
19
19
|
// Types
|
|
20
20
|
// =============================================================================
|
|
21
21
|
|
|
22
|
+
interface DiffSummary {
|
|
23
|
+
added: number;
|
|
24
|
+
removed: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface OutputSummary {
|
|
28
|
+
firstLines: string[];
|
|
29
|
+
lastLines: string[];
|
|
30
|
+
totalLines: number;
|
|
31
|
+
truncated: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
22
34
|
interface ToolEvent {
|
|
23
35
|
toolName: string;
|
|
24
36
|
input?: string;
|
|
@@ -27,6 +39,23 @@ interface ToolEvent {
|
|
|
27
39
|
success: boolean;
|
|
28
40
|
error?: string;
|
|
29
41
|
timestamp: number;
|
|
42
|
+
// File enrichment (Read/Edit/Write)
|
|
43
|
+
filePath?: string;
|
|
44
|
+
fileSize?: number;
|
|
45
|
+
lineCount?: number;
|
|
46
|
+
language?: string;
|
|
47
|
+
gitStatus?: 'clean' | 'modified' | 'new' | 'untracked' | null;
|
|
48
|
+
diff?: DiffSummary;
|
|
49
|
+
// Bash enrichment
|
|
50
|
+
command?: string;
|
|
51
|
+
exitCode?: number | null;
|
|
52
|
+
outputSummary?: OutputSummary;
|
|
53
|
+
workingDirectory?: string;
|
|
54
|
+
// Task enrichment
|
|
55
|
+
subagentType?: string;
|
|
56
|
+
promptSummary?: string;
|
|
57
|
+
resultSummary?: string;
|
|
58
|
+
isBackground?: boolean;
|
|
30
59
|
}
|
|
31
60
|
|
|
32
61
|
interface AuditLogStats {
|
|
@@ -56,6 +85,32 @@ function truncateInput(input: string | undefined, maxLength = 60): string {
|
|
|
56
85
|
return input.substring(0, maxLength) + '...';
|
|
57
86
|
}
|
|
58
87
|
|
|
88
|
+
function formatBytes(bytes: number | undefined): string {
|
|
89
|
+
if (bytes === undefined) return '';
|
|
90
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
91
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
92
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function formatFilePath(path: string | undefined): string {
|
|
96
|
+
if (!path) return '';
|
|
97
|
+
// Show last 3 segments for brevity
|
|
98
|
+
const parts = path.split('/');
|
|
99
|
+
if (parts.length <= 3) return path;
|
|
100
|
+
return '.../' + parts.slice(-3).join('/');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Render enrichment detail rows as label/value pairs */
|
|
104
|
+
function DetailRow({ label, value, mono }: { label: string; value: React.ReactNode; mono?: boolean }) {
|
|
105
|
+
if (value === undefined || value === null || value === '') return null;
|
|
106
|
+
return (
|
|
107
|
+
<div className="detail-row">
|
|
108
|
+
<span className="detail-label">{label}</span>
|
|
109
|
+
<span className={mono ? 'detail-value font-mono' : 'detail-value'}>{value}</span>
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
59
114
|
// =============================================================================
|
|
60
115
|
// Component
|
|
61
116
|
// =============================================================================
|
|
@@ -222,11 +277,10 @@ export function AuditLogPanel(): React.ReactElement {
|
|
|
222
277
|
)}
|
|
223
278
|
|
|
224
279
|
{/* Filters and Actions */}
|
|
225
|
-
<div className="audit-log-toolbar flex
|
|
280
|
+
<div className="audit-log-toolbar flex p-2 items-center">
|
|
226
281
|
<select
|
|
227
282
|
value={selectedType}
|
|
228
283
|
onChange={(e) => setSelectedType(e.target.value)}
|
|
229
|
-
className="audit-log-select px-2 py-1 bg-surface border border-border rounded text-sm"
|
|
230
284
|
>
|
|
231
285
|
<option value="">All Tools</option>
|
|
232
286
|
{toolTypes.map(type => (
|
|
@@ -237,7 +291,6 @@ export function AuditLogPanel(): React.ReactElement {
|
|
|
237
291
|
<select
|
|
238
292
|
value={selectedStatus}
|
|
239
293
|
onChange={(e) => setSelectedStatus(e.target.value)}
|
|
240
|
-
className="audit-log-select px-2 py-1 bg-surface border border-border rounded text-sm"
|
|
241
294
|
>
|
|
242
295
|
<option value="">All Status</option>
|
|
243
296
|
<option value="success">Success</option>
|
|
@@ -248,12 +301,7 @@ export function AuditLogPanel(): React.ReactElement {
|
|
|
248
301
|
|
|
249
302
|
<Tooltip>
|
|
250
303
|
<TooltipTrigger asChild>
|
|
251
|
-
<Button
|
|
252
|
-
variant="outline"
|
|
253
|
-
size="sm"
|
|
254
|
-
onClick={() => handleExport('json')}
|
|
255
|
-
className="audit-log-btn px-2 py-1 bg-surface border border-border rounded text-sm hover:bg-hover"
|
|
256
|
-
>
|
|
304
|
+
<Button variant="ghost" size="sm" className="audit-log-btn" onClick={() => handleExport('json')}>
|
|
257
305
|
JSON
|
|
258
306
|
</Button>
|
|
259
307
|
</TooltipTrigger>
|
|
@@ -261,12 +309,7 @@ export function AuditLogPanel(): React.ReactElement {
|
|
|
261
309
|
</Tooltip>
|
|
262
310
|
<Tooltip>
|
|
263
311
|
<TooltipTrigger asChild>
|
|
264
|
-
<Button
|
|
265
|
-
variant="outline"
|
|
266
|
-
size="sm"
|
|
267
|
-
onClick={() => handleExport('csv')}
|
|
268
|
-
className="audit-log-btn px-2 py-1 bg-surface border border-border rounded text-sm hover:bg-hover"
|
|
269
|
-
>
|
|
312
|
+
<Button variant="ghost" size="sm" className="audit-log-btn" onClick={() => handleExport('csv')}>
|
|
270
313
|
CSV
|
|
271
314
|
</Button>
|
|
272
315
|
</TooltipTrigger>
|
|
@@ -274,49 +317,41 @@ export function AuditLogPanel(): React.ReactElement {
|
|
|
274
317
|
</Tooltip>
|
|
275
318
|
<Tooltip>
|
|
276
319
|
<TooltipTrigger asChild>
|
|
277
|
-
<Button
|
|
278
|
-
variant="destructive"
|
|
279
|
-
size="sm"
|
|
280
|
-
onClick={handleClear}
|
|
281
|
-
className="audit-log-btn px-2 py-1 bg-surface border border-error text-error rounded text-sm hover:bg-error hover:text-white"
|
|
282
|
-
>
|
|
320
|
+
<Button variant="ghost" size="sm" className="audit-log-btn audit-log-btn-clear" onClick={handleClear}>
|
|
283
321
|
Clear
|
|
284
322
|
</Button>
|
|
285
323
|
</TooltipTrigger>
|
|
286
324
|
<TooltipContent>Clear audit log</TooltipContent>
|
|
287
325
|
</Tooltip>
|
|
288
326
|
</div>
|
|
289
|
-
<Separator />
|
|
290
327
|
|
|
291
328
|
{/* Entries List */}
|
|
292
329
|
<ScrollArea className="audit-log-entries flex-1">
|
|
293
330
|
{entries.length === 0 ? (
|
|
294
331
|
<div className="p-4 text-muted text-center">No entries</div>
|
|
295
332
|
) : (
|
|
296
|
-
<table
|
|
297
|
-
<thead
|
|
298
|
-
<tr
|
|
299
|
-
<th className="
|
|
300
|
-
<th className="
|
|
301
|
-
<th className="
|
|
302
|
-
<th className="
|
|
303
|
-
<th className="
|
|
333
|
+
<table>
|
|
334
|
+
<thead>
|
|
335
|
+
<tr>
|
|
336
|
+
<th className="text-left">Time</th>
|
|
337
|
+
<th className="text-left">Tool</th>
|
|
338
|
+
<th className="text-left">Input</th>
|
|
339
|
+
<th className="text-right">Duration</th>
|
|
340
|
+
<th className="text-center">Status</th>
|
|
304
341
|
</tr>
|
|
305
342
|
</thead>
|
|
306
343
|
<tbody>
|
|
307
344
|
{entries.map((entry, idx) => (
|
|
308
345
|
<React.Fragment key={`${entry.timestamp}-${idx}`}>
|
|
309
346
|
<tr
|
|
310
|
-
className={
|
|
311
|
-
expandedEntry === idx ? 'bg-hover' : ''
|
|
312
|
-
}`}
|
|
347
|
+
className={expandedEntry === idx ? 'expanded' : ''}
|
|
313
348
|
onClick={() => setExpandedEntry(expandedEntry === idx ? null : idx)}
|
|
314
349
|
>
|
|
315
|
-
<td className="
|
|
350
|
+
<td className="whitespace-nowrap">
|
|
316
351
|
{formatTimestamp(entry.timestamp)}
|
|
317
352
|
</td>
|
|
318
|
-
<td className="
|
|
319
|
-
<td className="
|
|
353
|
+
<td className="tool-name font-mono" data-tool={entry.toolName.toLowerCase()}>{entry.toolName}</td>
|
|
354
|
+
<td className="truncate max-w-[200px]">
|
|
320
355
|
{entry.input ? (
|
|
321
356
|
<Tooltip>
|
|
322
357
|
<TooltipTrigger asChild>
|
|
@@ -328,16 +363,16 @@ export function AuditLogPanel(): React.ReactElement {
|
|
|
328
363
|
truncateInput(entry.input)
|
|
329
364
|
)}
|
|
330
365
|
</td>
|
|
331
|
-
<td className="
|
|
366
|
+
<td className="text-right whitespace-nowrap">
|
|
332
367
|
{formatDuration(entry.durationMs)}
|
|
333
368
|
</td>
|
|
334
|
-
<td className="
|
|
369
|
+
<td className="text-center">
|
|
335
370
|
{entry.success ? (
|
|
336
|
-
<span className="
|
|
371
|
+
<span className="status-ok">✓</span>
|
|
337
372
|
) : (
|
|
338
373
|
<Tooltip>
|
|
339
374
|
<TooltipTrigger asChild>
|
|
340
|
-
<span className="
|
|
375
|
+
<span className="status-err">✗</span>
|
|
341
376
|
</TooltipTrigger>
|
|
342
377
|
<TooltipContent>{entry.error}</TooltipContent>
|
|
343
378
|
</Tooltip>
|
|
@@ -345,35 +380,74 @@ export function AuditLogPanel(): React.ReactElement {
|
|
|
345
380
|
</td>
|
|
346
381
|
</tr>
|
|
347
382
|
{expandedEntry === idx && (
|
|
348
|
-
<tr className="
|
|
349
|
-
<td colSpan={5}
|
|
350
|
-
<div className="
|
|
351
|
-
{
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
</pre>
|
|
357
|
-
</div>
|
|
358
|
-
)}
|
|
359
|
-
{entry.output && (
|
|
360
|
-
<div>
|
|
361
|
-
<div className="text-muted text-xs mb-1">Output:</div>
|
|
362
|
-
<pre className="bg-surface p-2 rounded overflow-x-auto max-h-32 text-xs">
|
|
363
|
-
{entry.output.substring(0, 500)}
|
|
364
|
-
{entry.output.length > 500 && '...'}
|
|
365
|
-
</pre>
|
|
366
|
-
</div>
|
|
383
|
+
<tr className="expanded-detail">
|
|
384
|
+
<td colSpan={5}>
|
|
385
|
+
<div className="detail-grid">
|
|
386
|
+
{/* Common fields */}
|
|
387
|
+
<DetailRow label="Tool" value={entry.toolName} />
|
|
388
|
+
<DetailRow label="Duration" value={entry.durationMs !== undefined ? formatDuration(entry.durationMs) : undefined} />
|
|
389
|
+
{entry.exitCode !== undefined && entry.exitCode !== null && (
|
|
390
|
+
<DetailRow label="Exit Code" value={entry.exitCode} mono />
|
|
367
391
|
)}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
392
|
+
|
|
393
|
+
{/* File enrichment (Read/Edit/Write) */}
|
|
394
|
+
<DetailRow label="File" value={entry.filePath} mono />
|
|
395
|
+
{entry.language && <DetailRow label="Language" value={entry.language} />}
|
|
396
|
+
{entry.fileSize !== undefined && <DetailRow label="Size" value={formatBytes(entry.fileSize)} />}
|
|
397
|
+
{entry.lineCount !== undefined && <DetailRow label="Lines" value={entry.lineCount.toLocaleString()} />}
|
|
398
|
+
{entry.gitStatus && <DetailRow label="Git" value={entry.gitStatus} />}
|
|
399
|
+
{entry.diff && (
|
|
400
|
+
<DetailRow label="Diff" value={
|
|
401
|
+
<span>
|
|
402
|
+
<span style={{color: 'var(--success, #22c55e)'}}>+{entry.diff.added}</span>
|
|
403
|
+
{' '}
|
|
404
|
+
<span style={{color: 'var(--error, #ef4444)'}}>-{entry.diff.removed}</span>
|
|
405
|
+
</span>
|
|
406
|
+
} />
|
|
375
407
|
)}
|
|
408
|
+
|
|
409
|
+
{/* Bash enrichment */}
|
|
410
|
+
{entry.command && <DetailRow label="Command" value={entry.command} mono />}
|
|
411
|
+
{entry.workingDirectory && <DetailRow label="CWD" value={formatFilePath(entry.workingDirectory)} mono />}
|
|
412
|
+
|
|
413
|
+
{/* Task enrichment */}
|
|
414
|
+
{entry.subagentType && <DetailRow label="Agent" value={entry.subagentType} />}
|
|
415
|
+
{entry.isBackground && <DetailRow label="Background" value="Yes" />}
|
|
416
|
+
{entry.promptSummary && <DetailRow label="Prompt" value={entry.promptSummary} />}
|
|
417
|
+
{entry.resultSummary && <DetailRow label="Result" value={entry.resultSummary} />}
|
|
376
418
|
</div>
|
|
419
|
+
|
|
420
|
+
{/* Output summary (Bash) */}
|
|
421
|
+
{entry.outputSummary && entry.outputSummary.totalLines > 0 && (
|
|
422
|
+
<div className="detail-output">
|
|
423
|
+
<div className="detail-label">Output ({entry.outputSummary.totalLines} lines{entry.outputSummary.truncated ? ', truncated' : ''})</div>
|
|
424
|
+
<pre>{entry.outputSummary.firstLines.join('\n')}{entry.outputSummary.truncated && entry.outputSummary.lastLines.length > 0 ? '\n...\n' + entry.outputSummary.lastLines.join('\n') : ''}</pre>
|
|
425
|
+
</div>
|
|
426
|
+
)}
|
|
427
|
+
|
|
428
|
+
{/* Raw input (fallback when no enrichment) */}
|
|
429
|
+
{entry.input && !entry.command && !entry.filePath && !entry.subagentType && (
|
|
430
|
+
<div className="detail-output">
|
|
431
|
+
<div className="detail-label">Input</div>
|
|
432
|
+
<pre>{entry.input}</pre>
|
|
433
|
+
</div>
|
|
434
|
+
)}
|
|
435
|
+
|
|
436
|
+
{/* Raw output (when no outputSummary) */}
|
|
437
|
+
{entry.output && !entry.outputSummary && (
|
|
438
|
+
<div className="detail-output">
|
|
439
|
+
<div className="detail-label">Output</div>
|
|
440
|
+
<pre>{entry.output.substring(0, 500)}{entry.output.length > 500 && '...'}</pre>
|
|
441
|
+
</div>
|
|
442
|
+
)}
|
|
443
|
+
|
|
444
|
+
{/* Error */}
|
|
445
|
+
{entry.error && (
|
|
446
|
+
<div className="detail-output">
|
|
447
|
+
<div className="detail-label status-err">Error</div>
|
|
448
|
+
<pre className="status-err">{entry.error}</pre>
|
|
449
|
+
</div>
|
|
450
|
+
)}
|
|
377
451
|
</td>
|
|
378
452
|
</tr>
|
|
379
453
|
)}
|
|
@@ -403,7 +403,7 @@ export function SettingsPanel(): React.ReactElement {
|
|
|
403
403
|
|
|
404
404
|
<section className="settings-section">
|
|
405
405
|
<h4>Workflow</h4>
|
|
406
|
-
<
|
|
406
|
+
<div className="toggle-setting">
|
|
407
407
|
<Switch
|
|
408
408
|
checked={settings.workflow?.bell_mode || false}
|
|
409
409
|
onCheckedChange={(checked: boolean) => handleToggle('workflow', 'bell_mode', checked)}
|
|
@@ -411,8 +411,8 @@ export function SettingsPanel(): React.ReactElement {
|
|
|
411
411
|
/>
|
|
412
412
|
Bell Mode
|
|
413
413
|
<span className="setting-description">Inject queued messages via PostToolUse hook instead of waiting</span>
|
|
414
|
-
</
|
|
415
|
-
<
|
|
414
|
+
</div>
|
|
415
|
+
<div className="toggle-setting">
|
|
416
416
|
<Switch
|
|
417
417
|
checked={settings.workflow?.relay_mode || false}
|
|
418
418
|
onCheckedChange={(checked: boolean) => handleToggle('workflow', 'relay_mode', checked)}
|
|
@@ -420,29 +420,29 @@ export function SettingsPanel(): React.ReactElement {
|
|
|
420
420
|
/>
|
|
421
421
|
Relay Mode
|
|
422
422
|
<span className="setting-description">Auto-handoff to next agent</span>
|
|
423
|
-
</
|
|
423
|
+
</div>
|
|
424
424
|
</section>
|
|
425
425
|
|
|
426
426
|
<Separator className="my-2" />
|
|
427
427
|
|
|
428
428
|
<section className="settings-section">
|
|
429
429
|
<h4>Notifications</h4>
|
|
430
|
-
<
|
|
430
|
+
<div className="toggle-setting">
|
|
431
431
|
<Switch
|
|
432
432
|
checked={settings.notifications?.phase_change || false}
|
|
433
433
|
onCheckedChange={(checked: boolean) => handleToggle('notifications', 'phase_change', checked)}
|
|
434
434
|
disabled={saving}
|
|
435
435
|
/>
|
|
436
436
|
Phase change alerts
|
|
437
|
-
</
|
|
438
|
-
<
|
|
437
|
+
</div>
|
|
438
|
+
<div className="toggle-setting">
|
|
439
439
|
<Switch
|
|
440
440
|
checked={settings.notifications?.sound || false}
|
|
441
441
|
onCheckedChange={(checked: boolean) => handleToggle('notifications', 'sound', checked)}
|
|
442
442
|
disabled={saving}
|
|
443
443
|
/>
|
|
444
444
|
Sound effects
|
|
445
|
-
</
|
|
445
|
+
</div>
|
|
446
446
|
</section>
|
|
447
447
|
|
|
448
448
|
<Separator className="my-2" />
|
|
@@ -456,7 +456,7 @@ export function SettingsPanel(): React.ReactElement {
|
|
|
456
456
|
const displayName = PANEL_DISPLAY_NAMES[panelId] || panelId;
|
|
457
457
|
|
|
458
458
|
return (
|
|
459
|
-
<
|
|
459
|
+
<div key={panelId} className="toggle-setting">
|
|
460
460
|
<Switch
|
|
461
461
|
checked={isVisible}
|
|
462
462
|
onCheckedChange={(checked: boolean) => handlePanelToggle(panelId, checked)}
|
|
@@ -466,7 +466,7 @@ export function SettingsPanel(): React.ReactElement {
|
|
|
466
466
|
{isProtected && (
|
|
467
467
|
<span className="setting-description">(always visible)</span>
|
|
468
468
|
)}
|
|
469
|
-
</
|
|
469
|
+
</div>
|
|
470
470
|
);
|
|
471
471
|
})}
|
|
472
472
|
</div>
|
|
@@ -9,7 +9,7 @@ const Switch = React.forwardRef<
|
|
|
9
9
|
>(({ className, ...props }, ref) => (
|
|
10
10
|
<SwitchPrimitives.Root
|
|
11
11
|
className={cn(
|
|
12
|
-
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2
|
|
12
|
+
"cyclist-switch peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
13
13
|
className
|
|
14
14
|
)}
|
|
15
15
|
{...props}
|
|
@@ -17,7 +17,7 @@ const Switch = React.forwardRef<
|
|
|
17
17
|
>
|
|
18
18
|
<SwitchPrimitives.Thumb
|
|
19
19
|
className={cn(
|
|
20
|
-
"pointer-events-none block h-4 w-4 rounded-full
|
|
20
|
+
"cyclist-switch-thumb pointer-events-none block h-4 w-4 rounded-full shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
|
|
21
21
|
)}
|
|
22
22
|
/>
|
|
23
23
|
</SwitchPrimitives.Root>
|
|
@@ -438,11 +438,30 @@
|
|
|
438
438
|
transition: transform 0.2s ease;
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
-
/* Tool count
|
|
442
|
-
.tool-stack-count {
|
|
441
|
+
/* Tool count - small de-emphasized badge */
|
|
442
|
+
.tool-stack-count-badge {
|
|
443
443
|
font-weight: 600;
|
|
444
|
+
font-size: 0.6875rem;
|
|
445
|
+
color: var(--text-muted);
|
|
446
|
+
background-color: var(--bg-tertiary);
|
|
447
|
+
padding: 0.0625rem 0.375rem;
|
|
448
|
+
border-radius: 9999px;
|
|
449
|
+
font-family: var(--font-mono);
|
|
450
|
+
min-width: 1.25rem;
|
|
451
|
+
text-align: center;
|
|
452
|
+
flex-shrink: 0;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/* Active tool summary - the main visual element while running */
|
|
456
|
+
.tool-stack-active-summary {
|
|
457
|
+
font-weight: 500;
|
|
444
458
|
font-size: 0.8125rem;
|
|
445
459
|
color: var(--text-primary);
|
|
460
|
+
flex: 1;
|
|
461
|
+
min-width: 0;
|
|
462
|
+
overflow: hidden;
|
|
463
|
+
text-overflow: ellipsis;
|
|
464
|
+
white-space: nowrap;
|
|
446
465
|
}
|
|
447
466
|
|
|
448
467
|
/* Mini tool badges in collapsed header */
|
|
@@ -472,10 +491,11 @@
|
|
|
472
491
|
.tool-mini-badge.badge-edit { background-color: var(--tool-edit-color); }
|
|
473
492
|
.tool-mini-badge.badge-task { background-color: var(--tool-task-color); }
|
|
474
493
|
|
|
475
|
-
/* Summary in collapsed state */
|
|
494
|
+
/* Summary in collapsed state - still prominent */
|
|
476
495
|
.tool-stack-summary {
|
|
477
|
-
color: var(--text-
|
|
478
|
-
font-size: 0.
|
|
496
|
+
color: var(--text-secondary);
|
|
497
|
+
font-size: 0.8125rem;
|
|
498
|
+
font-weight: 500;
|
|
479
499
|
overflow: hidden;
|
|
480
500
|
text-overflow: ellipsis;
|
|
481
501
|
white-space: nowrap;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Tailwind CSS for Cyclist React components */
|
|
2
2
|
|
|
3
3
|
@import "tailwindcss";
|
|
4
|
+
@config "../../../tailwind.config.js";
|
|
4
5
|
|
|
5
6
|
/* =============================================================================
|
|
6
7
|
Docking Workspace Styles
|
|
@@ -2313,25 +2314,34 @@
|
|
|
2313
2314
|
}
|
|
2314
2315
|
|
|
2315
2316
|
.settings-panel .toggle-setting {
|
|
2316
|
-
display:
|
|
2317
|
-
|
|
2317
|
+
display: grid;
|
|
2318
|
+
grid-template-columns: 36px 1fr;
|
|
2318
2319
|
align-items: center;
|
|
2319
|
-
gap: 8px;
|
|
2320
|
-
padding:
|
|
2320
|
+
gap: 4px 8px;
|
|
2321
|
+
padding: 4px 0;
|
|
2321
2322
|
font-size: 0.8rem;
|
|
2322
2323
|
cursor: pointer;
|
|
2323
2324
|
}
|
|
2324
2325
|
|
|
2325
|
-
.settings-panel .toggle-setting input {
|
|
2326
|
-
accent-color: var(--accent-color, #007acc);
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
2326
|
.settings-panel .setting-description {
|
|
2330
|
-
|
|
2327
|
+
grid-column: 2;
|
|
2331
2328
|
font-size: 0.7rem;
|
|
2332
2329
|
color: var(--text-secondary, #8b8b8b);
|
|
2333
|
-
|
|
2334
|
-
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
/* Switch (Radix) - explicit colors since Tailwind v4 data-[state] variants don't generate */
|
|
2333
|
+
.cyclist-switch {
|
|
2334
|
+
background: var(--bg-tertiary, #2d2d2d);
|
|
2335
|
+
border-color: var(--border-color, #3c3c3c);
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
.cyclist-switch[data-state="checked"] {
|
|
2339
|
+
background: var(--accent, #4f46e5);
|
|
2340
|
+
border-color: var(--accent, #4f46e5);
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
.cyclist-switch-thumb {
|
|
2344
|
+
background: var(--text-primary, #e4e4e7);
|
|
2335
2345
|
}
|
|
2336
2346
|
|
|
2337
2347
|
/* ThemePalette in settings - ensure solid background for dropdown */
|
|
@@ -2339,6 +2349,177 @@
|
|
|
2339
2349
|
background: var(--bg-secondary, #252526);
|
|
2340
2350
|
}
|
|
2341
2351
|
|
|
2352
|
+
/* Audit Log panel - Tufte-inspired: maximize data-ink, minimize chrome */
|
|
2353
|
+
.audit-log-panel {
|
|
2354
|
+
font-size: 0.8rem;
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
.audit-log-panel .audit-log-stats {
|
|
2358
|
+
font-size: 0.75rem;
|
|
2359
|
+
color: var(--text-secondary, #8b8b8b);
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
.audit-log-panel .audit-log-stats .text-success {
|
|
2363
|
+
color: var(--text-secondary, #8b8b8b);
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
.audit-log-panel .audit-log-stats .text-error {
|
|
2367
|
+
color: var(--error, #ef4444);
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
/* Only highlight error count when there are actual errors */
|
|
2371
|
+
.audit-log-panel .audit-log-stats .text-error:has(strong:empty),
|
|
2372
|
+
.audit-log-panel .audit-log-stats .text-error strong:only-child {
|
|
2373
|
+
opacity: 0.6;
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
.audit-log-panel .audit-log-toolbar {
|
|
2377
|
+
gap: 6px;
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
.audit-log-panel select {
|
|
2381
|
+
padding: 4px 8px;
|
|
2382
|
+
background: var(--bg-tertiary, #2d2d2d);
|
|
2383
|
+
border: 1px solid var(--border-color, #3c3c3c);
|
|
2384
|
+
border-radius: 4px;
|
|
2385
|
+
color: var(--text-primary, #d4d4d4);
|
|
2386
|
+
font-size: 0.75rem;
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
.audit-log-panel .audit-log-btn {
|
|
2390
|
+
padding: 4px 10px !important;
|
|
2391
|
+
background: transparent !important;
|
|
2392
|
+
border: none !important;
|
|
2393
|
+
color: var(--text-secondary, #8b8b8b);
|
|
2394
|
+
font-size: 0.7rem;
|
|
2395
|
+
font-weight: normal;
|
|
2396
|
+
letter-spacing: 0.03em;
|
|
2397
|
+
opacity: 0.7;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
.audit-log-panel .audit-log-btn:hover {
|
|
2401
|
+
background: var(--bg-tertiary, #2d2d2d) !important;
|
|
2402
|
+
color: var(--text-primary, #d4d4d4);
|
|
2403
|
+
opacity: 1;
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
.audit-log-panel .audit-log-btn-clear {
|
|
2407
|
+
color: var(--error, #ef4444);
|
|
2408
|
+
border-color: transparent !important;
|
|
2409
|
+
background: transparent;
|
|
2410
|
+
opacity: 0.6;
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
.audit-log-panel .audit-log-btn-clear:hover {
|
|
2414
|
+
opacity: 1;
|
|
2415
|
+
background: transparent;
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
.audit-log-panel table {
|
|
2419
|
+
border-collapse: collapse;
|
|
2420
|
+
width: 100%;
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
.audit-log-panel thead th {
|
|
2424
|
+
font-size: 0.65rem;
|
|
2425
|
+
font-weight: 500;
|
|
2426
|
+
text-transform: uppercase;
|
|
2427
|
+
letter-spacing: 0.06em;
|
|
2428
|
+
color: var(--text-muted, #666);
|
|
2429
|
+
border-bottom: 1px solid var(--border-color, #3c3c3c);
|
|
2430
|
+
padding: 6px 8px;
|
|
2431
|
+
background: var(--bg-primary, #1e1e1e);
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
.audit-log-panel tbody tr {
|
|
2435
|
+
border-bottom: none;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
.audit-log-panel tbody td {
|
|
2439
|
+
padding: 3px 8px;
|
|
2440
|
+
color: var(--text-secondary, #8b8b8b);
|
|
2441
|
+
font-size: 0.75rem;
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
.audit-log-panel tbody tr:hover {
|
|
2445
|
+
background: var(--bg-secondary, #252526);
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
.audit-log-panel tbody .tool-name {
|
|
2449
|
+
font-weight: 500;
|
|
2450
|
+
}
|
|
2451
|
+
.audit-log-panel tbody .tool-name[data-tool="read"] { color: var(--tool-read-color, #3b82f6); }
|
|
2452
|
+
.audit-log-panel tbody .tool-name[data-tool="write"] { color: var(--tool-write-color, #f97316); }
|
|
2453
|
+
.audit-log-panel tbody .tool-name[data-tool="bash"] { color: var(--tool-bash-color, #22c55e); }
|
|
2454
|
+
.audit-log-panel tbody .tool-name[data-tool="glob"] { color: var(--tool-glob-color, #a855f7); }
|
|
2455
|
+
.audit-log-panel tbody .tool-name[data-tool="grep"] { color: var(--tool-grep-color, #06b6d4); }
|
|
2456
|
+
.audit-log-panel tbody .tool-name[data-tool="edit"] { color: var(--tool-edit-color, #eab308); }
|
|
2457
|
+
.audit-log-panel tbody .tool-name[data-tool="task"] { color: var(--tool-task-color, #ec4899); }
|
|
2458
|
+
.audit-log-panel tbody .tool-name[data-tool="skill"] { color: var(--text-secondary, #8b8b8b); }
|
|
2459
|
+
|
|
2460
|
+
.audit-log-panel tbody .status-ok {
|
|
2461
|
+
color: var(--success, #22c55e);
|
|
2462
|
+
opacity: 0.4;
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
.audit-log-panel tbody .status-err {
|
|
2466
|
+
color: var(--error, #ef4444);
|
|
2467
|
+
opacity: 0.9;
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
.audit-log-panel .expanded-detail {
|
|
2471
|
+
background: var(--bg-secondary, #252526);
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
.audit-log-panel .expanded-detail > td {
|
|
2475
|
+
padding: 8px 12px !important;
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
.audit-log-panel .detail-grid {
|
|
2479
|
+
display: grid;
|
|
2480
|
+
grid-template-columns: auto 1fr;
|
|
2481
|
+
gap: 2px 12px;
|
|
2482
|
+
font-size: 0.72rem;
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
.audit-log-panel .detail-row {
|
|
2486
|
+
display: contents;
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
.audit-log-panel .detail-label {
|
|
2490
|
+
color: var(--text-muted, #666);
|
|
2491
|
+
font-size: 0.68rem;
|
|
2492
|
+
text-transform: uppercase;
|
|
2493
|
+
letter-spacing: 0.04em;
|
|
2494
|
+
white-space: nowrap;
|
|
2495
|
+
padding: 1px 0;
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
.audit-log-panel .detail-value {
|
|
2499
|
+
color: var(--text-primary, #d4d4d4);
|
|
2500
|
+
word-break: break-all;
|
|
2501
|
+
padding: 1px 0;
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
.audit-log-panel .detail-output {
|
|
2505
|
+
margin-top: 6px;
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
.audit-log-panel .detail-output .detail-label {
|
|
2509
|
+
margin-bottom: 3px;
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
.audit-log-panel .expanded-detail pre {
|
|
2513
|
+
background: var(--bg-tertiary, #2d2d2d);
|
|
2514
|
+
padding: 6px 8px;
|
|
2515
|
+
border-radius: 4px;
|
|
2516
|
+
font-size: 0.68rem;
|
|
2517
|
+
overflow-x: auto;
|
|
2518
|
+
max-height: 10rem;
|
|
2519
|
+
color: var(--text-secondary, #8b8b8b);
|
|
2520
|
+
margin: 0;
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2342
2523
|
/* Changed panel (FileTree) */
|
|
2343
2524
|
.changed-panel {
|
|
2344
2525
|
display: flex;
|