@voltro/ui 0.1.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.
@@ -0,0 +1,407 @@
1
+ import { AgentChatMessage } from '@voltro/client';
2
+ import { AgentChatPart } from '@voltro/client';
3
+ import { AgentChatState } from '@voltro/client';
4
+ import { CopilotAnswer } from '@voltro/client';
5
+ import { DataCopilotState } from '@voltro/client';
6
+ import { FieldDescriptor } from '@voltro/client';
7
+ import { FieldOption } from '@voltro/client';
8
+ import { FormBinding } from '@voltro/client';
9
+ import { QueryFiltersState } from '@voltro/client';
10
+ import { ReactNode } from 'react';
11
+ import { Schema } from 'effect';
12
+ import { UseDataTableOptions } from '@voltro/client';
13
+ import { WidgetKind } from '@voltro/client';
14
+ import { WorkflowRunStatus } from '@voltro/client';
15
+
16
+ /** Kit-styled default chat surface. Streaming bubbles, the live typewriter
17
+ * row, an input box wired to `<agent>.send`. */
18
+ export declare function AgentChat(props: AgentChatProps): ReactNode;
19
+
20
+ export declare interface AgentChatProps {
21
+ readonly api: string;
22
+ readonly agent: string;
23
+ readonly threadId: string;
24
+ readonly placeholder?: string;
25
+ readonly sendLabel?: string;
26
+ readonly emptyText?: string;
27
+ }
28
+
29
+ /** Render a message's content from its persisted parts, falling back to the
30
+ * flat `content` string when no parts are present (e.g. a user turn). */
31
+ export declare function AgentMessageView({ message }: {
32
+ readonly message: AgentChatMessage;
33
+ }): ReactNode;
34
+
35
+ /** Render one persisted message part by its `type`. Unknown types fall back to
36
+ * a labelled JSON dump so nothing is silently dropped. */
37
+ export declare function AgentPartView({ part }: {
38
+ readonly part: AgentChatPart;
39
+ }): ReactNode;
40
+
41
+ /** Headless render-prop over useAgentChat for fully custom layouts. */
42
+ export declare function AgentStream(props: AgentStreamProps): ReactNode;
43
+
44
+ export declare interface AgentStreamProps {
45
+ readonly api: string;
46
+ readonly agent: string;
47
+ readonly threadId: string;
48
+ readonly children: (chat: AgentChatState) => ReactNode;
49
+ }
50
+
51
+ export declare function AppAgent(props: AppAgentProps): ReactNode;
52
+
53
+ export declare interface AppAgentProps extends AgentChatProps {
54
+ /** The tools this agent can use — rendered as a capability disclosure. */
55
+ readonly tools?: ReadonlyArray<AppAgentTool>;
56
+ /** Show the tools disclosure (default true when `tools` is non-empty). */
57
+ readonly showTools?: boolean;
58
+ }
59
+
60
+ /** A tool the agent can call (the manifest's `exposeAsTool` view /
61
+ * synthesizeToolSpecs output). Structural — no @voltro/ai dep in the kit. */
62
+ export declare interface AppAgentTool {
63
+ readonly name: string;
64
+ readonly description?: string;
65
+ /** mutation/action → true. */
66
+ readonly write?: boolean;
67
+ /** Requires human confirmation before it runs. */
68
+ readonly confirm?: boolean;
69
+ }
70
+
71
+ /** Approve/Reject buttons bound to a parked `awaitSignal` / `awaitUpdate`.
72
+ * Controlled + gated: renders nothing unless the run is actually waiting. */
73
+ export declare function ApprovalControls(props: ApprovalControlsProps): ReactNode;
74
+
75
+ export declare interface ApprovalControlsProps {
76
+ /** Render ONLY when true — gate on `run.waitingFor?.name === 'approval'`. */
77
+ readonly when: boolean;
78
+ readonly onApprove: () => void;
79
+ readonly onReject: () => void;
80
+ readonly approveLabel?: string;
81
+ readonly rejectLabel?: string;
82
+ readonly pending?: boolean;
83
+ }
84
+
85
+ export declare const AsyncSelect: (props: AsyncSelectProps) => ReactNode;
86
+
87
+ export declare interface AsyncSelectProps extends WidgetProps {
88
+ /** The api the source query lives on. */
89
+ readonly api: string;
90
+ /** The source query tag that supplies the options (`'users.search'`). */
91
+ readonly source: string;
92
+ readonly labelField?: string;
93
+ readonly valueField?: string;
94
+ /** Map the typed term → the source query input. Default `{ q: term }`. */
95
+ readonly input?: (term: string) => Record<string, unknown>;
96
+ readonly debounceMs?: number;
97
+ }
98
+
99
+ export declare function AutoForm<Input extends Record<string, unknown> = Record<string, unknown>, Output = unknown>(props: AutoFormProps<Input, Output>): ReactNode;
100
+
101
+ export declare interface AutoFormProps<Input extends Record<string, unknown>, Output> {
102
+ readonly api: string;
103
+ /** The mutation tag to bind to (`'todos.create'`). create vs. update are
104
+ * different tags → different forms; no CRUD-mode abstraction. */
105
+ readonly mutation: string;
106
+ /** The mutation's input `Schema`. Optional — resolved from the mounted
107
+ * descriptor's capability-map entry (`descriptors[tag].input`) when omitted. */
108
+ readonly schema?: Schema.Schema.Any;
109
+ readonly defaults?: Partial<Input>;
110
+ readonly onSuccess?: (output: Output) => void;
111
+ readonly submitLabel?: string;
112
+ /** Rung 3 — own the layout; arrange `<Field>`s yourself. The binding is
113
+ * passed for derived UI (pending, isValid, values). */
114
+ readonly children?: (binding: FormBinding<Input, Output>) => ReactNode;
115
+ }
116
+
117
+ /** Kit-styled default copilot surface: a question box wired to the action +
118
+ * a results region that swaps between refusal, empty, and a generic table. */
119
+ export declare function DataCopilot(props: DataCopilotProps): ReactNode;
120
+
121
+ /** Render a copilot answer: a refusal message, an empty note, or a table. */
122
+ export declare function DataCopilotAnswer(props: {
123
+ readonly answer: CopilotAnswer | undefined;
124
+ readonly emptyText?: string;
125
+ }): ReactNode;
126
+
127
+ export declare interface DataCopilotProps {
128
+ readonly api: string;
129
+ /** The copilot action tag (input `{ question }`, output `CopilotAnswer`). */
130
+ readonly action: string;
131
+ readonly placeholder?: string;
132
+ readonly askLabel?: string;
133
+ readonly emptyText?: string;
134
+ }
135
+
136
+ /** Headless render-prop over `useDataCopilot` for a fully custom layout. */
137
+ export declare function DataCopilotStream(props: DataCopilotStreamProps): ReactNode;
138
+
139
+ export declare interface DataCopilotStreamProps {
140
+ readonly api: string;
141
+ readonly action: string;
142
+ readonly children: (copilot: DataCopilotState) => ReactNode;
143
+ }
144
+
145
+ export declare function DataTable<Row extends Record<string, unknown> = Record<string, unknown>>(props: DataTableProps<Row>): ReactNode;
146
+
147
+ export declare interface DataTableProps<Row> extends UseDataTableOptions {
148
+ readonly api: string;
149
+ /** The reactive query tag whose rows + output schema drive the table. */
150
+ readonly query: string;
151
+ readonly emptyText?: string;
152
+ readonly loadingText?: string;
153
+ /** Label for the "Load more" button (shown when `pageSize` is set + more rows exist). */
154
+ readonly loadMoreText?: string;
155
+ /** Custom cell renderer; defaults to a string/boolean format. */
156
+ readonly renderCell?: (row: Row, column: FieldDescriptor) => ReactNode;
157
+ /** Per-row action cell (e.g. edit/delete buttons wired to mutations). */
158
+ readonly rowActions?: (row: Row) => ReactNode;
159
+ /** Stable row key; defaults to `row.id` then index. */
160
+ readonly rowKey?: (row: Row) => string;
161
+ }
162
+
163
+ /** The English defaults. A consumer overrides any subtree via
164
+ * {@link UiStringsProvider}; unspecified keys fall through to these. */
165
+ export declare const defaultUiStrings: UiStrings;
166
+
167
+ /** The built-in defaults. Plain accessible HTML; overridable per app via the
168
+ * registry, per field via a `<Field>` render-prop. `async-select` renders a
169
+ * plain text input by design — a registry widget can't carry the `api`/`source`
170
+ * binding a query-bound picker needs, so use the shipped `<AsyncSelect>`
171
+ * component (or a rung-2 override) for the live picker. */
172
+ export declare const defaultWidgets: Record<WidgetKind, Widget>;
173
+
174
+ /** "X is editing" — surfaces that other users are active on the same
175
+ * row/field. Soft (warn, not a hard lock). Renders nothing when alone. */
176
+ export declare function EditingIndicator(props: EditingIndicatorProps): ReactNode;
177
+
178
+ export declare interface EditingIndicatorProps {
179
+ readonly members: ReadonlyArray<PresenceMemberLike>;
180
+ /** Exclude the current user from the roster. */
181
+ readonly selfKey?: string;
182
+ /** Soft field-level scope — only members whose `meta.field` matches. */
183
+ readonly field?: string;
184
+ readonly verb?: string;
185
+ readonly nameOf?: (m: PresenceMemberLike) => string;
186
+ }
187
+
188
+ /** Render one field of the enclosing `<AutoForm>` — via its render-prop child
189
+ * (rung 1) or the resolved widget (the seam). Returns null for an unknown
190
+ * field name. */
191
+ export declare const Field: ({ name, children }: FieldProps) => ReactNode;
192
+
193
+ export declare interface FieldProps {
194
+ readonly name: string;
195
+ /** Rung 1 — render this field with a custom widget; the binding still owns
196
+ * value / validation / submit. Omit to use the seam's widget for the kind. */
197
+ readonly children?: (props: WidgetProps) => ReactNode;
198
+ }
199
+
200
+ /** A form-shaped skeleton: one label+control placeholder per field of the
201
+ * mutation's input Schema. */
202
+ export declare function FormSkeleton(props: FormSkeletonProps): ReactNode;
203
+
204
+ export declare interface FormSkeletonProps {
205
+ readonly api: string;
206
+ readonly mutation: string;
207
+ /** Fallback field count when the descriptor isn't resolvable yet. Default 3. */
208
+ readonly fallbackFields?: number;
209
+ }
210
+
211
+ /** Up-to-two-letter initials from a display name. */
212
+ export declare const initials: (name: string) => string;
213
+
214
+ /** A caller may override any subtree, not the whole object — deep-partial. The
215
+ * `workflow.status` map is itself partial so you can relabel a single status. */
216
+ export declare type PartialUiStrings = {
217
+ readonly [K in keyof UiStrings]?: K extends 'workflow' ? Partial<Omit<UiStrings['workflow'], 'status'>> & {
218
+ readonly status?: Partial<UiStrings['workflow']['status']>;
219
+ } : Partial<UiStrings[K]>;
220
+ };
221
+
222
+ /** Live roster of who's viewing this record/page. Feed it `usePresence(...)`. */
223
+ export declare function PresenceAvatars(props: PresenceAvatarsProps): ReactNode;
224
+
225
+ export declare interface PresenceAvatarsProps {
226
+ readonly members: ReadonlyArray<PresenceMemberLike>;
227
+ /** Cap the visible avatars; the rest collapse into a "+N" chip. Default 5. */
228
+ readonly max?: number;
229
+ readonly nameOf?: (m: PresenceMemberLike) => string;
230
+ /** Override one avatar's rendering (rung-1 escape hatch). */
231
+ readonly renderAvatar?: (m: PresenceMemberLike, name: string) => ReactNode;
232
+ }
233
+
234
+ /** Structural shape of a presence member — matches `PresenceMember` from
235
+ * @voltro/plugin-presence/web without coupling the kit to that package. */
236
+ export declare interface PresenceMemberLike {
237
+ readonly key: string;
238
+ readonly meta: Record<string, unknown> | null;
239
+ readonly lastSeen: number;
240
+ }
241
+
242
+ export declare function QueryFilters<Row = Record<string, unknown>>(props: QueryFiltersProps<Row>): ReactNode;
243
+
244
+ export declare interface QueryFiltersProps<Row> {
245
+ readonly api: string;
246
+ readonly query: string;
247
+ readonly initial?: Readonly<Record<string, unknown>>;
248
+ readonly showCount?: boolean;
249
+ /** Render the live results beneath the panel. */
250
+ readonly children?: (state: QueryFiltersState<Row>) => ReactNode;
251
+ }
252
+
253
+ export declare function RecordView<R extends Record<string, unknown> = Record<string, unknown>>(props: RecordViewProps<R>): ReactNode;
254
+
255
+ export declare interface RecordViewProps<R extends Record<string, unknown>> {
256
+ readonly api: string;
257
+ readonly query: string;
258
+ readonly input?: Readonly<Record<string, unknown>>;
259
+ /** Restrict/order the scalar fields shown (defaults to all non-relation keys). */
260
+ readonly fields?: ReadonlyArray<string>;
261
+ /** Override one scalar field's rendering (rung-1 escape hatch). */
262
+ readonly renderField?: (name: string, value: unknown, record: R) => ReactNode;
263
+ /** Override a relation's rendering (default: a nested table of the items). */
264
+ readonly renderRelation?: (name: string, items: ReadonlyArray<Record<string, unknown>>, record: R) => ReactNode;
265
+ readonly loadingText?: string;
266
+ readonly emptyText?: string;
267
+ }
268
+
269
+ /** Build the "X is editing" / "X and Y are editing" / "X and N others are
270
+ * editing" sentence from the editor names. Pure — exported for tests. The
271
+ * sentence templates come from the kit strings (localizable); defaults to the
272
+ * English {@link defaultUiStrings} presence subtree when none is passed. */
273
+ export declare const summarizeEditors: (names: ReadonlyArray<string>, verb: string, strings?: UiStrings["presence"]) => string;
274
+
275
+ /** A table-shaped skeleton: the query's real columns as header placeholders +
276
+ * N placeholder rows. */
277
+ export declare function TableSkeleton(props: TableSkeletonProps): ReactNode;
278
+
279
+ export declare interface TableSkeletonProps {
280
+ readonly api: string;
281
+ readonly query: string;
282
+ /** Skeleton row count. Default 5. */
283
+ readonly rows?: number;
284
+ /** Fallback column count when the descriptor isn't resolvable. Default 4. */
285
+ readonly fallbackColumns?: number;
286
+ }
287
+
288
+ /** Every user-facing string the kit renders. Scalars are literals; anything
289
+ * that interpolates a value (count, name, label) is a function so a locale can
290
+ * reorder/pluralize. English defaults live in {@link defaultUiStrings}. */
291
+ export declare interface UiStrings {
292
+ /** <QueryFilters> — the reset button, the "Any <label>" option, the result
293
+ * count, and the group's aria-label. */
294
+ readonly filters: {
295
+ readonly clear: string;
296
+ readonly filtersLabel: string;
297
+ readonly anyOption: (label: string) => string;
298
+ readonly resultCount: (count: number | string) => string;
299
+ };
300
+ /** <AsyncSelect> — the search box + loading/empty option text. */
301
+ readonly asyncSelect: {
302
+ readonly searchPlaceholder: string;
303
+ readonly searchLabel: (label: string) => string;
304
+ readonly loading: string;
305
+ /** The empty/no-selection option glyph (default `—`). */
306
+ readonly empty: string;
307
+ };
308
+ /** <DataTable> — the row-actions column header. */
309
+ readonly dataTable: {
310
+ readonly actions: string;
311
+ };
312
+ /** <WorkflowProgress> — per-status labels, the attempt suffix, loading/empty. */
313
+ readonly workflow: {
314
+ readonly status: Record<WorkflowRunStatus, string>;
315
+ readonly attempt: (n: number) => string;
316
+ readonly loading: string;
317
+ readonly empty: string;
318
+ };
319
+ /** <AgentChat> — the reasoning disclosure, message box + send button. */
320
+ readonly agentChat: {
321
+ readonly reasoning: string;
322
+ readonly messagePlaceholder: string;
323
+ readonly messageLabel: string;
324
+ readonly send: string;
325
+ };
326
+ /** <AppAgent> — the tool disclosure summary + the confirm/write hints. */
327
+ readonly appAgent: {
328
+ readonly toolsSummary: (count: number) => string;
329
+ readonly needsConfirm: string;
330
+ };
331
+ /** <PresenceAvatars> / <EditingIndicator> — the "N more" overflow, the online
332
+ * count, and the "X is editing" sentence template. `verb` is the activity
333
+ * (e.g. "editing"), threaded from the component. */
334
+ readonly presence: {
335
+ readonly moreOverflow: (n: number) => string;
336
+ readonly onlineCount: (n: number) => string;
337
+ readonly editingOne: (name: string, verb: string) => string;
338
+ readonly editingTwo: (a: string, b: string, verb: string) => string;
339
+ readonly editingMany: (name: string, othersCount: number, verb: string) => string;
340
+ };
341
+ /** <RecordView> — the empty-relation line. `relation` is already humanized. */
342
+ readonly recordView: {
343
+ readonly emptyRelation: (relation: string) => string;
344
+ };
345
+ }
346
+
347
+ /** Provide localized (or otherwise overridden) UI strings to every @voltro/ui
348
+ * component below. Overrides are deep-merged onto the English defaults — supply
349
+ * only the sections/keys you change. Nesting providers merges onto the parent. */
350
+ export declare function UiStringsProvider(props: {
351
+ readonly strings: PartialUiStrings;
352
+ readonly children: ReactNode;
353
+ }): ReactNode;
354
+
355
+ /** Read the active UI strings. Returns the English defaults when no provider is
356
+ * mounted, so every component works with zero setup. */
357
+ export declare const useUiStrings: () => UiStrings;
358
+
359
+ /** Resolve the component for a widget kind: registry override → built-in
360
+ * default → the `custom` fallback (never throws). */
361
+ export declare const useWidget: (kind: WidgetKind) => Widget;
362
+
363
+ export declare type Widget = (props: WidgetProps) => ReactNode;
364
+
365
+ /** The contract every widget receives. The form binding owns state +
366
+ * validation; a widget only renders the control + reports changes. */
367
+ export declare interface WidgetProps {
368
+ readonly name: string;
369
+ readonly label: string;
370
+ readonly value: unknown;
371
+ readonly onChange: (value: unknown) => void;
372
+ readonly error?: string;
373
+ readonly required: boolean;
374
+ readonly disabled?: boolean;
375
+ readonly options?: ReadonlyArray<FieldOption>;
376
+ }
377
+
378
+ export declare type WidgetRegistry = Partial<Record<WidgetKind, Widget>>;
379
+
380
+ /** Override widgets app-wide (rung 2). Nests: an inner provider merges over
381
+ * the outer one, so a subtree can refine the registry further. */
382
+ export declare const WidgetRegistryProvider: (props: {
383
+ readonly widgets: WidgetRegistry;
384
+ readonly children: ReactNode;
385
+ }) => ReactNode;
386
+
387
+ /** Live step timeline from `_voltro_workflow_run_steps`, pushed reactively as
388
+ * the run advances — no polling. Shows attempts + durations. */
389
+ export declare function WorkflowProgress(props: WorkflowProgressProps): ReactNode;
390
+
391
+ export declare interface WorkflowProgressProps {
392
+ readonly api: string;
393
+ readonly runId: string | undefined;
394
+ readonly loadingText?: string;
395
+ readonly emptyText?: string;
396
+ }
397
+
398
+ /** running / waiting / succeeded / failed, with the failing step + a
399
+ * `voltro logs --trace <id>` hint on error. */
400
+ export declare function WorkflowStatusBadge(props: WorkflowStatusBadgeProps): ReactNode;
401
+
402
+ export declare interface WorkflowStatusBadgeProps {
403
+ readonly api: string;
404
+ readonly runId: string | undefined;
405
+ }
406
+
407
+ export { }