@bibobonking/opensol 0.0.0-stage → 3.0.1
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/.github/workflows/build.yml +25 -0
- package/README.md +74 -2
- package/dist/assets/index-BmN7pBji.css +1 -0
- package/dist/assets/index-DgNDjkOz.js +301 -0
- package/dist/favicon.svg +15 -0
- package/dist/index.html +55 -0
- package/dist/preview-host.html +33 -0
- package/index.html +54 -0
- package/package.json +43 -4
- package/postcss.config.js +6 -0
- package/public/favicon.svg +15 -0
- package/public/preview-host.html +33 -0
- package/shared/plugin-builder.test.mjs +43 -0
- package/shared/plugin-package.d.mts +34 -0
- package/shared/plugin-package.mjs +197 -0
- package/shared/plugin-package.test.mjs +94 -0
- package/src/App.tsx +152 -0
- package/src/agent/adapters/anthropic.ts +233 -0
- package/src/agent/adapters/gemini.ts +225 -0
- package/src/agent/adapters/openai-responses.ts +215 -0
- package/src/agent/adapters/openai.ts +200 -0
- package/src/agent/adapters/shared.ts +96 -0
- package/src/agent/approval.ts +20 -0
- package/src/agent/compact.ts +234 -0
- package/src/agent/index.ts +36 -0
- package/src/agent/model.ts +20 -0
- package/src/agent/prompt.ts +107 -0
- package/src/agent/queue.test.mjs +111 -0
- package/src/agent/queue.ts +84 -0
- package/src/agent/subagent.ts +109 -0
- package/src/agent/tools.ts +541 -0
- package/src/components/ChatView.tsx +403 -0
- package/src/components/CommandPalette.tsx +416 -0
- package/src/components/Composer.tsx +678 -0
- package/src/components/ContextBar.tsx +58 -0
- package/src/components/DeployModal.tsx +512 -0
- package/src/components/ErrorBoundary.tsx +75 -0
- package/src/components/HomeDashboard.tsx +189 -0
- package/src/components/Logo.tsx +41 -0
- package/src/components/Markdown.tsx +232 -0
- package/src/components/MessageItem.tsx +655 -0
- package/src/components/ModelPicker.tsx +487 -0
- package/src/components/PixleSol.tsx +243 -0
- package/src/components/PluginBuilder.tsx +285 -0
- package/src/components/PluginManager.tsx +341 -0
- package/src/components/PreviewPane.tsx +166 -0
- package/src/components/SettingsModal.tsx +2052 -0
- package/src/components/Sidebar.tsx +504 -0
- package/src/components/Splash.tsx +104 -0
- package/src/components/ToolCallCard.tsx +177 -0
- package/src/components/TopBar.tsx +182 -0
- package/src/components/TrajectoryView.tsx +178 -0
- package/src/components/WorkspacePanel.tsx +710 -0
- package/src/components/ui.tsx +311 -0
- package/src/core/kernel.ts +45 -0
- package/src/core/plugins/tools/fs.ts +219 -0
- package/src/core/plugins/tools/git.ts +83 -0
- package/src/core/plugins/tools/grep.ts +98 -0
- package/src/core/plugins/tools/shell.ts +88 -0
- package/src/core/plugins/tools/subagent.ts +36 -0
- package/src/core/plugins/tools/web.ts +97 -0
- package/src/core/services/agent-loop.ts +417 -0
- package/src/core/services/approval.ts +66 -0
- package/src/core/services/compaction.ts +45 -0
- package/src/core/services/llm.ts +32 -0
- package/src/core/services/sessions.ts +47 -0
- package/src/core/services/subagents.ts +15 -0
- package/src/core/services/system-prompt.ts +177 -0
- package/src/core/services/tools.ts +135 -0
- package/src/core/services/ui.ts +109 -0
- package/src/core/types.ts +139 -0
- package/src/data/providers.ts +125 -0
- package/src/data/skills.ts +68 -0
- package/src/index.css +776 -0
- package/src/lib/boot.ts +94 -0
- package/src/lib/context.ts +75 -0
- package/src/lib/conversation.test.mjs +64 -0
- package/src/lib/conversation.ts +59 -0
- package/src/lib/daemon.ts +475 -0
- package/src/lib/hooks.ts +33 -0
- package/src/lib/permissions.ts +60 -0
- package/src/lib/pixlesol.test.mjs +51 -0
- package/src/lib/pixlesol.ts +55 -0
- package/src/lib/plugin-folder.test.mjs +145 -0
- package/src/lib/plugin-folder.ts +96 -0
- package/src/lib/reasoning.ts +87 -0
- package/src/lib/sse.ts +75 -0
- package/src/lib/themes.test.mjs +72 -0
- package/src/lib/themes.ts +52 -0
- package/src/lib/usage.test.mjs +72 -0
- package/src/lib/usage.ts +155 -0
- package/src/lib/utils.ts +73 -0
- package/src/main.tsx +16 -0
- package/src/plugins/api.test.mjs +77 -0
- package/src/plugins/api.ts +113 -0
- package/src/plugins/bridge.d.mts +1 -0
- package/src/plugins/bridge.mjs +26 -0
- package/src/plugins/bridge.test.mjs +17 -0
- package/src/plugins/index.ts +52 -0
- package/src/plugins/preview.test.mjs +18 -0
- package/src/plugins/preview.ts +34 -0
- package/src/plugins/runtime.ts +194 -0
- package/src/plugins/types.ts +40 -0
- package/src/store/chats.ts +278 -0
- package/src/store/models.ts +120 -0
- package/src/store/settings.ts +445 -0
- package/src/store/ui.ts +132 -0
- package/src/store/workspace.ts +299 -0
- package/src/types.ts +337 -0
- package/src/vite-env.d.ts +1 -0
- package/tailwind.config.js +83 -0
- package/tsconfig.json +22 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vite.config.ts +19 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { Fragment, useEffect, useMemo, useRef, useState } from 'react'
|
|
2
|
+
import { createPortal } from 'react-dom'
|
|
3
|
+
import {
|
|
4
|
+
ArrowDownToLine,
|
|
5
|
+
ArrowUpRight,
|
|
6
|
+
Cpu,
|
|
7
|
+
FileText,
|
|
8
|
+
FolderOpen,
|
|
9
|
+
Globe2,
|
|
10
|
+
Info,
|
|
11
|
+
MessageSquare,
|
|
12
|
+
Moon,
|
|
13
|
+
Palette,
|
|
14
|
+
Plus,
|
|
15
|
+
Rocket,
|
|
16
|
+
Search,
|
|
17
|
+
Sparkles,
|
|
18
|
+
Sun,
|
|
19
|
+
Wrench,
|
|
20
|
+
} from 'lucide-react'
|
|
21
|
+
import type { LucideIcon } from 'lucide-react'
|
|
22
|
+
import { useChats, exportChats } from '../store/chats'
|
|
23
|
+
import { useSettings } from '../store/settings'
|
|
24
|
+
import { useUi } from '../store/ui'
|
|
25
|
+
import { useWorkspace } from '../store/workspace'
|
|
26
|
+
import { cn, formatRelativeTime } from '../lib/utils'
|
|
27
|
+
|
|
28
|
+
interface PaletteCommand {
|
|
29
|
+
id: string
|
|
30
|
+
label: string
|
|
31
|
+
hint?: string
|
|
32
|
+
group: string
|
|
33
|
+
icon: LucideIcon
|
|
34
|
+
keywords?: string
|
|
35
|
+
action: () => void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function CommandPalette() {
|
|
39
|
+
const open = useUi((state) => state.paletteOpen)
|
|
40
|
+
const setOpen = useUi((state) => state.setPaletteOpen)
|
|
41
|
+
const openSettings = useUi((state) => state.openSettings)
|
|
42
|
+
const setWorkspaceOpen = useUi((state) => state.setWorkspaceOpen)
|
|
43
|
+
const setDeployOpen = useUi((state) => state.setDeployOpen)
|
|
44
|
+
const focusComposer = useUi((state) => state.focusComposer)
|
|
45
|
+
const notify = useUi((state) => state.notify)
|
|
46
|
+
const sessions = useChats((state) => state.sessions)
|
|
47
|
+
const entries = useWorkspace((state) => state.entries)
|
|
48
|
+
const [query, setQuery] = useState('')
|
|
49
|
+
const [activeIndex, setActiveIndex] = useState(0)
|
|
50
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
51
|
+
const restoreRef = useRef<HTMLElement | null>(null)
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!open) return
|
|
55
|
+
setQuery('')
|
|
56
|
+
setActiveIndex(0)
|
|
57
|
+
restoreRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null
|
|
58
|
+
const previous = document.body.style.overflow
|
|
59
|
+
document.body.style.overflow = 'hidden'
|
|
60
|
+
const timer = window.setTimeout(() => inputRef.current?.focus(), 30)
|
|
61
|
+
return () => {
|
|
62
|
+
window.clearTimeout(timer)
|
|
63
|
+
document.body.style.overflow = previous
|
|
64
|
+
restoreRef.current?.focus()
|
|
65
|
+
}
|
|
66
|
+
}, [open])
|
|
67
|
+
|
|
68
|
+
const commands = useMemo<PaletteCommand[]>(() => {
|
|
69
|
+
const themeIsDark = document.documentElement.classList.contains('dark')
|
|
70
|
+
const close = () => setOpen(false)
|
|
71
|
+
const actions: PaletteCommand[] = [
|
|
72
|
+
{
|
|
73
|
+
id: 'action-new-chat',
|
|
74
|
+
label: 'New chat',
|
|
75
|
+
hint: 'Start a clean conversation',
|
|
76
|
+
group: 'Actions',
|
|
77
|
+
icon: Plus,
|
|
78
|
+
action: () => {
|
|
79
|
+
useChats.getState().newChat()
|
|
80
|
+
focusComposer()
|
|
81
|
+
close()
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'action-workspace',
|
|
86
|
+
label: 'Toggle workspace panel',
|
|
87
|
+
hint: 'Browse and edit files in the open folder',
|
|
88
|
+
group: 'Actions',
|
|
89
|
+
icon: FolderOpen,
|
|
90
|
+
action: () => {
|
|
91
|
+
setWorkspaceOpen(!useUi.getState().workspaceOpen)
|
|
92
|
+
close()
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'action-deploy',
|
|
97
|
+
label: 'Deploy a page',
|
|
98
|
+
hint: 'Cloudflare Pages, Netlify or Vercel',
|
|
99
|
+
group: 'Actions',
|
|
100
|
+
icon: Rocket,
|
|
101
|
+
action: () => {
|
|
102
|
+
setDeployOpen(true)
|
|
103
|
+
close()
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 'action-theme',
|
|
108
|
+
label: themeIsDark ? 'Switch to light theme' : 'Switch to dark theme',
|
|
109
|
+
group: 'Actions',
|
|
110
|
+
icon: themeIsDark ? Sun : Moon,
|
|
111
|
+
action: () => {
|
|
112
|
+
useSettings.getState().update({ theme: themeIsDark ? 'light' : 'dark' })
|
|
113
|
+
close()
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'action-models',
|
|
118
|
+
label: 'Model settings',
|
|
119
|
+
hint: 'Providers, encrypted keys and reasoning',
|
|
120
|
+
group: 'Settings',
|
|
121
|
+
icon: Cpu,
|
|
122
|
+
action: () => {
|
|
123
|
+
openSettings('models')
|
|
124
|
+
close()
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: 'action-tools',
|
|
129
|
+
label: 'Tool permissions',
|
|
130
|
+
hint: 'Approval and enablement',
|
|
131
|
+
group: 'Settings',
|
|
132
|
+
icon: Wrench,
|
|
133
|
+
action: () => {
|
|
134
|
+
openSettings('tools')
|
|
135
|
+
close()
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: 'action-search',
|
|
140
|
+
label: 'Search settings',
|
|
141
|
+
hint: 'Tavily, Brave, Serper, DuckDuckGo',
|
|
142
|
+
group: 'Settings',
|
|
143
|
+
icon: Globe2,
|
|
144
|
+
action: () => {
|
|
145
|
+
openSettings('search')
|
|
146
|
+
close()
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: 'action-skills',
|
|
151
|
+
label: 'Skills',
|
|
152
|
+
hint: 'Load and manage reusable instructions',
|
|
153
|
+
group: 'Settings',
|
|
154
|
+
icon: Sparkles,
|
|
155
|
+
action: () => {
|
|
156
|
+
openSettings('skills')
|
|
157
|
+
close()
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: 'action-deploy-settings',
|
|
162
|
+
label: 'Hosting credentials',
|
|
163
|
+
hint: 'Cloudflare, Netlify and Vercel tokens',
|
|
164
|
+
group: 'Settings',
|
|
165
|
+
icon: Rocket,
|
|
166
|
+
action: () => {
|
|
167
|
+
openSettings('deploy')
|
|
168
|
+
close()
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: 'action-appearance',
|
|
173
|
+
label: 'Appearance',
|
|
174
|
+
hint: 'Theme and token estimates',
|
|
175
|
+
group: 'Settings',
|
|
176
|
+
icon: Palette,
|
|
177
|
+
action: () => {
|
|
178
|
+
openSettings('appearance')
|
|
179
|
+
close()
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: 'action-about',
|
|
184
|
+
label: 'About OpenSol',
|
|
185
|
+
hint: 'Local storage and reset controls',
|
|
186
|
+
group: 'Settings',
|
|
187
|
+
icon: Info,
|
|
188
|
+
action: () => {
|
|
189
|
+
openSettings('about')
|
|
190
|
+
close()
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: 'action-export',
|
|
195
|
+
label: 'Export conversations',
|
|
196
|
+
hint: 'Download a JSON history file',
|
|
197
|
+
group: 'Actions',
|
|
198
|
+
icon: ArrowDownToLine,
|
|
199
|
+
action: () => {
|
|
200
|
+
exportChats()
|
|
201
|
+
notify('Conversation history exported.', 'success')
|
|
202
|
+
close()
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
const chatCommands: PaletteCommand[] = [...sessions]
|
|
208
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
209
|
+
.map((session) => ({
|
|
210
|
+
id: `chat-${session.id}`,
|
|
211
|
+
label: session.title,
|
|
212
|
+
hint: `${session.messages.length} messages · ${formatRelativeTime(session.updatedAt)}`,
|
|
213
|
+
group: 'Conversations',
|
|
214
|
+
icon: MessageSquare,
|
|
215
|
+
keywords: session.messages
|
|
216
|
+
.filter((message) => message.role === 'user')
|
|
217
|
+
.map((message) => message.content)
|
|
218
|
+
.join(' ')
|
|
219
|
+
.slice(0, 1000),
|
|
220
|
+
action: () => {
|
|
221
|
+
useChats.getState().setActive(session.id)
|
|
222
|
+
close()
|
|
223
|
+
},
|
|
224
|
+
}))
|
|
225
|
+
|
|
226
|
+
const fileCommands: PaletteCommand[] = entries
|
|
227
|
+
.filter((entry) => entry.type === 'file')
|
|
228
|
+
.slice(0, 1500)
|
|
229
|
+
.map((entry) => ({
|
|
230
|
+
id: `file-${entry.path}`,
|
|
231
|
+
label: entry.name,
|
|
232
|
+
hint: entry.path,
|
|
233
|
+
group: 'Workspace files',
|
|
234
|
+
icon: FileText,
|
|
235
|
+
keywords: entry.path,
|
|
236
|
+
action: () => {
|
|
237
|
+
useWorkspace.getState().setActiveFile(entry.path)
|
|
238
|
+
setWorkspaceOpen(true)
|
|
239
|
+
close()
|
|
240
|
+
},
|
|
241
|
+
}))
|
|
242
|
+
|
|
243
|
+
return [...actions, ...chatCommands, ...fileCommands]
|
|
244
|
+
}, [sessions, entries, focusComposer, notify, openSettings, setOpen, setWorkspaceOpen, setDeployOpen])
|
|
245
|
+
|
|
246
|
+
const filtered = useMemo(() => {
|
|
247
|
+
const needle = query.trim().toLowerCase()
|
|
248
|
+
const limit = needle ? 40 : 18
|
|
249
|
+
if (!needle) {
|
|
250
|
+
const recentChats = new Set(
|
|
251
|
+
[...sessions]
|
|
252
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
253
|
+
.slice(0, 2)
|
|
254
|
+
.map((session) => `chat-${session.id}`),
|
|
255
|
+
)
|
|
256
|
+
return commands
|
|
257
|
+
.filter(
|
|
258
|
+
(command) => command.group !== 'Conversations' || recentChats.has(command.id),
|
|
259
|
+
)
|
|
260
|
+
.filter((command, index, all) => all.findIndex((item) => item.id === command.id) === index)
|
|
261
|
+
.slice(0, limit)
|
|
262
|
+
}
|
|
263
|
+
return commands
|
|
264
|
+
.filter((command) =>
|
|
265
|
+
`${command.label} ${command.hint ?? ''} ${command.keywords ?? ''} ${command.group}`
|
|
266
|
+
.toLowerCase()
|
|
267
|
+
.includes(needle),
|
|
268
|
+
)
|
|
269
|
+
.slice(0, limit)
|
|
270
|
+
}, [commands, query, sessions])
|
|
271
|
+
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
setActiveIndex(0)
|
|
274
|
+
}, [query])
|
|
275
|
+
|
|
276
|
+
useEffect(() => {
|
|
277
|
+
if (!open) return
|
|
278
|
+
const node = document.querySelector<HTMLElement>(`[data-palette-index="${activeIndex}"]`)
|
|
279
|
+
node?.scrollIntoView({ block: 'nearest' })
|
|
280
|
+
}, [activeIndex, open])
|
|
281
|
+
|
|
282
|
+
useEffect(() => {
|
|
283
|
+
if (activeIndex > filtered.length - 1) setActiveIndex(Math.max(0, filtered.length - 1))
|
|
284
|
+
}, [filtered.length, activeIndex])
|
|
285
|
+
|
|
286
|
+
if (!open) return null
|
|
287
|
+
|
|
288
|
+
const run = (index: number) => {
|
|
289
|
+
const command = filtered[index]
|
|
290
|
+
if (!command) return
|
|
291
|
+
command.action()
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return createPortal(
|
|
295
|
+
<div className="fixed inset-0 z-[90] flex items-start justify-center px-3 pt-[10vh] sm:pt-[14vh]">
|
|
296
|
+
<button
|
|
297
|
+
type="button"
|
|
298
|
+
aria-label="Close command palette"
|
|
299
|
+
tabIndex={-1}
|
|
300
|
+
className="absolute inset-0 cursor-default bg-ink/20 backdrop-blur-[2px] dark:bg-black/45"
|
|
301
|
+
onClick={() => setOpen(false)}
|
|
302
|
+
/>
|
|
303
|
+
<div
|
|
304
|
+
role="dialog"
|
|
305
|
+
aria-modal="true"
|
|
306
|
+
aria-label="Search and commands"
|
|
307
|
+
className="modal-panel relative flex max-h-[min(72dvh,560px)] w-full max-w-[560px] flex-col overflow-hidden rounded-2xl border border-line surface-lift shadow-float"
|
|
308
|
+
>
|
|
309
|
+
<div className="flex h-12 shrink-0 items-center gap-2.5 border-b border-line px-3.5">
|
|
310
|
+
<Search size={15} className="shrink-0 text-faint" />
|
|
311
|
+
<input
|
|
312
|
+
ref={inputRef}
|
|
313
|
+
value={query}
|
|
314
|
+
onChange={(event) => setQuery(event.target.value)}
|
|
315
|
+
onKeyDown={(event) => {
|
|
316
|
+
if (event.key === 'ArrowDown') {
|
|
317
|
+
event.preventDefault()
|
|
318
|
+
if (filtered.length > 0) {
|
|
319
|
+
setActiveIndex((index) => Math.min(index + 1, filtered.length - 1))
|
|
320
|
+
}
|
|
321
|
+
} else if (event.key === 'ArrowUp') {
|
|
322
|
+
event.preventDefault()
|
|
323
|
+
setActiveIndex((index) => Math.max(index - 1, 0))
|
|
324
|
+
} else if (event.key === 'Enter') {
|
|
325
|
+
event.preventDefault()
|
|
326
|
+
run(activeIndex)
|
|
327
|
+
} else if (event.key === 'Escape') {
|
|
328
|
+
setOpen(false)
|
|
329
|
+
}
|
|
330
|
+
}}
|
|
331
|
+
placeholder="Search chats, files, settings, actions…"
|
|
332
|
+
className="h-full min-w-0 flex-1 bg-transparent text-[13px] text-ink outline-none placeholder:text-faint"
|
|
333
|
+
role="combobox"
|
|
334
|
+
aria-expanded="true"
|
|
335
|
+
aria-controls="palette-results"
|
|
336
|
+
aria-activedescendant={filtered[activeIndex] ? `palette-${filtered[activeIndex].id}` : undefined}
|
|
337
|
+
/>
|
|
338
|
+
<kbd className="hidden rounded border border-line bg-raised px-1.5 py-0.5 text-[9px] text-faint sm:block">
|
|
339
|
+
Esc
|
|
340
|
+
</kbd>
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
<div id="palette-results" role="listbox" className="thin-scroll min-h-0 flex-1 overflow-y-auto p-1.5">
|
|
344
|
+
{filtered.length === 0 && (
|
|
345
|
+
<div className="px-4 py-10 text-center">
|
|
346
|
+
<p className="text-[12px] font-medium text-muted">No matches</p>
|
|
347
|
+
<p className="mt-1 text-[10.5px] text-faint">Try a chat title, a file name or “deploy”.</p>
|
|
348
|
+
</div>
|
|
349
|
+
)}
|
|
350
|
+
{filtered.map((command, index) => {
|
|
351
|
+
const Icon = command.icon
|
|
352
|
+
const showGroup = index === 0 || filtered[index - 1].group !== command.group
|
|
353
|
+
return (
|
|
354
|
+
<Fragment key={command.id}>
|
|
355
|
+
{showGroup && (
|
|
356
|
+
<p className="px-2 pb-1 pt-2 text-[9.5px] font-medium uppercase tracking-[0.08em] text-faint">
|
|
357
|
+
{command.group}
|
|
358
|
+
</p>
|
|
359
|
+
)}
|
|
360
|
+
<button
|
|
361
|
+
type="button"
|
|
362
|
+
id={`palette-${command.id}`}
|
|
363
|
+
data-palette-index={index}
|
|
364
|
+
role="option"
|
|
365
|
+
aria-selected={activeIndex === index}
|
|
366
|
+
onMouseMove={() => setActiveIndex(index)}
|
|
367
|
+
onClick={() => run(index)}
|
|
368
|
+
className={cn(
|
|
369
|
+
'flex w-full items-center gap-2.5 rounded-lg px-2 py-2 text-left transition-colors',
|
|
370
|
+
activeIndex === index ? 'bg-raised' : 'hover:bg-raised/60',
|
|
371
|
+
)}
|
|
372
|
+
>
|
|
373
|
+
<span
|
|
374
|
+
className={cn(
|
|
375
|
+
'flex h-6 w-6 shrink-0 items-center justify-center rounded-md border border-line bg-panel',
|
|
376
|
+
activeIndex === index ? 'text-accent' : 'text-muted',
|
|
377
|
+
)}
|
|
378
|
+
>
|
|
379
|
+
<Icon size={12} />
|
|
380
|
+
</span>
|
|
381
|
+
<span className="min-w-0 flex-1">
|
|
382
|
+
<span className="block truncate text-[11.5px] font-medium text-ink">{command.label}</span>
|
|
383
|
+
{command.hint && (
|
|
384
|
+
<span className="mt-0.5 block truncate text-[9.5px] text-faint">{command.hint}</span>
|
|
385
|
+
)}
|
|
386
|
+
</span>
|
|
387
|
+
<ArrowUpRight
|
|
388
|
+
size={11}
|
|
389
|
+
className={cn(
|
|
390
|
+
'shrink-0 text-faint transition-opacity',
|
|
391
|
+
activeIndex === index ? 'opacity-100' : 'opacity-0',
|
|
392
|
+
)}
|
|
393
|
+
/>
|
|
394
|
+
</button>
|
|
395
|
+
</Fragment>
|
|
396
|
+
)
|
|
397
|
+
})}
|
|
398
|
+
</div>
|
|
399
|
+
|
|
400
|
+
<div className="flex h-8 shrink-0 items-center justify-between border-t border-line bg-raised/30 px-3 text-[9px] text-faint">
|
|
401
|
+
<span className="flex items-center gap-2">
|
|
402
|
+
<span>
|
|
403
|
+
<kbd className="rounded border border-line bg-panel px-1">↑</kbd>{' '}
|
|
404
|
+
<kbd className="rounded border border-line bg-panel px-1">↓</kbd> navigate
|
|
405
|
+
</span>
|
|
406
|
+
<span>
|
|
407
|
+
<kbd className="rounded border border-line bg-panel px-1">↵</kbd> open
|
|
408
|
+
</span>
|
|
409
|
+
</span>
|
|
410
|
+
<span>OpenSol</span>
|
|
411
|
+
</div>
|
|
412
|
+
</div>
|
|
413
|
+
</div>,
|
|
414
|
+
document.body,
|
|
415
|
+
)
|
|
416
|
+
}
|