@toolr/ui-design 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/ai-manifest.json +35 -20
  2. package/components/composites/dashboard-list-item.tsx +172 -0
  3. package/components/composites/dashboard-panel.tsx +218 -0
  4. package/components/content/info-panel-primitives.tsx +9 -8
  5. package/components/diagrams/diagram-utils.tsx +2 -1
  6. package/components/hooks/use-dropdown-portal.ts +39 -0
  7. package/components/hooks/use-modal-behavior.ts +32 -3
  8. package/components/lib/accent-context.ts +10 -0
  9. package/components/lib/{ai-tools.tsx → coding-agents.tsx} +23 -8
  10. package/components/lib/custom-icons.tsx +37 -0
  11. package/components/lib/git-providers.tsx +39 -0
  12. package/components/lib/theme-engine.ts +59 -10
  13. package/components/lib/toolr-brand.tsx +23 -9
  14. package/components/sections/captured-issues/captured-issues-panel.tsx +17 -8
  15. package/components/sections/{ai-tools-paths/tools-paths-panel.tsx → coding-agent-paths/agent-paths-panel.tsx} +70 -62
  16. package/components/sections/coding-agent-paths/index.ts +37 -0
  17. package/components/sections/{ai-tools-paths → coding-agent-paths}/types.ts +28 -28
  18. package/components/sections/coding-agent-paths/use-agent-paths.ts +159 -0
  19. package/components/sections/golden-snapshots/file-diff-viewer.tsx +11 -10
  20. package/components/sections/golden-snapshots/golden-sync-panel.tsx +12 -3
  21. package/components/sections/golden-snapshots/snapshot-manager.tsx +9 -7
  22. package/components/sections/golden-snapshots/status-overview.tsx +8 -8
  23. package/components/sections/golden-snapshots/version-manager.tsx +6 -6
  24. package/components/sections/prompt-editor/file-type-tabbed-prompt-editor.tsx +3 -3
  25. package/components/sections/prompt-editor/index.ts +1 -1
  26. package/components/sections/prompt-editor/simulator-prompt-editor.tsx +13 -5
  27. package/components/sections/prompt-editor/tabbed-prompt-editor.tsx +18 -10
  28. package/components/sections/prompt-editor/types.ts +2 -2
  29. package/components/sections/report-bug/report-bug-form.tsx +12 -4
  30. package/components/sections/report-bug/screenshot-uploader.tsx +11 -3
  31. package/components/sections/snapshot-browser/snapshot-browser-panel.tsx +12 -4
  32. package/components/sections/snapshot-browser/snapshot-tree.tsx +5 -4
  33. package/components/sections/snapshot-browser/types.ts +1 -1
  34. package/components/sections/snippets-editor/snippets-editor.tsx +16 -9
  35. package/components/settings/SettingsHeader.tsx +2 -2
  36. package/components/settings/SettingsPanel.tsx +11 -3
  37. package/components/settings/SettingsTreeNav.tsx +15 -9
  38. package/components/ui/action-dialog.tsx +37 -35
  39. package/components/ui/ai-action-button.tsx +12 -11
  40. package/components/ui/ai-execution-action-buttons.tsx +13 -5
  41. package/components/ui/badge.tsx +17 -6
  42. package/components/ui/bottom-panel-header.tsx +9 -5
  43. package/components/ui/breadcrumb.tsx +14 -6
  44. package/components/ui/{extension-list-card.tsx → capability-list-card.tsx} +14 -6
  45. package/components/ui/checkbox.tsx +23 -14
  46. package/components/ui/collapsible-section.tsx +38 -28
  47. package/components/ui/confirm-badge.tsx +17 -6
  48. package/components/ui/cookie-consent.tsx +13 -7
  49. package/components/ui/detail-section.tsx +24 -16
  50. package/components/ui/detail-view-wrapper.tsx +30 -22
  51. package/components/ui/editor-placeholder-card.tsx +28 -24
  52. package/components/ui/editor-toolbar.tsx +7 -4
  53. package/components/ui/execution-details-panel.tsx +10 -5
  54. package/components/ui/file-structure-section.tsx +3 -3
  55. package/components/ui/file-tree.tsx +7 -5
  56. package/components/ui/files-panel.tsx +147 -27
  57. package/components/ui/filter-dropdown.tsx +88 -75
  58. package/components/ui/form-actions.tsx +21 -11
  59. package/components/ui/frontmatter-form-header.tsx +10 -2
  60. package/components/ui/icon-button.tsx +27 -14
  61. package/components/ui/input.tsx +15 -7
  62. package/components/ui/label.tsx +9 -5
  63. package/components/ui/layout-tab-bar.tsx +11 -9
  64. package/components/ui/modal.tsx +26 -8
  65. package/components/ui/nav-card.tsx +7 -4
  66. package/components/ui/navigation-bar.tsx +40 -12
  67. package/components/ui/number-input.tsx +14 -4
  68. package/components/ui/project-explorer.tsx +666 -0
  69. package/components/ui/registry-browser.tsx +12 -1
  70. package/components/ui/registry-card.tsx +49 -42
  71. package/components/ui/registry-detail.tsx +34 -11
  72. package/components/ui/resizable-textarea.tsx +18 -11
  73. package/components/ui/scope-badge.tsx +18 -11
  74. package/components/ui/segmented-toggle.tsx +7 -2
  75. package/components/ui/select.tsx +17 -11
  76. package/components/ui/selection-grid.tsx +40 -37
  77. package/components/ui/setting-row.tsx +6 -4
  78. package/components/ui/settings-card.tsx +12 -5
  79. package/components/ui/settings-info-box.tsx +9 -6
  80. package/components/ui/settings-section-title.tsx +14 -2
  81. package/components/ui/snapshot-card.tsx +10 -2
  82. package/components/ui/snippets-panel.tsx +4 -2
  83. package/components/ui/sort-dropdown.tsx +45 -32
  84. package/components/ui/status-card.tsx +9 -1
  85. package/components/ui/tab-bar.tsx +26 -13
  86. package/components/ui/toggle.tsx +31 -17
  87. package/components/ui/tooltip.tsx +14 -6
  88. package/dist/content.js +8 -8
  89. package/dist/diagrams.d.ts +0 -1
  90. package/dist/index.d.ts +431 -186
  91. package/dist/index.js +3119 -1724
  92. package/dist/tokens/primitives.css +28 -6
  93. package/dist/tokens/semantic.css +15 -15
  94. package/dist/tokens/theme.css +23 -0
  95. package/index.ts +25 -11
  96. package/package.json +9 -1
  97. package/tokens/primitives.css +28 -6
  98. package/tokens/semantic.css +15 -15
  99. package/tokens/theme.css +23 -0
  100. package/components/sections/ai-tools-paths/index.ts +0 -37
  101. package/components/sections/ai-tools-paths/use-tools-paths.ts +0 -159
