Skip to content

Commit de69f28

Browse files
committed
Merge branch 'dev' into feat/markdown-frontmatter-interpolation
2 parents 92e0dfa + c4e98c0 commit de69f28

File tree

7 files changed

+166
-6
lines changed

7 files changed

+166
-6
lines changed

packages/desktop/src/pages/session.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { useSession, type LocalPTY } from "@/context/session"
3232
import { useLayout } from "@/context/layout"
3333
import { getDirectory, getFilename } from "@opencode-ai/util/path"
3434
import { Terminal } from "@/components/terminal"
35-
import { checksum } from "@opencode-ai/util/encode"
35+
3636

3737
export default function Page() {
3838
const layout = useLayout()
@@ -540,7 +540,6 @@ export default function Page() {
540540
file={{
541541
name: f().path,
542542
contents: f().content?.content ?? "",
543-
cacheKey: checksum(f().content?.content ?? ""),
544543
}}
545544
overflow="scroll"
546545
class="pb-40"

packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ export function DialogSessionList() {
2424

2525
const options = createMemo(() => {
2626
const today = new Date().toDateString()
27+
const sessionsListLimit = (sync.data.config.tui as any)?.session_list_limit
28+
const limit = sessionsListLimit === "none" ? undefined : sessionsListLimit || 150
29+
2730
return sync.data.session
2831
.filter((x) => x.parentID === undefined)
2932
.toSorted((a, b) => b.time.updated - a.time.updated)
@@ -42,7 +45,7 @@ export function DialogSessionList() {
4245
footer: Locale.time(x.time.updated),
4346
}
4447
})
45-
.slice(0, 150)
48+
.slice(0, limit)
4649
})
4750

4851
createEffect(() => {

packages/opencode/src/cli/cmd/tui/context/sync.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
187187
event.properties.info.sessionID,
188188
produce((draft) => {
189189
draft.splice(result.index, 0, event.properties.info)
190-
if (draft.length > 100) draft.shift()
190+
const maxMessages = (store.config.tui as any)?.messages_limit
191+
const maxMessagesCount = maxMessages === "none" ? Infinity : maxMessages || 100
192+
if (draft.length > maxMessagesCount) {
193+
draft.shift()
194+
}
191195
}),
192196
)
193197
break
@@ -336,9 +340,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
336340
},
337341
async sync(sessionID: string) {
338342
if (fullSyncedSessions.has(sessionID)) return
343+
const messagesLimit = (store.config.tui as any)?.messages_limit
344+
const limit = messagesLimit === "none" ? undefined : messagesLimit || 100
339345
const [session, messages, todo, diff] = await Promise.all([
340346
sdk.client.session.get({ sessionID }, { throwOnError: true }),
341-
sdk.client.session.messages({ sessionID, limit: 100 }),
347+
sdk.client.session.messages({ sessionID, limit }),
342348
sdk.client.session.todo({ sessionID }),
343349
sdk.client.session.diff({ sessionID }),
344350
])
@@ -352,6 +358,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
352358
for (const message of messages.data!) {
353359
draft.part[message.info.id] = message.parts
354360
}
361+
355362
draft.session_diff[sessionID] = diff.data ?? []
356363
}),
357364
)

packages/opencode/src/config/config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,18 @@ export namespace Config {
490490
.enum(["auto", "stacked"])
491491
.optional()
492492
.describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"),
493+
session_list_limit: z
494+
.union([z.number().min(1), z.literal("none")])
495+
.optional()
496+
.default(150)
497+
.describe("Maximum number of sessions to display in session list, or 'none' to show all sessions"),
498+
messages_limit: z
499+
.union([z.number().min(1), z.literal("none")])
500+
.optional()
501+
.default(100)
502+
.describe("Maximum number of message parts to load per session when syncing, or 'none' to load all messages"),
493503
})
504+
export type TUI = z.infer<typeof TUI>
494505

495506
export const Layout = z.enum(["auto", "stretch"]).meta({
496507
ref: "LayoutConfig",

packages/opencode/src/session/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,11 +276,23 @@ export namespace Session {
276276
}),
277277
async (input) => {
278278
const result = [] as MessageV2.WithParts[]
279+
let totalParts = 0
280+
279281
for await (const msg of MessageV2.stream(input.sessionID)) {
280-
if (input.limit && result.length >= input.limit) break
282+
if (input.limit && totalParts + msg.parts.length > input.limit) {
283+
// If adding this message would exceed the limit, check if we can fit a partial message
284+
if (totalParts < input.limit) {
285+
// We have room for some parts of this message, but this would be complex to implement
286+
// For now, just break to stay within the limit
287+
break
288+
}
289+
break
290+
}
281291
result.push(msg)
292+
totalParts += msg.parts.length
282293
}
283294
result.reverse()
295+
284296
return result
285297
},
286298
)

