@tangle-network/agent-app 0.45.63 → 0.45.65
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/dist/assistant/index.js +3 -3
- package/dist/chat-react/index.js +1 -1
- package/dist/{chunk-FZKNVQYP.js → chunk-23J72UUA.js} +104 -15
- package/dist/chunk-23J72UUA.js.map +1 -0
- package/dist/{chunk-FOXGPGXF.js → chunk-AWM4H5XR.js} +1393 -567
- package/dist/chunk-AWM4H5XR.js.map +1 -0
- package/dist/{chunk-PC2WYTK7.js → chunk-EDTWGSQT.js} +118 -1
- package/dist/chunk-EDTWGSQT.js.map +1 -0
- package/dist/session-shell/command-palette.d.ts +88 -0
- package/dist/session-shell/index.d.ts +1 -0
- package/dist/session-shell/index.js +13 -1
- package/dist/stories/chat-controls/fixtures.d.ts +5 -1
- package/dist/web-react/chat-composer.d.ts +115 -5
- package/dist/web-react/command-palette.d.ts +44 -0
- package/dist/web-react/composer-file-accept.d.ts +77 -0
- package/dist/web-react/index.d.ts +3 -0
- package/dist/web-react/index.js +23 -3
- package/dist/web-react/insight-card.d.ts +5 -1
- package/dist/web-react/record-grid-model.d.ts +45 -0
- package/dist/web-react/record-grid.d.ts +28 -2
- package/dist/web-react/use-composer-attachments.d.ts +3 -4
- package/dist/web-react/use-dictation.d.ts +67 -0
- package/dist/workspace-react/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-FOXGPGXF.js.map +0 -1
- package/dist/chunk-FZKNVQYP.js.map +0 -1
- package/dist/chunk-PC2WYTK7.js.map +0 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandPalette — the rendered half of the Cmd/Ctrl+K surface. Selection,
|
|
3
|
+
* ranking, and grouping live in `/session-shell` (`buildCommandPaletteItems`,
|
|
4
|
+
* `filterCommandPaletteItems`, `groupCommandPaletteItems`); this component is
|
|
5
|
+
* the overlay, the input, and the keyboard model.
|
|
6
|
+
*
|
|
7
|
+
* Placement follows the PopoverSurface canon (AGENTS.md "UI chrome
|
|
8
|
+
* ownership"): the panel PORTALS to `document.body` and positions in viewport
|
|
9
|
+
* coordinates (`fixed`), so no host markup — a scroll rail, a `transform`, a
|
|
10
|
+
* stacking context — can clip or trap it. Unlike the pickers it is CENTERED,
|
|
11
|
+
* not trigger-anchored: a palette has no trigger, so it does not reuse
|
|
12
|
+
* `PopoverSurface` itself, but it carries the same grammar — `bg-popover`,
|
|
13
|
+
* `border-card-edge`, `OVERLAY_SHADOW`, the stamped surface attribute.
|
|
14
|
+
*
|
|
15
|
+
* The keyboard model is the ARIA combobox pattern: focus stays in the input,
|
|
16
|
+
* ArrowUp/ArrowDown move `aria-activedescendant` across the FLAT result list
|
|
17
|
+
* (groups are presentation), Enter selects, Escape closes, and closing returns
|
|
18
|
+
* focus to whatever had it before the palette opened.
|
|
19
|
+
*/
|
|
20
|
+
import { type CommandPaletteItem } from '../session-shell/index';
|
|
21
|
+
export type { CommandPaletteItem } from '../session-shell/index';
|
|
22
|
+
export interface CommandPaletteProps {
|
|
23
|
+
/** The full item list, build-ordered (recent-first sessions, then actions).
|
|
24
|
+
* Filtering and ranking are owned here — pass the UNFILTERED list. */
|
|
25
|
+
items: CommandPaletteItem[];
|
|
26
|
+
/** A row was chosen (click or Enter). The palette closes itself. */
|
|
27
|
+
onSelect: (item: CommandPaletteItem) => void;
|
|
28
|
+
/** Controlled open state. Omit for self-managed state toggled by the hotkey. */
|
|
29
|
+
open?: boolean;
|
|
30
|
+
onOpenChange?: (open: boolean) => void;
|
|
31
|
+
/** Register the Cmd/Ctrl+K toggle. Default true. */
|
|
32
|
+
hotkey?: boolean;
|
|
33
|
+
/** Async source is still resolving — the input stays live, the list shows
|
|
34
|
+
* the loading row instead of a premature empty state. */
|
|
35
|
+
loading?: boolean;
|
|
36
|
+
/** Seed for the query (uncontrolled). */
|
|
37
|
+
initialQuery?: string;
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
/** Empty-state copy. Default names the query: `No results for “…”`. */
|
|
40
|
+
emptyMessage?: string;
|
|
41
|
+
/** Accessible name for the dialog. Default "Command palette". */
|
|
42
|
+
label?: string;
|
|
43
|
+
}
|
|
44
|
+
export declare function CommandPalette({ items, onSelect, open: controlledOpen, onOpenChange, hotkey, loading, initialQuery, placeholder, emptyMessage, label, }: CommandPaletteProps): import("react").ReactPortal | null;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The composer's file-ingress filter, and the clipboard rename that goes with
|
|
3
|
+
* it.
|
|
4
|
+
*
|
|
5
|
+
* A file reaches a composer by three routes — the picker dialog, a drag-and-drop,
|
|
6
|
+
* and a clipboard paste — and only the picker gets a native `accept` filter (one
|
|
7
|
+
* the user can defeat with "All Files"). Every route therefore funnels through
|
|
8
|
+
* {@link filterAcceptedFiles}, so a type the picker will not offer cannot arrive
|
|
9
|
+
* by another route instead.
|
|
10
|
+
*
|
|
11
|
+
* One route can still reach a different verdict, and it does so deliberately.
|
|
12
|
+
* Paste is the only route that RENAMES, and {@link renamePastedImages} names a
|
|
13
|
+
* file after the type it declares. So a clipboard bitmap called `image.png` that
|
|
14
|
+
* declares `image/heic` is judged as `.heic` on paste, while the picker and a
|
|
15
|
+
* drop judge the name they were handed and admit it under `accept=".png"`. The
|
|
16
|
+
* filter is the same on all three; what differs is the name it is given, and
|
|
17
|
+
* paste is stricter precisely because a rename that kept the contradicting name
|
|
18
|
+
* would let the composer manufacture its own way past the filter.
|
|
19
|
+
*
|
|
20
|
+
* ONE accept matcher serves the package: `ChatComposer` gates its ingress on it
|
|
21
|
+
* and `useComposerAttachments` gates `addFiles` on it. A second implementation
|
|
22
|
+
* of the `accept` grammar is how the two ends of the same staging path start
|
|
23
|
+
* disagreeing about what a file is.
|
|
24
|
+
*
|
|
25
|
+
* Pure data in, pure data out — nothing here throws, logs, or touches the DOM,
|
|
26
|
+
* so a caller decides how a rejection is surfaced. Import-free beyond the
|
|
27
|
+
* browser's own `File`, which keeps it usable from `/web-react`'s client bundle.
|
|
28
|
+
*/
|
|
29
|
+
/** A file the `accept` list refused, with the reason to show for it. */
|
|
30
|
+
export interface ComposerFileRejection {
|
|
31
|
+
file: File;
|
|
32
|
+
reason: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Checks one file against a comma-separated `accept` list, using the grammar of
|
|
36
|
+
* the native `<input accept>` attribute: extensions (`.png`), exact MIME types
|
|
37
|
+
* (`image/png`), and MIME wildcards (`image/*`). An absent or empty list accepts
|
|
38
|
+
* everything, which is what an unset `accept` prop means.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isAcceptedFileType(file: File, accept?: string): boolean;
|
|
41
|
+
/** The reason an `accept` list refused a file. One wording for every ingress
|
|
42
|
+
* route, so the same file reads the same whether it was picked or dropped. */
|
|
43
|
+
export declare function acceptRejectionReason(file: File, accept: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Splits a batch into what the `accept` list admits and what it refuses. Size
|
|
46
|
+
* and count limits are NOT applied here: they belong to the staging queue, which
|
|
47
|
+
* knows what is already staged (`useComposerAttachments`), while this runs at the
|
|
48
|
+
* composer's edge where that is unknown.
|
|
49
|
+
*/
|
|
50
|
+
export declare function filterAcceptedFiles(files: File[] | FileList, accept?: string): {
|
|
51
|
+
accepted: File[];
|
|
52
|
+
rejected: ComposerFileRejection[];
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Gives every generically-named clipboard image a distinct
|
|
56
|
+
* `pasted-image-<n>.<ext>` name. Two pastes of the same bitmap otherwise arrive
|
|
57
|
+
* as `image.png` twice, and a staging queue that keys on the name treats the
|
|
58
|
+
* second as a duplicate of the first.
|
|
59
|
+
*
|
|
60
|
+
* A number is never reused. The search avoids every `pasted-image-<n>` already
|
|
61
|
+
* present in `stagedNames` (the queue the host still holds, which outlives this
|
|
62
|
+
* composer's own count) and in the batch itself (one paste can carry a file
|
|
63
|
+
* already named that way beside a raw bitmap), so a collision is not reachable
|
|
64
|
+
* rather than merely unlikely. `startIndex` is the caller's running count, and
|
|
65
|
+
* `nextIndex` is the count to hand the next paste.
|
|
66
|
+
*
|
|
67
|
+
* Files that already carry a real name pass through untouched, so a copied
|
|
68
|
+
* `report.pdf` keeps being `report.pdf`. So does an image whose extension
|
|
69
|
+
* cannot be derived from what it declares — a renamed file must never claim a
|
|
70
|
+
* format it is not. Only the name changes: the bytes, the type and the
|
|
71
|
+
* modification time travel with it, so downstream fingerprinting still sees
|
|
72
|
+
* the file the user pasted.
|
|
73
|
+
*/
|
|
74
|
+
export declare function renamePastedImages(files: File[], startIndex: number, stagedNames?: Iterable<string>): {
|
|
75
|
+
files: File[];
|
|
76
|
+
nextIndex: number;
|
|
77
|
+
};
|
|
@@ -30,6 +30,7 @@ import type { WorkProductPersistedPart } from '../work-product/types';
|
|
|
30
30
|
export * from './chat-stream';
|
|
31
31
|
export * from './chat-interactions';
|
|
32
32
|
export * from './chat-composer';
|
|
33
|
+
export * from './composer-file-accept';
|
|
33
34
|
export * from './interaction-card-support';
|
|
34
35
|
export * from './interaction-question-card';
|
|
35
36
|
export * from './interaction-plan-card';
|
|
@@ -53,8 +54,10 @@ export * from './sandbox-terminal';
|
|
|
53
54
|
export * from './seat-paywall';
|
|
54
55
|
export * from './session-history';
|
|
55
56
|
export * from './record-grid';
|
|
57
|
+
export * from './command-palette';
|
|
56
58
|
export * from './sparkline';
|
|
57
59
|
export * from './insight-card';
|
|
60
|
+
export * from './use-dictation';
|
|
58
61
|
export { usePopover, usePending, PopoverSurface, POPOVER_SURFACE_ATTR, ModelPicker, EffortPicker, EffortMeter, effortMeterFill, effortLevelLabel, effortLevelsFromIds, reconcileEffortLevels, DEFAULT_EFFORT_LEVELS, EFFORT_METER_SEGMENTS, OVERLAY_SHADOW, type ModelPickerProps, type EffortPickerProps, type EffortLevel, type PopoverSurfaceProps, } from './controls';
|
|
59
62
|
export { AgentSessionControls, type AgentSessionControlsProps, } from './agent-session-controls';
|
|
60
63
|
import type { CatalogModel } from '../runtime/model-catalog';
|
package/dist/web-react/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ChatComposer,
|
|
4
4
|
ChatEmptyState,
|
|
5
5
|
ChatMessages,
|
|
6
|
+
CommandPalette,
|
|
6
7
|
DEFAULT_INSIGHT_PAGE_SIZE,
|
|
7
8
|
DEFAULT_MENTION_EMPTY_TEXT,
|
|
8
9
|
DEFAULT_MENTION_LIMIT,
|
|
@@ -50,12 +51,15 @@ import {
|
|
|
50
51
|
createSessionInteractionAttemptStore,
|
|
51
52
|
describeProvenance,
|
|
52
53
|
describeProvenanceSourceStatus,
|
|
54
|
+
dictationErrorMessage,
|
|
55
|
+
diffRecordGridProposal,
|
|
53
56
|
dispatchChatStreamLine,
|
|
54
57
|
durableChatCardsFromParts,
|
|
55
58
|
fieldAnswer,
|
|
56
59
|
fieldValuesFromAnswers,
|
|
57
60
|
formatActivityCost,
|
|
58
61
|
formatActivityDuration,
|
|
62
|
+
formatDictationElapsed,
|
|
59
63
|
formatInsightDelta,
|
|
60
64
|
formatModelCost,
|
|
61
65
|
formatRecordGridValue,
|
|
@@ -81,6 +85,7 @@ import {
|
|
|
81
85
|
nextRevealCount,
|
|
82
86
|
parseRecordGridInput,
|
|
83
87
|
pendingApprovalOf,
|
|
88
|
+
pickDictationMimeType,
|
|
84
89
|
projectRecordGridRows,
|
|
85
90
|
provenanceBasisMeta,
|
|
86
91
|
provenanceGaps,
|
|
@@ -113,6 +118,7 @@ import {
|
|
|
113
118
|
triggerAttachmentDownload,
|
|
114
119
|
upsertChatInteraction,
|
|
115
120
|
useChatInteractions,
|
|
121
|
+
useDictation,
|
|
116
122
|
useDurablePlanFlow,
|
|
117
123
|
useFileMentions,
|
|
118
124
|
useInfiniteScroll,
|
|
@@ -131,7 +137,7 @@ import {
|
|
|
131
137
|
withoutRecordGridCreated,
|
|
132
138
|
withoutRecordGridRemoved,
|
|
133
139
|
withoutRecordGridUpdate
|
|
134
|
-
} from "../chunk-
|
|
140
|
+
} from "../chunk-AWM4H5XR.js";
|
|
135
141
|
import "../chunk-FBVLEGEG.js";
|
|
136
142
|
import {
|
|
137
143
|
EvidenceLineageTable,
|
|
@@ -159,14 +165,18 @@ import {
|
|
|
159
165
|
OVERLAY_SHADOW,
|
|
160
166
|
POPOVER_SURFACE_ATTR,
|
|
161
167
|
PopoverSurface,
|
|
168
|
+
acceptRejectionReason,
|
|
162
169
|
effortLevelLabel,
|
|
163
170
|
effortLevelsFromIds,
|
|
164
171
|
effortMeterFill,
|
|
172
|
+
filterAcceptedFiles,
|
|
173
|
+
isAcceptedFileType,
|
|
165
174
|
reconcileEffortLevels,
|
|
175
|
+
renamePastedImages,
|
|
166
176
|
useComposerAttachments,
|
|
167
177
|
usePending,
|
|
168
178
|
usePopover
|
|
169
|
-
} from "../chunk-
|
|
179
|
+
} from "../chunk-23J72UUA.js";
|
|
170
180
|
import {
|
|
171
181
|
ProviderLogo
|
|
172
182
|
} from "../chunk-MLG6XKPV.js";
|
|
@@ -175,7 +185,7 @@ import {
|
|
|
175
185
|
useSandboxTerminalConnection
|
|
176
186
|
} from "../chunk-BATKJP3P.js";
|
|
177
187
|
import "../chunk-3UBAO3N5.js";
|
|
178
|
-
import "../chunk-
|
|
188
|
+
import "../chunk-EDTWGSQT.js";
|
|
179
189
|
import "../chunk-QY4BRKRJ.js";
|
|
180
190
|
import {
|
|
181
191
|
ATTACHMENT_ACCEPT
|
|
@@ -238,6 +248,7 @@ export {
|
|
|
238
248
|
ChatComposer,
|
|
239
249
|
ChatEmptyState,
|
|
240
250
|
ChatMessages,
|
|
251
|
+
CommandPalette,
|
|
241
252
|
DEFAULT_EFFORT_LEVELS,
|
|
242
253
|
DEFAULT_INSIGHT_PAGE_SIZE,
|
|
243
254
|
DEFAULT_MENTION_EMPTY_TEXT,
|
|
@@ -296,6 +307,7 @@ export {
|
|
|
296
307
|
Sparkline,
|
|
297
308
|
WorkProductCard,
|
|
298
309
|
__resetAttachmentFileCacheForTests,
|
|
310
|
+
acceptRejectionReason,
|
|
299
311
|
activityTone,
|
|
300
312
|
attachmentInputToPart,
|
|
301
313
|
attachmentKindForMime,
|
|
@@ -320,6 +332,8 @@ export {
|
|
|
320
332
|
dedupeQuestionInteractionsByContent,
|
|
321
333
|
describeProvenance,
|
|
322
334
|
describeProvenanceSourceStatus,
|
|
335
|
+
dictationErrorMessage,
|
|
336
|
+
diffRecordGridProposal,
|
|
323
337
|
dispatchChatStreamLine,
|
|
324
338
|
durableChatCardsFromParts,
|
|
325
339
|
effortLevelLabel,
|
|
@@ -329,8 +343,10 @@ export {
|
|
|
329
343
|
fieldAnswer,
|
|
330
344
|
fieldValuesFromAnswers,
|
|
331
345
|
fileMentionsToParts,
|
|
346
|
+
filterAcceptedFiles,
|
|
332
347
|
formatActivityCost,
|
|
333
348
|
formatActivityDuration,
|
|
349
|
+
formatDictationElapsed,
|
|
334
350
|
formatInsightDelta,
|
|
335
351
|
formatModelCost,
|
|
336
352
|
formatRecordGridValue,
|
|
@@ -350,6 +366,7 @@ export {
|
|
|
350
366
|
interactionSubmissionSignature,
|
|
351
367
|
interactionTerminalNotes,
|
|
352
368
|
interactionToPersistedPart,
|
|
369
|
+
isAcceptedFileType,
|
|
353
370
|
isChatAttachmentPart,
|
|
354
371
|
isLateAnswerableStatus,
|
|
355
372
|
isRecordGridCellApplicable,
|
|
@@ -375,6 +392,7 @@ export {
|
|
|
375
392
|
parseReviewQueueItem,
|
|
376
393
|
pendingApprovalOf,
|
|
377
394
|
persistedPartToInteraction,
|
|
395
|
+
pickDictationMimeType,
|
|
378
396
|
projectRecordGridRows,
|
|
379
397
|
provenanceBasisMeta,
|
|
380
398
|
provenanceGaps,
|
|
@@ -390,6 +408,7 @@ export {
|
|
|
390
408
|
recordGridFail,
|
|
391
409
|
recordGridOk,
|
|
392
410
|
recordGridRowLabel,
|
|
411
|
+
renamePastedImages,
|
|
393
412
|
resolveChatInteraction,
|
|
394
413
|
resolveProvenanceStanding,
|
|
395
414
|
responseErrorMessage,
|
|
@@ -413,6 +432,7 @@ export {
|
|
|
413
432
|
upsertChatInteraction,
|
|
414
433
|
useChatInteractions,
|
|
415
434
|
useComposerAttachments,
|
|
435
|
+
useDictation,
|
|
416
436
|
useDurablePlanFlow,
|
|
417
437
|
useFileMentions,
|
|
418
438
|
useInfiniteScroll,
|
|
@@ -81,6 +81,10 @@ export interface InsightAction {
|
|
|
81
81
|
onClick: () => void;
|
|
82
82
|
}
|
|
83
83
|
export interface InsightCardProps {
|
|
84
|
+
/** The lane the metric belongs to ("Spend", "Missions"), set small above the
|
|
85
|
+
* title. A deck of cards from different surfaces needs the grouping word
|
|
86
|
+
* before the metric's own name, not after it. */
|
|
87
|
+
eyebrow?: string;
|
|
84
88
|
/** What was measured, in the reader's words ("Spend today"). */
|
|
85
89
|
title: string;
|
|
86
90
|
/** The number that moved. A `string` renders verbatim — a total the caller
|
|
@@ -113,7 +117,7 @@ export interface InsightCardProps {
|
|
|
113
117
|
className?: string;
|
|
114
118
|
style?: CSSProperties;
|
|
115
119
|
}
|
|
116
|
-
export declare function InsightCard({ title, value, unit, previous, polarity, format, series, seriesLabel, description, action, live, liveLabel, className, style, }: InsightCardProps): ReactElement;
|
|
120
|
+
export declare function InsightCard({ eyebrow, title, value, unit, previous, polarity, format, series, seriesLabel, description, action, live, liveLabel, className, style, }: InsightCardProps): ReactElement;
|
|
117
121
|
export interface Insight extends InsightCardProps {
|
|
118
122
|
/** Stable across refreshes: it keys the card. Paired with the deck holding
|
|
119
123
|
* the last loaded page across a reload, a stable id is what lets a settled
|
|
@@ -177,6 +177,51 @@ export declare function isRecordGridCellApplicable(column: RecordGridColumn, val
|
|
|
177
177
|
/** Value equality across the grid's value union, treating `undefined` as
|
|
178
178
|
* `null` so an absent key and an explicit null never read as a change. */
|
|
179
179
|
export declare function sameRecordGridValue(a: RecordGridValue | undefined, b: RecordGridValue | undefined): boolean;
|
|
180
|
+
/**
|
|
181
|
+
* A proposed change set, diffed against the live rows by
|
|
182
|
+
* {@link diffRecordGridProposal}. The grid's review mode renders exactly what
|
|
183
|
+
* this shape declares — it owns no opinion about where the proposal came from
|
|
184
|
+
* (an agent's `submit_proposal` call, a record store's pending entries).
|
|
185
|
+
*/
|
|
186
|
+
export interface RecordGridProposal {
|
|
187
|
+
/** Proposed new cell values for EXISTING rows: row id → column id → value.
|
|
188
|
+
* Only cells that differ from the live value diff; an update that restates
|
|
189
|
+
* the current value is not a change. An id with no live row is ignored —
|
|
190
|
+
* adding a row is `additions`' job. */
|
|
191
|
+
updates?: Readonly<Record<string, Readonly<Record<string, RecordGridValue>>>>;
|
|
192
|
+
/** Proposed new rows. An addition whose id already names a live row is
|
|
193
|
+
* ignored — changing an existing row is `updates`' job. */
|
|
194
|
+
additions?: readonly RecordGridRow[];
|
|
195
|
+
/** Live row ids proposed for removal. Unknown ids are ignored. */
|
|
196
|
+
removals?: readonly string[];
|
|
197
|
+
}
|
|
198
|
+
/** One cell whose proposed value differs from the live one. */
|
|
199
|
+
export interface RecordGridCellDiff {
|
|
200
|
+
columnId: string;
|
|
201
|
+
/** The live value — what rejecting keeps. */
|
|
202
|
+
before: RecordGridValue;
|
|
203
|
+
/** The proposed value — what accepting writes. */
|
|
204
|
+
after: RecordGridValue;
|
|
205
|
+
}
|
|
206
|
+
export type RecordGridRowDiffKind = 'changed' | 'added' | 'removed';
|
|
207
|
+
/** One row's verdict: what the proposal does to it. */
|
|
208
|
+
export interface RecordGridRowDiff {
|
|
209
|
+
rowId: string;
|
|
210
|
+
kind: RecordGridRowDiffKind;
|
|
211
|
+
/** The differing cells. Empty for `added`/`removed` — every cell of those is
|
|
212
|
+
* part of the change by definition. */
|
|
213
|
+
cells: readonly RecordGridCellDiff[];
|
|
214
|
+
/** The live row for `changed`/`removed`, the proposed row for `added`. */
|
|
215
|
+
row: RecordGridRow;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Diff a proposal against the live rows. Pure and deterministic: input order
|
|
219
|
+
* in, diff order out — updates follow `rows` order, removals follow `rows`
|
|
220
|
+
* order, additions follow the proposal's order. A row whose update bag diffs
|
|
221
|
+
* to nothing produces no entry, so a no-op proposal yields an empty diff and
|
|
222
|
+
* the grid has nothing to review.
|
|
223
|
+
*/
|
|
224
|
+
export declare function diffRecordGridProposal(rows: readonly RecordGridRow[], proposal: RecordGridProposal): RecordGridRowDiff[];
|
|
180
225
|
/**
|
|
181
226
|
* Turn what an editor control produced into a typed value. Syntax only —
|
|
182
227
|
* range, length, and membership are {@link validateRecordGridCell}'s job.
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
* - **Provenance per cell.** An optional quote + link + basis, so a
|
|
18
18
|
* record-backed grid shows where a value came from without the product
|
|
19
19
|
* building a second surface for it.
|
|
20
|
+
* - **Review of a proposed change set.** Hand the grid a `proposed` patch
|
|
21
|
+
* (`./record-grid-model`'s `diffRecordGridProposal`) and it becomes the
|
|
22
|
+
* red/green row-diff surface a tax/legal review needs: changed cells render
|
|
23
|
+
* the struck live value against the proposed one, added/removed rows are
|
|
24
|
+
* marked, and every diffed row carries accept/reject — per row and for the
|
|
25
|
+
* whole set. What accepting MEANS stays the caller's (a record-store review
|
|
26
|
+
* write); the grid reports decisions, it does not persist them.
|
|
20
27
|
* - **Three distinct data states, on `web-react/async`'s own contract.**
|
|
21
28
|
* `state: AsyncResourceState<Row[]>` and `empty: AsyncEmptySpec` are the
|
|
22
29
|
* same types every other screen fetches through — loading, error-with-
|
|
@@ -35,7 +42,7 @@
|
|
|
35
42
|
*/
|
|
36
43
|
import { type ReactNode } from 'react';
|
|
37
44
|
import { type AsyncEmptySpec, type AsyncResourceState } from './async';
|
|
38
|
-
import { type RecordGridColumn, type RecordGridRow, type RecordGridValue } from './record-grid-model';
|
|
45
|
+
import { type RecordGridColumn, type RecordGridProposal, type RecordGridRow, type RecordGridValue } from './record-grid-model';
|
|
39
46
|
export * from './record-grid-model';
|
|
40
47
|
/** One committed cell edit, handed to `onUpdate`. */
|
|
41
48
|
export interface RecordGridCellChange {
|
|
@@ -88,6 +95,25 @@ export interface RecordGridProps {
|
|
|
88
95
|
onUpdate?: (change: RecordGridCellChange) => Promise<RecordGridWriteOutcome>;
|
|
89
96
|
/** Delete one row. Absent → no delete affordance. */
|
|
90
97
|
onDelete?: (row: RecordGridRow) => Promise<RecordGridWriteOutcome>;
|
|
98
|
+
/** A proposed change set to review against the live rows (see
|
|
99
|
+
* `diffRecordGridProposal`). While a non-empty diff is on the table the grid
|
|
100
|
+
* is a REVIEW surface, not an editor: cell editing, row add, and row delete
|
|
101
|
+
* are inert; changed cells render the struck live value against the
|
|
102
|
+
* proposed one; added/removed rows are marked; each diffed row carries
|
|
103
|
+
* accept/reject controls. A proposal that diffs to nothing renders the grid
|
|
104
|
+
* unchanged — there is nothing to review. */
|
|
105
|
+
proposed?: RecordGridProposal;
|
|
106
|
+
/** Accept one diffed row — write its proposed values, adopt the addition, or
|
|
107
|
+
* confirm the removal. The caller owns what accepting MEANS (a record-store
|
|
108
|
+
* review write); the grid reports the decision and the caller moves the row
|
|
109
|
+
* out of `proposed`. */
|
|
110
|
+
onAcceptRow?: (rowId: string) => void;
|
|
111
|
+
/** Reject one diffed row — the live row stands. */
|
|
112
|
+
onRejectRow?: (rowId: string) => void;
|
|
113
|
+
/** Accept every remaining diffed row at once. */
|
|
114
|
+
onAcceptAll?: () => void;
|
|
115
|
+
/** Reject every remaining diffed row at once. */
|
|
116
|
+
onRejectAll?: () => void;
|
|
91
117
|
/** Starting values for the add form. */
|
|
92
118
|
newRowDefaults?: Readonly<Record<string, RecordGridValue>>;
|
|
93
119
|
/** Label of the add control and of the add form. Defaults to `Add row`. */
|
|
@@ -105,4 +131,4 @@ export interface RecordGridProps {
|
|
|
105
131
|
* id, so a record store's fold output maps straight on: one cell per entry,
|
|
106
132
|
* its quote and link in `sources`.
|
|
107
133
|
*/
|
|
108
|
-
export declare function RecordGrid({ columns, caption, state, empty, onCreate, onUpdate, onDelete, newRowDefaults, addLabel, locale, toolbar, loadingRowCount, className, }: RecordGridProps): import("react").JSX.Element;
|
|
134
|
+
export declare function RecordGrid({ columns, caption, state, empty, onCreate, onUpdate, onDelete, proposed, onAcceptRow, onRejectRow, onAcceptAll, onRejectAll, newRowDefaults, addLabel, locale, toolbar, loadingRowCount, className, }: RecordGridProps): import("react").JSX.Element;
|
|
@@ -15,10 +15,9 @@
|
|
|
15
15
|
* the URL and a `RequestInit` override, e.g. an auth header);
|
|
16
16
|
* - `sonner` toasts become `onReject` (client pre-validation, never hits the
|
|
17
17
|
* network) and `onError` (a request that reached the server and failed);
|
|
18
|
-
* - the
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* stays free of the sandbox-ui peer;
|
|
18
|
+
* - the `accept`-string gate comes from `./composer-file-accept`, the one
|
|
19
|
+
* matcher `ChatComposer` also funnels its picker/drop/paste ingress
|
|
20
|
+
* through, so both ends of the staging path admit the same files;
|
|
22
21
|
* - the response is expected to be `{ files: ChatAttachmentInput[] }` (full
|
|
23
22
|
* server-authoritative descriptors — size/mediaType/kind — not gtm's
|
|
24
23
|
* `{path, name}`), so `references` is a verbatim pass-through with no
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `useDictation` — the capture half of composer dictation.
|
|
3
|
+
*
|
|
4
|
+
* Dictation splits at a clean seam: the browser owns capture (`getUserMedia` +
|
|
5
|
+
* `MediaRecorder`), the host owns what the audio MEANS (transcription —
|
|
6
|
+
* `sequences-react`'s Whisper provider — or a straight upload). This hook is
|
|
7
|
+
* the capture half and nothing else: it asks for the mic, records, ticks whole
|
|
8
|
+
* seconds while it does, and hands the assembled `Blob` to the host's
|
|
9
|
+
* `onDictate`. A hook rather than composer-private code, because a host whose
|
|
10
|
+
* composer is fully composed (hotkey, push-to-talk) needs the same capture
|
|
11
|
+
* without re-deriving it.
|
|
12
|
+
*
|
|
13
|
+
* The rules the implementation exists to hold:
|
|
14
|
+
*
|
|
15
|
+
* - **Unsupported is a render signal, not an exception.** A browser without
|
|
16
|
+
* `MediaRecorder`/`getUserMedia` gets `supported: false`, and the composer
|
|
17
|
+
* renders no dead button. `start()` stays a no-op rather than throwing, so
|
|
18
|
+
* a host that wired it to a hotkey cannot crash on such a browser.
|
|
19
|
+
* - **The mic is released the moment recording ends.** Tracks are stopped in
|
|
20
|
+
* every exit — stop, error, cancel-during-prompt, unmount. A red dot the
|
|
21
|
+
* browser keeps showing after the composer says "idle" is the failure this
|
|
22
|
+
* is written against.
|
|
23
|
+
* - **A denied prompt is a message, not a crash.** `NotAllowedError` and a
|
|
24
|
+
* missing device are reported through `onError` as words the composer can
|
|
25
|
+
* show; the hook returns to idle.
|
|
26
|
+
* - **Unmount discards.** A composer that unmounts mid-recording delivers
|
|
27
|
+
* nothing: the host it would have called has moved on, and an arriving
|
|
28
|
+
* transcript would land in a conversation the user left.
|
|
29
|
+
* - **Duration is measured, not counted.** `durationSeconds` comes off the
|
|
30
|
+
* clock at stop; the one-second ticker drives only the visible elapsed
|
|
31
|
+
* display, so a throttled timer never falsifies the delivered figure.
|
|
32
|
+
*/
|
|
33
|
+
/** The audio a finished recording hands to the host. */
|
|
34
|
+
export interface DictationAudio {
|
|
35
|
+
/** The assembled recording, typed with the MIME the recorder actually used. */
|
|
36
|
+
readonly blob: Blob;
|
|
37
|
+
/** `blob.type`, surfaced so a host can switch on it without touching the blob. */
|
|
38
|
+
readonly mimeType: string;
|
|
39
|
+
/** Clock-measured whole seconds between start and stop. */
|
|
40
|
+
readonly durationSeconds: number;
|
|
41
|
+
}
|
|
42
|
+
export interface UseDictationOptions {
|
|
43
|
+
/** The host callback: receive the recording. Transcription is the host's. */
|
|
44
|
+
onDictate: (audio: DictationAudio) => void;
|
|
45
|
+
/** Capture failures in words ("Microphone access was denied…"). Optional —
|
|
46
|
+
* the composer shows its own notice either way; this is for hosts that log. */
|
|
47
|
+
onError?: (message: string) => void;
|
|
48
|
+
}
|
|
49
|
+
export interface DictationControls {
|
|
50
|
+
/** Whether this browser can record at all. When false, render no affordance. */
|
|
51
|
+
readonly supported: boolean;
|
|
52
|
+
readonly recording: boolean;
|
|
53
|
+
/** Whole seconds since the current recording started; drives the indicator. */
|
|
54
|
+
readonly elapsedSeconds: number;
|
|
55
|
+
/** Ask for the mic and start. A no-op while a recording or a prompt is open. */
|
|
56
|
+
readonly start: () => void;
|
|
57
|
+
/** Stop and deliver. Cancels a still-pending permission prompt instead. */
|
|
58
|
+
readonly stop: () => void;
|
|
59
|
+
}
|
|
60
|
+
/** The mime to ask the recorder for, or `undefined` to take the UA default. */
|
|
61
|
+
export declare function pickDictationMimeType(): string | undefined;
|
|
62
|
+
/** The failure as a sentence. The denied prompt is the common case and the one
|
|
63
|
+
* whose generic name ("NotAllowedError") says nothing to a reader. */
|
|
64
|
+
export declare function dictationErrorMessage(error: unknown): string;
|
|
65
|
+
/** `0:00`, `0:09`, `1:05`, `60:00` — minutes unbounded, seconds always two digits. */
|
|
66
|
+
export declare function formatDictationElapsed(totalSeconds: number): string;
|
|
67
|
+
export declare function useDictation({ onDictate, onError }: UseDictationOptions): DictationControls;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-app",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.65",
|
|
4
4
|
"packageManager": "pnpm@11.17.0",
|
|
5
5
|
"description": "Build agent applications with typed chat, tools, sandboxes, integrations, billing, and evaluation.",
|
|
6
6
|
"keywords": [
|
|
@@ -524,7 +524,7 @@
|
|
|
524
524
|
"@tangle-network/agent-interface": "1.0.1",
|
|
525
525
|
"@tangle-network/agent-knowledge": "8.0.8",
|
|
526
526
|
"@tangle-network/agent-profile-materialize": "0.16.0",
|
|
527
|
-
"@tangle-network/agent-runtime": "0.
|
|
527
|
+
"@tangle-network/agent-runtime": "0.141.1",
|
|
528
528
|
"@tangle-network/brand": "1.5.0",
|
|
529
529
|
"@tangle-network/sandbox": "0.27.2",
|
|
530
530
|
"@tangle-network/sandbox-ui": "0.105.0",
|
|
@@ -576,7 +576,7 @@
|
|
|
576
576
|
"@tangle-network/agent-interface": "^1.0.0",
|
|
577
577
|
"@tangle-network/agent-knowledge": ">=8.0.8",
|
|
578
578
|
"@tangle-network/agent-profile-materialize": ">=0.16.0",
|
|
579
|
-
"@tangle-network/agent-runtime": ">=0.
|
|
579
|
+
"@tangle-network/agent-runtime": ">=0.141.1",
|
|
580
580
|
"@tangle-network/brand": ">=1.5.0",
|
|
581
581
|
"@tangle-network/sandbox": ">=0.27.2",
|
|
582
582
|
"@tangle-network/sandbox-ui": ">=0.105.0",
|