@@ -1,4 +1,6 @@
1
1
  import { type ReactNode } from 'react'
2
+ import { useAccentColor, AccentColorProvider } from '../lib/accent-context.ts'
3
+ import type { FormColor } from '../lib/form-colors.ts'
2
4
 
3
5
  export interface DetailViewWrapperProps {
4
6
  /** Main editor content */
@@ -12,6 +14,7 @@ export interface DetailViewWrapperProps {
12
14
  /** Optional right sidebar */
13
15
  rightSidebar?: ReactNode
14
16
  showRightSidebar?: boolean
17
+ accentColor?: FormColor
15
18
  }
16
19
 
17
20
  export function DetailViewWrapper({
@@ -20,36 +23,41 @@ export function DetailViewWrapper({
20
23
  bottomPanel,
21
24
  rightSidebar,
22
25
  showRightSidebar = false,
26
+ accentColor,
23
27
  }: DetailViewWrapperProps) {
28
+ const contextAccent = useAccentColor()
29
+ const effectiveColor = accentColor ?? contextAccent ?? 'blue'
24
30
 
25
31
  return (
26
- <div className="flex-1 flex flex-col overflow-hidden min-h-0">
27
- {actions && (
28
- <div className="flex items-center justify-end border-b border-neutral-800 bg-neutral-950/50 px-2 h-[36px]">
29
- <div className="flex items-center gap-1">{actions}</div>
30
- </div>
31
- )}
32
+ <AccentColorProvider value={effectiveColor}>
33
+ <div className="flex-1 flex flex-col overflow-hidden min-h-0">
34
+ {actions && (
35
+ <div className="flex items-center justify-end border-b border-neutral-960 bg-neutral-990/50 px-2 h-[36px]">
36
+ <div className="flex items-center gap-1">{actions}</div>
37
+ </div>
38
+ )}
32
39
 
33
- {/* Main content area */}
34
- <div className="flex-1 flex overflow-hidden min-h-0">
35
- <div className="flex-1 flex flex-col min-w-0 overflow-hidden">
36
- {editorContent && (
37
- <div className="flex-1 flex flex-col overflow-hidden">{editorContent}</div>
40
+ {/* Main content area */}
41
+ <div className="flex-1 flex overflow-hidden min-h-0">
42
+ <div className="flex-1 flex flex-col min-w-0 overflow-hidden">
43
+ {editorContent && (
44
+ <div className="flex-1 flex flex-col overflow-hidden">{editorContent}</div>
45
+ )}
46
+ </div>
47
+
48
+ {/* Optional right sidebar */}
49
+ {showRightSidebar && rightSidebar && (
50
+ <div className="w-[300px] border-l border-neutral-960 overflow-hidden flex-shrink-0">
51
+ {rightSidebar}
52
+ </div>
38
53
  )}
39
54
  </div>
40
55
 
41
- {/* Optional right sidebar */}
42
- {showRightSidebar && rightSidebar && (
43
- <div className="w-[300px] border-l border-neutral-800 overflow-hidden flex-shrink-0">
44
- {rightSidebar}
45
- </div>
56
+ {/* Optional bottom panel */}
57
+ {bottomPanel && (
58
+ <div className="border-t border-neutral-960">{bottomPanel}</div>
46
59
  )}
47
60
  </div>
48
-
49
- {/* Optional bottom panel */}
50
- {bottomPanel && (
51
- <div className="border-t border-neutral-800">{bottomPanel}</div>
52
- )}
53
- </div>
61
+ </AccentColorProvider>
54
62
  )
55
63
  }
@@ -1,6 +1,8 @@
1
1
  import { useState, useRef, useLayoutEffect } from 'react'
2
2
  import { IconButton, type ActionItem } from './icon-button.tsx'
3
3
  import { Input } from './input.tsx'
4
+ import { useAccentColor } from '../lib/accent-context.ts'
5
+ import type { FormColor } from '../lib/form-colors.ts'
4
6
 
5
7
  export interface EditorPlaceholderCardProps {
6
8
  /** Placeholder name (without braces) */
@@ -14,7 +16,7 @@ export interface EditorPlaceholderCardProps {
14
16
  /** Label for the value section (default: "Value:") */
15
17
  valueLabel?: string
16
18
  /** Color scheme */
17
- accentColor?: 'purple' | 'blue' | 'neutral' | 'sky'
19
+ accentColor?: FormColor
18
20
  /** Action buttons to show on the right (e.g., edit/delete for settings) */
19
21
  actions?: ActionItem[]
20
22
  /** Show copy button that copies the {{PLACEHOLDER}} syntax */
@@ -27,23 +29,22 @@ export interface EditorPlaceholderCardProps {
27
29
  className?: string
28
30
  }
29
31
 
30
- const COLORS = {
31
- purple: {
32
- name: 'text-purple-400',
33
- nameBg: 'bg-purple-500/10',
34
- },
35
- blue: {
36
- name: 'text-blue-400',
37
- nameBg: 'bg-blue-500/10',
38
- },
39
- neutral: {
40
- name: 'text-neutral-400',
41
- nameBg: 'bg-neutral-500/10',
42
- },
43
- sky: {
44
- name: 'text-sky-400',
45
- nameBg: 'bg-sky-500/10',
46
- },
32
+ const COLORS: Record<FormColor, { name: string; nameBg: string }> = {
33
+ blue: { name: 'text-blue-400', nameBg: 'bg-blue-500/10' },
34
+ green: { name: 'text-green-400', nameBg: 'bg-green-500/10' },
35
+ red: { name: 'text-red-400', nameBg: 'bg-red-500/10' },
36
+ orange: { name: 'text-orange-400', nameBg: 'bg-orange-500/10' },
37
+ cyan: { name: 'text-cyan-400', nameBg: 'bg-cyan-500/10' },
38
+ yellow: { name: 'text-yellow-400', nameBg: 'bg-yellow-500/10' },
39
+ purple: { name: 'text-purple-400', nameBg: 'bg-purple-500/10' },
40
+ indigo: { name: 'text-indigo-400', nameBg: 'bg-indigo-500/10' },
41
+ emerald: { name: 'text-emerald-400', nameBg: 'bg-emerald-500/10' },
42
+ amber: { name: 'text-amber-400', nameBg: 'bg-amber-500/10' },
43
+ violet: { name: 'text-violet-400', nameBg: 'bg-violet-500/10' },
44
+ neutral: { name: 'text-neutral-400', nameBg: 'bg-neutral-500/10' },
45
+ sky: { name: 'text-sky-400', nameBg: 'bg-sky-500/10' },
46
+ pink: { name: 'text-pink-400', nameBg: 'bg-pink-500/10' },
47
+ teal: { name: 'text-teal-400', nameBg: 'bg-teal-500/10' },
47
48
  }
48
49
 
49
50
  export function EditorPlaceholderCard({
@@ -52,20 +53,22 @@ export function EditorPlaceholderCard({
52
53
  value,
53
54
  required = false,
54
55
  valueLabel = 'Value:',
55
- accentColor = 'purple',
56
+ accentColor,
56
57
  actions,
57
58
  showCopyPlaceholder = false,
58
59
  showCopyValue = false,
59
60
  hideValue = false,
60
61
  className = '',
61
62
  }: EditorPlaceholderCardProps) {
63
+ const contextAccent = useAccentColor()
64
+ const effectiveColor = accentColor ?? contextAccent ?? 'purple'
62
65
  const [isExpanded, setIsExpanded] = useState(false)
63
66
  const [isPlaceholderCopied, setIsPlaceholderCopied] = useState(false)
64
67
  const [isValueCopied, setIsValueCopied] = useState(false)
65
68
  const [isOverflowing, setIsOverflowing] = useState(false)
66
69
  const valueRef = useRef<HTMLDivElement>(null)
67
70
 
68
- const colors = COLORS[accentColor]
71
+ const colors = COLORS[effectiveColor]
69
72
  const hasValue = !!value
70
73
 
71
74
  // Check if content overflows (truncated) - sync state with DOM measurement
@@ -123,7 +126,7 @@ export function EditorPlaceholderCard({
123
126
  icon={isPlaceholderCopied ? 'check' : 'copy'}
124
127
  onClick={handleCopyPlaceholder}
125
128
  size="sm"
126
- color={isPlaceholderCopied ? 'green' : 'neutral'}
129
+ accentColor={isPlaceholderCopied ? 'green' : 'neutral'}
127
130
  tooltip={{ description: `Copy {{${name}}}` }}
128
131
  tooltipPosition="left"
129
132
  />
@@ -133,7 +136,7 @@ export function EditorPlaceholderCard({
133
136
  icon={isValueCopied ? 'check' : 'copy'}
134
137
  onClick={handleCopyValue}
135
138
  size="sm"
136
- color={isValueCopied ? 'green' : 'neutral'}
139
+ accentColor={isValueCopied ? 'green' : 'neutral'}
137
140
  tooltip={{ description: 'Copy value to clipboard' }}
138
141
  tooltipPosition="left"
139
142
  />
@@ -156,7 +159,7 @@ export function EditorPlaceholderCard({
156
159
  readOnly
157
160
  size="xs"
158
161
  variant="filled"
159
- color="neutral"
162
+ accentColor="neutral"
160
163
 
161
164
  />
162
165
  </div>
@@ -170,13 +173,14 @@ export function EditorPlaceholderCard({
170
173
  icon={isExpanded ? 'chevron-up' : 'chevron-down'}
171
174
  onClick={() => setIsExpanded(!isExpanded)}
172
175
  size="xss"
176
+ accentColor="neutral"
173
177
  tooltip={{ description: isExpanded ? 'Show less' : 'Show more' }}
174
178
  />
175
179
  )}
176
180
  </div>
177
181
  <div
178
182
  ref={valueRef}
179
- className={`mt-1.5 px-2 py-1.5 bg-neutral-800/50 rounded text-sm text-neutral-400 font-mono ${
183
+ className={`mt-1.5 px-2 py-1.5 bg-neutral-960/50 rounded text-sm text-neutral-400 font-mono ${
180
184
  isExpanded
181
185
  ? 'whitespace-pre-wrap break-all max-h-[190px] overflow-y-auto'
182
186
  : 'truncate'
@@ -2,6 +2,7 @@ import { useState } from 'react'
2
2
  import { IconButton, type ActionItem } from './icon-button.tsx'
3
3
  import { Label } from './label.tsx'
4
4
  import { ConfirmModal } from './modal.tsx'
5
+ import type { FormColor } from '../lib/form-colors.ts'
5
6
 
6
7
  export interface EditorToolbarProps {
7
8
  /** Optional title displayed in the toolbar */
@@ -31,6 +32,7 @@ export interface EditorToolbarProps {
31
32
  leftActions?: ActionItem[]
32
33
  /** Optional: Additional action buttons on the right side (before save) */
33
34
  rightActions?: ActionItem[]
35
+ accentColor?: FormColor
34
36
  }
35
37
 
36
38
  export function EditorToolbar({
@@ -49,6 +51,7 @@ export function EditorToolbar({
49
51
  hasError = false,
50
52
  leftActions,
51
53
  rightActions,
54
+ accentColor: _accentColor,
52
55
  }: EditorToolbarProps) {
53
56
  const [showConfirmDialog, setShowConfirmDialog] = useState(false)
54
57
 
@@ -69,7 +72,7 @@ export function EditorToolbar({
69
72
 
70
73
  return (
71
74
  <>
72
- <div className="flex items-center justify-between px-4 py-1.5 bg-neutral-900 border-b border-neutral-800">
75
+ <div className="flex items-center justify-between px-4 py-1.5 bg-neutral-980 border-b border-neutral-960">
73
76
  {/* Left side */}
74
77
  <div className="flex items-center gap-2">
75
78
  {(title || description) && (
@@ -85,7 +88,7 @@ export function EditorToolbar({
85
88
  {isDirty && (
86
89
  <Label
87
90
  text="modified"
88
- color="yellow"
91
+ accentColor="yellow"
89
92
  icon="pencil"
90
93
  size="xs"
91
94
  tooltip={{ description: 'File has unsaved changes' }}
@@ -101,7 +104,7 @@ export function EditorToolbar({
101
104
  icon="rotate"
102
105
  onClick={handleResetClick}
103
106
  size="sm"
104
- color="orange"
107
+ accentColor="orange"
105
108
  tooltip={resetTooltip}
106
109
  />
107
110
  )}
@@ -112,7 +115,7 @@ export function EditorToolbar({
112
115
  onClick={onSave}
113
116
  disabled={!isDirty || isSaving || hasError}
114
117
  size="sm"
115
- color={isDirty && !hasError ? 'amber' : 'neutral'}
118
+ accentColor={isDirty && !hasError ? 'green' : 'neutral'}
116
119
  status={isSaving ? 'loading' : undefined}
117
120
  tooltip={{ description: 'Save changes to file' }}
118
121
  />
@@ -6,10 +6,12 @@
6
6
  * Used inside ActionDialog as the mandatory execution details section.
7
7
  */
8
8
 
9
- import { AlertTriangle, Info } from 'lucide-react'
9
+ import { AlertTriangle } from 'lucide-react'
10
10
  import { Checkbox } from './checkbox.tsx'
11
11
  import { cn } from '../lib/cn.ts'
12
12
  import type { DetailRow } from './detail-section.tsx'
13
+ import type { FormColor } from '../lib/form-colors.ts'
14
+ import { useAccentColor, AccentColorProvider } from '../lib/accent-context.ts'
13
15
 
14
16
  export interface ExecutionDetailsPanelProps {
15
17
  details: DetailRow[]
@@ -19,6 +21,7 @@ export interface ExecutionDetailsPanelProps {
19
21
  /** Warning message shown below the toggle */
20
22
  warningMessage?: string
21
23
  className?: string
24
+ accentColor?: FormColor
22
25
  }
23
26
 
24
27
  export function ExecutionDetailsPanel({
@@ -27,16 +30,17 @@ export function ExecutionDetailsPanel({
27
30
  onAllowDirectEditsChange,
28
31
  warningMessage,
29
32
  className,
33
+ accentColor: accentColorProp,
30
34
  }: ExecutionDetailsPanelProps) {
35
+ const contextAccent = useAccentColor()
36
+ const effectiveColor = accentColorProp ?? contextAccent ?? 'blue'
31
37
  const showToggle = onAllowDirectEditsChange !== undefined
32
38
 
33
39
  return (
40
+ <AccentColorProvider value={effectiveColor}>
34
41
  <div className={cn('space-y-3', className)}>
35
42
  {/* Header */}
36
- <div className="flex items-center gap-2">
37
- <Info className="w-4 h-4 text-neutral-500" />
38
- <span className="font-medium text-neutral-400 text-md">Execution Details</span>
39
- </div>
43
+ <div className="font-medium text-neutral-400 text-md">Execution Details:</div>
40
44
 
41
45
  {/* Direct edits toggle */}
42
46
  {showToggle && (
@@ -84,5 +88,6 @@ export function ExecutionDetailsPanel({
84
88
  </div>
85
89
  )}
86
90
  </div>
91
+ </AccentColorProvider>
87
92
  )
88
93
  }
@@ -328,14 +328,14 @@ export function FileStructureSection({
328
328
  }
329
329
 
330
330
  const treePanel = (
331
- <div className={`flex flex-col bg-neutral-900 border ${ACCENT_BORDER[accentColor]} rounded-lg overflow-hidden ${variant === 'split' && effectiveFilePath ? 'w-1/3 shrink-0' : 'flex-1'}`}>
331
+ <div className={`flex flex-col bg-neutral-980 border ${ACCENT_BORDER[accentColor]} rounded-lg overflow-hidden ${variant === 'split' && effectiveFilePath ? 'w-1/3 shrink-0' : 'flex-1'}`}>
332
332
  <div className={`flex items-center px-3 py-2 border-b ${ACCENT_BORDER[accentColor]} shrink-0 gap-2 min-w-0`}>
333
333
  <FolderTree className={`w-3.5 h-3.5 shrink-0 ${ACCENT_ICON[accentColor]}`} />
334
334
  <span className="text-sm text-neutral-200 truncate flex-1">Files</span>
335
335
  <CollapseButton
336
336
  collapsed={allCollapsed}
337
337
  onToggle={() => setExpandedPaths(allCollapsed ? new Set(allDirPaths) : new Set())}
338
- color={accentColor}
338
+ accentColor={accentColor}
339
339
  />
340
340
  </div>
341
341
  <div className={`${variant === 'split' ? 'flex-1 overflow-y-auto' : ''} p-3`}>
@@ -353,7 +353,7 @@ export function FileStructureSection({
353
353
  )
354
354
 
355
355
  const previewPanel = effectiveFilePath ? (
356
- <div className={`flex-1 flex flex-col bg-neutral-900 border ${ACCENT_BORDER[accentColor]} rounded-lg overflow-hidden`}>
356
+ <div className={`flex-1 flex flex-col bg-neutral-980 border ${ACCENT_BORDER[accentColor]} rounded-lg overflow-hidden`}>
357
357
  <div className={`flex items-center px-3 py-2 border-b ${ACCENT_BORDER[accentColor]} shrink-0 gap-2 min-w-0`}>
358
358
  <FileCode className={`w-3.5 h-3.5 shrink-0 ${ACCENT_ICON[accentColor]}`} />
359
359
  <span className="text-sm text-neutral-200 truncate flex-1">{selectedFileName}</span>
@@ -1,5 +1,6 @@
1
1
  import { FileCode, Folder, FolderOpen, ChevronRight, ChevronDown } from 'lucide-react'
2
2
  import { ACCENT_ICON, type AccentColor } from '../lib/form-colors.ts'
3
+ import { useAccentColor } from '../lib/accent-context.ts'
3
4
 
4
5
  export interface FileTreeNode {
5
6
  name: string
@@ -57,11 +58,12 @@ export function collectDirPaths(nodes: FileTreeNode[], rootName?: string, prefix
57
58
  return paths
58
59
  }
59
60
 
60
- export function FileTree({ nodes, rootName, selectedPath, onSelectFile, prefix = '', expandedPaths, onTogglePath, accentColor = 'blue' }: FileTreeProps) {
61
+ export function FileTree({ nodes, rootName, selectedPath, onSelectFile, prefix = '', expandedPaths, onTogglePath, accentColor: accentColorProp }: FileTreeProps) {
62
+ const accentColor = accentColorProp ?? useAccentColor() ?? 'blue'
61
63
  if (rootName) {
62
64
  const rootNode: FileTreeNode = { name: rootName, type: 'directory', children: nodes }
63
65
  return (
64
- <ul className="space-y-0.5">
66
+ <ul role="tree" className="space-y-0.5">
65
67
  <FileTreeNodeItem
66
68
  node={rootNode}
67
69
  path={rootName}
@@ -76,7 +78,7 @@ export function FileTree({ nodes, rootName, selectedPath, onSelectFile, prefix =
76
78
  }
77
79
 
78
80
  return (
79
- <ul className="space-y-0.5">
81
+ <ul role="tree" className="space-y-0.5">
80
82
  {nodes.filter(nodeHasFiles).map((node) => {
81
83
  const fullPath = prefix ? `${prefix}/${node.name}` : node.name
82
84
  return (
@@ -120,7 +122,7 @@ function FileTreeNodeItem({ node, path, selectedPath, onSelectFile, expandedPath
120
122
  : `${base} cursor-pointer text-white hover:bg-neutral-700/50 hover:text-neutral-200`
121
123
 
122
124
  return (
123
- <li>
125
+ <li role="treeitem" aria-expanded={isDir ? expanded : undefined} aria-selected={isSelected}>
124
126
  <button
125
127
  onClick={isDir ? () => onTogglePath(path) : () => onSelectFile(path)}
126
128
  className={rowClass}
@@ -138,7 +140,7 @@ function FileTreeNodeItem({ node, path, selectedPath, onSelectFile, expandedPath
138
140
  <span className="truncate">{node.name}</span>
139
141
  </button>
140
142
  {isDir && expanded && node.children && (
141
- <ul className="ml-4 space-y-0.5">
143
+ <ul role="group" className="ml-4 space-y-0.5">
142
144
  {node.children.filter(nodeHasFiles).map((child) => {
143
145
  const childPath = `${path}/${child.name}`
144
146
  return (