@agent-native/toolkit 0.13.1 → 0.13.3

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.
@@ -84,6 +84,10 @@ export interface PromptComposerProps {
84
84
  ) => void;
85
85
  placeholder?: string;
86
86
  disabled?: boolean;
87
+ /** Override the generic document attachment cap for a multipart host. */
88
+ maxDocumentAttachmentBytes?: number;
89
+ /** Label used in the visible document attachment limit error. */
90
+ documentAttachmentLimitLabel?: string;
87
91
  autoFocus?: boolean;
88
92
  className?: string;
89
93
  style?: CSSProperties;
@@ -458,6 +462,8 @@ function PromptComposerInner({
458
462
  onSubmit,
459
463
  placeholder,
460
464
  disabled,
465
+ maxDocumentAttachmentBytes,
466
+ documentAttachmentLimitLabel,
461
467
  autoFocus,
462
468
  className,
463
469
  style,
@@ -629,6 +635,8 @@ function PromptComposerInner({
629
635
  <TiptapComposer
630
636
  focusRef={handleRef}
631
637
  disabled={disabled || gateComposer}
638
+ maxDocumentAttachmentBytes={maxDocumentAttachmentBytes}
639
+ documentAttachmentLimitLabel={documentAttachmentLimitLabel}
632
640
  placeholder={
633
641
  gateComposer ? "Connect AI above to continue..." : placeholder
634
642
  }
@@ -11,6 +11,7 @@ import {
11
11
  composerModelCostTier,
12
12
  createTiptapComposerExtensions,
13
13
  displayableComposerModeMessage,
14
+ getComposerSendTooltipKey,
14
15
  getComposerSubmitIntentForEnterKey,
15
16
  getComposerPopoverPosition,
16
17
  getComposerReasoningEffortOptions,
@@ -137,6 +138,11 @@ describe("createTiptapComposerExtensions", () => {
137
138
  ).toBe("send");
138
139
  });
139
140
 
141
+ it("uses the queue tooltip when the submit will wait", () => {
142
+ expect(getComposerSendTooltipKey(true)).toBe("composer.queueMessage");
143
+ expect(getComposerSendTooltipKey(false)).toBe("composer.sendMessage");
144
+ });
145
+
140
146
  it("selects and removes context chips one Backspace at a time", () => {
141
147
  let contextItemKeys = ["dashboard", "panel"];
142
148
  let selectedKey: string | null = null;
@@ -209,7 +215,7 @@ describe("createTiptapComposerExtensions", () => {
209
215
  file,
210
216
  },
211
217
  ]),
212
- ).toContain('"large.pdf" is 4.0 MB PDFs are capped at 4 MB');
218
+ ).toContain('"large.pdf" is 4.0 MB. PDFs are capped at 4 MB');
213
219
  expect(
214
220
  getOversizedDocumentAttachmentError([
215
221
  {
@@ -222,6 +228,31 @@ describe("createTiptapComposerExtensions", () => {
222
228
  ).toBeNull();
223
229
  });
224
230
 
231
+ it("allows hosts to use a larger multipart document cap", () => {
232
+ const file = new File(
233
+ [new Uint8Array(4 * 1024 * 1024 + 1)],
234
+ "reference.pdf",
235
+ { type: "application/pdf" },
236
+ );
237
+
238
+ expect(
239
+ getOversizedDocumentAttachmentError(
240
+ [
241
+ {
242
+ type: "document",
243
+ name: "reference.pdf",
244
+ contentType: "application/pdf",
245
+ file,
246
+ },
247
+ ],
248
+ {
249
+ maxBytes: 50 * 1024 * 1024,
250
+ label: "Slides reference files",
251
+ },
252
+ ),
253
+ ).toBeNull();
254
+ });
255
+
225
256
  it("maps Enter keybindings to immediate and queued submit intents", () => {
226
257
  const enter = {
227
258
  key: "Enter",
@@ -123,6 +123,12 @@ export function resolveComposerPrimaryAction(options: {
123
123
  return !options.canSubmit && options.hasStopButton ? "stop" : "send";
124
124
  }
125
125
 
126
+ export function getComposerSendTooltipKey(
127
+ willQueue: boolean,
128
+ ): "composer.queueMessage" | "composer.sendMessage" {
129
+ return willQueue ? "composer.queueMessage" : "composer.sendMessage";
130
+ }
131
+
126
132
  export type ContextChipBackspaceAction =
127
133
  | { type: "select"; key: string }
128
134
  | { type: "remove"; key: string }
@@ -280,21 +286,27 @@ function isDocumentAttachment(value: Record<string, unknown>): boolean {
280
286
 
281
287
  export function getOversizedDocumentAttachmentError(
282
288
  attachments: ReadonlyArray<unknown>,
289
+ options: {
290
+ maxBytes?: number;
291
+ label?: string;
292
+ } = {},
283
293
  ): string | null {
294
+ const maxBytes = options.maxBytes ?? MAX_DOCUMENT_ATTACHMENT_BYTES;
295
+ const label = options.label ?? "PDFs";
284
296
  for (const attachment of attachments) {
285
297
  if (!attachment || typeof attachment !== "object") continue;
286
298
  const candidate = attachment as Record<string, unknown>;
287
299
  if (!isDocumentAttachment(candidate)) continue;
288
300
  const file = candidate.file;
289
301
  if (!(file instanceof File)) continue;
290
- if (file.size <= MAX_DOCUMENT_ATTACHMENT_BYTES) continue;
302
+ if (file.size <= maxBytes) continue;
291
303
  const name =
292
304
  typeof candidate.name === "string" && candidate.name.trim()
293
305
  ? candidate.name
294
306
  : file.name;
295
307
  const mb = (file.size / 1024 / 1024).toFixed(1);
296
- const maxMb = (MAX_DOCUMENT_ATTACHMENT_BYTES / 1024 / 1024).toFixed(0);
297
- return `"${name}" is ${mb} MB PDFs are capped at ${maxMb} MB to stay within message limits. Please reduce the file size or split it into smaller parts.`;
308
+ const maxMb = (maxBytes / 1024 / 1024).toFixed(0);
309
+ return `"${name}" is ${mb} MB. ${label} are capped at ${maxMb} MB to stay within message limits. Please reduce the file size or split it into smaller parts.`;
298
310
  }
299
311
  return null;
300
312
  }
@@ -549,6 +561,10 @@ type ExecMode = "build" | "plan";
549
561
  export interface TiptapComposerProps {
550
562
  placeholder?: string;
551
563
  disabled?: boolean;
564
+ /** Override the generic document attachment cap for a multipart host. */
565
+ maxDocumentAttachmentBytes?: number;
566
+ /** Label used in the visible document attachment limit error. */
567
+ documentAttachmentLimitLabel?: string;
552
568
  focusRef?: React.Ref<TiptapComposerHandle>;
553
569
  /** Programmatically seed the editor with plain text. */
554
570
  initialText?: string;
@@ -576,6 +592,8 @@ export interface TiptapComposerProps {
576
592
  onTextChange?: (text: string) => void;
577
593
  /** Custom action button (e.g. stop button) to render instead of the default send button. */
578
594
  actionButton?: React.ReactNode;
595
+ /** Whether the default send action will wait behind existing work. */
596
+ willQueue?: boolean;
579
597
  /** Extra button to render alongside the primary action. */
580
598
  extraActionButton?: React.ReactNode;
581
599
  /**
@@ -1494,6 +1512,8 @@ type PopoverState = {
1494
1512
  export function TiptapComposer({
1495
1513
  placeholder = "Message agent...",
1496
1514
  disabled = false,
1515
+ maxDocumentAttachmentBytes = MAX_DOCUMENT_ATTACHMENT_BYTES,
1516
+ documentAttachmentLimitLabel = "PDFs",
1497
1517
  focusRef,
1498
1518
  initialText,
1499
1519
  initialTextKey,
@@ -1502,6 +1522,7 @@ export function TiptapComposer({
1502
1522
  clearOnSubmit = true,
1503
1523
  onTextChange,
1504
1524
  actionButton,
1525
+ willQueue = false,
1505
1526
  extraActionButton,
1506
1527
  stopButton,
1507
1528
  attachButton,
@@ -1538,6 +1559,9 @@ export function TiptapComposer({
1538
1559
  }: TiptapComposerProps) {
1539
1560
  const adapters = useComposerRuntimeAdapters();
1540
1561
  const t = adapters.translate!;
1562
+ const sendButtonTooltip = t(getComposerSendTooltipKey(willQueue), {
1563
+ defaultValue: willQueue ? "Queue message" : "Send message",
1564
+ });
1541
1565
  const [popover, setPopover] = useState<PopoverState>(null);
1542
1566
  const popoverRef = useRef<MentionPopoverRef>(null);
1543
1567
  const composerRuntime = useComposerRuntime();
@@ -2498,8 +2522,13 @@ export function TiptapComposer({
2498
2522
  const attachments = composerRuntime.getState().attachments;
2499
2523
  if (!text.trim() && references.length === 0 && attachments.length === 0)
2500
2524
  return;
2501
- const oversizedDocumentError =
2502
- getOversizedDocumentAttachmentError(attachments);
2525
+ const oversizedDocumentError = getOversizedDocumentAttachmentError(
2526
+ attachments,
2527
+ {
2528
+ maxBytes: maxDocumentAttachmentBytes,
2529
+ label: documentAttachmentLimitLabel,
2530
+ },
2531
+ );
2503
2532
  if (oversizedDocumentError) {
2504
2533
  onAttachmentErrorRef.current?.(oversizedDocumentError);
2505
2534
  return;
@@ -2995,7 +3024,7 @@ export function TiptapComposer({
2995
3024
  <IconArrowUp className="h-3.5 w-3.5" />
2996
3025
  </button>
2997
3026
  </TooltipTrigger>
2998
- <TooltipContent>Send message</TooltipContent>
3027
+ <TooltipContent>{sendButtonTooltip}</TooltipContent>
2999
3028
  </Tooltip>
3000
3029
  )}
3001
3030
  </>