@cmodjs/core 0.1.9 → 0.1.10
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/docs/index.md +1 -0
- package/docs/mod.md +18 -0
- package/docs/ui.md +107 -2
- package/mod.d.ts +2 -2
- package/mod.d.ts.map +1 -1
- package/package.json +1 -1
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +4 -18
- package/src/mod.ts +2 -1
- package/src/runtime/ui.ts +4 -18
- package/src/ui/components.ts +70 -0
- package/ui/components.d.ts +48 -0
- package/ui/components.d.ts.map +1 -0
- package/ui/components.js +56 -0
package/docs/index.md
CHANGED
|
@@ -80,6 +80,7 @@ A mod imports from the files below, and every other file in this package is inte
|
|
|
80
80
|
| `testing.js` | `testMod`, `TestOptions`, `TestInput`, `TestedMod`, `Fakes`, `Shown`, `TestCall` | [testing.md](testing.md) |
|
|
81
81
|
| `ui/define-pane.js` | `definePane`, `Pane` | [ui.md](ui.md) |
|
|
82
82
|
| `ui/elements.js` | `Box`, `Text`, `Button`, `Link`, `Code`, `Markdown`, `Input`, `Select`, `Image`, `drawWith` | [ui.md](ui.md) |
|
|
83
|
+
| `ui/components.js` | `Tabs`, `Tab`, `Split`, `Panel`, `Toggle`, `Tooltip`, `Help`, `Dialog`, `Pagination`, `ProgressBar` | [ui.md](ui.md) |
|
|
83
84
|
| `ui/slots.js` | `slots`, `Slot`, `SlotProps` | [ui.md](ui.md) |
|
|
84
85
|
| `ui/markdown.js` | `markdownSlots`, `markdownBlocks`, `MarkdownKind`, `MarkdownReader`, `MarkdownPiece` | [ui.md](ui.md) |
|
|
85
86
|
| `jobs/slash-command.js` | `slashCommand`, `Reply` | [jobs.md](jobs.md) |
|
package/docs/mod.md
CHANGED
|
@@ -240,6 +240,24 @@ mod.agent.spawn(args: { prompt: string; description?; subagentType?; model?; nam
|
|
|
240
240
|
|
|
241
241
|
`spawn` starts a subagent in the background, the way the Agent tool starts one, and resolves `{ agentId, model }` once it started, or `{ deny }` when a hook refused it. It does not wait for the subagent to finish. `subagentType` names an agent type, such as `explorer`. `mod.session.messages({ agentId })` reads the subagent's conversation so far.
|
|
242
242
|
|
|
243
|
+
The subagent's answer arrives on the mod's `SubagentStop` hook once it finishes: the input's `agent_id` is the `agentId` that `spawn` resolved, and its `last_assistant_message` is the subagent's final reply. A hook may wait for it, as this one does:
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
const answers = new Map<string, (answer: string) => void>()
|
|
247
|
+
|
|
248
|
+
mod.on('SubagentStop', (input) => {
|
|
249
|
+
answers.get(input.agent_id)?.(input.last_assistant_message ?? '')
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
mod.on('UserPromptSubmit', async (input) => {
|
|
253
|
+
if (!input.prompt.startsWith('/summarize')) return
|
|
254
|
+
const spawned = await mod.agent.spawn({ prompt: 'Summarize README.md in one line.', subagentType: 'Explore' })
|
|
255
|
+
if (spawned.agentId === undefined) return
|
|
256
|
+
const summary = await new Promise<string>((resolve) => answers.set(spawned.agentId as string, resolve))
|
|
257
|
+
return { hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: summary } }
|
|
258
|
+
})
|
|
259
|
+
```
|
|
260
|
+
|
|
243
261
|
## messageOf
|
|
244
262
|
|
|
245
263
|
```ts
|
package/docs/ui.md
CHANGED
|
@@ -5,7 +5,7 @@ A mod draws in four places: its own panes, the rows Claude Code draws (slots), t
|
|
|
5
5
|
```ts
|
|
6
6
|
mod.ui.pane(pane: Pane<State>): PaneHandle
|
|
7
7
|
mod.ui.render<S extends Slot>(slot: S, Component: (props: SlotProps<S>) => RenderElement): void
|
|
8
|
-
mod.ui.toast(text: string): void
|
|
8
|
+
mod.ui.toast(text: string, options?: { timeoutMs?: number }): void
|
|
9
9
|
mod.ui.progress<T>(title: string, task: (report: (step: ProgressStep) => void) => Promise<T>): Promise<T>
|
|
10
10
|
mod.ui.ask(question: string, options?: readonly string[] | AskOptions): Promise<string>
|
|
11
11
|
mod.ui.scroll(args: UiScrollArgs): Promise<UiScrollResult>
|
|
@@ -49,6 +49,111 @@ An element called outside a render throws `<Element> was called outside a render
|
|
|
49
49
|
|
|
50
50
|
`drawWith(table, draw, markdown?)` is the function cmod draws a render with. A mod never calls it.
|
|
51
51
|
|
|
52
|
+
## Components
|
|
53
|
+
|
|
54
|
+
`ui/components.js` exports controls built from the elements and Claude Code's theme colors, so they follow the person's theme and look the same in every mod. Each is a component like the ones a mod writes: a function of props, used in a pane's `render` or a slot's component.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
Tabs(props: { tabs: readonly { key: string; label: string }[]; selected: string; onSelect(key: string): void; children? })
|
|
58
|
+
Split(props: { children: readonly RenderElement[] })
|
|
59
|
+
Panel(props: { title: string; children? })
|
|
60
|
+
Toggle(props: { label: string; checked: boolean; onChange(checked: boolean): void })
|
|
61
|
+
Tooltip(props: { text: string; children? })
|
|
62
|
+
Help(props: { text: string; isOpen: boolean; onOpenChange(isOpen: boolean): void })
|
|
63
|
+
Dialog(props: { title: string; children?; actions })
|
|
64
|
+
Pagination(props: { page: number; pages: number; onPage(page: number): void })
|
|
65
|
+
ProgressBar(props: { done: number; total: number; width?: number })
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- `Tabs` draws a row of tabs over `children`. The selected tab's number is in the accent color and its label bold and underlined. Each other tab is a `Button` whose hotkey is its number, so a pane that holds the keyboard switches tabs by number, Tab, or a click. A hotkey is one digit, so only the first nine tabs have a number.
|
|
69
|
+
- `Split` lays its children side by side in equal widths. `Panel` frames one part of a pane in a rounded border with a bold title.
|
|
70
|
+
- `Toggle` draws `[x] label` or `[ ] label` as a `Button`. Enter or a click calls `onChange` with the other value.
|
|
71
|
+
- `Tooltip` shows `text` in an inverted card above its children while the pointer rests on them. The card opens upward, because Claude Code paints a card over the rows drawn before it and under the rows drawn after it. It has no border, so a one-line card needs one free row above its children. The pointer alone opens it, so put nothing only a tooltip says.
|
|
72
|
+
- `Help` draws a `?` that shows `text` the same way. A press of `?` keeps the card open until the next press, so the keyboard reaches it too. Keep `isOpen` in `mod.state`.
|
|
73
|
+
- `Dialog` lays out a dialog's title, body, and `actions`, the buttons in a row. Put the safe action first, as the `primary` Button with `autoFocus`.
|
|
74
|
+
- `Pagination` draws `p: Previous 2 of 5 n: Next`. `p` and `n` are its hotkeys, and an end it cannot pass is dim text.
|
|
75
|
+
- `ProgressBar` fills `width` columns, 24 unless given, in proportion to `done` of `total`, and shows the count. The progress lines `mod.ui.progress` draws use it too.
|
|
76
|
+
- `Tabs`, `Toggle`, `Help`, and `Pagination` hold no state of their own. Keep their value in `mod.state` and set it in the callback, as in React's controlled components.
|
|
77
|
+
|
|
78
|
+
A pane opened with `focus`, `closeOnEscape`, and `holdToasts` is a dialog. Claude Code shows it as a tab of the dock, beside the mod's other panes, and Escape closes it:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { definePane } from '../../node_modules/@cmodjs/core/ui/define-pane.js'
|
|
82
|
+
import { Dialog } from '../../node_modules/@cmodjs/core/ui/components.js'
|
|
83
|
+
import { Button, Text } from '../../node_modules/@cmodjs/core/ui/elements.js'
|
|
84
|
+
|
|
85
|
+
export const confirmPane = definePane<{ session: { deleting: boolean } }>({
|
|
86
|
+
id: 'delete-branch',
|
|
87
|
+
title: 'Delete branch',
|
|
88
|
+
rows: 9,
|
|
89
|
+
closeOnEscape: true,
|
|
90
|
+
holdToasts: true,
|
|
91
|
+
render: (mod) => (
|
|
92
|
+
<Dialog
|
|
93
|
+
title="Delete the branch feature/kit?"
|
|
94
|
+
actions={[
|
|
95
|
+
<Button variant="primary" autoFocus label="Keep it" onPress={() => void mod.ui.toast('Kept feature/kit')} />,
|
|
96
|
+
<Button label="Delete" onPress={() => void (mod.state.session.deleting = true)} />,
|
|
97
|
+
]}
|
|
98
|
+
>
|
|
99
|
+
<Text dimColor wrap="wrap">Its 3 commits are not on main. Deleting it removes them from this machine.</Text>
|
|
100
|
+
</Dialog>
|
|
101
|
+
),
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Open it with `mod.ui.pane(confirmPane).open({ focus: true })`. It draws as:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
Widgets Delete branch ✕
|
|
109
|
+
Delete the branch feature/kit?
|
|
110
|
+
|
|
111
|
+
Its 3 commits are not on main. Deleting it removes them from
|
|
112
|
+
this machine.
|
|
113
|
+
|
|
114
|
+
[ Keep it ] [ Delete ]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
A pane with `Tabs`, a `Tooltip`, a `ProgressBar`, `Pagination`, and a `Toggle` with `Help` draws as:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
1: Controls 2: Split 3: Files
|
|
121
|
+
|
|
122
|
+
3 files read
|
|
123
|
+
|
|
124
|
+
██████████████░░░░░░░░░░ 7/12
|
|
125
|
+
|
|
126
|
+
p: Previous 2 of 5 n: Next
|
|
127
|
+
|
|
128
|
+
[x] Show hidden files ?
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
With the pointer on `3 files read` and `Help` open, the two cards cover the rows above them:
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
1: Controls 2: Split 3: Files
|
|
135
|
+
Files Claude read this session
|
|
136
|
+
3 files read
|
|
137
|
+
|
|
138
|
+
██████████████░░░░░░░░░░ 7/12
|
|
139
|
+
|
|
140
|
+
p: Previous 2 of 5 n Hidden files start with a dot,
|
|
141
|
+
such as .env.
|
|
142
|
+
[x] Show hidden files ?
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
and its Split tab, two `Panel`s in a `Split`, as:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
1: Controls 2: Split 3: Files
|
|
149
|
+
|
|
150
|
+
╭─────────────────────────────────╮ ╭────────────────────────────────╮
|
|
151
|
+
│ Changed files │ │ src/mod.tsx │
|
|
152
|
+
│ src/mod.tsx │ │ + mod.ui.pane(widgetsPane) │
|
|
153
|
+
│ src/state.ts │ │ - mod.ui.pane(oldPane) │
|
|
154
|
+
╰─────────────────────────────────╯ ╰────────────────────────────────╯
|
|
155
|
+
```
|
|
156
|
+
|
|
52
157
|
## Panes
|
|
53
158
|
|
|
54
159
|
A pane is a framed region the mod opens. Claude Code docks it beside the transcript in fullscreen, or shows it above the prompt.
|
|
@@ -250,7 +355,7 @@ markdownBlocks<S>(text: string, slot: S): Omit<SlotProps<S>, 'Default'>[]
|
|
|
250
355
|
|
|
251
356
|
## Toasts
|
|
252
357
|
|
|
253
|
-
`mod.ui.toast(text)` shows the person a short message.
|
|
358
|
+
`mod.ui.toast(text, options?)` shows the person a short message on Claude Code's stack of plugin toasts, over the transcript's top right corner and under the mod's name. It stays 4 seconds, or `timeoutMs`. A click takes it off, and the pointer resting on it holds it. While a pane opened with `holdToasts` is shown, it waits.
|
|
254
359
|
|
|
255
360
|
## Progress lines
|
|
256
361
|
|
package/mod.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentSpawnArgs, AgentSpawnResult, AskOptions, ClassicHookInputs, CmodDependencies, FsEntry, FsStat, FsStatOptions, HookStream, HttpInit, HttpResponse, PaneOpenArgs, PermissionRequestDecision, ProcessRunInit, ProcessRunResult, ProcessSpawnChunk, ProcessSpawnRequest, ProcessSpawnResult, RenderElement, Settings, SettingsReadArgs, UiScrollArgs, UiScrollResult } from 'claude-code';
|
|
1
|
+
import type { AgentSpawnArgs, AgentSpawnResult, AskOptions, ClassicHookInputs, CmodDependencies, FsEntry, FsStat, FsStatOptions, HookStream, HttpInit, HttpResponse, PaneOpenArgs, PermissionRequestDecision, ProcessRunInit, ProcessRunResult, ProcessSpawnChunk, ProcessSpawnRequest, ProcessSpawnResult, RenderElement, Settings, SettingsReadArgs, ToastOptions, UiScrollArgs, UiScrollResult } from 'claude-code';
|
|
2
2
|
import type { Claude } from './runtime/claude.js';
|
|
3
3
|
import type { RoutedEvent } from './runtime/hooks.js';
|
|
4
4
|
import type { RoutedHook } from './runtime/router.js';
|
|
@@ -80,7 +80,7 @@ export type Mod<State extends object = Record<never, never>> = {
|
|
|
80
80
|
readonly ui: {
|
|
81
81
|
pane(pane: Pane<State>): PaneHandle;
|
|
82
82
|
render<S extends Slot>(slot: S, Component: (props: SlotProps<S>) => RenderElement): void;
|
|
83
|
-
toast(text: string): void;
|
|
83
|
+
toast(text: string, options?: ToastOptions): void;
|
|
84
84
|
progress<T>(title: string, task: (report: (step: ProgressStep) => void) => Promise<T>): Promise<T>;
|
|
85
85
|
ask(question: string, options?: readonly string[] | AskOptions): Promise<string>;
|
|
86
86
|
scroll(args: UiScrollArgs): Promise<UiScrollResult>;
|
package/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["src/mod.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["src/mod.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/runtime/ui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/runtime/ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA+B,aAAa,EAAS,MAAM,aAAa,CAAA;AAClG,OAAO,KAAK,EAAE,GAAG,EAAc,YAAY,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/runtime/ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA+B,aAAa,EAAS,MAAM,aAAa,CAAA;AAClG,OAAO,KAAK,EAAE,GAAG,EAAc,YAAY,EAAE,MAAM,WAAW,CAAA;AAO9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAA;IAChC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,GAAG,IAAI,IAAI,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAAA;IAClC,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,CAAA;CAC5F,CAAA;AAkBD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAqDvD;AAsGD,MAAM,MAAM,SAAS,CAAC,KAAK,SAAS,MAAM,IAAI;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,KAAK,CAAC,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,MAAM,CAAC,KAAK,SAAS,MAAM,IAAI;IACzC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAA;IAC7B,OAAO,IAAI,IAAI,CAAA;IACf,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9B,CAAA;AAED,wBAAgB,QAAQ,CAAC,KAAK,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,SAAS,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAmNjI"}
|
package/runtime/ui.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ProgressBar } from '../ui/components.js';
|
|
1
2
|
import { Box, drawWith, Text } from '../ui/elements.js';
|
|
2
3
|
import { messageOf } from '../utils/text.js';
|
|
3
4
|
const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
@@ -77,23 +78,8 @@ function drawLine(line, frame, columns) {
|
|
|
77
78
|
if (line.step === undefined)
|
|
78
79
|
return Text({ wrap: 'truncate-end', children: [glyph, ' ', title, Text({ dimColor: true, children: '…' })] });
|
|
79
80
|
const { done, total, label = '' } = line.step;
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
const filled = total > 0 ? Math.round(Math.min(done / total, 1) * width) : 0;
|
|
83
|
-
return Text({
|
|
84
|
-
wrap: 'truncate-end',
|
|
85
|
-
children: [
|
|
86
|
-
glyph,
|
|
87
|
-
' ',
|
|
88
|
-
title,
|
|
89
|
-
' ',
|
|
90
|
-
Text({ color: 'claude', children: '█'.repeat(filled) }),
|
|
91
|
-
Text({ color: 'subtle', children: '░'.repeat(width - filled) }),
|
|
92
|
-
' ',
|
|
93
|
-
Text({ dimColor: true, children: count }),
|
|
94
|
-
...(label === '' ? [] : [' ', label]),
|
|
95
|
-
],
|
|
96
|
-
});
|
|
81
|
+
const width = barWidth(columns, `${line.title}${done}/${total}${label}`.length);
|
|
82
|
+
return Text({ wrap: 'truncate-end', children: [glyph, ' ', title, ' ', ProgressBar({ done, total, width }), ...(label === '' ? [] : [' ', label])] });
|
|
97
83
|
}
|
|
98
84
|
function barWidth(columns, textLength) {
|
|
99
85
|
if (columns === undefined)
|
|
@@ -347,7 +333,7 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
347
333
|
return drawWith(table, () => drawPieces([drawn], e.props.isFirstOfReply ? 'opensReply' : 'inReply'));
|
|
348
334
|
});
|
|
349
335
|
},
|
|
350
|
-
toast: (text) => claude.ui.toast(text),
|
|
336
|
+
toast: (text, options) => claude.ui.toast(text, options),
|
|
351
337
|
async progress(title, task) {
|
|
352
338
|
const line = progress.start(title);
|
|
353
339
|
try {
|
package/src/mod.ts
CHANGED
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
RenderElement,
|
|
21
21
|
Settings,
|
|
22
22
|
SettingsReadArgs,
|
|
23
|
+
ToastOptions,
|
|
23
24
|
UiScrollArgs,
|
|
24
25
|
UiScrollResult,
|
|
25
26
|
} from 'claude-code'
|
|
@@ -125,7 +126,7 @@ export type Mod<State extends object = Record<never, never>> = {
|
|
|
125
126
|
readonly ui: {
|
|
126
127
|
pane(pane: Pane<State>): PaneHandle
|
|
127
128
|
render<S extends Slot>(slot: S, Component: (props: SlotProps<S>) => RenderElement): void
|
|
128
|
-
toast(text: string): void
|
|
129
|
+
toast(text: string, options?: ToastOptions): void
|
|
129
130
|
progress<T>(title: string, task: (report: (step: ProgressStep) => void) => Promise<T>): Promise<T>
|
|
130
131
|
ask(question: string, options?: readonly string[] | AskOptions): Promise<string>
|
|
131
132
|
scroll(args: UiScrollArgs): Promise<UiScrollResult>
|
package/src/runtime/ui.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ElementTable, MarkdownProps, PaneOpenArgs, RenderElement, Timer } from 'claude-code'
|
|
2
2
|
import type { Mod, PaneHandle, ProgressStep } from '../mod.js'
|
|
3
|
+
import { ProgressBar } from '../ui/components.js'
|
|
3
4
|
import type { Pane } from '../ui/define-pane.js'
|
|
4
5
|
import { Box, drawWith, Text } from '../ui/elements.js'
|
|
5
6
|
import type { MarkdownKind, MarkdownReader } from '../ui/markdown.js'
|
|
@@ -109,23 +110,8 @@ function drawLine(line: Line, frame: number, columns: number | undefined): Rende
|
|
|
109
110
|
const glyph = Text({ color: 'claude', children: spinner[frame % spinner.length] })
|
|
110
111
|
if (line.step === undefined) return Text({ wrap: 'truncate-end', children: [glyph, ' ', title, Text({ dimColor: true, children: '…' })] })
|
|
111
112
|
const { done, total, label = '' } = line.step
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
const filled = total > 0 ? Math.round(Math.min(done / total, 1) * width) : 0
|
|
115
|
-
return Text({
|
|
116
|
-
wrap: 'truncate-end',
|
|
117
|
-
children: [
|
|
118
|
-
glyph,
|
|
119
|
-
' ',
|
|
120
|
-
title,
|
|
121
|
-
' ',
|
|
122
|
-
Text({ color: 'claude', children: '█'.repeat(filled) }),
|
|
123
|
-
Text({ color: 'subtle', children: '░'.repeat(width - filled) }),
|
|
124
|
-
' ',
|
|
125
|
-
Text({ dimColor: true, children: count }),
|
|
126
|
-
...(label === '' ? [] : [' ', label]),
|
|
127
|
-
],
|
|
128
|
-
})
|
|
113
|
+
const width = barWidth(columns, `${line.title}${done}/${total}${label}`.length)
|
|
114
|
+
return Text({ wrap: 'truncate-end', children: [glyph, ' ', title, ' ', ProgressBar({ done, total, width }), ...(label === '' ? [] : [' ', label])] })
|
|
129
115
|
}
|
|
130
116
|
|
|
131
117
|
function barWidth(columns: number | undefined, textLength: number): number {
|
|
@@ -402,7 +388,7 @@ export function createUi<State extends object>({ name, claude, router, progress,
|
|
|
402
388
|
return drawWith(table, () => drawPieces([drawn], e.props.isFirstOfReply ? 'opensReply' : 'inReply'))
|
|
403
389
|
})
|
|
404
390
|
},
|
|
405
|
-
toast: (text) => claude.ui.toast(text),
|
|
391
|
+
toast: (text, options) => claude.ui.toast(text, options),
|
|
406
392
|
async progress(title, task) {
|
|
407
393
|
const line = progress.start(title)
|
|
408
394
|
try {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { RenderChildren, RenderElement } from 'claude-code'
|
|
2
|
+
import { Box, Button, Text } from './elements.js'
|
|
3
|
+
|
|
4
|
+
export type Tab = { readonly key: string; readonly label: string }
|
|
5
|
+
|
|
6
|
+
export function Tabs({ tabs, selected, onSelect, children }: { readonly tabs: readonly Tab[]; readonly selected: string; readonly onSelect: (key: string) => void; readonly children?: RenderChildren }): RenderElement {
|
|
7
|
+
const row = tabs.map((tab, index) => {
|
|
8
|
+
const hotkey = index < 9 ? `${index + 1}` : undefined
|
|
9
|
+
return tab.key === selected
|
|
10
|
+
? Text({ children: [...(hotkey === undefined ? [] : [Text({ color: 'claude', children: hotkey }), ': ']), Text({ bold: true, underline: true, children: tab.label })] })
|
|
11
|
+
: Button({ plain: true, dimColor: true, key: `tab:${tab.key}`, ...(hotkey === undefined ? {} : { hotkey }), label: tab.label, onPress: () => onSelect(tab.key) })
|
|
12
|
+
})
|
|
13
|
+
return Box({ flexDirection: 'column', gap: 1, children: [Box({ gap: 3, children: row }), children] })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function Split({ children }: { readonly children: readonly RenderElement[] }): RenderElement {
|
|
17
|
+
const width = `${Math.floor(100 / Math.max(children.length, 1))}%`
|
|
18
|
+
return Box({ gap: 1, children: children.map((child) => Box({ width, children: child })) })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function Panel({ title, children }: { readonly title: string; readonly children?: RenderChildren }): RenderElement {
|
|
22
|
+
return Box({ flexDirection: 'column', flexGrow: 1, borderStyle: 'round', borderColor: 'subtle', paddingX: 1, children: [Text({ bold: true, children: title }), children] })
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function Toggle({ label, checked, onChange }: { readonly label: string; readonly checked: boolean; readonly onChange: (checked: boolean) => void }): RenderElement {
|
|
26
|
+
return Button({ plain: true, key: `toggle:${label}`, label: `${checked ? '[x]' : '[ ]'} ${label}`, onPress: () => onChange(!checked) })
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function Tooltip({ text, children }: { readonly text: string; readonly children?: RenderChildren }): RenderElement {
|
|
30
|
+
return Box({ key: `tooltip:${text}`, children: [children, card(text, false)] })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function Help({ text, isOpen, onOpenChange }: { readonly text: string; readonly isOpen: boolean; readonly onOpenChange: (isOpen: boolean) => void }): RenderElement {
|
|
34
|
+
return Box({ key: `help:${text}`, children: [Button({ plain: true, key: `help-button:${text}`, label: '?', onPress: () => onOpenChange(!isOpen) }), card(text, isOpen)] })
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function Dialog({ title, children, actions }: { readonly title: string; readonly children?: RenderChildren; readonly actions: RenderChildren }): RenderElement {
|
|
38
|
+
return Box({ flexDirection: 'column', gap: 1, paddingX: 1, children: [Text({ bold: true, children: title }), children, Box({ gap: 2, children: actions })] })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function Pagination({ page, pages, onPage }: { readonly page: number; readonly pages: number; readonly onPage: (page: number) => void }): RenderElement {
|
|
42
|
+
return Box({
|
|
43
|
+
gap: 2,
|
|
44
|
+
children: [
|
|
45
|
+
page > 1 ? Button({ plain: true, hotkey: 'p', label: 'Previous', onPress: () => onPage(page - 1) }) : Text({ dimColor: true, children: 'p: Previous' }),
|
|
46
|
+
Text({ dimColor: true, children: `${page} of ${pages}` }),
|
|
47
|
+
page < pages ? Button({ plain: true, hotkey: 'n', label: 'Next', onPress: () => onPage(page + 1) }) : Text({ dimColor: true, children: 'n: Next' }),
|
|
48
|
+
],
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function ProgressBar({ done, total, width = 24 }: { readonly done: number; readonly total: number; readonly width?: number }): RenderElement {
|
|
53
|
+
const filled = total > 0 ? Math.round(Math.min(done / total, 1) * width) : 0
|
|
54
|
+
return Text({ children: [Text({ color: 'claude', children: '█'.repeat(filled) }), Text({ color: 'subtle', children: '░'.repeat(width - filled) }), ' ', Text({ dimColor: true, children: `${done}/${total}` })] })
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const cardColumns = 36
|
|
58
|
+
|
|
59
|
+
function card(text: string, isShown: boolean): RenderElement {
|
|
60
|
+
return Box({
|
|
61
|
+
position: 'absolute',
|
|
62
|
+
bottom: 1,
|
|
63
|
+
left: 0,
|
|
64
|
+
width: Math.min(text.length + 2, cardColumns),
|
|
65
|
+
...(isShown ? {} : { display: 'none' as const, hover: { display: 'flex' as const } }),
|
|
66
|
+
backgroundColor: 'text',
|
|
67
|
+
paddingX: 1,
|
|
68
|
+
children: Text({ color: 'inverseText', wrap: 'wrap', children: text }),
|
|
69
|
+
})
|
|
70
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { RenderChildren, RenderElement } from 'claude-code';
|
|
2
|
+
export type Tab = {
|
|
3
|
+
readonly key: string;
|
|
4
|
+
readonly label: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function Tabs({ tabs, selected, onSelect, children }: {
|
|
7
|
+
readonly tabs: readonly Tab[];
|
|
8
|
+
readonly selected: string;
|
|
9
|
+
readonly onSelect: (key: string) => void;
|
|
10
|
+
readonly children?: RenderChildren;
|
|
11
|
+
}): RenderElement;
|
|
12
|
+
export declare function Split({ children }: {
|
|
13
|
+
readonly children: readonly RenderElement[];
|
|
14
|
+
}): RenderElement;
|
|
15
|
+
export declare function Panel({ title, children }: {
|
|
16
|
+
readonly title: string;
|
|
17
|
+
readonly children?: RenderChildren;
|
|
18
|
+
}): RenderElement;
|
|
19
|
+
export declare function Toggle({ label, checked, onChange }: {
|
|
20
|
+
readonly label: string;
|
|
21
|
+
readonly checked: boolean;
|
|
22
|
+
readonly onChange: (checked: boolean) => void;
|
|
23
|
+
}): RenderElement;
|
|
24
|
+
export declare function Tooltip({ text, children }: {
|
|
25
|
+
readonly text: string;
|
|
26
|
+
readonly children?: RenderChildren;
|
|
27
|
+
}): RenderElement;
|
|
28
|
+
export declare function Help({ text, isOpen, onOpenChange }: {
|
|
29
|
+
readonly text: string;
|
|
30
|
+
readonly isOpen: boolean;
|
|
31
|
+
readonly onOpenChange: (isOpen: boolean) => void;
|
|
32
|
+
}): RenderElement;
|
|
33
|
+
export declare function Dialog({ title, children, actions }: {
|
|
34
|
+
readonly title: string;
|
|
35
|
+
readonly children?: RenderChildren;
|
|
36
|
+
readonly actions: RenderChildren;
|
|
37
|
+
}): RenderElement;
|
|
38
|
+
export declare function Pagination({ page, pages, onPage }: {
|
|
39
|
+
readonly page: number;
|
|
40
|
+
readonly pages: number;
|
|
41
|
+
readonly onPage: (page: number) => void;
|
|
42
|
+
}): RenderElement;
|
|
43
|
+
export declare function ProgressBar({ done, total, width }: {
|
|
44
|
+
readonly done: number;
|
|
45
|
+
readonly total: number;
|
|
46
|
+
readonly width?: number;
|
|
47
|
+
}): RenderElement;
|
|
48
|
+
//# sourceMappingURL=components.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../src/ui/components.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhE,MAAM,MAAM,GAAG,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAElE,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,EAAE,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAA;CAAE,GAAG,aAAa,CAQtN;AAED,wBAAgB,KAAK,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAA;CAAE,GAAG,aAAa,CAGlG;AAED,wBAAgB,KAAK,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAA;CAAE,GAAG,aAAa,CAExH;AAED,wBAAgB,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG,aAAa,CAExK;AAED,wBAAgB,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAA;CAAE,GAAG,aAAa,CAExH;AAED,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG,aAAa,CAEzK;AAED,wBAAgB,MAAM,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAAG,aAAa,CAEpK;AAED,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GAAG,aAAa,CAS7J;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAU,EAAE,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CAGlJ"}
|
package/ui/components.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Box, Button, Text } from './elements.js';
|
|
2
|
+
export function Tabs({ tabs, selected, onSelect, children }) {
|
|
3
|
+
const row = tabs.map((tab, index) => {
|
|
4
|
+
const hotkey = index < 9 ? `${index + 1}` : undefined;
|
|
5
|
+
return tab.key === selected
|
|
6
|
+
? Text({ children: [...(hotkey === undefined ? [] : [Text({ color: 'claude', children: hotkey }), ': ']), Text({ bold: true, underline: true, children: tab.label })] })
|
|
7
|
+
: Button({ plain: true, dimColor: true, key: `tab:${tab.key}`, ...(hotkey === undefined ? {} : { hotkey }), label: tab.label, onPress: () => onSelect(tab.key) });
|
|
8
|
+
});
|
|
9
|
+
return Box({ flexDirection: 'column', gap: 1, children: [Box({ gap: 3, children: row }), children] });
|
|
10
|
+
}
|
|
11
|
+
export function Split({ children }) {
|
|
12
|
+
const width = `${Math.floor(100 / Math.max(children.length, 1))}%`;
|
|
13
|
+
return Box({ gap: 1, children: children.map((child) => Box({ width, children: child })) });
|
|
14
|
+
}
|
|
15
|
+
export function Panel({ title, children }) {
|
|
16
|
+
return Box({ flexDirection: 'column', flexGrow: 1, borderStyle: 'round', borderColor: 'subtle', paddingX: 1, children: [Text({ bold: true, children: title }), children] });
|
|
17
|
+
}
|
|
18
|
+
export function Toggle({ label, checked, onChange }) {
|
|
19
|
+
return Button({ plain: true, key: `toggle:${label}`, label: `${checked ? '[x]' : '[ ]'} ${label}`, onPress: () => onChange(!checked) });
|
|
20
|
+
}
|
|
21
|
+
export function Tooltip({ text, children }) {
|
|
22
|
+
return Box({ key: `tooltip:${text}`, children: [children, card(text, false)] });
|
|
23
|
+
}
|
|
24
|
+
export function Help({ text, isOpen, onOpenChange }) {
|
|
25
|
+
return Box({ key: `help:${text}`, children: [Button({ plain: true, key: `help-button:${text}`, label: '?', onPress: () => onOpenChange(!isOpen) }), card(text, isOpen)] });
|
|
26
|
+
}
|
|
27
|
+
export function Dialog({ title, children, actions }) {
|
|
28
|
+
return Box({ flexDirection: 'column', gap: 1, paddingX: 1, children: [Text({ bold: true, children: title }), children, Box({ gap: 2, children: actions })] });
|
|
29
|
+
}
|
|
30
|
+
export function Pagination({ page, pages, onPage }) {
|
|
31
|
+
return Box({
|
|
32
|
+
gap: 2,
|
|
33
|
+
children: [
|
|
34
|
+
page > 1 ? Button({ plain: true, hotkey: 'p', label: 'Previous', onPress: () => onPage(page - 1) }) : Text({ dimColor: true, children: 'p: Previous' }),
|
|
35
|
+
Text({ dimColor: true, children: `${page} of ${pages}` }),
|
|
36
|
+
page < pages ? Button({ plain: true, hotkey: 'n', label: 'Next', onPress: () => onPage(page + 1) }) : Text({ dimColor: true, children: 'n: Next' }),
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
export function ProgressBar({ done, total, width = 24 }) {
|
|
41
|
+
const filled = total > 0 ? Math.round(Math.min(done / total, 1) * width) : 0;
|
|
42
|
+
return Text({ children: [Text({ color: 'claude', children: '█'.repeat(filled) }), Text({ color: 'subtle', children: '░'.repeat(width - filled) }), ' ', Text({ dimColor: true, children: `${done}/${total}` })] });
|
|
43
|
+
}
|
|
44
|
+
const cardColumns = 36;
|
|
45
|
+
function card(text, isShown) {
|
|
46
|
+
return Box({
|
|
47
|
+
position: 'absolute',
|
|
48
|
+
bottom: 1,
|
|
49
|
+
left: 0,
|
|
50
|
+
width: Math.min(text.length + 2, cardColumns),
|
|
51
|
+
...(isShown ? {} : { display: 'none', hover: { display: 'flex' } }),
|
|
52
|
+
backgroundColor: 'text',
|
|
53
|
+
paddingX: 1,
|
|
54
|
+
children: Text({ color: 'inverseText', wrap: 'wrap', children: text }),
|
|
55
|
+
});
|
|
56
|
+
}
|