@toolr/ui-design 0.1.8 → 0.1.10

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/lib/accent-context.ts +10 -0
  8. package/components/lib/{ai-tools.tsx → coding-agents.tsx} +23 -8
  9. package/components/lib/custom-icons.tsx +37 -0
  10. package/components/lib/form-colors.ts +16 -16
  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 +10 -9
  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 +24 -30
  39. package/components/ui/ai-action-button.tsx +10 -7
  40. package/components/ui/ai-execution-action-buttons.tsx +13 -5
  41. package/components/ui/badge.tsx +7 -4
  42. package/components/ui/bottom-panel-header.tsx +9 -5
  43. package/components/ui/breadcrumb.tsx +13 -5
  44. package/components/ui/{extension-list-card.tsx → capability-list-card.tsx} +13 -5
  45. package/components/ui/checkbox.tsx +6 -3
  46. package/components/ui/collapsible-section.tsx +38 -29
  47. package/components/ui/confirm-badge.tsx +7 -4
  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 +7 -4
  55. package/components/ui/file-tree.tsx +3 -1
  56. package/components/ui/files-panel.tsx +147 -27
  57. package/components/ui/filter-dropdown.tsx +84 -74
  58. package/components/ui/form-actions.tsx +14 -6
  59. package/components/ui/frontmatter-form-header.tsx +10 -2
  60. package/components/ui/icon-button.tsx +22 -9
  61. package/components/ui/input.tsx +7 -4
  62. package/components/ui/label.tsx +5 -5
  63. package/components/ui/layout-tab-bar.tsx +7 -5
  64. package/components/ui/modal.tsx +18 -4
  65. package/components/ui/nav-card.tsx +6 -3
  66. package/components/ui/navigation-bar.tsx +164 -82
  67. package/components/ui/number-input.tsx +8 -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 +40 -17
  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 +5 -2
  75. package/components/ui/select.tsx +12 -9
  76. package/components/ui/selection-grid.tsx +36 -37
  77. package/components/ui/setting-row.tsx +2 -2
  78. package/components/ui/settings-card.tsx +10 -3
  79. package/components/ui/settings-info-box.tsx +9 -5
  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 +39 -29
  84. package/components/ui/status-card.tsx +9 -1
  85. package/components/ui/tab-bar.tsx +12 -9
  86. package/components/ui/toggle.tsx +13 -7
  87. package/components/ui/tooltip.tsx +9 -1
  88. package/dist/content.js +8 -8
  89. package/dist/diagrams.d.ts +0 -1
  90. package/dist/index.d.ts +427 -184
  91. package/dist/index.js +3098 -1761
  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 +1 -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
@@ -5,6 +5,8 @@ import { useModalBehavior } from '../hooks/use-modal-behavior.ts'
5
5
  import { IconButton, type ActionItem } from './icon-button.tsx'
6
6
  import { FormActions } from './form-actions.tsx'
7
7
  import type { ReactNode } from 'react'
8
+ import type { FormColor } from '../lib/form-colors.ts'
9
+ import { useAccentColor, AccentColorProvider } from '../lib/accent-context.ts'
8
10
 
9
11
  export type ModalKind = 'info' | 'warning' | 'error' | 'orange' | 'success'
10
12
  export type ModalSize = 'sm' | 'md' | 'lg' | 'xl'
