@workerdeck/ui 0.9.0 → 0.12.0
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/README.md +81 -3
- package/build/SessionPanel-Dy9lQrOV.d.mts +319 -0
- package/build/SessionPanel-NQ8ksCfj.mjs +8474 -0
- package/build/SessionPanel-NQ8ksCfj.mjs.map +1 -0
- package/build/format-DqR56Y8l.mjs +162 -0
- package/build/format-DqR56Y8l.mjs.map +1 -0
- package/build/format-ljc3lKpA.d.mts +59 -0
- package/build/format.d.mts +66 -0
- package/build/format.mjs +119 -0
- package/build/format.mjs.map +1 -0
- package/build/index.d.mts +671 -88
- package/build/index.mjs +387 -5160
- package/build/index.mjs.map +1 -1
- package/build/workspace.d.mts +226 -0
- package/build/workspace.mjs +861 -0
- package/build/workspace.mjs.map +1 -0
- package/package.json +22 -4
- package/src/components/agent/CodeEditor.tsx +300 -0
- package/src/components/agent/Composer.tsx +522 -87
- package/src/components/agent/ContextDialog.tsx +99 -0
- package/src/components/agent/Conversation.tsx +11 -3
- package/src/components/agent/EditorTabs.tsx +165 -0
- package/src/components/agent/FileCard.tsx +26 -0
- package/src/components/agent/FileTree.tsx +287 -0
- package/src/components/agent/FileViewer.tsx +148 -0
- package/src/components/agent/HostFilesDialog.tsx +218 -0
- package/src/components/agent/Loader.tsx +82 -14
- package/src/components/agent/McpDialog.tsx +363 -0
- package/src/components/agent/Message.tsx +51 -17
- package/src/components/agent/ModelSelect.tsx +34 -6
- package/src/components/agent/PermissionModeSelect.tsx +133 -22
- package/src/components/agent/PermissionPrompt.tsx +164 -6
- package/src/components/agent/PromptTokenText.tsx +39 -0
- package/src/components/agent/QuestionPrompt.tsx +122 -0
- package/src/components/agent/Reasoning.tsx +20 -5
- package/src/components/agent/Response.tsx +128 -0
- package/src/components/agent/SessionBrowser.tsx +428 -0
- package/src/components/agent/SessionEmptyState.tsx +65 -0
- package/src/components/agent/SessionInfoDialog.tsx +163 -0
- package/src/components/agent/SessionPanel.tsx +783 -91
- package/src/components/agent/SessionWorkspace.tsx +317 -0
- package/src/components/agent/SkillsDialog.tsx +195 -0
- package/src/components/agent/StatusBar.tsx +85 -18
- package/src/components/agent/ToolCallCard.tsx +252 -30
- package/src/components/agent/Transcript.tsx +513 -30
- package/src/components/agent/UsageDialog.tsx +168 -0
- package/src/components/agent/line-prompt.tsx +249 -0
- package/src/components/agent/pulse.tsx +60 -0
- package/src/components/agent/transcript-variant.tsx +123 -0
- package/src/components/prompt-area/prompt-area-engine.ts +53 -0
- package/src/components/prompt-area/types.ts +15 -0
- package/src/components/prompt-area/use-prompt-area.ts +20 -0
- package/src/components/ui/CodeBlock.tsx +40 -2
- package/src/components/ui/CopyButton.tsx +28 -3
- package/src/components/ui/Dialog.tsx +92 -0
- package/src/components/ui/Menu.tsx +55 -0
- package/src/components/ui/Splitter.tsx +133 -0
- package/src/components/ui/Tooltip.tsx +22 -5
- package/src/format.ts +11 -0
- package/src/index.ts +67 -2
- package/src/lib/clipboard.ts +56 -0
- package/src/lib/format.ts +114 -0
- package/src/lib/status.ts +124 -0
- package/src/lib/tool-icon.ts +96 -0
- package/src/workspace.ts +28 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import type { WorkerDeckClient } from '@workerdeck/client'
|
|
3
|
+
import type { SessionFileInfo } from '@workerdeck/protocol'
|
|
4
|
+
import type { TranscriptState } from '@workerdeck/react'
|
|
5
|
+
import { Download } from 'lucide-react'
|
|
6
|
+
import { CopyButton } from '../ui/CopyButton.tsx'
|
|
7
|
+
import { Dialog, DialogBody, DialogContent, DialogHeader, DialogRow } from '../ui/Dialog.tsx'
|
|
8
|
+
import { Spinner } from '../ui/Spinner.tsx'
|
|
9
|
+
import { formatBytes, formatCost, formatRelativeTime } from '../../lib/format.ts'
|
|
10
|
+
import { permissionModeMeta } from './PermissionModeSelect.tsx'
|
|
11
|
+
|
|
12
|
+
export interface SessionInfoDialogProps {
|
|
13
|
+
state: TranscriptState
|
|
14
|
+
client: WorkerDeckClient
|
|
15
|
+
sessionId: string | undefined
|
|
16
|
+
open: boolean
|
|
17
|
+
onOpenChange: (open: boolean) => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* What this session *is*: engine, profile, model, mode, where it runs, which
|
|
22
|
+
* credentials it found, and the files it has handed over.
|
|
23
|
+
*
|
|
24
|
+
* The identity half of the session's facts. Context and usage have their own
|
|
25
|
+
* panels — they change every turn and are consulted mid-run, while everything
|
|
26
|
+
* here is fixed at creation and looked up once.
|
|
27
|
+
*/
|
|
28
|
+
export function SessionInfoDialog({
|
|
29
|
+
state,
|
|
30
|
+
client,
|
|
31
|
+
sessionId,
|
|
32
|
+
open,
|
|
33
|
+
onOpenChange,
|
|
34
|
+
}: SessionInfoDialogProps) {
|
|
35
|
+
const session = state.session
|
|
36
|
+
const mode = state.permissionMode ? permissionModeMeta(state.permissionMode) : undefined
|
|
37
|
+
return (
|
|
38
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
39
|
+
<DialogContent>
|
|
40
|
+
<DialogHeader title='Session info' description={session?.title} />
|
|
41
|
+
<DialogBody>
|
|
42
|
+
<div className='flex flex-col divide-y divide-border'>
|
|
43
|
+
<div className='pb-2'>
|
|
44
|
+
<DialogRow label='Engine'>{state.engine ?? 'claude'}</DialogRow>
|
|
45
|
+
{session?.profile ? (
|
|
46
|
+
<DialogRow label='Profile'>{session.profile}</DialogRow>
|
|
47
|
+
) : null}
|
|
48
|
+
{state.model ? (
|
|
49
|
+
<DialogRow label='Model' mono>
|
|
50
|
+
{state.model}
|
|
51
|
+
</DialogRow>
|
|
52
|
+
) : null}
|
|
53
|
+
{mode ? <DialogRow label='Permission mode'>{mode.label}</DialogRow> : null}
|
|
54
|
+
{session?.apiKeySource ? (
|
|
55
|
+
<DialogRow label='Credentials'>{session.apiKeySource}</DialogRow>
|
|
56
|
+
) : null}
|
|
57
|
+
</div>
|
|
58
|
+
<div className='py-2'>
|
|
59
|
+
{state.cwd ? <CopyRow label='Working directory' value={state.cwd} /> : null}
|
|
60
|
+
{state.sdkSessionId ? (
|
|
61
|
+
<CopyRow label='Engine session id' value={state.sdkSessionId} />
|
|
62
|
+
) : null}
|
|
63
|
+
{session ? <CopyRow label='Gateway session id' value={session.id} /> : null}
|
|
64
|
+
</div>
|
|
65
|
+
<div className='py-2'>
|
|
66
|
+
{session?.createdAt ? (
|
|
67
|
+
<DialogRow label='Started'>{formatRelativeTime(session.createdAt)}</DialogRow>
|
|
68
|
+
) : null}
|
|
69
|
+
{session?.numTurns !== undefined ? (
|
|
70
|
+
<DialogRow label='Turns'>{session.numTurns}</DialogRow>
|
|
71
|
+
) : null}
|
|
72
|
+
<DialogRow label='Cost' mono>
|
|
73
|
+
{formatCost(state.totalCostUsd)}
|
|
74
|
+
</DialogRow>
|
|
75
|
+
</div>
|
|
76
|
+
{/* Only for an engine that has a scratch VFS — for the rest the route
|
|
77
|
+
404s, and a "Files" heading over nothing reads as a failed load. */}
|
|
78
|
+
{state.capabilities.vfs ? (
|
|
79
|
+
<SessionFiles client={client} sessionId={sessionId} open={open} />
|
|
80
|
+
) : null}
|
|
81
|
+
</div>
|
|
82
|
+
</DialogBody>
|
|
83
|
+
</DialogContent>
|
|
84
|
+
</Dialog>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Long ids are for pasting elsewhere, so give them a copy target. */
|
|
89
|
+
function CopyRow({ label, value }: { label: string; value: string }) {
|
|
90
|
+
return (
|
|
91
|
+
<div className='flex items-start justify-between gap-2 py-1.5'>
|
|
92
|
+
<div className='min-w-0 flex-1'>
|
|
93
|
+
<div className='text-label text-fg-3'>{label}</div>
|
|
94
|
+
<div className='mt-0.5 font-mono text-label break-all text-fg-1'>{value}</div>
|
|
95
|
+
</div>
|
|
96
|
+
<CopyButton value={value} aria-label={`Copy ${label.toLowerCase()}`} />
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Files the agent produced inside the session's VFS. Fetched when the panel
|
|
102
|
+
* opens — this is not a live list and nothing pushes changes to it. */
|
|
103
|
+
function SessionFiles({
|
|
104
|
+
client,
|
|
105
|
+
sessionId,
|
|
106
|
+
open,
|
|
107
|
+
}: {
|
|
108
|
+
client: WorkerDeckClient
|
|
109
|
+
sessionId: string | undefined
|
|
110
|
+
open: boolean
|
|
111
|
+
}) {
|
|
112
|
+
const [files, setFiles] = useState<SessionFileInfo[] | undefined>()
|
|
113
|
+
const [loading, setLoading] = useState(false)
|
|
114
|
+
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (!open || !sessionId) return
|
|
117
|
+
let cancelled = false
|
|
118
|
+
setLoading(true)
|
|
119
|
+
client
|
|
120
|
+
.listSessionFiles(sessionId)
|
|
121
|
+
.then((list) => {
|
|
122
|
+
if (!cancelled) setFiles(list)
|
|
123
|
+
})
|
|
124
|
+
.catch(() => {
|
|
125
|
+
if (!cancelled) setFiles([])
|
|
126
|
+
})
|
|
127
|
+
.finally(() => {
|
|
128
|
+
if (!cancelled) setLoading(false)
|
|
129
|
+
})
|
|
130
|
+
return () => {
|
|
131
|
+
cancelled = true
|
|
132
|
+
}
|
|
133
|
+
}, [client, sessionId, open])
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<div className='pt-3'>
|
|
137
|
+
<h3 className='text-label font-medium text-fg-3'>Files</h3>
|
|
138
|
+
{loading ? (
|
|
139
|
+
<div className='py-3 text-center'>
|
|
140
|
+
<Spinner className='size-4 text-fg-4' />
|
|
141
|
+
</div>
|
|
142
|
+
) : !files?.length ? (
|
|
143
|
+
<p className='py-2 text-body-sm text-fg-4'>Nothing delivered yet.</p>
|
|
144
|
+
) : (
|
|
145
|
+
<ul className='mt-1 flex flex-col'>
|
|
146
|
+
{files.map((file) => (
|
|
147
|
+
<li key={file.path}>
|
|
148
|
+
<a
|
|
149
|
+
href={sessionId ? client.sessionFileUrl(sessionId, file.path) : undefined}
|
|
150
|
+
className='flex items-center gap-2 rounded-md px-1 py-1.5 hover:bg-surface-hover'>
|
|
151
|
+
<Download className='size-3.5 shrink-0 text-fg-4' />
|
|
152
|
+
<span className='min-w-0 flex-1 truncate font-mono text-label text-fg-1'>
|
|
153
|
+
{file.path}
|
|
154
|
+
</span>
|
|
155
|
+
<span className='shrink-0 text-label text-fg-4'>{formatBytes(file.bytes)}</span>
|
|
156
|
+
</a>
|
|
157
|
+
</li>
|
|
158
|
+
))}
|
|
159
|
+
</ul>
|
|
160
|
+
)}
|
|
161
|
+
</div>
|
|
162
|
+
)
|
|
163
|
+
}
|