Apply PR #40427: some experimental perf improvements

This commit is contained in:
opencode-agent[bot]
2026-08-12 16:24:02 +00:00
13 changed files with 145 additions and 69 deletions
@@ -17,7 +17,7 @@ import { installSseTransport } from "../utils/sse-transport"
import { expectSessionTitle } from "../utils/waits"
const initialPageSize = 20
const historyPageSize = 200
const historyPageSize = 50
const messages = Array.from({ length: initialPageSize + 1 }, (_, index) => {
const id = `msg_${String(index + 1001).padStart(4, "0")}_history_root_user`
return [
+2 -27
View File
@@ -52,10 +52,9 @@ import { DirectoryDataProvider } from "@/pages/directory-layout"
import Layout from "@/pages/layout"
import { ErrorPage } from "./pages/error"
import { useCheckServerHealth } from "./utils/server-health"
import { legacySessionServer, requireServerKey, sessionHref } from "./utils/session-route"
import { legacySessionServer, sessionHref } from "./utils/session-route"
import { decode64 } from "@/utils/base64"
import { TargetSessionRouteContent } from "@/pages/session"
import { TargetSessionRoute } from "@/pages/session-lazy"
import { Home } from "@/pages/home"
const NewSession = lazy(() => import("@/pages/new-session"))
@@ -76,30 +75,6 @@ const DirectoryDraftRedirect = () => {
return null
}
function TargetServerRoute(props: ParentProps) {
const params = useParams<{ serverKey: string; id: string }>()
const global = useGlobal()
const conn = createMemo(() => {
const key = requireServerKey(params.serverKey)
return global.servers.list().find((item) => ServerConnection.key(item) === key)
})
return (
// Owns the server-identity remount. Session changes must not remount this subtree.
<Show when={requireServerKey(params.serverKey)} keyed>
<ServerSDKProvider server={conn}>
<ServerSyncProvider server={conn}>{props.children}</ServerSyncProvider>
</ServerSDKProvider>
</Show>
)
}
const TargetSessionRoute = () => (
<TargetServerRoute>
<TargetSessionRouteContent />
</TargetServerRoute>
)
// Wraps the non-draft routes. They are gated on (and keyed to) the globally selected
// server via ServerKey, then provide the server-scoped shell for that server.
function SelectedServerProviders(props: ParentProps) {
+8 -1
View File
@@ -16,7 +16,7 @@ type MessageApi = ServerApi["message"]
const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
const SKIP_PARTS = new Set(["patch", "step-start", "step-finish"])
const initialMessagePageSize = 20
const historyMessagePageSize = 200
const historyMessagePageSize = 50
const sessionInfoLimit = 2_048
const emptyIDs: ReadonlySet<string> = new Set()
@@ -45,6 +45,12 @@ function projectMessageSource(message: Message): SessionMessageInfo[] {
]
}
function yieldToMain() {
const scheduler = (globalThis as { scheduler?: { yield: () => Promise<void> } }).scheduler
if (scheduler) return scheduler.yield()
return new Promise<void>((resolve) => setTimeout(resolve, 0))
}
function needsOlderTurnRoot(source: readonly SessionMessageInfo[]) {
const boundary = source.find(
(message) =>
@@ -538,6 +544,7 @@ export function createServerSession(
if (!response.data.length) break
}
const response = pages.at(-1)!
await yieldToMain()
const source = pages.flatMap((page) => page.data).toReversed()
const normalized = normalizeSessionMessages(sessionID, source)
return {
+1
View File
@@ -28,3 +28,4 @@ export {
} from "./wsl/types"
export { ServerConnection } from "./context/server"
export { createDraftStore, type DraftStore } from "./utils/draft-store"
export { preloadSessionRoute } from "./pages/session-lazy"
+16
View File
@@ -1,4 +1,5 @@
import { ScrollView } from "@opencode-ai/ui/scroll-view"
import { onCleanup, onMount } from "solid-js"
import { createHomeController } from "./home/home-controller"
import { createHomeProjectsController } from "./home/home-projects-controller"
import { HomeUtilityNav } from "./home/home-projects-view"
@@ -7,8 +8,23 @@ import { createHomeScrollController } from "./home/home-scroll-controller"
import { createHomeSessionSearchController } from "./home/home-session-search-controller"
import { createHomeSessionsController } from "./home/home-sessions-controller"
import { HomeSessions } from "./home/home-sessions"
import { preloadSessionRoute } from "./session-lazy"
export function Home() {
onMount(() => {
let idle: number | undefined
const timer = setTimeout(() => {
if ("requestIdleCallback" in window) {
idle = requestIdleCallback(() => void preloadSessionRoute(), { timeout: 3_000 })
return
}
void preloadSessionRoute()
}, 1_500)
onCleanup(() => {
clearTimeout(timer)
if (idle !== undefined) cancelIdleCallback(idle)
})
})
const home = createHomeController()
const projects = createHomeProjectsController(home)
const sessions = createHomeSessionsController(home)
+4
View File
@@ -0,0 +1,4 @@
import { lazy } from "solid-js"
export const TargetSessionRoute = lazy(() => import("./target-session-route"))
export const preloadSessionRoute = TargetSessionRoute.preload
@@ -0,0 +1,27 @@
import { createMemo, Show } from "solid-js"
import { useParams } from "@solidjs/router"
import { useGlobal } from "@/context/global"
import { ServerConnection } from "@/context/server"
import { ServerSDKProvider } from "@/context/server-sdk"
import { ServerSyncProvider } from "@/context/server-sync"
import { requireServerKey } from "@/utils/session-route"
import { TargetSessionRouteContent } from "./session"
export default function TargetSessionRoute() {
const params = useParams<{ serverKey: string }>()
const global = useGlobal()
const connection = createMemo(() => {
const key = requireServerKey(params.serverKey)
return global.servers.list().find((item) => ServerConnection.key(item) === key)
})
return (
<Show when={requireServerKey(params.serverKey)} keyed>
<ServerSDKProvider server={connection}>
<ServerSyncProvider server={connection}>
<TargetSessionRouteContent />
</ServerSyncProvider>
</ServerSDKProvider>
</Show>
)
}
+4 -1
View File
@@ -9,6 +9,7 @@ import {
type Locale,
type Platform,
PlatformProvider,
preloadSessionRoute,
createDraftStore,
ServerConnection,
useCommand,
@@ -442,7 +443,9 @@ render(() => {
const api = window.api as typeof window.api & {
getWindowID?: () => Promise<string>
}
return { id: await api.getWindowID?.() }
const id = await api.getWindowID?.()
if (/^\/server\/[^/]+\/session\/[^/]+/.test(getLastActiveUrl(id ?? "browser"))) await preloadSessionRoute()
return { id }
})
return (
@@ -158,6 +158,12 @@ describe("markdown stream", () => {
expect(final.blocks[2]).toEqual({ raw: "- final item", src: "- final item", mode: "full" })
})
test("splits completed markdown into bounded top-level blocks", () => {
const result = project(undefined, "# Plan\n\nFirst paragraph.\n\nSecond paragraph.", false)
expect(result.blocks.map((block) => block.raw)).toEqual(["# Plan", "First paragraph.", "Second paragraph."])
})
test("catches up paced text before finalizing", () => {
const live = project(undefined, "# Plan\n\nFinished paragraph.\n\n- final", true)
const final = project(live, `${live.text} item`, false)
@@ -51,7 +51,7 @@ function heal(text: string) {
}
export function stream(text: string, live: boolean): Block[] {
if (!live) return completedProjection(text).blocks
if (!live) return completedBlocks(text)
if (refs(text)) return [{ raw: text, src: heal(text), mode: "live" }] satisfies Block[]
const tokens = marked.lexer(text)
const tail = tokens.findLastIndex((token) => token.type !== "space")
@@ -85,6 +85,17 @@ export function stream(text: string, live: boolean): Block[] {
return [...result, { raw, src: openCode(code.raw), mode: "code", language: language(code.lang) }]
}
function completedBlocks(text: string) {
if (refs(text)) return completedProjection(text).blocks
const tokens = marked.lexer(text)
return tokens.flatMap((token): Block[] => {
if (token.type === "space") return []
if (token.type !== "code") return [{ raw: token.raw, src: token.raw, mode: "full" }]
const code = token as Tokens.Code
return [{ raw: code.raw, src: code.text, mode: "code", language: language(code.lang), complete: true }]
})
}
export function project(previous: Projection | undefined, text: string, live: boolean): Projection {
if (!live) {
const current =
@@ -93,7 +104,7 @@ export function project(previous: Projection | undefined, text: string, live: bo
: previous && text.startsWith(previous.text)
? project(previous, text, true)
: undefined
if (!current) return completedProjection(text)
if (!current) return { text, blocks: completedBlocks(text) }
return {
text,
blocks: current.blocks.map((block) => {
+35 -14
View File
@@ -491,6 +491,8 @@ export function Markdown(
)
let copyCleanup: (() => void) | undefined
let renderFrame: number | undefined
let renderGeneration = 0
createEffect(() => {
const container = root()
@@ -499,6 +501,9 @@ export function Markdown(
const content = local.text ? pendingBlocks(result, projected, local.cacheKey, owner) : []
if (!container) return
if (isServer) return
const generation = ++renderGeneration
if (renderFrame !== undefined) cancelAnimationFrame(renderFrame)
renderFrame = undefined
if (content.length === 0) {
disposeCopyButtons(container)
container.innerHTML = ""
@@ -515,24 +520,40 @@ export function Markdown(
})
activeCodeKeys.clear()
nextCodeKeys.forEach((key) => activeCodeKeys.add(key))
content.forEach((block, index) => updateBlock(container, index, block, labels))
while (container.children.length > content.length) {
const child = container.lastElementChild
if (!child) break
disposeCopyButtons(child)
child.remove()
let index = 0
const update = () => {
renderFrame = undefined
if (generation !== renderGeneration) return
const deadline = performance.now() + 8
while (index < content.length && performance.now() < deadline) {
updateBlock(container, index, content[index]!, labels)
index += 1
}
if (index < content.length) {
renderFrame = requestAnimationFrame(update)
return
}
while (container.children.length > content.length) {
const child = container.lastElementChild
if (!child) break
disposeCopyButtons(child)
child.remove()
}
container
.querySelectorAll<HTMLElement>('[data-slot="markdown-copy-button"]')
.forEach((button) => setCopyState(button, labels, button.dataset.copied === "true"))
if (!copyCleanup)
copyCleanup = setupCodeCopy(container, () => ({
copy: i18n.t("ui.message.copy"),
copied: i18n.t("ui.message.copied"),
}))
}
container
.querySelectorAll<HTMLElement>('[data-slot="markdown-copy-button"]')
.forEach((button) => setCopyState(button, labels, button.dataset.copied === "true"))
if (!copyCleanup)
copyCleanup = setupCodeCopy(container, () => ({
copy: i18n.t("ui.message.copy"),
copied: i18n.t("ui.message.copied"),
}))
update()
})
onCleanup(() => {
renderGeneration += 1
if (renderFrame !== undefined) cancelAnimationFrame(renderFrame)
if (copyCleanup) copyCleanup()
disposeMarkdownProjection(owner)
activeCodeKeys.forEach(disposeCode)
+1 -1
View File
@@ -24,7 +24,7 @@ function createPool(lineDiffType: "none" | "word-alt") {
{
theme: "OpenCode",
lineDiffType,
preferredHighlighter: "shiki-wasm",
preferredHighlighter: "shiki-js",
},
)
@@ -53,9 +53,11 @@ export function PromptInputV2(props: PromptInputV2Props) {
const view = props.controller.view
let editor: HTMLDivElement | undefined
let localInput = false
const updateCursor = () => {
const updateCursor = (event: KeyboardEvent | PointerEvent) => {
if (!editor || !window.getSelection()?.isCollapsed) return
props.controller.onCursor(promptInputV2Cursor(editor))
if (event instanceof KeyboardEvent && !["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(event.key))
return
props.controller.onCursor(parsePromptInputV2Editor(editor).cursor)
}
const mode = createMemo(() => state.mode)
const buttons = createMemo(() => ({
@@ -164,8 +166,7 @@ export function PromptInputV2(props: PromptInputV2Props) {
class="relative z-10 block min-h-[60px] max-h-[180px] w-full overflow-y-auto whitespace-pre-wrap bg-transparent px-4 pt-4 pb-2 text-[13px] font-[440] leading-5 text-v2-text-text-base focus:outline-none empty:before:content-['\200B'] [&_[data-mention=file]]:text-syntax-property [&_[data-mention=agent]]:text-syntax-type [&_[data-mention=reference]]:text-syntax-keyword"
classList={{ "font-mono!": state.mode === "shell", "opacity-50": props.disabled }}
onInput={(event) => {
const cursor = promptInputV2Cursor(event.currentTarget)
const prompt = parsePromptInputV2Editor(event.currentTarget)
const { prompt, cursor } = parsePromptInputV2Editor(event.currentTarget)
const images = props.controller.parts().filter((part) => part.type === "image")
localInput = true
props.controller.onInput(prompt.map((part) => part.content).join(""), [...prompt, ...images], cursor)
@@ -302,8 +303,13 @@ function renderPromptInputV2Editor(editor: HTMLDivElement, prompt: PromptInputV2
function parsePromptInputV2Editor(editor: HTMLDivElement) {
const parts: Exclude<PromptInputV2Prompt[number], PromptInputV2Attachment>[] = []
const selection = window.getSelection()
const anchorNode = selection && editor.contains(selection.anchorNode) ? selection.anchorNode : undefined
const anchorOffset = anchorNode ? selection!.anchorOffset : 0
let buffer = ""
let position = 0
let cursor: number | undefined
const offset = () => position + buffer.length
const flush = () => {
if (!buffer) return
@@ -338,43 +344,42 @@ function parsePromptInputV2Editor(editor: HTMLDivElement) {
}
const visit = (node: Node) => {
if (node.nodeType === Node.TEXT_NODE) {
if (node === anchorNode) cursor = offset() + Math.min(anchorOffset, node.textContent?.length ?? 0)
buffer += node.textContent ?? ""
return
}
if (!(node instanceof HTMLElement)) return
if (node.dataset.mention) {
if (node === anchorNode) cursor = offset() + (anchorOffset > 0 ? (node.textContent?.length ?? 0) : 0)
mention(node)
return
}
if (node.tagName === "BR") {
if (node === anchorNode) cursor = offset() + (anchorOffset > 0 ? 1 : 0)
buffer += "\n"
return
}
Array.from(node.childNodes).forEach(visit)
Array.from(node.childNodes).forEach((child, index) => {
if (node === anchorNode && anchorOffset === index) cursor = offset()
visit(child)
})
if (node === anchorNode && anchorOffset >= node.childNodes.length) cursor = offset()
}
Array.from(editor.childNodes).forEach((node, index, nodes) => {
if (editor === anchorNode && anchorOffset === index) cursor = offset()
visit(node)
if (node instanceof HTMLElement && ["DIV", "P"].includes(node.tagName) && index < nodes.length - 1) buffer += "\n"
})
if (editor === anchorNode && anchorOffset >= editor.childNodes.length) cursor = offset()
flush()
if (
parts.every((part) => part.type === "text") &&
parts.every((part) => part.content.replace(/[\n\u200B]/g, "") === "")
) {
return [{ type: "text" as const, content: "", start: 0, end: 0 }]
}
if (parts.length > 0) return parts
return [{ type: "text" as const, content: "", start: 0, end: 0 }]
}
function promptInputV2Cursor(editor: HTMLDivElement) {
const selection = window.getSelection()
if (!selection?.rangeCount || !editor.contains(selection.anchorNode)) return editor.textContent?.length ?? 0
const range = selection.getRangeAt(0).cloneRange()
range.selectNodeContents(editor)
range.setEnd(selection.anchorNode!, selection.anchorOffset)
return range.toString().length
const result =
parts.length === 0 ||
(parts.every((part) => part.type === "text") &&
parts.every((part) => part.content.replace(/[\n\u200B]/g, "") === ""))
? [{ type: "text" as const, content: "", start: 0, end: 0 }]
: parts
return { prompt: result, cursor: cursor ?? offset() }
}
export function PromptInputV2Attachments(props: {