Compare commits

...
Author SHA1 Message Date
James Long 3d8e1ecafc feat(tui): clarify theme mode toggle 2026-07-16 19:02:51 +00:00
James Long 4885c3a5d3 test(tui): add theme screenshot gallery 2026-07-16 18:54:39 +00:00
104 changed files with 1714 additions and 2 deletions
@@ -52,7 +52,7 @@ function Mcp(props: { context: Plugin.Context }) {
function View(props: { context: Plugin.Context }) {
const { themeV2, mode, setMode } = useTheme()
const dimensions = useTerminalDimensions()
const modeLabel = createMemo(() => (mode() === "dark" ? "Light mode" : "Dark mode"))
const modeLabel = createMemo(() => (mode() === "dark" ? "Switch to light" : "Switch to dark"))
const mcpWidth = createMemo(() => {
const list = props.context.data.location.mcp.server.list(props.context.location) ?? []
if (list.length === 0) return 0
+37
View File
@@ -0,0 +1,37 @@
# TUI theme screenshot gallery
This gallery runs one repeatable OpenCode Drive workflow for every V1 theme in a directory. Each theme runs in
light and dark mode and produces a flat set of screenshots covering the home screen, markdown, permission prompt,
form prompt, and session switcher.
It expects `opencode-drive` to be installed globally and available on `PATH`.
The checked-in fixtures include selected built-in OpenCode themes and community themes from
[`vaprdev/opencode-themes`](https://github.com/vaprdev/opencode-themes). Theme-specific attribution and licenses
remain documented in that source repository.
## Run
Place theme JSON files in `themes/`, then run from the repository root:
```sh
bun packages/tui/test/theme/gallery/run.ts
```
Custom input and output directories can be passed as positional arguments:
```sh
bun packages/tui/test/theme/gallery/run.ts ./my-themes ./theme-screenshots
```
Output files are named `<theme>-<mode>-<state>.png` in one flat directory. Existing files with the same names are
overwritten. Runs are sequential so each isolated OpenCode Drive instance owns its own ports and artifacts.
The current TUI discovers V1 theme files and migrates them to V2 at runtime. Native V2 JSON files are reported as
unsupported and make the runner exit nonzero; they are not converted or silently rendered with a fallback theme.
Before changing the scenario, typecheck it with:
```sh
opencode-drive check packages/tui/test/theme/gallery/scenario.ts
```
+79
View File
@@ -0,0 +1,79 @@
import path from "node:path"
import { mkdir } from "node:fs/promises"
const root = path.resolve(import.meta.dir, "../../../../..")
const themes = path.resolve(Bun.argv[2] ?? path.join(import.meta.dir, "themes"))
const screenshots = path.resolve(Bun.argv[3] ?? path.join(import.meta.dir, "screenshots"))
const scenario = path.join(import.meta.dir, "scenario.ts")
const files = await Array.fromAsync(new Bun.Glob("**/*.json").scan({ cwd: themes, absolute: true }))
if (!files.length) {
console.error(`No JSON themes found in ${themes}`)
process.exit(1)
}
await mkdir(screenshots, { recursive: true })
const failures: string[] = []
const slugs = new Set<string>()
for (const file of files.sort()) {
const source = await Bun.file(file).json().catch(() => undefined)
const slug = path.basename(file, ".json").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")
if (!slug) {
failures.push(`${file}: filename does not produce a usable theme prefix`)
continue
}
if (slugs.has(slug)) {
failures.push(`${file}: duplicate theme prefix "${slug}"`)
continue
}
slugs.add(slug)
if (!isRecord(source)) {
failures.push(`${file}: invalid JSON theme object`)
continue
}
if (source.version === 2) {
failures.push(`${file}: native V2 theme loading is not supported by the TUI yet`)
continue
}
if (!isRecord(source.theme)) {
failures.push(`${file}: expected a V1 theme with a "theme" object`)
continue
}
for (const mode of ["light", "dark"] as const) {
const name = `theme-gallery-${slug}-${mode}-${process.pid}`
console.log(`\n[${slug}/${mode}] capturing screenshots`)
const child = Bun.spawn(
["opencode-drive", "start", "--name", name, "--script", scenario, "--dev", root],
{
cwd: root,
env: {
...process.env,
OPENCODE_THEME_GALLERY_FILE: file,
OPENCODE_THEME_GALLERY_MODE: mode,
OPENCODE_THEME_GALLERY_OUTPUT: screenshots,
OPENCODE_THEME_GALLERY_SLUG: slug,
},
stdout: "inherit",
stderr: "inherit",
},
)
const exit = await child.exited
if (exit !== 0) failures.push(`${file} (${mode}): OpenCode Drive exited with ${exit}`)
}
}
if (failures.length) {
console.error("\nTheme gallery completed with errors:")
failures.forEach((failure) => console.error(`- ${failure}`))
process.exit(1)
}
console.log(`\nScreenshots written to ${screenshots}`)
function isRecord(value: unknown): value is Record<string, unknown> {
return !!value && typeof value === "object" && !Array.isArray(value)
}
+123
View File
@@ -0,0 +1,123 @@
import path from "node:path"
import { mkdir } from "node:fs/promises"
import { defineScript, wait } from "opencode-drive"
const file = required("OPENCODE_THEME_GALLERY_FILE")
const mode = required("OPENCODE_THEME_GALLERY_MODE")
const output = required("OPENCODE_THEME_GALLERY_OUTPUT")
const slug = required("OPENCODE_THEME_GALLERY_SLUG")
const themeName = `gallery-${slug}`
export default defineScript({
async setup({ fs, config }) {
await fs.writeFile("README.md", "# Theme gallery fixture\n\nA stable project for OpenCode TUI screenshots.\n")
await fs.writeFile("src/example.ts", "export const palette = ['neutral', 'accent', 'interactive']\n")
await fs.writeFile(`.opencode/themes/${themeName}.json`, await Bun.file(file).text())
await fs.writeFile(
".opencode/cli.json",
`${JSON.stringify({
theme: { name: themeName, mode },
}, undefined, 2)}\n`,
)
config.permissions = [
{ action: "*", resource: "*", effect: "allow" },
{ action: "shell", resource: "*", effect: "ask" },
]
},
async run({ llm, ui }) {
await mkdir(output, { recursive: true })
llm.title((_request, index) => (index === 0 ? "Theme gallery" : `Theme gallery ${index + 1}`))
await ui.waitFor((state) => state.focused.editor)
await ui.waitFor("local")
await wait(750)
await capture(ui, "01-home")
await ui.submit("Show me a compact markdown theme specimen")
await llm.send(
llm.reasoning("I will provide a stable markdown sample for the theme gallery."),
llm.text(
[
"# Theme Review",
"",
"A compact response with **strong text**, *emphasis*, and an `inline token`.",
"",
"> Good themes preserve hierarchy without losing contrast.",
"",
"- Neutral surfaces",
"- Interactive accents",
"- Success, warning, and error feedback",
"",
"```ts",
"const mode = 'gallery'",
"```",
].join("\n"),
),
)
await ui.waitFor("Theme Review")
await capture(ui, "02-markdown")
const permission = llm.send(
llm.toolCall({
id: "theme-gallery-permission",
index: 0,
name: "shell",
input: { command: "printf 'theme gallery permission'" },
}),
)
await ui.submit("Run a protected command so I can inspect the permission prompt")
await ui.waitFor("Permission required")
await capture(ui, "03-permission")
await ui.enter()
await permission
await llm.send(llm.text("The protected command completed successfully."))
await ui.waitFor("completed successfully")
const form = llm.send(
llm.toolCall({
id: "theme-gallery-question",
index: 0,
name: "question",
input: {
questions: [
{
header: "Theme direction",
question: "Which direction should this theme emphasize?",
options: [
{ label: "Balanced", description: "Keep surfaces and accents evenly weighted" },
{ label: "Expressive", description: "Give accent colors more visual presence" },
{ label: "Quiet", description: "Favor neutral surfaces and restrained contrast" },
],
},
],
},
}),
)
await ui.submit("Ask me for a theme direction")
await ui.waitFor("Which direction should this theme emphasize?")
await capture(ui, "04-form")
await ui.enter()
await form
await llm.send(llm.text("The theme review form was submitted."))
await ui.waitFor("form was submitted")
await ui.submit("/new")
await ui.waitFor((state) => state.focused.editor)
await ui.submit("Create a second session for the session switcher gallery")
await llm.send(llm.text("This second session makes the switcher state visible."))
await ui.submit("/sessions")
await ui.waitFor("Sessions")
await capture(ui, "05-session-switcher")
},
})
async function capture(ui: { screenshot(name?: string): Promise<string> }, state: string) {
const source = await ui.screenshot(`${slug}-${mode}-${state}`)
await Bun.write(path.join(output, `${slug}-${mode}-${state}.png`), Bun.file(source))
}
function required(name: string) {
const value = process.env[name]
if (!value) throw new Error(`${name} is required`)
return value
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

@@ -0,0 +1,79 @@
{
"$schema": "https://opencode.ai/theme.json",
"defs": {
"bg": "#2e1a47",
"panel": "#371f54",
"element": "#4a2b7a",
"borderSubtle": "#553183",
"border": "#683b9f",
"text": "#ffffff",
"textSoft": "#e0c3fc",
"textMuted": "#b39ddb",
"pink": "#ec4899",
"pinkBright": "#f472b6",
"purple": "#a78bfa",
"cyan": "#22d3ee",
"green": "#34d399",
"greenBright": "#6ee7b7",
"yellow": "#facc15",
"red": "#fb7185",
"addedBg": "#2f294e",
"removedBg": "#3f214c",
"contextBg": "#371f54",
"addedLineBg": "#304052",
"removedLineBg": "#54294f"
},
"theme": {
"primary": "pink",
"secondary": "cyan",
"accent": "pinkBright",
"error": "red",
"warning": "yellow",
"success": "green",
"info": "cyan",
"text": "text",
"textMuted": "textMuted",
"background": "bg",
"backgroundPanel": "panel",
"backgroundElement": "element",
"backgroundMenu": "element",
"border": "border",
"borderActive": "pink",
"borderSubtle": "borderSubtle",
"diffAdded": "green",
"diffRemoved": "red",
"diffContext": "textMuted",
"diffHunkHeader": "pink",
"diffHighlightAdded": "greenBright",
"diffHighlightRemoved": "pinkBright",
"diffAddedBg": "addedBg",
"diffRemovedBg": "removedBg",
"diffContextBg": "contextBg",
"diffLineNumber": "textMuted",
"diffAddedLineNumberBg": "addedLineBg",
"diffRemovedLineNumberBg": "removedLineBg",
"markdownText": "text",
"markdownHeading": "pink",
"markdownLink": "cyan",
"markdownLinkText": "pinkBright",
"markdownCode": "green",
"markdownBlockQuote": "purple",
"markdownEmph": "pinkBright",
"markdownStrong": "text",
"markdownHorizontalRule": "border",
"markdownListItem": "pink",
"markdownListEnumeration": "cyan",
"markdownImage": "cyan",
"markdownImageText": "pinkBright",
"markdownCodeBlock": "textSoft",
"syntaxComment": "textMuted",
"syntaxKeyword": "pink",
"syntaxFunction": "cyan",
"syntaxVariable": "text",
"syntaxString": "green",
"syntaxNumber": "yellow",
"syntaxType": "pinkBright",
"syntaxOperator": "textSoft",
"syntaxPunctuation": "textMuted"
}
}
@@ -0,0 +1,80 @@
{
"$schema": "https://opencode.ai/theme.json",
"defs": {
"bg": "#fdf2f8",
"panel": "#fce7f3",
"element": "#fbcfe8",
"menu": "#ffffff",
"borderSubtle": "#fbcfe8",
"border": "#f9a8d4",
"text": "#831843",
"textSoft": "#9d174d",
"textMuted": "#be185d",
"pink": "#db2777",
"pinkBright": "#be185d",
"purple": "#7c3aed",
"cyan": "#0891b2",
"green": "#059669",
"greenBright": "#047857",
"yellow": "#d97706",
"red": "#e11d48",
"addedBg": "#e0f2e8",
"removedBg": "#fce1e8",
"contextBg": "#fce7f3",
"addedLineBg": "#c9ead9",
"removedLineBg": "#f8cbd7"
},
"theme": {
"primary": "pink",
"secondary": "cyan",
"accent": "pinkBright",
"error": "red",
"warning": "yellow",
"success": "green",
"info": "cyan",
"text": "text",
"textMuted": "textMuted",
"background": "bg",
"backgroundPanel": "panel",
"backgroundElement": "element",
"backgroundMenu": "menu",
"border": "border",
"borderActive": "pink",
"borderSubtle": "borderSubtle",
"diffAdded": "green",
"diffRemoved": "red",
"diffContext": "textMuted",
"diffHunkHeader": "pink",
"diffHighlightAdded": "greenBright",
"diffHighlightRemoved": "pinkBright",
"diffAddedBg": "addedBg",
"diffRemovedBg": "removedBg",
"diffContextBg": "contextBg",
"diffLineNumber": "textMuted",
"diffAddedLineNumberBg": "addedLineBg",
"diffRemovedLineNumberBg": "removedLineBg",
"markdownText": "text",
"markdownHeading": "pink",
"markdownLink": "cyan",
"markdownLinkText": "pinkBright",
"markdownCode": "green",
"markdownBlockQuote": "purple",
"markdownEmph": "pinkBright",
"markdownStrong": "text",
"markdownHorizontalRule": "border",
"markdownListItem": "pink",
"markdownListEnumeration": "cyan",
"markdownImage": "cyan",
"markdownImageText": "pinkBright",
"markdownCodeBlock": "textSoft",
"syntaxComment": "textMuted",
"syntaxKeyword": "pink",
"syntaxFunction": "cyan",
"syntaxVariable": "text",
"syntaxString": "green",
"syntaxNumber": "yellow",
"syntaxType": "pinkBright",
"syntaxOperator": "textSoft",
"syntaxPunctuation": "textMuted"
}
}
@@ -0,0 +1,75 @@
{
"$schema": "https://opencode.ai/theme.json",
"defs": {
"bg": "#1C2427",
"panel": "#273136",
"element": "#323E45",
"borderSubtle": "#304E37",
"text": "#D1DED3",
"textMuted": "#9AAEA7",
"commentMuted": "#60777D",
"green": "#7EB08A",
"tan": "#D2B48C",
"pink": "#FF819F",
"purple": "#BA8EAF",
"blue": "#7EA4B0",
"violet": "#8A7EB0",
"addedBg": "#2B3D33",
"removedBg": "#4D3740",
"contextBg": "#222A2E",
"addedLineBg": "#3A5244",
"removedLineBg": "#60414B"
},
"theme": {
"primary": "green",
"secondary": "tan",
"accent": "pink",
"error": "pink",
"warning": "purple",
"success": "green",
"info": "tan",
"text": "text",
"textMuted": "textMuted",
"background": "bg",
"backgroundPanel": "panel",
"backgroundElement": "element",
"border": "element",
"borderActive": "green",
"borderSubtle": "borderSubtle",
"diffAdded": "green",
"diffRemoved": "pink",
"diffContext": "textMuted",
"diffHunkHeader": "tan",
"diffHighlightAdded": "green",
"diffHighlightRemoved": "pink",
"diffAddedBg": "addedBg",
"diffRemovedBg": "removedBg",
"diffContextBg": "contextBg",
"diffLineNumber": "blue",
"diffAddedLineNumberBg": "addedLineBg",
"diffRemovedLineNumberBg": "removedLineBg",
"markdownText": "text",
"markdownHeading": "tan",
"markdownLink": "tan",
"markdownLinkText": "green",
"markdownCode": "tan",
"markdownBlockQuote": "blue",
"markdownEmph": "text",
"markdownStrong": "text",
"markdownHorizontalRule": "tan",
"markdownListItem": "tan",
"markdownListEnumeration": "tan",
"markdownImage": "tan",
"markdownImageText": "pink",
"markdownCodeBlock": "green",
"syntaxComment": "commentMuted",
"syntaxKeyword": "tan",
"syntaxFunction": "green",
"syntaxVariable": "text",
"syntaxString": "purple",
"syntaxNumber": "blue",
"syntaxType": "blue",
"syntaxOperator": "tan",
"syntaxPunctuation": "blue"
}
}
@@ -0,0 +1,221 @@
{
"$schema": "https://opencode.ai/theme.json",
"defs": {
"nightOwlBg": "#011627",
"nightOwlFg": "#d6deeb",
"nightOwlBlue": "#82AAFF",
"nightOwlCyan": "#7fdbca",
"nightOwlGreen": "#c5e478",
"nightOwlYellow": "#ecc48d",
"nightOwlOrange": "#F78C6C",
"nightOwlRed": "#EF5350",
"nightOwlPink": "#ff5874",
"nightOwlPurple": "#c792ea",
"nightOwlMuted": "#5f7e97",
"nightOwlGray": "#637777",
"nightOwlLightGray": "#89a4bb",
"nightOwlPanel": "#0b253a"
},
"theme": {
"primary": {
"dark": "nightOwlBlue",
"light": "nightOwlBlue"
},
"secondary": {
"dark": "nightOwlCyan",
"light": "nightOwlCyan"
},
"accent": {
"dark": "nightOwlPurple",
"light": "nightOwlPurple"
},
"error": {
"dark": "nightOwlRed",
"light": "nightOwlRed"
},
"warning": {
"dark": "nightOwlYellow",
"light": "nightOwlYellow"
},
"success": {
"dark": "nightOwlGreen",
"light": "nightOwlGreen"
},
"info": {
"dark": "nightOwlBlue",
"light": "nightOwlBlue"
},
"text": {
"dark": "nightOwlFg",
"light": "nightOwlFg"
},
"textMuted": {
"dark": "nightOwlMuted",
"light": "nightOwlMuted"
},
"background": {
"dark": "nightOwlBg",
"light": "nightOwlBg"
},
"backgroundPanel": {
"dark": "nightOwlPanel",
"light": "nightOwlPanel"
},
"backgroundElement": {
"dark": "nightOwlPanel",
"light": "nightOwlPanel"
},
"border": {
"dark": "nightOwlMuted",
"light": "nightOwlMuted"
},
"borderActive": {
"dark": "nightOwlBlue",
"light": "nightOwlBlue"
},
"borderSubtle": {
"dark": "nightOwlMuted",
"light": "nightOwlMuted"
},
"diffAdded": {
"dark": "nightOwlGreen",
"light": "nightOwlGreen"
},
"diffRemoved": {
"dark": "nightOwlRed",
"light": "nightOwlRed"
},
"diffContext": {
"dark": "nightOwlMuted",
"light": "nightOwlMuted"
},
"diffHunkHeader": {
"dark": "nightOwlMuted",
"light": "nightOwlMuted"
},
"diffHighlightAdded": {
"dark": "nightOwlGreen",
"light": "nightOwlGreen"
},
"diffHighlightRemoved": {
"dark": "nightOwlRed",
"light": "nightOwlRed"
},
"diffAddedBg": {
"dark": "#0a2e1a",
"light": "#0a2e1a"
},
"diffRemovedBg": {
"dark": "#2d1b1b",
"light": "#2d1b1b"
},
"diffContextBg": {
"dark": "nightOwlPanel",
"light": "nightOwlPanel"
},
"diffLineNumber": {
"dark": "#7791a6",
"light": "#7791a6"
},
"diffAddedLineNumberBg": {
"dark": "#0a2e1a",
"light": "#0a2e1a"
},
"diffRemovedLineNumberBg": {
"dark": "#2d1b1b",
"light": "#2d1b1b"
},
"markdownText": {
"dark": "nightOwlFg",
"light": "nightOwlFg"
},
"markdownHeading": {
"dark": "nightOwlBlue",
"light": "nightOwlBlue"
},
"markdownLink": {
"dark": "nightOwlCyan",
"light": "nightOwlCyan"
},
"markdownLinkText": {
"dark": "nightOwlBlue",
"light": "nightOwlBlue"
},
"markdownCode": {
"dark": "nightOwlGreen",
"light": "nightOwlGreen"
},
"markdownBlockQuote": {
"dark": "nightOwlMuted",
"light": "nightOwlMuted"
},
"markdownEmph": {
"dark": "nightOwlPurple",
"light": "nightOwlPurple"
},
"markdownStrong": {
"dark": "nightOwlYellow",
"light": "nightOwlYellow"
},
"markdownHorizontalRule": {
"dark": "nightOwlMuted",
"light": "nightOwlMuted"
},
"markdownListItem": {
"dark": "nightOwlBlue",
"light": "nightOwlBlue"
},
"markdownListEnumeration": {
"dark": "nightOwlCyan",
"light": "nightOwlCyan"
},
"markdownImage": {
"dark": "nightOwlCyan",
"light": "nightOwlCyan"
},
"markdownImageText": {
"dark": "nightOwlBlue",
"light": "nightOwlBlue"
},
"markdownCodeBlock": {
"dark": "nightOwlFg",
"light": "nightOwlFg"
},
"syntaxComment": {
"dark": "nightOwlGray",
"light": "nightOwlGray"
},
"syntaxKeyword": {
"dark": "nightOwlPurple",
"light": "nightOwlPurple"
},
"syntaxFunction": {
"dark": "nightOwlBlue",
"light": "nightOwlBlue"
},
"syntaxVariable": {
"dark": "nightOwlFg",
"light": "nightOwlFg"
},
"syntaxString": {
"dark": "nightOwlYellow",
"light": "nightOwlYellow"
},
"syntaxNumber": {
"dark": "nightOwlOrange",
"light": "nightOwlOrange"
},
"syntaxType": {
"dark": "nightOwlGreen",
"light": "nightOwlGreen"
},
"syntaxOperator": {
"dark": "nightOwlCyan",
"light": "nightOwlCyan"
},
"syntaxPunctuation": {
"dark": "nightOwlFg",
"light": "nightOwlFg"
}
}
}
@@ -0,0 +1,245 @@
{
"$schema": "https://opencode.ai/theme.json",
"defs": {
"darkStep1": "#0a0a0a",
"darkStep2": "#141414",
"darkStep3": "#1e1e1e",
"darkStep4": "#282828",
"darkStep5": "#323232",
"darkStep6": "#3c3c3c",
"darkStep7": "#484848",
"darkStep8": "#606060",
"darkStep9": "#fab283",
"darkStep10": "#ffc09f",
"darkStep11": "#808080",
"darkStep12": "#eeeeee",
"darkSecondary": "#5c9cf5",
"darkAccent": "#9d7cd8",
"darkRed": "#e06c75",
"darkOrange": "#f5a742",
"darkGreen": "#7fd88f",
"darkCyan": "#56b6c2",
"darkYellow": "#e5c07b",
"lightStep1": "#ffffff",
"lightStep2": "#fafafa",
"lightStep3": "#f5f5f5",
"lightStep4": "#ebebeb",
"lightStep5": "#e1e1e1",
"lightStep6": "#d4d4d4",
"lightStep7": "#b8b8b8",
"lightStep8": "#a0a0a0",
"lightStep9": "#3b7dd8",
"lightStep10": "#2968c3",
"lightStep11": "#8a8a8a",
"lightStep12": "#1a1a1a",
"lightSecondary": "#7b5bb6",
"lightAccent": "#d68c27",
"lightRed": "#d1383d",
"lightOrange": "#d68c27",
"lightGreen": "#3d9a57",
"lightCyan": "#318795",
"lightYellow": "#b0851f"
},
"theme": {
"primary": {
"dark": "darkStep9",
"light": "lightStep9"
},
"secondary": {
"dark": "darkSecondary",
"light": "lightSecondary"
},
"accent": {
"dark": "darkAccent",
"light": "lightAccent"
},
"error": {
"dark": "darkRed",
"light": "lightRed"
},
"warning": {
"dark": "darkOrange",
"light": "lightOrange"
},
"success": {
"dark": "darkGreen",
"light": "lightGreen"
},
"info": {
"dark": "darkCyan",
"light": "lightCyan"
},
"text": {
"dark": "darkStep12",
"light": "lightStep12"
},
"textMuted": {
"dark": "darkStep11",
"light": "lightStep11"
},
"background": {
"dark": "darkStep1",
"light": "lightStep1"
},
"backgroundPanel": {
"dark": "darkStep2",
"light": "lightStep2"
},
"backgroundElement": {
"dark": "darkStep3",
"light": "lightStep3"
},
"border": {
"dark": "darkStep7",
"light": "lightStep7"
},
"borderActive": {
"dark": "darkStep8",
"light": "lightStep8"
},
"borderSubtle": {
"dark": "darkStep6",
"light": "lightStep6"
},
"diffAdded": {
"dark": "#4fd6be",
"light": "#1e725c"
},
"diffRemoved": {
"dark": "#c53b53",
"light": "#c53b53"
},
"diffContext": {
"dark": "#828bb8",
"light": "#7086b5"
},
"diffHunkHeader": {
"dark": "#828bb8",
"light": "#7086b5"
},
"diffHighlightAdded": {
"dark": "#b8db87",
"light": "#4db380"
},
"diffHighlightRemoved": {
"dark": "#e26a75",
"light": "#f52a65"
},
"diffAddedBg": {
"dark": "#20303b",
"light": "#d5e5d5"
},
"diffRemovedBg": {
"dark": "#37222c",
"light": "#f7d8db"
},
"diffContextBg": {
"dark": "darkStep2",
"light": "lightStep2"
},
"diffLineNumber": {
"dark": "#8f8f8f",
"light": "#595959"
},
"diffAddedLineNumberBg": {
"dark": "#1b2b34",
"light": "#c5d5c5"
},
"diffRemovedLineNumberBg": {
"dark": "#2d1f26",
"light": "#e7c8cb"
},
"markdownText": {
"dark": "darkStep12",
"light": "lightStep12"
},
"markdownHeading": {
"dark": "darkAccent",
"light": "lightAccent"
},
"markdownLink": {
"dark": "darkStep9",
"light": "lightStep9"
},
"markdownLinkText": {
"dark": "darkCyan",
"light": "lightCyan"
},
"markdownCode": {
"dark": "darkGreen",
"light": "lightGreen"
},
"markdownBlockQuote": {
"dark": "darkYellow",
"light": "lightYellow"
},
"markdownEmph": {
"dark": "darkYellow",
"light": "lightYellow"
},
"markdownStrong": {
"dark": "darkOrange",
"light": "lightOrange"
},
"markdownHorizontalRule": {
"dark": "darkStep11",
"light": "lightStep11"
},
"markdownListItem": {
"dark": "darkStep9",
"light": "lightStep9"
},
"markdownListEnumeration": {
"dark": "darkCyan",
"light": "lightCyan"
},
"markdownImage": {
"dark": "darkStep9",
"light": "lightStep9"
},
"markdownImageText": {
"dark": "darkCyan",
"light": "lightCyan"
},
"markdownCodeBlock": {
"dark": "darkStep12",
"light": "lightStep12"
},
"syntaxComment": {
"dark": "darkStep11",
"light": "lightStep11"
},
"syntaxKeyword": {
"dark": "darkAccent",
"light": "lightAccent"
},
"syntaxFunction": {
"dark": "darkStep9",
"light": "lightStep9"
},
"syntaxVariable": {
"dark": "darkRed",
"light": "lightRed"
},
"syntaxString": {
"dark": "darkGreen",
"light": "lightGreen"
},
"syntaxNumber": {
"dark": "darkOrange",
"light": "lightOrange"
},
"syntaxType": {
"dark": "darkYellow",
"light": "lightYellow"
},
"syntaxOperator": {
"dark": "darkCyan",
"light": "lightCyan"
},
"syntaxPunctuation": {
"dark": "darkStep12",
"light": "lightStep12"
}
}
}
@@ -0,0 +1,78 @@
{
"$schema": "https://opencode.ai/theme.json",
"defs": {
"bg": "#1b1e28",
"panel": "#202430",
"element": "#303340",
"borderSubtle": "#252834",
"text": "#a6accd",
"textMuted": "#767c9d",
"offWhite": "#e4f0fb",
"blue": "#ADD7FF",
"cyan": "#89ddff",
"mint": "#5DE4c7",
"green": "#5fb3a1",
"typeBlue": "#91B4D5",
"slate": "#7390AA",
"red": "#d0679d",
"pink": "#f087bd",
"yellow": "#fffac2",
"addedBg": "#233037",
"removedBg": "#332631",
"contextBg": "#1e212c",
"addedLineBg": "#2b3c40",
"removedLineBg": "#422938"
},
"theme": {
"primary": "blue",
"secondary": "mint",
"accent": "cyan",
"error": "red",
"warning": "yellow",
"success": "mint",
"info": "cyan",
"text": "text",
"textMuted": "textMuted",
"background": "bg",
"backgroundPanel": "panel",
"backgroundElement": "element",
"border": "element",
"borderActive": "blue",
"borderSubtle": "borderSubtle",
"diffAdded": "green",
"diffRemoved": "red",
"diffContext": "textMuted",
"diffHunkHeader": "cyan",
"diffHighlightAdded": "mint",
"diffHighlightRemoved": "pink",
"diffAddedBg": "addedBg",
"diffRemovedBg": "removedBg",
"diffContextBg": "contextBg",
"diffLineNumber": "slate",
"diffAddedLineNumberBg": "addedLineBg",
"diffRemovedLineNumberBg": "removedLineBg",
"markdownText": "offWhite",
"markdownHeading": "offWhite",
"markdownLink": "blue",
"markdownLinkText": "mint",
"markdownCode": "typeBlue",
"markdownBlockQuote": "mint",
"markdownEmph": "slate",
"markdownStrong": "offWhite",
"markdownHorizontalRule": "textMuted",
"markdownListItem": "blue",
"markdownListEnumeration": "blue",
"markdownImage": "blue",
"markdownImageText": "mint",
"markdownCodeBlock": "blue",
"syntaxComment": "textMuted",
"syntaxKeyword": "text",
"syntaxFunction": "blue",
"syntaxVariable": "offWhite",
"syntaxString": "mint",
"syntaxNumber": "mint",
"syntaxType": "typeBlue",
"syntaxOperator": "typeBlue",
"syntaxPunctuation": "text"
}
}

Some files were not shown because too many files have changed in this diff Show More