fix(ui): restore pasted text expand controls\n\nFixes #67

This commit is contained in:
Shantur Rathore
2026-01-15 08:36:56 +00:00
parent ae322c53cc
commit 7749225f71

View File

@@ -1,11 +1,12 @@
import { Show, For, createMemo, createEffect, type Component } from "solid-js"
import { Expand } from "lucide-solid"
import type { Session } from "../../types/session"
import type { Attachment } from "../../types/attachment"
import type { ClientPart } from "../../types/message"
import MessageSection from "../message-section"
import { messageStoreBus } from "../../stores/message-v2/bus"
import PromptInput from "../prompt-input"
import AttachmentChip from "../attachment-chip"
import type { Attachment as PromptAttachment } from "../../types/attachment"
import { getAttachments, removeAttachment } from "../../stores/attachments"
import { instances } from "../../stores/instances"
import { loadMessages, sendMessage, forkSession, isSessionMessagesLoading, setActiveParentSession, setActiveSession, runShellCommand, abortSession } from "../../stores/sessions"
@@ -49,6 +50,54 @@ export const SessionView: Component<SessionViewProps> = (props) => {
})
const attachments = createMemo(() => getAttachments(props.instanceId, props.sessionId))
function handleExpandTextAttachment(attachment: PromptAttachment) {
if (attachment.source.type !== "text") return
const textarea = rootRef?.querySelector(".prompt-input") as HTMLTextAreaElement | null
const value = attachment.source.value
const match = attachment.display.match(/pasted #(\d+)/)
const placeholder = match ? `[pasted #${match[1]}]` : null
const currentText = textarea?.value ?? ""
let nextText = currentText
let selectionTarget: number | null = null
if (placeholder) {
const placeholderIndex = currentText.indexOf(placeholder)
if (placeholderIndex !== -1) {
nextText =
currentText.substring(0, placeholderIndex) +
value +
currentText.substring(placeholderIndex + placeholder.length)
selectionTarget = placeholderIndex + value.length
}
}
if (nextText === currentText) {
if (textarea) {
const start = textarea.selectionStart
const end = textarea.selectionEnd
nextText = currentText.substring(0, start) + value + currentText.substring(end)
selectionTarget = start + value.length
} else {
nextText = currentText + value
}
}
if (textarea) {
textarea.value = nextText
textarea.dispatchEvent(new Event("input", { bubbles: true }))
textarea.focus()
if (selectionTarget !== null) {
textarea.setSelectionRange(selectionTarget, selectionTarget)
}
}
removeAttachment(props.instanceId, props.sessionId, attachment.id)
}
let scrollToBottomHandle: (() => void) | undefined
let rootRef: HTMLDivElement | undefined
function scheduleScrollToBottom() {
@@ -235,14 +284,35 @@ export const SessionView: Component<SessionViewProps> = (props) => {
<Show when={attachments().length > 0}>
<div class="flex flex-wrap gap-1.5 border-t px-3 py-2" style="border-color: var(--border-base);">
<div class="flex flex-wrap items-center gap-1.5 border-t px-3 py-2" style="border-color: var(--border-base);">
<For each={attachments()}>
{(attachment) => (
<AttachmentChip
attachment={attachment}
onRemove={() => removeAttachment(props.instanceId, props.sessionId, attachment.id)}
/>
)}
{(attachment) => {
const isText = attachment.source.type === "text"
return (
<div class="attachment-chip" title={attachment.source.type === "file" ? attachment.source.path : undefined}>
<span class="font-mono">{attachment.display}</span>
<Show when={isText}>
<button
type="button"
class="attachment-expand"
onClick={() => handleExpandTextAttachment(attachment)}
aria-label="Expand pasted text"
title="Insert pasted text"
>
<Expand class="h-3 w-3" aria-hidden="true" />
</button>
</Show>
<button
type="button"
class="attachment-remove"
onClick={() => removeAttachment(props.instanceId, props.sessionId, attachment.id)}
aria-label="Remove attachment"
>
×
</button>
</div>
)
}}
</For>
</div>
</Show>