mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
* feat(native-chat): a queued message's Save can replace its images (host, wire and capability) * feat(native-chat): edit a queued message's images on desktop: remove, attach, paste or drop * refactor(native-chat): share the queued edit's image rules and drop the rarely-shown Save reasons * fix(native-chat): a queued edit's refusal says the edit wasn't saved, and its wire takes a full image set An expired upload refused on Save read "The command didn't run": the queued message update fell through to the command write kind. It now has its own kind whose sentence is "Your edit wasn't saved." The images param caps at MAX_BLOCKS, matching the shared edit rule that counts them with the text. * fix(native-chat): a queued edit keeps image markers right and owns drops and pastes on its card - Removing a kept image drops its [Image #k] marker from the edit's text and renumbers the later ones (shared rule, reused by the phone), so the agent never reads a marker that names the wrong image. - A file dragged from Orca's own tree onto the editing card goes to the edit, like a Finder/Explorer drop; the chat box overlay no longer covers the card. - Cmd/Ctrl+V with focus on the card's buttons goes to the edit, not the chat box. - A "changed" answer says "Your edit is in the chat box" only when something went there; otherwise it says the edit wasn't saved. - The empty hint counts an uploading image as content and is not shown for agents without image input; the list grows only while thumbnails show. - Every hand-off releases the edit's preview URLs. * fix(native-chat): an in-app drop on the editing card goes through the chat box's owner check The edit no longer runs its own copy of the in-app drop check, which resolved the target host through a path that can read the Active Server setting. The chat box's claim checks the drop and routes it by the chat's owner as before, then hands the files to the editing card's sink when the drop landed on it. Also: the kept-image id list is readonly where it is read, and the marker rule looks up the remaining images in a Set. * fix(native-chat): only an edit that takes images keeps its card's pastes, and no drop joins a Save in flight The pane paste bridge no longer skips every paste inside a queued editor: the card's own paste handler already claims image and file pastes, which the bridge respects. A menu paste is left to the card only when the edit takes images (the card is a drop owner), so an older host or an agent without image input behaves as before, and text pasted with focus on the card's buttons goes to the chat box. An in-app drop handed to the card while its Save is in flight adds nothing. * fix(native-chat): a failed capability re-check keeps the host's last answer, and an edit takes no files before its lease A re-check after regained contact that failed wrote "unknown" over a known answer for the same host, so features the host had reported (such as editing a queued message's images) vanished on a connection blip, mid-edit. A failed check now keeps the last answer; a different host still starts at unknown. Paste, Attach and OS or in-app drops into a queued edit are now blocked while it acquires its lease, as typing already is. * fix(native-chat): an image change is never saved as text alone when the host stops reading as taking images Reverts the shared capability hook to keep main's behavior: a failed re-check reads as unknown. Instead the queued edit derives from live state that an edit whose image set changed can't be saved while the host doesn't read as taking images, so a connection blip never saves renumbered markers over the old images or drops added ones. Save comes back once the host answers again. * fix(native-chat): keep queued image markers and drop feedback in sync * fix(native-chat): derive queued image markers without changing edit text * test(native-chat): the pane drop test's intake append returns the paths it didn't take * fix(native-chat): route menu paste to the focused queued edit * fix(native-chat): honor queued edit paste ownership before focus redirection * fix(native-chat): preserve native paste without clipboard access * fix(native-chat): keep paste in the owning queued edit
47 lines
1.6 KiB
TypeScript
47 lines
1.6 KiB
TypeScript
// A composer image chip's text form. It names which attached image it means, never a number: the
|
|
// number is drawn from the thumbnail row on screen and worked out once at send.
|
|
|
|
const IMAGE_REFERENCES = /\[Image\]\(orca-image:([A-Za-z0-9._:-]{1,200})\)/g
|
|
|
|
export function imageReferenceToken(attachmentId: string): string {
|
|
return `[Image](orca-image:${attachmentId})`
|
|
}
|
|
|
|
/** Spaces keep inserted chips separate from the words on either side of the caret. */
|
|
export function imageReferenceInsertion(
|
|
before: string,
|
|
after: string,
|
|
ids: readonly string[]
|
|
): string {
|
|
if (ids.length === 0) {
|
|
return ''
|
|
}
|
|
const tokens = ids.map(imageReferenceToken).join(' ')
|
|
return `${before && !/\s$/.test(before) ? ' ' : ''}${tokens}${/^\s/.test(after) ? '' : ' '}`
|
|
}
|
|
|
|
/** The text's runs and the images its tokens name, in order; empty runs are left out. */
|
|
export function splitImageReferences(text: string): (string | { attachmentId: string })[] {
|
|
const parts: (string | { attachmentId: string })[] = []
|
|
let last = 0
|
|
for (const match of text.matchAll(IMAGE_REFERENCES)) {
|
|
if (match.index > last) {
|
|
parts.push(text.slice(last, match.index))
|
|
}
|
|
parts.push({ attachmentId: match[1] ?? '' })
|
|
last = match.index + match[0].length
|
|
}
|
|
if (last < text.length) {
|
|
parts.push(text.slice(last))
|
|
}
|
|
return parts
|
|
}
|
|
|
|
/** Replaces every token with what `replace` gives for the image it names. */
|
|
export function replaceImageReferences(
|
|
text: string,
|
|
replace: (attachmentId: string) => string
|
|
): string {
|
|
return text.replace(IMAGE_REFERENCES, (_token, attachmentId: string) => replace(attachmentId))
|
|
}
|