@toolr/ui-design 0.1.5 → 0.1.7

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 (82) hide show
  1. package/agent-rules.json +91 -0
  2. package/ai-manifest.json +190 -0
  3. package/components/content/info-panel-primitives.tsx +14 -14
  4. package/components/hooks/use-click-outside.ts +10 -3
  5. package/components/hooks/use-modal-behavior.ts +24 -0
  6. package/components/hooks/use-navigation-history.ts +7 -2
  7. package/components/hooks/use-resizable-sidebar.ts +38 -0
  8. package/components/lib/ai-tools.tsx +1 -1
  9. package/components/lib/form-colors.ts +40 -0
  10. package/components/sections/ai-tools-paths/tools-paths-panel.tsx +7 -7
  11. package/components/sections/captured-issues/captured-issues-panel.tsx +13 -13
  12. package/components/sections/captured-issues/use-captured-issues.ts +9 -3
  13. package/components/sections/golden-snapshots/file-diff-viewer.tsx +13 -13
  14. package/components/sections/golden-snapshots/golden-sync-panel.tsx +5 -5
  15. package/components/sections/golden-snapshots/snapshot-manager.tsx +11 -11
  16. package/components/sections/golden-snapshots/status-overview.tsx +20 -20
  17. package/components/sections/golden-snapshots/version-manager.tsx +8 -8
  18. package/components/sections/prompt-editor/file-type-tabbed-prompt-editor.tsx +8 -44
  19. package/components/sections/prompt-editor/index.ts +0 -7
  20. package/components/sections/prompt-editor/simulator-prompt-editor.tsx +9 -45
  21. package/components/sections/prompt-editor/tabbed-prompt-editor.tsx +11 -43
  22. package/components/sections/report-bug/report-bug-form.tsx +14 -14
  23. package/components/sections/report-bug/screenshot-uploader.tsx +6 -6
  24. package/components/sections/snapshot-browser/snapshot-browser-panel.tsx +3 -3
  25. package/components/sections/snapshot-browser/snapshot-tree.tsx +8 -8
  26. package/components/sections/snippets-editor/snippets-editor.tsx +74 -48
  27. package/components/settings/SettingsHeader.tsx +1 -2
  28. package/components/settings/SettingsTreeNav.tsx +31 -16
  29. package/components/ui/action-dialog.tsx +12 -56
  30. package/components/ui/badge.tsx +8 -24
  31. package/components/ui/bottom-panel-header.tsx +4 -4
  32. package/components/ui/breadcrumb.tsx +8 -68
  33. package/components/ui/checkbox.tsx +2 -16
  34. package/components/ui/collapsible-section.tsx +4 -42
  35. package/components/ui/confirm-badge.tsx +3 -20
  36. package/components/ui/cookie-consent.tsx +21 -5
  37. package/components/ui/debounce-border-overlay.tsx +31 -0
  38. package/components/ui/detail-section.tsx +5 -22
  39. package/components/ui/editor-placeholder-card.tsx +17 -16
  40. package/components/ui/editor-toolbar.tsx +12 -0
  41. package/components/ui/execution-details-panel.tsx +8 -13
  42. package/components/ui/extension-list-card.tsx +3 -3
  43. package/components/ui/file-structure-section.tsx +20 -35
  44. package/components/ui/file-tree.tsx +4 -14
  45. package/components/ui/files-panel.tsx +28 -18
  46. package/components/ui/filter-dropdown.tsx +5 -5
  47. package/components/ui/form-actions.tsx +7 -6
  48. package/components/ui/frontmatter-form-header.tsx +4 -4
  49. package/components/ui/icon-button.tsx +3 -2
  50. package/components/ui/input.tsx +15 -31
  51. package/components/ui/label.tsx +7 -21
  52. package/components/ui/layout-tab-bar.tsx +4 -4
  53. package/components/ui/modal.tsx +5 -17
  54. package/components/ui/nav-card.tsx +5 -20
  55. package/components/ui/navigation-bar.tsx +13 -74
  56. package/components/ui/number-input.tsx +4 -4
  57. package/components/ui/registry-browser.tsx +10 -24
  58. package/components/ui/registry-card.tsx +16 -20
  59. package/components/ui/registry-detail.tsx +6 -6
  60. package/components/ui/resizable-textarea.tsx +13 -35
  61. package/components/ui/segmented-toggle.tsx +6 -5
  62. package/components/ui/select.tsx +7 -16
  63. package/components/ui/selection-grid.tsx +6 -54
  64. package/components/ui/setting-row.tsx +2 -4
  65. package/components/ui/settings-card.tsx +3 -3
  66. package/components/ui/settings-info-box.tsx +6 -23
  67. package/components/ui/settings-section-title.tsx +1 -1
  68. package/components/ui/snapshot-card.tsx +7 -7
  69. package/components/ui/snippets-panel.tsx +10 -10
  70. package/components/ui/sort-dropdown.tsx +2 -2
  71. package/components/ui/status-card.tsx +6 -17
  72. package/components/ui/tab-bar.tsx +5 -31
  73. package/components/ui/toggle.tsx +3 -19
  74. package/components/ui/tooltip.tsx +9 -21
  75. package/dist/content.js +14 -14
  76. package/dist/index.d.ts +71 -141
  77. package/dist/index.js +1634 -2450
  78. package/dist/tokens/primitives.css +9 -2
  79. package/index.ts +8 -7
  80. package/package.json +13 -3
  81. package/tokens/primitives.css +9 -2
  82. package/components/sections/prompt-editor/use-prompt-editor.ts +0 -131
