Attach files from the chat composer via the file picker
This commit is contained in:
1 parent
ae13111bff
commit
86bb8f36cb
6 files changed
+40
-17
No files matched your search
@@ -188,9 +188,9 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
||||
|
||||
ipcMain.handle(IPC_CHANNELS.importFile, async (_event, input) => {
|
||||
try {
|
||||
await files.importFile(input);
|
||||
deps.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "snapshot-changed" });
|
||||
return ok(snapshot.load());
|
||||
const fileId = await files.importFile(input);
|
||||
if (fileId) deps.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "snapshot-changed" });
|
||||
return ok(fileId);
|
||||
} catch (error) {
|
||||
return fail("file_import_failed", error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
|
||||
@@ -224,6 +224,13 @@ export function ChatPage({ snapshot, actions }: PageProps) {
|
||||
actorId: employee.id,
|
||||
}));
|
||||
|
||||
const attachFile = () => {
|
||||
void window.agentCompany.importFile({ orgId }).then((result) => {
|
||||
if (!result.ok) throw new Error(result.error.message);
|
||||
if (result.data) setAttachmentFileId(result.data);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="split-main">
|
||||
<ChatSidebar
|
||||
@@ -255,6 +262,7 @@ export function ChatPage({ snapshot, actions }: PageProps) {
|
||||
attachmentFileId={attachmentFileId}
|
||||
currentEmployeeId={employee.id}
|
||||
onAttachmentChange={setAttachmentFileId}
|
||||
onAttach={attachFile}
|
||||
onOpenThread={setOpenThreadId}
|
||||
onApprove={(approvalId) => decideApproval(approvalId, "approved")}
|
||||
onDecline={(approvalId) => decideApproval(approvalId, "declined")}
|
||||
|
||||
@@ -35,6 +35,12 @@ export function FilesPage({ snapshot, actions }: PageProps) {
|
||||
const pinnedFiles = files.filter((file) => file.pinned);
|
||||
const totalBytes = visibleFiles.reduce((sum, file) => sum + file.sizeBytes, 0);
|
||||
|
||||
const upload = () => {
|
||||
void window.agentCompany.importFile({ orgId }).then((result) => {
|
||||
if (!result.ok) throw new Error(result.error.message);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: "grid", gridTemplateColumns: "minmax(160px, 200px) minmax(0, 1fr)", gap: 24, height: "100%", minHeight: 0, padding: "28px 32px 0" }}>
|
||||
<FilesTree
|
||||
@@ -57,7 +63,7 @@ export function FilesPage({ snapshot, actions }: PageProps) {
|
||||
{ id: "list", label: "List" },
|
||||
]}
|
||||
/>
|
||||
<Button tone="primary" onClick={() => void actions.run(() => window.agentCompany.importFile({ orgId }))}>
|
||||
<Button tone="primary" onClick={upload}>
|
||||
<Icon name="upload" size={13} /> Upload
|
||||
</Button>
|
||||
</TopbarActions>
|
||||
|
||||
@@ -8,6 +8,7 @@ export function ChatComposer({
|
||||
files,
|
||||
attachmentFileId,
|
||||
onAttachmentChange,
|
||||
onAttach,
|
||||
onSubmit,
|
||||
composerRef,
|
||||
}: {
|
||||
@@ -15,10 +16,12 @@ export function ChatComposer({
|
||||
files: FileObjectRecord[];
|
||||
attachmentFileId: string;
|
||||
onAttachmentChange: (next: string) => void;
|
||||
onAttach: () => void;
|
||||
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
|
||||
composerRef: MutableRefObject<HTMLTextAreaElement | null>;
|
||||
}) {
|
||||
const [slashOpen, setSlashOpen] = useState(false);
|
||||
const attachment = files.find((file) => file.id === attachmentFileId) ?? null;
|
||||
|
||||
const insert = (text: string) => {
|
||||
const node = composerRef.current;
|
||||
@@ -61,19 +64,22 @@ export function ChatComposer({
|
||||
}}
|
||||
/>
|
||||
<div className="row" style={{ paddingTop: 6, borderTop: "1px solid var(--border-soft)", gap: 4 }}>
|
||||
<IconButton icon="attach" label="Attach" />
|
||||
<select
|
||||
className="input"
|
||||
value={attachmentFileId}
|
||||
onChange={(event) => onAttachmentChange(event.target.value)}
|
||||
aria-label="File attachment"
|
||||
style={{ maxWidth: 180, padding: "4px 8px", height: 28, fontSize: 12 }}
|
||||
disabled={disabled}
|
||||
>
|
||||
<option value="">No attachment</option>
|
||||
{files.map((file) => <option key={file.id} value={file.id}>{file.name}</option>)}
|
||||
</select>
|
||||
<IconButton icon="attach" label="Attach file" onClick={onAttach} disabled={disabled} />
|
||||
<IconButton icon="sparkle" label="Commands" onClick={() => setSlashOpen((open) => !open)} />
|
||||
{attachment && (
|
||||
<span className="pill" style={{ maxWidth: 220 }}>
|
||||
<Icon name="file" size={11} />
|
||||
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{attachment.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Remove attachment"
|
||||
onClick={() => onAttachmentChange("")}
|
||||
style={{ display: "inline-flex", border: 0, background: "transparent", padding: 0, color: "var(--text-3)", cursor: "pointer" }}
|
||||
>
|
||||
<Icon name="x" size={11} />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
<div style={{ marginLeft: "auto", display: "flex", alignItems: "center", gap: 6 }}>
|
||||
<span className="mono" style={{ fontSize: 11, color: "var(--text-3)" }}>⌘↵</span>
|
||||
<Button tone="primary" size="small" type="submit" disabled={disabled}><Icon name="send" size={12} /> Send</Button>
|
||||
|
||||
@@ -29,6 +29,7 @@ export function ChatConversation({
|
||||
attachmentFileId,
|
||||
currentEmployeeId,
|
||||
onAttachmentChange,
|
||||
onAttach,
|
||||
onOpenThread,
|
||||
onApprove,
|
||||
onDecline,
|
||||
@@ -51,6 +52,7 @@ export function ChatConversation({
|
||||
attachmentFileId: string;
|
||||
currentEmployeeId: string;
|
||||
onAttachmentChange: (next: string) => void;
|
||||
onAttach: () => void;
|
||||
onOpenThread: (threadId: string) => void;
|
||||
onApprove: (approvalId: string) => void;
|
||||
onDecline: (approvalId: string) => void;
|
||||
@@ -93,6 +95,7 @@ export function ChatConversation({
|
||||
files={files}
|
||||
attachmentFileId={attachmentFileId}
|
||||
onAttachmentChange={onAttachmentChange}
|
||||
onAttach={onAttach}
|
||||
onSubmit={onSubmit}
|
||||
composerRef={composerRef}
|
||||
/>
|
||||
|
||||
@@ -96,7 +96,7 @@ export interface DesktopApi {
|
||||
launchSocialLogin(input: LaunchSocialLoginInput): Promise<DesktopResult<null>>;
|
||||
createScheduledPost(input: CreateScheduledPostInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
setScheduledPostState(input: SetScheduledPostStateInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
importFile(input: ImportFileInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
importFile(input: ImportFileInput): Promise<DesktopResult<string | null>>;
|
||||
pickDirectory(): Promise<DesktopResult<string | null>>;
|
||||
setFilePinned(input: SetFilePinnedInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
createMigrationReport(input: CreateMigrationReportInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
|
||||
Reference in new issue
Block a user