@agent-native/toolkit 0.22.2-nightly-20260927082735 → 0.22.2
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/composer/ComposerContextMenu.d.ts +2 -1
- package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
- package/dist/composer/ComposerContextMenu.js +7 -2
- package/dist/composer/ComposerContextMenu.js.map +1 -1
- package/dist/composer/ComposerContextMenu.spec.js +9 -0
- package/dist/composer/ComposerContextMenu.spec.js.map +1 -1
- package/dist/composer/ComposerPlusMenu.js +1 -1
- package/dist/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/composer/PromptComposer.context.spec.js +34 -0
- package/dist/composer/PromptComposer.context.spec.js.map +1 -1
- package/dist/composer/PromptComposer.d.ts +85 -1
- package/dist/composer/PromptComposer.d.ts.map +1 -1
- package/dist/composer/PromptComposer.js +39 -4
- package/dist/composer/PromptComposer.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts +136 -1
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +101 -2
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/package.json +1 -1
- package/src/composer/ComposerContextMenu.spec.tsx +15 -0
- package/src/composer/ComposerContextMenu.tsx +10 -1
- package/src/composer/ComposerPlusMenu.tsx +2 -2
- package/src/composer/PromptComposer.context.spec.tsx +48 -0
- package/src/composer/PromptComposer.tsx +131 -0
- package/src/composer/TiptapComposer.tsx +221 -0
|
@@ -56,6 +56,54 @@ describe("controlled composer context", () => {
|
|
|
56
56
|
container.querySelector('button[aria-label="Add context"]'),
|
|
57
57
|
).toBeNull();
|
|
58
58
|
});
|
|
59
|
+
it("requests storage setup only after choosing Upload File", async () => {
|
|
60
|
+
const onAttachmentRequest = vi.fn();
|
|
61
|
+
await mount({
|
|
62
|
+
attachmentsEnabled: false,
|
|
63
|
+
plusMenuMode: "full",
|
|
64
|
+
onAttachmentRequest,
|
|
65
|
+
});
|
|
66
|
+
expect(onAttachmentRequest).not.toHaveBeenCalled();
|
|
67
|
+
|
|
68
|
+
await act(async () => {
|
|
69
|
+
container
|
|
70
|
+
.querySelector<HTMLButtonElement>(
|
|
71
|
+
'button[data-agent-composer-slot="plus-button"]',
|
|
72
|
+
)!
|
|
73
|
+
.click();
|
|
74
|
+
});
|
|
75
|
+
const uploadFile = Array.from(
|
|
76
|
+
document.querySelectorAll<HTMLButtonElement>("button"),
|
|
77
|
+
).find((button) => button.textContent?.includes("Upload File"));
|
|
78
|
+
expect(uploadFile).toBeDefined();
|
|
79
|
+
await act(async () => uploadFile!.click());
|
|
80
|
+
|
|
81
|
+
expect(onAttachmentRequest).toHaveBeenCalledOnce();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("requests storage setup from the upload-only button", async () => {
|
|
85
|
+
const onAttachmentRequest = vi.fn();
|
|
86
|
+
await mount({
|
|
87
|
+
attachmentsEnabled: false,
|
|
88
|
+
onAttachmentRequest,
|
|
89
|
+
plusMenuMode: "upload-only",
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(onAttachmentRequest).not.toHaveBeenCalled();
|
|
93
|
+
const trigger = container.querySelector<HTMLButtonElement>(
|
|
94
|
+
'button[aria-label="Add context"]',
|
|
95
|
+
)!;
|
|
96
|
+
await act(async () =>
|
|
97
|
+
trigger.dispatchEvent(
|
|
98
|
+
new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }),
|
|
99
|
+
),
|
|
100
|
+
);
|
|
101
|
+
const uploadFile = document.querySelector<HTMLElement>('[role="menuitem"]');
|
|
102
|
+
expect(uploadFile).toBeDefined();
|
|
103
|
+
expect(uploadFile?.textContent).toContain("Upload File");
|
|
104
|
+
await act(async () => uploadFile!.click());
|
|
105
|
+
expect(onAttachmentRequest).toHaveBeenCalledOnce();
|
|
106
|
+
});
|
|
59
107
|
async function mount(props: Partial<PromptComposerProps> = {}) {
|
|
60
108
|
const composerRef = React.createRef<TiptapComposerHandle>();
|
|
61
109
|
const onSubmit = vi.fn();
|
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
isLocalRuntimeEngine,
|
|
55
55
|
TiptapComposer,
|
|
56
56
|
type ComposerAgentOption,
|
|
57
|
+
type ComposerImageModelMenu,
|
|
57
58
|
type ComposerSubmitIntent,
|
|
58
59
|
type TiptapComposerHandle,
|
|
59
60
|
type TiptapComposerSubmitOptions,
|
|
@@ -67,6 +68,11 @@ import type {
|
|
|
67
68
|
|
|
68
69
|
const MAX_INLINE_TEXT_FILE_CHARS = 60_000;
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Files the user attached via the "+" button in PromptComposer. The host owns
|
|
73
|
+
* what to do with them — typically POST to a per-app upload endpoint and pass
|
|
74
|
+
* the resulting URLs/paths into the prompt that gets sent to the agent.
|
|
75
|
+
*/
|
|
70
76
|
export type PromptComposerFile = File;
|
|
71
77
|
|
|
72
78
|
export interface PromptComposerSubmitOptions {
|
|
@@ -84,83 +90,165 @@ export interface PromptComposerProps {
|
|
|
84
90
|
onInspectContextItem?: (key: string) => void;
|
|
85
91
|
onRetryContextItem?: (key: string) => void;
|
|
86
92
|
contextMenuItems?: readonly ComposerContextMenuItem[];
|
|
93
|
+
/** Called when the user submits the composer. */
|
|
87
94
|
onSubmit: (
|
|
88
95
|
text: string,
|
|
89
96
|
files: PromptComposerFile[],
|
|
90
97
|
references: Reference[],
|
|
91
98
|
options: PromptComposerSubmitOptions,
|
|
92
99
|
) => void | Promise<void>;
|
|
100
|
+
/** Return false to stop a submit before it reaches the host runtime. */
|
|
101
|
+
onBeforeSubmit?: () => boolean | Promise<boolean>;
|
|
102
|
+
/** Handle file paste/drop errors in the host chat surface. */
|
|
103
|
+
onAttachmentError?: (message: string) => void;
|
|
104
|
+
/** Delegate app-scaffolding prompts to the enclosing Builder chat. */
|
|
105
|
+
interceptBuildRequestsForBuilder?: boolean;
|
|
93
106
|
placeholder?: string;
|
|
107
|
+
/** Accessible name forwarded to the rich text editor. */
|
|
94
108
|
ariaLabel?: string;
|
|
95
109
|
disabled?: boolean;
|
|
110
|
+
/** Block all submission paths while allowing draft, file, and context staging. */
|
|
96
111
|
submissionDisabled?: boolean;
|
|
112
|
+
/** Prevent submission while preserving editor focus and draft entry. */
|
|
97
113
|
submitting?: boolean;
|
|
114
|
+
/** Present the primary action as queueing instead of immediate send. */
|
|
98
115
|
willQueue?: boolean;
|
|
116
|
+
/** Called when a host-gated composer is clicked while it is disabled. */
|
|
99
117
|
onDisabledClick?: () => void;
|
|
118
|
+
/** Override the generic document attachment cap for a multipart host. */
|
|
100
119
|
maxDocumentAttachmentBytes?: number;
|
|
120
|
+
/** Label used in the visible document attachment limit error. */
|
|
101
121
|
documentAttachmentLimitLabel?: string;
|
|
102
122
|
autoFocus?: boolean;
|
|
103
123
|
className?: string;
|
|
104
124
|
style?: CSSProperties;
|
|
105
125
|
rootClassName?: string;
|
|
106
126
|
rootStyle?: CSSProperties;
|
|
127
|
+
/** Forwarded to TiptapComposer for draft persistence. */
|
|
107
128
|
draftScope?: string;
|
|
129
|
+
/** Keep the submitted prompt in the editor. Default: false. */
|
|
108
130
|
preserveDraftOnSubmit?: boolean;
|
|
131
|
+
/** Show the model selector (default: true). */
|
|
109
132
|
showModelSelector?: boolean;
|
|
133
|
+
/** Controlled open state for hosts that resize around the model picker. */
|
|
110
134
|
modelSelectorOpen?: boolean;
|
|
135
|
+
/** Show the legacy provider-level Auto model option (default: true). */
|
|
111
136
|
showAutoModelOption?: boolean;
|
|
137
|
+
/** Show the voice dictation button. Defaults to DEFAULT_VOICE_DICTATION_ENABLED. */
|
|
112
138
|
voiceEnabled?: boolean;
|
|
139
|
+
/** Show file upload controls and pass submitted files to onSubmit (default: true). */
|
|
113
140
|
attachmentsEnabled?: boolean;
|
|
141
|
+
/** Opens host-owned storage setup when the user chooses an upload action. */
|
|
114
142
|
onAttachmentRequest?: () => void;
|
|
143
|
+
/** Hide the Add context tooltip while the host storage popover is open. */
|
|
144
|
+
contextButtonTooltipDisabled?: boolean;
|
|
145
|
+
/** Host-owned file acceptance and staging; the shared composer still owns picker and chips. */
|
|
115
146
|
attachmentAdapter?: AttachmentAdapter;
|
|
147
|
+
/** Let hosts extract ordinary uploaded text without also inlining it. */
|
|
116
148
|
inlineTextAttachments?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Controls the shared "+" affordance. Defaults to upload-only for standalone
|
|
151
|
+
* prompt forms; chat surfaces can opt into the full sidebar menu.
|
|
152
|
+
*/
|
|
117
153
|
plusMenuMode?: "full" | "upload-only" | "terminal" | "hidden";
|
|
154
|
+
/** Controls the terminal-specific plus menu when `plusMenuMode` is terminal. */
|
|
118
155
|
terminalModeControl?: ComposerTerminalModeControl;
|
|
156
|
+
/**
|
|
157
|
+
* Include extension creation in the full "+" menu. Defaults to false.
|
|
158
|
+
*/
|
|
119
159
|
extensionTools?: boolean;
|
|
160
|
+
/** Programmatically seed the composer with plain text. */
|
|
120
161
|
initialText?: string;
|
|
162
|
+
/** Stable key used to re-apply `initialText` when the host picks a preset. */
|
|
121
163
|
initialTextKey?: string | number;
|
|
164
|
+
/** Optional host-owned control rendered directly after the "+" button. */
|
|
122
165
|
modeControl?: ReactNode;
|
|
166
|
+
/** Current agent execution mode shown in the shared composer toolbar. */
|
|
123
167
|
execMode?: "build" | "plan";
|
|
168
|
+
/** Called when the user switches between acting and read-only planning. */
|
|
124
169
|
onExecModeChange?: (mode: "build" | "plan") => void;
|
|
170
|
+
/** Disable Plan mode while leaving Act mode available. */
|
|
171
|
+
planModeDisabled?: boolean;
|
|
172
|
+
/** Explanation shown next to the disabled Plan option. */
|
|
173
|
+
planModeDisabledReason?: string;
|
|
174
|
+
/** Explicit host-owned toolbar slot rendered directly after the "+" button. */
|
|
125
175
|
toolbarSlot?: ReactNode;
|
|
176
|
+
/** Custom attachment button to render instead of the default "+" affordance. */
|
|
126
177
|
attachButton?: ReactNode;
|
|
178
|
+
/** Custom action button to render instead of the default send button. */
|
|
127
179
|
actionButton?: ReactNode;
|
|
180
|
+
/** Extra button rendered alongside the default send button. */
|
|
128
181
|
extraActionButton?: ReactNode;
|
|
182
|
+
/** Optional stop control shown while the host runtime is active. */
|
|
183
|
+
stopButton?: ReactNode;
|
|
184
|
+
/** Shared sizing/layout variant for host surfaces. Default keeps sidebar behavior. */
|
|
129
185
|
layoutVariant?: AgentComposerLayoutVariant;
|
|
186
|
+
/** Additional slash commands surfaced in the shared / menu. */
|
|
130
187
|
slashCommands?: SlashCommand[];
|
|
188
|
+
/** Additional slash skills surfaced in the shared / menu. */
|
|
131
189
|
slashSkills?: SkillResult[];
|
|
190
|
+
/** Include built-in sidebar slash commands when onSlashCommand is provided. */
|
|
132
191
|
includeDefaultSlashCommands?: boolean;
|
|
192
|
+
/** Include app-discovered skills from the default agent endpoint. Default true. */
|
|
133
193
|
includeDefaultSlashSkills?: boolean;
|
|
194
|
+
/** Called when a slash command from the shared / menu is executed. */
|
|
134
195
|
onSlashCommand?: (command: string) => void;
|
|
196
|
+
/** External model list for hosts that already resolve models outside the app. */
|
|
135
197
|
availableModels?: EngineModelGroup[];
|
|
198
|
+
/** Whether the external model list is still being resolved. */
|
|
136
199
|
modelListLoading?: boolean;
|
|
137
200
|
selectedModel?: string;
|
|
138
201
|
selectedEngine?: string;
|
|
139
202
|
selectedEffort?: ReasoningEffort;
|
|
140
203
|
onModelChange?: (model: string, engine: string) => void;
|
|
141
204
|
onEffortChange?: (effort: ReasoningEffort) => void;
|
|
205
|
+
/** Local or hosted agent runtimes shown above the model list. */
|
|
142
206
|
availableAgents?: ComposerAgentOption[];
|
|
207
|
+
/** Selected agent runtime identifier. */
|
|
143
208
|
selectedAgent?: string;
|
|
209
|
+
/** Show only the selected agent in the model control. */
|
|
144
210
|
agentOnly?: boolean;
|
|
211
|
+
/** Callback when the user picks an agent runtime. */
|
|
145
212
|
onAgentChange?: (agent: string) => void;
|
|
213
|
+
/** Called when the shared model picker opens or closes. */
|
|
146
214
|
onModelSelectorOpenChange?: (open: boolean) => void;
|
|
215
|
+
/** Enable server-backed model/provider status checks. Defaults on, except for a selected local runtime. */
|
|
147
216
|
modelStatusChecksEnabled?: boolean;
|
|
217
|
+
/** Called whenever the plain editor text changes. */
|
|
148
218
|
onTextChange?: (text: string) => void;
|
|
219
|
+
/** Called whenever attached files change, before the composer is submitted. */
|
|
149
220
|
onAttachmentsChange?: (files: PromptComposerFile[]) => void;
|
|
221
|
+
/** Called whenever the composer resolves a model, engine, or effort choice. */
|
|
150
222
|
onModelSelectionChange?: (
|
|
151
223
|
selection: Pick<PromptComposerSubmitOptions, "model" | "engine" | "effort">,
|
|
152
224
|
) => void;
|
|
225
|
+
/**
|
|
226
|
+
* Override the Builder.io connect action in the model picker. When provided,
|
|
227
|
+
* clicking "Connect Builder.io" calls this instead of opening a browser popup.
|
|
228
|
+
* Used by the Electron desktop app to route through the native IPC handler.
|
|
229
|
+
*/
|
|
153
230
|
onConnectProvider?: () => void;
|
|
231
|
+
/** Called when a local runtime needs its native sign-in/setup flow. */
|
|
154
232
|
onConnectLocalRuntime?: (engine: string) => void;
|
|
233
|
+
imageModelMenu?: ComposerImageModelMenu;
|
|
234
|
+
/** Imperative handle for focusing the composer. */
|
|
155
235
|
composerRef?: Ref<TiptapComposerHandle>;
|
|
156
236
|
}
|
|
157
237
|
|
|
238
|
+
// Minimal pass-through adapter. PromptComposer always submits through
|
|
239
|
+
// onSubmitOverride, so the runtime never actually calls this — but
|
|
240
|
+
// `useLocalRuntime` needs *something* shaped like a ChatModelAdapter.
|
|
158
241
|
const NOOP_ADAPTER: ChatModelAdapter = {
|
|
159
242
|
async *run() {
|
|
160
243
|
yield* [];
|
|
161
244
|
},
|
|
162
245
|
};
|
|
163
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Local binary document adapter so reference PDFs, decks, and docs can be
|
|
249
|
+
* attached without dragging the whole assistant chat module into bundles that
|
|
250
|
+
* just want a prompt popover.
|
|
251
|
+
*/
|
|
164
252
|
class BinaryDocumentAttachmentAdapter implements AttachmentAdapter {
|
|
165
253
|
public accept = PROMPT_DOCUMENT_ATTACHMENT_ACCEPT;
|
|
166
254
|
|
|
@@ -223,12 +311,17 @@ function formatInlineTextFile(name: string, text: string): string {
|
|
|
223
311
|
.join("\n");
|
|
224
312
|
}
|
|
225
313
|
|
|
314
|
+
/** Chat stays closed until the provider check confirms it can run. */
|
|
226
315
|
export function shouldGateComposerForEngine(
|
|
227
316
|
state: ComposerAgentEngineState,
|
|
228
317
|
): boolean {
|
|
229
318
|
return state !== "configured";
|
|
230
319
|
}
|
|
231
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Show setup treatment only for a confirmed-missing engine with a setup
|
|
323
|
+
* component. Unresolved status uses the retry treatment instead.
|
|
324
|
+
*/
|
|
232
325
|
export function shouldGateComposerForMissingEngine(input: {
|
|
233
326
|
state: string;
|
|
234
327
|
hasSetupComponent: boolean;
|
|
@@ -286,6 +379,11 @@ export async function buildPromptComposerSubmission(options: {
|
|
|
286
379
|
// Keep the upload path fallback below.
|
|
287
380
|
}
|
|
288
381
|
}
|
|
382
|
+
// Note: images are NOT inlined into the prompt text even when small.
|
|
383
|
+
// Inlining a base64 data-URL into a text string consumes an enormous
|
|
384
|
+
// number of tokens (≈ 700 K per MB) and most hosts handle images via
|
|
385
|
+
// proper attachment channels. The `files` array below carries the image
|
|
386
|
+
// for the host to process through a dedicated attachment pipeline.
|
|
289
387
|
files.push(file);
|
|
290
388
|
}
|
|
291
389
|
}
|
|
@@ -507,6 +605,7 @@ function PromptComposerInner({
|
|
|
507
605
|
voiceEnabled = DEFAULT_VOICE_DICTATION_ENABLED,
|
|
508
606
|
attachmentsEnabled = true,
|
|
509
607
|
onAttachmentRequest,
|
|
608
|
+
contextButtonTooltipDisabled = false,
|
|
510
609
|
inlineTextAttachments = true,
|
|
511
610
|
plusMenuMode,
|
|
512
611
|
terminalModeControl,
|
|
@@ -516,10 +615,13 @@ function PromptComposerInner({
|
|
|
516
615
|
modeControl,
|
|
517
616
|
execMode,
|
|
518
617
|
onExecModeChange,
|
|
618
|
+
planModeDisabled,
|
|
619
|
+
planModeDisabledReason,
|
|
519
620
|
toolbarSlot,
|
|
520
621
|
attachButton,
|
|
521
622
|
actionButton,
|
|
522
623
|
extraActionButton,
|
|
624
|
+
stopButton,
|
|
523
625
|
layoutVariant,
|
|
524
626
|
slashCommands,
|
|
525
627
|
slashSkills,
|
|
@@ -544,7 +646,11 @@ function PromptComposerInner({
|
|
|
544
646
|
onModelSelectionChange,
|
|
545
647
|
onConnectProvider,
|
|
546
648
|
onConnectLocalRuntime,
|
|
649
|
+
imageModelMenu,
|
|
547
650
|
composerRef,
|
|
651
|
+
onBeforeSubmit,
|
|
652
|
+
onAttachmentError,
|
|
653
|
+
interceptBuildRequestsForBuilder,
|
|
548
654
|
}: PromptComposerProps) {
|
|
549
655
|
const adapters = useComposerRuntimeAdapters();
|
|
550
656
|
const t = adapters.translate!;
|
|
@@ -665,6 +771,12 @@ function PromptComposerInner({
|
|
|
665
771
|
attachments?: ReadonlyArray<unknown>,
|
|
666
772
|
submitOptions?: TiptapComposerSubmitOptions,
|
|
667
773
|
) => {
|
|
774
|
+
// PromptComposer hosts (NewWorkspaceAppFlow, create-extension, create-deck,
|
|
775
|
+
// …) submit a single string prompt — they don't run the assistant-ui
|
|
776
|
+
// attachment send pipeline. TiptapComposer auto-converts large pastes
|
|
777
|
+
// into a "Pasted text" chip, which would otherwise disappear into an
|
|
778
|
+
// unprocessed File. Inline the chip body back into the prompt text so
|
|
779
|
+
// newlines and full content survive the round-trip.
|
|
668
780
|
const { text: finalText, files } = await buildPromptComposerSubmission({
|
|
669
781
|
text,
|
|
670
782
|
attachments,
|
|
@@ -759,6 +871,7 @@ function PromptComposerInner({
|
|
|
759
871
|
onRetryContextItem={onRetryContextItem}
|
|
760
872
|
attachmentsEnabled={attachmentsEnabled}
|
|
761
873
|
onAttachmentRequest={onAttachmentRequest}
|
|
874
|
+
contextButtonTooltipDisabled={contextButtonTooltipDisabled}
|
|
762
875
|
ariaLabel={ariaLabel}
|
|
763
876
|
focusRef={handleRef}
|
|
764
877
|
disabled={disabled || gateComposer}
|
|
@@ -779,6 +892,9 @@ function PromptComposerInner({
|
|
|
779
892
|
initialText={initialText}
|
|
780
893
|
initialTextKey={initialTextKey}
|
|
781
894
|
onSubmit={handleSubmit}
|
|
895
|
+
onBeforeSubmit={onBeforeSubmit}
|
|
896
|
+
onAttachmentError={onAttachmentError}
|
|
897
|
+
interceptBuildRequestsForBuilder={interceptBuildRequestsForBuilder}
|
|
782
898
|
clearOnSubmit={!preserveDraftOnSubmit}
|
|
783
899
|
plusMenuMode={
|
|
784
900
|
gateComposer
|
|
@@ -798,9 +914,12 @@ function PromptComposerInner({
|
|
|
798
914
|
modeControl={modeControl}
|
|
799
915
|
execMode={execMode}
|
|
800
916
|
onExecModeChange={onExecModeChange}
|
|
917
|
+
planModeDisabled={planModeDisabled}
|
|
918
|
+
planModeDisabledReason={planModeDisabledReason}
|
|
801
919
|
toolbarSlot={toolbarSlot}
|
|
802
920
|
actionButton={actionButton}
|
|
803
921
|
extraActionButton={extraActionButton}
|
|
922
|
+
stopButton={stopButton}
|
|
804
923
|
layoutVariant={layoutVariant}
|
|
805
924
|
slashCommands={slashCommands}
|
|
806
925
|
slashSkills={slashSkills}
|
|
@@ -827,12 +946,24 @@ function PromptComposerInner({
|
|
|
827
946
|
providerConnectStatusEnabled={resolvedModelStatusChecksEnabled}
|
|
828
947
|
onConnectProvider={onConnectProvider}
|
|
829
948
|
onConnectLocalRuntime={onConnectLocalRuntime}
|
|
949
|
+
imageModelMenu={imageModelMenu}
|
|
830
950
|
/>
|
|
831
951
|
</AgentComposerFrame>
|
|
832
952
|
</>
|
|
833
953
|
);
|
|
834
954
|
}
|
|
835
955
|
|
|
956
|
+
/**
|
|
957
|
+
* Standalone composer that mirrors the agent sidebar's input experience —
|
|
958
|
+
* voice dictation, file upload, model selector, submit-on-Enter — for use in
|
|
959
|
+
* popovers and inline prompt forms (create tool, create deck, create dashboard,
|
|
960
|
+
* the Dispatch new-app flow, etc.).
|
|
961
|
+
*
|
|
962
|
+
* The host owns submission: when the user presses Enter or clicks submit,
|
|
963
|
+
* `onSubmit(text, files, references, options)` is called. PromptComposer runs
|
|
964
|
+
* its own minimal assistant-ui runtime so it can be dropped into any subtree
|
|
965
|
+
* without needing the outer chat to be mounted.
|
|
966
|
+
*/
|
|
836
967
|
function PromptComposerRuntime(props: PromptComposerProps) {
|
|
837
968
|
const StaleIndexBoundary =
|
|
838
969
|
useComposerRuntimeAdapters().agentChat!.StaleIndexBoundary!;
|