@toolr/ui-design 0.1.9 → 0.1.11

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.
@@ -38,22 +38,22 @@ export const ACCENT_TEXT: Record<AccentColor, string> = {
38
38
 
39
39
  export const ACCENT_ICON = ACCENT_TEXT
40
40
 
41
- export const ACCENT_NAV: Record<AccentColor, { bg: string; text: string }> = {
42
- blue: { bg: 'bg-blue-500/10', text: 'text-blue-400' },
43
- green: { bg: 'bg-green-500/10', text: 'text-green-400' },
44
- red: { bg: 'bg-red-500/10', text: 'text-red-400' },
45
- orange: { bg: 'bg-orange-500/10', text: 'text-orange-400' },
46
- cyan: { bg: 'bg-cyan-500/10', text: 'text-cyan-400' },
47
- yellow: { bg: 'bg-yellow-500/10', text: 'text-yellow-400' },
48
- purple: { bg: 'bg-purple-500/10', text: 'text-purple-400' },
49
- indigo: { bg: 'bg-indigo-500/10', text: 'text-indigo-400' },
50
- emerald: { bg: 'bg-emerald-500/10', text: 'text-emerald-400' },
51
- amber: { bg: 'bg-amber-500/10', text: 'text-amber-400' },
52
- violet: { bg: 'bg-violet-500/10', text: 'text-violet-400' },
53
- neutral: { bg: 'bg-neutral-500/10', text: 'text-neutral-400' },
54
- sky: { bg: 'bg-sky-500/10', text: 'text-sky-400' },
55
- pink: { bg: 'bg-pink-500/10', text: 'text-pink-400' },
56
- teal: { bg: 'bg-teal-500/10', text: 'text-teal-400' },
41
+ export const ACCENT_NAV: Record<AccentColor, { bg: string; text: string; border: string }> = {
42
+ blue: { bg: 'bg-blue-500/10', text: 'text-blue-400', border: 'border-blue-500/30' },
43
+ green: { bg: 'bg-green-500/10', text: 'text-green-400', border: 'border-green-500/30' },
44
+ red: { bg: 'bg-red-500/10', text: 'text-red-400', border: 'border-red-500/30' },
45
+ orange: { bg: 'bg-orange-500/10', text: 'text-orange-400', border: 'border-orange-500/30' },
46
+ cyan: { bg: 'bg-cyan-500/10', text: 'text-cyan-400', border: 'border-cyan-500/30' },
47
+ yellow: { bg: 'bg-yellow-500/10', text: 'text-yellow-400', border: 'border-yellow-500/30' },
48
+ purple: { bg: 'bg-purple-500/10', text: 'text-purple-400', border: 'border-purple-500/30' },
49
+ indigo: { bg: 'bg-indigo-500/10', text: 'text-indigo-400', border: 'border-indigo-500/30' },
50
+ emerald: { bg: 'bg-emerald-500/10', text: 'text-emerald-400', border: 'border-emerald-500/30' },
51
+ amber: { bg: 'bg-amber-500/10', text: 'text-amber-400', border: 'border-amber-500/30' },
52
+ violet: { bg: 'bg-violet-500/10', text: 'text-violet-400', border: 'border-violet-500/30' },
53
+ neutral: { bg: 'bg-neutral-500/10', text: 'text-neutral-400', border: 'border-neutral-500/30' },
54
+ sky: { bg: 'bg-sky-500/10', text: 'text-sky-400', border: 'border-sky-500/30' },
55
+ pink: { bg: 'bg-pink-500/10', text: 'text-pink-400', border: 'border-pink-500/30' },
56
+ teal: { bg: 'bg-teal-500/10', text: 'text-teal-400', border: 'border-teal-500/30' },
57
57
  }
58
58
 
59
59
  export const FORM_COLORS: Record<FormColor, FormColorConfig> = {
@@ -9,7 +9,7 @@ export const TOOLR_APPS: Record<ToolrAppId, { name: string; accent: string }> =
9
9
  configr: { name: 'Configr', accent: 'violet' }, // #8b5cf6
10
10
  reviewr: { name: 'Reviewr', accent: 'orange' }, // #f97316
11
11
  learnr: { name: 'Learnr', accent: 'yellow' }, // #eab308
12
- seedr: { name: 'Seedr', accent: 'cyan' }, // #06b6d4
12
+ seedr: { name: 'Seedr', accent: 'green' }, // #22c55e
13
13
  planr: { name: 'Planr', accent: 'pink' }, // #ec4899 (not in ACCENT_DEFS, fallback hex)
14
14
  vibr: { name: 'Vibr', accent: 'red' }, // #ef4444
15
15
  }
@@ -77,13 +77,13 @@ export function Breadcrumb({
77
77
  <nav className={cn('flex items-center min-w-0', className)}>
78
78
  <div className={cn(
79
79
  'flex items-center gap-1',
80
- isBox && [s.px, s.py, 'bg-neutral-960/50 border border-neutral-700/50 rounded-lg'],
80
+ isBox && [s.px, s.py, 'bg-neutral-960/50 rounded-lg'],
81
81
  )}>
82
82
  {segments.map((segment, index) => {
83
83
  const isLast = index === segments.length - 1
84
84
  const isClickable = !isLast && !!segment.onClick
85
85
  const colors = segment.color && ACCENT_NAV[segment.color as AccentColor] ? ACCENT_NAV[segment.color as AccentColor] : null
86
- const isFirstPlain = !isBox && index === 0
86
+ const isFirst = !isBox && index === 0
87
87
 
88
88
  return (
89
89
  <div key={segment.id} className="flex items-center gap-1 min-w-0">
@@ -94,7 +94,7 @@ export function Breadcrumb({
94
94
  onClick={segment.onClick}
95
95
  className={cn(
96
96
  'flex items-center gap-1.5 pr-2 py-0.5 rounded-md transition-colors cursor-pointer min-w-0',
97
- isFirstPlain ? 'pl-0' : 'pl-2',
97
+ isFirst ? 'pl-0' : 'pl-2',
98
98
  s.text,
99
99
  'font-medium hover:text-white',
100
100
  colors ? [colors.text, `hover:${colors.bg}`] : ['text-neutral-300', 'hover:bg-neutral-700/50'],
@@ -107,10 +107,10 @@ export function Breadcrumb({
107
107
  <div
108
108
  className={cn(
109
109
  'flex items-center gap-1.5 pr-2 py-0.5 rounded-md min-w-0',
110
- isFirstPlain ? 'pl-0' : 'pl-2',
110
+ isFirst ? 'pl-0' : 'pl-2',
111
111
  s.text,
112
112
  isLast
113
- ? ['font-medium bg-neutral-700/50', colors ? colors.text : 'text-white']
113
+ ? 'font-medium text-white'
114
114
  : ['font-medium', colors ? colors.text : 'text-neutral-300'],
115
115
  )}
116
116
  >
@@ -1,6 +1,7 @@
1
1
  import { useState, useEffect, useCallback, useRef, useMemo, type ReactNode } from 'react'
2
2
  import { FileCode, FolderTree, Loader2, AlertCircle, AlignLeft, Code2, Type } from 'lucide-react'
3
3
  import { type AccentColor, ACCENT_ICON } from '../lib/form-colors.ts'
4
+ import { useAccentColor } from '../lib/accent-context.ts'
4
5
  import { CollapseButton } from './icon-button.tsx'
5
6
  import { SegmentedToggle } from './segmented-toggle.tsx'
6
7
  import { FileTree, collectDirPaths, type FileTreeNode } from './file-tree.tsx'
@@ -124,10 +125,12 @@ export function FileStructureSection({
124
125
  format,
125
126
  language,
126
127
  default: defaultMode,
127
- accentColor = 'blue',
128
+ accentColor: accentColorProp,
128
129
  renderPreview,
129
130
  initialHeight,
130
131
  }: FileStructureSectionProps) {
132
+ const contextAccent = useAccentColor()
133
+ const accentColor = accentColorProp ?? contextAccent ?? 'blue'
131
134
  const [selectedFilePath, setSelectedFilePath] = useState<string | null>(null)
132
135
  const [fileContent, setFileContent] = useState<string | null>(null)
133
136
  const [fetchedFilePath, setFetchedFilePath] = useState<string | null>(null)
@@ -335,7 +338,7 @@ export function FileStructureSection({
335
338
  <CollapseButton
336
339
  collapsed={allCollapsed}
337
340
  onToggle={() => setExpandedPaths(allCollapsed ? new Set(allDirPaths) : new Set())}
338
- accentColor={accentColor}
341
+ accentColor="neutral"
339
342
  />
340
343
  </div>
341
344
  <div className={`${variant === 'split' ? 'flex-1 overflow-y-auto' : ''} p-3`}>
@@ -26,7 +26,7 @@ import { useAccentColor } from '../lib/accent-context.ts'
26
26
  export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'size' | 'type'> {
27
27
  value: string
28
28
  onChange: (value: string) => void
29
- type?: 'text' | 'search' | 'password'
29
+ type?: 'text' | 'search' | 'password' | 'email'
30
30
  debounceMs?: number
31
31
  error?: boolean | string
32
32
  variant?: 'filled' | 'outline'
@@ -18,10 +18,13 @@ export interface NavigationBarProps {
18
18
  onForward?: () => void
19
19
  showHistory?: boolean
20
20
  historyEntries?: BreadcrumbSegment[][]
21
+ currentHistoryIndex?: number
21
22
  onHistorySelect?: (index: number) => void
22
23
  leadingAction?: { icon: IconName; onClick?: () => void }
23
24
  separator?: 'chevron' | 'slash' | 'dot'
24
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'
25
28
  accentColor?: AccentColor
26
29
  className?: string
27
30
  }
@@ -113,10 +116,12 @@ export function NavigationBar({
113
116
  onForward,
114
117
  showHistory = false,
115
118
  historyEntries,
119
+ currentHistoryIndex,
116
120
  onHistorySelect,
117
121
  leadingAction,
118
122
  separator = 'chevron',
119
123
  size = 'sm',
124
+ layout = 'bar',
120
125
  accentColor,
121
126
  className,
122
127
  }: NavigationBarProps) {
@@ -135,6 +140,128 @@ export function NavigationBar({
135
140
 
136
141
  const hasHistoryEntries = historyEntries && historyEntries.length > 0
137
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
+
138
265
  return (
139
266
  <nav className={cn('flex items-center', className)}>
140
267
  <div className={cn('flex items-center gap-1', s.px, s.py, 'bg-neutral-960/50 border rounded-lg', colorStyle.border)}>
@@ -166,86 +293,13 @@ export function NavigationBar({
166
293
  active={historyOpen}
167
294
  colorStyle={colorStyle}
168
295
  />
169
- {historyOpen && hasHistoryEntries && (
170
- <div className="absolute left-0 top-full mt-1 w-max min-w-[200px] max-w-[420px] bg-neutral-960 border border-neutral-700 rounded-lg shadow-lg z-50">
171
- <div className="px-3 py-1.5 border-b border-neutral-700/50">
172
- <p className="text-sm font-medium text-neutral-500">History</p>
173
- </div>
174
- <div className="max-h-[300px] overflow-y-auto py-1">
175
- {historyEntries.map((entry, i) => (
176
- <button
177
- key={i}
178
- type="button"
179
- onClick={() => {
180
- onHistorySelect?.(i)
181
- setHistoryOpen(false)
182
- }}
183
- className="w-full px-3 py-1.5 flex items-center gap-1 text-left hover:bg-neutral-960 transition-colors cursor-pointer"
184
- >
185
- {entry.map((seg, segIdx) => (
186
- <span key={seg.id} className="flex items-center gap-1 min-w-0">
187
- {segIdx > 0 && <ChevronRight className="w-2.5 h-2.5 text-neutral-600 flex-shrink-0" />}
188
- {seg.icon && <SegmentIcon icon={seg.icon} color={seg.color} size="xs" />}
189
- <span className={cn(
190
- 'text-sm truncate',
191
- seg.color && ACCENT_NAV[seg.color as AccentColor] ? ACCENT_NAV[seg.color as AccentColor].text : 'text-neutral-300',
192
- )}>
193
- {seg.label}
194
- </span>
195
- </span>
196
- ))}
197
- </button>
198
- ))}
199
- </div>
200
- <div className="px-3 py-1.5 border-t border-neutral-700/50">
201
- <p className="text-sm text-neutral-600">Click to navigate</p>
202
- </div>
203
- </div>
204
- )}
296
+ {renderHistoryDropdown()}
205
297
  </div>
206
298
  <Divider size={size} />
207
299
  </>
208
300
  )}
209
301
 
210
- {segments.map((segment, index) => {
211
- const isLast = index === segments.length - 1
212
- const isClickable = !isLast && !!segment.onClick
213
- const colors = segment.color && ACCENT_NAV[segment.color as AccentColor] ? ACCENT_NAV[segment.color as AccentColor] : null
214
-
215
- return (
216
- <div key={segment.id} className="flex items-center gap-1 min-w-0">
217
- {index > 0 && <SegmentSeparator type={separator} size={size} />}
218
- {isClickable ? (
219
- <button
220
- type="button"
221
- onClick={segment.onClick}
222
- className={cn(
223
- 'flex items-center gap-1.5 px-2 py-0.5 rounded-md transition-colors cursor-pointer min-w-0',
224
- s.text,
225
- 'font-medium hover:text-white',
226
- colors ? [colors.text, `hover:${colors.bg}`] : ['text-neutral-300', 'hover:bg-neutral-700/50'],
227
- )}
228
- >
229
- {segment.icon && <SegmentIcon icon={segment.icon} color={segment.color} size={size} />}
230
- <span className="truncate max-w-[200px]">{segment.label}</span>
231
- </button>
232
- ) : (
233
- <div
234
- className={cn(
235
- 'flex items-center gap-1.5 px-2 py-0.5 rounded-md min-w-0',
236
- s.text,
237
- isLast
238
- ? ['font-medium bg-neutral-700/50', colors ? colors.text : 'text-white']
239
- : ['font-medium', colors ? colors.text : 'text-neutral-300'],
240
- )}
241
- >
242
- {segment.icon && <SegmentIcon icon={segment.icon} color={segment.color} size={size} />}
243
- <span className="truncate max-w-[200px]">{segment.label}</span>
244
- </div>
245
- )}
246
- </div>
247
- )
248
- })}
302
+ {renderSegments()}
249
303
  </div>
250
304
  </nav>
251
305
  )
@@ -3,6 +3,7 @@ import { ChevronsUpDown, ChevronsDownUp } from 'lucide-react'
3
3
  import { IconButton } from './icon-button.tsx'
4
4
  import { Label, type LabelProps } from './label.tsx'
5
5
  import { CodingAgentIcon, CODING_AGENT_NAMES, type CodingAgentKey } from '../lib/coding-agents.tsx'
6
+ import { Tooltip } from './tooltip.tsx'
6
7
  import { FileStructureSection, type FileStructureSectionProps } from './file-structure-section.tsx'
7
8
  import { type AccentColor } from '../lib/form-colors.ts'
8
9
  import { AccentColorProvider, useAccentColor } from '../lib/accent-context.ts'
@@ -24,7 +25,7 @@ export interface RegistryDetailProps {
24
25
 
25
26
  // Standard sections (rendered in order before children)
26
27
  description?: string
27
- longDescription?: string
28
+ longDescription?: ReactNode
28
29
  integration?: boolean
29
30
  compatibleTools?: string[]
30
31
 
@@ -56,7 +57,7 @@ const MARKDOWN_CLASSES = 'text-md text-neutral-400 leading-relaxed [&_strong]:te
56
57
 
57
58
  const COLLAPSED_MAX_HEIGHT = 240
58
59
 
59
- function CollapsibleTextSection({ children, header }: { children: string; header?: string }) {
60
+ function CollapsibleTextSection({ children, header }: { children: ReactNode; header?: string }) {
60
61
  const contentRef = useRef<HTMLDivElement>(null)
61
62
  const [overflows, setOverflows] = useState(false)
62
63
  const [expanded, setExpanded] = useState(false)
@@ -107,7 +108,7 @@ function CollapsibleTextSection({ children, header }: { children: string; header
107
108
  className={MARKDOWN_CLASSES}
108
109
  style={showCollapsed ? { maxHeight: COLLAPSED_MAX_HEIGHT, overflow: 'hidden' } : undefined}
109
110
  >
110
- <p>{children}</p>
111
+ <div>{children}</div>
111
112
  </div>
112
113
 
113
114
  {showCollapsed && (
@@ -128,10 +129,9 @@ function CompatibleWithSection({ agents }: { agents: string[] }) {
128
129
  <h3 className="text-sm font-medium text-neutral-500 uppercase tracking-wider mb-3">Compatible with</h3>
129
130
  <div className="flex items-start gap-3">
130
131
  {agents.map((agent) => (
131
- <div key={agent} className="flex flex-col items-center gap-1">
132
- <CodingAgentIcon agent={agent} size={18} />
133
- <span className="text-xs text-neutral-400">{CODING_AGENT_NAMES[agent as CodingAgentKey] ?? agent}</span>
134
- </div>
132
+ <Tooltip key={agent} content={{ description: CODING_AGENT_NAMES[agent as CodingAgentKey] ?? agent }} position="top">
133
+ <CodingAgentIcon agent={agent} size={24} />
134
+ </Tooltip>
135
135
  ))}
136
136
  </div>
137
137
  </div>
@@ -166,8 +166,8 @@ export function RegistryDetail({
166
166
  const resolvedAccent = accentColor ?? contextAccent
167
167
 
168
168
  const content = (
169
- <div className="h-full overflow-y-auto p-6">
170
- <div className={`${maxWidth} mx-auto space-y-6`}>
169
+ <div className="h-full overflow-y-auto">
170
+ <div className={`${maxWidth} mx-auto px-4 py-6 space-y-8`}>
171
171
  {/* Above header badges */}
172
172
  {aboveHeader}
173
173
  {/* Header row */}
@@ -247,6 +247,29 @@ export function Tooltip({
247
247
  return () => window.removeEventListener('scroll', handleScroll, true)
248
248
  }, [trigger, isVisible, position, align])
249
249
 
250
+ // Dismiss hover tooltips on scroll, keyboard, click elsewhere, or window blur to prevent orphans
251
+ useEffect(() => {
252
+ if (trigger !== 'hover' || !isVisible) return
253
+ const dismiss = () => setIsVisible(false)
254
+ window.addEventListener('scroll', dismiss, true)
255
+ window.addEventListener('keydown', dismiss)
256
+ window.addEventListener('pointerdown', dismiss)
257
+ window.addEventListener('blur', dismiss)
258
+ return () => {
259
+ window.removeEventListener('scroll', dismiss, true)
260
+ window.removeEventListener('keydown', dismiss)
261
+ window.removeEventListener('pointerdown', dismiss)
262
+ window.removeEventListener('blur', dismiss)
263
+ }
264
+ }, [trigger, isVisible])
265
+
266
+ // Clear pending hide timeout on unmount
267
+ useEffect(() => {
268
+ return () => {
269
+ if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current)
270
+ }
271
+ }, [])
272
+
250
273
  const resolvedPos = position === 'auto' ? actualPosition : position
251
274
  const arrowClasses = getArrowClasses(resolvedPos, align)
252
275
 
package/dist/index.d.ts CHANGED
@@ -24,6 +24,7 @@ declare const ACCENT_ICON: Record<FormColor, string>;
24
24
  declare const ACCENT_NAV: Record<AccentColor, {
25
25
  bg: string;
26
26
  text: string;
27
+ border: string;
27
28
  }>;
28
29
  declare const FORM_COLORS: Record<FormColor, FormColorConfig>;
29
30
 
@@ -399,7 +400,7 @@ declare function Toggle({ checked, onChange, disabled, size, className, accentCo
399
400
  interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'size' | 'type'> {
400
401
  value: string;
401
402
  onChange: (value: string) => void;
402
- type?: 'text' | 'search' | 'password';
403
+ type?: 'text' | 'search' | 'password' | 'email';
403
404
  debounceMs?: number;
404
405
  error?: boolean | string;
405
406
  variant?: 'filled' | 'outline';
@@ -1030,7 +1031,7 @@ interface FileStructureSectionProps {
1030
1031
  initialHeight?: number;
1031
1032
  }
1032
1033
  declare function getLanguageFromPath(filePath: string): string;
1033
- declare function FileStructureSection({ files, rootName, variant, isLoading, error, onFetchContent, format, language, default: defaultMode, accentColor, renderPreview, initialHeight, }: FileStructureSectionProps): react_jsx_runtime.JSX.Element | null;
1034
+ declare function FileStructureSection({ files, rootName, variant, isLoading, error, onFetchContent, format, language, default: defaultMode, accentColor: accentColorProp, renderPreview, initialHeight, }: FileStructureSectionProps): react_jsx_runtime.JSX.Element | null;
1034
1035
 
1035
1036
  interface RegistryDetailProps {
1036
1037
  icon: LucideIcon;
@@ -1041,7 +1042,7 @@ interface RegistryDetailProps {
1041
1042
  actionButton?: ReactNode;
1042
1043
  labels?: LabelProps[];
1043
1044
  description?: string;
1044
- longDescription?: string;
1045
+ longDescription?: ReactNode;
1045
1046
  integration?: boolean;
1046
1047
  compatibleTools?: string[];
1047
1048
  files?: FileStructureSectionProps['files'];
@@ -2540,6 +2541,7 @@ interface NavigationBarProps {
2540
2541
  onForward?: () => void;
2541
2542
  showHistory?: boolean;
2542
2543
  historyEntries?: BreadcrumbSegment[][];
2544
+ currentHistoryIndex?: number;
2543
2545
  onHistorySelect?: (index: number) => void;
2544
2546
  leadingAction?: {
2545
2547
  icon: IconName;
@@ -2547,10 +2549,12 @@ interface NavigationBarProps {
2547
2549
  };
2548
2550
  separator?: 'chevron' | 'slash' | 'dot';
2549
2551
  size?: 'xss' | 'xs' | 'sm' | 'md' | 'lg';
2552
+ /** 'bar' (default): all-in-one bordered box. 'header': nav controls left-aligned, breadcrumb absolutely centered. */
2553
+ layout?: 'bar' | 'header';
2550
2554
  accentColor?: AccentColor;
2551
2555
  className?: string;
2552
2556
  }
2553
- declare function NavigationBar({ segments, canGoBack, canGoForward, onBack, onForward, showHistory, historyEntries, onHistorySelect, leadingAction, separator, size, accentColor, className, }: NavigationBarProps): react_jsx_runtime.JSX.Element;
2557
+ declare function NavigationBar({ segments, canGoBack, canGoForward, onBack, onForward, showHistory, historyEntries, currentHistoryIndex, onHistorySelect, leadingAction, separator, size, layout, accentColor, className, }: NavigationBarProps): react_jsx_runtime.JSX.Element;
2554
2558
 
2555
2559
  interface Tab {
2556
2560
  id: string;
package/dist/index.js CHANGED
@@ -18,21 +18,21 @@ var ACCENT_TEXT = {
18
18
  };
19
19
  var ACCENT_ICON = ACCENT_TEXT;
20
20
  var ACCENT_NAV = {
21
- blue: { bg: "bg-blue-500/10", text: "text-blue-400" },
22
- green: { bg: "bg-green-500/10", text: "text-green-400" },
23
- red: { bg: "bg-red-500/10", text: "text-red-400" },
24
- orange: { bg: "bg-orange-500/10", text: "text-orange-400" },
25
- cyan: { bg: "bg-cyan-500/10", text: "text-cyan-400" },
26
- yellow: { bg: "bg-yellow-500/10", text: "text-yellow-400" },
27
- purple: { bg: "bg-purple-500/10", text: "text-purple-400" },
28
- indigo: { bg: "bg-indigo-500/10", text: "text-indigo-400" },
29
- emerald: { bg: "bg-emerald-500/10", text: "text-emerald-400" },
30
- amber: { bg: "bg-amber-500/10", text: "text-amber-400" },
31
- violet: { bg: "bg-violet-500/10", text: "text-violet-400" },
32
- neutral: { bg: "bg-neutral-500/10", text: "text-neutral-400" },
33
- sky: { bg: "bg-sky-500/10", text: "text-sky-400" },
34
- pink: { bg: "bg-pink-500/10", text: "text-pink-400" },
35
- teal: { bg: "bg-teal-500/10", text: "text-teal-400" }
21
+ blue: { bg: "bg-blue-500/10", text: "text-blue-400", border: "border-blue-500/30" },
22
+ green: { bg: "bg-green-500/10", text: "text-green-400", border: "border-green-500/30" },
23
+ red: { bg: "bg-red-500/10", text: "text-red-400", border: "border-red-500/30" },
24
+ orange: { bg: "bg-orange-500/10", text: "text-orange-400", border: "border-orange-500/30" },
25
+ cyan: { bg: "bg-cyan-500/10", text: "text-cyan-400", border: "border-cyan-500/30" },
26
+ yellow: { bg: "bg-yellow-500/10", text: "text-yellow-400", border: "border-yellow-500/30" },
27
+ purple: { bg: "bg-purple-500/10", text: "text-purple-400", border: "border-purple-500/30" },
28
+ indigo: { bg: "bg-indigo-500/10", text: "text-indigo-400", border: "border-indigo-500/30" },
29
+ emerald: { bg: "bg-emerald-500/10", text: "text-emerald-400", border: "border-emerald-500/30" },
30
+ amber: { bg: "bg-amber-500/10", text: "text-amber-400", border: "border-amber-500/30" },
31
+ violet: { bg: "bg-violet-500/10", text: "text-violet-400", border: "border-violet-500/30" },
32
+ neutral: { bg: "bg-neutral-500/10", text: "text-neutral-400", border: "border-neutral-500/30" },
33
+ sky: { bg: "bg-sky-500/10", text: "text-sky-400", border: "border-sky-500/30" },
34
+ pink: { bg: "bg-pink-500/10", text: "text-pink-400", border: "border-pink-500/30" },
35
+ teal: { bg: "bg-teal-500/10", text: "text-teal-400", border: "border-teal-500/30" }
36
36
  };
37
37
  var FORM_COLORS = {
38
38
  blue: { border: "border-blue-500/30", hover: "hover:bg-blue-500/20 hover:border-blue-500/40", focus: "focus:border-blue-500", selectedBg: "bg-blue-600/20", accent: "text-blue-400" },
@@ -193,8 +193,8 @@ var TOOLR_APPS = {
193
193
  // #f97316
194
194
  learnr: { name: "Learnr", accent: "yellow" },
195
195
  // #eab308
196
- seedr: { name: "Seedr", accent: "cyan" },
197
- // #06b6d4
196
+ seedr: { name: "Seedr", accent: "green" },
197
+ // #22c55e
198
198
  planr: { name: "Planr", accent: "pink" },
199
199
  // #ec4899 (not in ACCENT_DEFS, fallback hex)
200
200
  vibr: { name: "Vibr", accent: "red" }
@@ -566,6 +566,25 @@ function Tooltip({
566
566
  window.addEventListener("scroll", handleScroll, true);
567
567
  return () => window.removeEventListener("scroll", handleScroll, true);
568
568
  }, [trigger, isVisible, position, align]);
569
+ useEffect2(() => {
570
+ if (trigger !== "hover" || !isVisible) return;
571
+ const dismiss = () => setIsVisible(false);
572
+ window.addEventListener("scroll", dismiss, true);
573
+ window.addEventListener("keydown", dismiss);
574
+ window.addEventListener("pointerdown", dismiss);
575
+ window.addEventListener("blur", dismiss);
576
+ return () => {
577
+ window.removeEventListener("scroll", dismiss, true);
578
+ window.removeEventListener("keydown", dismiss);
579
+ window.removeEventListener("pointerdown", dismiss);
580
+ window.removeEventListener("blur", dismiss);
581
+ };
582
+ }, [trigger, isVisible]);
583
+ useEffect2(() => {
584
+ return () => {
585
+ if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
586
+ };
587
+ }, []);
569
588
  const resolvedPos = position === "auto" ? actualPosition : position;
570
589
  const arrowClasses = getArrowClasses(resolvedPos, align);
571
590
  const tooltipContent = /* @__PURE__ */ jsxs3(
@@ -4377,10 +4396,12 @@ function FileStructureSection({
4377
4396
  format,
4378
4397
  language,
4379
4398
  default: defaultMode,
4380
- accentColor = "blue",
4399
+ accentColor: accentColorProp,
4381
4400
  renderPreview,
4382
4401
  initialHeight
4383
4402
  }) {
4403
+ const contextAccent = useAccentColor();
4404
+ const accentColor = accentColorProp ?? contextAccent ?? "blue";
4384
4405
  const [selectedFilePath, setSelectedFilePath] = useState14(null);
4385
4406
  const [fileContent, setFileContent] = useState14(null);
4386
4407
  const [fetchedFilePath, setFetchedFilePath] = useState14(null);
@@ -4550,7 +4571,7 @@ function FileStructureSection({
4550
4571
  {
4551
4572
  collapsed: allCollapsed,
4552
4573
  onToggle: () => setExpandedPaths(allCollapsed ? new Set(allDirPaths) : /* @__PURE__ */ new Set()),
4553
- accentColor
4574
+ accentColor: "neutral"
4554
4575
  }
4555
4576
  )
4556
4577
  ] }),
@@ -4661,7 +4682,7 @@ function CollapsibleTextSection({ children, header }) {
4661
4682
  ref: contentRef,
4662
4683
  className: MARKDOWN_CLASSES,
4663
4684
  style: showCollapsed ? { maxHeight: COLLAPSED_MAX_HEIGHT, overflow: "hidden" } : void 0,
4664
- children: /* @__PURE__ */ jsx34("p", { children })
4685
+ children: /* @__PURE__ */ jsx34("div", { children })
4665
4686
  }
4666
4687
  ),
4667
4688
  showCollapsed && /* @__PURE__ */ jsx34("div", { className: "absolute bottom-0 left-0 right-0 h-16 bg-gradient-to-t from-neutral-960 to-transparent pointer-events-none" })
@@ -4672,10 +4693,7 @@ function CompatibleWithSection({ agents }) {
4672
4693
  if (agents.length === 0) return null;
4673
4694
  return /* @__PURE__ */ jsxs26("div", { children: [
4674
4695
  /* @__PURE__ */ jsx34("h3", { className: "text-sm font-medium text-neutral-500 uppercase tracking-wider mb-3", children: "Compatible with" }),
4675
- /* @__PURE__ */ jsx34("div", { className: "flex items-start gap-3", children: agents.map((agent) => /* @__PURE__ */ jsxs26("div", { className: "flex flex-col items-center gap-1", children: [
4676
- /* @__PURE__ */ jsx34(CodingAgentIcon, { agent, size: 18 }),
4677
- /* @__PURE__ */ jsx34("span", { className: "text-xs text-neutral-400", children: CODING_AGENT_NAMES[agent] ?? agent })
4678
- ] }, agent)) })
4696
+ /* @__PURE__ */ jsx34("div", { className: "flex items-start gap-3", children: agents.map((agent) => /* @__PURE__ */ jsx34(Tooltip, { content: { description: CODING_AGENT_NAMES[agent] ?? agent }, position: "top", children: /* @__PURE__ */ jsx34(CodingAgentIcon, { agent, size: 24 }) }, agent)) })
4679
4697
  ] });
4680
4698
  }
4681
4699
  function RegistryDetail({
@@ -4702,7 +4720,7 @@ function RegistryDetail({
4702
4720
  }) {
4703
4721
  const contextAccent = useAccentColor();
4704
4722
  const resolvedAccent = accentColor ?? contextAccent;
4705
- const content = /* @__PURE__ */ jsx34("div", { className: "h-full overflow-y-auto p-6", children: /* @__PURE__ */ jsxs26("div", { className: `${maxWidth} mx-auto space-y-6`, children: [
4723
+ const content = /* @__PURE__ */ jsx34("div", { className: "h-full overflow-y-auto", children: /* @__PURE__ */ jsxs26("div", { className: `${maxWidth} mx-auto px-4 py-6 space-y-8`, children: [
4706
4724
  aboveHeader,
4707
4725
  /* @__PURE__ */ jsxs26("div", { className: "flex items-start justify-between gap-4", children: [
4708
4726
  /* @__PURE__ */ jsxs26("div", { className: "min-w-0", children: [
@@ -9886,12 +9904,12 @@ function Breadcrumb({
9886
9904
  const isBox = variant === "box";
9887
9905
  return /* @__PURE__ */ jsx56(AccentColorProvider, { value: effectiveColor, children: /* @__PURE__ */ jsx56("nav", { className: cn("flex items-center min-w-0", className), children: /* @__PURE__ */ jsx56("div", { className: cn(
9888
9906
  "flex items-center gap-1",
9889
- isBox && [s.px, s.py, "bg-neutral-960/50 border border-neutral-700/50 rounded-lg"]
9907
+ isBox && [s.px, s.py, "bg-neutral-960/50 rounded-lg"]
9890
9908
  ), children: segments.map((segment, index) => {
9891
9909
  const isLast = index === segments.length - 1;
9892
9910
  const isClickable = !isLast && !!segment.onClick;
9893
9911
  const colors = segment.color && ACCENT_NAV[segment.color] ? ACCENT_NAV[segment.color] : null;
9894
- const isFirstPlain = !isBox && index === 0;
9912
+ const isFirst = !isBox && index === 0;
9895
9913
  return /* @__PURE__ */ jsxs47("div", { className: "flex items-center gap-1 min-w-0", children: [
9896
9914
  index > 0 && /* @__PURE__ */ jsx56(Separator, { type: separator, size }),
9897
9915
  isClickable ? /* @__PURE__ */ jsxs47(
@@ -9901,7 +9919,7 @@ function Breadcrumb({
9901
9919
  onClick: segment.onClick,
9902
9920
  className: cn(
9903
9921
  "flex items-center gap-1.5 pr-2 py-0.5 rounded-md transition-colors cursor-pointer min-w-0",
9904
- isFirstPlain ? "pl-0" : "pl-2",
9922
+ isFirst ? "pl-0" : "pl-2",
9905
9923
  s.text,
9906
9924
  "font-medium hover:text-white",
9907
9925
  colors ? [colors.text, `hover:${colors.bg}`] : ["text-neutral-300", "hover:bg-neutral-700/50"]
@@ -9916,9 +9934,9 @@ function Breadcrumb({
9916
9934
  {
9917
9935
  className: cn(
9918
9936
  "flex items-center gap-1.5 pr-2 py-0.5 rounded-md min-w-0",
9919
- isFirstPlain ? "pl-0" : "pl-2",
9937
+ isFirst ? "pl-0" : "pl-2",
9920
9938
  s.text,
9921
- isLast ? ["font-medium bg-neutral-700/50", colors ? colors.text : "text-white"] : ["font-medium", colors ? colors.text : "text-neutral-300"]
9939
+ isLast ? "font-medium text-white" : ["font-medium", colors ? colors.text : "text-neutral-300"]
9922
9940
  ),
9923
9941
  children: [
9924
9942
  segment.icon && /* @__PURE__ */ jsx56(SegmentIcon, { icon: segment.icon, color: segment.color, size }),
@@ -9998,10 +10016,12 @@ function NavigationBar({
9998
10016
  onForward,
9999
10017
  showHistory = false,
10000
10018
  historyEntries,
10019
+ currentHistoryIndex,
10001
10020
  onHistorySelect,
10002
10021
  leadingAction,
10003
10022
  separator = "chevron",
10004
10023
  size = "sm",
10024
+ layout = "bar",
10005
10025
  accentColor,
10006
10026
  className
10007
10027
  }) {
@@ -10016,6 +10036,109 @@ function NavigationBar({
10016
10036
  const closeHistory = useCallback20(() => setHistoryOpen(false), []);
10017
10037
  useClickOutside(historyRef, historyOpen, closeHistory);
10018
10038
  const hasHistoryEntries = historyEntries && historyEntries.length > 0;
10039
+ const renderSegments = () => segments.map((segment, index) => {
10040
+ const isLast = index === segments.length - 1;
10041
+ const isClickable = !isLast && !!segment.onClick;
10042
+ const colors = segment.color && ACCENT_NAV[segment.color] ? ACCENT_NAV[segment.color] : null;
10043
+ return /* @__PURE__ */ jsxs48("div", { className: "flex items-center gap-1 min-w-0", children: [
10044
+ index > 0 && /* @__PURE__ */ jsx57(SegmentSeparator, { type: separator, size }),
10045
+ isClickable ? /* @__PURE__ */ jsxs48(
10046
+ "button",
10047
+ {
10048
+ type: "button",
10049
+ onClick: segment.onClick,
10050
+ className: cn(
10051
+ "flex items-center gap-1.5 px-2 py-0.5 rounded-md transition-colors cursor-pointer min-w-0",
10052
+ s.text,
10053
+ "font-medium hover:text-white",
10054
+ colors ? [colors.text, `hover:${colors.bg}`] : ["text-neutral-300", "hover:bg-neutral-700/50"]
10055
+ ),
10056
+ children: [
10057
+ segment.icon && /* @__PURE__ */ jsx57(SegmentIcon2, { icon: segment.icon, color: segment.color, size }),
10058
+ /* @__PURE__ */ jsx57("span", { className: "truncate max-w-[200px]", children: segment.label })
10059
+ ]
10060
+ }
10061
+ ) : /* @__PURE__ */ jsxs48(
10062
+ "div",
10063
+ {
10064
+ className: cn(
10065
+ "flex items-center gap-1.5 px-2 py-0.5 rounded-md min-w-0",
10066
+ s.text,
10067
+ isLast ? ["font-medium bg-neutral-700/50", colors ? colors.text : "text-white"] : ["font-medium", colors ? colors.text : "text-neutral-300"]
10068
+ ),
10069
+ children: [
10070
+ segment.icon && /* @__PURE__ */ jsx57(SegmentIcon2, { icon: segment.icon, color: segment.color, size }),
10071
+ /* @__PURE__ */ jsx57("span", { className: "truncate max-w-[200px]", children: segment.label })
10072
+ ]
10073
+ }
10074
+ )
10075
+ ] }, segment.id);
10076
+ });
10077
+ const renderHistoryDropdown = () => {
10078
+ if (!historyOpen || !hasHistoryEntries || !historyEntries) return null;
10079
+ const entries = layout === "header" ? [...historyEntries].reverse() : historyEntries;
10080
+ return /* @__PURE__ */ jsxs48("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", children: [
10081
+ /* @__PURE__ */ jsx57("div", { className: "px-3 py-1.5 border-b border-neutral-700/50", children: /* @__PURE__ */ jsxs48("p", { className: "text-sm font-medium text-neutral-500", children: [
10082
+ "History",
10083
+ layout === "header" && ` (${historyEntries.length})`
10084
+ ] }) }),
10085
+ /* @__PURE__ */ jsx57("div", { className: "max-h-[300px] overflow-y-auto py-1", children: entries.map((entry, entryIdx) => {
10086
+ const actualIdx = layout === "header" ? historyEntries.length - 1 - entryIdx : entryIdx;
10087
+ const isCurrent = currentHistoryIndex !== void 0 && actualIdx === currentHistoryIndex;
10088
+ return /* @__PURE__ */ jsxs48(
10089
+ "button",
10090
+ {
10091
+ type: "button",
10092
+ onClick: () => {
10093
+ onHistorySelect?.(actualIdx);
10094
+ setHistoryOpen(false);
10095
+ },
10096
+ className: cn(
10097
+ "w-full px-3 py-1.5 flex items-center gap-1 text-left transition-colors cursor-pointer",
10098
+ isCurrent ? "bg-green-500/10" : "hover:bg-neutral-800"
10099
+ ),
10100
+ children: [
10101
+ entry.map((seg, segIdx) => /* @__PURE__ */ jsxs48("span", { className: "flex items-center gap-1 min-w-0", children: [
10102
+ segIdx > 0 && /* @__PURE__ */ jsx57(ChevronRight10, { className: "w-2.5 h-2.5 text-neutral-600 flex-shrink-0" }),
10103
+ seg.icon && /* @__PURE__ */ jsx57(SegmentIcon2, { icon: seg.icon, color: seg.color, size: "xs" }),
10104
+ /* @__PURE__ */ jsx57("span", { className: cn(
10105
+ "text-sm truncate",
10106
+ seg.color && ACCENT_NAV[seg.color] ? ACCENT_NAV[seg.color].text : "text-neutral-300"
10107
+ ), children: seg.label })
10108
+ ] }, seg.id)),
10109
+ isCurrent && /* @__PURE__ */ jsx57("span", { className: "ml-auto pl-4 text-xs text-green-400 flex-shrink-0", children: "Current" })
10110
+ ]
10111
+ },
10112
+ actualIdx
10113
+ );
10114
+ }) })
10115
+ ] });
10116
+ };
10117
+ if (layout === "header") {
10118
+ return /* @__PURE__ */ jsxs48("nav", { className: cn("relative flex items-center h-full", className), children: [
10119
+ /* @__PURE__ */ jsxs48("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
10120
+ hasNav && /* @__PURE__ */ jsxs48(Fragment12, { children: [
10121
+ /* @__PURE__ */ jsx57(NavButton, { icon: ChevronLeft2, onClick: onBack, disabled: !canGoBack, size, colorStyle }),
10122
+ /* @__PURE__ */ jsx57(NavButton, { icon: ChevronRight10, onClick: onForward, disabled: !canGoForward, size, colorStyle })
10123
+ ] }),
10124
+ showHistory && /* @__PURE__ */ jsxs48("div", { className: "relative", ref: historyRef, children: [
10125
+ /* @__PURE__ */ jsx57(
10126
+ NavButton,
10127
+ {
10128
+ icon: History3,
10129
+ onClick: () => setHistoryOpen((o) => !o),
10130
+ disabled: !hasHistoryEntries,
10131
+ size,
10132
+ active: historyOpen,
10133
+ colorStyle
10134
+ }
10135
+ ),
10136
+ renderHistoryDropdown()
10137
+ ] })
10138
+ ] }),
10139
+ /* @__PURE__ */ jsx57("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsx57("div", { className: cn("pointer-events-auto flex items-center gap-1", s.px, s.py, "bg-neutral-960/50 rounded-lg"), children: renderSegments() }) })
10140
+ ] });
10141
+ }
10019
10142
  return /* @__PURE__ */ jsx57("nav", { className: cn("flex items-center", className), children: /* @__PURE__ */ jsxs48("div", { className: cn("flex items-center gap-1", s.px, s.py, "bg-neutral-960/50 border rounded-lg", colorStyle.border), children: [
10020
10143
  leadingAction && LeadIcon && /* @__PURE__ */ jsxs48(Fragment12, { children: [
10021
10144
  /* @__PURE__ */ jsx57(NavButton, { icon: LeadIcon, onClick: leadingAction.onClick, size, colorStyle }),
@@ -10041,71 +10164,11 @@ function NavigationBar({
10041
10164
  colorStyle
10042
10165
  }
10043
10166
  ),
10044
- historyOpen && hasHistoryEntries && /* @__PURE__ */ jsxs48("div", { className: "absolute left-0 top-full mt-1 w-max min-w-[200px] max-w-[420px] bg-neutral-960 border border-neutral-700 rounded-lg shadow-lg z-50", children: [
10045
- /* @__PURE__ */ jsx57("div", { className: "px-3 py-1.5 border-b border-neutral-700/50", children: /* @__PURE__ */ jsx57("p", { className: "text-sm font-medium text-neutral-500", children: "History" }) }),
10046
- /* @__PURE__ */ jsx57("div", { className: "max-h-[300px] overflow-y-auto py-1", children: historyEntries.map((entry, i) => /* @__PURE__ */ jsx57(
10047
- "button",
10048
- {
10049
- type: "button",
10050
- onClick: () => {
10051
- onHistorySelect?.(i);
10052
- setHistoryOpen(false);
10053
- },
10054
- className: "w-full px-3 py-1.5 flex items-center gap-1 text-left hover:bg-neutral-960 transition-colors cursor-pointer",
10055
- children: entry.map((seg, segIdx) => /* @__PURE__ */ jsxs48("span", { className: "flex items-center gap-1 min-w-0", children: [
10056
- segIdx > 0 && /* @__PURE__ */ jsx57(ChevronRight10, { className: "w-2.5 h-2.5 text-neutral-600 flex-shrink-0" }),
10057
- seg.icon && /* @__PURE__ */ jsx57(SegmentIcon2, { icon: seg.icon, color: seg.color, size: "xs" }),
10058
- /* @__PURE__ */ jsx57("span", { className: cn(
10059
- "text-sm truncate",
10060
- seg.color && ACCENT_NAV[seg.color] ? ACCENT_NAV[seg.color].text : "text-neutral-300"
10061
- ), children: seg.label })
10062
- ] }, seg.id))
10063
- },
10064
- i
10065
- )) }),
10066
- /* @__PURE__ */ jsx57("div", { className: "px-3 py-1.5 border-t border-neutral-700/50", children: /* @__PURE__ */ jsx57("p", { className: "text-sm text-neutral-600", children: "Click to navigate" }) })
10067
- ] })
10167
+ renderHistoryDropdown()
10068
10168
  ] }),
10069
10169
  /* @__PURE__ */ jsx57(Divider, { size })
10070
10170
  ] }),
10071
- segments.map((segment, index) => {
10072
- const isLast = index === segments.length - 1;
10073
- const isClickable = !isLast && !!segment.onClick;
10074
- const colors = segment.color && ACCENT_NAV[segment.color] ? ACCENT_NAV[segment.color] : null;
10075
- return /* @__PURE__ */ jsxs48("div", { className: "flex items-center gap-1 min-w-0", children: [
10076
- index > 0 && /* @__PURE__ */ jsx57(SegmentSeparator, { type: separator, size }),
10077
- isClickable ? /* @__PURE__ */ jsxs48(
10078
- "button",
10079
- {
10080
- type: "button",
10081
- onClick: segment.onClick,
10082
- className: cn(
10083
- "flex items-center gap-1.5 px-2 py-0.5 rounded-md transition-colors cursor-pointer min-w-0",
10084
- s.text,
10085
- "font-medium hover:text-white",
10086
- colors ? [colors.text, `hover:${colors.bg}`] : ["text-neutral-300", "hover:bg-neutral-700/50"]
10087
- ),
10088
- children: [
10089
- segment.icon && /* @__PURE__ */ jsx57(SegmentIcon2, { icon: segment.icon, color: segment.color, size }),
10090
- /* @__PURE__ */ jsx57("span", { className: "truncate max-w-[200px]", children: segment.label })
10091
- ]
10092
- }
10093
- ) : /* @__PURE__ */ jsxs48(
10094
- "div",
10095
- {
10096
- className: cn(
10097
- "flex items-center gap-1.5 px-2 py-0.5 rounded-md min-w-0",
10098
- s.text,
10099
- isLast ? ["font-medium bg-neutral-700/50", colors ? colors.text : "text-white"] : ["font-medium", colors ? colors.text : "text-neutral-300"]
10100
- ),
10101
- children: [
10102
- segment.icon && /* @__PURE__ */ jsx57(SegmentIcon2, { icon: segment.icon, color: segment.color, size }),
10103
- /* @__PURE__ */ jsx57("span", { className: "truncate max-w-[200px]", children: segment.label })
10104
- ]
10105
- }
10106
- )
10107
- ] }, segment.id);
10108
- })
10171
+ renderSegments()
10109
10172
  ] }) });
10110
10173
  }
10111
10174
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolr/ui-design",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",