* chore: remove dead/unreferenced files across ui, vscode Remove 59 unused source files (components, hooks, lib utils, stores, barrels, and orphaned vscode github modules) that are not imported by any entry-reachable code. Also drop a stale test mock for the removed execCommands module. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * refactor: remove unused exported symbols (types, functions, consts, hooks) Remove exported symbols whose identifier is referenced nowhere in the repository (verified via repo-wide search), across ui types/contracts, lib utilities, sync layer, stores, and components. Also drop the few imports/private helpers orphaned by these removals. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * refactor: remove more unused exports (desktop, shortcuts, worktree, vscode) Continue removing repo-wide unreferenced exported functions, consts and types across lib/desktop, shortcuts, worktreeSessionCreator, sync, and vscode gitService, with cascading orphaned helpers/imports cleaned up. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * chore: add dead-code cleanup tooling * refactor: checkpoint dead-code cleanup * refactor: remove dead-code suppressions --------- Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
88 lines
2.6 KiB
TypeScript
88 lines
2.6 KiB
TypeScript
import type { Message, Part } from "@opencode-ai/sdk/v2/client"
|
|
import { Binary } from "./binary"
|
|
|
|
const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
|
|
|
|
function sortParts(parts: Part[]) {
|
|
return parts.filter((part) => !!part?.id).sort((a, b) => cmp(a.id, b.id))
|
|
}
|
|
|
|
export type OptimisticItem = {
|
|
message: Message
|
|
parts: Part[]
|
|
}
|
|
|
|
export type MessagePage = {
|
|
session: Message[]
|
|
part: { id: string; part: Part[] }[]
|
|
cursor?: string
|
|
complete: boolean
|
|
}
|
|
|
|
const hasParts = (parts: Part[] | undefined, want: Part[]) => {
|
|
if (!parts) return want.length === 0
|
|
return want.every((part) => Binary.search(parts, part.id, (item) => item.id).found)
|
|
}
|
|
|
|
const mergeParts = (parts: Part[] | undefined, want: Part[]) => {
|
|
if (!parts) return sortParts(want)
|
|
const next = [...parts]
|
|
let changed = false
|
|
for (const part of want) {
|
|
const result = Binary.search(next, part.id, (item) => item.id)
|
|
if (result.found) continue
|
|
next.splice(result.index, 0, part)
|
|
changed = true
|
|
}
|
|
if (!changed) return parts
|
|
return next
|
|
}
|
|
|
|
export function mergeOptimisticPage(page: MessagePage, items: OptimisticItem[]) {
|
|
if (items.length === 0) return { ...page, confirmed: [] as string[] }
|
|
|
|
const session = [...page.session]
|
|
const part = new Map(page.part.map((item) => [item.id, sortParts(item.part)]))
|
|
const confirmed: string[] = []
|
|
|
|
for (const item of items) {
|
|
const result = Binary.search(session, item.message.id, (message) => message.id)
|
|
const found = result.found
|
|
if (!found) session.splice(result.index, 0, item.message)
|
|
|
|
const current = part.get(item.message.id)
|
|
if (found && hasParts(current, item.parts)) {
|
|
confirmed.push(item.message.id)
|
|
continue
|
|
}
|
|
|
|
part.set(item.message.id, mergeParts(current, item.parts))
|
|
}
|
|
|
|
return {
|
|
cursor: page.cursor,
|
|
complete: page.complete,
|
|
session,
|
|
part: [...part.entries()]
|
|
.sort((a, b) => cmp(a[0], b[0]))
|
|
.map(([id, part]) => ({ id, part })),
|
|
confirmed,
|
|
}
|
|
}
|
|
|
|
/** Merge two sorted message arrays by id, deduplicating.
|
|
* Preserves references from `a` for items that already exist — avoids
|
|
* unnecessary React re-renders when prepending older history. */
|
|
export function mergeMessages<T extends { id: string }>(a: readonly T[], b: readonly T[]) {
|
|
const existing = new Map(a.map((item) => [item.id, item] as const))
|
|
let changed = false
|
|
for (const item of b) {
|
|
if (!existing.has(item.id)) {
|
|
existing.set(item.id, item)
|
|
changed = true
|
|
}
|
|
}
|
|
if (!changed) return a as T[]
|
|
return [...existing.values()].sort((x, y) => cmp(x.id, y.id))
|
|
}
|