feat(tui): support pasting clipboard images (#5563)

* feat(tui): support pasting clipboard images

* fix(tui): keep image placeholders atomic

* fix(tui): reconcile duplicate image placeholders

* fix(tui): retain highlighted image placeholders

* fix(tui): preserve image placeholder layout

* fix(tui): keep image display state local

* fix(tui): reject images in commands
This commit is contained in:
chengyongru
2026-08-27 20:37:43 +08:00
committed by GitHub
parent b9e7c7f6fe
commit 4d204ba077
11 changed files with 1132 additions and 50 deletions
+14 -3
View File
@@ -53,12 +53,17 @@ interface FileDiff {
text?: string
}
interface MediaAttachment {
export interface MediaAttachment {
kind: "image" | "video" | "file"
url: string
name?: string
}
export interface OutboundMedia {
data_url: string
name?: string
}
export interface WorkspaceScopePayload {
project_path: string
project_name?: string
@@ -181,6 +186,7 @@ type OutboundEvent =
turn_id: string
webui: true
workspace_scope?: WorkspaceScopePayload
media?: OutboundMedia[]
cli_apps?: Array<{ name: string }>
mcp_presets?: Array<{ name: string }>
session_mentions?: SessionMention[]
@@ -225,6 +231,7 @@ export interface HistoryMessage {
role: "user" | "assistant" | "activity"
content: string
turnId?: string
media?: MediaAttachment[]
toolEvents?: ToolProgressEvent[]
fileEdits?: FileEditEvent[]
forkIndex?: number
@@ -288,6 +295,7 @@ export interface SkillCandidate {
}
export interface MessageOptions {
media?: OutboundMedia[]
cliApps?: Array<{ name: string }>
mcpPresets?: Array<{ name: string }>
sessionMentions?: SessionMention[]
@@ -623,18 +631,20 @@ export async function fetchHistory(
(role !== "user" && role !== "assistant")
|| message.kind === "reasoning"
|| typeof content !== "string"
|| !content.trim()
) {
continue
}
const media = Array.isArray(message.media) ? message.media.filter(isMediaAttachment) : []
if (role === "user") {
if (!content.trim() && !media.length) continue
userIndex += 1
messages.push({
role: "user",
content,
...(media.length ? { media } : {}),
...(typeof message.turnId === "string" ? { turnId: message.turnId } : {}),
})
} else {
} else if (content.trim()) {
messages.push({ role: "assistant", content, forkIndex: userIndex })
}
}
@@ -1190,6 +1200,7 @@ export class NanobotClient {
webui: true,
...(this.workspaceScope ? { workspace_scope: this.workspaceScope } : {}),
...(options.userShell ? { user_shell: true } : {}),
...(options.media?.length ? { media: options.media } : {}),
...(options.cliApps?.length ? { cli_apps: options.cliApps } : {}),
...(options.mcpPresets?.length ? { mcp_presets: options.mcpPresets } : {}),
...(options.sessionMentions?.length