diff --git a/packages/ui/src/components/session/session-view.tsx b/packages/ui/src/components/session/session-view.tsx index 1efcda6b..7de204b3 100644 --- a/packages/ui/src/components/session/session-view.tsx +++ b/packages/ui/src/components/session/session-view.tsx @@ -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 = (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 = (props) => { 0}> -
+
- {(attachment) => ( - removeAttachment(props.instanceId, props.sessionId, attachment.id)} - /> - )} + {(attachment) => { + const isText = attachment.source.type === "text" + return ( +
+ {attachment.display} + + + + +
+ ) + }}