@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.
- package/agent-rules.json +91 -0
- package/ai-manifest.json +190 -0
- package/components/content/info-panel-primitives.tsx +14 -14
- package/components/hooks/use-click-outside.ts +10 -3
- package/components/hooks/use-modal-behavior.ts +24 -0
- package/components/hooks/use-navigation-history.ts +7 -2
- package/components/hooks/use-resizable-sidebar.ts +38 -0
- package/components/lib/ai-tools.tsx +1 -1
- package/components/lib/form-colors.ts +40 -0
- package/components/sections/ai-tools-paths/tools-paths-panel.tsx +7 -7
- package/components/sections/captured-issues/captured-issues-panel.tsx +13 -13
- package/components/sections/captured-issues/use-captured-issues.ts +9 -3
- package/components/sections/golden-snapshots/file-diff-viewer.tsx +13 -13
- package/components/sections/golden-snapshots/golden-sync-panel.tsx +5 -5
- package/components/sections/golden-snapshots/snapshot-manager.tsx +11 -11
- package/components/sections/golden-snapshots/status-overview.tsx +20 -20
- package/components/sections/golden-snapshots/version-manager.tsx +8 -8
- package/components/sections/prompt-editor/file-type-tabbed-prompt-editor.tsx +8 -44
- package/components/sections/prompt-editor/index.ts +0 -7
- package/components/sections/prompt-editor/simulator-prompt-editor.tsx +9 -45
- package/components/sections/prompt-editor/tabbed-prompt-editor.tsx +11 -43
- package/components/sections/report-bug/report-bug-form.tsx +14 -14
- package/components/sections/report-bug/screenshot-uploader.tsx +6 -6
- package/components/sections/snapshot-browser/snapshot-browser-panel.tsx +3 -3
- package/components/sections/snapshot-browser/snapshot-tree.tsx +8 -8
- package/components/sections/snippets-editor/snippets-editor.tsx +74 -48
- package/components/settings/SettingsHeader.tsx +1 -2
- package/components/settings/SettingsTreeNav.tsx +31 -16
- package/components/ui/action-dialog.tsx +12 -56
- package/components/ui/badge.tsx +8 -24
- package/components/ui/bottom-panel-header.tsx +4 -4
- package/components/ui/breadcrumb.tsx +8 -68
- package/components/ui/checkbox.tsx +2 -16
- package/components/ui/collapsible-section.tsx +4 -42
- package/components/ui/confirm-badge.tsx +3 -20
- package/components/ui/cookie-consent.tsx +21 -5
- package/components/ui/debounce-border-overlay.tsx +31 -0
- package/components/ui/detail-section.tsx +5 -22
- package/components/ui/editor-placeholder-card.tsx +17 -16
- package/components/ui/editor-toolbar.tsx +12 -0
- package/components/ui/execution-details-panel.tsx +8 -13
- package/components/ui/extension-list-card.tsx +3 -3
- package/components/ui/file-structure-section.tsx +20 -35
- package/components/ui/file-tree.tsx +4 -14
- package/components/ui/files-panel.tsx +28 -18
- package/components/ui/filter-dropdown.tsx +5 -5
- package/components/ui/form-actions.tsx +7 -6
- package/components/ui/frontmatter-form-header.tsx +4 -4
- package/components/ui/icon-button.tsx +3 -2
- package/components/ui/input.tsx +15 -31
- package/components/ui/label.tsx +7 -21
- package/components/ui/layout-tab-bar.tsx +4 -4
- package/components/ui/modal.tsx +5 -17
- package/components/ui/nav-card.tsx +5 -20
- package/components/ui/navigation-bar.tsx +13 -74
- package/components/ui/number-input.tsx +4 -4
- package/components/ui/registry-browser.tsx +10 -24
- package/components/ui/registry-card.tsx +16 -20
- package/components/ui/registry-detail.tsx +6 -6
- package/components/ui/resizable-textarea.tsx +13 -35
- package/components/ui/segmented-toggle.tsx +6 -5
- package/components/ui/select.tsx +7 -16
- package/components/ui/selection-grid.tsx +6 -54
- package/components/ui/setting-row.tsx +2 -4
- package/components/ui/settings-card.tsx +3 -3
- package/components/ui/settings-info-box.tsx +6 -23
- package/components/ui/settings-section-title.tsx +1 -1
- package/components/ui/snapshot-card.tsx +7 -7
- package/components/ui/snippets-panel.tsx +10 -10
- package/components/ui/sort-dropdown.tsx +2 -2
- package/components/ui/status-card.tsx +6 -17
- package/components/ui/tab-bar.tsx +5 -31
- package/components/ui/toggle.tsx +3 -19
- package/components/ui/tooltip.tsx +9 -21
- package/dist/content.js +14 -14
- package/dist/index.d.ts +71 -141
- package/dist/index.js +1634 -2450
- package/dist/tokens/primitives.css +9 -2
- package/index.ts +8 -7
- package/package.json +13 -3
- package/tokens/primitives.css +9 -2
- package/components/sections/prompt-editor/use-prompt-editor.ts +0 -131
|
@@ -19,19 +19,12 @@
|
|
|
19
19
|
* />
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import { useState,
|
|
22
|
+
import { useState, useCallback } from 'react'
|
|
23
|
+
import { useResizableSidebar } from '../../hooks/use-resizable-sidebar.ts'
|
|
23
24
|
import { FileCode, GripVertical, Crosshair } from 'lucide-react'
|
|
24
25
|
import { TabbedPromptEditor } from './tabbed-prompt-editor.tsx'
|
|
25
26
|
import type { ToolTab, PromptPlaceholder, FileTypeOption } from './types.ts'
|
|
26
27
|
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// Constants
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
const MIN_SIDEBAR_WIDTH = 160
|
|
32
|
-
const MAX_SIDEBAR_WIDTH = 320
|
|
33
|
-
const DEFAULT_SIDEBAR_WIDTH = 200
|
|
34
|
-
|
|
35
28
|
// ---------------------------------------------------------------------------
|
|
36
29
|
// Props
|
|
37
30
|
// ---------------------------------------------------------------------------
|
|
@@ -81,8 +74,7 @@ export function FileTypeTabbedPromptEditor({
|
|
|
81
74
|
className = '',
|
|
82
75
|
}: FileTypeTabbedPromptEditorProps) {
|
|
83
76
|
const [selectedFileType, setSelectedFileType] = useState(fileTypes[0]?.id ?? '')
|
|
84
|
-
const
|
|
85
|
-
const isDraggingRef = useRef(false)
|
|
77
|
+
const { width: sidebarWidth, onPointerDown: handleSidebarPointerDown } = useResizableSidebar({ min: 160, max: 320, defaultWidth: 200, direction: 'right' })
|
|
86
78
|
|
|
87
79
|
// Derive prompts for current file type
|
|
88
80
|
const currentPrompts = prompts[selectedFileType] ?? Object.fromEntries(tools.map((t) => [t.id, '']))
|
|
@@ -105,48 +97,20 @@ export function FileTypeTabbedPromptEditor({
|
|
|
105
97
|
? (tool: string, content: string) => onSave(selectedFileType, tool, content)
|
|
106
98
|
: undefined
|
|
107
99
|
|
|
108
|
-
// Sidebar resize via pointer events
|
|
109
|
-
const handleSidebarPointerDown = useCallback((e: React.PointerEvent) => {
|
|
110
|
-
e.preventDefault()
|
|
111
|
-
e.stopPropagation()
|
|
112
|
-
isDraggingRef.current = true
|
|
113
|
-
const target = e.currentTarget as HTMLElement
|
|
114
|
-
target.setPointerCapture(e.pointerId)
|
|
115
|
-
|
|
116
|
-
const startX = e.clientX
|
|
117
|
-
const startWidth = sidebarWidth
|
|
118
|
-
|
|
119
|
-
const handlePointerMove = (moveEvent: PointerEvent) => {
|
|
120
|
-
if (!isDraggingRef.current) return
|
|
121
|
-
const deltaX = moveEvent.clientX - startX
|
|
122
|
-
const newWidth = Math.max(MIN_SIDEBAR_WIDTH, Math.min(MAX_SIDEBAR_WIDTH, startWidth + deltaX))
|
|
123
|
-
setSidebarWidth(newWidth)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const handlePointerUp = () => {
|
|
127
|
-
isDraggingRef.current = false
|
|
128
|
-
target.removeEventListener('pointermove', handlePointerMove)
|
|
129
|
-
target.removeEventListener('pointerup', handlePointerUp)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
target.addEventListener('pointermove', handlePointerMove)
|
|
133
|
-
target.addEventListener('pointerup', handlePointerUp)
|
|
134
|
-
}, [sidebarWidth])
|
|
135
|
-
|
|
136
100
|
return (
|
|
137
101
|
<div className={`flex w-full max-w-full bg-neutral-900 border border-neutral-700 rounded-lg overflow-hidden ${className}`}>
|
|
138
102
|
{/* Left Sidebar — File Type Selector */}
|
|
139
103
|
<div
|
|
140
104
|
className="relative shrink-0 bg-neutral-950 overflow-hidden flex flex-col"
|
|
141
|
-
style={{ width: sidebarWidth, minWidth:
|
|
105
|
+
style={{ width: sidebarWidth, minWidth: 160, maxWidth: 320 }}
|
|
142
106
|
>
|
|
143
107
|
{/* Header */}
|
|
144
108
|
<div className="h-[52px] px-3 flex items-center border-b border-neutral-700 shrink-0">
|
|
145
109
|
<div className="flex items-center gap-2">
|
|
146
110
|
<Crosshair className="w-4 h-4 text-neutral-500 shrink-0" />
|
|
147
111
|
<div className="min-w-0">
|
|
148
|
-
<div className="text-
|
|
149
|
-
<div className="text-
|
|
112
|
+
<div className="text-sm font-medium text-neutral-400">{selectorLabel}</div>
|
|
113
|
+
<div className="text-sm text-neutral-600">{selectorSublabel}</div>
|
|
150
114
|
</div>
|
|
151
115
|
</div>
|
|
152
116
|
</div>
|
|
@@ -169,11 +133,11 @@ export function FileTypeTabbedPromptEditor({
|
|
|
169
133
|
{ft.icon ?? <FileCode className="w-4 h-4" />}
|
|
170
134
|
</div>
|
|
171
135
|
<div className="min-w-0 flex-1">
|
|
172
|
-
<div className={`text-
|
|
136
|
+
<div className={`text-md font-medium truncate ${isSelected ? 'text-neutral-300' : 'text-neutral-400'}`}>
|
|
173
137
|
{ft.name}
|
|
174
138
|
</div>
|
|
175
139
|
{ft.description && (
|
|
176
|
-
<div className="text-
|
|
140
|
+
<div className="text-sm text-neutral-500 mt-0.5 leading-relaxed">
|
|
177
141
|
{ft.description}
|
|
178
142
|
</div>
|
|
179
143
|
)}
|
|
@@ -97,13 +97,6 @@ export type {
|
|
|
97
97
|
ScenarioOption,
|
|
98
98
|
} from './types.ts'
|
|
99
99
|
|
|
100
|
-
// Hook
|
|
101
|
-
export {
|
|
102
|
-
usePromptEditor,
|
|
103
|
-
type UsePromptEditorOptions,
|
|
104
|
-
type UsePromptEditorReturn,
|
|
105
|
-
} from './use-prompt-editor.ts'
|
|
106
|
-
|
|
107
100
|
// Components
|
|
108
101
|
export {
|
|
109
102
|
TabbedPromptEditor,
|
|
@@ -19,19 +19,12 @@
|
|
|
19
19
|
* />
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import { useState,
|
|
22
|
+
import { useState, useCallback, useMemo } from 'react'
|
|
23
|
+
import { useResizableSidebar } from '../../hooks/use-resizable-sidebar.ts'
|
|
23
24
|
import { ChevronDown, ChevronRight, GripVertical, Crosshair } from 'lucide-react'
|
|
24
25
|
import { TabbedPromptEditor } from './tabbed-prompt-editor.tsx'
|
|
25
26
|
import type { ToolTab, PromptPlaceholder, ScenarioOption } from './types.ts'
|
|
26
27
|
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// Constants
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
const MIN_SIDEBAR_WIDTH = 180
|
|
32
|
-
const MAX_SIDEBAR_WIDTH = 360
|
|
33
|
-
const DEFAULT_SIDEBAR_WIDTH = 220
|
|
34
|
-
|
|
35
28
|
// ---------------------------------------------------------------------------
|
|
36
29
|
// Props
|
|
37
30
|
// ---------------------------------------------------------------------------
|
|
@@ -82,8 +75,7 @@ export function SimulatorPromptEditor({
|
|
|
82
75
|
const [expandedScenarios, setExpandedScenarios] = useState<Set<string>>(
|
|
83
76
|
new Set([defaultScenarioId]),
|
|
84
77
|
)
|
|
85
|
-
const
|
|
86
|
-
const isDraggingRef = useRef(false)
|
|
78
|
+
const { width: sidebarWidth, onPointerDown: handleSidebarPointerDown } = useResizableSidebar({ min: 180, max: 360, defaultWidth: 220, direction: 'right' })
|
|
87
79
|
|
|
88
80
|
// Ensure selected scenario is always expanded
|
|
89
81
|
const effectiveExpanded = useMemo(() => {
|
|
@@ -138,48 +130,20 @@ export function SimulatorPromptEditor({
|
|
|
138
130
|
}
|
|
139
131
|
}
|
|
140
132
|
|
|
141
|
-
// Sidebar resize via pointer events
|
|
142
|
-
const handleSidebarPointerDown = useCallback((e: React.PointerEvent) => {
|
|
143
|
-
e.preventDefault()
|
|
144
|
-
e.stopPropagation()
|
|
145
|
-
isDraggingRef.current = true
|
|
146
|
-
const target = e.currentTarget as HTMLElement
|
|
147
|
-
target.setPointerCapture(e.pointerId)
|
|
148
|
-
|
|
149
|
-
const startX = e.clientX
|
|
150
|
-
const startWidth = sidebarWidth
|
|
151
|
-
|
|
152
|
-
const handlePointerMove = (moveEvent: PointerEvent) => {
|
|
153
|
-
if (!isDraggingRef.current) return
|
|
154
|
-
const deltaX = moveEvent.clientX - startX
|
|
155
|
-
const newWidth = Math.max(MIN_SIDEBAR_WIDTH, Math.min(MAX_SIDEBAR_WIDTH, startWidth + deltaX))
|
|
156
|
-
setSidebarWidth(newWidth)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const handlePointerUp = () => {
|
|
160
|
-
isDraggingRef.current = false
|
|
161
|
-
target.removeEventListener('pointermove', handlePointerMove)
|
|
162
|
-
target.removeEventListener('pointerup', handlePointerUp)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
target.addEventListener('pointermove', handlePointerMove)
|
|
166
|
-
target.addEventListener('pointerup', handlePointerUp)
|
|
167
|
-
}, [sidebarWidth])
|
|
168
|
-
|
|
169
133
|
return (
|
|
170
134
|
<div className={`flex w-full max-w-full bg-neutral-900 border border-neutral-700 rounded-lg overflow-hidden ${className}`}>
|
|
171
135
|
{/* Left Sidebar — Tree Selector */}
|
|
172
136
|
<div
|
|
173
137
|
className="relative shrink-0 bg-neutral-950 overflow-hidden flex flex-col"
|
|
174
|
-
style={{ width: sidebarWidth, minWidth:
|
|
138
|
+
style={{ width: sidebarWidth, minWidth: 180, maxWidth: 360 }}
|
|
175
139
|
>
|
|
176
140
|
{/* Header */}
|
|
177
141
|
<div className="h-[52px] px-3 flex items-center border-b border-neutral-700 shrink-0">
|
|
178
142
|
<div className="flex items-center gap-2">
|
|
179
143
|
<Crosshair className="w-4 h-4 text-neutral-500 shrink-0" />
|
|
180
144
|
<div className="min-w-0">
|
|
181
|
-
<div className="text-
|
|
182
|
-
<div className="text-
|
|
145
|
+
<div className="text-sm font-medium text-neutral-400">Scenario</div>
|
|
146
|
+
<div className="text-sm text-neutral-600">Select scenario and step</div>
|
|
183
147
|
</div>
|
|
184
148
|
</div>
|
|
185
149
|
</div>
|
|
@@ -207,11 +171,11 @@ export function SimulatorPromptEditor({
|
|
|
207
171
|
)}
|
|
208
172
|
</div>
|
|
209
173
|
<div className="min-w-0 flex-1">
|
|
210
|
-
<div className={`text-
|
|
174
|
+
<div className={`text-md font-medium ${isScenarioActive ? 'text-neutral-300' : 'text-neutral-400'}`}>
|
|
211
175
|
{scenario.name}
|
|
212
176
|
</div>
|
|
213
177
|
{scenario.description && (
|
|
214
|
-
<div className="text-
|
|
178
|
+
<div className="text-sm text-neutral-500 mt-0.5 leading-relaxed">
|
|
215
179
|
{scenario.description}
|
|
216
180
|
</div>
|
|
217
181
|
)}
|
|
@@ -236,7 +200,7 @@ export function SimulatorPromptEditor({
|
|
|
236
200
|
<div className={`w-1.5 h-1.5 rounded-full flex-shrink-0 ${
|
|
237
201
|
isStepSelected ? 'bg-blue-400' : 'bg-neutral-600'
|
|
238
202
|
}`} />
|
|
239
|
-
<span className={`text-
|
|
203
|
+
<span className={`text-sm ${isStepSelected ? 'text-neutral-300' : 'text-neutral-400'}`}>
|
|
240
204
|
{step.name}
|
|
241
205
|
</span>
|
|
242
206
|
</button>
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { useState, useRef, useCallback, useEffect, useMemo } from 'react'
|
|
23
|
+
import { useResizableSidebar } from '../../hooks/use-resizable-sidebar.ts'
|
|
23
24
|
import Editor, { type Monaco } from '@monaco-editor/react'
|
|
24
25
|
import type { editor, languages } from 'monaco-editor'
|
|
25
26
|
import { Variable, Info, Search, X, AlertTriangle } from 'lucide-react'
|
|
@@ -37,9 +38,6 @@ import { AiToolIcon } from '../../lib/ai-tools.tsx'
|
|
|
37
38
|
const THEME_NAME = 'prompt-editor-dark'
|
|
38
39
|
let themeRegistered = false
|
|
39
40
|
|
|
40
|
-
const MIN_SIDEBAR_WIDTH = 220
|
|
41
|
-
const MAX_SIDEBAR_WIDTH = 400
|
|
42
|
-
const DEFAULT_SIDEBAR_WIDTH = 280
|
|
43
41
|
const DEFAULT_EDITOR_HEIGHT = 400
|
|
44
42
|
|
|
45
43
|
// ---------------------------------------------------------------------------
|
|
@@ -85,12 +83,11 @@ export function TabbedPromptEditor({
|
|
|
85
83
|
className = '',
|
|
86
84
|
}: TabbedPromptEditorProps) {
|
|
87
85
|
const [activeTab, setActiveTab] = useState(tools[0]?.id ?? '')
|
|
88
|
-
const
|
|
86
|
+
const { width: sidebarWidth, onPointerDown: handleSidebarPointerDown } = useResizableSidebar({ min: 220, max: 400, defaultWidth: 280, direction: 'left' })
|
|
89
87
|
const [variableSearch, setVariableSearch] = useState('')
|
|
90
88
|
const [localContent, setLocalContent] = useState<Record<string, string>>(prompts)
|
|
91
89
|
const [isDirty, setIsDirty] = useState(false)
|
|
92
90
|
|
|
93
|
-
const isDraggingSidebarRef = useRef(false)
|
|
94
91
|
const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null)
|
|
95
92
|
const monacoRef = useRef<Monaco | null>(null)
|
|
96
93
|
const decorationsRef = useRef<string[]>([])
|
|
@@ -236,35 +233,6 @@ export function TabbedPromptEditor({
|
|
|
236
233
|
}
|
|
237
234
|
}, [variables, registerCompletionProvider])
|
|
238
235
|
|
|
239
|
-
// --- Sidebar resize ---
|
|
240
|
-
|
|
241
|
-
const handleSidebarMouseDown = useCallback((e: React.PointerEvent) => {
|
|
242
|
-
e.preventDefault()
|
|
243
|
-
e.stopPropagation()
|
|
244
|
-
isDraggingSidebarRef.current = true
|
|
245
|
-
const target = e.currentTarget as HTMLElement
|
|
246
|
-
target.setPointerCapture(e.pointerId)
|
|
247
|
-
|
|
248
|
-
const startX = e.clientX
|
|
249
|
-
const startWidth = sidebarWidth
|
|
250
|
-
|
|
251
|
-
const handlePointerMove = (moveEvent: PointerEvent) => {
|
|
252
|
-
if (!isDraggingSidebarRef.current) return
|
|
253
|
-
const deltaX = startX - moveEvent.clientX
|
|
254
|
-
const newWidth = Math.max(MIN_SIDEBAR_WIDTH, Math.min(MAX_SIDEBAR_WIDTH, startWidth + deltaX))
|
|
255
|
-
setSidebarWidth(newWidth)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const handlePointerUp = () => {
|
|
259
|
-
isDraggingSidebarRef.current = false
|
|
260
|
-
target.removeEventListener('pointermove', handlePointerMove)
|
|
261
|
-
target.removeEventListener('pointerup', handlePointerUp)
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
target.addEventListener('pointermove', handlePointerMove)
|
|
265
|
-
target.addEventListener('pointerup', handlePointerUp)
|
|
266
|
-
}, [sidebarWidth])
|
|
267
|
-
|
|
268
236
|
// --- Content change ---
|
|
269
237
|
|
|
270
238
|
const handleEditorChange = useCallback(
|
|
@@ -350,7 +318,7 @@ export function TabbedPromptEditor({
|
|
|
350
318
|
<button
|
|
351
319
|
key={tool.id}
|
|
352
320
|
onClick={() => setActiveTab(tool.id)}
|
|
353
|
-
className={`flex-1 flex items-center justify-center gap-1.5 px-2 text-
|
|
321
|
+
className={`flex-1 flex items-center justify-center gap-1.5 px-2 text-sm font-medium border-b-2 transition-colors ${
|
|
354
322
|
isActive
|
|
355
323
|
? `border-current ${activeColor} bg-neutral-850`
|
|
356
324
|
: 'border-neutral-700 text-neutral-500 hover:text-neutral-400 hover:bg-neutral-850/50'
|
|
@@ -368,7 +336,7 @@ export function TabbedPromptEditor({
|
|
|
368
336
|
{checklistMissing && (
|
|
369
337
|
<div className="flex items-start gap-2 px-3 py-2 bg-red-400/10 border-b border-red-400/30 shrink-0">
|
|
370
338
|
<AlertTriangle className="w-4 h-4 text-red-400 shrink-0 mt-0.5" />
|
|
371
|
-
<div className="text-
|
|
339
|
+
<div className="text-sm text-red-400">
|
|
372
340
|
<span className="font-medium">Missing "# Verification Checklist" section.</span>
|
|
373
341
|
{' '}When verification is enabled, only content under this heading will be sent to the AI for output validation.
|
|
374
342
|
</div>
|
|
@@ -431,11 +399,11 @@ export function TabbedPromptEditor({
|
|
|
431
399
|
{hasVariables && (
|
|
432
400
|
<div
|
|
433
401
|
className="relative shrink-0 bg-neutral-950 overflow-hidden flex flex-col border-l border-neutral-700"
|
|
434
|
-
style={{ width: sidebarWidth, minWidth:
|
|
402
|
+
style={{ width: sidebarWidth, minWidth: 220, maxWidth: 400 }}
|
|
435
403
|
>
|
|
436
404
|
{/* Resize handle on left edge */}
|
|
437
405
|
<div
|
|
438
|
-
onPointerDown={
|
|
406
|
+
onPointerDown={handleSidebarPointerDown}
|
|
439
407
|
className="absolute left-0 top-0 bottom-0 w-1 cursor-col-resize hover:bg-violet-400/30 transition-colors z-10"
|
|
440
408
|
/>
|
|
441
409
|
|
|
@@ -444,11 +412,11 @@ export function TabbedPromptEditor({
|
|
|
444
412
|
<div className="flex items-center gap-2">
|
|
445
413
|
<Variable className="w-4 h-4 text-neutral-500 shrink-0" />
|
|
446
414
|
<div className="min-w-0">
|
|
447
|
-
<div className="text-
|
|
448
|
-
<div className="text-
|
|
415
|
+
<div className="text-sm font-medium text-neutral-400">Placeholders</div>
|
|
416
|
+
<div className="text-sm text-neutral-600 truncate">Available placeholders</div>
|
|
449
417
|
</div>
|
|
450
418
|
</div>
|
|
451
|
-
<span className="px-1.5 py-0.5 bg-violet-400/20 text-violet-400 rounded text-
|
|
419
|
+
<span className="px-1.5 py-0.5 bg-violet-400/20 text-violet-400 rounded text-sm">
|
|
452
420
|
{variableSearch.trim() ? `${filteredVariables.length}/${variables!.length}` : variables!.length}
|
|
453
421
|
</span>
|
|
454
422
|
</div>
|
|
@@ -479,7 +447,7 @@ export function TabbedPromptEditor({
|
|
|
479
447
|
|
|
480
448
|
{/* Autocomplete tip */}
|
|
481
449
|
<div className="px-3 py-2 border-b border-neutral-700 shrink-0">
|
|
482
|
-
<div className="flex items-center gap-1.5 text-
|
|
450
|
+
<div className="flex items-center gap-1.5 text-sm text-neutral-500">
|
|
483
451
|
<Info className="w-3 h-3 flex-shrink-0" />
|
|
484
452
|
<span>Type <code className="text-violet-400">{'{{'}</code> in editor for autocomplete</span>
|
|
485
453
|
</div>
|
|
@@ -502,7 +470,7 @@ export function TabbedPromptEditor({
|
|
|
502
470
|
</div>
|
|
503
471
|
))
|
|
504
472
|
) : variableSearch.trim() ? (
|
|
505
|
-
<div className="text-center py-4 text-
|
|
473
|
+
<div className="text-center py-4 text-sm text-neutral-500">
|
|
506
474
|
No placeholders match "{variableSearch}"
|
|
507
475
|
</div>
|
|
508
476
|
) : null}
|
|
@@ -124,13 +124,13 @@ export function ReportBugForm({
|
|
|
124
124
|
return (
|
|
125
125
|
<div className={cn('rounded-lg border border-neutral-700 bg-neutral-800', className)}>
|
|
126
126
|
<div className="border-b border-neutral-700 px-4 py-3">
|
|
127
|
-
<h3 className="text-
|
|
127
|
+
<h3 className="text-md font-medium text-neutral-200">Report a Bug</h3>
|
|
128
128
|
</div>
|
|
129
129
|
|
|
130
130
|
<div className="space-y-4 p-4">
|
|
131
131
|
{/* Issue type pills */}
|
|
132
132
|
<div>
|
|
133
|
-
<label className="mb-2 block text-
|
|
133
|
+
<label className="mb-2 block text-sm text-neutral-400">Issue Type</label>
|
|
134
134
|
<div className="flex flex-wrap gap-2">
|
|
135
135
|
{ISSUE_TYPES.map((type) => (
|
|
136
136
|
<button
|
|
@@ -138,7 +138,7 @@ export function ReportBugForm({
|
|
|
138
138
|
type="button"
|
|
139
139
|
onClick={() => setIssueType(type.value as IssueType)}
|
|
140
140
|
className={cn(
|
|
141
|
-
'px-3 py-1.5 text-
|
|
141
|
+
'px-3 py-1.5 text-sm font-medium rounded-md transition-all',
|
|
142
142
|
issueType === type.value
|
|
143
143
|
? 'bg-blue-600 text-white'
|
|
144
144
|
: 'bg-neutral-700 text-neutral-400 hover:bg-neutral-600 hover:text-neutral-200',
|
|
@@ -152,7 +152,7 @@ export function ReportBugForm({
|
|
|
152
152
|
|
|
153
153
|
{/* Title */}
|
|
154
154
|
<div>
|
|
155
|
-
<label className="mb-1.5 block text-
|
|
155
|
+
<label className="mb-1.5 block text-sm text-neutral-400">
|
|
156
156
|
Title <span className="text-red-400">*</span>
|
|
157
157
|
</label>
|
|
158
158
|
<Input
|
|
@@ -165,7 +165,7 @@ export function ReportBugForm({
|
|
|
165
165
|
|
|
166
166
|
{/* Description */}
|
|
167
167
|
<div>
|
|
168
|
-
<label className="mb-1.5 block text-
|
|
168
|
+
<label className="mb-1.5 block text-sm text-neutral-400">
|
|
169
169
|
Description <span className="text-red-400">*</span>
|
|
170
170
|
</label>
|
|
171
171
|
<ResizableTextarea
|
|
@@ -173,13 +173,13 @@ export function ReportBugForm({
|
|
|
173
173
|
onChange={(e) => setDescription(e.target.value)}
|
|
174
174
|
placeholder="Describe the issue in detail. Include steps to reproduce if applicable."
|
|
175
175
|
rows={6}
|
|
176
|
-
className="w-full px-3 py-1.5 bg-neutral-800 border border-neutral-700 rounded-lg text-
|
|
176
|
+
className="w-full px-3 py-1.5 bg-neutral-800 border border-neutral-700 rounded-lg text-md text-neutral-200 placeholder-neutral-500 focus:outline-none focus:border-blue-500 transition-colors resize-none min-h-[120px]"
|
|
177
177
|
/>
|
|
178
178
|
</div>
|
|
179
179
|
|
|
180
180
|
{/* Email */}
|
|
181
181
|
<div>
|
|
182
|
-
<label className="mb-1.5 block text-
|
|
182
|
+
<label className="mb-1.5 block text-sm text-neutral-400">Email (optional)</label>
|
|
183
183
|
<Input
|
|
184
184
|
type="text"
|
|
185
185
|
value={email}
|
|
@@ -192,7 +192,7 @@ export function ReportBugForm({
|
|
|
192
192
|
|
|
193
193
|
{/* Screenshots */}
|
|
194
194
|
<div>
|
|
195
|
-
<label className="mb-2 block text-
|
|
195
|
+
<label className="mb-2 block text-sm text-neutral-400">
|
|
196
196
|
Screenshots (optional, max 20MB total)
|
|
197
197
|
</label>
|
|
198
198
|
<ScreenshotUploader
|
|
@@ -208,7 +208,7 @@ export function ReportBugForm({
|
|
|
208
208
|
<div className="flex items-center gap-2.5">
|
|
209
209
|
<Checkbox checked={includeLogs} onChange={setIncludeLogs} />
|
|
210
210
|
<span
|
|
211
|
-
className="text-
|
|
211
|
+
className="text-md text-neutral-400 cursor-pointer select-none"
|
|
212
212
|
onClick={() => setIncludeLogs(!includeLogs)}
|
|
213
213
|
>
|
|
214
214
|
Include error logs
|
|
@@ -224,11 +224,11 @@ export function ReportBugForm({
|
|
|
224
224
|
{includeLogs && capturedErrors && capturedErrors.length > 0 && (
|
|
225
225
|
<div className="rounded-md border border-neutral-700 bg-neutral-900/50">
|
|
226
226
|
<div className="px-3 py-2 border-b border-neutral-700">
|
|
227
|
-
<span className="text-
|
|
227
|
+
<span className="text-sm font-medium text-neutral-400">Captured Errors</span>
|
|
228
228
|
</div>
|
|
229
229
|
<div className="max-h-[200px] overflow-y-auto divide-y divide-neutral-800">
|
|
230
230
|
{capturedErrors.map((error) => (
|
|
231
|
-
<div key={error.fingerprint} className="px-3 py-2 text-
|
|
231
|
+
<div key={error.fingerprint} className="px-3 py-2 text-sm">
|
|
232
232
|
<div className="flex items-start gap-2">
|
|
233
233
|
{error.count > 1 && (
|
|
234
234
|
<span className="shrink-0 text-orange-400 font-mono font-medium">
|
|
@@ -251,14 +251,14 @@ export function ReportBugForm({
|
|
|
251
251
|
{/* Footer */}
|
|
252
252
|
<div className="flex items-center justify-end gap-2 border-t border-neutral-700 px-4 py-3">
|
|
253
253
|
{isSubmitting && submissionStatus && (
|
|
254
|
-
<span className="text-
|
|
254
|
+
<span className="text-sm text-neutral-400 mr-auto">{submissionStatus}</span>
|
|
255
255
|
)}
|
|
256
256
|
{onCancel && (
|
|
257
257
|
<button
|
|
258
258
|
type="button"
|
|
259
259
|
onClick={onCancel}
|
|
260
260
|
disabled={isSubmitting}
|
|
261
|
-
className="rounded-md border border-neutral-700 bg-transparent px-3 py-1.5 text-
|
|
261
|
+
className="rounded-md border border-neutral-700 bg-transparent px-3 py-1.5 text-md text-neutral-400 transition-colors hover:bg-neutral-700 hover:text-neutral-200 disabled:opacity-50"
|
|
262
262
|
>
|
|
263
263
|
Cancel
|
|
264
264
|
</button>
|
|
@@ -267,7 +267,7 @@ export function ReportBugForm({
|
|
|
267
267
|
type="button"
|
|
268
268
|
onClick={handleSubmit}
|
|
269
269
|
disabled={!canSubmit}
|
|
270
|
-
className="flex items-center gap-1.5 rounded-md bg-blue-600 px-3 py-1.5 text-
|
|
270
|
+
className="flex items-center gap-1.5 rounded-md bg-blue-600 px-3 py-1.5 text-md text-white transition-colors hover:bg-blue-500 disabled:cursor-not-allowed disabled:opacity-50"
|
|
271
271
|
>
|
|
272
272
|
{isSubmitting ? (
|
|
273
273
|
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
|
@@ -158,10 +158,10 @@ export function ScreenshotUploader({
|
|
|
158
158
|
>
|
|
159
159
|
<ImagePlus className={cn('w-6 h-6', isDragging ? 'text-blue-400' : 'text-neutral-500')} />
|
|
160
160
|
<div className="text-center">
|
|
161
|
-
<p className="text-
|
|
161
|
+
<p className="text-md text-neutral-400">
|
|
162
162
|
{isDragging ? 'Drop images here' : 'Click or drag images to attach'}
|
|
163
163
|
</p>
|
|
164
|
-
<p className="text-
|
|
164
|
+
<p className="text-sm text-neutral-500 mt-1">
|
|
165
165
|
{formatFileSize(remainingSize)} remaining of {formatFileSize(maxTotalSize)}
|
|
166
166
|
</p>
|
|
167
167
|
</div>
|
|
@@ -177,7 +177,7 @@ export function ScreenshotUploader({
|
|
|
177
177
|
</div>
|
|
178
178
|
|
|
179
179
|
{error && (
|
|
180
|
-
<div className="flex items-center gap-2 text-
|
|
180
|
+
<div className="flex items-center gap-2 text-md text-red-400">
|
|
181
181
|
<AlertCircle className="w-4 h-4 flex-shrink-0" />
|
|
182
182
|
<span>{error}</span>
|
|
183
183
|
</div>
|
|
@@ -207,9 +207,9 @@ export function ScreenshotUploader({
|
|
|
207
207
|
>
|
|
208
208
|
<X className="w-3 h-3" />
|
|
209
209
|
</button>
|
|
210
|
-
<span className="text-
|
|
210
|
+
<span className="text-sm text-white truncate">{s.filename}</span>
|
|
211
211
|
</div>
|
|
212
|
-
<div className="absolute bottom-1 right-1 px-1.5 py-0.5 bg-[var(--background)]/70 rounded text-
|
|
212
|
+
<div className="absolute bottom-1 right-1 px-1.5 py-0.5 bg-[var(--background)]/70 rounded text-sm text-neutral-400">
|
|
213
213
|
{formatFileSize(s.size)}
|
|
214
214
|
</div>
|
|
215
215
|
</div>
|
|
@@ -218,7 +218,7 @@ export function ScreenshotUploader({
|
|
|
218
218
|
)}
|
|
219
219
|
|
|
220
220
|
{screenshots.length > 0 && (
|
|
221
|
-
<div className="flex items-center justify-between text-
|
|
221
|
+
<div className="flex items-center justify-between text-sm text-neutral-500">
|
|
222
222
|
<span>{screenshots.length} image{screenshots.length !== 1 ? 's' : ''} attached</span>
|
|
223
223
|
<span>Total: {formatFileSize(totalSize)}</span>
|
|
224
224
|
</div>
|
|
@@ -75,7 +75,7 @@ export function SnapshotBrowserPanel({
|
|
|
75
75
|
<div className="flex items-center justify-between">
|
|
76
76
|
<div>
|
|
77
77
|
<label className="text-neutral-300">Snapshot Limit</label>
|
|
78
|
-
<p className="text-
|
|
78
|
+
<p className="text-md text-neutral-500">
|
|
79
79
|
Maximum number of snapshots to keep per item (1-50)
|
|
80
80
|
</p>
|
|
81
81
|
</div>
|
|
@@ -92,7 +92,7 @@ export function SnapshotBrowserPanel({
|
|
|
92
92
|
<div className="flex items-center justify-between mb-3">
|
|
93
93
|
<div>
|
|
94
94
|
<label className="text-neutral-300">Browse Snapshots</label>
|
|
95
|
-
<p className="text-
|
|
95
|
+
<p className="text-md text-neutral-500">
|
|
96
96
|
{totalSnapshotCount === 0
|
|
97
97
|
? 'No snapshots saved yet'
|
|
98
98
|
: `${totalSnapshotCount} snapshot${totalSnapshotCount === 1 ? '' : 's'} stored`}
|
|
@@ -129,7 +129,7 @@ export function SnapshotBrowserPanel({
|
|
|
129
129
|
<div className="bg-neutral-900/50 border border-neutral-700 rounded-lg p-4">
|
|
130
130
|
<div className="flex items-start gap-3">
|
|
131
131
|
<HelpCircle className="w-4 h-4 text-neutral-500 mt-0.5 shrink-0" />
|
|
132
|
-
<div className="text-
|
|
132
|
+
<div className="text-md text-neutral-500 space-y-2">
|
|
133
133
|
<p>
|
|
134
134
|
<strong className="text-neutral-400">How snapshots work:</strong>
|
|
135
135
|
</p>
|
|
@@ -194,14 +194,14 @@ function SnapshotEntryRow({
|
|
|
194
194
|
|
|
195
195
|
return (
|
|
196
196
|
<div
|
|
197
|
-
className="flex items-center gap-2 px-2 py-1.5 text-
|
|
197
|
+
className="flex items-center gap-2 px-2 py-1.5 text-md rounded-md group transition-colors hover:bg-neutral-700/50 text-neutral-400"
|
|
198
198
|
style={{ paddingLeft: `${depth * 16 + 8}px` }}
|
|
199
199
|
>
|
|
200
200
|
<Clock className="w-3 h-3 shrink-0 text-neutral-500" />
|
|
201
|
-
<span className="text-
|
|
201
|
+
<span className="text-sm flex-1 truncate">
|
|
202
202
|
{searchQuery ? highlightMatch(displayName, searchQuery) : displayName}
|
|
203
203
|
</span>
|
|
204
|
-
<span className="text-
|
|
204
|
+
<span className="text-xs text-neutral-500 shrink-0" title={formatFullDate(entry.savedAt)}>
|
|
205
205
|
{formatRelativeTime(entry.savedAt)}
|
|
206
206
|
</span>
|
|
207
207
|
<IconButton
|
|
@@ -248,7 +248,7 @@ function ExpandableNode({
|
|
|
248
248
|
<div>
|
|
249
249
|
<button
|
|
250
250
|
onClick={() => onToggle(path)}
|
|
251
|
-
className="w-full flex items-center gap-2 px-2 py-1.5 text-
|
|
251
|
+
className="w-full flex items-center gap-2 px-2 py-1.5 text-md rounded-md transition-colors cursor-pointer text-neutral-400 hover:bg-neutral-700/50 hover:text-neutral-300"
|
|
252
252
|
style={{ paddingLeft: `${depth * 16 + 8}px` }}
|
|
253
253
|
>
|
|
254
254
|
<span className="w-4 h-4 flex items-center justify-center shrink-0">
|
|
@@ -266,7 +266,7 @@ function ExpandableNode({
|
|
|
266
266
|
</span>
|
|
267
267
|
|
|
268
268
|
{snapshotCount > 0 && (
|
|
269
|
-
<span className="text-
|
|
269
|
+
<span className="text-sm text-neutral-500 bg-neutral-700 px-1.5 py-0.5 rounded shrink-0">
|
|
270
270
|
{snapshotCount}
|
|
271
271
|
</span>
|
|
272
272
|
)}
|
|
@@ -322,10 +322,10 @@ export function SnapshotTree({
|
|
|
322
322
|
|
|
323
323
|
if (totalCount === 0) {
|
|
324
324
|
return (
|
|
325
|
-
<div className={cn('text-
|
|
325
|
+
<div className={cn('text-md text-neutral-500 py-8 text-center', className)}>
|
|
326
326
|
No snapshots saved yet.
|
|
327
327
|
<br />
|
|
328
|
-
<span className="text-
|
|
328
|
+
<span className="text-sm">Use the camera button in editors to save snapshots.</span>
|
|
329
329
|
</div>
|
|
330
330
|
)
|
|
331
331
|
}
|
|
@@ -377,7 +377,7 @@ export function SnapshotTree({
|
|
|
377
377
|
{/* Tree */}
|
|
378
378
|
<div className="bg-neutral-900 border border-neutral-700 rounded-lg p-2 min-h-[200px] max-h-[60vh] overflow-y-auto">
|
|
379
379
|
{filteredScopes.length === 0 && searchQuery ? (
|
|
380
|
-
<p className="text-
|
|
380
|
+
<p className="text-sm text-neutral-500 text-center py-4">
|
|
381
381
|
No snapshots match “{searchQuery}”
|
|
382
382
|
</p>
|
|
383
383
|
) : (
|