@@ -57,9 +59,9 @@ function Modal({ isOpen, onClose, title, children, kind = 'info', size = 'md', h
57
59
  aria-modal="true"
58
60
  aria-labelledby={titleId}
59
61
  data-testid={testId}
60
- className={`relative bg-neutral-900 border border-neutral-700 rounded-xl shadow-lg ${SIZE_CLASSES[size]} w-full mx-4 overflow-hidden`}
62
+ className={`relative bg-neutral-980 border border-neutral-700 rounded-lg shadow-lg ${SIZE_CLASSES[size]} w-full mx-4 overflow-hidden`}
61
63
  >
62
- <div className="flex items-center gap-3 px-5 py-4 border-b border-neutral-800">
64
+ <div className="flex items-center gap-3 px-4 py-4 border-b border-neutral-960">
63
65
  {KIND_ICON[kind]}
64
66
  <h3 id={titleId} className="text-lg font-semibold text-white flex-1 min-w-0 truncate">{title}</h3>
65
67
  {headerActions?.map((a, i) => <IconButton key={i} {...a} />)}
@@ -68,13 +70,13 @@ function Modal({ isOpen, onClose, title, children, kind = 'info', size = 'md', h
68
70
  icon="x"
69
71
  onClick={onClose}
70
72
  size="sm"
71
- color="neutral"
73
+ accentColor="neutral"
72
74
  tooltip={{ description: 'Close this modal' }}
73
75
  testId="modal-close"
74
76
  />
75
77
  )}
76
78
  </div>
77
- <div className="px-5 py-4">{children}</div>
79
+ <div className="px-4 py-4">{children}</div>
78
80
  </div>
79
81
  </div>,
80
82
  document.body,
@@ -93,6 +95,7 @@ export interface ConfirmModalProps {
93
95
  confirmColor?: 'red' | 'blue' | 'orange' | 'yellow'
94
96
  isLoading?: boolean
95
97
  confirmDisabled?: boolean
98
+ accentColor?: FormColor
96
99
  }
97
100
 
98
101
  export function ConfirmModal({
@@ -107,7 +110,10 @@ export function ConfirmModal({
107
110
  confirmColor = 'blue',
108
111
  isLoading = false,
109
112
  confirmDisabled = false,
113
+ accentColor: accentColorProp,
110
114
  }: ConfirmModalProps) {
115
+ const contextAccent = useAccentColor()
116
+ const effectiveColor = accentColorProp ?? contextAccent ?? 'blue'
111
117
  const [isConfirming, setIsConfirming] = useState(false)
112
118
 
113
119
  const isDisabled = isLoading || isConfirming || confirmDisabled
@@ -126,6 +132,7 @@ export function ConfirmModal({
126
132
  }
127
133
 
128
134
  return (
135
+ <AccentColorProvider value={effectiveColor}>
129
136
  <Modal isOpen={isOpen} onClose={onClose} title={title} kind={kind} hideCloseButton>
130
137
  <div className="text-neutral-300 mb-6">
131
138
  {message}
@@ -154,6 +161,7 @@ export function ConfirmModal({
154
161
  confirmStatus={isInProgress ? 'loading' : undefined}
155
162
  />
156
163
  </Modal>
164
+ </AccentColorProvider>
157
165
  )
158
166
  }
159
167
 
@@ -163,6 +171,7 @@ export interface AlertModalProps {
163
171
  title: string
164
172
  message: string
165
173
  kind?: ModalKind
174
+ accentColor?: FormColor
166
175
  }
167
176
 
168
177
  export function AlertModal({
@@ -171,8 +180,12 @@ export function AlertModal({
171
180
  title,
172
181
  message,
173
182
  kind = 'info',
183
+ accentColor: accentColorProp,
174
184
  }: AlertModalProps) {
185
+ const contextAccent = useAccentColor()
186
+ const effectiveColor = accentColorProp ?? contextAccent ?? 'blue'
175
187
  return (
188
+ <AccentColorProvider value={effectiveColor}>
176
189
  <Modal isOpen={isOpen} onClose={onClose} title={title} kind={kind} hideCloseButton>
177
190
  <div className="text-neutral-300 mb-6">{message}</div>
178
191
  <FormActions
@@ -182,5 +195,6 @@ export function AlertModal({
182
195
  confirmTooltip="Dismiss this alert"
183
196
  />
184
197
  </Modal>
198
+ </AccentColorProvider>
185
199
  )
186
200
  }
@@ -3,6 +3,7 @@
3
3
  import { iconMap, type IconName } from './icon-button.tsx'
4
4
  import { Label, type LabelColor } from './label.tsx'
5
5
  import { cn } from '../lib/cn.ts'
6
+ import type { FormColor } from '../lib/form-colors.ts'
6
7
 
7
8
  export interface NavCardProps {
8
9
  title: string
@@ -11,11 +12,12 @@ export interface NavCardProps {
11
12
  /** Custom icon component. Takes precedence over icon name. */
12
13
  IconComponent?: React.ComponentType<{ className?: string }>
13
14
  iconColor?: string
14
- label?: { text: string; color: LabelColor; tooltip: { description: string } }
15
+ label?: { text: string; color: LabelColor; icon?: IconName; tooltip: { description: string } }
15
16
  stats?: string
16
17
  onClick?: () => void
17
18
  disabled?: boolean
18
19
  className?: string
20
+ accentColor?: FormColor
19
21
  }
20
22
 
21
23
  export function NavCard({
@@ -29,6 +31,7 @@ export function NavCard({
29
31
  onClick,
30
32
  disabled = false,
31
33
  className,
34
+ accentColor: _accentColor,
32
35
  }: NavCardProps) {
33
36
  const Icon = IconComponent ?? (icon ? iconMap[icon] : undefined)
34
37
 
@@ -38,7 +41,7 @@ export function NavCard({
38
41
  onClick={disabled ? undefined : onClick}
39
42
  disabled={disabled}
40
43
  className={cn(
41
- 'relative w-full text-left rounded-lg border border-neutral-700 bg-neutral-800 p-4 transition-all duration-200 cursor-pointer',
44
+ 'relative w-full text-left rounded-lg border border-neutral-700 bg-neutral-960 p-4 transition-all duration-200 cursor-pointer',
42
45
  !disabled && 'hover:-translate-y-0.5 hover:border-neutral-600 hover:bg-neutral-700',
43
46
  disabled && 'opacity-50 cursor-not-allowed',
44
47
  className,
@@ -46,7 +49,7 @@ export function NavCard({
46
49
  >
47
50
  {label && (
48
51
  <span className="absolute top-3 right-3">
49
- <Label text={label.text} color={label.color} size="xs" tooltip={label.tooltip} />
52
+ <Label text={label.text} accentColor={label.color} icon={label.icon} size="xs" tooltip={label.tooltip} />
50
53
  </span>
51
54
  )}
52
55
 
@@ -6,6 +6,7 @@ import type { LucideIcon } from 'lucide-react'
6
6
  import { iconMap, type IconName } from './icon-button.tsx'
7
7
  import type { BreadcrumbSegment } from './breadcrumb.tsx'
8
8
  import { ACCENT_NAV, type AccentColor } from '../lib/form-colors.ts'
9
+ import { useAccentColor } from '../lib/accent-context.ts'
9
10
  import { cn } from '../lib/cn.ts'
10
11
  import { useClickOutside } from '../hooks/use-click-outside.ts'
11
12
 
@@ -17,10 +18,14 @@ export interface NavigationBarProps {
17
18
  onForward?: () => void
18
19
  showHistory?: boolean
19
20
  historyEntries?: BreadcrumbSegment[][]
21
+ currentHistoryIndex?: number
20
22
  onHistorySelect?: (index: number) => void
21
23
  leadingAction?: { icon: IconName; onClick?: () => void }
22
24
  separator?: 'chevron' | 'slash' | 'dot'
23
25
  size?: 'xss' | 'xs' | 'sm' | 'md' | 'lg'
26
+ /** 'bar' (default): all-in-one bordered box. 'header': nav controls left-aligned, breadcrumb absolutely centered. */
27
+ layout?: 'bar' | 'header'
28
+ accentColor?: AccentColor
24
29
  className?: string
25
30
  }
26
31
 
@@ -33,12 +38,32 @@ const sizeConfig = {
33
38
  }
34
39
 
35
40
 
36
- function NavButton({ icon: Icon, onClick, disabled, size, active }: {
41
+ // Accent color classes for NavButton same pattern as IconButton's colorClasses
42
+ const navColorClasses: Record<AccentColor, { text: string; border: string; hover: string; active: string }> = {
43
+ green: { text: 'text-green-400', border: 'border-green-500/30', hover: 'hover:bg-green-500/20 hover:border-green-500/40 hover:text-green-300', active: 'bg-green-500/20 text-green-300 border-green-500/40' },
44
+ red: { text: 'text-red-400', border: 'border-red-500/30', hover: 'hover:bg-red-500/20 hover:border-red-500/40 hover:text-red-300', active: 'bg-red-500/20 text-red-300 border-red-500/40' },
45
+ blue: { text: 'text-blue-400', border: 'border-blue-500/30', hover: 'hover:bg-blue-500/20 hover:border-blue-500/40 hover:text-blue-300', active: 'bg-blue-500/20 text-blue-300 border-blue-500/40' },
46
+ orange: { text: 'text-orange-400', border: 'border-orange-500/30', hover: 'hover:bg-orange-500/20 hover:border-orange-500/40 hover:text-orange-300', active: 'bg-orange-500/20 text-orange-300 border-orange-500/40' },
47
+ cyan: { text: 'text-cyan-400', border: 'border-cyan-500/30', hover: 'hover:bg-cyan-500/20 hover:border-cyan-500/40 hover:text-cyan-300', active: 'bg-cyan-500/20 text-cyan-300 border-cyan-500/40' },
48
+ yellow: { text: 'text-yellow-400', border: 'border-yellow-500/30', hover: 'hover:bg-yellow-500/20 hover:border-yellow-500/40 hover:text-yellow-300', active: 'bg-yellow-500/20 text-yellow-300 border-yellow-500/40' },
49
+ purple: { text: 'text-purple-400', border: 'border-purple-500/30', hover: 'hover:bg-purple-500/20 hover:border-purple-500/40 hover:text-purple-300', active: 'bg-purple-500/20 text-purple-300 border-purple-500/40' },
50
+ indigo: { text: 'text-indigo-400', border: 'border-indigo-500/30', hover: 'hover:bg-indigo-500/20 hover:border-indigo-500/40 hover:text-indigo-300', active: 'bg-indigo-500/20 text-indigo-300 border-indigo-500/40' },
51
+ emerald: { text: 'text-emerald-400', border: 'border-emerald-500/30', hover: 'hover:bg-emerald-500/20 hover:border-emerald-500/40 hover:text-emerald-300', active: 'bg-emerald-500/20 text-emerald-300 border-emerald-500/40' },
52
+ amber: { text: 'text-amber-400', border: 'border-amber-500/30', hover: 'hover:bg-amber-500/20 hover:border-amber-500/40 hover:text-amber-300', active: 'bg-amber-500/20 text-amber-300 border-amber-500/40' },
53
+ violet: { text: 'text-violet-400', border: 'border-violet-500/30', hover: 'hover:bg-violet-500/20 hover:border-violet-500/40 hover:text-violet-300', active: 'bg-violet-500/20 text-violet-300 border-violet-500/40' },
54
+ neutral: { text: 'text-neutral-400', border: 'border-neutral-500/30', hover: 'hover:bg-neutral-500/20 hover:border-neutral-500/40 hover:text-neutral-300', active: 'bg-neutral-500/20 text-neutral-300 border-neutral-500/40' },
55
+ sky: { text: 'text-sky-400', border: 'border-sky-500/30', hover: 'hover:bg-sky-500/20 hover:border-sky-500/40 hover:text-sky-300', active: 'bg-sky-500/20 text-sky-300 border-sky-500/40' },
56
+ pink: { text: 'text-pink-400', border: 'border-pink-500/30', hover: 'hover:bg-pink-500/20 hover:border-pink-500/40 hover:text-pink-300', active: 'bg-pink-500/20 text-pink-300 border-pink-500/40' },
57
+ teal: { text: 'text-teal-400', border: 'border-teal-500/30', hover: 'hover:bg-teal-500/20 hover:border-teal-500/40 hover:text-teal-300', active: 'bg-teal-500/20 text-teal-300 border-teal-500/40' },
58
+ }
59
+
60
+ function NavButton({ icon: Icon, onClick, disabled, size, active, colorStyle }: {
37
61
  icon: LucideIcon
38
62
  onClick?: () => void
39
63
  disabled?: boolean
40
64
  size: keyof typeof sizeConfig
41
65
  active?: boolean
66
+ colorStyle: typeof navColorClasses[AccentColor]
42
67
  }) {
43
68
  const s = sizeConfig[size]
44
69
  return (
@@ -52,8 +77,8 @@ function NavButton({ icon: Icon, onClick, disabled, size, active }: {
52
77
  disabled
53
78
  ? 'text-neutral-600 cursor-not-allowed'
54
79
  : active
55
- ? 'text-white bg-neutral-700/50 border border-neutral-600 cursor-pointer'
56
- : 'text-neutral-400 border border-neutral-700/50 hover:text-white hover:bg-neutral-700/50 cursor-pointer',
80
+ ? cn('border cursor-pointer', colorStyle.active)
81
+ : cn('border cursor-pointer', colorStyle.text, colorStyle.border, colorStyle.hover),
57
82
  )}
58
83
  >
59
84
  <Icon className={s.navIcon} />
@@ -91,12 +116,19 @@ export function NavigationBar({
91
116
  onForward,
92
117
  showHistory = false,
93
118
  historyEntries,
119
+ currentHistoryIndex,
94
120
  onHistorySelect,
95
121
  leadingAction,
96
122
  separator = 'chevron',
97
123
  size = 'sm',
124
+ layout = 'bar',
125
+ accentColor,
98
126
  className,
99
127
  }: NavigationBarProps) {
128
+ const contextAccentColor = useAccentColor()
129
+ const effectiveColor = accentColor ?? contextAccentColor ?? 'neutral'
130
+ const colorStyle = navColorClasses[effectiveColor]
131
+
100
132
  const s = sizeConfig[size]
101
133
  const hasNav = !!(onBack || onForward)
102
134
  const LeadIcon = leadingAction ? iconMap[leadingAction.icon] : null
@@ -108,12 +140,134 @@ export function NavigationBar({
108
140
 
109
141
  const hasHistoryEntries = historyEntries && historyEntries.length > 0
110
142
 
143
+ const renderSegments = () => segments.map((segment, index) => {
144
+ const isLast = index === segments.length - 1
145
+ const isClickable = !isLast && !!segment.onClick
146
+ const colors = segment.color && ACCENT_NAV[segment.color as AccentColor] ? ACCENT_NAV[segment.color as AccentColor] : null
147
+
148
+ return (
149
+ <div key={segment.id} className="flex items-center gap-1 min-w-0">
150
+ {index > 0 && <SegmentSeparator type={separator} size={size} />}
151
+ {isClickable ? (
152
+ <button
153
+ type="button"
154
+ onClick={segment.onClick}
155
+ className={cn(
156
+ 'flex items-center gap-1.5 px-2 py-0.5 rounded-md transition-colors cursor-pointer min-w-0',
157
+ s.text,
158
+ 'font-medium hover:text-white',
159
+ colors ? [colors.text, `hover:${colors.bg}`] : ['text-neutral-300', 'hover:bg-neutral-700/50'],
160
+ )}
161
+ >
162
+ {segment.icon && <SegmentIcon icon={segment.icon} color={segment.color} size={size} />}
163
+ <span className="truncate max-w-[200px]">{segment.label}</span>
164
+ </button>
165
+ ) : (
166
+ <div
167
+ className={cn(
168
+ 'flex items-center gap-1.5 px-2 py-0.5 rounded-md min-w-0',
169
+ s.text,
170
+ isLast
171
+ ? ['font-medium bg-neutral-700/50', colors ? colors.text : 'text-white']
172
+ : ['font-medium', colors ? colors.text : 'text-neutral-300'],
173
+ )}
174
+ >
175
+ {segment.icon && <SegmentIcon icon={segment.icon} color={segment.color} size={size} />}
176
+ <span className="truncate max-w-[200px]">{segment.label}</span>
177
+ </div>
178
+ )}
179
+ </div>
180
+ )
181
+ })
182
+
183
+ const renderHistoryDropdown = () => {
184
+ if (!historyOpen || !hasHistoryEntries || !historyEntries) return null
185
+ const entries = layout === 'header' ? [...historyEntries].reverse() : historyEntries
186
+ return (
187
+ <div className="absolute left-0 top-full mt-1 w-max min-w-[200px] max-w-[420px] bg-black/80 backdrop-blur-sm border border-neutral-700 rounded-lg shadow-lg z-50">
188
+ <div className="px-3 py-1.5 border-b border-neutral-700/50">
189
+ <p className="text-sm font-medium text-neutral-500">History{layout === 'header' && ` (${historyEntries.length})`}</p>
190
+ </div>
191
+ <div className="max-h-[300px] overflow-y-auto py-1">
192
+ {entries.map((entry, entryIdx) => {
193
+ const actualIdx = layout === 'header' ? historyEntries.length - 1 - entryIdx : entryIdx
194
+ const isCurrent = currentHistoryIndex !== undefined && actualIdx === currentHistoryIndex
195
+ return (
196
+ <button
197
+ key={actualIdx}
198
+ type="button"
199
+ onClick={() => {
200
+ onHistorySelect?.(actualIdx)
201
+ setHistoryOpen(false)
202
+ }}
203
+ className={cn(
204
+ 'w-full px-3 py-1.5 flex items-center gap-1 text-left transition-colors cursor-pointer',
205
+ isCurrent ? 'bg-green-500/10' : 'hover:bg-neutral-800',
206
+ )}
207
+ >
208
+ {entry.map((seg, segIdx) => (
209
+ <span key={seg.id} className="flex items-center gap-1 min-w-0">
210
+ {segIdx > 0 && <ChevronRight className="w-2.5 h-2.5 text-neutral-600 flex-shrink-0" />}
211
+ {seg.icon && <SegmentIcon icon={seg.icon} color={seg.color} size="xs" />}
212
+ <span className={cn(
213
+ 'text-sm truncate',
214
+ seg.color && ACCENT_NAV[seg.color as AccentColor] ? ACCENT_NAV[seg.color as AccentColor].text : 'text-neutral-300',
215
+ )}>
216
+ {seg.label}
217
+ </span>
218
+ </span>
219
+ ))}
220
+ {isCurrent && <span className="ml-auto pl-4 text-xs text-green-400 flex-shrink-0">Current</span>}
221
+ </button>
222
+ )
223
+ })}
224
+ </div>
225
+ </div>
226
+ )
227
+ }
228
+
229
+ if (layout === 'header') {
230
+ return (
231
+ <nav className={cn('relative flex items-center h-full', className)}>
232
+ {/* Left: nav controls */}
233
+ <div className="flex items-center gap-2 flex-shrink-0">
234
+ {hasNav && (
235
+ <>
236
+ <NavButton icon={ChevronLeft} onClick={onBack} disabled={!canGoBack} size={size} colorStyle={colorStyle} />
237
+ <NavButton icon={ChevronRight} onClick={onForward} disabled={!canGoForward} size={size} colorStyle={colorStyle} />
238
+ </>
239
+ )}
240
+ {showHistory && (
241
+ <div className="relative" ref={historyRef}>
242
+ <NavButton
243
+ icon={History}
244
+ onClick={() => setHistoryOpen(o => !o)}
245
+ disabled={!hasHistoryEntries}
246
+ size={size}
247
+ active={historyOpen}
248
+ colorStyle={colorStyle}
249
+ />
250
+ {renderHistoryDropdown()}
251
+ </div>
252
+ )}
253
+ </div>
254
+
255
+ {/* Center: breadcrumb absolutely centered */}
256
+ <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
257
+ <div className={cn('pointer-events-auto flex items-center gap-1', s.px, s.py, 'bg-neutral-960/50 rounded-lg')}>
258
+ {renderSegments()}
259
+ </div>
260
+ </div>
261
+ </nav>
262
+ )
263
+ }
264
+
111
265
  return (
112
266
  <nav className={cn('flex items-center', className)}>
113
- <div className={cn('flex items-center gap-1', s.px, s.py, 'bg-neutral-800/50 border border-neutral-700/50 rounded-lg')}>
267
+ <div className={cn('flex items-center gap-1', s.px, s.py, 'bg-neutral-960/50 border rounded-lg', colorStyle.border)}>
114
268
  {leadingAction && LeadIcon && (
115
269
  <>
116
- <NavButton icon={LeadIcon} onClick={leadingAction.onClick} size={size} />
270
+ <NavButton icon={LeadIcon} onClick={leadingAction.onClick} size={size} colorStyle={colorStyle} />
117
271
  <Divider size={size} />
118
272
  </>
119
273
  )}
@@ -121,8 +275,8 @@ export function NavigationBar({
121
275
  {hasNav && (
122
276
  <>
123
277
  <div className="flex items-center gap-0.5">
124
- <NavButton icon={ChevronLeft} onClick={onBack} disabled={!canGoBack} size={size} />
125
- <NavButton icon={ChevronRight} onClick={onForward} disabled={!canGoForward} size={size} />
278
+ <NavButton icon={ChevronLeft} onClick={onBack} disabled={!canGoBack} size={size} colorStyle={colorStyle} />
279
+ <NavButton icon={ChevronRight} onClick={onForward} disabled={!canGoForward} size={size} colorStyle={colorStyle} />
126
280
  </div>
127
281
  <Divider size={size} />
128
282
  </>
@@ -137,87 +291,15 @@ export function NavigationBar({
137
291
  disabled={!hasHistoryEntries}
138
292
  size={size}
139
293
  active={historyOpen}
294
+ colorStyle={colorStyle}
140
295
  />
141
- {historyOpen && hasHistoryEntries && (
142
- <div className="absolute left-0 top-full mt-1 w-max min-w-[200px] max-w-[420px] bg-neutral-800 border border-neutral-700 rounded-lg shadow-lg z-50">
143
- <div className="px-3 py-1.5 border-b border-neutral-700/50">
144
- <p className="text-sm font-medium text-neutral-500">History</p>
145
- </div>
146
- <div className="max-h-[300px] overflow-y-auto py-1">
147
- {historyEntries.map((entry, i) => (
148
- <button
149
- key={i}
150
- type="button"
151
- onClick={() => {
152
- onHistorySelect?.(i)
153
- setHistoryOpen(false)
154
- }}
155
- className="w-full px-3 py-1.5 flex items-center gap-1 text-left hover:bg-neutral-800 transition-colors cursor-pointer"
156
- >
157
- {entry.map((seg, segIdx) => (
158
- <span key={seg.id} className="flex items-center gap-1 min-w-0">
159
- {segIdx > 0 && <ChevronRight className="w-2.5 h-2.5 text-neutral-600 flex-shrink-0" />}
160
- {seg.icon && <SegmentIcon icon={seg.icon} color={seg.color} size="xs" />}
161
- <span className={cn(
162
- 'text-sm truncate',
163
- seg.color && ACCENT_NAV[seg.color as AccentColor] ? ACCENT_NAV[seg.color as AccentColor].text : 'text-neutral-300',
164
- )}>
165
- {seg.label}
166
- </span>
167
- </span>
168
- ))}
169
- </button>
170
- ))}
171
- </div>
172
- <div className="px-3 py-1.5 border-t border-neutral-700/50">
173
- <p className="text-sm text-neutral-600">Click to navigate</p>
174
- </div>
175
- </div>
176
- )}
296
+ {renderHistoryDropdown()}
177
297
  </div>
178
298
  <Divider size={size} />
179
299
  </>
180
300
  )}
181
301
 
182
- {segments.map((segment, index) => {
183
- const isLast = index === segments.length - 1
184
- const isClickable = !isLast && !!segment.onClick
185
- const colors = segment.color && ACCENT_NAV[segment.color as AccentColor] ? ACCENT_NAV[segment.color as AccentColor] : null
186
-
187
- return (
188
- <div key={segment.id} className="flex items-center gap-1 min-w-0">
189
- {index > 0 && <SegmentSeparator type={separator} size={size} />}
190
- {isClickable ? (
191
- <button
192
- type="button"
193
- onClick={segment.onClick}
194
- className={cn(
195
- 'flex items-center gap-1.5 px-2 py-0.5 rounded-md transition-colors cursor-pointer min-w-0',
196
- s.text,
197
- 'font-medium hover:text-white',
198
- colors ? [colors.text, `hover:${colors.bg}`] : ['text-neutral-300', 'hover:bg-neutral-700/50'],
199
- )}
200
- >
201
- {segment.icon && <SegmentIcon icon={segment.icon} color={segment.color} size={size} />}
202
- <span className="truncate max-w-[200px]">{segment.label}</span>
203
- </button>
204
- ) : (
205
- <div
206
- className={cn(
207
- 'flex items-center gap-1.5 px-2 py-0.5 rounded-md min-w-0',
208
- s.text,
209
- isLast
210
- ? ['font-medium bg-neutral-700/50', colors ? colors.text : 'text-white']
211
- : ['font-medium', colors ? colors.text : 'text-neutral-300'],
212
- )}
213
- >
214
- {segment.icon && <SegmentIcon icon={segment.icon} color={segment.color} size={size} />}
215
- <span className="truncate max-w-[200px]">{segment.label}</span>
216
- </div>
217
- )}
218
- </div>
219
- )
220
- })}
302
+ {renderSegments()}
221
303
  </div>
222
304
  </nav>
223
305
  )
@@ -1,6 +1,7 @@
1
1
  import { useState, useRef, useCallback, type KeyboardEvent } from 'react'
2
2
  import { ChevronUp, ChevronDown } from 'lucide-react'
3
3
  import { FORM_COLORS, type FormColor } from '../lib/form-colors.ts'
4
+ import { useAccentColor } from '../lib/accent-context.ts'
4
5
 
5
6
  export interface NumberInputProps {
6
7
  value: number
@@ -9,7 +10,7 @@ export interface NumberInputProps {
9
10
  max?: number
10
11
  step?: number
11
12
  variant?: 'filled' | 'outline'
12
- color?: FormColor
13
+ accentColor?: FormColor
13
14
  size?: 'xss' | 'xs' | 'sm' | 'md' | 'lg'
14
15
  disabled?: boolean
15
16
  className?: string
@@ -26,7 +27,7 @@ const SIZE_CONFIG = {
26
27
  }
27
28
 
28
29
  const VARIANT_CLASSES = {
29
- filled: 'bg-neutral-800',
30
+ filled: 'bg-neutral-960',
30
31
  outline: 'bg-transparent',
31
32
  }
32
33
 
@@ -37,7 +38,7 @@ export function NumberInput({
37
38
  max,
38
39
  step = 1,
39
40
  variant = 'outline',
40
- color = 'blue',
41
+ accentColor,
41
42
  size = 'sm',
42
43
  disabled = false,
43
44
  className = '',
@@ -78,8 +79,11 @@ export function NumberInput({
78
79
  setFocused(false)
79
80
  }
80
81
 
82
+ const contextAccent = useAccentColor()
83
+ const effectiveColor = accentColor ?? contextAccent ?? 'blue'
84
+
81
85
  const sc = SIZE_CONFIG[size]
82
- const fc = FORM_COLORS[color]
86
+ const fc = FORM_COLORS[effectiveColor]
83
87
 
84
88
  return (
85
89
  <div