fix(desktop): stage dropped files into the remote session workspace
Finder/OS drops became `@file:/Users/...` refs that only resolve when the gateway shares the local disk, so on a remote gateway non-image files (PDF/CSV/Markdown/...) never reached the agent. Route OS drops through the file.attach / image.attach_bytes upload pipeline — in-app project-tree and gutter drags stay inline workspace-relative refs — across every drop surface: the conversation area, the composer form, the contenteditable input, and the message-edit composer (which still reproduced the bug). Also: - upload dropped files eagerly when a session exists, so the card shows a spinner instead of stalling the send (images stay submit-time to avoid racing their thumbnail write); - round the attachment card and drop the monospace detail; - render image previews from the bytes we already hold, so a pasted/dropped screenshot shows its thumbnail and previews even when its only on-disk copy is a transient path (the data URL is not persisted to localStorage). Supersedes #38615, #41203. Co-authored-by: LeonSGP <154585401+LeonSGP43@users.noreply.github.com> Co-authored-by: Teknium <127238744+teknium1@users.noreply.github.com>
This commit is contained in:
co-authored by
LeonSGP
Teknium
parent
02f878ec5a
commit
4906dcfc25
@@ -11,6 +11,10 @@ export interface ComposerAttachment {
|
||||
previewUrl?: string
|
||||
path?: string
|
||||
attachedSessionId?: string
|
||||
/** Set while the file/image bytes are being staged into the session
|
||||
* workspace (remote upload or local stage), and 'error' if that failed.
|
||||
* Drives the spinner / error state on the composer attachment card. */
|
||||
uploadState?: 'uploading' | 'error'
|
||||
}
|
||||
|
||||
export const $composerDraft = atom('')
|
||||
@@ -73,6 +77,21 @@ export function clearComposerAttachments() {
|
||||
$composerAttachments.set([])
|
||||
}
|
||||
|
||||
/** Update only the upload state of an existing attachment (no-op if it's gone,
|
||||
* e.g. the user removed it mid-upload). Pass `undefined` to clear it. */
|
||||
export function setComposerAttachmentUploadState(id: string, uploadState?: ComposerAttachment['uploadState']) {
|
||||
const current = $composerAttachments.get()
|
||||
const index = current.findIndex(attachment => attachment.id === id)
|
||||
|
||||
if (index < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const next = [...current]
|
||||
next[index] = { ...next[index]!, uploadState }
|
||||
$composerAttachments.set(next)
|
||||
}
|
||||
|
||||
const TERMINAL_REF_RE = /@terminal:(`[^`\n]+`|"[^"\n]+"|'[^'\n]+'|\S+)/g
|
||||
|
||||
function unquoteRefValue(raw: string) {
|
||||
|
||||
@@ -6,6 +6,11 @@ import { $activeSessionId, $selectedStoredSessionId } from './session'
|
||||
export interface PreviewTarget {
|
||||
binary?: boolean
|
||||
byteSize?: number
|
||||
/** Inline image bytes (a `data:` URL) when the renderer already holds them —
|
||||
* e.g. a pasted/dropped screenshot whose only on-disk copy is a transient
|
||||
* path the preview can't reliably re-read. Rendered directly and NOT
|
||||
* persisted to the session-preview registry (it would bloat localStorage). */
|
||||
dataUrl?: string
|
||||
kind: 'file' | 'url'
|
||||
label: string
|
||||
large?: boolean
|
||||
@@ -214,7 +219,11 @@ function persistSessionPreviewRegistry(registry: SessionPreviewRegistry) {
|
||||
}
|
||||
|
||||
try {
|
||||
window.localStorage.setItem(REGISTRY_STORAGE_KEY, JSON.stringify(pruneRegistry(registry)))
|
||||
// Drop the inline image bytes before persisting — a screenshot data URL is
|
||||
// megabytes and would blow the localStorage quota. On reload the record
|
||||
// falls back to reading its `path`/`url`.
|
||||
const lean = JSON.stringify(pruneRegistry(registry), (key, value) => (key === 'dataUrl' ? undefined : value))
|
||||
window.localStorage.setItem(REGISTRY_STORAGE_KEY, lean)
|
||||
} catch {
|
||||
// Session previews are a desktop convenience; storage failures are nonfatal.
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user