From 04db4fcf94a8e1cdead96f28ef15fa2138483132 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Sat, 15 Nov 2025 21:51:01 +0000 Subject: [PATCH] persist pasted text in history and align sdk command types --- src/App.tsx | 4 ++-- src/components/prompt-input.tsx | 5 ++++- src/lib/prompt-placeholders.ts | 36 ++++++++++++++++++++++++++++++++ src/stores/commands.ts | 4 ++-- src/stores/session-actions.ts | 37 ++------------------------------- 5 files changed, 46 insertions(+), 40 deletions(-) create mode 100644 src/lib/prompt-placeholders.ts diff --git a/src/App.tsx b/src/App.tsx index e8707d15..4243d8db 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1306,7 +1306,7 @@ function commandRequiresArguments(template?: string) { return /\$(?:\d+|ARGUMENTS)/.test(template) } -function promptForCommandArguments(command: SDKCommand.Info) { +function promptForCommandArguments(command: SDKCommand) { if (!commandRequiresArguments(command.template)) { return "" } @@ -1322,7 +1322,7 @@ function formatCommandLabel(name: string) { return name.charAt(0).toUpperCase() + name.slice(1) } -function buildCustomCommandEntries(instanceId: string, commands: SDKCommand.Info[]): Command[] { +function buildCustomCommandEntries(instanceId: string, commands: SDKCommand[]): Command[] { return commands.map((cmd) => ({ id: `custom:${instanceId}:${cmd.name}`, label: formatCommandLabel(cmd.name), diff --git a/src/components/prompt-input.tsx b/src/components/prompt-input.tsx index b3c7dddc..24c585b6 100644 --- a/src/components/prompt-input.tsx +++ b/src/components/prompt-input.tsx @@ -2,6 +2,7 @@ import { createSignal, Show, onMount, For, onCleanup, createEffect, on, untrack import UnifiedPicker from "./unified-picker" import { addToHistory, getHistory } from "../stores/message-history" import { getAttachments, addAttachment, clearAttachments, removeAttachment } from "../stores/attachments" +import { resolvePastedPlaceholders } from "../lib/prompt-placeholders" import { createFileAttachment, createTextAttachment, createAgentAttachment } from "../types/attachment" import type { Attachment } from "../types/attachment" import type { Agent } from "../types/session" @@ -465,6 +466,8 @@ export default function PromptInput(props: PromptInputProps) { const currentAttachments = attachments() if (!text || props.disabled) return + const resolvedPrompt = resolvePastedPlaceholders(text, currentAttachments) + clearPrompt() clearAttachments(props.instanceId, props.sessionId) setIgnoredAtPositions(new Set()) @@ -473,7 +476,7 @@ export default function PromptInput(props: PromptInputProps) { setHistoryDraft(null) try { - await addToHistory(props.instanceFolder, text) + await addToHistory(props.instanceFolder, resolvedPrompt) const updated = await getHistory(props.instanceFolder) setHistory(updated) setHistoryIndex(-1) diff --git a/src/lib/prompt-placeholders.ts b/src/lib/prompt-placeholders.ts new file mode 100644 index 00000000..1d8ee1eb --- /dev/null +++ b/src/lib/prompt-placeholders.ts @@ -0,0 +1,36 @@ +import type { Attachment } from "../types/attachment" + +export function resolvePastedPlaceholders(prompt: string, attachments: Attachment[] = []): string { + if (!prompt || !prompt.includes("[pasted #")) { + return prompt + } + + if (!attachments || attachments.length === 0) { + return prompt + } + + const lookup = new Map() + + for (const attachment of attachments) { + const source = attachment?.source + if (!source || source.type !== "text") continue + const display = attachment?.display + const value = source.value + if (typeof display !== "string" || typeof value !== "string") continue + const match = display.match(/pasted #(\d+)/) + if (!match) continue + const placeholder = `[pasted #${match[1]}]` + if (!lookup.has(placeholder)) { + lookup.set(placeholder, value) + } + } + + if (lookup.size === 0) { + return prompt + } + + return prompt.replace(/\[pasted #(\d+)\]/g, (fullMatch) => { + const replacement = lookup.get(fullMatch) + return typeof replacement === "string" ? replacement : fullMatch + }) +} diff --git a/src/stores/commands.ts b/src/stores/commands.ts index c1b75ac5..a975990d 100644 --- a/src/stores/commands.ts +++ b/src/stores/commands.ts @@ -2,7 +2,7 @@ import { createSignal } from "solid-js" import type { Command as SDKCommand } from "@opencode-ai/sdk" import type { OpencodeClient } from "@opencode-ai/sdk/client" -const [commandMap, setCommandMap] = createSignal>(new Map()) +const [commandMap, setCommandMap] = createSignal>(new Map()) export async function fetchCommands(instanceId: string, client: OpencodeClient): Promise { const response = await client.command.list() @@ -14,7 +14,7 @@ export async function fetchCommands(instanceId: string, client: OpencodeClient): }) } -export function getCommands(instanceId: string): SDKCommand.Info[] { +export function getCommands(instanceId: string): SDKCommand[] { return commandMap().get(instanceId) ?? [] } diff --git a/src/stores/session-actions.ts b/src/stores/session-actions.ts index f6abbc30..c227fcb9 100644 --- a/src/stores/session-actions.ts +++ b/src/stores/session-actions.ts @@ -1,6 +1,8 @@ import type { Message } from "../types/message" +import { resolvePastedPlaceholders } from "../lib/prompt-placeholders" import { instances } from "./instances" + import { addRecentModelPreference, preferences, @@ -62,41 +64,6 @@ function createId(prefix: string): string { return `${prefix}_${hex}${random}` } -function resolvePastedPlaceholders(prompt: string, attachments: any[] = []): string { - if (!prompt || !prompt.includes("[pasted #")) { - return prompt - } - - if (!attachments || attachments.length === 0) { - return prompt - } - - const lookup = new Map() - - for (const attachment of attachments) { - const source = attachment?.source - if (!source || source.type !== "text") continue - const display: string | undefined = attachment?.display - const value: unknown = source.value - if (typeof display !== "string" || typeof value !== "string") continue - const match = display.match(/pasted #(\d+)/) - if (!match) continue - const placeholder = `[pasted #${match[1]}]` - if (!lookup.has(placeholder)) { - lookup.set(placeholder, value) - } - } - - if (lookup.size === 0) { - return prompt - } - - return prompt.replace(/\[pasted #(\d+)\]/g, (fullMatch) => { - const replacement = lookup.get(fullMatch) - return typeof replacement === "string" ? replacement : fullMatch - }) -} - async function sendMessage( instanceId: string, sessionId: string,