@cortexkit/opencode-magic-context 0.4.2 → 0.5.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 +41 -0
- package/dist/cli/config-paths.d.ts +2 -0
- package/dist/cli/config-paths.d.ts.map +1 -1
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli.js +8549 -125
- package/dist/features/builtin-commands/commands.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
- package/dist/features/magic-context/scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/search.d.ts +2 -0
- package/dist/features/magic-context/search.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/send-session-notification.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8653 -238
- package/dist/plugin/conflict-warning-hook.d.ts +24 -0
- package/dist/plugin/conflict-warning-hook.d.ts.map +1 -0
- package/dist/shared/conflict-detector.d.ts +29 -0
- package/dist/shared/conflict-detector.d.ts.map +1 -0
- package/dist/shared/conflict-fixer.d.ts +3 -0
- package/dist/shared/conflict-fixer.d.ts.map +1 -0
- package/dist/shared/tui-config.d.ts +10 -0
- package/dist/shared/tui-config.d.ts.map +1 -0
- package/dist/tools/ctx-search/tools.d.ts.map +1 -1
- package/dist/tui/data/context-db.d.ts +54 -0
- package/dist/tui/data/context-db.d.ts.map +1 -0
- package/package.json +20 -1
- package/src/tui/data/context-db.ts +584 -0
- package/src/tui/index.tsx +461 -0
- package/src/tui/slots/sidebar-content.tsx +422 -0
- package/src/tui/types/opencode-plugin-tui.d.ts +232 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import { createEffect, createMemo, createSignal, on, onCleanup } from "solid-js"
|
|
3
|
+
import type { TuiSlotPlugin, TuiPluginApi, TuiThemeCurrent } from "@opencode-ai/plugin/tui"
|
|
4
|
+
import { loadSidebarSnapshot, type SidebarSnapshot } from "../data/context-db"
|
|
5
|
+
|
|
6
|
+
const SINGLE_BORDER = { type: "single" } as any
|
|
7
|
+
const REFRESH_DEBOUNCE_MS = 150
|
|
8
|
+
|
|
9
|
+
function compactTokens(value: number): string {
|
|
10
|
+
if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`
|
|
11
|
+
if (value >= 1_000) return `${(value / 1_000).toFixed(0)}K`
|
|
12
|
+
return String(value)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function relativeTime(ms: number): string {
|
|
16
|
+
const diff = Date.now() - ms
|
|
17
|
+
if (diff < 60_000) return "just now"
|
|
18
|
+
if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`
|
|
19
|
+
if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`
|
|
20
|
+
return `${Math.floor(diff / 86_400_000)}d ago`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Token breakdown segment colors (hardcoded hex values)
|
|
24
|
+
const COLORS = {
|
|
25
|
+
system: "#c084fc", // Purple-ish
|
|
26
|
+
compartments: "#60a5fa", // Blue-ish
|
|
27
|
+
facts: "#fbbf24", // Yellow/orange
|
|
28
|
+
memories: "#34d399", // Green
|
|
29
|
+
conversation: "#9ca3af", // Gray (will use theme.textMuted)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface TokenSegment {
|
|
33
|
+
key: string
|
|
34
|
+
tokens: number
|
|
35
|
+
color: string
|
|
36
|
+
label: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Segmented token breakdown bar with legend
|
|
40
|
+
const TokenBreakdown = (props: {
|
|
41
|
+
theme: TuiThemeCurrent
|
|
42
|
+
snapshot: SidebarSnapshot
|
|
43
|
+
}) => {
|
|
44
|
+
const barWidth = 36
|
|
45
|
+
|
|
46
|
+
const segments = createMemo<TokenSegment[]>(() => {
|
|
47
|
+
const s = props.snapshot
|
|
48
|
+
const total = s.inputTokens || 1
|
|
49
|
+
const result: TokenSegment[] = []
|
|
50
|
+
|
|
51
|
+
// System Prompt (purple)
|
|
52
|
+
if (s.systemPromptTokens > 0) {
|
|
53
|
+
result.push({
|
|
54
|
+
key: "sys",
|
|
55
|
+
tokens: s.systemPromptTokens,
|
|
56
|
+
color: COLORS.system,
|
|
57
|
+
label: "System",
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Compartments (blue)
|
|
62
|
+
if (s.compartmentTokens > 0) {
|
|
63
|
+
result.push({
|
|
64
|
+
key: "comp",
|
|
65
|
+
tokens: s.compartmentTokens,
|
|
66
|
+
color: COLORS.compartments,
|
|
67
|
+
label: "Compartments",
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Facts (yellow/orange)
|
|
72
|
+
if (s.factTokens > 0) {
|
|
73
|
+
result.push({
|
|
74
|
+
key: "fact",
|
|
75
|
+
tokens: s.factTokens,
|
|
76
|
+
color: COLORS.facts,
|
|
77
|
+
label: "Facts",
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Memories (green)
|
|
82
|
+
if (s.memoryTokens > 0) {
|
|
83
|
+
result.push({
|
|
84
|
+
key: "mem",
|
|
85
|
+
tokens: s.memoryTokens,
|
|
86
|
+
color: COLORS.memories,
|
|
87
|
+
label: "Memories",
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Conversation = remaining tokens (gray)
|
|
92
|
+
const used = s.systemPromptTokens + s.compartmentTokens + s.factTokens + s.memoryTokens
|
|
93
|
+
const convTokens = Math.max(0, s.inputTokens - used)
|
|
94
|
+
if (convTokens > 0) {
|
|
95
|
+
result.push({
|
|
96
|
+
key: "conv",
|
|
97
|
+
tokens: convTokens,
|
|
98
|
+
color: props.theme.textMuted,
|
|
99
|
+
label: "Conversation",
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return result
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const totalTokens = createMemo(() => props.snapshot.inputTokens || 1)
|
|
107
|
+
|
|
108
|
+
// Calculate proportional widths for each segment
|
|
109
|
+
const segmentWidths = createMemo(() => {
|
|
110
|
+
const total = totalTokens()
|
|
111
|
+
const segs = segments()
|
|
112
|
+
if (segs.length === 0) return []
|
|
113
|
+
|
|
114
|
+
// Calculate raw proportions
|
|
115
|
+
const proportions = segs.map((seg) => seg.tokens / total)
|
|
116
|
+
|
|
117
|
+
// Convert to character widths (minimum 1 char if tokens > 0)
|
|
118
|
+
let widths = proportions.map((p) => Math.max(1, Math.round(p * barWidth)))
|
|
119
|
+
|
|
120
|
+
// Adjust to exactly barWidth
|
|
121
|
+
const sum = widths.reduce((a, b) => a + b, 0)
|
|
122
|
+
if (sum > barWidth) {
|
|
123
|
+
// Shrink from the largest segments
|
|
124
|
+
let excess = sum - barWidth
|
|
125
|
+
while (excess > 0) {
|
|
126
|
+
const maxIdx = widths.indexOf(Math.max(...widths))
|
|
127
|
+
if (widths[maxIdx] > 1) {
|
|
128
|
+
widths[maxIdx]--
|
|
129
|
+
excess--
|
|
130
|
+
} else {
|
|
131
|
+
break
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} else if (sum < barWidth) {
|
|
135
|
+
// Expand the largest segments
|
|
136
|
+
let deficit = barWidth - sum
|
|
137
|
+
while (deficit > 0) {
|
|
138
|
+
const maxIdx = widths.indexOf(Math.max(...widths))
|
|
139
|
+
widths[maxIdx]++
|
|
140
|
+
deficit--
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return widths
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const barSegments = createMemo(() => {
|
|
148
|
+
const segs = segments()
|
|
149
|
+
const widths = segmentWidths()
|
|
150
|
+
return segs.map((seg, i) => ({
|
|
151
|
+
chars: "█".repeat(widths[i] || 0),
|
|
152
|
+
color: seg.color,
|
|
153
|
+
}))
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<box width="100%" flexDirection="column">
|
|
158
|
+
{/* Segmented bar */}
|
|
159
|
+
<box flexDirection="row">
|
|
160
|
+
{barSegments().map((seg, i) => (
|
|
161
|
+
<text key={i} fg={seg.color}>{seg.chars}</text>
|
|
162
|
+
))}
|
|
163
|
+
</box>
|
|
164
|
+
|
|
165
|
+
{/* Legend rows */}
|
|
166
|
+
<box flexDirection="column" marginTop={0}>
|
|
167
|
+
{segments().map((seg) => {
|
|
168
|
+
const pct = ((seg.tokens / totalTokens()) * 100).toFixed(0)
|
|
169
|
+
return (
|
|
170
|
+
<box
|
|
171
|
+
key={seg.key}
|
|
172
|
+
width="100%"
|
|
173
|
+
flexDirection="row"
|
|
174
|
+
justifyContent="space-between"
|
|
175
|
+
>
|
|
176
|
+
<text fg={seg.color}>{seg.label}</text>
|
|
177
|
+
<text fg={props.theme.textMuted}>
|
|
178
|
+
{compactTokens(seg.tokens)} ({pct}%)
|
|
179
|
+
</text>
|
|
180
|
+
</box>
|
|
181
|
+
)
|
|
182
|
+
})}
|
|
183
|
+
</box>
|
|
184
|
+
</box>
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const StatRow = (props: {
|
|
189
|
+
theme: TuiThemeCurrent
|
|
190
|
+
label: string
|
|
191
|
+
value: string
|
|
192
|
+
accent?: boolean
|
|
193
|
+
warning?: boolean
|
|
194
|
+
dim?: boolean
|
|
195
|
+
}) => {
|
|
196
|
+
const fg = createMemo(() => {
|
|
197
|
+
if (props.warning) return props.theme.warning
|
|
198
|
+
if (props.accent) return props.theme.accent
|
|
199
|
+
if (props.dim) return props.theme.textMuted
|
|
200
|
+
return props.theme.text
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<box width="100%" flexDirection="row" justifyContent="space-between">
|
|
205
|
+
<text fg={props.theme.textMuted}>{props.label}</text>
|
|
206
|
+
<text fg={fg()}>
|
|
207
|
+
<b>{props.value}</b>
|
|
208
|
+
</text>
|
|
209
|
+
</box>
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const SectionHeader = (props: { theme: TuiThemeCurrent; title: string }) => (
|
|
214
|
+
<box width="100%" marginTop={1}>
|
|
215
|
+
<text fg={props.theme.text}>
|
|
216
|
+
<b>{props.title}</b>
|
|
217
|
+
</text>
|
|
218
|
+
</box>
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
const SidebarContent = (props: {
|
|
222
|
+
api: TuiPluginApi
|
|
223
|
+
sessionID: () => string
|
|
224
|
+
theme: TuiThemeCurrent
|
|
225
|
+
}) => {
|
|
226
|
+
const [snapshot, setSnapshot] = createSignal<SidebarSnapshot | null>(null)
|
|
227
|
+
let refreshTimer: ReturnType<typeof setTimeout> | undefined
|
|
228
|
+
|
|
229
|
+
const refresh = () => {
|
|
230
|
+
const sid = props.sessionID()
|
|
231
|
+
if (!sid) return
|
|
232
|
+
const directory = props.api.state.path.directory ?? ""
|
|
233
|
+
const data = loadSidebarSnapshot(sid, directory)
|
|
234
|
+
setSnapshot(data)
|
|
235
|
+
try {
|
|
236
|
+
props.api.renderer.requestRender()
|
|
237
|
+
} catch {
|
|
238
|
+
// Ignore render errors
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const scheduleRefresh = () => {
|
|
243
|
+
if (refreshTimer) clearTimeout(refreshTimer)
|
|
244
|
+
refreshTimer = setTimeout(() => {
|
|
245
|
+
refreshTimer = undefined
|
|
246
|
+
refresh()
|
|
247
|
+
}, REFRESH_DEBOUNCE_MS)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
onCleanup(() => {
|
|
251
|
+
if (refreshTimer) clearTimeout(refreshTimer)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// Refresh on session change
|
|
255
|
+
createEffect(
|
|
256
|
+
on(props.sessionID, () => {
|
|
257
|
+
refresh()
|
|
258
|
+
}),
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
// Subscribe to events for live updates
|
|
262
|
+
createEffect(
|
|
263
|
+
on(
|
|
264
|
+
props.sessionID,
|
|
265
|
+
(sessionID) => {
|
|
266
|
+
const unsubs = [
|
|
267
|
+
props.api.event.on("message.updated", (event) => {
|
|
268
|
+
if (event.properties.info.sessionID !== sessionID) return
|
|
269
|
+
scheduleRefresh()
|
|
270
|
+
}),
|
|
271
|
+
props.api.event.on("session.updated", (event) => {
|
|
272
|
+
if (event.properties.info.id !== sessionID) return
|
|
273
|
+
scheduleRefresh()
|
|
274
|
+
}),
|
|
275
|
+
props.api.event.on("message.removed", (event) => {
|
|
276
|
+
if (event.properties.sessionID !== sessionID) return
|
|
277
|
+
scheduleRefresh()
|
|
278
|
+
}),
|
|
279
|
+
]
|
|
280
|
+
|
|
281
|
+
onCleanup(() => {
|
|
282
|
+
for (const unsub of unsubs) unsub()
|
|
283
|
+
})
|
|
284
|
+
},
|
|
285
|
+
{ defer: false },
|
|
286
|
+
),
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
const s = createMemo(() => snapshot())
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
<box
|
|
293
|
+
width="100%"
|
|
294
|
+
flexDirection="column"
|
|
295
|
+
border={SINGLE_BORDER}
|
|
296
|
+
borderColor={props.theme.borderActive}
|
|
297
|
+
paddingTop={1}
|
|
298
|
+
paddingBottom={1}
|
|
299
|
+
paddingLeft={1}
|
|
300
|
+
paddingRight={1}
|
|
301
|
+
>
|
|
302
|
+
{/* Header */}
|
|
303
|
+
<box flexDirection="row" justifyContent="space-between" alignItems="center">
|
|
304
|
+
<box paddingLeft={1} paddingRight={1} backgroundColor={props.theme.accent}>
|
|
305
|
+
<text fg={props.theme.background}>
|
|
306
|
+
<b>Magic CTX</b>
|
|
307
|
+
</text>
|
|
308
|
+
</box>
|
|
309
|
+
<text fg={props.theme.success}>active</text>
|
|
310
|
+
</box>
|
|
311
|
+
|
|
312
|
+
{/* Token breakdown bar */}
|
|
313
|
+
{s() && s()!.inputTokens > 0 && (
|
|
314
|
+
<box marginTop={1}>
|
|
315
|
+
<TokenBreakdown theme={props.theme} snapshot={s()!} />
|
|
316
|
+
</box>
|
|
317
|
+
)}
|
|
318
|
+
|
|
319
|
+
{/* Historian section */}
|
|
320
|
+
<box width="100%" marginTop={1} flexDirection="row" justifyContent="space-between">
|
|
321
|
+
<text fg={props.theme.text}>
|
|
322
|
+
<b>Historian</b>
|
|
323
|
+
</text>
|
|
324
|
+
{s()?.historianRunning ? (
|
|
325
|
+
<text fg={props.theme.warning}>compacting ⟳</text>
|
|
326
|
+
) : (
|
|
327
|
+
<text fg={props.theme.textMuted}>idle</text>
|
|
328
|
+
)}
|
|
329
|
+
</box>
|
|
330
|
+
<StatRow
|
|
331
|
+
theme={props.theme}
|
|
332
|
+
label="Compartments"
|
|
333
|
+
value={String(s()?.compartmentCount ?? 0)}
|
|
334
|
+
/>
|
|
335
|
+
<StatRow
|
|
336
|
+
theme={props.theme}
|
|
337
|
+
label="Facts"
|
|
338
|
+
value={String(s()?.factCount ?? 0)}
|
|
339
|
+
/>
|
|
340
|
+
|
|
341
|
+
{/* Memory section */}
|
|
342
|
+
<SectionHeader theme={props.theme} title="Memory" />
|
|
343
|
+
<StatRow
|
|
344
|
+
theme={props.theme}
|
|
345
|
+
label="Memories"
|
|
346
|
+
value={String(s()?.memoryCount ?? 0)}
|
|
347
|
+
accent
|
|
348
|
+
/>
|
|
349
|
+
{(s()?.memoryBlockCount ?? 0) > 0 && (
|
|
350
|
+
<StatRow
|
|
351
|
+
theme={props.theme}
|
|
352
|
+
label="Injected"
|
|
353
|
+
value={String(s()!.memoryBlockCount)}
|
|
354
|
+
dim
|
|
355
|
+
/>
|
|
356
|
+
)}
|
|
357
|
+
|
|
358
|
+
{/* Queue & Status */}
|
|
359
|
+
{((s()?.pendingOpsCount ?? 0) > 0 ||
|
|
360
|
+
(s()?.sessionNoteCount ?? 0) > 0 ||
|
|
361
|
+
(s()?.readySmartNoteCount ?? 0) > 0) && (
|
|
362
|
+
<>
|
|
363
|
+
<SectionHeader theme={props.theme} title="Status" />
|
|
364
|
+
{(s()?.pendingOpsCount ?? 0) > 0 && (
|
|
365
|
+
<StatRow
|
|
366
|
+
theme={props.theme}
|
|
367
|
+
label="Queue"
|
|
368
|
+
value={`${s()!.pendingOpsCount} pending`}
|
|
369
|
+
warning
|
|
370
|
+
/>
|
|
371
|
+
)}
|
|
372
|
+
{(s()?.sessionNoteCount ?? 0) > 0 && (
|
|
373
|
+
<StatRow
|
|
374
|
+
theme={props.theme}
|
|
375
|
+
label="Notes"
|
|
376
|
+
value={String(s()!.sessionNoteCount)}
|
|
377
|
+
/>
|
|
378
|
+
)}
|
|
379
|
+
{(s()?.readySmartNoteCount ?? 0) > 0 && (
|
|
380
|
+
<StatRow
|
|
381
|
+
theme={props.theme}
|
|
382
|
+
label="Smart Notes"
|
|
383
|
+
value={`${s()!.readySmartNoteCount} ready`}
|
|
384
|
+
accent
|
|
385
|
+
/>
|
|
386
|
+
)}
|
|
387
|
+
</>
|
|
388
|
+
)}
|
|
389
|
+
|
|
390
|
+
{/* Dreamer */}
|
|
391
|
+
{s()?.lastDreamerRunAt && (
|
|
392
|
+
<>
|
|
393
|
+
<SectionHeader theme={props.theme} title="Dreamer" />
|
|
394
|
+
<StatRow
|
|
395
|
+
theme={props.theme}
|
|
396
|
+
label="Last run"
|
|
397
|
+
value={relativeTime(s()!.lastDreamerRunAt!)}
|
|
398
|
+
dim
|
|
399
|
+
/>
|
|
400
|
+
</>
|
|
401
|
+
)}
|
|
402
|
+
</box>
|
|
403
|
+
)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export function createSidebarContentSlot(api: TuiPluginApi): TuiSlotPlugin {
|
|
407
|
+
return {
|
|
408
|
+
order: 150,
|
|
409
|
+
slots: {
|
|
410
|
+
sidebar_content: (ctx, value) => {
|
|
411
|
+
const theme = createMemo(() => ctx.theme.current)
|
|
412
|
+
return (
|
|
413
|
+
<SidebarContent
|
|
414
|
+
api={api}
|
|
415
|
+
sessionID={() => value.session_id}
|
|
416
|
+
theme={theme()}
|
|
417
|
+
/>
|
|
418
|
+
)
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
}
|
|
422
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// Type declarations for @opencode-ai/plugin/tui
|
|
2
|
+
// These types are not yet exported by the installed @opencode-ai/plugin package
|
|
3
|
+
|
|
4
|
+
declare module "@opencode-ai/plugin/tui" {
|
|
5
|
+
import type {
|
|
6
|
+
createOpencodeClient as createOpencodeClientV2,
|
|
7
|
+
Event as TuiEvent,
|
|
8
|
+
Message,
|
|
9
|
+
Part,
|
|
10
|
+
Provider,
|
|
11
|
+
Config as SdkConfig,
|
|
12
|
+
} from "@opencode-ai/sdk/v2";
|
|
13
|
+
|
|
14
|
+
import type { CliRenderer, RGBA } from "@opentui/core";
|
|
15
|
+
import type { JSX, SolidPlugin } from "@opentui/solid";
|
|
16
|
+
|
|
17
|
+
type PluginOptions = Record<string, unknown>;
|
|
18
|
+
|
|
19
|
+
export type { CliRenderer };
|
|
20
|
+
|
|
21
|
+
export type TuiThemeCurrent = {
|
|
22
|
+
readonly primary: RGBA;
|
|
23
|
+
readonly secondary: RGBA;
|
|
24
|
+
readonly accent: RGBA;
|
|
25
|
+
readonly error: RGBA;
|
|
26
|
+
readonly warning: RGBA;
|
|
27
|
+
readonly success: RGBA;
|
|
28
|
+
readonly info: RGBA;
|
|
29
|
+
readonly text: RGBA;
|
|
30
|
+
readonly textMuted: RGBA;
|
|
31
|
+
readonly background: RGBA;
|
|
32
|
+
readonly backgroundPanel: RGBA;
|
|
33
|
+
readonly backgroundElement: RGBA;
|
|
34
|
+
readonly backgroundMenu: RGBA;
|
|
35
|
+
readonly border: RGBA;
|
|
36
|
+
readonly borderActive: RGBA;
|
|
37
|
+
readonly borderSubtle: RGBA;
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type TuiTheme = {
|
|
42
|
+
readonly current: TuiThemeCurrent;
|
|
43
|
+
has: (name: string) => boolean;
|
|
44
|
+
set: (name: string) => boolean;
|
|
45
|
+
mode: () => "dark" | "light";
|
|
46
|
+
readonly ready: boolean;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type TuiSlotMap = {
|
|
50
|
+
app: Record<string, never>;
|
|
51
|
+
home_logo: Record<string, never>;
|
|
52
|
+
home_bottom: Record<string, never>;
|
|
53
|
+
sidebar_title: {
|
|
54
|
+
session_id: string;
|
|
55
|
+
title: string;
|
|
56
|
+
share_url?: string;
|
|
57
|
+
};
|
|
58
|
+
sidebar_content: {
|
|
59
|
+
session_id: string;
|
|
60
|
+
};
|
|
61
|
+
sidebar_footer: {
|
|
62
|
+
session_id: string;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type TuiSlotContext = {
|
|
67
|
+
theme: TuiTheme;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type TuiSlotPlugin = Omit<SolidPlugin<TuiSlotMap, TuiSlotContext>, "id"> & {
|
|
71
|
+
id?: never;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export type TuiToast = {
|
|
75
|
+
variant?: "info" | "success" | "warning" | "error";
|
|
76
|
+
title?: string;
|
|
77
|
+
message: string;
|
|
78
|
+
duration?: number;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type TuiDialogStack = {
|
|
82
|
+
replace: (render: () => JSX.Element, onClose?: () => void) => void;
|
|
83
|
+
clear: () => void;
|
|
84
|
+
setSize: (size: "medium" | "large" | "xlarge") => void;
|
|
85
|
+
readonly size: "medium" | "large" | "xlarge";
|
|
86
|
+
readonly depth: number;
|
|
87
|
+
readonly open: boolean;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export type TuiDialogAlertProps = {
|
|
91
|
+
title: string;
|
|
92
|
+
message: string;
|
|
93
|
+
onConfirm?: () => void;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export type TuiDialogConfirmProps = {
|
|
97
|
+
title: string;
|
|
98
|
+
message: string;
|
|
99
|
+
onConfirm?: () => void;
|
|
100
|
+
onCancel?: () => void;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export type TuiDialogPromptProps = {
|
|
104
|
+
title: string;
|
|
105
|
+
description?: () => JSX.Element;
|
|
106
|
+
placeholder?: string;
|
|
107
|
+
value?: string;
|
|
108
|
+
busy?: boolean;
|
|
109
|
+
busyText?: string;
|
|
110
|
+
onConfirm?: (value: string) => void;
|
|
111
|
+
onCancel?: () => void;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export type TuiDialogSelectOption<Value = unknown> = {
|
|
115
|
+
title: string;
|
|
116
|
+
value: Value;
|
|
117
|
+
description?: string;
|
|
118
|
+
footer?: JSX.Element | string;
|
|
119
|
+
category?: string;
|
|
120
|
+
disabled?: boolean;
|
|
121
|
+
onSelect?: () => void;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export type TuiDialogSelectProps<Value = unknown> = {
|
|
125
|
+
title: string;
|
|
126
|
+
placeholder?: string;
|
|
127
|
+
options: TuiDialogSelectOption<Value>[];
|
|
128
|
+
flat?: boolean;
|
|
129
|
+
onMove?: (option: TuiDialogSelectOption<Value>) => void;
|
|
130
|
+
onFilter?: (query: string) => void;
|
|
131
|
+
onSelect?: (option: TuiDialogSelectOption<Value>) => void;
|
|
132
|
+
skipFilter?: boolean;
|
|
133
|
+
current?: Value;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export type TuiState = {
|
|
137
|
+
readonly ready: boolean;
|
|
138
|
+
readonly config: SdkConfig;
|
|
139
|
+
readonly provider: ReadonlyArray<Provider>;
|
|
140
|
+
readonly path: {
|
|
141
|
+
state: string;
|
|
142
|
+
config: string;
|
|
143
|
+
worktree: string;
|
|
144
|
+
directory: string;
|
|
145
|
+
};
|
|
146
|
+
session: {
|
|
147
|
+
count: () => number;
|
|
148
|
+
messages: (sessionID: string) => ReadonlyArray<Message>;
|
|
149
|
+
};
|
|
150
|
+
part: (messageID: string) => ReadonlyArray<Part>;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export type TuiEventBus = {
|
|
154
|
+
on: <Type extends TuiEvent["type"]>(
|
|
155
|
+
type: Type,
|
|
156
|
+
handler: (event: Extract<TuiEvent, { type: Type }>) => void,
|
|
157
|
+
) => () => void;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export type TuiLifecycle = {
|
|
161
|
+
readonly signal: AbortSignal;
|
|
162
|
+
onDispose: (fn: () => void | Promise<void>) => () => void;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export type TuiPluginApi = {
|
|
166
|
+
app: { readonly version: string };
|
|
167
|
+
command: {
|
|
168
|
+
register: (
|
|
169
|
+
cb: () => Array<{
|
|
170
|
+
title: string;
|
|
171
|
+
value: string;
|
|
172
|
+
description?: string;
|
|
173
|
+
category?: string;
|
|
174
|
+
keybind?: string;
|
|
175
|
+
suggested?: boolean;
|
|
176
|
+
hidden?: boolean;
|
|
177
|
+
enabled?: boolean;
|
|
178
|
+
slash?: {
|
|
179
|
+
name: string;
|
|
180
|
+
aliases?: string[];
|
|
181
|
+
};
|
|
182
|
+
onSelect?: () => void;
|
|
183
|
+
}>,
|
|
184
|
+
) => () => void;
|
|
185
|
+
trigger: (value: string) => void;
|
|
186
|
+
};
|
|
187
|
+
route: {
|
|
188
|
+
register: (
|
|
189
|
+
routes: Array<{
|
|
190
|
+
name: string;
|
|
191
|
+
render: (input: { params?: Record<string, unknown> }) => JSX.Element;
|
|
192
|
+
}>,
|
|
193
|
+
) => () => void;
|
|
194
|
+
navigate: (name: string, params?: Record<string, unknown>) => void;
|
|
195
|
+
readonly current:
|
|
196
|
+
| { name: "home" }
|
|
197
|
+
| { name: "session"; params: { sessionID: string; initialPrompt?: unknown } }
|
|
198
|
+
| { name: string; params?: Record<string, unknown> };
|
|
199
|
+
};
|
|
200
|
+
ui: {
|
|
201
|
+
DialogAlert: (props: TuiDialogAlertProps) => JSX.Element;
|
|
202
|
+
DialogConfirm: (props: TuiDialogConfirmProps) => JSX.Element;
|
|
203
|
+
DialogPrompt: (props: TuiDialogPromptProps) => JSX.Element;
|
|
204
|
+
DialogSelect: <Value = unknown>(props: TuiDialogSelectProps<Value>) => JSX.Element;
|
|
205
|
+
toast: (input: TuiToast) => void;
|
|
206
|
+
dialog: TuiDialogStack;
|
|
207
|
+
};
|
|
208
|
+
state: TuiState;
|
|
209
|
+
theme: TuiTheme;
|
|
210
|
+
client: ReturnType<typeof createOpencodeClientV2>;
|
|
211
|
+
event: TuiEventBus;
|
|
212
|
+
renderer: CliRenderer;
|
|
213
|
+
slots: {
|
|
214
|
+
register: (plugin: TuiSlotPlugin) => string;
|
|
215
|
+
};
|
|
216
|
+
lifecycle: TuiLifecycle;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export type TuiPluginMeta = {
|
|
220
|
+
state: "first" | "updated" | "same";
|
|
221
|
+
id: string;
|
|
222
|
+
source: "file" | "npm" | "internal";
|
|
223
|
+
spec: string;
|
|
224
|
+
target: string;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export type TuiPlugin = (
|
|
228
|
+
api: TuiPluginApi,
|
|
229
|
+
options: PluginOptions | undefined,
|
|
230
|
+
meta: TuiPluginMeta,
|
|
231
|
+
) => Promise<void>;
|
|
232
|
+
}
|