@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.
- package/ai-manifest.json +35 -20
- package/components/composites/dashboard-list-item.tsx +172 -0
- package/components/composites/dashboard-panel.tsx +218 -0
- package/components/content/info-panel-primitives.tsx +9 -8
- package/components/diagrams/diagram-utils.tsx +2 -1
- package/components/hooks/use-dropdown-portal.ts +39 -0
- package/components/hooks/use-modal-behavior.ts +32 -3
- package/components/lib/accent-context.ts +10 -0
- package/components/lib/{ai-tools.tsx → coding-agents.tsx} +23 -8
- package/components/lib/custom-icons.tsx +37 -0
- package/components/lib/git-providers.tsx +39 -0
- package/components/lib/theme-engine.ts +59 -10
- package/components/lib/toolr-brand.tsx +23 -9
- package/components/sections/captured-issues/captured-issues-panel.tsx +17 -8
- package/components/sections/{ai-tools-paths/tools-paths-panel.tsx → coding-agent-paths/agent-paths-panel.tsx} +70 -62
- package/components/sections/coding-agent-paths/index.ts +37 -0
- package/components/sections/{ai-tools-paths → coding-agent-paths}/types.ts +28 -28
- package/components/sections/coding-agent-paths/use-agent-paths.ts +159 -0
- package/components/sections/golden-snapshots/file-diff-viewer.tsx +11 -10
- package/components/sections/golden-snapshots/golden-sync-panel.tsx +12 -3
- package/components/sections/golden-snapshots/snapshot-manager.tsx +9 -7
- package/components/sections/golden-snapshots/status-overview.tsx +8 -8
- package/components/sections/golden-snapshots/version-manager.tsx +6 -6
- package/components/sections/prompt-editor/file-type-tabbed-prompt-editor.tsx +3 -3
- package/components/sections/prompt-editor/index.ts +1 -1
- package/components/sections/prompt-editor/simulator-prompt-editor.tsx +13 -5
- package/components/sections/prompt-editor/tabbed-prompt-editor.tsx +18 -10
- package/components/sections/prompt-editor/types.ts +2 -2
- package/components/sections/report-bug/report-bug-form.tsx +12 -4
- package/components/sections/report-bug/screenshot-uploader.tsx +11 -3
- package/components/sections/snapshot-browser/snapshot-browser-panel.tsx +12 -4
- package/components/sections/snapshot-browser/snapshot-tree.tsx +5 -4
- package/components/sections/snapshot-browser/types.ts +1 -1
- package/components/sections/snippets-editor/snippets-editor.tsx +16 -9
- package/components/settings/SettingsHeader.tsx +2 -2
- package/components/settings/SettingsPanel.tsx +11 -3
- package/components/settings/SettingsTreeNav.tsx +15 -9
- package/components/ui/action-dialog.tsx +37 -35
- package/components/ui/ai-action-button.tsx +12 -11
- package/components/ui/ai-execution-action-buttons.tsx +13 -5
- package/components/ui/badge.tsx +17 -6
- package/components/ui/bottom-panel-header.tsx +9 -5
- package/components/ui/breadcrumb.tsx +14 -6
- package/components/ui/{extension-list-card.tsx → capability-list-card.tsx} +14 -6
- package/components/ui/checkbox.tsx +23 -14
- package/components/ui/collapsible-section.tsx +38 -28
- package/components/ui/confirm-badge.tsx +17 -6
- package/components/ui/cookie-consent.tsx +13 -7
- package/components/ui/detail-section.tsx +24 -16
- package/components/ui/detail-view-wrapper.tsx +30 -22
- package/components/ui/editor-placeholder-card.tsx +28 -24
- package/components/ui/editor-toolbar.tsx +7 -4
- package/components/ui/execution-details-panel.tsx +10 -5
- package/components/ui/file-structure-section.tsx +3 -3
- package/components/ui/file-tree.tsx +7 -5
- package/components/ui/files-panel.tsx +147 -27
- package/components/ui/filter-dropdown.tsx +88 -75
- package/components/ui/form-actions.tsx +21 -11
- package/components/ui/frontmatter-form-header.tsx +10 -2
- package/components/ui/icon-button.tsx +27 -14
- package/components/ui/input.tsx +15 -7
- package/components/ui/label.tsx +9 -5
- package/components/ui/layout-tab-bar.tsx +11 -9
- package/components/ui/modal.tsx +26 -8
- package/components/ui/nav-card.tsx +7 -4
- package/components/ui/navigation-bar.tsx +40 -12
- package/components/ui/number-input.tsx +14 -4
- package/components/ui/project-explorer.tsx +666 -0
- package/components/ui/registry-browser.tsx +12 -1
- package/components/ui/registry-card.tsx +49 -42
- package/components/ui/registry-detail.tsx +34 -11
- package/components/ui/resizable-textarea.tsx +18 -11
- package/components/ui/scope-badge.tsx +18 -11
- package/components/ui/segmented-toggle.tsx +7 -2
- package/components/ui/select.tsx +17 -11
- package/components/ui/selection-grid.tsx +40 -37
- package/components/ui/setting-row.tsx +6 -4
- package/components/ui/settings-card.tsx +12 -5
- package/components/ui/settings-info-box.tsx +9 -6
- package/components/ui/settings-section-title.tsx +14 -2
- package/components/ui/snapshot-card.tsx +10 -2
- package/components/ui/snippets-panel.tsx +4 -2
- package/components/ui/sort-dropdown.tsx +45 -32
- package/components/ui/status-card.tsx +9 -1
- package/components/ui/tab-bar.tsx +26 -13
- package/components/ui/toggle.tsx +31 -17
- package/components/ui/tooltip.tsx +14 -6
- package/dist/content.js +8 -8
- package/dist/diagrams.d.ts +0 -1
- package/dist/index.d.ts +431 -186
- package/dist/index.js +3119 -1724
- package/dist/tokens/primitives.css +28 -6
- package/dist/tokens/semantic.css +15 -15
- package/dist/tokens/theme.css +23 -0
- package/index.ts +25 -11
- package/package.json +9 -1
- package/tokens/primitives.css +28 -6
- package/tokens/semantic.css +15 -15
- package/tokens/theme.css +23 -0
- package/components/sections/ai-tools-paths/index.ts +0 -37
- 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
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<div className="flex items-center
|
|
30
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
42
|
-
{
|
|
43
|
-
<div className="
|
|
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?:
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
},
|
|
43
|
-
sky: {
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
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[
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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="
|
|
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-
|
|
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
|
-
|
|
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-
|
|
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
|
|
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 (
|