mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-04 08:03:14 +00:00
fix(app): timeline jank
This commit is contained in:
@@ -1091,7 +1091,9 @@ export default function Page() {
|
||||
|
||||
const el = scroller
|
||||
const delta = next - dockHeight
|
||||
const stick = el ? el.scrollHeight - el.clientHeight - el.scrollTop < 10 + Math.max(0, delta) : false
|
||||
const stick = el
|
||||
? !autoScroll.userScrolled() || el.scrollHeight - el.clientHeight - el.scrollTop < 10 + Math.max(0, delta)
|
||||
: false
|
||||
|
||||
dockHeight = next
|
||||
|
||||
|
||||
@@ -689,7 +689,9 @@ export function MessageTimeline(props: {
|
||||
if (!item || active()) return false
|
||||
return messageID > item.id
|
||||
})
|
||||
const comments = createMemo(() => messageComments(sync.data.part[messageID] ?? []))
|
||||
const comments = createMemo(() => messageComments(sync.data.part[messageID] ?? []), [], {
|
||||
equals: (a, b) => JSON.stringify(a) === JSON.stringify(b),
|
||||
})
|
||||
const commentCount = createMemo(() => comments().length)
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Show,
|
||||
Switch,
|
||||
onCleanup,
|
||||
Index,
|
||||
type JSX,
|
||||
} from "solid-js"
|
||||
import stripAnsi from "strip-ansi"
|
||||
@@ -458,50 +459,67 @@ export function AssistantParts(props: {
|
||||
const last = createMemo(() => grouped().at(-1)?.key)
|
||||
|
||||
return (
|
||||
<For each={grouped()}>
|
||||
{(entry) => {
|
||||
if (entry.type === "context") {
|
||||
const parts = createMemo(
|
||||
() =>
|
||||
entry.refs
|
||||
.map((ref) => partByID(list(data.store.part?.[ref.messageID], emptyParts), ref.partID))
|
||||
.filter((part): part is ToolPart => !!part && isContextGroupTool(part)),
|
||||
emptyTools,
|
||||
{ equals: same },
|
||||
)
|
||||
const busy = createMemo(() => props.working && last() === entry.key)
|
||||
|
||||
return (
|
||||
<Show when={parts().length > 0}>
|
||||
<ContextToolGroup parts={parts()} busy={busy()} />
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
const message = createMemo(() => props.messages.find((item) => item.id === entry.ref.messageID))
|
||||
const part = createMemo(() =>
|
||||
partByID(list(data.store.part?.[entry.ref.messageID], emptyParts), entry.ref.partID),
|
||||
)
|
||||
<Index each={grouped()}>
|
||||
{(entryAccessor) => {
|
||||
const entryType = createMemo(() => entryAccessor().type)
|
||||
|
||||
return (
|
||||
<Show when={message()}>
|
||||
{(message) => (
|
||||
<Show when={part()}>
|
||||
{(part) => (
|
||||
<Part
|
||||
part={part()}
|
||||
message={message()}
|
||||
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
||||
turnDurationMs={props.turnDurationMs}
|
||||
defaultOpen={partDefaultOpen(part(), props.shellToolDefaultOpen, props.editToolDefaultOpen)}
|
||||
/>
|
||||
)}
|
||||
</Show>
|
||||
)}
|
||||
</Show>
|
||||
<Switch>
|
||||
<Match when={entryType() === "context"}>
|
||||
{(() => {
|
||||
const parts = createMemo(
|
||||
() => {
|
||||
const entry = entryAccessor() as { type: "context"; refs: PartRef[] }
|
||||
return entry.refs
|
||||
.map((ref) => partByID(list(data.store.part?.[ref.messageID], emptyParts), ref.partID))
|
||||
.filter((part): part is ToolPart => !!part && isContextGroupTool(part))
|
||||
},
|
||||
emptyTools,
|
||||
{ equals: same },
|
||||
)
|
||||
const busy = createMemo(() => props.working && last() === entryAccessor().key)
|
||||
|
||||
return (
|
||||
<Show when={parts().length > 0}>
|
||||
<ContextToolGroup parts={parts()} busy={busy()} />
|
||||
</Show>
|
||||
)
|
||||
})()}
|
||||
</Match>
|
||||
<Match when={entryType() === "part"}>
|
||||
{(() => {
|
||||
const message = createMemo(() => {
|
||||
const entry = entryAccessor() as { type: "part"; ref: PartRef }
|
||||
return props.messages.find((item) => item.id === entry.ref.messageID)
|
||||
})
|
||||
const part = createMemo(() => {
|
||||
const entry = entryAccessor() as { type: "part"; ref: PartRef }
|
||||
return partByID(list(data.store.part?.[entry.ref.messageID], emptyParts), entry.ref.partID)
|
||||
})
|
||||
|
||||
return (
|
||||
<Show when={message()}>
|
||||
{(msg) => (
|
||||
<Show when={part()}>
|
||||
{(p) => (
|
||||
<Part
|
||||
part={p()}
|
||||
message={msg()}
|
||||
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
||||
turnDurationMs={props.turnDurationMs}
|
||||
defaultOpen={partDefaultOpen(p(), props.shellToolDefaultOpen, props.editToolDefaultOpen)}
|
||||
/>
|
||||
)}
|
||||
</Show>
|
||||
)}
|
||||
</Show>
|
||||
)
|
||||
})()}
|
||||
</Match>
|
||||
</Switch>
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
</Index>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -632,36 +650,56 @@ export function AssistantMessageDisplay(props: {
|
||||
)
|
||||
|
||||
return (
|
||||
<For each={grouped()}>
|
||||
{(entry) => {
|
||||
if (entry.type === "context") {
|
||||
const parts = createMemo(
|
||||
() =>
|
||||
entry.refs
|
||||
.map((ref) => partByID(props.parts, ref.partID))
|
||||
.filter((part): part is ToolPart => !!part && isContextGroupTool(part)),
|
||||
emptyTools,
|
||||
{ equals: same },
|
||||
)
|
||||
|
||||
return (
|
||||
<Show when={parts().length > 0}>
|
||||
<ContextToolGroup parts={parts()} />
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
const part = createMemo(() => partByID(props.parts, entry.ref.partID))
|
||||
<Index each={grouped()}>
|
||||
{(entryAccessor) => {
|
||||
const entryType = createMemo(() => entryAccessor().type)
|
||||
|
||||
return (
|
||||
<Show when={part()}>
|
||||
{(part) => (
|
||||
<Part part={part()} message={props.message} showAssistantCopyPartID={props.showAssistantCopyPartID} />
|
||||
)}
|
||||
</Show>
|
||||
<Switch>
|
||||
<Match when={entryType() === "context"}>
|
||||
{(() => {
|
||||
const parts = createMemo(
|
||||
() => {
|
||||
const entry = entryAccessor() as { type: "context"; refs: PartRef[] }
|
||||
return entry.refs
|
||||
.map((ref) => partByID(props.parts, ref.partID))
|
||||
.filter((part): part is ToolPart => !!part && isContextGroupTool(part))
|
||||
},
|
||||
emptyTools,
|
||||
{ equals: same },
|
||||
)
|
||||
|
||||
return (
|
||||
<Show when={parts().length > 0}>
|
||||
<ContextToolGroup parts={parts()} />
|
||||
</Show>
|
||||
)
|
||||
})()}
|
||||
</Match>
|
||||
<Match when={entryType() === "part"}>
|
||||
{(() => {
|
||||
const part = createMemo(() => {
|
||||
const entry = entryAccessor() as { type: "part"; ref: PartRef }
|
||||
return partByID(props.parts, entry.ref.partID)
|
||||
})
|
||||
|
||||
return (
|
||||
<Show when={part()}>
|
||||
{(p) => (
|
||||
<Part
|
||||
part={p()}
|
||||
message={props.message}
|
||||
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
||||
/>
|
||||
)}
|
||||
</Show>
|
||||
)
|
||||
})()}
|
||||
</Match>
|
||||
</Switch>
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
</Index>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,10 @@ export function createAutoScroll(options: AutoScrollOptions) {
|
||||
|
||||
const handleInteraction = () => {
|
||||
if (!active()) return
|
||||
stop()
|
||||
const selection = window.getSelection()
|
||||
if (selection && selection.toString().length > 0) {
|
||||
stop()
|
||||
}
|
||||
}
|
||||
|
||||
const updateOverflowAnchor = (el: HTMLElement) => {
|
||||
|
||||
Reference in New Issue
Block a user