fix(ui): render prompt attachments above input
This commit is contained in:
@@ -1080,101 +1080,8 @@ export default function PromptInput(props: PromptInputProps) {
|
|||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<div class="flex flex-1 flex-col">
|
<div class="flex flex-1 flex-col">
|
||||||
<Show when={attachments().length > 0}>
|
|
||||||
<div class="flex flex-wrap gap-1.5 border-b pb-2" style="border-color: var(--border-base);">
|
|
||||||
<For each={attachments()}>
|
|
||||||
{(attachment) => {
|
|
||||||
const isImage = attachment.mediaType.startsWith("image/")
|
|
||||||
const textValue = attachment.source.type === "text" ? attachment.source.value : undefined
|
|
||||||
const isTextAttachment = typeof textValue === "string"
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
class={`attachment-chip ${isImage ? "attachment-chip-image" : ""}`}
|
|
||||||
title={textValue}
|
|
||||||
>
|
|
||||||
<Show
|
|
||||||
when={isImage}
|
|
||||||
fallback={
|
|
||||||
<Show
|
|
||||||
when={isTextAttachment}
|
|
||||||
fallback={
|
|
||||||
<Show
|
|
||||||
when={attachment.source.type === "agent"}
|
|
||||||
fallback={
|
|
||||||
<svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</Show>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</Show>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<img src={attachment.url} alt={attachment.filename} class="h-5 w-5 rounded object-cover" />
|
|
||||||
</Show>
|
|
||||||
<span>{isTextAttachment ? attachment.display : attachment.filename}</span>
|
|
||||||
<Show when={isTextAttachment}>
|
|
||||||
<button
|
|
||||||
onClick={() => handleExpandTextAttachment(attachment)}
|
|
||||||
class="attachment-expand"
|
|
||||||
aria-label="Expand pasted text"
|
|
||||||
title="Insert pasted text"
|
|
||||||
>
|
|
||||||
<svg class="h-3 w-3" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M7 7h6v6H7z" />
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4 4h12v12" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</Show>
|
|
||||||
<button
|
|
||||||
onClick={() => handleRemoveAttachment(attachment.id)}
|
|
||||||
class="attachment-remove"
|
|
||||||
aria-label="Remove attachment"
|
|
||||||
>
|
|
||||||
<svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M6 18L18 6M6 6l12 12"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<Show when={isImage}>
|
|
||||||
<div class="attachment-chip-preview">
|
|
||||||
<img src={attachment.url} alt={attachment.filename} />
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
<div class={`prompt-input-field-container ${expandState() === "expanded" ? "is-expanded" : ""}`}>
|
<div class={`prompt-input-field-container ${expandState() === "expanded" ? "is-expanded" : ""}`}>
|
||||||
|
|
||||||
<div class={`prompt-input-field ${expandState() === "expanded" ? "is-expanded" : ""}`}>
|
<div class={`prompt-input-field ${expandState() === "expanded" ? "is-expanded" : ""}`}>
|
||||||
<textarea
|
<textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
@@ -1188,7 +1095,6 @@ export default function PromptInput(props: PromptInputProps) {
|
|||||||
onBlur={() => setIsFocused(false)}
|
onBlur={() => setIsFocused(false)}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
rows={expandState() === "expanded" ? 15 : 4}
|
rows={expandState() === "expanded" ? 15 : 4}
|
||||||
style={attachments().length > 0 ? { "padding-top": "8px" } : undefined}
|
|
||||||
spellcheck={false}
|
spellcheck={false}
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Show, createMemo, createEffect, type Component } from "solid-js"
|
import { Show, For, createMemo, createEffect, type Component } from "solid-js"
|
||||||
import type { Session } from "../../types/session"
|
import type { Session } from "../../types/session"
|
||||||
import type { Attachment } from "../../types/attachment"
|
import type { Attachment } from "../../types/attachment"
|
||||||
import type { ClientPart } from "../../types/message"
|
import type { ClientPart } from "../../types/message"
|
||||||
import MessageSection from "../message-section"
|
import MessageSection from "../message-section"
|
||||||
import { messageStoreBus } from "../../stores/message-v2/bus"
|
import { messageStoreBus } from "../../stores/message-v2/bus"
|
||||||
import PromptInput from "../prompt-input"
|
import PromptInput from "../prompt-input"
|
||||||
|
import AttachmentChip from "../attachment-chip"
|
||||||
|
import { getAttachments, removeAttachment } from "../../stores/attachments"
|
||||||
import { instances } from "../../stores/instances"
|
import { instances } from "../../stores/instances"
|
||||||
import { loadMessages, sendMessage, forkSession, isSessionMessagesLoading, setActiveParentSession, setActiveSession, runShellCommand, abortSession } from "../../stores/sessions"
|
import { loadMessages, sendMessage, forkSession, isSessionMessagesLoading, setActiveParentSession, setActiveSession, runShellCommand, abortSession } from "../../stores/sessions"
|
||||||
import { isSessionBusy as getSessionBusyStatus } from "../../stores/session-status"
|
import { isSessionBusy as getSessionBusyStatus } from "../../stores/session-status"
|
||||||
@@ -45,6 +47,8 @@ export const SessionView: Component<SessionViewProps> = (props) => {
|
|||||||
if (!currentSession) return false
|
if (!currentSession) return false
|
||||||
return Boolean(currentSession.pendingPermission || (currentSession as any).pendingQuestion)
|
return Boolean(currentSession.pendingPermission || (currentSession as any).pendingQuestion)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const attachments = createMemo(() => getAttachments(props.instanceId, props.sessionId))
|
||||||
let scrollToBottomHandle: (() => void) | undefined
|
let scrollToBottomHandle: (() => void) | undefined
|
||||||
let rootRef: HTMLDivElement | undefined
|
let rootRef: HTMLDivElement | undefined
|
||||||
function scheduleScrollToBottom() {
|
function scheduleScrollToBottom() {
|
||||||
@@ -230,7 +234,20 @@ export const SessionView: Component<SessionViewProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
<PromptInput
|
<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);">
|
||||||
|
<For each={attachments()}>
|
||||||
|
{(attachment) => (
|
||||||
|
<AttachmentChip
|
||||||
|
attachment={attachment}
|
||||||
|
onRemove={() => removeAttachment(props.instanceId, props.sessionId, attachment.id)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<PromptInput
|
||||||
instanceId={props.instanceId}
|
instanceId={props.instanceId}
|
||||||
instanceFolder={props.instanceFolder}
|
instanceFolder={props.instanceFolder}
|
||||||
sessionId={activeSession.id}
|
sessionId={activeSession.id}
|
||||||
|
|||||||
Reference in New Issue
Block a user