chore: cleanup

This commit is contained in:
Adam
2026-01-19 10:54:56 -06:00
parent c720a2163c
commit eb779a7cc5
2 changed files with 67 additions and 6 deletions

View File

@@ -1,4 +1,6 @@
[data-component="session-turn"] { [data-component="session-turn"] {
--session-turn-sticky-height: 0px;
--sticky-header-height: calc(var(--session-title-height, 0px) + var(--session-turn-sticky-height, 0px) + 12px);
/* flex: 1; */ /* flex: 1; */
height: 100%; height: 100%;
min-height: 0; min-height: 0;
@@ -44,6 +46,12 @@
} }
} }
[data-slot="session-turn-attachments"] {
width: 100%;
min-width: 0;
align-self: stretch;
}
[data-slot="session-turn-sticky"] { [data-slot="session-turn-sticky"] {
width: calc(100% + 9px); width: calc(100% + 9px);
position: sticky; position: sticky;
@@ -331,11 +339,10 @@
} }
[data-component="sticky-accordion-header"] { [data-component="sticky-accordion-header"] {
top: var(--sticky-header-height, 40px); top: var(--sticky-header-height, 0px);
&[data-expanded]::before { &[data-expanded]::before {
top: calc(-1 * var(--sticky-header-height, 40px)); top: calc(-1 * var(--sticky-header-height, 0px));
} }
/* position: static; */
} }
[data-slot="session-turn-accordion-trigger-content"] { [data-slot="session-turn-accordion-trigger-content"] {

View File

@@ -1,5 +1,6 @@
import { import {
AssistantMessage, AssistantMessage,
FilePart,
Message as MessageType, Message as MessageType,
Part as PartType, Part as PartType,
type PermissionRequest, type PermissionRequest,
@@ -29,6 +30,7 @@ import { Spinner } from "./spinner"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { DateTime, DurationUnit, Interval } from "luxon" import { DateTime, DurationUnit, Interval } from "luxon"
import { createAutoScroll } from "../hooks" import { createAutoScroll } from "../hooks"
import { createResizeObserver } from "@solid-primitives/resize-observer"
function computeStatusFromPart(part: PartType | undefined): string | undefined { function computeStatusFromPart(part: PartType | undefined): string | undefined {
if (!part) return undefined if (!part) return undefined
@@ -75,6 +77,12 @@ function same<T>(a: readonly T[], b: readonly T[]) {
return a.every((x, i) => x === b[i]) return a.every((x, i) => x === b[i])
} }
function isAttachment(part: PartType | undefined) {
if (part?.type !== "file") return false
const mime = (part as FilePart).mime ?? ""
return mime.startsWith("image/") || mime === "application/pdf"
}
function AssistantMessageItem(props: { function AssistantMessageItem(props: {
message: AssistantMessage message: AssistantMessage
responsePartId: string | undefined responsePartId: string | undefined
@@ -133,6 +141,7 @@ export function SessionTurn(
const emptyMessages: MessageType[] = [] const emptyMessages: MessageType[] = []
const emptyParts: PartType[] = [] const emptyParts: PartType[] = []
const emptyFiles: FilePart[] = []
const emptyAssistant: AssistantMessage[] = [] const emptyAssistant: AssistantMessage[] = []
const emptyPermissions: PermissionRequest[] = [] const emptyPermissions: PermissionRequest[] = []
const emptyPermissionParts: { part: ToolPart; message: AssistantMessage }[] = [] const emptyPermissionParts: { part: ToolPart; message: AssistantMessage }[] = []
@@ -180,6 +189,19 @@ export function SessionTurn(
return data.store.part[msg.id] ?? emptyParts return data.store.part[msg.id] ?? emptyParts
}) })
const attachmentParts = createMemo(() => {
const msgParts = parts()
if (msgParts.length === 0) return emptyFiles
return msgParts.filter((part) => isAttachment(part)) as FilePart[]
})
const stickyParts = createMemo(() => {
const msgParts = parts()
if (msgParts.length === 0) return emptyParts
if (attachmentParts().length === 0) return msgParts
return msgParts.filter((part) => !isAttachment(part))
})
const assistantMessages = createMemo( const assistantMessages = createMemo(
() => { () => {
const msg = message() const msg = message()
@@ -331,6 +353,15 @@ export function SessionTurn(
const hideResponsePart = createMemo(() => !working() && !!responsePartId()) const hideResponsePart = createMemo(() => !working() && !!responsePartId())
const [responseCopied, setResponseCopied] = createSignal(false) const [responseCopied, setResponseCopied] = createSignal(false)
const [rootRef, setRootRef] = createSignal<HTMLDivElement | undefined>()
const [stickyRef, setStickyRef] = createSignal<HTMLDivElement | undefined>()
const updateStickyHeight = (height: number) => {
const root = rootRef()
if (!root) return
const next = Math.ceil(height)
root.style.setProperty("--session-turn-sticky-height", `${next}px`)
}
const handleCopyResponse = async () => { const handleCopyResponse = async () => {
const content = response() const content = response()
if (!content) return if (!content) return
@@ -361,6 +392,24 @@ export function SessionTurn(
onUserInteracted: props.onUserInteracted, onUserInteracted: props.onUserInteracted,
}) })
createResizeObserver(
() => stickyRef(),
({ height }) => {
updateStickyHeight(height)
},
)
createEffect(() => {
const root = rootRef()
if (!root) return
const sticky = stickyRef()
if (!sticky) {
root.style.setProperty("--session-turn-sticky-height", "0px")
return
}
updateStickyHeight(sticky.getBoundingClientRect().height)
})
const diffInit = 20 const diffInit = 20
const diffBatch = 20 const diffBatch = 20
@@ -438,7 +487,7 @@ export function SessionTurn(
}) })
return ( return (
<div data-component="session-turn" class={props.classes?.root}> <div data-component="session-turn" class={props.classes?.root} ref={setRootRef}>
<div <div
ref={autoScroll.scrollRef} ref={autoScroll.scrollRef}
onScroll={autoScroll.handleScroll} onScroll={autoScroll.handleScroll}
@@ -459,10 +508,15 @@ export function SessionTurn(
<Part part={shellModePart()!} message={msg()} defaultOpen /> <Part part={shellModePart()!} message={msg()} defaultOpen />
</Match> </Match>
<Match when={true}> <Match when={true}>
<div data-slot="session-turn-sticky"> <Show when={attachmentParts().length > 0}>
<div data-slot="session-turn-attachments">
<Message message={msg()} parts={attachmentParts()} />
</div>
</Show>
<div data-slot="session-turn-sticky" ref={setStickyRef}>
{/* User Message */} {/* User Message */}
<div data-slot="session-turn-message-content"> <div data-slot="session-turn-message-content">
<Message message={msg()} parts={parts()} /> <Message message={msg()} parts={stickyParts()} />
</div> </div>
{/* Trigger (sticky) */} {/* Trigger (sticky) */}