@dudousxd/nestjs-agent-react 0.17.0 → 0.18.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.
package/README.md CHANGED
@@ -662,15 +662,52 @@ merged. For a single already-atomic row, `storedMessageToUiMessage` maps it 1:1
662
662
  Persisted reasoning comes back as a `reasoning` part before the text (with its duration), and
663
663
  persisted pushed components as `data-ui` parts, so a reloaded thread shows what the live one did.
664
664
 
665
- ### Attachments and the raw client
665
+ ### Attachments
666
666
 
667
- `uploadAttachment` and the rest of `AgentClient` work outside `useAgentChat` too — e.g. from a
668
- file-picker or a standalone approvals-inbox screen:
667
+ `useAttachments` is the composer's file tray without the tray: validation, one upload per file
668
+ with progress and cancel, retry, image previews, and the handlers for a file input, a drop zone and
669
+ paste.
669
670
 
670
- ```ts
671
- const attachment = await chat.client.uploadAttachment(file); // → MessageAttachment
672
- await chat.sendMessage({ text: 'what is in this?' }, { body: { attachments: [attachment] } });
671
+ ```tsx
672
+ import { messageFiles, useAttachments } from '@dudousxd/nestjs-agent-react';
673
+
674
+ const files = useAttachments({
675
+ backend: chat.backend, // or `upload: (file, { signal, onProgress }) => myUpload(file)`
676
+ accept: 'image/*,.pdf',
677
+ maxBytes: 20 * 1024 * 1024,
678
+ maxFiles: 5,
679
+ });
673
680
 
681
+ <div {...files.dropZoneProps} data-dragging={files.isDragging}>
682
+ <textarea onPaste={files.onPaste} />
683
+ <input {...files.inputProps} hidden ref={pickerRef} />
684
+ {files.items.map((item) => (
685
+ <span key={item.id} data-status={item.status}>
686
+ {item.previewUrl ? <img src={item.previewUrl} alt="" /> : item.name}
687
+ {item.status === 'uploading' ? ` ${Math.round(item.progress * 100)}%` : null}
688
+ {item.error}
689
+ <button onClick={() => files.remove(item.id)}>Remove</button>
690
+ </span>
691
+ ))}
692
+ </div>
693
+
694
+ <button
695
+ disabled={files.isUploading}
696
+ onClick={async () => {
697
+ await chat.sendMessage({ text }, { body: { attachments: files.refs } });
698
+ files.clear();
699
+ }}
700
+ />
701
+ ```
702
+
703
+ An item is `uploading`, `ready`, `error` (retry with `files.retry(id)`) or `rejected` (failed
704
+ `accept`/`maxBytes`/`maxFiles` and never uploaded; `error` says why). `remove` cancels an upload in
705
+ flight. `messageFiles(message)` reads the files back off any message — live or replayed — with a
706
+ `kind` (`image`, `pdf`, `text`, …), the extension and, for replayed ones, the stored `mediaId`.
707
+
708
+ The rest of `AgentClient` works outside `useAgentChat` too — e.g. a standalone approvals inbox:
709
+
710
+ ```ts
674
711
  // Human-in-the-loop, callable from any component — not just the chat screen that raised it:
675
712
  await chat.approve({ toolCallId });
676
713
  await chat.reject({ toolCallId, reason: 'not now' });