fix(app): user messages not rendering consistently
This commit is contained in:
@@ -1463,7 +1463,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
draft.part[messageID] = optimisticParts
|
draft.part[messageID] = optimisticParts
|
||||||
.filter((p) => !!p?.id)
|
.filter((p) => !!p?.id)
|
||||||
.slice()
|
.slice()
|
||||||
.sort((a, b) => a.id.localeCompare(b.id))
|
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -1481,7 +1481,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
draft.part[messageID] = optimisticParts
|
draft.part[messageID] = optimisticParts
|
||||||
.filter((p) => !!p?.id)
|
.filter((p) => !!p?.id)
|
||||||
.slice()
|
.slice()
|
||||||
.sort((a, b) => a.id.localeCompare(b.id))
|
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ type ChildOptions = {
|
|||||||
bootstrap?: boolean
|
bootstrap?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
|
||||||
|
|
||||||
function normalizeProviderList(input: ProviderListResponse): ProviderListResponse {
|
function normalizeProviderList(input: ProviderListResponse): ProviderListResponse {
|
||||||
return {
|
return {
|
||||||
...input,
|
...input,
|
||||||
@@ -297,7 +299,7 @@ function createGlobalSync() {
|
|||||||
const aUpdated = sessionUpdatedAt(a)
|
const aUpdated = sessionUpdatedAt(a)
|
||||||
const bUpdated = sessionUpdatedAt(b)
|
const bUpdated = sessionUpdatedAt(b)
|
||||||
if (aUpdated !== bUpdated) return bUpdated - aUpdated
|
if (aUpdated !== bUpdated) return bUpdated - aUpdated
|
||||||
return a.id.localeCompare(b.id)
|
return cmp(a.id, b.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
function takeRecentSessions(sessions: Session[], limit: number, cutoff: number) {
|
function takeRecentSessions(sessions: Session[], limit: number, cutoff: number) {
|
||||||
@@ -325,7 +327,7 @@ function createGlobalSync() {
|
|||||||
const all = input
|
const all = input
|
||||||
.filter((s) => !!s?.id)
|
.filter((s) => !!s?.id)
|
||||||
.filter((s) => !s.time?.archived)
|
.filter((s) => !s.time?.archived)
|
||||||
.sort((a, b) => a.id.localeCompare(b.id))
|
.sort((a, b) => cmp(a.id, b.id))
|
||||||
|
|
||||||
const roots = all.filter((s) => !s.parentID)
|
const roots = all.filter((s) => !s.parentID)
|
||||||
const children = all.filter((s) => !!s.parentID)
|
const children = all.filter((s) => !!s.parentID)
|
||||||
@@ -342,7 +344,7 @@ function createGlobalSync() {
|
|||||||
return sessionUpdatedAt(s) > cutoff
|
return sessionUpdatedAt(s) > cutoff
|
||||||
})
|
})
|
||||||
|
|
||||||
return [...keepRoots, ...keepChildren].sort((a, b) => a.id.localeCompare(b.id))
|
return [...keepRoots, ...keepChildren].sort((a, b) => cmp(a.id, b.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureChild(directory: string) {
|
function ensureChild(directory: string) {
|
||||||
@@ -457,7 +459,7 @@ function createGlobalSync() {
|
|||||||
const nonArchived = (x.data ?? [])
|
const nonArchived = (x.data ?? [])
|
||||||
.filter((s) => !!s?.id)
|
.filter((s) => !!s?.id)
|
||||||
.filter((s) => !s.time?.archived)
|
.filter((s) => !s.time?.archived)
|
||||||
.sort((a, b) => a.id.localeCompare(b.id))
|
.sort((a, b) => cmp(a.id, b.id))
|
||||||
|
|
||||||
// Read the current limit at resolve-time so callers that bump the limit while
|
// Read the current limit at resolve-time so callers that bump the limit while
|
||||||
// a request is in-flight still get the expanded result.
|
// a request is in-flight still get the expanded result.
|
||||||
@@ -559,7 +561,7 @@ function createGlobalSync() {
|
|||||||
"permission",
|
"permission",
|
||||||
sessionID,
|
sessionID,
|
||||||
reconcile(
|
reconcile(
|
||||||
permissions.filter((p) => !!p?.id).sort((a, b) => a.id.localeCompare(b.id)),
|
permissions.filter((p) => !!p?.id).sort((a, b) => cmp(a.id, b.id)),
|
||||||
{ key: "id" },
|
{ key: "id" },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -588,7 +590,7 @@ function createGlobalSync() {
|
|||||||
"question",
|
"question",
|
||||||
sessionID,
|
sessionID,
|
||||||
reconcile(
|
reconcile(
|
||||||
questions.filter((q) => !!q?.id).sort((a, b) => a.id.localeCompare(b.id)),
|
questions.filter((q) => !!q?.id).sort((a, b) => cmp(a.id, b.id)),
|
||||||
{ key: "id" },
|
{ key: "id" },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -986,7 +988,7 @@ function createGlobalSync() {
|
|||||||
.filter((p) => !!p?.id)
|
.filter((p) => !!p?.id)
|
||||||
.filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
|
.filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
|
||||||
.slice()
|
.slice()
|
||||||
.sort((a, b) => a.id.localeCompare(b.id))
|
.sort((a, b) => cmp(a.id, b.id))
|
||||||
setGlobalStore("project", projects)
|
setGlobalStore("project", projects)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import type { Message, Part } from "@opencode-ai/sdk/v2/client"
|
|||||||
|
|
||||||
const keyFor = (directory: string, id: string) => `${directory}\n${id}`
|
const keyFor = (directory: string, id: string) => `${directory}\n${id}`
|
||||||
|
|
||||||
|
const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
|
||||||
|
|
||||||
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
name: "Sync",
|
name: "Sync",
|
||||||
init: () => {
|
init: () => {
|
||||||
@@ -59,7 +61,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
const next = items
|
const next = items
|
||||||
.map((x) => x.info)
|
.map((x) => x.info)
|
||||||
.filter((m) => !!m?.id)
|
.filter((m) => !!m?.id)
|
||||||
.sort((a, b) => a.id.localeCompare(b.id))
|
.sort((a, b) => cmp(a.id, b.id))
|
||||||
|
|
||||||
batch(() => {
|
batch(() => {
|
||||||
input.setStore("message", input.sessionID, reconcile(next, { key: "id" }))
|
input.setStore("message", input.sessionID, reconcile(next, { key: "id" }))
|
||||||
@@ -69,7 +71,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
"part",
|
"part",
|
||||||
message.info.id,
|
message.info.id,
|
||||||
reconcile(
|
reconcile(
|
||||||
message.parts.filter((p) => !!p?.id).sort((a, b) => a.id.localeCompare(b.id)),
|
message.parts.filter((p) => !!p?.id).sort((a, b) => cmp(a.id, b.id)),
|
||||||
{ key: "id" },
|
{ key: "id" },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -129,7 +131,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
const result = Binary.search(messages, input.messageID, (m) => m.id)
|
const result = Binary.search(messages, input.messageID, (m) => m.id)
|
||||||
messages.splice(result.index, 0, message)
|
messages.splice(result.index, 0, message)
|
||||||
}
|
}
|
||||||
draft.part[input.messageID] = input.parts.filter((p) => !!p?.id).sort((a, b) => a.id.localeCompare(b.id))
|
draft.part[input.messageID] = input.parts.filter((p) => !!p?.id).sort((a, b) => cmp(a.id, b.id))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -271,7 +273,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
await client.session.list().then((x) => {
|
await client.session.list().then((x) => {
|
||||||
const sessions = (x.data ?? [])
|
const sessions = (x.data ?? [])
|
||||||
.filter((s) => !!s?.id)
|
.filter((s) => !!s?.id)
|
||||||
.sort((a, b) => a.id.localeCompare(b.id))
|
.sort((a, b) => cmp(a.id, b.id))
|
||||||
.slice(0, store.limit)
|
.slice(0, store.limit)
|
||||||
setStore("session", reconcile(sessions, { key: "id" }))
|
setStore("session", reconcile(sessions, { key: "id" }))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -499,7 +499,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const bUpdated = b.time.updated ?? b.time.created
|
const bUpdated = b.time.updated ?? b.time.created
|
||||||
const aRecent = aUpdated > oneMinuteAgo
|
const aRecent = aUpdated > oneMinuteAgo
|
||||||
const bRecent = bUpdated > oneMinuteAgo
|
const bRecent = bUpdated > oneMinuteAgo
|
||||||
if (aRecent && bRecent) return a.id.localeCompare(b.id)
|
if (aRecent && bRecent) return a.id < b.id ? -1 : a.id > b.id ? 1 : 0
|
||||||
if (aRecent && !bRecent) return -1
|
if (aRecent && !bRecent) return -1
|
||||||
if (!aRecent && bRecent) return 1
|
if (!aRecent && bRecent) return 1
|
||||||
return bUpdated - aUpdated
|
return bUpdated - aUpdated
|
||||||
@@ -739,7 +739,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function prefetchMessages(directory: string, sessionID: string, token: number) {
|
async function prefetchMessages(directory: string, sessionID: string, token: number) {
|
||||||
const [, setStore] = globalSync.child(directory, { bootstrap: false })
|
const [store, setStore] = globalSync.child(directory, { bootstrap: false })
|
||||||
|
|
||||||
return retry(() => globalSDK.client.session.messages({ directory, sessionID, limit: prefetchChunk }))
|
return retry(() => globalSDK.client.session.messages({ directory, sessionID, limit: prefetchChunk }))
|
||||||
.then((messages) => {
|
.then((messages) => {
|
||||||
@@ -750,23 +750,49 @@ export default function Layout(props: ParentProps) {
|
|||||||
.map((x) => x.info)
|
.map((x) => x.info)
|
||||||
.filter((m) => !!m?.id)
|
.filter((m) => !!m?.id)
|
||||||
.slice()
|
.slice()
|
||||||
.sort((a, b) => a.id.localeCompare(b.id))
|
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
||||||
|
|
||||||
|
const current = store.message[sessionID] ?? []
|
||||||
|
const merged = (() => {
|
||||||
|
if (current.length === 0) return next
|
||||||
|
|
||||||
|
const map = new Map<string, Message>()
|
||||||
|
for (const item of current) {
|
||||||
|
if (!item?.id) continue
|
||||||
|
map.set(item.id, item)
|
||||||
|
}
|
||||||
|
for (const item of next) {
|
||||||
|
map.set(item.id, item)
|
||||||
|
}
|
||||||
|
return [...map.values()].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
||||||
|
})()
|
||||||
|
|
||||||
batch(() => {
|
batch(() => {
|
||||||
setStore("message", sessionID, reconcile(next, { key: "id" }))
|
setStore("message", sessionID, reconcile(merged, { key: "id" }))
|
||||||
|
|
||||||
for (const message of items) {
|
for (const message of items) {
|
||||||
setStore(
|
const currentParts = store.part[message.info.id] ?? []
|
||||||
"part",
|
const mergedParts = (() => {
|
||||||
message.info.id,
|
if (currentParts.length === 0) {
|
||||||
reconcile(
|
return message.parts
|
||||||
message.parts
|
|
||||||
.filter((p) => !!p?.id)
|
.filter((p) => !!p?.id)
|
||||||
.slice()
|
.slice()
|
||||||
.sort((a, b) => a.id.localeCompare(b.id)),
|
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
||||||
{ key: "id" },
|
}
|
||||||
),
|
|
||||||
)
|
const map = new Map<string, (typeof currentParts)[number]>()
|
||||||
|
for (const item of currentParts) {
|
||||||
|
if (!item?.id) continue
|
||||||
|
map.set(item.id, item)
|
||||||
|
}
|
||||||
|
for (const item of message.parts) {
|
||||||
|
if (!item?.id) continue
|
||||||
|
map.set(item.id, item)
|
||||||
|
}
|
||||||
|
return [...map.values()].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
||||||
|
})()
|
||||||
|
|
||||||
|
setStore("part", message.info.id, reconcile(mergedParts, { key: "id" }))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -161,12 +161,14 @@ export function SessionTurn(
|
|||||||
const messageIndex = createMemo(() => {
|
const messageIndex = createMemo(() => {
|
||||||
const messages = allMessages() ?? emptyMessages
|
const messages = allMessages() ?? emptyMessages
|
||||||
const result = Binary.search(messages, props.messageID, (m) => m.id)
|
const result = Binary.search(messages, props.messageID, (m) => m.id)
|
||||||
if (!result.found) return -1
|
|
||||||
|
|
||||||
const msg = messages[result.index]
|
const index = result.found ? result.index : messages.findIndex((m) => m.id === props.messageID)
|
||||||
|
if (index < 0) return -1
|
||||||
|
|
||||||
|
const msg = messages[index]
|
||||||
if (!msg || msg.role !== "user") return -1
|
if (!msg || msg.role !== "user") return -1
|
||||||
|
|
||||||
return result.index
|
return index
|
||||||
})
|
})
|
||||||
|
|
||||||
const message = createMemo(() => {
|
const message = createMemo(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user