packages/opencode/test/config/config.test.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,3 +501,123 @@ test("deduplicates duplicate plugins from global and local configs", async () =>
501501
},
502502
})
503503
})
504+
505+
test("handles TUI configuration with session_list_limit and messages_limit", async () => {
506+
await using tmp = await tmpdir({
507+
init: async (dir) => {
508+
await Bun.write(
509+
path.join(dir, "opencode.json"),
510+
JSON.stringify({
511+
$schema: "https://opencode.ai/config.json",
512+
tui: {
513+
session_list_limit: 200,
514+
messages_limit: 50,
515+
},
516+
}),
517+
)
518+
},
519+
})
520+
await Instance.provide({
521+
directory: tmp.path,
522+
fn: async () => {
523+
const config = await Config.get()
524+
expect(config.tui?.session_list_limit).toBe(200)
525+
expect(config.tui?.messages_limit).toBe(50)
526+
},
527+
})
528+
})
529+
530+
test("handles TUI configuration with session_list_limit set to 'none'", async () => {
531+
await using tmp = await tmpdir({
532+
init: async (dir) => {
533+
await Bun.write(
534+
path.join(dir, "opencode.json"),
535+
JSON.stringify({
536+
$schema: "https://opencode.ai/config.json",
537+
tui: {
538+
session_list_limit: "none",
539+
messages_limit: 75,
540+
},
541+
}),
542+
)
543+
},
544+
})
545+
await Instance.provide({
546+
directory: tmp.path,
547+
fn: async () => {
548+
const config = await Config.get()
549+
expect(config.tui?.session_list_limit).toBe("none")
550+
expect(config.tui?.messages_limit).toBe(75)
551+
},
552+
})
553+
})
554+
555+
test("validates TUI session_list_limit schema - rejects invalid values", async () => {
556+
await using tmp = await tmpdir({
557+
init: async (dir) => {
558+
await Bun.write(
559+
path.join(dir, "opencode.json"),
560+
JSON.stringify({
561+
$schema: "https://opencode.ai/config.json",
562+
tui: {
563+
session_list_limit: -5, // Invalid: negative number
564+
},
565+
}),
566+
)
567+
},
568+
})
569+
await Instance.provide({
570+
directory: tmp.path,
571+
fn: async () => {
572+
await expect(Config.get()).rejects.toThrow()
573+
},
574+
})
575+
})
576+
577+
test("validates TUI messages_limit schema - rejects invalid values", async () => {
578+
await using tmp = await tmpdir({
579+
init: async (dir) => {
580+
await Bun.write(
581+
path.join(dir, "opencode.json"),
582+
JSON.stringify({
583+
$schema: "https://opencode.ai/config.json",
584+
tui: {
585+
messages_limit: 0, // Invalid: must be >= 1
586+
},
587+
}),
588+
)
589+
},
590+
})
591+
await Instance.provide({
592+
directory: tmp.path,
593+
fn: async () => {
594+
await expect(Config.get()).rejects.toThrow()
595+
},
596+
})
597+
})
598+
599+
test("handles partial TUI configuration with backward compatibility", async () => {
600+
await using tmp = await tmpdir({
601+
init: async (dir) => {
602+
await Bun.write(
603+
path.join(dir, "opencode.json"),
604+
JSON.stringify({
605+
$schema: "https://opencode.ai/config.json",
606+
tui: {
607+
scroll_speed: 2.5,
608+
// session_list_limit and messages_limit not specified - should inherit from global config
609+
},
610+
}),
611+
)
612+
},
613+
})
614+
await Instance.provide({
615+
directory: tmp.path,
616+
fn: async () => {
617+
const config = await Config.get()
618+
expect(config.tui?.scroll_speed).toBe(2.5)
619+
// Note: session_list_limit and messages_limit may be inherited from global config
620+
// The important thing is that the config loads successfully and scroll_speed is set correctly
621+
},
622+
})
623+
})

packages/sdk/js/src/gen/types.gen.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,6 +1183,14 @@ export type Config = {
11831183
* Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column
11841184
*/
11851185
diff_style?: "auto" | "stacked"
1186+
/**
1187+
* Maximum number of sessions to display in session list, or 'none' to show all sessions
1188+
*/
1189+
session_list_limit?: number | "none"
1190+
/**
1191+
* Maximum number of message parts to load per session when syncing, or 'none' to load all messages
1192+
*/
1193+
messages_limit?: number | "none"
11861194
}
11871195
/**
11881196
* Command configuration, see https://opencode.ai/docs/commands

0 commit comments

Comments
 (0)