@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.
@@ -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 gap-2 p-2 items-center flex-wrap">
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 className="w-full text-sm">
297
- <thead className="sticky top-0 bg-surface">
298
- <tr className="border-b border-border">
299
- <th className="p-2 text-left text-muted font-normal">Time</th>
300
- <th className="p-2 text-left text-muted font-normal">Tool</th>
301
- <th className="p-2 text-left text-muted font-normal">Input</th>
302
- <th className="p-2 text-right text-muted font-normal">Duration</th>
303
- <th className="p-2 text-center text-muted font-normal">Status</th>
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={`border-b border-border/50 hover:bg-hover cursor-pointer ${
311
- expandedEntry === idx ? 'bg-hover' : ''
312
- }`}
347
+ className={expandedEntry === idx ? 'expanded' : ''}
313
348
  onClick={() => setExpandedEntry(expandedEntry === idx ? null : idx)}
314
349
  >
315
- <td className="p-2 text-muted whitespace-nowrap">
350
+ <td className="whitespace-nowrap">
316
351
  {formatTimestamp(entry.timestamp)}
317
352
  </td>
318
- <td className="p-2 font-mono">{entry.toolName}</td>
319
- <td className="p-2 text-muted truncate max-w-[200px]">
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="p-2 text-right text-muted whitespace-nowrap">
366
+ <td className="text-right whitespace-nowrap">
332
367
  {formatDuration(entry.durationMs)}
333
368
  </td>
334
- <td className="p-2 text-center">
369
+ <td className="text-center">
335
370
  {entry.success ? (
336
- <span className="text-success">✓</span>
371
+ <span className="status-ok">✓</span>
337
372
  ) : (
338
373
  <Tooltip>
339
374
  <TooltipTrigger asChild>
340
- <span className="text-error">✗</span>
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="bg-surface-alt">
349
- <td colSpan={5} className="p-3">
350
- <div className="space-y-2 text-sm">
351
- {entry.input && (
352
- <div>
353
- <div className="text-muted text-xs mb-1">Input:</div>
354
- <pre className="bg-surface p-2 rounded overflow-x-auto max-h-32 text-xs">
355
- {entry.input}
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
- {entry.error && (
369
- <div>
370
- <div className="text-error text-xs mb-1">Error:</div>
371
- <pre className="bg-surface p-2 rounded text-error text-xs">
372
- {entry.error}
373
- </pre>
374
- </div>
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
- <label className="toggle-setting">
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
- </label>
415
- <label className="toggle-setting">
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
- </label>
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
- <label className="toggle-setting">
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
- </label>
438
- <label className="toggle-setting">
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
- </label>
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
- <label key={panelId} className="toggle-setting">
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
- </label>
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 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
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 bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
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 with status context */
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-muted);
478
- font-size: 0.75rem;
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: flex;
2317
- flex-wrap: wrap;
2317
+ display: grid;
2318
+ grid-template-columns: 36px 1fr;
2318
2319
  align-items: center;
2319
- gap: 8px;
2320
- padding: 6px 0;
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
- width: 100%;
2327
+ grid-column: 2;
2331
2328
  font-size: 0.7rem;
2332
2329
  color: var(--text-secondary, #8b8b8b);
2333
- margin-left: 24px;
2334
- margin-top: -4px;
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;