From 374275eeb691006fa8f422d6267aa694ab38a992 Mon Sep 17 00:00:00 2001
From: Adam <2363879+adamdotdevin@users.noreply.github.com>
Date: Wed, 7 Jan 2026 08:41:16 -0600
Subject: feat(app): chunk message loading, lazy load diffs
---
packages/app/src/pages/layout.tsx | 17 +++
packages/app/src/pages/session.tsx | 243 ++++++++++++++++++++++++++-----------
2 files changed, 192 insertions(+), 68 deletions(-)
(limited to 'packages/app/src/pages')
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index a0f364d9a..50553795c 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -55,6 +55,7 @@ import { DialogEditProject } from "@/components/dialog-edit-project"
import { DialogSelectServer } from "@/components/dialog-select-server"
import { useCommand, type CommandOption } from "@/context/command"
import { ConstrainDragXAxis } from "@/utils/solid-dnd"
+import { navStart } from "@/utils/perf"
import { DialogSelectDirectory } from "@/components/dialog-select-directory"
import { useServer } from "@/context/server"
@@ -309,6 +310,14 @@ export default function Layout(props: ParentProps) {
if (targetIndex >= 0 && targetIndex < sessions.length) {
const session = sessions[targetIndex]
+ if (import.meta.env.DEV) {
+ navStart({
+ dir: base64Encode(session.directory),
+ from: params.id,
+ to: session.id,
+ trigger: offset > 0 ? "alt+arrowdown" : "alt+arrowup",
+ })
+ }
navigateToSession(session)
queueMicrotask(() => scrollToSession(session.id))
return
@@ -325,6 +334,14 @@ export default function Layout(props: ParentProps) {
}
const targetSession = offset > 0 ? nextProjectSessions[0] : nextProjectSessions[nextProjectSessions.length - 1]
+ if (import.meta.env.DEV) {
+ navStart({
+ dir: base64Encode(targetSession.directory),
+ from: params.id,
+ to: targetSession.id,
+ trigger: offset > 0 ? "alt+arrowdown" : "alt+arrowup",
+ })
+ }
navigateToSession(targetSession)
queueMicrotask(() => scrollToSession(targetSession.id))
}
diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx
index d3d8ef387..4a40ae7d2 100644
--- a/packages/app/src/pages/session.tsx
+++ b/packages/app/src/pages/session.tsx
@@ -1,4 +1,4 @@
-import { For, onCleanup, Show, Match, Switch, createMemo, createEffect, on } from "solid-js"
+import { For, onCleanup, onMount, Show, Match, Switch, createMemo, createEffect, on } from "solid-js"
import { createMediaQuery } from "@solid-primitives/media"
import { createResizeObserver } from "@solid-primitives/resize-observer"
import { Dynamic } from "solid-js/web"
@@ -8,6 +8,7 @@ import { createStore } from "solid-js/store"
import { PromptInput } from "@/components/prompt-input"
import { SessionContextUsage } from "@/components/session-context-usage"
import { IconButton } from "@opencode-ai/ui/icon-button"
+import { Button } from "@opencode-ai/ui/button"
import { Icon } from "@opencode-ai/ui/icon"
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
@@ -49,6 +50,7 @@ import {
NewSessionView,
} from "@/components/session"
import { usePlatform } from "@/context/platform"
+import { navMark, navParams } from "@/utils/perf"
import { same } from "@/utils/same"
type DiffStyle = "unified" | "split"
@@ -162,6 +164,46 @@ export default function Page() {
const tabs = createMemo(() => layout.tabs(sessionKey()))
const view = createMemo(() => layout.view(sessionKey()))
+ if (import.meta.env.DEV) {
+ createEffect(
+ on(
+ () => [params.dir, params.id] as const,
+ ([dir, id], prev) => {
+ if (!id) return
+ navParams({ dir, from: prev?.[1], to: id })
+ },
+ ),
+ )
+
+ createEffect(() => {
+ const id = params.id
+ if (!id) return
+ if (!prompt.ready()) return
+ navMark({ dir: params.dir, to: id, name: "storage:prompt-ready" })
+ })
+
+ createEffect(() => {
+ const id = params.id
+ if (!id) return
+ if (!terminal.ready()) return
+ navMark({ dir: params.dir, to: id, name: "storage:terminal-ready" })
+ })
+
+ createEffect(() => {
+ const id = params.id
+ if (!id) return
+ if (!file.ready()) return
+ navMark({ dir: params.dir, to: id, name: "storage:file-view-ready" })
+ })
+
+ createEffect(() => {
+ const id = params.id
+ if (!id) return
+ if (sync.data.message[id] === undefined) return
+ navMark({ dir: params.dir, to: id, name: "session:data-ready" })
+ })
+ }
+
const isDesktop = createMediaQuery("(min-width: 768px)")
function normalizeTab(tab: string) {
@@ -216,6 +258,8 @@ export default function Page() {
})
const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
+ const reviewCount = createMemo(() => info()?.summary?.files ?? 0)
+ const hasReview = createMemo(() => reviewCount() > 0)
const revertMessageID = createMemo(() => info()?.revert?.messageID)
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
const messagesReady = createMemo(() => {
@@ -223,6 +267,16 @@ export default function Page() {
if (!id) return true
return sync.data.message[id] !== undefined
})
+ const historyMore = createMemo(() => {
+ const id = params.id
+ if (!id) return false
+ return sync.session.history.more(id)
+ })
+ const historyLoading = createMemo(() => {
+ const id = params.id
+ if (!id) return false
+ return sync.session.history.loading(id)
+ })
const emptyUserMessages: UserMessage[] = []
const userMessages = createMemo(() => messages().filter((m) => m.role === "user") as UserMessage[], emptyUserMessages)
const visibleUserMessages = createMemo(() => {
@@ -290,6 +344,12 @@ export default function Page() {
}
const diffs = createMemo(() => (params.id ? (sync.data.session_diff[params.id] ?? []) : []))
+ const diffsReady = createMemo(() => {
+ const id = params.id
+ if (!id) return true
+ if (!hasReview()) return true
+ return sync.data.session_diff[id] !== undefined
+ })
const idle = { type: "idle" as const }
let inputRef!: HTMLDivElement
@@ -643,12 +703,10 @@ export default function Page() {
.filter((tab) => tab !== "context"),
)
- const reviewTab = createMemo(() => diffs().length > 0 || tabs().active() === "review")
- const mobileReview = createMemo(() => !isDesktop() && diffs().length > 0 && store.mobileTab === "review")
+ const reviewTab = createMemo(() => hasReview() || tabs().active() === "review")
+ const mobileReview = createMemo(() => !isDesktop() && hasReview() && store.mobileTab === "review")
- const showTabs = createMemo(
- () => layout.review.opened() && (diffs().length > 0 || tabs().all().length > 0 || contextOpen()),
- )
+ const showTabs = createMemo(() => layout.review.opened() && (hasReview() || tabs().all().length > 0 || contextOpen()))
const activeTab = createMemo(() => {
const active = tabs().active()
@@ -664,10 +722,22 @@ export default function Page() {
createEffect(() => {
if (!layout.ready()) return
if (tabs().active()) return
- if (diffs().length === 0 && openedTabs().length === 0 && !contextOpen()) return
+ if (!hasReview() && openedTabs().length === 0 && !contextOpen()) return
tabs().setActive(activeTab())
})
+ createEffect(() => {
+ const id = params.id
+ if (!id) return
+ if (!hasReview()) return
+
+ const wants = isDesktop() ? layout.review.opened() && activeTab() === "review" : store.mobileTab === "review"
+ if (!wants) return
+ if (diffsReady()) return
+
+ sync.session.diff(id)
+ })
+
const isWorking = createMemo(() => status().type !== "idle")
const autoScroll = createAutoScroll({
working: isWorking,
@@ -779,7 +849,7 @@ export default function Page() {