diff --git a/packages/opencode/src/cli/cmd/run/scrollback.surface.ts b/packages/opencode/src/cli/cmd/run/scrollback.surface.ts index 2ec45d3cc1..9cebae92f8 100644 --- a/packages/opencode/src/cli/cmd/run/scrollback.surface.ts +++ b/packages/opencode/src/cli/cmd/run/scrollback.surface.ts @@ -182,37 +182,37 @@ export class RunScrollbackStream { const renderable = body.type === "text" ? new TextRenderable(surface.renderContext, { - id, - content: "", - width: "100%", - wrapMode: "word", - fg: look(commit, this.theme.entry).fg, - attributes: look(commit, this.theme.entry).attrs, - }) + id, + content: "", + width: "100%", + wrapMode: "word", + fg: look(commit, this.theme.entry).fg, + attributes: look(commit, this.theme.entry).attrs, + }) : body.type === "code" ? new CodeRenderable(surface.renderContext, { - id, - content: "", - filetype: body.filetype, - syntaxStyle: syntaxFor(commit, this.theme), - width: "100%", - wrapMode: "word", - drawUnstyledText: false, - streaming: true, - fg: entryColor(commit, this.theme), - treeSitterClient: this.treeSitterClient, - }) + id, + content: "", + filetype: body.filetype, + syntaxStyle: syntaxFor(commit, this.theme), + width: "100%", + wrapMode: "word", + drawUnstyledText: false, + streaming: true, + fg: entryColor(commit, this.theme), + treeSitterClient: this.treeSitterClient, + }) : new MarkdownRenderable(surface.renderContext, { - id, - content: "", - syntaxStyle: syntaxFor(commit, this.theme), - width: "100%", - streaming: true, - internalBlockMode: "top-level", - tableOptions: { widthMode: "content" }, - fg: entryColor(commit, this.theme), - treeSitterClient: this.treeSitterClient, - }) + id, + content: "", + syntaxStyle: syntaxFor(commit, this.theme), + width: "100%", + streaming: true, + internalBlockMode: "top-level", + tableOptions: { widthMode: "content" }, + fg: entryColor(commit, this.theme), + treeSitterClient: this.treeSitterClient, + }) surface.root.add(renderable) diff --git a/packages/opencode/src/cli/cmd/run/scrollback.writer.tsx b/packages/opencode/src/cli/cmd/run/scrollback.writer.tsx index 97c67a06d4..ae4e97ab9d 100644 --- a/packages/opencode/src/cli/cmd/run/scrollback.writer.tsx +++ b/packages/opencode/src/cli/cmd/run/scrollback.writer.tsx @@ -103,21 +103,6 @@ function entryColor(commit: StreamCommit, theme: RunTheme): ColorInput { return look(commit, theme.entry).fg } -function blankWriter(): ScrollbackWriter { - return (ctx) => ({ - root: new TextRenderable(ctx.renderContext, { - id: "run-scrollback-spacer", - width: Math.max(1, Math.trunc(ctx.width)), - height: 1, - content: "", - }), - width: Math.max(1, Math.trunc(ctx.width)), - height: 1, - startOnNewLine: true, - trailingNewline: true, - }) -} - function todoText(item: { status: string; content: string }): string { if (item.status === "completed") { return `[x] ${item.content}` @@ -359,5 +344,16 @@ export function entryWriter(input: { } export function spacerWriter(): ScrollbackWriter { - return blankWriter() + return (ctx) => ({ + root: new TextRenderable(ctx.renderContext, { + id: "run-scrollback-spacer", + width: Math.max(1, Math.trunc(ctx.width)), + height: 1, + content: "", + }), + width: Math.max(1, Math.trunc(ctx.width)), + height: 1, + startOnNewLine: true, + trailingNewline: true, + }) } diff --git a/packages/opencode/src/cli/cmd/run/theme.ts b/packages/opencode/src/cli/cmd/run/theme.ts index 344fb9f287..4993a04cf1 100644 --- a/packages/opencode/src/cli/cmd/run/theme.ts +++ b/packages/opencode/src/cli/cmd/run/theme.ts @@ -99,8 +99,42 @@ function fade(color: RGBA, base: RGBA, fallback: number, scale: number, limit: n ) } +function blend(color: RGBA, bg: RGBA): RGBA { + if (color.a >= 1) { + return color + } + + return RGBA.fromValues( + bg.r + (color.r - bg.r) * color.a, + bg.g + (color.g - bg.g) * color.a, + bg.b + (color.b - bg.b) * color.a, + 1, + ) +} + +export function opaqueSyntaxStyle(style: SyntaxStyle | undefined, bg: RGBA): SyntaxStyle | undefined { + if (!style) { + return + } + + return SyntaxStyle.fromStyles( + Object.fromEntries( + [...style.getAllStyles()].map(([name, value]) => [ + name, + { + ...value, + fg: value.fg ? blend(value.fg, bg) : value.fg, + bg: value.bg ? blend(value.bg, bg) : value.bg, + }, + ]), + ), + ) +} + function map(theme: TuiThemeCurrent, syntax?: SyntaxStyle, subtleSyntax?: SyntaxStyle): RunTheme { const bg = theme.background + const opaqueSubtleSyntax = opaqueSyntaxStyle(subtleSyntax, bg) + subtleSyntax?.destroy() const pane = theme.backgroundElement const shade = fade(pane, bg, 0.12, 0.56, 0.72) const surface = fade(pane, bg, 0.18, 0.76, 0.9) @@ -146,7 +180,7 @@ function map(theme: TuiThemeCurrent, syntax?: SyntaxStyle, subtleSyntax?: Syntax text: theme.text, muted: theme.textMuted, syntax, - subtleSyntax, + subtleSyntax: opaqueSubtleSyntax, diffAdded: theme.diffAdded, diffRemoved: theme.diffRemoved, diffAddedBg: theme.diffAddedBg, diff --git a/packages/opencode/test/cli/run/theme.test.ts b/packages/opencode/test/cli/run/theme.test.ts new file mode 100644 index 0000000000..f2ebbc709e --- /dev/null +++ b/packages/opencode/test/cli/run/theme.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, test } from "bun:test" +import { RGBA, SyntaxStyle } from "@opentui/core" +import { opaqueSyntaxStyle } from "../../../src/cli/cmd/run/theme" + +describe("run theme", () => { + test("flattens subtle syntax alpha against the run background", () => { + const syntax = SyntaxStyle.fromStyles({ + default: { + fg: RGBA.fromInts(169, 177, 214, 153), + }, + emphasis: { + fg: RGBA.fromInts(224, 175, 104, 153), + italic: true, + bold: true, + }, + }) + const subtle = opaqueSyntaxStyle(syntax, RGBA.fromInts(42, 43, 61)) + + try { + expect(subtle?.getStyle("default")?.fg?.toInts()).toEqual([118, 123, 153, 255]) + expect(subtle?.getStyle("emphasis")?.fg?.toInts()).toEqual([151, 122, 87, 255]) + expect(subtle?.getStyle("emphasis")?.italic).toBe(true) + expect(subtle?.getStyle("emphasis")?.bold).toBe(true) + } finally { + syntax.destroy() + subtle?.destroy() + } + }) +})