@@ -1,53 +1,10 @@
1
1
  /** Breadcrumb navigation with clickable segments, color-coded icons, and configurable separators. */
2
2
 
3
- import {
4
- ChevronRight,
5
- Settings, Folder, File, Code, Terminal, Database,
6
- Globe, Star, Users, User, Tag, Search, Heart,
7
- Zap, Shield, ShieldCheck, Sparkles, Eye, Lock,
8
- Cloud, Wand2, Bell, Bookmark, Pin, Mail, Send,
9
- Image, Bot, Puzzle, Plug, Webhook, House, Package,
10
- } from 'lucide-react'
11
- import type { LucideIcon } from 'lucide-react'
12
- import type { IconName } from './icon-button.tsx'
3
+ import { ChevronRight } from 'lucide-react'
4
+ import { iconMap, type IconName } from './icon-button.tsx'
5
+ import { ACCENT_NAV, type AccentColor } from '../lib/form-colors.ts'
13
6
  import { cn } from '../lib/cn.ts'
14
7
 
15
- const iconSubset: Partial<Record<IconName, LucideIcon>> = {
16
- folder: Folder,
17
- file: File,
18
- settings: Settings,
19
- code: Code,
20
- terminal: Terminal,
21
- database: Database,
22
- globe: Globe,
23
- star: Star,
24
- users: Users,
25
- user: User,
26
- tag: Tag,
27
- zap: Zap,
28
- shield: Shield,
29
- 'shield-check': ShieldCheck,
30
- sparkles: Sparkles,
31
- eye: Eye,
32
- lock: Lock,
33
- search: Search,
34
- heart: Heart,
35
- cloud: Cloud,
36
- wand: Wand2,
37
- bell: Bell,
38
- bookmark: Bookmark,
39
- pin: Pin,
40
- mail: Mail,
41
- send: Send,
42
- image: Image,
43
- bot: Bot,
44
- puzzle: Puzzle,
45
- plug: Plug,
46
- webhook: Webhook,
47
- home: House,
48
- 'package': Package,
49
- }
50
-
51
8
  export interface BreadcrumbSegment {
52
9
  id: string
53
10
  label: string
@@ -67,35 +24,18 @@ export interface BreadcrumbProps {
67
24
 
68
25
  const sizeConfig = {
69
26
  xss: { text: 'text-xss', icon: 'w-2.5 h-2.5', px: 'px-1', py: 'py-0.5', gap: 'gap-0.5', sep: 'w-2 h-2' },
70
- xs: { text: 'text-xs', icon: 'w-3 h-3', px: 'px-1.5', py: 'py-0.5', gap: 'gap-1', sep: 'w-2.5 h-2.5' },
71
- sm: { text: 'text-sm', icon: 'w-3.5 h-3.5', px: 'px-2', py: 'py-1', gap: 'gap-1.5', sep: 'w-3 h-3' },
27
+ xs: { text: 'text-sm', icon: 'w-3 h-3', px: 'px-1.5', py: 'py-0.5', gap: 'gap-1', sep: 'w-2.5 h-2.5' },
28
+ sm: { text: 'text-md', icon: 'w-3.5 h-3.5', px: 'px-2', py: 'py-1', gap: 'gap-1.5', sep: 'w-3 h-3' },
72
29
  md: { text: 'text-base', icon: 'w-4 h-4', px: 'px-2.5', py: 'py-1', gap: 'gap-1.5', sep: 'w-3.5 h-3.5' },
73
30
  lg: { text: 'text-lg', icon: 'w-5 h-5', px: 'px-3', py: 'py-1.5', gap: 'gap-2', sep: 'w-4 h-4' },
74
31
  }
75
32
 
76
- const colorMap: Record<string, { bg: string; text: string }> = {
77
- blue: { bg: 'bg-blue-500/10', text: 'text-blue-400' },
78
- green: { bg: 'bg-green-500/10', text: 'text-green-400' },
79
- purple: { bg: 'bg-purple-500/10', text: 'text-purple-400' },
80
- red: { bg: 'bg-red-500/10', text: 'text-red-400' },
81
- orange: { bg: 'bg-orange-500/10', text: 'text-orange-400' },
82
- cyan: { bg: 'bg-cyan-500/10', text: 'text-cyan-400' },
83
- yellow: { bg: 'bg-yellow-500/10', text: 'text-yellow-400' },
84
- amber: { bg: 'bg-amber-500/10', text: 'text-amber-400' },
85
- emerald: { bg: 'bg-emerald-500/10', text: 'text-emerald-400' },
86
- indigo: { bg: 'bg-indigo-500/10', text: 'text-indigo-400' },
87
- violet: { bg: 'bg-violet-500/10', text: 'text-violet-400' },
88
- sky: { bg: 'bg-sky-500/10', text: 'text-sky-400' },
89
- pink: { bg: 'bg-pink-500/10', text: 'text-pink-400' },
90
- teal: { bg: 'bg-teal-500/10', text: 'text-teal-400' },
91
- neutral: { bg: 'bg-neutral-500/10', text: 'text-neutral-400' },
92
- }
93
33
 
94
34
  function SegmentIcon({ icon, color, size }: { icon: IconName; color?: string; size: 'xss' | 'xs' | 'sm' | 'md' | 'lg' }) {
95
- const Icon = iconSubset[icon]
35
+ const Icon = iconMap[icon]
96
36
  if (!Icon) return null
97
37
  const s = sizeConfig[size]
98
- const c = color && colorMap[color] ? colorMap[color] : null
38
+ const c = color && ACCENT_NAV[color as AccentColor] ? ACCENT_NAV[color as AccentColor] : null
99
39
 
100
40
  return (
101
41
  <span className={c?.text || ''}>
@@ -135,7 +75,7 @@ export function Breadcrumb({
135
75
  {segments.map((segment, index) => {
136
76
  const isLast = index === segments.length - 1
137
77
  const isClickable = !isLast && !!segment.onClick
138
- const colors = segment.color && colorMap[segment.color] ? colorMap[segment.color] : null
78
+ const colors = segment.color && ACCENT_NAV[segment.color as AccentColor] ? ACCENT_NAV[segment.color as AccentColor] : null
139
79
  const isFirstPlain = !isBox && index === 0
140
80
 
141
81
  return (
@@ -8,25 +8,11 @@
8
8
  */
9
9
 
10
10
  import { Check } from 'lucide-react'
11
+ import { type AccentColor } from '../lib/form-colors.ts'
11
12
 
12
13
  export type CheckboxSize = 'xss' | 'xs' | 'sm' | 'md' | 'lg'
13
14
 
14
- export type CheckboxColor =
15
- | 'blue'
16
- | 'green'
17
- | 'red'
18
- | 'orange'
19
- | 'cyan'
20
- | 'yellow'
21
- | 'purple'
22
- | 'indigo'
23
- | 'emerald'
24
- | 'amber'
25
- | 'violet'
26
- | 'neutral'
27
- | 'sky'
28
- | 'pink'
29
- | 'teal'
15
+ export type CheckboxColor = AccentColor
30
16
 
31
17
  const CHECKBOX_COLORS: Record<CheckboxColor, { bg: string; border: string; icon: string; hover: string }> = {
32
18
  blue: { bg: 'bg-blue-500/20', border: 'border-blue-500/40', icon: 'text-blue-300', hover: 'hover:bg-blue-500/15 hover:border-blue-500/30' },
@@ -1,47 +1,9 @@
1
1
  import { useState } from 'react'
2
- import {
3
- ChevronRight, Settings, Folder, Code, Terminal, Database,
4
- Star, Heart, Bell, Bookmark, Tag, Pin, Mail, Globe, Cloud,
5
- Shield, Zap, Sparkles, Search, Filter, Eye, Lock, User, Users,
6
- File, Image, Download, Upload, Play, Pause,
7
- } from 'lucide-react'
8
- import type { LucideIcon } from 'lucide-react'
9
- import type { IconName } from './icon-button.tsx'
2
+ import { ChevronRight } from 'lucide-react'
3
+ import { iconMap, type IconName } from './icon-button.tsx'
10
4
  import { Badge, type BadgeColor } from './badge.tsx'
11
5
  import { cn } from '../lib/cn.ts'
12
6
 
13
- const iconSubset: Partial<Record<IconName, LucideIcon>> = {
14
- settings: Settings,
15
- folder: Folder,
16
- code: Code,
17
- terminal: Terminal,
18
- database: Database,
19
- star: Star,
20
- heart: Heart,
21
- bell: Bell,
22
- bookmark: Bookmark,
23
- tag: Tag,
24
- pin: Pin,
25
- mail: Mail,
26
- globe: Globe,
27
- cloud: Cloud,
28
- shield: Shield,
29
- zap: Zap,
30
- sparkles: Sparkles,
31
- search: Search,
32
- filter: Filter,
33
- eye: Eye,
34
- lock: Lock,
35
- user: User,
36
- users: Users,
37
- file: File,
38
- image: Image,
39
- download: Download,
40
- upload: Upload,
41
- play: Play,
42
- pause: Pause,
43
- }
44
-
45
7
  export interface CollapsibleSectionProps {
46
8
  title: string
47
9
  icon?: IconName
@@ -64,7 +26,7 @@ export function CollapsibleSection({
64
26
  className,
65
27
  }: CollapsibleSectionProps) {
66
28
  const [open, setOpen] = useState(defaultOpen)
67
- const Icon = icon ? iconSubset[icon] : undefined
29
+ const Icon = icon ? iconMap[icon] : undefined
68
30
 
69
31
  return (
70
32
  <div className={cn('border-b border-neutral-700', className)}>
@@ -87,7 +49,7 @@ export function CollapsibleSection({
87
49
  <Icon className="w-3.5 h-3.5" />
88
50
  </span>
89
51
  )}
90
- <span className="text-sm font-medium text-neutral-200">{title}</span>
52
+ <span className="text-md font-medium text-neutral-200">{title}</span>
91
53
  {badge !== undefined && (
92
54
  <span className="ml-auto">
93
55
  <Badge value={badge} color={badgeColor} size="xss" />
@@ -12,8 +12,9 @@
12
12
  */
13
13
 
14
14
  import { Check } from 'lucide-react'
15
+ import { FORM_COLORS, type AccentColor } from '../lib/form-colors.ts'
15
16
 
16
- export type ConfirmBadgeColor = 'blue' | 'green' | 'red' | 'orange' | 'cyan' | 'yellow' | 'purple' | 'indigo' | 'emerald' | 'amber' | 'violet' | 'neutral' | 'sky' | 'pink' | 'teal'
17
+ export type ConfirmBadgeColor = AccentColor
17
18
 
18
19
  export interface ConfirmBadgeProps {
19
20
  color?: ConfirmBadgeColor
@@ -22,24 +23,6 @@ export interface ConfirmBadgeProps {
22
23
  testId?: string
23
24
  }
24
25
 
25
- const colorClasses: Record<ConfirmBadgeColor, string> = {
26
- green: 'border-green-500/30 text-green-400',
27
- red: 'border-red-500/30 text-red-400',
28
- blue: 'border-blue-500/30 text-blue-400',
29
- orange: 'border-orange-500/30 text-orange-400',
30
- cyan: 'border-cyan-500/30 text-cyan-400',
31
- yellow: 'border-yellow-500/30 text-yellow-400',
32
- purple: 'border-purple-500/30 text-purple-400',
33
- indigo: 'border-indigo-500/30 text-indigo-400',
34
- emerald: 'border-emerald-500/30 text-emerald-400',
35
- amber: 'border-amber-500/30 text-amber-400',
36
- violet: 'border-violet-500/30 text-violet-400',
37
- neutral: 'border-neutral-500/30 text-neutral-400',
38
- sky: 'border-sky-500/30 text-sky-400',
39
- pink: 'border-pink-500/30 text-pink-400',
40
- teal: 'border-teal-500/30 text-teal-400',
41
- }
42
-
43
26
  const sizeClasses = {
44
27
  xss: 'w-[14px] h-[14px] rounded-full',
45
28
  xs: 'w-[16px] h-[16px] rounded-full',
@@ -65,7 +48,7 @@ export function ConfirmBadge({
65
48
  return (
66
49
  <span
67
50
  data-testid={testId}
68
- className={`inline-flex items-center justify-center border ${colorClasses[color]} ${sizeClasses[size]} ${className}`}
51
+ className={`inline-flex items-center justify-center border ${FORM_COLORS[color].border} ${FORM_COLORS[color].accent} ${sizeClasses[size]} ${className}`}
69
52
  >
70
53
  <Check className={iconSizeClasses[size]} />
71
54
  </span>
@@ -16,6 +16,22 @@ export interface CookieConsentProps {
16
16
  onConsent?: (choice: ConsentChoice) => void
17
17
  }
18
18
 
19
+ const ACCENT_BUTTON_CLASSES: Record<string, string> = {
20
+ cyan: 'bg-cyan-600 border-cyan-500 hover:bg-cyan-500',
21
+ blue: 'bg-blue-600 border-blue-500 hover:bg-blue-500',
22
+ green: 'bg-green-600 border-green-500 hover:bg-green-500',
23
+ purple: 'bg-purple-600 border-purple-500 hover:bg-purple-500',
24
+ orange: 'bg-orange-600 border-orange-500 hover:bg-orange-500',
25
+ red: 'bg-red-600 border-red-500 hover:bg-red-500',
26
+ amber: 'bg-amber-600 border-amber-500 hover:bg-amber-500',
27
+ emerald: 'bg-emerald-600 border-emerald-500 hover:bg-emerald-500',
28
+ indigo: 'bg-indigo-600 border-indigo-500 hover:bg-indigo-500',
29
+ pink: 'bg-pink-600 border-pink-500 hover:bg-pink-500',
30
+ teal: 'bg-teal-600 border-teal-500 hover:bg-teal-500',
31
+ violet: 'bg-violet-600 border-violet-500 hover:bg-violet-500',
32
+ sky: 'bg-sky-600 border-sky-500 hover:bg-sky-500',
33
+ }
34
+
19
35
  export function CookieConsent({
20
36
  storageKey,
21
37
  accentColor = 'cyan',
@@ -42,26 +58,26 @@ export function CookieConsent({
42
58
  <div className="fixed bottom-0 left-0 right-0 z-50 p-4 bg-neutral-900/95 backdrop-blur-sm border-t border-neutral-700/50">
43
59
  <div className="max-w-6xl mx-auto flex flex-col sm:flex-row items-start sm:items-center gap-4">
44
60
  <div className="flex-grow">
45
- <p className="text-sm text-neutral-200 mb-1">{heading}</p>
46
- <p className="text-xs text-neutral-400">{description}</p>
61
+ <p className="text-md text-neutral-200 mb-1">{heading}</p>
62
+ <p className="text-sm text-neutral-400">{description}</p>
47
63
  </div>
48
64
 
49
65
  <div className="flex items-center gap-2 flex-shrink-0">
50
66
  <button
51
67
  onClick={() => handleConsent('declined')}
52
- className="px-3 py-1.5 text-sm h-[26px] inline-flex items-center justify-center font-medium rounded-md cursor-pointer text-neutral-400 border border-transparent hover:text-neutral-200 hover:border-neutral-600 hover:bg-neutral-800 transition-colors"
68
+ className="px-3 py-1.5 text-md h-[26px] inline-flex items-center justify-center font-medium rounded-md cursor-pointer text-neutral-400 border border-transparent hover:text-neutral-200 hover:border-neutral-600 hover:bg-neutral-800 transition-colors"
53
69
  >
54
70
  Decline
55
71
  </button>
56
72
  <button
57
73
  onClick={() => handleConsent('essential')}
58
- className="px-3 py-1.5 text-sm h-[26px] inline-flex items-center justify-center font-medium rounded-md cursor-pointer text-neutral-400 border border-transparent hover:text-neutral-200 hover:border-neutral-600 hover:bg-neutral-800 transition-colors"
74
+ className="px-3 py-1.5 text-md h-[26px] inline-flex items-center justify-center font-medium rounded-md cursor-pointer text-neutral-400 border border-transparent hover:text-neutral-200 hover:border-neutral-600 hover:bg-neutral-800 transition-colors"
59
75
  >
60
76
  Essential Only
61
77
  </button>
62
78
  <button
63
79
  onClick={() => handleConsent('accepted')}
64
- className={`px-3 py-1.5 text-sm h-[26px] inline-flex items-center justify-center font-medium rounded-md cursor-pointer text-white border transition-colors bg-${accentColor}-600 border-${accentColor}-500 hover:bg-${accentColor}-500`}
80
+ className={`px-3 py-1.5 text-md h-[26px] inline-flex items-center justify-center font-medium rounded-md cursor-pointer text-white border transition-colors ${ACCENT_BUTTON_CLASSES[accentColor] ?? ACCENT_BUTTON_CLASSES.cyan}`}
65
81
  >
66
82
  Accept All
67
83
  </button>
@@ -0,0 +1,31 @@
1
+ export interface DebounceBorderOverlayProps {
2
+ debounceKey: number
3
+ durationMs: number
4
+ }
5
+
6
+ export function DebounceBorderOverlay({ debounceKey, durationMs }: DebounceBorderOverlayProps) {
7
+ if (debounceKey <= 0) return null
8
+
9
+ return (
10
+ <svg
11
+ key={debounceKey}
12
+ className="absolute inset-0 pointer-events-none text-emerald-400/70"
13
+ style={{ width: '100%', height: '100%' }}
14
+ >
15
+ <rect
16
+ x="1" y="1" rx="5" ry="5"
17
+ fill="none"
18
+ stroke="currentColor"
19
+ strokeWidth="1.5"
20
+ pathLength="100"
21
+ strokeDasharray="100"
22
+ strokeDashoffset="0"
23
+ style={{
24
+ width: 'calc(100% - 2px)',
25
+ height: 'calc(100% - 2px)',
26
+ animation: `debounce-border-drain ${durationMs}ms linear forwards`,
27
+ }}
28
+ />
29
+ </svg>
30
+ )
31
+ }
@@ -7,26 +7,9 @@
7
7
  * - Any key-value metadata layout
8
8
  */
9
9
 
10
- import {
11
- Info, Settings, Code, Shield, Terminal, Database, Globe, Zap,
12
- Star, Cloud, Bell, Heart, Sparkles, Bot, Plug, Lock, Eye,
13
- File, Folder, User, Users, Tag, Bookmark, Mail, Send, Search,
14
- Play, ShieldCheck, Wand2, Copy,
15
- } from 'lucide-react'
16
- import type { LucideIcon } from 'lucide-react'
17
- import type { IconName } from './icon-button.tsx'
10
+ import { iconMap, type IconName } from './icon-button.tsx'
18
11
  import { cn } from '../lib/cn.ts'
19
12
 
20
- const iconSubset: Partial<Record<IconName, LucideIcon>> = {
21
- info: Info, settings: Settings, code: Code, shield: Shield,
22
- terminal: Terminal, database: Database, globe: Globe, zap: Zap,
23
- star: Star, cloud: Cloud, bell: Bell, heart: Heart,
24
- sparkles: Sparkles, bot: Bot, plug: Plug, lock: Lock, eye: Eye,
25
- file: File, folder: Folder, user: User, users: Users, tag: Tag,
26
- bookmark: Bookmark, mail: Mail, send: Send, search: Search,
27
- play: Play, 'shield-check': ShieldCheck, wand: Wand2, copy: Copy,
28
- }
29
-
30
13
  export interface DetailRow {
31
14
  label: string
32
15
  value: string
@@ -44,19 +27,19 @@ export interface DetailSectionProps {
44
27
  }
45
28
 
46
29
  export function DetailSection({ title, icon, rows, className }: DetailSectionProps) {
47
- const Icon = icon ? iconSubset[icon] : undefined
30
+ const Icon = icon ? iconMap[icon] : undefined
48
31
 
49
32
  return (
50
33
  <div className={className}>
51
34
  <div className="flex items-center gap-2 mb-3">
52
35
  {Icon && <Icon className="w-4 h-4 text-neutral-500" />}
53
- <span className="text-sm font-medium text-neutral-400">{title}</span>
36
+ <span className="text-md font-medium text-neutral-400">{title}</span>
54
37
  </div>
55
38
  <div className="space-y-2">
56
39
  {rows.map((row) => (
57
40
  <div key={row.label} className="flex items-start gap-3">
58
- <span className="text-xs text-neutral-500 w-24 shrink-0">{row.label}:</span>
59
- <span className={cn('text-xs text-neutral-400', row.mono && 'font-mono')}>
41
+ <span className="text-sm text-neutral-500 w-24 shrink-0">{row.label}:</span>
42
+ <span className={cn('text-sm text-neutral-400', row.mono && 'font-mono')}>
60
43
  {row.value}
61
44
  </span>
62
45
  </div>
@@ -60,7 +60,8 @@ export function EditorPlaceholderCard({
60
60
  className = '',
61
61
  }: EditorPlaceholderCardProps) {
62
62
  const [isExpanded, setIsExpanded] = useState(false)
63
- const [isCopied, setIsCopied] = useState(false)
63
+ const [isPlaceholderCopied, setIsPlaceholderCopied] = useState(false)
64
+ const [isValueCopied, setIsValueCopied] = useState(false)
64
65
  const [isOverflowing, setIsOverflowing] = useState(false)
65
66
  const valueRef = useRef<HTMLDivElement>(null)
66
67
 
@@ -78,8 +79,8 @@ export function EditorPlaceholderCard({
78
79
  const handleCopyPlaceholder = async () => {
79
80
  try {
80
81
  await navigator.clipboard.writeText(`{{${name}}}`)
81
- setIsCopied(true)
82
- setTimeout(() => setIsCopied(false), 1500)
82
+ setIsPlaceholderCopied(true)
83
+ setTimeout(() => setIsPlaceholderCopied(false), 1500)
83
84
  } catch {
84
85
  // Clipboard API not available
85
86
  }
@@ -89,8 +90,8 @@ export function EditorPlaceholderCard({
89
90
  if (!value) return
90
91
  try {
91
92
  await navigator.clipboard.writeText(value)
92
- setIsCopied(true)
93
- setTimeout(() => setIsCopied(false), 1500)
93
+ setIsValueCopied(true)
94
+ setTimeout(() => setIsValueCopied(false), 1500)
94
95
  } catch {
95
96
  // Clipboard API not available
96
97
  }
@@ -102,37 +103,37 @@ export function EditorPlaceholderCard({
102
103
  <div className="flex items-start justify-between gap-2">
103
104
  <div className="flex-1 min-w-0">
104
105
  {/* Placeholder name with {{ }} */}
105
- <code className={`text-xs font-mono px-1.5 py-0.5 rounded ${colors.name} ${colors.nameBg}`}>
106
+ <code className={`text-sm font-mono px-1.5 py-0.5 rounded ${colors.name} ${colors.nameBg}`}>
106
107
  {'{{' + name + '}}'}
107
108
  </code>
108
109
  {/* Required badge */}
109
110
  {required && (
110
- <span className="ml-2 inline-block px-1.5 py-0.5 text-xs font-semibold uppercase bg-red-500/15 text-red-400 border border-red-500/30 rounded">
111
+ <span className="ml-2 inline-block px-1.5 py-0.5 text-sm font-semibold uppercase bg-red-500/15 text-red-400 border border-red-500/30 rounded">
111
112
  Required
112
113
  </span>
113
114
  )}
114
115
  {/* Description */}
115
- <p className="text-xs text-neutral-500 mt-1.5 line-clamp-2">{description}</p>
116
+ <p className="text-sm text-neutral-500 mt-1.5 line-clamp-2">{description}</p>
116
117
  </div>
117
118
 
118
119
  {/* Actions (copy for templates, edit/delete for settings) */}
119
120
  <div className="flex items-center gap-1 shrink-0">
120
121
  {showCopyPlaceholder && (
121
122
  <IconButton
122
- icon={isCopied ? 'check' : 'copy'}
123
+ icon={isPlaceholderCopied ? 'check' : 'copy'}
123
124
  onClick={handleCopyPlaceholder}
124
125
  size="sm"
125
- color={isCopied ? 'green' : 'neutral'}
126
+ color={isPlaceholderCopied ? 'green' : 'neutral'}
126
127
  tooltip={{ description: `Copy {{${name}}}` }}
127
128
  tooltipPosition="left"
128
129
  />
129
130
  )}
130
131
  {showCopyValue && hasValue && (
131
132
  <IconButton
132
- icon={isCopied ? 'check' : 'copy'}
133
+ icon={isValueCopied ? 'check' : 'copy'}
133
134
  onClick={handleCopyValue}
134
135
  size="sm"
135
- color={isCopied ? 'green' : 'neutral'}
136
+ color={isValueCopied ? 'green' : 'neutral'}
136
137
  tooltip={{ description: 'Copy value to clipboard' }}
137
138
  tooltipPosition="left"
138
139
  />
@@ -146,7 +147,7 @@ export function EditorPlaceholderCard({
146
147
  <div className="mt-2">
147
148
  {hideValue ? (
148
149
  <>
149
- <span className="text-xs text-neutral-500 font-medium">{valueLabel}</span>
150
+ <span className="text-sm text-neutral-500 font-medium">{valueLabel}</span>
150
151
  <div className="mt-1.5">
151
152
  <Input
152
153
  type="password"
@@ -163,7 +164,7 @@ export function EditorPlaceholderCard({
163
164
  ) : (
164
165
  <>
165
166
  <div className="flex items-center justify-between">
166
- <span className="text-xs text-neutral-500 font-medium">{valueLabel}</span>
167
+ <span className="text-sm text-neutral-500 font-medium">{valueLabel}</span>
167
168
  {(isOverflowing || isExpanded) && (
168
169
  <IconButton
169
170
  icon={isExpanded ? 'chevron-up' : 'chevron-down'}
@@ -175,7 +176,7 @@ export function EditorPlaceholderCard({
175
176
  </div>
176
177
  <div
177
178
  ref={valueRef}
178
- className={`mt-1.5 px-2 py-1.5 bg-neutral-800/50 rounded text-xs text-neutral-400 font-mono ${
179
+ className={`mt-1.5 px-2 py-1.5 bg-neutral-800/50 rounded text-sm text-neutral-400 font-mono ${
179
180
  isExpanded
180
181
  ? 'whitespace-pre-wrap break-all max-h-[190px] overflow-y-auto'
181
182
  : 'truncate'
@@ -190,7 +191,7 @@ export function EditorPlaceholderCard({
190
191
 
191
192
  {/* No value hint */}
192
193
  {!hasValue && (
193
- <p className="mt-1.5 text-xs text-neutral-600 italic">No value set - add one in Settings</p>
194
+ <p className="mt-1.5 text-sm text-neutral-600 italic">No value set - add one in Settings</p>
194
195
  )}
195
196
  </div>
196
197
  )
@@ -4,6 +4,10 @@ import { Label } from './label.tsx'
4
4
  import { ConfirmModal } from './modal.tsx'
5
5
 
6
6
  export interface EditorToolbarProps {
7
+ /** Optional title displayed in the toolbar */
8
+ title?: string
9
+ /** Optional description displayed below the title */
10
+ description?: string
7
11
  /** Whether content has unsaved changes */
8
12
  isDirty: boolean
9
13
  /** Whether save operation is in progress */
@@ -30,6 +34,8 @@ export interface EditorToolbarProps {
30
34
  }
31
35
 
32
36
  export function EditorToolbar({
37
+ title,
38
+ description,
33
39
  isDirty,
34
40
  isSaving = false,
35
41
  onSave,
@@ -66,6 +72,12 @@ export function EditorToolbar({
66
72
  <div className="flex items-center justify-between px-4 py-1.5 bg-neutral-900 border-b border-neutral-800">
67
73
  {/* Left side */}
68
74
  <div className="flex items-center gap-2">
75
+ {(title || description) && (
76
+ <div className="flex flex-col mr-2">
77
+ {title && <span className="text-sm font-medium text-neutral-200">{title}</span>}
78
+ {description && <span className="text-xs text-neutral-500">{description}</span>}
79
+ </div>
80
+ )}
69
81
  {leftActions?.map((a, i) => <IconButton key={i} {...a} />)}
70
82
  </div>
71
83
 
@@ -9,15 +9,10 @@
9
9
  import { AlertTriangle, Info } from 'lucide-react'
10
10
  import { Checkbox } from './checkbox.tsx'
11
11
  import { cn } from '../lib/cn.ts'
12
-
13
- export interface ExecutionDetailRow {
14
- label: string
15
- value: string
16
- mono?: boolean
17
- }
12
+ import type { DetailRow } from './detail-section.tsx'
18
13
 
19
14
  export interface ExecutionDetailsPanelProps {
20
- details: ExecutionDetailRow[]
15
+ details: DetailRow[]
21
16
  /** Show the "Allow direct edits" toggle */
22
17
  allowDirectEdits?: boolean
23
18
  onAllowDirectEditsChange?: (value: boolean) => void
@@ -40,7 +35,7 @@ export function ExecutionDetailsPanel({
40
35
  {/* Header */}
41
36
  <div className="flex items-center gap-2">
42
37
  <Info className="w-4 h-4 text-neutral-500" />
43
- <span className="font-medium text-neutral-400 text-sm">Execution Details</span>
38
+ <span className="font-medium text-neutral-400 text-md">Execution Details</span>
44
39
  </div>
45
40
 
46
41
  {/* Direct edits toggle */}
@@ -57,8 +52,8 @@ export function ExecutionDetailsPanel({
57
52
  />
58
53
  </div>
59
54
  <div>
60
- <span className="text-neutral-300 text-sm">Allow direct file edits</span>
61
- <p className="text-neutral-500 text-xs mt-0.5">
55
+ <span className="text-neutral-300 text-md">Allow direct file edits</span>
56
+ <p className="text-neutral-500 text-sm mt-0.5">
62
57
  {allowDirectEdits
63
58
  ? 'AI will modify files directly. Changes saved immediately.'
64
59
  : 'Changes will be shown in editor for review.'}
@@ -72,7 +67,7 @@ export function ExecutionDetailsPanel({
72
67
  <div className="rounded border border-red-500/50 bg-red-500/10 p-2">
73
68
  <div className="flex items-start gap-2">
74
69
  <AlertTriangle className="h-4 w-4 text-red-400 shrink-0 mt-0.5" />
75
- <p className="text-red-300 text-xs">{warningMessage}</p>
70
+ <p className="text-red-300 text-sm">{warningMessage}</p>
76
71
  </div>
77
72
  </div>
78
73
  )}
@@ -82,8 +77,8 @@ export function ExecutionDetailsPanel({
82
77
  <div className="space-y-2">
83
78
  {details.map((row) => (
84
79
  <div key={row.label} className="flex items-start gap-3">
85
- <span className="text-neutral-500 text-xs w-24 shrink-0">{row.label}:</span>
86
- <span className={cn('text-neutral-300 text-xs', row.mono && 'font-mono')}>{row.value}</span>
80
+ <span className="text-neutral-500 text-sm w-24 shrink-0">{row.label}:</span>
81
+ <span className={cn('text-neutral-300 text-sm', row.mono && 'font-mono')}>{row.value}</span>
87
82
  </div>
88
83
  ))}
89
84
  </div>
@@ -81,11 +81,11 @@ export const ExtensionListCard = memo(function ExtensionListCard({
81
81
  <Icon className={cn('w-5 h-5 shrink-0', iconColor)} />
82
82
  <div className="min-w-0 flex-1">
83
83
  <div className="flex items-center gap-2 flex-wrap">
84
- <span className={cn('text-sm font-medium', titleClassName)}>{title}</span>
84
+ <span className={cn('text-md font-medium', titleClassName)}>{title}</span>
85
85
  {badges}
86
86
  </div>
87
87
  {description && (
88
- <div className={cn('text-xs text-neutral-500 mt-1', !isHovered && 'line-clamp-2')}>{description}</div>
88
+ <div className={cn('text-sm text-neutral-500 mt-1', !isHovered && 'line-clamp-2')}>{description}</div>
89
89
  )}
90
90
  </div>
91
91
  </div>
@@ -96,7 +96,7 @@ export const ExtensionListCard = memo(function ExtensionListCard({
96
96
  )}
97
97
  </div>
98
98
  {metadata != null && (
99
- <div className="flex items-center justify-between mt-2 ml-8 mr-2 text-xs text-neutral-500">
99
+ <div className="flex items-center justify-between mt-2 ml-8 mr-2 text-sm text-neutral-500">
100
100
  {typeof metadata === 'function' ? metadata(isHovered) : metadata}
101
101
  </div>
102
102
  )}