Compare commits

...

16 Commits

Author SHA1 Message Date
Eric Clemmons
18fb19da3b fix(opencode): pass missing auth headers in run --attach (#16097)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
2026-03-09 07:32:13 +00:00
opencode-agent[bot]
849e1ac543 docs(i18n): sync locale docs from english changes 2026-03-09 02:08:46 +00:00
Ariane Emory
656a8d8f55 docs: add session_child_first keybinding to documentation (#16631) 2026-03-08 21:03:52 -05:00
Adam
b976f339e8 feat(app): generate color palettes (#16232) 2026-03-08 19:28:58 -05:00
Dax Raad
7d7837e5b6 disable fallback to free nano for small model 2026-03-08 19:27:15 -04:00
opencode
1db292f4df release: v1.2.22 2026-03-08 22:34:59 +00:00
Sebastian
49a3a9fe36 guard tui exit (#16640) 2026-03-08 23:14:41 +01:00
Luke Parker
e51ed460a6 fix(tui): canonicalize cwd after chdir (#16641) 2026-03-09 07:57:48 +10:00
David Hill
d15c2ce349 tui: fix sidebar background color when collapsed
When the sidebar was collapsed (not on mobile), the background color was showing as the stronger variant instead of matching the base background. This fixes the hover state detection so users see a consistent lighter background when the sidebar is in collapsed mode.
2026-03-08 13:34:56 +00:00
David Hill
5cc4bb4089 app: suppress hover when opening project menu or right-clicking to prevent flickering 2026-03-08 13:31:18 +00:00
Shoubhit Dash
6e9e027886 fix: trim retained desktop terminal buffers (#16583) 2026-03-08 07:50:04 -05:00
opencode-agent[bot]
f9a3d129a4 chore: update nix node_modules hashes 2026-03-08 12:25:35 +00:00
Adam
c53d1d3ad8 fix(app): less auto-expand/collapse 2026-03-08 07:11:15 -05:00
Adam
f386137fba chore: refactoring ui hooks 2026-03-08 07:11:15 -05:00
Adam
c797b60069 fix(app): messages not loading reliably 2026-03-08 07:11:15 -05:00
Shoubhit Dash
a139e9297d fix: prune and evict stale app session caches (#16584) 2026-03-08 07:10:00 -05:00
101 changed files with 2482 additions and 3529 deletions

View File

@@ -26,7 +26,7 @@
},
"packages/app": {
"name": "@opencode-ai/app",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -76,7 +76,7 @@
},
"packages/console/app": {
"name": "@opencode-ai/console-app",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
@@ -110,7 +110,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -137,7 +137,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2",
@@ -161,7 +161,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -185,7 +185,7 @@
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@opencode-ai/app": "workspace:*",
"@opencode-ai/ui": "workspace:*",
@@ -218,7 +218,7 @@
},
"packages/desktop-electron": {
"name": "@opencode-ai/desktop-electron",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@opencode-ai/app": "workspace:*",
"@opencode-ai/ui": "workspace:*",
@@ -248,7 +248,7 @@
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@opencode-ai/ui": "workspace:*",
"@opencode-ai/util": "workspace:*",
@@ -277,7 +277,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
@@ -293,7 +293,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "1.2.21",
"version": "1.2.22",
"bin": {
"opencode": "./bin/opencode",
},
@@ -409,7 +409,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"zod": "catalog:",
@@ -429,7 +429,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "1.2.21",
"version": "1.2.22",
"devDependencies": {
"@hey-api/openapi-ts": "0.90.10",
"@tsconfig/node22": "catalog:",
@@ -440,7 +440,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@@ -475,7 +475,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -483,8 +483,11 @@
"@pierre/diffs": "catalog:",
"@shikijs/transformers": "3.9.2",
"@solid-primitives/bounds": "0.1.3",
"@solid-primitives/lifecycle": "0.1.2",
"@solid-primitives/media": "2.3.3",
"@solid-primitives/page-visibility": "2.1.1",
"@solid-primitives/resize-observer": "2.1.3",
"@solid-primitives/rootless": "1.5.2",
"@solidjs/meta": "catalog:",
"@solidjs/router": "catalog:",
"dompurify": "3.3.1",
@@ -521,7 +524,7 @@
},
"packages/util": {
"name": "@opencode-ai/util",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"zod": "catalog:",
},
@@ -532,7 +535,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
@@ -1834,10 +1837,14 @@
"@solid-primitives/keyed": ["@solid-primitives/keyed@1.5.3", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-zNadtyYBhJSOjXtogkGHmRxjGdz9KHc8sGGVAGlUABkE8BED2tbIZoxkwSqzOwde8OcUEH0bb5DLZUWIMvyBSA=="],
"@solid-primitives/lifecycle": ["@solid-primitives/lifecycle@0.1.2", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-+K0T10kZXqorocFj0coIqt8NYm2UqoZfpF3nm2RwrDMZMV+C+SC0Oi3N6Dnq2i7W/n1cHAnfpoV4CBLsW21lJw=="],
"@solid-primitives/map": ["@solid-primitives/map@0.4.13", "", { "dependencies": { "@solid-primitives/trigger": "^1.1.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-B1zyFbsiTQvqPr+cuPCXO72sRuczG9Swncqk5P74NCGw1VE8qa/Ry9GlfI1e/VdeQYHjan+XkbE3rO2GW/qKew=="],
"@solid-primitives/media": ["@solid-primitives/media@2.3.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-hQ4hLOGvfbugQi5Eu1BFWAIJGIAzztq9x0h02xgBGl2l0Jaa3h7tg6bz5tV1NSuNYVGio4rPoa7zVQQLkkx9dA=="],
"@solid-primitives/page-visibility": ["@solid-primitives/page-visibility@2.1.1", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.1", "@solid-primitives/rootless": "^1.5.1", "@solid-primitives/utils": "^6.3.1" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-CV9BqMqhunf4OOyBkhJCH9f5ivg0ADavdcaBsrqoFvwIk1FoD/blPSHYM4CK8IjS/AEXNcsjlNVc34lMu+2Wdg=="],
"@solid-primitives/props": ["@solid-primitives/props@3.2.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-lZOTwFJajBrshSyg14nBMEP0h8MXzPowGO0s3OeiR3z6nXHTfj0FhzDtJMv+VYoRJKQHG2QRnJTgCzK6erARAw=="],
"@solid-primitives/refs": ["@solid-primitives/refs@1.1.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-K7tf2thy7L+YJjdqXspXOg5xvNEOH8tgEWsp0+1mQk3obHBRD6hEjYZk7p7FlJphSZImS35je3UfmWuD7MhDfg=="],

View File

@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-4kjoJ06VNvHltPHfzQRBG0bC6R39jao10ffGzrNZ230=",
"aarch64-linux": "sha256-6Uio+S2rcyBWbBEeOZb9N1CCKgkbKi68lOIKi3Ws/pQ=",
"aarch64-darwin": "sha256-8ngN5KVN4vhdsk0QJ11BGgSVBrcaEbwSj23c77HBpgs=",
"x86_64-darwin": "sha256-v/ueYGb9a0Nymzy+mkO4uQr78DAuJnES1qOT0onFgnQ="
"x86_64-linux": "sha256-c99eE1cKAQHvwJosaFo42U9Hk0Rtp/U5oTTlyiz2Zw4=",
"aarch64-linux": "sha256-LbdssPrf8Bijyp4mRo8QaO/swxwUWSo1g0jLPm2rvUA=",
"aarch64-darwin": "sha256-0L9y6Zk4l2vAxsM2bENahhtRZY1C3XhdxLgnnYlhkkY=",
"x86_64-darwin": "sha256-0J5sFG/kHHRDcTpdpdPBMJEOHwCRnAUYmbxEHPPLDvU="
}
}

View File

@@ -25,16 +25,26 @@ test("closing active project navigates to another open project", async ({ page,
await clickMenuItem(menu, /^Close$/i, { force: true })
await expect
.poll(() => {
const pathname = new URL(page.url()).pathname
if (new RegExp(`^/${slug}/session(?:/[^/]+)?/?$`).test(pathname)) return "project"
if (pathname === "/") return "home"
return ""
})
.poll(
() => {
const pathname = new URL(page.url()).pathname
if (new RegExp(`^/${slug}/session(?:/[^/]+)?/?$`).test(pathname)) return "project"
if (pathname === "/") return "home"
return ""
},
{ timeout: 15_000 },
)
.toMatch(/^(project|home)$/)
await expect(page).not.toHaveURL(new RegExp(`/${otherSlug}/session(?:[/?#]|$)`))
await expect(otherButton).toHaveCount(0)
await expect
.poll(
async () => {
return await page.locator(projectSwitchSelector(otherSlug)).count()
},
{ timeout: 15_000 },
)
.toBe(0)
},
{ extra: [other] },
)

View File

@@ -83,16 +83,23 @@ test("changing theme persists in localStorage", async ({ page, gotoSession }) =>
const select = dialog.locator(settingsThemeSelector)
await expect(select).toBeVisible()
const currentThemeId = await page.evaluate(() => {
return document.documentElement.getAttribute("data-theme")
})
const currentTheme = (await select.locator('[data-slot="select-select-trigger-value"]').textContent())?.trim() ?? ""
await select.locator('[data-slot="select-select-trigger"]').click()
const items = page.locator('[data-slot="select-select-item"]')
const count = await items.count()
expect(count).toBeGreaterThan(1)
const firstTheme = await items.nth(1).locator('[data-slot="select-select-item-label"]').textContent()
expect(firstTheme).toBeTruthy()
const nextTheme = (await items.locator('[data-slot="select-select-item-label"]').allTextContents())
.map((x) => x.trim())
.find((x) => x && x !== currentTheme)
expect(nextTheme).toBeTruthy()
await items.nth(1).click()
await items.filter({ hasText: nextTheme! }).first().click()
await page.keyboard.press("Escape")
@@ -101,7 +108,7 @@ test("changing theme persists in localStorage", async ({ page, gotoSession }) =>
})
expect(storedThemeId).not.toBeNull()
expect(storedThemeId).not.toBe("oc-1")
expect(storedThemeId).not.toBe(currentThemeId)
const dataTheme = await page.evaluate(() => {
return document.documentElement.getAttribute("data-theme")

View File

@@ -44,12 +44,14 @@ async function store(page: Page, key: string) {
}, key)
}
test("terminal tab buffers persist across tab switches", async ({ page, withProject }) => {
test("inactive terminal tab buffers persist across tab switches", async ({ page, withProject }) => {
await withProject(async ({ directory, gotoSession }) => {
const key = workspacePersistKey(directory, "terminal")
const one = `E2E_TERM_ONE_${Date.now()}`
const two = `E2E_TERM_TWO_${Date.now()}`
const tabs = page.locator('#terminal-panel [data-slot="tabs-trigger"]')
const first = tabs.filter({ hasText: /Terminal 1/ }).first()
const second = tabs.filter({ hasText: /Terminal 2/ }).first()
await gotoSession()
await open(page)
@@ -61,22 +63,39 @@ test("terminal tab buffers persist across tab switches", async ({ page, withProj
await run(page, `echo ${two}`)
await tabs
.filter({ hasText: /Terminal 1/ })
.first()
.click()
await first.click()
await expect(first).toHaveAttribute("aria-selected", "true")
await expect
.poll(
async () => {
const state = await store(page, key)
const first = state?.all.find((item) => item.titleNumber === 1)?.buffer ?? ""
const second = state?.all.find((item) => item.titleNumber === 2)?.buffer ?? ""
return first.includes(one) && second.includes(two)
return {
first: first.includes(one),
second: second.includes(two),
}
},
{ timeout: 30_000 },
)
.toBe(true)
.toEqual({ first: false, second: true })
await second.click()
await expect(second).toHaveAttribute("aria-selected", "true")
await expect
.poll(
async () => {
const state = await store(page, key)
const first = state?.all.find((item) => item.titleNumber === 1)?.buffer ?? ""
const second = state?.all.find((item) => item.titleNumber === 2)?.buffer ?? ""
return {
first: first.includes(one),
second: second.includes(two),
}
},
{ timeout: 30_000 },
)
.toEqual({ first: true, second: false })
})
})

View File

@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
"version": "1.2.21",
"version": "1.2.22",
"description": "",
"type": "module",
"exports": {

View File

@@ -1,6 +1,5 @@
;(function () {
var themeId = localStorage.getItem("opencode-theme-id")
if (!themeId) return
var themeId = localStorage.getItem("opencode-theme-id") || "oc-2"
var scheme = localStorage.getItem("opencode-color-scheme") || "system"
var isDark = scheme === "dark" || (scheme === "system" && matchMedia("(prefers-color-scheme: dark)").matches)
@@ -9,9 +8,9 @@
document.documentElement.dataset.theme = themeId
document.documentElement.dataset.colorScheme = mode
if (themeId === "oc-1") return
if (themeId === "oc-2") return
var css = localStorage.getItem("opencode-theme-css-" + themeId + "-" + mode)
var css = localStorage.getItem("opencode-theme-css-" + mode)
if (css) {
var style = document.createElement("style")
style.id = "oc-theme-preload"

View File

@@ -217,7 +217,7 @@ export const Terminal = (props: TerminalProps) => {
const currentTheme = theme.themes()[theme.themeId()]
if (!currentTheme) return fallback
const variant = mode === "dark" ? currentTheme.dark : currentTheme.light
if (!variant?.seeds) return fallback
if (!variant?.seeds && !variant?.palette) return fallback
const resolved = resolveThemeVariant(variant, mode === "dark")
const text = resolved["text-stronger"] ?? fallback.foreground
const background = resolved["background-stronger"] ?? fallback.background

View File

@@ -27,7 +27,7 @@ import type { InitError } from "../pages/error"
import { useGlobalSDK } from "./global-sdk"
import { bootstrapDirectory, bootstrapGlobal } from "./global-sync/bootstrap"
import { createChildStoreManager } from "./global-sync/child-store"
import { applyDirectoryEvent, applyGlobalEvent } from "./global-sync/event-reducer"
import { applyDirectoryEvent, applyGlobalEvent, cleanupDroppedSessionCaches } from "./global-sync/event-reducer"
import { createRefreshQueue } from "./global-sync/queue"
import { estimateRootSessionTotal, loadRootSessionsWithFallback } from "./global-sync/session-load"
import { trimSessions } from "./global-sync/session-trim"
@@ -189,6 +189,7 @@ function createGlobalSync() {
})
if (next.length !== store.session.length) {
setStore("session", reconcile(next, { key: "id" }))
cleanupDroppedSessionCaches(store, setStore, next, setSessionTodo)
}
children.unpin(directory)
return
@@ -220,6 +221,7 @@ function createGlobalSync() {
}),
)
setStore("session", reconcile(sessions, { key: "id" }))
cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo)
sessionMeta.set(directory, { limit })
})
.catch((err) => {

View File

@@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test"
import type { Message, Part, PermissionRequest, Project, QuestionRequest, Session } from "@opencode-ai/sdk/v2/client"
import { createStore } from "solid-js/store"
import type { State } from "./types"
import { applyDirectoryEvent, applyGlobalEvent } from "./event-reducer"
import { applyDirectoryEvent, applyGlobalEvent, cleanupDroppedSessionCaches } from "./event-reducer"
const rootSession = (input: { id: string; parentID?: string; archived?: number }) =>
({
@@ -248,6 +248,62 @@ describe("applyDirectoryEvent", () => {
}
})
test("cleans caches for trimmed sessions on session.created", () => {
const dropped = rootSession({ id: "ses_b" })
const kept = rootSession({ id: "ses_a" })
const message = userMessage("msg_1", dropped.id)
const todos: string[] = []
const [store, setStore] = createStore(
baseState({
limit: 1,
session: [dropped],
message: { [dropped.id]: [message] },
part: { [message.id]: [textPart("prt_1", dropped.id, message.id)] },
session_diff: { [dropped.id]: [] },
todo: { [dropped.id]: [] },
permission: { [dropped.id]: [] },
question: { [dropped.id]: [] },
session_status: { [dropped.id]: { type: "busy" } },
}),
)
applyDirectoryEvent({
event: { type: "session.created", properties: { info: kept } },
store,
setStore,
push() {},
directory: "/tmp",
loadLsp() {},
setSessionTodo(sessionID, value) {
if (value !== undefined) return
todos.push(sessionID)
},
})
expect(store.session.map((x) => x.id)).toEqual([kept.id])
expect(store.message[dropped.id]).toBeUndefined()
expect(store.part[message.id]).toBeUndefined()
expect(store.session_diff[dropped.id]).toBeUndefined()
expect(store.todo[dropped.id]).toBeUndefined()
expect(store.permission[dropped.id]).toBeUndefined()
expect(store.question[dropped.id]).toBeUndefined()
expect(store.session_status[dropped.id]).toBeUndefined()
expect(todos).toEqual([dropped.id])
})
test("cleanupDroppedSessionCaches clears part-only orphan state", () => {
const [store, setStore] = createStore(
baseState({
session: [rootSession({ id: "ses_keep" })],
part: { msg_1: [textPart("prt_1", "ses_drop", "msg_1")] },
}),
)
cleanupDroppedSessionCaches(store, setStore, store.session)
expect(store.part.msg_1).toBeUndefined()
})
test("upserts and removes messages while clearing orphaned parts", () => {
const sessionID = "ses_1"
const [store, setStore] = createStore(

View File

@@ -13,6 +13,7 @@ import type {
} from "@opencode-ai/sdk/v2/client"
import type { State, VcsCache } from "./types"
import { trimSessions } from "./session-trim"
import { dropSessionCaches } from "./session-cache"
export function applyGlobalEvent(input: {
event: { type: string; properties?: unknown }
@@ -40,37 +41,44 @@ export function applyGlobalEvent(input: {
}
function cleanupSessionCaches(
store: Store<State>,
setStore: SetStoreFunction<State>,
sessionID: string,
setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void,
) {
if (!sessionID) return
const hasAny =
store.message[sessionID] !== undefined ||
store.session_diff[sessionID] !== undefined ||
store.todo[sessionID] !== undefined ||
store.permission[sessionID] !== undefined ||
store.question[sessionID] !== undefined ||
store.session_status[sessionID] !== undefined
setSessionTodo?.(sessionID, undefined)
if (!hasAny) return
setStore(
produce((draft) => {
const messages = draft.message[sessionID]
if (messages) {
for (const message of messages) {
const id = message?.id
if (!id) continue
delete draft.part[id]
}
}
delete draft.message[sessionID]
delete draft.session_diff[sessionID]
delete draft.todo[sessionID]
delete draft.permission[sessionID]
delete draft.question[sessionID]
delete draft.session_status[sessionID]
dropSessionCaches(draft, [sessionID])
}),
)
}
export function cleanupDroppedSessionCaches(
store: Store<State>,
setStore: SetStoreFunction<State>,
next: Session[],
setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void,
) {
const keep = new Set(next.map((item) => item.id))
const stale = [
...Object.keys(store.message),
...Object.keys(store.session_diff),
...Object.keys(store.todo),
...Object.keys(store.permission),
...Object.keys(store.question),
...Object.keys(store.session_status),
...Object.values(store.part)
.map((parts) => parts?.find((part) => !!part?.sessionID)?.sessionID)
.filter((sessionID): sessionID is string => !!sessionID),
].filter((sessionID, index, list) => !keep.has(sessionID) && list.indexOf(sessionID) === index)
if (stale.length === 0) return
for (const sessionID of stale) {
setSessionTodo?.(sessionID, undefined)
}
setStore(
produce((draft) => {
dropSessionCaches(draft, stale)
}),
)
}
@@ -102,6 +110,7 @@ export function applyDirectoryEvent(input: {
next.splice(result.index, 0, info)
const trimmed = trimSessions(next, { limit: input.store.limit, permission: input.store.permission })
input.setStore("session", reconcile(trimmed, { key: "id" }))
cleanupDroppedSessionCaches(input.store, input.setStore, trimmed, input.setSessionTodo)
if (!info.parentID) input.setStore("sessionTotal", (value) => value + 1)
break
}
@@ -117,7 +126,7 @@ export function applyDirectoryEvent(input: {
}),
)
}
cleanupSessionCaches(input.store, input.setStore, info.id, input.setSessionTodo)
cleanupSessionCaches(input.setStore, info.id, input.setSessionTodo)
if (info.parentID) break
input.setStore("sessionTotal", (value) => Math.max(0, value - 1))
break
@@ -130,6 +139,7 @@ export function applyDirectoryEvent(input: {
next.splice(result.index, 0, info)
const trimmed = trimSessions(next, { limit: input.store.limit, permission: input.store.permission })
input.setStore("session", reconcile(trimmed, { key: "id" }))
cleanupDroppedSessionCaches(input.store, input.setStore, trimmed, input.setSessionTodo)
break
}
case "session.deleted": {
@@ -143,7 +153,7 @@ export function applyDirectoryEvent(input: {
}),
)
}
cleanupSessionCaches(input.store, input.setStore, info.id, input.setSessionTodo)
cleanupSessionCaches(input.setStore, info.id, input.setSessionTodo)
if (info.parentID) break
input.setStore("sessionTotal", (value) => Math.max(0, value - 1))
break

View File

@@ -0,0 +1,102 @@
import { describe, expect, test } from "bun:test"
import type {
FileDiff,
Message,
Part,
PermissionRequest,
QuestionRequest,
SessionStatus,
Todo,
} from "@opencode-ai/sdk/v2/client"
import { dropSessionCaches, pickSessionCacheEvictions } from "./session-cache"
const msg = (id: string, sessionID: string) =>
({
id,
sessionID,
role: "user",
time: { created: 1 },
agent: "assistant",
model: { providerID: "openai", modelID: "gpt" },
}) as Message
const part = (id: string, sessionID: string, messageID: string) =>
({
id,
sessionID,
messageID,
type: "text",
text: id,
}) as Part
describe("app session cache", () => {
test("dropSessionCaches clears orphaned parts without message rows", () => {
const store: {
session_status: Record<string, SessionStatus | undefined>
session_diff: Record<string, FileDiff[] | undefined>
todo: Record<string, Todo[] | undefined>
message: Record<string, Message[] | undefined>
part: Record<string, Part[] | undefined>
permission: Record<string, PermissionRequest[] | undefined>
question: Record<string, QuestionRequest[] | undefined>
} = {
session_status: { ses_1: { type: "busy" } as SessionStatus },
session_diff: { ses_1: [] },
todo: { ses_1: [] as Todo[] },
message: {},
part: { msg_1: [part("prt_1", "ses_1", "msg_1")] },
permission: { ses_1: [] as PermissionRequest[] },
question: { ses_1: [] as QuestionRequest[] },
}
dropSessionCaches(store, ["ses_1"])
expect(store.message.ses_1).toBeUndefined()
expect(store.part.msg_1).toBeUndefined()
expect(store.todo.ses_1).toBeUndefined()
expect(store.session_diff.ses_1).toBeUndefined()
expect(store.session_status.ses_1).toBeUndefined()
expect(store.permission.ses_1).toBeUndefined()
expect(store.question.ses_1).toBeUndefined()
})
test("dropSessionCaches clears message-backed parts", () => {
const m = msg("msg_1", "ses_1")
const store: {
session_status: Record<string, SessionStatus | undefined>
session_diff: Record<string, FileDiff[] | undefined>
todo: Record<string, Todo[] | undefined>
message: Record<string, Message[] | undefined>
part: Record<string, Part[] | undefined>
permission: Record<string, PermissionRequest[] | undefined>
question: Record<string, QuestionRequest[] | undefined>
} = {
session_status: {},
session_diff: {},
todo: {},
message: { ses_1: [m] },
part: { [m.id]: [part("prt_1", "ses_1", m.id)] },
permission: {},
question: {},
}
dropSessionCaches(store, ["ses_1"])
expect(store.message.ses_1).toBeUndefined()
expect(store.part[m.id]).toBeUndefined()
})
test("pickSessionCacheEvictions preserves requested sessions", () => {
const seen = new Set(["ses_1", "ses_2", "ses_3"])
const stale = pickSessionCacheEvictions({
seen,
keep: "ses_4",
limit: 2,
preserve: ["ses_1"],
})
expect(stale).toEqual(["ses_2", "ses_3"])
expect([...seen]).toEqual(["ses_1", "ses_4"])
})
})

View File

@@ -0,0 +1,62 @@
import type {
FileDiff,
Message,
Part,
PermissionRequest,
QuestionRequest,
SessionStatus,
Todo,
} from "@opencode-ai/sdk/v2/client"
export const SESSION_CACHE_LIMIT = 40
type SessionCache = {
session_status: Record<string, SessionStatus | undefined>
session_diff: Record<string, FileDiff[] | undefined>
todo: Record<string, Todo[] | undefined>
message: Record<string, Message[] | undefined>
part: Record<string, Part[] | undefined>
permission: Record<string, PermissionRequest[] | undefined>
question: Record<string, QuestionRequest[] | undefined>
}
export function dropSessionCaches(store: SessionCache, sessionIDs: Iterable<string>) {
const stale = new Set(Array.from(sessionIDs).filter(Boolean))
if (stale.size === 0) return
for (const key of Object.keys(store.part)) {
const parts = store.part[key]
if (!parts?.some((part) => stale.has(part?.sessionID ?? ""))) continue
delete store.part[key]
}
for (const sessionID of stale) {
delete store.message[sessionID]
delete store.todo[sessionID]
delete store.session_diff[sessionID]
delete store.session_status[sessionID]
delete store.permission[sessionID]
delete store.question[sessionID]
}
}
export function pickSessionCacheEvictions(input: {
seen: Set<string>
keep: string
limit: number
preserve?: Iterable<string>
}) {
const stale: string[] = []
const keep = new Set([input.keep, ...Array.from(input.preserve ?? [])])
if (input.seen.has(input.keep)) input.seen.delete(input.keep)
input.seen.add(input.keep)
for (const id of input.seen) {
if (input.seen.size - stale.length <= input.limit) break
if (keep.has(id)) continue
stale.push(id)
}
for (const id of stale) {
input.seen.delete(id)
}
return stale
}

View File

@@ -6,6 +6,7 @@ import { createSimpleContext } from "@opencode-ai/ui/context"
import { useGlobalSync } from "./global-sync"
import { useSDK } from "./sdk"
import type { Message, Part } from "@opencode-ai/sdk/v2/client"
import { SESSION_CACHE_LIMIT, dropSessionCaches, pickSessionCacheEvictions } from "./global-sync/session-cache"
function sortParts(parts: Part[]) {
return parts.filter((part) => !!part?.id).sort((a, b) => cmp(a.id, b.id))
@@ -108,6 +109,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
const inflight = new Map<string, Promise<void>>()
const inflightDiff = new Map<string, Promise<void>>()
const inflightTodo = new Map<string, Promise<void>>()
const maxDirs = 30
const seen = new Map<string, Set<string>>()
const [meta, setMeta] = createStore({
limit: {} as Record<string, number>,
complete: {} as Record<string, boolean>,
@@ -121,6 +124,62 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
return undefined
}
const seenFor = (directory: string) => {
const existing = seen.get(directory)
if (existing) {
seen.delete(directory)
seen.set(directory, existing)
return existing
}
const created = new Set<string>()
seen.set(directory, created)
while (seen.size > maxDirs) {
const first = seen.keys().next().value
if (!first) break
const stale = [...(seen.get(first) ?? [])]
seen.delete(first)
const [, setStore] = globalSync.child(first, { bootstrap: false })
evict(first, setStore, stale)
}
return created
}
const clearMeta = (directory: string, sessionIDs: string[]) => {
if (sessionIDs.length === 0) return
setMeta(
produce((draft) => {
for (const sessionID of sessionIDs) {
const key = keyFor(directory, sessionID)
delete draft.limit[key]
delete draft.complete[key]
delete draft.loading[key]
}
}),
)
}
const evict = (directory: string, setStore: Setter, sessionIDs: string[]) => {
if (sessionIDs.length === 0) return
for (const sessionID of sessionIDs) {
globalSync.todo.set(sessionID, undefined)
}
setStore(
produce((draft) => {
dropSessionCaches(draft, sessionIDs)
}),
)
clearMeta(directory, sessionIDs)
}
const touch = (directory: string, setStore: Setter, sessionID: string) => {
const stale = pickSessionCacheEvictions({
seen: seenFor(directory),
keep: sessionID,
limit: SESSION_CACHE_LIMIT,
})
evict(directory, setStore, stale)
}
const fetchMessages = async (input: { client: typeof sdk.client; sessionID: string; limit: number }) => {
const messages = await retry(() =>
input.client.session.messages({ sessionID: input.sessionID, limit: input.limit }),
@@ -135,6 +194,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
}
}
const tracked = (directory: string, sessionID: string) => seen.get(directory)?.has(sessionID) ?? false
const loadMessages = async (input: {
directory: string
client: typeof sdk.client
@@ -148,6 +209,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
setMeta("loading", key, true)
await fetchMessages(input)
.then((next) => {
if (!tracked(input.directory, input.sessionID)) return
batch(() => {
input.setStore("message", input.sessionID, reconcile(next.session, { key: "id" }))
for (const p of next.part) {
@@ -158,6 +220,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
})
})
.finally(() => {
if (!tracked(input.directory, input.sessionID)) return
setMeta("loading", key, false)
})
}
@@ -224,11 +287,16 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
const key = keyFor(directory, sessionID)
const hasSession = Binary.search(store.session, sessionID, (s) => s.id).found
touch(directory, setStore, sessionID)
if (store.message[sessionID] !== undefined && hasSession && meta.limit[key] !== undefined) return
const limit = meta.limit[key] ?? messagePageSize
const sessionReq = hasSession
? Promise.resolve()
: retry(() => client.session.get({ sessionID })).then((session) => {
if (!tracked(directory, sessionID)) return
const data = session.data
if (!data) return
setStore(
@@ -258,11 +326,13 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
const directory = sdk.directory
const client = sdk.client
const [store, setStore] = globalSync.child(directory)
touch(directory, setStore, sessionID)
if (store.session_diff[sessionID] !== undefined) return
const key = keyFor(directory, sessionID)
return runInflight(inflightDiff, key, () =>
retry(() => client.session.diff({ sessionID })).then((diff) => {
if (!tracked(directory, sessionID)) return
setStore("session_diff", sessionID, reconcile(diff.data ?? [], { key: "file" }))
}),
)
@@ -271,6 +341,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
const directory = sdk.directory
const client = sdk.client
const [store, setStore] = globalSync.child(directory)
touch(directory, setStore, sessionID)
const existing = store.todo[sessionID]
const cached = globalSync.data.session_todo[sessionID]
if (existing !== undefined) {
@@ -287,6 +358,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
const key = keyFor(directory, sessionID)
return runInflight(inflightTodo, key, () =>
retry(() => client.session.todo({ sessionID })).then((todo) => {
if (!tracked(directory, sessionID)) return
const list = todo.data ?? []
setStore("todo", sessionID, reconcile(list, { key: "id" }))
globalSync.todo.set(sessionID, list)
@@ -310,6 +382,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
const directory = sdk.directory
const client = sdk.client
const [, setStore] = globalSync.child(directory)
touch(directory, setStore, sessionID)
const key = keyFor(directory, sessionID)
const step = count ?? messagePageSize
if (meta.loading[key]) return
@@ -325,6 +398,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
})
},
},
evict(sessionID: string, directory = sdk.directory) {
const [, setStore] = globalSync.child(directory)
seenFor(directory).delete(sessionID)
evict(directory, setStore, [sessionID])
},
fetch: async (count = 10) => {
const directory = sdk.directory
const client = sdk.client

View File

@@ -1,6 +1,6 @@
import { createStore, produce } from "solid-js/store"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { batch, createEffect, createMemo, createRoot, onCleanup } from "solid-js"
import { batch, createEffect, createMemo, createRoot, on, onCleanup } from "solid-js"
import { useParams } from "@solidjs/router"
import { useSDK } from "./sdk"
import type { Platform } from "./platform"
@@ -38,6 +38,16 @@ type TerminalCacheEntry = {
const caches = new Set<Map<string, TerminalCacheEntry>>()
const trimTerminal = (pty: LocalPTY) => {
if (!pty.buffer && pty.cursor === undefined && pty.scrollY === undefined) return pty
return {
...pty,
buffer: undefined,
cursor: undefined,
scrollY: undefined,
}
}
export function clearWorkspaceTerminals(dir: string, sessionIDs?: string[], platform?: Platform) {
const key = getWorkspaceTerminalCacheKey(dir)
for (const cache of caches) {
@@ -188,6 +198,18 @@ function createWorkspaceTerminalSession(sdk: ReturnType<typeof useSDK>, dir: str
console.error("Failed to update terminal", error)
})
},
trim(id: string) {
const index = store.all.findIndex((x) => x.id === id)
if (index === -1) return
setStore("all", index, (pty) => trimTerminal(pty))
},
trimAll() {
setStore("all", (all) => {
const next = all.map(trimTerminal)
if (next.every((pty, index) => pty === all[index])) return all
return next
})
},
async clone(id: string) {
const index = store.all.findIndex((x) => x.id === id)
const pty = store.all[index]
@@ -322,12 +344,27 @@ export const { use: useTerminal, provider: TerminalProvider } = createSimpleCont
const workspace = createMemo(() => loadWorkspace(params.dir!, params.id))
createEffect(
on(
() => ({ dir: params.dir, id: params.id }),
(next, prev) => {
if (!prev?.dir) return
if (next.dir === prev.dir && next.id === prev.id) return
if (next.dir === prev.dir && next.id) return
loadWorkspace(prev.dir, prev.id).trimAll()
},
{ defer: true },
),
)
return {
ready: () => workspace().ready(),
all: () => workspace().all(),
active: () => workspace().active(),
new: () => workspace().new(),
update: (pty: Partial<LocalPTY> & { id: string }) => workspace().update(pty),
trim: (id: string) => workspace().trim(id),
trimAll: () => workspace().trimAll(),
clone: (id: string) => workspace().clone(id),
open: (id: string) => workspace().open(id),
close: (id: string) => workspace().close(id),

View File

@@ -34,6 +34,7 @@ import { useProviders } from "@/hooks/use-providers"
import { showToast, Toast, toaster } from "@opencode-ai/ui/toast"
import { useGlobalSDK } from "@/context/global-sdk"
import { clearWorkspaceTerminals } from "@/context/terminal"
import { dropSessionCaches, pickSessionCacheEvictions } from "@/context/global-sync/session-cache"
import { useNotification } from "@/context/notification"
import { usePermission } from "@/context/permission"
import { Binary } from "@opencode-ai/util/binary"
@@ -657,25 +658,24 @@ export default function Layout(props: ParentProps) {
const prefetchQueues = new Map<string, PrefetchQueue>()
const PREFETCH_MAX_SESSIONS_PER_DIR = 10
const prefetchedByDir = new Map<string, Map<string, true>>()
const prefetchedByDir = new Map<string, Set<string>>()
const lruFor = (directory: string) => {
const existing = prefetchedByDir.get(directory)
if (existing) return existing
const created = new Map<string, true>()
const created = new Set<string>()
prefetchedByDir.set(directory, created)
return created
}
const markPrefetched = (directory: string, sessionID: string) => {
const lru = lruFor(directory)
if (lru.has(sessionID)) lru.delete(sessionID)
lru.set(sessionID, true)
while (lru.size > PREFETCH_MAX_SESSIONS_PER_DIR) {
const oldest = lru.keys().next().value as string | undefined
if (!oldest) return
lru.delete(oldest)
}
return pickSessionCacheEvictions({
seen: lru,
keep: sessionID,
limit: PREFETCH_MAX_SESSIONS_PER_DIR,
preserve: directory === params.dir && params.id ? [params.id] : undefined,
})
}
createEffect(() => {
@@ -724,6 +724,7 @@ export default function Layout(props: ParentProps) {
return retry(() => globalSDK.client.session.messages({ directory, sessionID, limit: prefetchChunk }))
.then((messages) => {
if (prefetchToken.value !== token) return
if (!lruFor(directory).has(sessionID)) return
const items = (messages.data ?? []).filter((x) => !!x?.info?.id)
const next = items.map((x) => x.info).filter((m): m is Message => !!m?.id)
@@ -787,7 +788,18 @@ export default function Layout(props: ParentProps) {
const lru = lruFor(directory)
const known = lru.has(session.id)
if (!known && lru.size >= PREFETCH_MAX_SESSIONS_PER_DIR && priority !== "high") return
markPrefetched(directory, session.id)
const stale = markPrefetched(directory, session.id)
if (stale.length > 0) {
const [, setStore] = globalSync.child(directory, { bootstrap: false })
for (const id of stale) {
globalSync.todo.set(id, undefined)
}
setStore(
produce((draft) => {
dropSessionCaches(draft, stale)
}),
)
}
if (priority === "high") q.pending.unshift(session.id)
if (priority !== "high") q.pending.push(session.id)
@@ -1879,6 +1891,7 @@ export default function Layout(props: ParentProps) {
const SidebarPanel = (panelProps: { project: LocalProject | undefined; mobile?: boolean; merged?: boolean }) => {
const merged = createMemo(() => panelProps.mobile || (panelProps.merged ?? layout.sidebar.opened()))
const hover = createMemo(() => !panelProps.mobile && panelProps.merged === false && !layout.sidebar.opened())
const projectName = createMemo(() => {
const project = panelProps.project
if (!project) return ""
@@ -1907,8 +1920,8 @@ export default function Layout(props: ParentProps) {
"flex flex-col min-h-0 min-w-0 rounded-tl-[12px] px-2": true,
"border border-b-0 border-border-weak-base": !merged(),
"border-l border-t border-border-weaker-base": merged(),
"bg-background-base": merged(),
"bg-background-stronger": !merged(),
"bg-background-base": merged() || hover(),
"bg-background-stronger": !merged() && !hover(),
"flex-1 min-w-0": panelProps.mobile,
"max-w-full overflow-hidden": panelProps.mobile,
}}

View File

@@ -91,6 +91,7 @@ const ProjectTile = (props: {
modal={!props.sidebarHovering()}
onOpenChange={(value) => {
props.setMenu(value)
props.setSuppressHover(value)
if (value) props.setOpen(false)
}}
>
@@ -107,6 +108,12 @@ const ProjectTile = (props: {
!props.selected() && !props.active(),
"bg-surface-base-hover border border-border-weak-base": !props.selected() && props.active(),
}}
onPointerDown={(event) => {
if (!props.overlay()) return
if (event.button !== 2 && !(event.button === 0 && event.ctrlKey)) return
props.setSuppressHover(true)
event.preventDefault()
}}
onMouseEnter={(event: MouseEvent) => {
if (!props.overlay()) return
if (props.suppressHover()) return

View File

@@ -41,216 +41,12 @@ import { createScrollSpy } from "@/pages/session/scroll-spy"
import { SessionMobileTabs } from "@/pages/session/session-mobile-tabs"
import { SessionSidePanel } from "@/pages/session/session-side-panel"
import { TerminalPanel } from "@/pages/session/terminal-panel"
import { createSessionHistoryWindow, emptyUserMessages } from "@/pages/session/history-window"
import { useSessionCommands } from "@/pages/session/use-session-commands"
import { useSessionHashScroll } from "@/pages/session/use-session-hash-scroll"
import { same } from "@/utils/same"
import { formatServerError } from "@/utils/server-errors"
const emptyUserMessages: UserMessage[] = []
type SessionHistoryWindowInput = {
sessionID: () => string | undefined
messagesReady: () => boolean
visibleUserMessages: () => UserMessage[]
historyMore: () => boolean
historyLoading: () => boolean
loadMore: (sessionID: string) => Promise<void>
userScrolled: () => boolean
scroller: () => HTMLDivElement | undefined
}
/**
* Maintains the rendered history window for a session timeline.
*
* It keeps initial paint bounded to recent turns, reveals cached turns in
* small batches while scrolling upward, and prefetches older history near top.
*/
function createSessionHistoryWindow(input: SessionHistoryWindowInput) {
const turnInit = 10
const turnBatch = 8
const turnScrollThreshold = 200
const turnPrefetchBuffer = 16
const prefetchCooldownMs = 400
const prefetchNoGrowthLimit = 2
const [state, setState] = createStore({
turnID: undefined as string | undefined,
turnStart: 0,
prefetchUntil: 0,
prefetchNoGrowth: 0,
})
const initialTurnStart = (len: number) => (len > turnInit ? len - turnInit : 0)
const turnStart = createMemo(() => {
const id = input.sessionID()
const len = input.visibleUserMessages().length
if (!id || len <= 0) return 0
if (state.turnID !== id) return initialTurnStart(len)
if (state.turnStart <= 0) return 0
if (state.turnStart >= len) return initialTurnStart(len)
return state.turnStart
})
const setTurnStart = (start: number) => {
const id = input.sessionID()
const next = start > 0 ? start : 0
if (!id) {
setState({ turnID: undefined, turnStart: next })
return
}
setState({ turnID: id, turnStart: next })
}
const renderedUserMessages = createMemo(
() => {
const msgs = input.visibleUserMessages()
const start = turnStart()
if (start <= 0) return msgs
return msgs.slice(start)
},
emptyUserMessages,
{
equals: same,
},
)
const preserveScroll = (fn: () => void) => {
const el = input.scroller()
if (!el) {
fn()
return
}
const beforeTop = el.scrollTop
fn()
void el.scrollHeight
el.scrollTop = beforeTop
}
const backfillTurns = () => {
const start = turnStart()
if (start <= 0) return
const next = start - turnBatch
const nextStart = next > 0 ? next : 0
preserveScroll(() => setTurnStart(nextStart))
}
/** Button path: reveal all cached turns, fetch older history, reveal one batch. */
const loadAndReveal = async () => {
const id = input.sessionID()
if (!id) return
const start = turnStart()
const beforeVisible = input.visibleUserMessages().length
if (start > 0) setTurnStart(0)
if (!input.historyMore() || input.historyLoading()) return
await input.loadMore(id)
if (input.sessionID() !== id) return
const afterVisible = input.visibleUserMessages().length
const growth = afterVisible - beforeVisible
if (state.prefetchNoGrowth) setState("prefetchNoGrowth", 0)
if (growth <= 0) return
if (turnStart() !== 0) return
const target = Math.min(afterVisible, Math.max(beforeVisible, renderedUserMessages().length) + turnBatch)
const nextStart = Math.max(0, afterVisible - target)
preserveScroll(() => setTurnStart(nextStart))
}
/** Scroll/prefetch path: fetch older history from server. */
const fetchOlderMessages = async (opts?: { prefetch?: boolean }) => {
const id = input.sessionID()
if (!id) return
if (!input.historyMore() || input.historyLoading()) return
if (opts?.prefetch) {
const now = Date.now()
if (state.prefetchUntil > now) return
if (state.prefetchNoGrowth >= prefetchNoGrowthLimit) return
setState("prefetchUntil", now + prefetchCooldownMs)
}
const start = turnStart()
const beforeVisible = input.visibleUserMessages().length
const beforeRendered = start <= 0 ? beforeVisible : renderedUserMessages().length
await input.loadMore(id)
if (input.sessionID() !== id) return
const afterVisible = input.visibleUserMessages().length
const growth = afterVisible - beforeVisible
if (opts?.prefetch) {
setState("prefetchNoGrowth", growth > 0 ? 0 : state.prefetchNoGrowth + 1)
} else if (growth > 0 && state.prefetchNoGrowth) {
setState("prefetchNoGrowth", 0)
}
if (growth <= 0) return
if (turnStart() !== start) return
const reveal = !opts?.prefetch
const currentRendered = renderedUserMessages().length
const base = Math.max(beforeRendered, currentRendered)
const target = reveal ? Math.min(afterVisible, base + turnBatch) : base
const nextStart = Math.max(0, afterVisible - target)
preserveScroll(() => setTurnStart(nextStart))
}
const onScrollerScroll = () => {
if (!input.userScrolled()) return
const el = input.scroller()
if (!el) return
if (el.scrollHeight - el.clientHeight + el.scrollTop >= turnScrollThreshold) return
const start = turnStart()
if (start > 0) {
if (start <= turnPrefetchBuffer) {
void fetchOlderMessages({ prefetch: true })
}
backfillTurns()
return
}
void fetchOlderMessages()
}
createEffect(
on(
input.sessionID,
() => {
setState({ prefetchUntil: 0, prefetchNoGrowth: 0 })
},
{ defer: true },
),
)
createEffect(
on(
() => [input.sessionID(), input.messagesReady()] as const,
([id, ready]) => {
if (!id || !ready) return
setTurnStart(initialTurnStart(input.visibleUserMessages().length))
},
{ defer: true },
),
)
return {
turnStart,
setTurnStart,
renderedUserMessages,
loadAndReveal,
onScrollerScroll,
}
}
export default function Page() {
const globalSync = useGlobalSync()
const layout = useLayout()
@@ -426,10 +222,12 @@ export default function Page() {
createEffect(
on(
() => params.id,
(id, prev) => {
if (id || !prev) return
resetSessionModel(local)
() => ({ dir: params.dir, id: params.id }),
(next, prev) => {
if (!prev) return
if (next.dir === prev.dir && next.id === prev.id) return
if (prev.id) sync.session.evict(prev.id, prev.dir)
if (!next.id) resetSessionModel(local)
},
{ defer: true },
),
@@ -1088,6 +886,7 @@ export default function Page() {
let scrollStateFrame: number | undefined
let scrollStateTarget: HTMLDivElement | undefined
let historyFillFrame: number | undefined
const scrollSpy = createScrollSpy({
onActive: (id) => {
if (id === store.messageId) return
@@ -1157,7 +956,9 @@ export default function Page() {
scroller = el
autoScroll.scrollRef(el)
scrollSpy.setContainer(el)
if (el) scheduleScrollState(el)
if (!el) return
scheduleScrollState(el)
scheduleHistoryFill()
}
createResizeObserver(
@@ -1166,6 +967,7 @@ export default function Page() {
const el = scroller
if (el) scheduleScrollState(el)
scrollSpy.markDirty()
scheduleHistoryFill()
},
)
@@ -1180,6 +982,45 @@ export default function Page() {
scroller: () => scroller,
})
const scheduleHistoryFill = () => {
if (historyFillFrame !== undefined) return
historyFillFrame = requestAnimationFrame(() => {
historyFillFrame = undefined
if (!params.id || !messagesReady()) return
if (autoScroll.userScrolled() || historyLoading()) return
const el = scroller
if (!el) return
if (el.scrollHeight > el.clientHeight + 1) return
if (historyWindow.turnStart() <= 0 && !historyMore()) return
void historyWindow.loadAndReveal()
})
}
createEffect(
on(
() =>
[
params.id,
messagesReady(),
historyWindow.turnStart(),
historyMore(),
historyLoading(),
autoScroll.userScrolled(),
visibleUserMessages().length,
] as const,
([id, ready, start, more, loading, scrolled]) => {
if (!id || !ready || loading || scrolled) return
if (start <= 0 && !more) return
scheduleHistoryFill()
},
{ defer: true },
),
)
createResizeObserver(
() => promptDock,
({ height }) => {
@@ -1197,6 +1038,7 @@ export default function Page() {
if (el) scheduleScrollState(el)
scrollSpy.markDirty()
scheduleHistoryFill()
},
)
@@ -1226,6 +1068,7 @@ export default function Page() {
document.removeEventListener("keydown", handleKeyDown)
scrollSpy.destroy()
if (scrollStateFrame !== undefined) cancelAnimationFrame(scrollStateFrame)
if (historyFillFrame !== undefined) cancelAnimationFrame(historyFillFrame)
})
return (

View File

@@ -0,0 +1,35 @@
import { describe, expect, test } from "bun:test"
import { historyLoadMode, historyRevealTop } from "./history-window"
describe("historyLoadMode", () => {
test("reveals cached turns before fetching", () => {
expect(historyLoadMode({ start: 10, more: true, loading: false })).toBe("reveal")
})
test("fetches older history when cache is already revealed", () => {
expect(historyLoadMode({ start: 0, more: true, loading: false })).toBe("fetch")
})
test("does nothing while history is unavailable or loading", () => {
expect(historyLoadMode({ start: 0, more: false, loading: false })).toBe("noop")
expect(historyLoadMode({ start: 0, more: true, loading: true })).toBe("noop")
})
})
describe("historyRevealTop", () => {
test("pins the viewport to the top when older turns were revealed there", () => {
expect(historyRevealTop({ top: -400, height: 1000, gap: 0, max: 400 }, { clientHeight: 600, height: 2000 })).toBe(
-1400,
)
})
test("keeps the latest turns pinned when the viewport was underfilled", () => {
expect(historyRevealTop({ top: 0, height: 200, gap: -400, max: -400 }, { clientHeight: 600, height: 2000 })).toBe(0)
})
test("keeps the current anchor when the user was not at the top", () => {
expect(historyRevealTop({ top: -200, height: 1000, gap: 200, max: 400 }, { clientHeight: 600, height: 2000 })).toBe(
-200,
)
})
})

View File

@@ -0,0 +1,273 @@
import type { UserMessage } from "@opencode-ai/sdk/v2"
import { createEffect, createMemo, on } from "solid-js"
import { createStore } from "solid-js/store"
import { same } from "@/utils/same"
export const emptyUserMessages: UserMessage[] = []
export type SessionHistoryWindowInput = {
sessionID: () => string | undefined
messagesReady: () => boolean
visibleUserMessages: () => UserMessage[]
historyMore: () => boolean
historyLoading: () => boolean
loadMore: (sessionID: string) => Promise<void>
userScrolled: () => boolean
scroller: () => HTMLDivElement | undefined
}
type Snap = {
top: number
height: number
gap: number
max: number
}
export const historyLoadMode = (input: { start: number; more: boolean; loading: boolean }) => {
if (input.start > 0) return "reveal"
if (!input.more || input.loading) return "noop"
return "fetch"
}
export const historyRevealTop = (
mark: { top: number; height: number; gap: number; max: number },
next: { clientHeight: number; height: number },
threshold = 16,
) => {
const delta = next.height - mark.height
if (delta <= 0) return mark.top
if (mark.max <= 0) return mark.top
if (mark.gap > threshold) return mark.top
const max = next.height - next.clientHeight
if (max <= 0) return 0
return Math.max(-max, Math.min(0, mark.top - delta))
}
const snap = (el: HTMLDivElement | undefined): Snap | undefined => {
if (!el) return
const max = el.scrollHeight - el.clientHeight
return {
top: el.scrollTop,
height: el.scrollHeight,
gap: max + el.scrollTop,
max,
}
}
const clamp = (el: HTMLDivElement, top: number) => {
const max = el.scrollHeight - el.clientHeight
if (max <= 0) return 0
return Math.max(-max, Math.min(0, top))
}
const revealThreshold = 16
const reveal = (input: SessionHistoryWindowInput, mark: Snap | undefined) => {
const el = input.scroller()
if (!el || !mark) return
el.scrollTop = clamp(
el,
historyRevealTop(mark, { clientHeight: el.clientHeight, height: el.scrollHeight }, revealThreshold),
)
}
const preserve = (input: SessionHistoryWindowInput, fn: () => void) => {
const el = input.scroller()
if (!el) {
fn()
return
}
const top = el.scrollTop
fn()
el.scrollTop = top
}
/**
* Maintains the rendered history window for a session timeline.
*
* It keeps initial paint bounded to recent turns, reveals cached turns in
* small batches while scrolling upward, and prefetches older history near top.
*/
export function createSessionHistoryWindow(input: SessionHistoryWindowInput) {
const turnInit = 10
const turnBatch = 8
const turnScrollThreshold = 200
const turnPrefetchBuffer = 16
const prefetchCooldownMs = 400
const prefetchNoGrowthLimit = 2
const [state, setState] = createStore({
turnID: undefined as string | undefined,
turnStart: 0,
prefetchUntil: 0,
prefetchNoGrowth: 0,
})
const initialTurnStart = (len: number) => (len > turnInit ? len - turnInit : 0)
const turnStart = createMemo(() => {
const id = input.sessionID()
const len = input.visibleUserMessages().length
if (!id || len <= 0) return 0
if (state.turnID !== id) return initialTurnStart(len)
if (state.turnStart <= 0) return 0
if (state.turnStart >= len) return initialTurnStart(len)
return state.turnStart
})
const setTurnStart = (start: number) => {
const id = input.sessionID()
const next = start > 0 ? start : 0
if (!id) {
setState({ turnID: undefined, turnStart: next })
return
}
setState({ turnID: id, turnStart: next })
}
const renderedUserMessages = createMemo(
() => {
const msgs = input.visibleUserMessages()
const start = turnStart()
if (start <= 0) return msgs
return msgs.slice(start)
},
emptyUserMessages,
{
equals: same,
},
)
const backfillTurns = () => {
const start = turnStart()
if (start <= 0) return
const next = start - turnBatch
const nextStart = next > 0 ? next : 0
preserve(input, () => setTurnStart(nextStart))
}
/** Button path: reveal cached turns first, then fetch older history. */
const loadAndReveal = async () => {
const id = input.sessionID()
if (!id) return
const start = turnStart()
const mode = historyLoadMode({
start,
more: input.historyMore(),
loading: input.historyLoading(),
})
if (mode === "reveal") {
const mark = snap(input.scroller())
setTurnStart(0)
reveal(input, mark)
return
}
if (mode === "noop") return
const beforeVisible = input.visibleUserMessages().length
const mark = snap(input.scroller())
await input.loadMore(id)
if (input.sessionID() !== id) return
const afterVisible = input.visibleUserMessages().length
const growth = afterVisible - beforeVisible
if (growth <= 0) return
if (state.prefetchNoGrowth) setState("prefetchNoGrowth", 0)
reveal(input, mark)
}
/** Scroll/prefetch path: fetch older history from server. */
const fetchOlderMessages = async (opts?: { prefetch?: boolean }) => {
const id = input.sessionID()
if (!id) return
if (!input.historyMore() || input.historyLoading()) return
if (opts?.prefetch) {
const now = Date.now()
if (state.prefetchUntil > now) return
if (state.prefetchNoGrowth >= prefetchNoGrowthLimit) return
setState("prefetchUntil", now + prefetchCooldownMs)
}
const start = turnStart()
const beforeVisible = input.visibleUserMessages().length
const beforeRendered = start <= 0 ? beforeVisible : renderedUserMessages().length
await input.loadMore(id)
if (input.sessionID() !== id) return
const afterVisible = input.visibleUserMessages().length
const growth = afterVisible - beforeVisible
if (opts?.prefetch) {
setState("prefetchNoGrowth", growth > 0 ? 0 : state.prefetchNoGrowth + 1)
} else if (growth > 0 && state.prefetchNoGrowth) {
setState("prefetchNoGrowth", 0)
}
if (growth <= 0) return
if (turnStart() !== start) return
const revealMore = !opts?.prefetch
const currentRendered = renderedUserMessages().length
const base = Math.max(beforeRendered, currentRendered)
const target = revealMore ? Math.min(afterVisible, base + turnBatch) : base
const nextStart = Math.max(0, afterVisible - target)
preserve(input, () => setTurnStart(nextStart))
}
const onScrollerScroll = () => {
if (!input.userScrolled()) return
const el = input.scroller()
if (!el) return
if (el.scrollHeight - el.clientHeight + el.scrollTop >= turnScrollThreshold) return
const start = turnStart()
if (start > 0) {
if (start <= turnPrefetchBuffer) {
void fetchOlderMessages({ prefetch: true })
}
backfillTurns()
return
}
void fetchOlderMessages()
}
createEffect(
on(
input.sessionID,
() => {
setState({ prefetchUntil: 0, prefetchNoGrowth: 0 })
},
{ defer: true },
),
)
createEffect(
on(
() => [input.sessionID(), input.messagesReady()] as const,
([id, ready]) => {
if (!id || !ready) return
setTurnStart(initialTurnStart(input.visibleUserMessages().length))
},
{ defer: true },
),
)
return {
turnStart,
setTurnStart,
renderedUserMessages,
loadAndReveal,
onScrollerScroll,
}
}

View File

@@ -2,10 +2,10 @@ import { createEffect, createMemo, on, onCleanup, Show } from "solid-js"
import { createStore, produce } from "solid-js/store"
import { useNavigate, useParams } from "@solidjs/router"
import { Button } from "@opencode-ai/ui/button"
import { useReducedMotion } from "@opencode-ai/ui/hooks"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
import { Dialog } from "@opencode-ai/ui/dialog"
import { prefersReducedMotion } from "@opencode-ai/ui/hooks"
import { InlineInput } from "@opencode-ai/ui/inline-input"
import { animate, type AnimationPlaybackControls, clearFadeStyles, FAST_SPRING } from "@opencode-ai/ui/motion"
import { showToast } from "@opencode-ai/ui/toast"
@@ -32,7 +32,7 @@ export function SessionTimelineHeader(props: {
const sync = useSync()
const dialog = useDialog()
const language = useLanguage()
const reduce = prefersReducedMotion
const reduce = useReducedMotion()
const [title, setTitle] = createStore({
draft: "",

View File

@@ -250,6 +250,7 @@ export function TerminalPanel() {
<div id={`terminal-wrapper-${id}`} class="absolute inset-0">
<Terminal
pty={pty()}
onConnect={() => terminal.trim(id)}
onCleanup={terminal.update}
onConnectError={() => terminal.clone(id)}
/>

View File

@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-app",
"version": "1.2.21",
"version": "1.2.22",
"type": "module",
"license": "MIT",
"scripts": {

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
"version": "1.2.21",
"version": "1.2.22",
"private": true,
"type": "module",
"license": "MIT",

View File

@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-function",
"version": "1.2.21",
"version": "1.2.22",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-mail",
"version": "1.2.21",
"version": "1.2.22",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",

View File

@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/desktop-electron",
"private": true,
"version": "1.2.21",
"version": "1.2.22",
"type": "module",
"license": "MIT",
"homepage": "https://opencode.ai",

View File

@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/desktop",
"private": true,
"version": "1.2.21",
"version": "1.2.22",
"type": "module",
"license": "MIT",
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/enterprise",
"version": "1.2.21",
"version": "1.2.22",
"private": true,
"type": "module",
"license": "MIT",

View File

@@ -1,7 +1,7 @@
id = "opencode"
name = "OpenCode"
description = "The open source coding agent."
version = "1.2.21"
version = "1.2.22"
schema_version = 1
authors = ["Anomaly"]
repository = "https://github.com/anomalyco/opencode"
@@ -11,26 +11,26 @@ name = "OpenCode"
icon = "./icons/opencode.svg"
[agent_servers.opencode.targets.darwin-aarch64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.21/opencode-darwin-arm64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.22/opencode-darwin-arm64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.darwin-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.21/opencode-darwin-x64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.22/opencode-darwin-x64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-aarch64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.21/opencode-linux-arm64.tar.gz"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.22/opencode-linux-arm64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.21/opencode-linux-x64.tar.gz"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.22/opencode-linux-x64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.windows-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.21/opencode-windows-x64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.22/opencode-windows-x64.zip"
cmd = "./opencode.exe"
args = ["acp"]

View File

@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/function",
"version": "1.2.21",
"version": "1.2.22",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.2.21",
"version": "1.2.22",
"name": "opencode",
"type": "module",
"license": "MIT",

View File

@@ -280,6 +280,11 @@ export const RunCommand = cmd({
type: "string",
describe: "attach to a running opencode server (e.g., http://localhost:4096)",
})
.option("password", {
alias: ["p"],
type: "string",
describe: "basic auth password (defaults to OPENCODE_SERVER_PASSWORD)",
})
.option("dir", {
type: "string",
describe: "directory to run in, path on remote server if attaching",
@@ -648,7 +653,14 @@ export const RunCommand = cmd({
}
if (args.attach) {
const sdk = createOpencodeClient({ baseUrl: args.attach, directory })
const headers = (() => {
const password = args.password ?? process.env.OPENCODE_SERVER_PASSWORD
if (!password) return undefined
const username = process.env.OPENCODE_SERVER_USERNAME ?? "opencode"
const auth = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`
return { Authorization: auth }
})()
const sdk = createOpencodeClient({ baseUrl: args.attach, directory, headers })
return await execute(sdk)
}

View File

@@ -111,7 +111,6 @@ export function tui(input: {
fetch?: typeof fetch
headers?: RequestInit["headers"]
events?: EventSource
onExit?: () => Promise<void>
}) {
// promise to prevent immediate exit
return new Promise<void>(async (resolve) => {
@@ -126,7 +125,6 @@ export function tui(input: {
const onExit = async () => {
unguard?.()
await input.onExit?.()
resolve()
}

View File

@@ -15,6 +15,7 @@ export const { use: useExit, provider: ExitProvider } = createSimpleContext({
init: (input: { onExit?: () => Promise<void> }) => {
const renderer = useRenderer()
let message: string | undefined
let task: Promise<void> | undefined
const store = {
set: (value?: string) => {
const prev = message
@@ -29,20 +30,24 @@ export const { use: useExit, provider: ExitProvider } = createSimpleContext({
get: () => message,
}
const exit: Exit = Object.assign(
async (reason?: unknown) => {
// Reset window title before destroying renderer
renderer.setTerminalTitle("")
renderer.destroy()
win32FlushInputBuffer()
if (reason) {
const formatted = FormatError(reason) ?? FormatUnknownError(reason)
if (formatted) {
process.stderr.write(formatted + "\n")
(reason?: unknown) => {
if (task) return task
task = (async () => {
// Reset window title before destroying renderer
renderer.setTerminalTitle("")
renderer.destroy()
win32FlushInputBuffer()
if (reason) {
const formatted = FormatError(reason) ?? FormatUnknownError(reason)
if (formatted) {
process.stderr.write(formatted + "\n")
}
}
}
const text = store.get()
if (text) process.stdout.write(text + "\n")
await input.onExit?.()
const text = store.get()
if (text) process.stdout.write(text + "\n")
await input.onExit?.()
})()
return task
},
{
message: store,

View File

@@ -110,18 +110,20 @@ export const TuiThreadCommand = cmd({
return
}
// Resolve relative paths against PWD to preserve behavior when using --cwd flag
// Resolve relative --project paths from PWD, then use the real cwd after
// chdir so the thread and worker share the same directory key.
const root = Filesystem.resolve(process.env.PWD ?? process.cwd())
const cwd = args.project
const next = args.project
? Filesystem.resolve(path.isAbsolute(args.project) ? args.project : path.join(root, args.project))
: root
: Filesystem.resolve(process.cwd())
const file = await target()
try {
process.chdir(cwd)
process.chdir(next)
} catch {
UI.error("Failed to change directory to " + cwd)
UI.error("Failed to change directory to " + next)
return
}
const cwd = Filesystem.resolve(process.cwd())
const worker = new Worker(file, {
env: Object.fromEntries(

View File

@@ -1288,12 +1288,6 @@ export namespace Provider {
}
}
// Check if opencode provider is available before using it
const opencodeProvider = await state().then((state) => state.providers["opencode"])
if (opencodeProvider && opencodeProvider.models["gpt-5-nano"]) {
return getModel("opencode", "gpt-5-nano")
}
return undefined
}

View File

@@ -0,0 +1,157 @@
import { describe, expect, mock, test } from "bun:test"
import fs from "fs/promises"
import path from "path"
import { tmpdir } from "../../fixture/fixture"
const stop = new Error("stop")
const seen = {
tui: [] as string[],
inst: [] as string[],
}
mock.module("../../../src/cli/cmd/tui/app", () => ({
tui: async (input: { directory: string }) => {
seen.tui.push(input.directory)
throw stop
},
}))
mock.module("@/util/rpc", () => ({
Rpc: {
client: () => ({
call: async () => ({ url: "http://127.0.0.1" }),
on: () => {},
}),
},
}))
mock.module("@/cli/ui", () => ({
UI: {
error: () => {},
},
}))
mock.module("@/util/log", () => ({
Log: {
init: async () => {},
create: () => ({
error: () => {},
info: () => {},
warn: () => {},
debug: () => {},
time: () => ({ stop: () => {} }),
}),
Default: {
error: () => {},
info: () => {},
warn: () => {},
debug: () => {},
},
},
}))
mock.module("@/util/timeout", () => ({
withTimeout: <T>(input: Promise<T>) => input,
}))
mock.module("@/cli/network", () => ({
withNetworkOptions: <T>(input: T) => input,
resolveNetworkOptions: async () => ({
mdns: false,
port: 0,
hostname: "127.0.0.1",
}),
}))
mock.module("../../../src/cli/cmd/tui/win32", () => ({
win32DisableProcessedInput: () => {},
win32InstallCtrlCGuard: () => undefined,
}))
mock.module("@/config/tui", () => ({
TuiConfig: {
get: () => ({}),
},
}))
mock.module("@/project/instance", () => ({
Instance: {
provide: async (input: { directory: string; fn: () => Promise<unknown> | unknown }) => {
seen.inst.push(input.directory)
return input.fn()
},
},
}))
describe("tui thread", () => {
async function call(project?: string) {
const { TuiThreadCommand } = await import("../../../src/cli/cmd/tui/thread")
const args: Parameters<NonNullable<typeof TuiThreadCommand.handler>>[0] = {
_: [],
$0: "opencode",
project,
prompt: "hi",
model: undefined,
agent: undefined,
session: undefined,
continue: false,
fork: false,
port: 0,
hostname: "127.0.0.1",
mdns: false,
"mdns-domain": "opencode.local",
mdnsDomain: "opencode.local",
cors: [],
}
return TuiThreadCommand.handler(args)
}
async function check(project?: string) {
await using tmp = await tmpdir({ git: true })
const cwd = process.cwd()
const pwd = process.env.PWD
const worker = globalThis.Worker
const tty = Object.getOwnPropertyDescriptor(process.stdin, "isTTY")
const link = path.join(path.dirname(tmp.path), path.basename(tmp.path) + "-link")
const type = process.platform === "win32" ? "junction" : "dir"
seen.tui.length = 0
seen.inst.length = 0
await fs.symlink(tmp.path, link, type)
Object.defineProperty(process.stdin, "isTTY", {
configurable: true,
value: true,
})
globalThis.Worker = class extends EventTarget {
onerror = null
onmessage = null
onmessageerror = null
postMessage() {}
terminate() {}
} as unknown as typeof Worker
try {
process.chdir(tmp.path)
process.env.PWD = link
await expect(call(project)).rejects.toBe(stop)
expect(seen.inst[0]).toBe(tmp.path)
expect(seen.tui[0]).toBe(tmp.path)
} finally {
process.chdir(cwd)
if (pwd === undefined) delete process.env.PWD
else process.env.PWD = pwd
if (tty) Object.defineProperty(process.stdin, "isTTY", tty)
else delete (process.stdin as { isTTY?: boolean }).isTTY
globalThis.Worker = worker
await fs.rm(link, { recursive: true, force: true }).catch(() => undefined)
}
}
test("uses the real cwd when PWD points at a symlink", async () => {
await check()
})
test("uses the real cwd after resolving a relative project from PWD", async () => {
await check(".")
})
})

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin",
"version": "1.2.21",
"version": "1.2.22",
"type": "module",
"license": "MIT",
"scripts": {

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk",
"version": "1.2.21",
"version": "1.2.22",
"type": "module",
"license": "MIT",
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/slack",
"version": "1.2.21",
"version": "1.2.22",
"type": "module",
"license": "MIT",
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/ui",
"version": "1.2.21",
"version": "1.2.22",
"type": "module",
"license": "MIT",
"exports": {
@@ -48,8 +48,11 @@
"@pierre/diffs": "catalog:",
"@shikijs/transformers": "3.9.2",
"@solid-primitives/bounds": "0.1.3",
"@solid-primitives/lifecycle": "0.1.2",
"@solid-primitives/media": "2.3.3",
"@solid-primitives/page-visibility": "2.1.1",
"@solid-primitives/resize-observer": "2.1.3",
"@solid-primitives/rootless": "1.5.2",
"@solidjs/meta": "catalog:",
"@solidjs/router": "catalog:",
"dompurify": "3.3.1",

View File

@@ -1,8 +1,8 @@
import { createMemo, createSignal, For, onMount } from "solid-js"
import type { ToolPart } from "@opencode-ai/sdk/v2"
import { getFilename } from "@opencode-ai/util/path"
import { useReducedMotion } from "../hooks/use-reduced-motion"
import { useI18n } from "../context/i18n"
import { prefersReducedMotion } from "../hooks/use-reduced-motion"
import { ToolCall } from "./basic-tool"
import { ToolStatusTitle } from "./tool-status-title"
import { AnimatedCountList } from "./tool-count-summary"
@@ -58,11 +58,9 @@ export function ContextToolGroupHeader(props: {
<ToolCall
variant="row"
icon="magnifying-glass-menu"
open={!props.pending && props.open}
showArrow={!props.pending}
onOpenChange={(v) => {
if (!props.pending) props.onOpenChange(v)
}}
open={props.open}
showArrow
onOpenChange={props.onOpenChange}
trigger={
<div data-component="context-tool-group-trigger" data-pending={props.pending || undefined}>
<span
@@ -149,10 +147,10 @@ export function ContextToolExpandedList(props: { parts: ToolPart[]; expanded: bo
}
export function ContextToolRollingResults(props: { parts: ToolPart[]; pending: boolean }) {
const reduce = useReducedMotion()
const wiped = new Set<string>()
const [mounted, setMounted] = createSignal(false)
onMount(() => setMounted(true))
const reduce = prefersReducedMotion
const show = () => mounted() && props.pending
const opacity = useSpring(() => (show() ? 1 : 0), GROW_SPRING)
const blur = useSpring(() => (show() ? 0 : 2), GROW_SPRING)

View File

@@ -1,6 +1,6 @@
import { createEffect, on, type JSX, onMount, onCleanup } from "solid-js"
import { useReducedMotion } from "../hooks/use-reduced-motion"
import { animate, tunableSpringValue, type AnimationPlaybackControls, GROW_SPRING, type SpringConfig } from "./motion"
import { prefersReducedMotion } from "../hooks/use-reduced-motion"
export interface GrowBoxProps {
children: JSX.Element
@@ -49,7 +49,7 @@ export interface GrowBoxProps {
* Used for timeline turns, assistant part groups, and user messages.
*/
export function GrowBox(props: GrowBoxProps) {
const reduce = prefersReducedMotion
const reduce = useReducedMotion()
const spring = () => props.spring ?? GROW_SPRING
const toggleSpring = () => props.toggleSpring ?? spring()
let mode: "mount" | "toggle" = "mount"
@@ -293,6 +293,18 @@ export function GrowBox(props: GrowBoxProps) {
offChange()
})
if (watch()) {
observer = new ResizeObserver(() => {
if (!open()) return
if (resizeFrame !== undefined) return
resizeFrame = requestAnimationFrame(() => {
resizeFrame = undefined
setHeight("mount")
})
})
observer.observe(body)
}
if (!animated()) {
setInstant(open())
return
@@ -318,17 +330,6 @@ export function GrowBox(props: GrowBoxProps) {
if (grow()) setHeight("mount")
})
}
if (watch()) {
observer = new ResizeObserver(() => {
if (!open()) return
if (resizeFrame !== undefined) return
resizeFrame = requestAnimationFrame(() => {
resizeFrame = undefined
setHeight("mount")
})
})
observer.observe(body)
}
})
createEffect(
@@ -402,7 +403,12 @@ export function GrowBox(props: GrowBoxProps) {
ref={root}
data-slot={props.slot}
class={props.class}
style={{ transform: "translateZ(0)", position: "relative" }}
style={{
transform: "translateZ(0)",
position: "relative",
height: open() ? undefined : "0px",
overflow: open() ? undefined : "clip",
}}
>
<div ref={body} style={{ "padding-top": gap() > 0 ? `${gap()}px` : undefined }}>
{props.children}

View File

@@ -38,7 +38,7 @@ import { TextShimmer } from "./text-shimmer"
import { list } from "./text-utils"
import { GrowBox } from "./grow-box"
import { COLLAPSIBLE_SPRING } from "./motion"
import { busy, hold, createThrottledValue, useToolFade, useContextToolPending } from "./tool-utils"
import { busy, createThrottledValue, useToolFade, useContextToolPending } from "./tool-utils"
import { ContextToolGroupHeader, ContextToolExpandedList, ContextToolRollingResults } from "./context-tool-results"
import { ShellRollingResults } from "./shell-rolling-results"
@@ -254,8 +254,6 @@ function urls(text: string | undefined) {
const CONTEXT_GROUP_TOOLS = new Set(["read", "glob", "grep", "list"])
const HIDDEN_TOOLS = new Set(["todowrite", "todoread"])
import { pageVisible } from "../hooks/use-page-visible"
function createGroupOpenState() {
const [state, setState] = createStore<Record<string, boolean>>({})
const read = (key?: string, collapse?: boolean) => {
@@ -274,18 +272,6 @@ function createGroupOpenState() {
return { read, controlled, write }
}
function shouldCollapseGroup(
statuses: (string | undefined)[],
opts: { afterTool?: boolean; groupTail?: boolean; working?: boolean },
) {
if (opts.afterTool) return true
if (opts.groupTail === false) return true
if (!pageVisible()) return false
if (opts.working) return false
if (!statuses.length) return false
return !statuses.some((s) => busy(s))
}
function renderable(part: PartType, showReasoningSummaries = true) {
if (part.type === "tool") {
if (HIDDEN_TOOLS.has(part.tool)) return false
@@ -480,20 +466,9 @@ export function AssistantParts(props: {
return COLLAPSIBLE_SPRING
})
const contextOpen = createMemo(() => {
const collapse = (
afterTool?: boolean,
groupTail?: boolean,
group?: { part: ToolPart; message: AssistantMessage }[],
) =>
shouldCollapseGroup(group?.map((item) => item.part.state.status) ?? [], {
afterTool,
groupTail,
working: props.working,
})
const value = ctx()
if (value) return groupState.read(value.groupKey, collapse(value.afterTool, value.tail, value.parts))
const entry = part()
return groupState.read(entry?.groupKey, collapse(entry?.afterTool, entry?.groupTail, entry?.groupParts))
if (value) return groupState.read(value.groupKey, true)
return groupState.read(part()?.groupKey, true)
})
const visible = createMemo(() => {
if (!context()) return true
@@ -539,9 +514,7 @@ export function AssistantParts(props: {
ctxPartsPrev = result
return result
})
const ctxPendingRaw = useContextToolPending(ctxParts, () => !!(props.working && ctx()?.tail))
const ctxPending = ctxPendingRaw
const ctxHoldOpen = hold(ctxPendingRaw)
const ctxPending = useContextToolPending(ctxParts, () => !!(props.working && ctx()?.tail))
const shell = createMemo(() => {
const value = part()
if (!value) return
@@ -593,12 +566,20 @@ export function AssistantParts(props: {
onOpenChange={(value: boolean) => groupState.write(entry().groupKey, value)}
/>
</PartGrow>
<ContextToolExpandedList parts={ctxParts()} expanded={!ctxPending() && contextOpen()} />
<ContextToolRollingResults parts={ctxParts()} pending={ctxHoldOpen()} />
<ContextToolExpandedList parts={ctxParts()} expanded={contextOpen() && !ctxPending()} />
<ContextToolRollingResults parts={ctxParts()} pending={contextOpen() && ctxPending()} />
</>
)}
</Show>
<Show when={shell()}>{(value) => <ShellRollingResults part={value()} animate={props.animate} />}</Show>
<Show when={shell()}>
{(value) => (
<ShellRollingResults
part={value()}
animate={props.animate}
defaultOpen={props.shellToolDefaultOpen}
/>
)}
</Show>
<Show when={!shell() ? part() : undefined}>
{(entry) => (
<Show when={!entry().context}>

View File

@@ -1,7 +1,7 @@
import { attachSpring, motionValue } from "motion"
import type { SpringOptions } from "motion"
import { createEffect, createSignal, onCleanup } from "solid-js"
import { prefersReducedMotion } from "../hooks/use-reduced-motion"
import { useReducedMotion } from "../hooks/use-reduced-motion"
type Opt = Pick<SpringOptions, "visualDuration" | "bounce" | "stiffness" | "damping" | "mass" | "velocity">
const eq = (a: Opt | undefined, b: Opt | undefined) =>
@@ -14,7 +14,7 @@ const eq = (a: Opt | undefined, b: Opt | undefined) =>
export function useSpring(target: () => number, options?: Opt | (() => Opt)) {
const read = () => (typeof options === "function" ? options() : options)
const reduce = prefersReducedMotion
const reduce = useReducedMotion()
const [value, setValue] = createSignal(target())
const source = motionValue(value())
const spring = motionValue(value())

View File

@@ -1,6 +1,6 @@
import { For, Show, batch, createEffect, createMemo, createSignal, on, onCleanup, onMount, type JSX } from "solid-js"
import { useReducedMotion } from "../hooks/use-reduced-motion"
import { animate, clearMaskStyles, GROW_SPRING, type AnimationPlaybackControls, type SpringConfig } from "./motion"
import { prefersReducedMotion } from "../hooks/use-reduced-motion"
export type RollingResultsProps<T> = {
items: T[]
@@ -27,8 +27,7 @@ export function RollingResults<T>(props: RollingResultsProps<T>) {
let shift: AnimationPlaybackControls | undefined
let resize: AnimationPlaybackControls | undefined
let edgeFade: AnimationPlaybackControls | undefined
const reducedMotion = prefersReducedMotion
const reduce = useReducedMotion()
const rows = createMemo(() => Math.max(1, Math.round(props.rows ?? 3)))
const rowHeight = createMemo(() => Math.max(16, Math.round(props.rowHeight ?? 22)))
@@ -54,7 +53,7 @@ export function RollingResults<T>(props: RollingResultsProps<T>) {
return count() - rendered().length
})
const open = createMemo(() => props.open !== false)
const active = createMemo(() => (props.animate !== false || props.spring !== undefined) && !reducedMotion())
const active = createMemo(() => (props.animate !== false || props.spring !== undefined) && !reduce())
const noFade = () => props.noFadeOnCollapse === true
const overflowing = createMemo(() => count() > rows())
const shown = createMemo(() => Math.min(rows(), count()))

View File

@@ -1,7 +1,7 @@
import { createEffect, createMemo, createSignal, onCleanup, onMount, Show } from "solid-js"
import stripAnsi from "strip-ansi"
import type { ToolPart } from "@opencode-ai/sdk/v2"
import { prefersReducedMotion } from "../hooks/use-reduced-motion"
import { useReducedMotion } from "../hooks/use-reduced-motion"
import { useI18n } from "../context/i18n"
import { RollingResults } from "./rolling-results"
import { Icon } from "./icon"
@@ -10,15 +10,7 @@ import { TextShimmer } from "./text-shimmer"
import { Tooltip } from "./tooltip"
import { GROW_SPRING } from "./motion"
import { useSpring } from "./motion-spring"
import {
busy,
createThrottledValue,
hold,
updateScrollMask,
useCollapsible,
useRowWipe,
useToolFade,
} from "./tool-utils"
import { busy, createThrottledValue, updateScrollMask, useCollapsible, useRowWipe, useToolFade } from "./tool-utils"
function ShellRollingSubtitle(props: { text: string; animate?: boolean }) {
let ref: HTMLSpanElement | undefined
@@ -176,23 +168,17 @@ function ShellExpanded(props: { cmd: string; out: string; open: boolean }) {
)
}
export function ShellRollingResults(props: { part: ToolPart; animate?: boolean }) {
export function ShellRollingResults(props: { part: ToolPart; animate?: boolean; defaultOpen?: boolean }) {
const i18n = useI18n()
const reduce = useReducedMotion()
const wiped = new Set<string>()
const [mounted, setMounted] = createSignal(false)
const [userToggled, setUserToggled] = createSignal(false)
const [userOpen, setUserOpen] = createSignal(false)
const [open, setOpen] = createSignal(props.defaultOpen ?? true)
onMount(() => setMounted(true))
const state = createMemo(() => props.part.state as Record<string, any>)
const pending = createMemo(() => busy(props.part.state.status))
const autoOpen = hold(pending, 2000)
const effectiveOpen = createMemo(() => {
if (pending()) return true
if (userToggled()) return userOpen()
return autoOpen()
})
const expanded = createMemo(() => !pending() && !autoOpen() && userToggled() && userOpen())
const previewOpen = createMemo(() => effectiveOpen() && !expanded())
const expanded = createMemo(() => open() && !pending())
const previewOpen = createMemo(() => open() && pending())
const command = createMemo(() => {
const value = state().input?.command ?? state().metadata?.command
if (typeof value === "string") return value
@@ -208,7 +194,6 @@ export function ShellRollingResults(props: { part: ToolPart; animate?: boolean }
if (typeof value === "string") return value
return ""
})
const reduce = prefersReducedMotion
const skip = () => reduce() || props.animate === false
const opacity = useSpring(() => (mounted() ? 1 : 0), GROW_SPRING)
const blur = useSpring(() => (mounted() ? 0 : 2), GROW_SPRING)
@@ -217,12 +202,10 @@ export function ShellRollingResults(props: { part: ToolPart; animate?: boolean }
const headerHeight = useSpring(() => (mounted() ? 37 : 0), GROW_SPRING)
let headerClipRef: HTMLDivElement | undefined
const handleHeaderClick = () => {
if (pending()) return
const el = headerClipRef
const viewport = el?.closest(".scroll-view__viewport") as HTMLElement | null
const beforeY = el?.getBoundingClientRect().top ?? 0
setUserToggled(true)
setUserOpen((prev) => !prev)
setOpen((prev) => !prev)
if (viewport && el) {
requestAnimationFrame(() => {
const afterY = el.getBoundingClientRect().top
@@ -249,7 +232,7 @@ export function ShellRollingResults(props: { part: ToolPart; animate?: boolean }
ref={headerClipRef}
data-slot="shell-rolling-header-clip"
data-scroll-preserve
data-clickable={!pending() ? "true" : "false"}
data-clickable="true"
onClick={handleHeaderClick}
style={{ height: `${skip() ? (mounted() ? 37 : 0) : headerHeight()}px`, overflow: "clip" }}
>
@@ -258,13 +241,11 @@ export function ShellRollingResults(props: { part: ToolPart; animate?: boolean }
<TextShimmer text={i18n.t("ui.tool.shell")} active={pending()} />
</span>
<Show when={subtitle()}>{(text) => <ShellRollingSubtitle text={text()} animate={props.animate} />}</Show>
<Show when={!pending()}>
<span data-slot="shell-rolling-actions">
<span data-slot="shell-rolling-arrow" data-open={effectiveOpen() ? "true" : "false"}>
<Icon name="chevron-down" size="small" />
</span>
<span data-slot="shell-rolling-actions">
<span data-slot="shell-rolling-arrow" data-open={open() ? "true" : "false"}>
<Icon name="chevron-down" size="small" />
</span>
</Show>
</span>
</div>
</div>
<div

View File

@@ -1,4 +1,5 @@
import { createEffect, createSignal, on, onCleanup, onMount } from "solid-js"
import { useReducedMotion } from "../hooks/use-reduced-motion"
import {
animate,
type AnimationPlaybackControls,
@@ -7,7 +8,6 @@ import {
GROW_SPRING,
WIPE_MASK,
} from "./motion"
import { prefersReducedMotion } from "../hooks/use-reduced-motion"
const px = (value: number | string | undefined, fallback: number) => {
if (typeof value === "number") return `${value}px`
@@ -143,12 +143,13 @@ export function TextWipe(props: { text?: string; class?: string; delay?: number;
let ref: HTMLSpanElement | undefined
let frame: number | undefined
let anim: AnimationPlaybackControls | undefined
const reduce = useReducedMotion()
const run = () => {
if (props.animate === false) return
const el = ref
if (!el || !props.text || typeof window === "undefined") return
if (prefersReducedMotion()) return
if (reduce()) return
const mask =
typeof CSS !== "undefined" &&

View File

@@ -1,8 +1,8 @@
import { Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from "solid-js"
import { useReducedMotion } from "../hooks/use-reduced-motion"
import { animate, type AnimationPlaybackControls, GROW_SPRING } from "./motion"
import { TextShimmer } from "./text-shimmer"
import { commonPrefix } from "./text-utils"
import { prefersReducedMotion } from "../hooks/use-reduced-motion"
function contentWidth(el: HTMLSpanElement | undefined) {
if (!el) return 0
@@ -18,6 +18,7 @@ export function ToolStatusTitle(props: {
class?: string
split?: boolean
}) {
const reduce = useReducedMotion()
const split = createMemo(() => commonPrefix(props.activeText, props.doneText))
const suffix = createMemo(
() =>
@@ -38,8 +39,6 @@ export function ToolStatusTitle(props: {
const node = () => (suffix() ? tailRef : swapRef)
const reduce = prefersReducedMotion
const setNodeWidth = (width: string) => {
if (swapRef) swapRef.style.width = width
if (tailRef) tailRef.style.width = width

View File

@@ -1,4 +1,6 @@
import type { ToolPart } from "@opencode-ai/sdk/v2"
import { createEffect, createMemo, createSignal, on, onCleanup, onMount } from "solid-js"
import { useReducedMotion } from "../hooks/use-reduced-motion"
import {
animate,
type AnimationPlaybackControls,
@@ -8,8 +10,6 @@ import {
GROW_SPRING,
WIPE_MASK,
} from "./motion"
import { prefersReducedMotion } from "../hooks/use-reduced-motion"
import type { ToolPart } from "@opencode-ai/sdk/v2"
export const TEXT_RENDER_THROTTLE_MS = 100
@@ -106,57 +106,67 @@ export function useCollapsible(options: {
measure?: () => number
onOpen?: () => void
}) {
const reduce = useReducedMotion()
let heightAnim: AnimationPlaybackControls | undefined
let fadeAnim: AnimationPlaybackControls | undefined
let gen = 0
createEffect(
on(
options.open,
(isOpen) => {
const content = options.content()
const body = options.body()
if (!content || !body) return
heightAnim?.stop()
fadeAnim?.stop()
const id = ++gen
on(options.open, (isOpen) => {
const content = options.content()
const body = options.body()
if (!content || !body) return
heightAnim?.stop()
fadeAnim?.stop()
if (reduce()) {
body.style.opacity = ""
body.style.filter = ""
if (isOpen) {
content.style.display = ""
content.style.height = "0px"
body.style.opacity = "0"
body.style.filter = "blur(2px)"
fadeAnim = animate(body, { opacity: [0, 1], filter: ["blur(2px)", "blur(0px)"] }, COLLAPSIBLE_SPRING)
queueMicrotask(() => {
if (gen !== id) return
const c = options.content()
if (!c) return
const h = options.measure?.() ?? Math.ceil(body.getBoundingClientRect().height)
heightAnim = animate(c, { height: ["0px", `${h}px`] }, COLLAPSIBLE_SPRING)
heightAnim.finished.then(
() => {
if (gen !== id) return
c.style.height = "auto"
options.onOpen?.()
},
() => {},
)
})
content.style.height = "auto"
options.onOpen?.()
return
}
content.style.height = "0px"
content.style.display = "none"
return
}
const id = ++gen
if (isOpen) {
content.style.display = ""
content.style.height = "0px"
body.style.opacity = "0"
body.style.filter = "blur(2px)"
fadeAnim = animate(body, { opacity: [0, 1], filter: ["blur(2px)", "blur(0px)"] }, COLLAPSIBLE_SPRING)
queueMicrotask(() => {
if (gen !== id) return
const c = options.content()
if (!c) return
const h = options.measure?.() ?? Math.ceil(body.getBoundingClientRect().height)
heightAnim = animate(c, { height: ["0px", `${h}px`] }, COLLAPSIBLE_SPRING)
heightAnim.finished.then(
() => {
if (gen !== id) return
c.style.height = "auto"
options.onOpen?.()
},
() => {},
)
})
return
}
const h = content.getBoundingClientRect().height
heightAnim = animate(content, { height: [`${h}px`, "0px"] }, COLLAPSIBLE_SPRING)
fadeAnim = animate(body, { opacity: [1, 0], filter: ["blur(0px)", "blur(2px)"] }, COLLAPSIBLE_SPRING)
heightAnim.finished.then(
() => {
if (gen !== id) return
content.style.display = "none"
},
() => {},
)
},
{ defer: true },
),
const h = content.getBoundingClientRect().height
heightAnim = animate(content, { height: [`${h}px`, "0px"] }, COLLAPSIBLE_SPRING)
fadeAnim = animate(body, { opacity: [1, 0], filter: ["blur(0px)", "blur(2px)"] }, COLLAPSIBLE_SPRING)
heightAnim.finished.then(
() => {
if (gen !== id) return
content.style.display = "none"
},
() => {},
)
}),
)
onCleanup(() => {
@@ -181,7 +191,7 @@ export function useRowWipe(opts: {
ref: () => HTMLElement | undefined
seen: Set<string>
}) {
const reduce = prefersReducedMotion
const reduce = useReducedMotion()
createEffect(() => {
const id = opts.id()
@@ -265,13 +275,14 @@ export function useToolFade(
const delay = options?.delay ?? 0
const wipe = options?.wipe ?? false
const active = options?.animate !== false
const reduce = useReducedMotion()
onMount(() => {
if (!active) return
const el = ref()
if (!el || typeof window === "undefined") return
if (prefersReducedMotion()) return
if (reduce()) return
const mask =
wipe &&

View File

@@ -1,5 +1,3 @@
export * from "./use-filtered-list"
export * from "./create-auto-scroll"
export * from "./use-element-height"
export * from "./use-reduced-motion"
export * from "./use-page-visible"

View File

@@ -1,25 +0,0 @@
import { createEffect, createSignal, onCleanup, type Accessor } from "solid-js"
/**
* Tracks an element's height via ResizeObserver.
* Returns a reactive signal that updates whenever the element resizes.
*/
export function useElementHeight(
ref: Accessor<HTMLElement | undefined> | (() => HTMLElement | undefined),
initial = 0,
): Accessor<number> {
const [height, setHeight] = createSignal(initial)
createEffect(() => {
const el = ref()
if (!el) return
setHeight(el.getBoundingClientRect().height)
const observer = new ResizeObserver(() => {
setHeight(el.getBoundingClientRect().height)
})
observer.observe(el)
onCleanup(() => observer.disconnect())
})
return height
}

View File

@@ -1,11 +0,0 @@
import { createSignal } from "solid-js"
export const pageVisible = /* @__PURE__ */ (() => {
const [visible, setVisible] = createSignal(true)
if (typeof document !== "undefined") {
const sync = () => setVisible(document.visibilityState !== "hidden")
sync()
document.addEventListener("visibilitychange", sync)
}
return visible
})()

View File

@@ -1,9 +1,10 @@
import { createSignal } from "solid-js"
import { isHydrated } from "@solid-primitives/lifecycle"
import { createMediaQuery } from "@solid-primitives/media"
import { createHydratableSingletonRoot } from "@solid-primitives/rootless"
export const prefersReducedMotion = /* @__PURE__ */ (() => {
if (typeof window === "undefined") return () => false
const mql = window.matchMedia("(prefers-reduced-motion: reduce)")
const [reduced, setReduced] = createSignal(mql.matches)
mql.addEventListener("change", () => setReduced(mql.matches))
return reduced
})()
const query = "(prefers-reduced-motion: reduce)"
export const useReducedMotion = createHydratableSingletonRoot(() => {
const value = createMediaQuery(query)
return () => !isHydrated() || value()
})

View File

@@ -85,214 +85,218 @@
0 0 0 1px var(--border-weak-base, rgba(0, 0, 0, 0.07)), 0 36px 80px 0 rgba(0, 0, 0, 0.03),
0 13.141px 29.201px 0 rgba(0, 0, 0, 0.04), 0 6.38px 14.177px 0 rgba(0, 0, 0, 0.05),
0 3.127px 6.95px 0 rgba(0, 0, 0, 0.06), 0 1.237px 2.748px 0 rgba(0, 0, 0, 0.09);
--shadow-sidebar-overlay:
0 100px 80px 0 rgba(0, 0, 0, 0.29), 0 41.778px 33.422px 0 rgba(0, 0, 0, 0.21),
0 22.336px 17.869px 0 rgba(0, 0, 0, 0.17), 0 12.522px 10.017px 0 rgba(0, 0, 0, 0.14),
0 6.65px 5.32px 0 rgba(0, 0, 0, 0.12), 0 2.767px 2.214px 0 rgba(0, 0, 0, 0.08);
color-scheme: light;
--text-mix-blend-mode: multiply;
/* OC-1 fallback variables (light) */
--background-base: #f8f7f7;
--background-weak: var(--smoke-light-3);
--background-strong: var(--smoke-light-1);
/* OC-2 fallback variables (light) */
--background-base: #f8f8f8;
--background-weak: #f3f3f3;
--background-strong: #fcfcfc;
--background-stronger: #fcfcfc;
--surface-base: var(--smoke-light-alpha-2);
--base: var(--smoke-light-alpha-2);
--surface-base-hover: #0500000f;
--surface-base-active: var(--smoke-light-alpha-3);
--surface-base-interactive-active: var(--cobalt-light-alpha-3);
--base2: var(--smoke-light-alpha-2);
--base3: var(--smoke-light-alpha-2);
--surface-inset-base: var(--smoke-light-alpha-2);
--surface-inset-base-hover: var(--smoke-light-alpha-3);
--surface-inset-strong: #1f000017;
--surface-inset-strong-hover: #1f000017;
--surface-raised-base: var(--smoke-light-alpha-1);
--surface-float-base: var(--smoke-dark-1);
--surface-float-base-hover: var(--smoke-dark-2);
--surface-raised-base-hover: var(--smoke-light-alpha-2);
--surface-raised-base-active: var(--smoke-light-alpha-3);
--surface-raised-strong: var(--smoke-light-1);
--surface-raised-strong-hover: var(--white);
--surface-raised-stronger: var(--white);
--surface-raised-stronger-hover: var(--white);
--surface-weak: var(--smoke-light-alpha-3);
--surface-weaker: var(--smoke-light-alpha-4);
--surface-base: rgba(0, 0, 0, 0.031);
--base: rgba(0, 0, 0, 0.034);
--surface-base-hover: rgba(0, 0, 0, 0.059);
--surface-base-active: rgba(0, 0, 0, 0.051);
--surface-base-interactive-active: rgba(3, 76, 255, 0.09);
--base2: rgba(0, 0, 0, 0.034);
--base3: rgba(0, 0, 0, 0.034);
--surface-inset-base: rgba(0, 0, 0, 0.034);
--surface-inset-base-hover: rgba(0, 0, 0, 0.055);
--surface-inset-strong: rgba(0, 0, 0, 0.09);
--surface-inset-strong-hover: rgba(0, 0, 0, 0.09);
--surface-raised-base: rgba(0, 0, 0, 0.031);
--surface-float-base: #161616;
--surface-float-base-hover: #1c1c1c;
--surface-raised-base-hover: rgba(0, 0, 0, 0.051);
--surface-raised-base-active: rgba(0, 0, 0, 0.09);
--surface-raised-strong: #fcfcfc;
--surface-raised-strong-hover: #ffffff;
--surface-raised-stronger: #ffffff;
--surface-raised-stronger-hover: #ffffff;
--surface-weak: rgba(0, 0, 0, 0.051);
--surface-weaker: rgba(0, 0, 0, 0.071);
--surface-strong: #ffffff;
--surface-stronger-non-alpha: var(--surface-raised-stronger-non-alpha);
--surface-raised-stronger-non-alpha: var(--white);
--surface-brand-base: var(--yuzu-light-9);
--surface-brand-hover: var(--yuzu-light-10);
--surface-interactive-base: var(--cobalt-light-3);
--surface-interactive-hover: #e5f0ff;
--surface-interactive-weak: var(--cobalt-light-2);
--surface-interactive-weak-hover: var(--cobalt-light-3);
--surface-success-base: var(--apple-light-3);
--surface-success-weak: var(--apple-light-2);
--surface-success-strong: var(--apple-light-9);
--surface-warning-base: var(--solaris-light-3);
--surface-warning-weak: var(--solaris-light-2);
--surface-warning-strong: var(--solaris-light-9);
--surface-critical-base: var(--ember-light-3);
--surface-critical-weak: var(--ember-light-2);
--surface-critical-strong: var(--ember-light-9);
--surface-info-base: var(--lilac-light-3);
--surface-info-weak: var(--lilac-light-2);
--surface-info-strong: var(--lilac-light-9);
--surface-raised-stronger-non-alpha: #ffffff;
--surface-brand-base: #dcde8d;
--surface-brand-hover: #d0d283;
--surface-interactive-base: #ecf3ff;
--surface-interactive-hover: #e0eaff;
--surface-interactive-weak: #f7faff;
--surface-interactive-weak-hover: #ecf3ff;
--surface-success-base: #dbfed7;
--surface-success-weak: #f0feee;
--surface-success-strong: #12c905;
--surface-warning-base: #fcf3cb;
--surface-warning-weak: #fdfaec;
--surface-warning-strong: #fbdd46;
--surface-critical-base: #feefeb;
--surface-critical-weak: #fff8f6;
--surface-critical-strong: #fc533a;
--surface-info-base: #fdecfe;
--surface-info-weak: #fef7ff;
--surface-info-strong: #a753ae;
--surface-diff-unchanged-base: #ffffff00;
--surface-diff-skip-base: var(--smoke-light-2);
--surface-diff-hidden-base: var(--blue-light-3);
--surface-diff-hidden-weak: var(--blue-light-2);
--surface-diff-hidden-weaker: var(--blue-light-1);
--surface-diff-hidden-strong: var(--blue-light-5);
--surface-diff-hidden-stronger: var(--blue-light-9);
--surface-diff-add-base: #dafbe0;
--surface-diff-add-weak: var(--mint-light-2);
--surface-diff-add-weaker: var(--mint-light-1);
--surface-diff-add-strong: var(--mint-light-5);
--surface-diff-add-stronger: var(--mint-light-9);
--surface-diff-delete-base: var(--ember-light-3);
--surface-diff-delete-weak: var(--ember-light-2);
--surface-diff-delete-weaker: var(--ember-light-1);
--surface-diff-delete-strong: var(--ember-light-6);
--surface-diff-delete-stronger: var(--ember-light-9);
--input-base: var(--smoke-light-1);
--input-hover: var(--smoke-light-2);
--input-active: var(--cobalt-light-1);
--input-selected: var(--cobalt-light-4);
--input-focus: var(--cobalt-light-1);
--input-disabled: var(--smoke-light-4);
--text-base: var(--smoke-light-11);
--text-weak: var(--smoke-light-9);
--text-weaker: var(--smoke-light-8);
--text-strong: var(--smoke-light-12);
--text-invert-base: var(--smoke-dark-alpha-11);
--text-invert-weak: var(--smoke-dark-alpha-9);
--text-invert-weaker: var(--smoke-dark-alpha-8);
--text-invert-strong: var(--smoke-dark-alpha-12);
--text-interactive-base: var(--cobalt-light-9);
--text-on-brand-base: var(--smoke-light-alpha-11);
--text-on-interactive-base: var(--smoke-light-1);
--text-on-interactive-weak: var(--smoke-dark-alpha-11);
--text-on-success-base: var(--apple-light-10);
--text-on-critical-base: var(--ember-light-10);
--text-on-critical-weak: var(--ember-light-8);
--text-on-critical-strong: var(--ember-light-12);
--text-on-warning-base: var(--smoke-dark-alpha-11);
--text-on-info-base: var(--smoke-dark-alpha-11);
--text-diff-add-base: var(--mint-light-11);
--text-diff-delete-base: var(--ember-light-10);
--text-diff-delete-strong: var(--ember-light-12);
--text-diff-add-strong: var(--mint-light-12);
--text-on-info-weak: var(--smoke-dark-alpha-9);
--text-on-info-strong: var(--smoke-dark-alpha-12);
--text-on-warning-weak: var(--smoke-dark-alpha-9);
--text-on-warning-strong: var(--smoke-dark-alpha-12);
--text-on-success-weak: var(--apple-light-6);
--text-on-success-strong: var(--apple-light-12);
--text-on-brand-weak: var(--smoke-light-alpha-9);
--text-on-brand-weaker: var(--smoke-light-alpha-8);
--text-on-brand-strong: var(--smoke-light-alpha-12);
--button-primary-base: var(--smoke-light-12);
--button-secondary-base: #fdfcfc;
--button-secondary-hover: #faf9f9;
--border-base: var(--smoke-light-alpha-7);
--border-hover: var(--smoke-light-alpha-8);
--border-active: var(--smoke-light-alpha-9);
--border-selected: var(--cobalt-light-alpha-9);
--border-disabled: var(--smoke-light-alpha-8);
--border-focus: var(--smoke-light-alpha-9);
--border-weak-base: var(--smoke-light-alpha-5);
--border-strong-base: var(--smoke-light-alpha-7);
--border-strong-hover: var(--smoke-light-alpha-8);
--border-strong-active: var(--smoke-light-alpha-7);
--border-strong-selected: var(--cobalt-light-alpha-6);
--border-strong-disabled: var(--smoke-light-alpha-6);
--border-strong-focus: var(--smoke-light-alpha-7);
--border-weak-hover: var(--smoke-light-alpha-6);
--border-weak-active: var(--smoke-light-alpha-7);
--border-weak-selected: var(--cobalt-light-alpha-5);
--border-weak-disabled: var(--smoke-light-alpha-6);
--border-weak-focus: var(--smoke-light-alpha-7);
--border-weaker-base: var(--smoke-light-alpha-3);
--border-interactive-base: var(--cobalt-light-7);
--border-interactive-hover: var(--cobalt-light-8);
--border-interactive-active: var(--cobalt-light-9);
--border-interactive-selected: var(--cobalt-light-9);
--border-interactive-disabled: var(--smoke-light-8);
--border-interactive-focus: var(--cobalt-light-9);
--border-success-base: var(--apple-light-6);
--border-success-hover: var(--apple-light-7);
--border-success-selected: var(--apple-light-9);
--border-warning-base: var(--solaris-light-6);
--border-warning-hover: var(--solaris-light-7);
--border-warning-selected: var(--solaris-light-9);
--border-critical-base: var(--ember-light-6);
--border-critical-hover: var(--ember-light-7);
--border-critical-selected: var(--ember-light-9);
--border-info-base: var(--lilac-light-6);
--border-info-hover: var(--lilac-light-7);
--border-info-selected: var(--lilac-light-9);
--icon-base: var(--smoke-light-9);
--icon-hover: var(--smoke-light-11);
--icon-active: var(--smoke-light-12);
--icon-selected: var(--smoke-light-12);
--icon-disabled: var(--smoke-light-8);
--icon-focus: var(--smoke-light-12);
--surface-diff-skip-base: #f8f8f8;
--surface-diff-hidden-base: #eaf4ff;
--surface-diff-hidden-weak: #f6faff;
--surface-diff-hidden-weaker: #fbfdff;
--surface-diff-hidden-strong: #cae3ff;
--surface-diff-hidden-stronger: #2090f5;
--surface-diff-add-base: #e3fae1;
--surface-diff-add-weak: #f4fcf3;
--surface-diff-add-weaker: #fbfefb;
--surface-diff-add-strong: #c2eebf;
--surface-diff-add-stronger: #9ff29a;
--surface-diff-delete-base: #feefeb;
--surface-diff-delete-weak: #fff8f6;
--surface-diff-delete-weaker: #fffcfb;
--surface-diff-delete-strong: #fdc3b7;
--surface-diff-delete-stronger: #fc533a;
--input-base: #fcfcfc;
--input-hover: #f8f8f8;
--input-active: #fcfdff;
--input-selected: #e0eaff;
--input-focus: #fcfdff;
--input-disabled: #ededed;
--text-base: #6f6f6f;
--text-weak: #8f8f8f;
--text-weaker: #c7c7c7;
--text-strong: #171717;
--text-invert-base: #f8f8f8;
--text-invert-weak: #f3f3f3;
--text-invert-weaker: #ededed;
--text-invert-strong: #fcfcfc;
--text-interactive-base: #034cff;
--text-on-brand-base: rgba(0, 0, 0, 0.574);
--text-on-interactive-base: #fcfcfc;
--text-on-interactive-weak: rgba(0, 0, 0, 0.574);
--text-on-success-base: #2dba26;
--text-on-critical-base: #ed4831;
--text-on-critical-weak: #fe806a;
--text-on-critical-strong: #601a0f;
--text-on-warning-base: rgba(0, 0, 0, 0.574);
--text-on-info-base: rgba(0, 0, 0, 0.574);
--text-diff-add-base: #3a8437;
--text-diff-delete-base: #ed4831;
--text-diff-delete-strong: #601a0f;
--text-diff-add-strong: #1d3e1c;
--text-on-info-weak: rgba(0, 0, 0, 0.453);
--text-on-info-strong: rgba(0, 0, 0, 0.915);
--text-on-warning-weak: rgba(0, 0, 0, 0.453);
--text-on-warning-strong: rgba(0, 0, 0, 0.915);
--text-on-success-weak: #96ec8e;
--text-on-success-strong: #044202;
--text-on-brand-weak: rgba(0, 0, 0, 0.453);
--text-on-brand-weaker: rgba(0, 0, 0, 0.232);
--text-on-brand-strong: rgba(0, 0, 0, 0.915);
--button-primary-base: #171717;
--button-secondary-base: #fcfcfc;
--button-secondary-hover: #f8f8f8;
--button-ghost-hover: rgba(0, 0, 0, 0.031);
--button-ghost-hover2: rgba(0, 0, 0, 0.051);
--border-base: rgba(0, 0, 0, 0.162);
--border-hover: rgba(0, 0, 0, 0.236);
--border-active: rgba(0, 0, 0, 0.46);
--border-selected: rgba(3, 76, 255, 0.99);
--border-disabled: rgba(0, 0, 0, 0.236);
--border-focus: rgba(0, 0, 0, 0.46);
--border-weak-base: #e5e5e5;
--border-strong-base: rgba(0, 0, 0, 0.151);
--border-strong-hover: rgba(0, 0, 0, 0.232);
--border-strong-active: rgba(0, 0, 0, 0.151);
--border-strong-selected: rgba(3, 76, 255, 0.31);
--border-strong-disabled: rgba(0, 0, 0, 0.118);
--border-strong-focus: rgba(0, 0, 0, 0.151);
--border-weak-hover: rgba(0, 0, 0, 0.118);
--border-weak-active: rgba(0, 0, 0, 0.151);
--border-weak-selected: rgba(3, 76, 255, 0.24);
--border-weak-disabled: rgba(0, 0, 0, 0.118);
--border-weak-focus: rgba(0, 0, 0, 0.151);
--border-weaker-base: #f0f0f0;
--border-weaker-hover: rgba(0, 0, 0, 0.075);
--border-weaker-active: rgba(0, 0, 0, 0.118);
--border-weaker-selected: rgba(3, 76, 255, 0.16);
--border-weaker-disabled: rgba(0, 0, 0, 0.034);
--border-weaker-focus: rgba(0, 0, 0, 0.118);
--border-interactive-base: #a3c1fd;
--border-interactive-hover: #7ea9ff;
--border-interactive-active: #034cff;
--border-interactive-selected: #034cff;
--border-interactive-disabled: #c7c7c7;
--border-interactive-focus: #034cff;
--border-success-base: #96ec8e;
--border-success-hover: #7add71;
--border-success-selected: #12c905;
--border-warning-base: #e8d479;
--border-warning-hover: #d8c158;
--border-warning-selected: #fbdd46;
--border-critical-base: #fdc3b7;
--border-critical-hover: #ffa796;
--border-critical-selected: #fc533a;
--border-info-base: #f4bdf8;
--border-info-hover: #e6a8ea;
--border-info-selected: #a753ae;
--border-color: #ffffff;
--icon-base: #8f8f8f;
--icon-hover: #6f6f6f;
--icon-active: #171717;
--icon-selected: #171717;
--icon-disabled: #c7c7c7;
--icon-focus: #171717;
--icon-invert-base: #ffffff;
--icon-weak-base: var(--smoke-light-7);
--icon-weak-hover: var(--smoke-light-8);
--icon-weak-active: var(--smoke-light-9);
--icon-weak-selected: var(--smoke-light-10);
--icon-weak-disabled: var(--smoke-light-6);
--icon-weak-focus: var(--smoke-light-9);
--icon-strong-base: var(--smoke-light-12);
--icon-weak-base: #dbdbdb;
--icon-weak-hover: #c7c7c7;
--icon-weak-active: #8f8f8f;
--icon-weak-selected: #858585;
--icon-weak-disabled: #e2e2e2;
--icon-weak-focus: #8f8f8f;
--icon-strong-base: #171717;
--icon-strong-hover: #151313;
--icon-strong-active: #020202;
--icon-strong-selected: #020202;
--icon-strong-disabled: var(--smoke-light-8);
--icon-strong-disabled: #c7c7c7;
--icon-strong-focus: #020202;
--icon-brand-base: var(--smoke-light-12);
--icon-interactive-base: var(--cobalt-light-9);
--icon-success-base: var(--apple-light-7);
--icon-success-hover: var(--apple-light-8);
--icon-success-active: var(--apple-light-11);
--icon-warning-base: var(--amber-light-9);
--icon-warning-hover: var(--amber-light-8);
--icon-warning-active: var(--amber-light-11);
--icon-critical-base: var(--ember-light-10);
--icon-critical-hover: var(--ember-light-11);
--icon-critical-active: var(--ember-light-12);
--icon-info-base: var(--lilac-light-7);
--icon-info-hover: var(--lilac-light-8);
--icon-info-active: var(--lilac-light-11);
--icon-on-brand-base: var(--smoke-light-alpha-11);
--icon-on-brand-hover: var(--smoke-light-alpha-12);
--icon-on-brand-selected: var(--smoke-light-alpha-12);
--icon-on-interactive-base: var(--smoke-light-1);
--icon-agent-plan-base: var(--purple-light-9);
--icon-agent-docs-base: var(--amber-light-9);
--icon-agent-ask-base: var(--cyan-light-9);
--icon-agent-build-base: var(--cobalt-light-9);
--icon-on-success-base: var(--apple-light-alpha-9);
--icon-on-success-hover: var(--apple-light-alpha-10);
--icon-on-success-selected: var(--apple-light-alpha-11);
--icon-on-warning-base: var(--amber-lightalpha-9);
--icon-on-warning-hover: var(--amber-lightalpha-10);
--icon-on-warning-selected: var(--amber-lightalpha-11);
--icon-on-critical-base: var(--ember-light-alpha-9);
--icon-on-critical-hover: var(--ember-light-alpha-10);
--icon-on-critical-selected: var(--ember-light-alpha-11);
--icon-on-info-base: var(--lilac-light-9);
--icon-on-info-hover: var(--lilac-light-alpha-10);
--icon-on-info-selected: var(--lilac-light-alpha-11);
--icon-diff-add-base: var(--mint-light-11);
--icon-diff-add-hover: var(--mint-light-12);
--icon-diff-add-active: var(--mint-light-12);
--icon-diff-delete-base: var(--ember-light-10);
--icon-diff-delete-hover: var(--ember-light-11);
--icon-brand-base: #171717;
--icon-interactive-base: #034cff;
--icon-success-base: #7add71;
--icon-success-hover: #4cc944;
--icon-success-active: #078901;
--icon-warning-base: #ebb76e;
--icon-warning-hover: #da9e40;
--icon-warning-active: #95671b;
--icon-critical-base: #ed4831;
--icon-critical-hover: #ca2d17;
--icon-critical-active: #601a0f;
--icon-info-base: #e6a8ea;
--icon-info-hover: #d58cda;
--icon-info-active: #9b4da1;
--icon-on-brand-base: rgba(0, 0, 0, 0.574);
--icon-on-brand-hover: rgba(0, 0, 0, 0.915);
--icon-on-brand-selected: rgba(0, 0, 0, 0.915);
--icon-on-interactive-base: #fcfcfc;
--icon-agent-plan-base: #a753ae;
--icon-agent-docs-base: #fcb239;
--icon-agent-ask-base: #2090f5;
--icon-agent-build-base: #034cff;
--icon-on-success-base: rgba(18, 201, 5, 0.9);
--icon-on-success-hover: rgba(45, 186, 38, 0.9);
--icon-on-success-selected: rgba(7, 137, 1, 0.9);
--icon-on-warning-base: rgba(252, 178, 57, 0.9);
--icon-on-warning-hover: rgba(239, 167, 46, 0.9);
--icon-on-warning-selected: rgba(149, 103, 27, 0.9);
--icon-on-critical-base: rgba(252, 83, 58, 0.9);
--icon-on-critical-hover: rgba(237, 72, 49, 0.9);
--icon-on-critical-selected: rgba(202, 45, 23, 0.9);
--icon-on-info-base: #a753ae;
--icon-on-info-hover: rgba(155, 73, 162, 0.9);
--icon-on-info-selected: rgba(155, 77, 161, 0.9);
--icon-diff-add-base: #3a8437;
--icon-diff-add-hover: #1d3e1c;
--icon-diff-add-active: #1d3e1c;
--icon-diff-delete-base: #ed4831;
--icon-diff-delete-hover: #ca2d17;
--icon-diff-modified-base: #ff8c00;
--syntax-comment: var(--text-weak);
--syntax-regexp: var(--text-base);
@@ -306,12 +310,12 @@
--syntax-constant: #007b80;
--syntax-punctuation: var(--text-base);
--syntax-object: var(--text-strong);
--syntax-success: var(--apple-light-10);
--syntax-warning: var(--amber-light-10);
--syntax-critical: var(--ember-light-10);
--syntax-success: #2dba26;
--syntax-warning: #efa72e;
--syntax-critical: #ed4831;
--syntax-info: #0092a8;
--syntax-diff-add: var(--mint-light-11);
--syntax-diff-delete: var(--ember-light-11);
--syntax-diff-add: #3a8437;
--syntax-diff-delete: #ca2d17;
--syntax-diff-unknown: #ff0000;
--markdown-heading: #d68c27;
--markdown-text: #1a1a1a;
@@ -327,9 +331,6 @@
--markdown-image: #3b7dd8;
--markdown-image-text: #318795;
--markdown-code-block: #1a1a1a;
--border-color: #ffffff;
--button-ghost-hover: var(--smoke-light-alpha-2);
--button-ghost-hover2: var(--smoke-light-alpha-3);
--avatar-background-pink: #feeef8;
--avatar-background-mint: #e1fbf4;
--avatar-background-orange: #fff1e7;
@@ -342,210 +343,220 @@
--avatar-text-purple: #8445bc;
--avatar-text-cyan: #0894b3;
--avatar-text-lime: #5d770d;
--text-stronger: #171717;
@media (prefers-color-scheme: dark) {
color-scheme: dark;
--text-mix-blend-mode: plus-lighter;
/* OC-1 fallback variables (dark) */
--background-base: var(--smoke-dark-1);
--background-weak: #1c1717;
--background-strong: #151313;
--background-stronger: #191515;
--surface-base: var(--smoke-dark-alpha-2);
--base: var(--smoke-dark-alpha-2);
--surface-base-hover: #e0b7b716;
--surface-base-active: var(--smoke-dark-alpha-3);
--surface-base-interactive-active: var(--cobalt-dark-alpha-2);
--base2: var(--smoke-dark-alpha-2);
--base3: var(--smoke-dark-alpha-2);
--surface-inset-base: #0e0b0b7f;
--surface-inset-base-hover: #0e0b0b7f;
--surface-inset-strong: #060505cc;
--surface-inset-strong-hover: #060505cc;
--surface-raised-base: var(--smoke-dark-alpha-3);
--surface-float-base: var(--smoke-dark-1);
--surface-float-base-hover: var(--smoke-dark-2);
--surface-raised-base-hover: var(--smoke-dark-alpha-4);
--surface-raised-base-active: var(--smoke-dark-alpha-5);
--surface-raised-strong: var(--smoke-dark-alpha-4);
--surface-raised-strong-hover: var(--smoke-dark-alpha-6);
--surface-raised-stronger: var(--smoke-dark-alpha-6);
--surface-raised-stronger-hover: var(--smoke-dark-alpha-7);
--surface-weak: var(--smoke-dark-alpha-4);
--surface-weaker: var(--smoke-dark-alpha-5);
--surface-strong: var(--smoke-dark-alpha-7);
/* OC-2 fallback variables (dark) */
--background-base: #101010;
--background-weak: #1e1e1e;
--background-strong: #121212;
--background-stronger: #151515;
--surface-base: rgba(255, 255, 255, 0.031);
--base: rgba(255, 255, 255, 0.034);
--surface-base-hover: rgba(255, 255, 255, 0.039);
--surface-base-active: rgba(255, 255, 255, 0.059);
--surface-base-interactive-active: rgba(3, 76, 255, 0.125);
--base2: rgba(255, 255, 255, 0.034);
--base3: rgba(255, 255, 255, 0.034);
--surface-inset-base: rgba(0, 0, 0, 0.5);
--surface-inset-base-hover: rgba(0, 0, 0, 0.5);
--surface-inset-strong: rgba(0, 0, 0, 0.8);
--surface-inset-strong-hover: rgba(0, 0, 0, 0.8);
--surface-raised-base: rgba(255, 255, 255, 0.059);
--surface-float-base: #161616;
--surface-float-base-hover: #1c1c1c;
--surface-raised-base-hover: rgba(255, 255, 255, 0.078);
--surface-raised-base-active: rgba(255, 255, 255, 0.102);
--surface-raised-strong: rgba(255, 255, 255, 0.078);
--surface-raised-strong-hover: rgba(255, 255, 255, 0.129);
--surface-raised-stronger: rgba(255, 255, 255, 0.129);
--surface-raised-stronger-hover: rgba(255, 255, 255, 0.169);
--surface-weak: rgba(255, 255, 255, 0.078);
--surface-weaker: rgba(255, 255, 255, 0.102);
--surface-strong: rgba(255, 255, 255, 0.169);
--surface-stronger-non-alpha: var(--surface-raised-stronger-non-alpha);
--surface-raised-stronger-non-alpha: var(--smoke-dark-3);
--surface-brand-base: var(--yuzu-light-9);
--surface-brand-hover: var(--yuzu-light-10);
--surface-interactive-base: var(--cobalt-dark-3);
--surface-interactive-hover: #0a1d4d;
--surface-interactive-weak: var(--cobalt-dark-2);
--surface-interactive-weak-hover: var(--cobalt-light-3);
--surface-success-base: var(--apple-light-3);
--surface-success-weak: var(--apple-light-2);
--surface-success-strong: var(--apple-light-9);
--surface-warning-base: var(--solaris-light-3);
--surface-warning-weak: var(--solaris-light-2);
--surface-warning-strong: var(--solaris-light-9);
--surface-critical-base: var(--ember-dark-3);
--surface-critical-weak: var(--ember-dark-2);
--surface-critical-strong: var(--ember-dark-9);
--surface-info-base: var(--lilac-light-3);
--surface-info-weak: var(--lilac-light-2);
--surface-info-strong: var(--lilac-light-9);
--surface-diff-unchanged-base: var(--smoke-dark-1);
--surface-diff-skip-base: var(--smoke-dark-alpha-1);
--surface-diff-hidden-base: var(--blue-dark-2);
--surface-diff-hidden-weak: var(--blue-dark-1);
--surface-diff-hidden-weaker: var(--blue-dark-3);
--surface-diff-hidden-strong: var(--blue-dark-5);
--surface-diff-hidden-stronger: var(--blue-dark-11);
--surface-diff-add-base: var(--mint-dark-3);
--surface-diff-add-weak: var(--mint-dark-4);
--surface-diff-add-weaker: var(--mint-dark-3);
--surface-diff-add-strong: var(--mint-dark-5);
--surface-diff-add-stronger: var(--mint-dark-11);
--surface-diff-delete-base: var(--ember-dark-3);
--surface-diff-delete-weak: var(--ember-dark-4);
--surface-diff-delete-weaker: var(--ember-dark-3);
--surface-diff-delete-strong: var(--ember-dark-5);
--surface-diff-delete-stronger: var(--ember-dark-11);
--input-base: var(--smoke-dark-2);
--input-hover: var(--smoke-dark-2);
--input-active: var(--cobalt-dark-1);
--input-selected: var(--cobalt-dark-2);
--input-focus: var(--cobalt-dark-1);
--input-disabled: var(--smoke-dark-4);
--text-base: var(--smoke-dark-alpha-11);
--text-weak: var(--smoke-dark-alpha-9);
--text-weaker: var(--smoke-dark-alpha-8);
--text-strong: var(--smoke-dark-alpha-12);
--text-invert-base: var(--smoke-dark-alpha-11);
--text-invert-weak: var(--smoke-dark-alpha-9);
--text-invert-weaker: var(--smoke-dark-alpha-8);
--text-invert-strong: var(--smoke-dark-alpha-12);
--text-interactive-base: var(--cobalt-dark-11);
--text-on-brand-base: var(--smoke-dark-alpha-11);
--text-on-interactive-base: var(--smoke-dark-12);
--text-on-interactive-weak: var(--smoke-dark-alpha-11);
--text-on-success-base: var(--apple-dark-9);
--text-on-critical-base: var(--ember-dark-9);
--text-on-critical-weak: var(--ember-dark-8);
--text-on-critical-strong: var(--ember-dark-12);
--text-on-warning-base: var(--smoke-dark-alpha-11);
--text-on-info-base: var(--smoke-dark-alpha-11);
--text-diff-add-base: var(--mint-dark-11);
--text-diff-delete-base: var(--ember-dark-9);
--text-diff-delete-strong: var(--ember-dark-12);
--text-diff-add-strong: var(--mint-dark-8);
--text-on-info-weak: var(--smoke-dark-alpha-9);
--text-on-info-strong: var(--smoke-dark-alpha-12);
--text-on-warning-weak: var(--smoke-dark-alpha-9);
--text-on-warning-strong: var(--smoke-dark-alpha-12);
--text-on-success-weak: var(--apple-dark-8);
--text-on-success-strong: var(--apple-dark-12);
--text-on-brand-weak: var(--smoke-dark-alpha-9);
--text-on-brand-weaker: var(--smoke-dark-alpha-8);
--text-on-brand-strong: var(--smoke-dark-alpha-12);
--button-primary-base: var(--smoke-dark-12);
--button-secondary-base: #231f1f;
--button-secondary-hover: #2a2727;
--border-base: var(--smoke-dark-alpha-7);
--border-hover: var(--smoke-dark-alpha-8);
--border-active: var(--smoke-dark-alpha-9);
--border-selected: var(--cobalt-dark-alpha-11);
--border-disabled: var(--smoke-dark-alpha-8);
--border-focus: var(--smoke-dark-alpha-9);
--border-weak-base: var(--smoke-dark-alpha-6);
--border-strong-base: var(--smoke-dark-alpha-8);
--border-strong-hover: var(--smoke-dark-alpha-7);
--border-strong-active: var(--smoke-dark-alpha-8);
--border-strong-selected: var(--cobalt-dark-alpha-6);
--border-strong-disabled: var(--smoke-dark-alpha-6);
--border-strong-focus: var(--smoke-dark-alpha-8);
--border-weak-hover: var(--smoke-dark-alpha-7);
--border-weak-active: var(--smoke-dark-alpha-8);
--border-weak-selected: var(--cobalt-dark-alpha-6);
--border-weak-disabled: var(--smoke-dark-alpha-6);
--border-weak-focus: var(--smoke-dark-alpha-8);
--border-interactive-base: var(--cobalt-light-7);
--border-interactive-hover: var(--cobalt-light-8);
--border-interactive-active: var(--cobalt-light-9);
--border-interactive-selected: var(--cobalt-light-9);
--border-interactive-disabled: var(--smoke-light-8);
--border-interactive-focus: var(--cobalt-light-9);
--border-success-base: var(--apple-light-6);
--border-success-hover: var(--apple-light-7);
--border-success-selected: var(--apple-light-9);
--border-warning-base: var(--solaris-light-6);
--border-warning-hover: var(--solaris-light-7);
--border-warning-selected: var(--solaris-light-9);
--border-critical-base: var(--ember-dark-5);
--border-critical-hover: var(--ember-dark-7);
--border-critical-selected: var(--ember-dark-9);
--border-info-base: var(--lilac-light-6);
--border-info-hover: var(--lilac-light-7);
--border-info-selected: var(--lilac-light-9);
--icon-base: var(--smoke-dark-9);
--icon-hover: var(--smoke-dark-10);
--icon-active: var(--smoke-dark-11);
--icon-selected: var(--smoke-dark-12);
--icon-disabled: var(--smoke-dark-7);
--icon-focus: var(--smoke-dark-12);
--icon-invert-base: var(--smoke-dark-1);
--icon-weak-base: var(--smoke-dark-6);
--icon-weak-hover: var(--smoke-light-7);
--icon-weak-active: var(--smoke-light-8);
--icon-weak-selected: var(--smoke-light-9);
--icon-weak-disabled: var(--smoke-light-4);
--icon-weak-focus: var(--smoke-light-9);
--icon-strong-base: var(--smoke-dark-12);
--surface-raised-stronger-non-alpha: #1c1c1c;
--surface-brand-base: #fab283;
--surface-brand-hover: #eda779;
--surface-interactive-base: #091f52;
--surface-interactive-hover: #091f52;
--surface-interactive-weak: #0b1730;
--surface-interactive-weak-hover: #ecf3ff;
--surface-success-base: #062d04;
--surface-success-weak: #0a1e08;
--surface-success-strong: #12c905;
--surface-warning-base: #fdf3cf;
--surface-warning-weak: #fdfaed;
--surface-warning-strong: #fcd53a;
--surface-critical-base: #42120b;
--surface-critical-weak: #28110c;
--surface-critical-strong: #fc533a;
--surface-info-base: #feecfe;
--surface-info-weak: #fdf7fe;
--surface-info-strong: #edb2f1;
--surface-diff-unchanged-base: #161616;
--surface-diff-skip-base: #00000000;
--surface-diff-hidden-base: #0c1928;
--surface-diff-hidden-weak: #09131d;
--surface-diff-hidden-weaker: #082542;
--surface-diff-hidden-strong: #073966;
--surface-diff-hidden-stronger: #8ec2fc;
--surface-diff-add-base: #1a2919;
--surface-diff-add-weak: #1f351e;
--surface-diff-add-weaker: #1a2919;
--surface-diff-add-strong: #264024;
--surface-diff-add-stronger: #9bcd97;
--surface-diff-delete-base: #42120b;
--surface-diff-delete-weak: #580f06;
--surface-diff-delete-weaker: #42120b;
--surface-diff-delete-strong: #6a1206;
--surface-diff-delete-stronger: #faa494;
--input-base: #1c1c1c;
--input-hover: #1c1c1c;
--input-active: #091123;
--input-selected: #0b1730;
--input-focus: #091123;
--input-disabled: #282828;
--text-base: rgba(255, 255, 255, 0.618);
--text-weak: rgba(255, 255, 255, 0.422);
--text-weaker: rgba(255, 255, 255, 0.284);
--text-strong: rgba(255, 255, 255, 0.936);
--text-invert-base: #a0a0a0;
--text-invert-weak: #707070;
--text-invert-weaker: #505050;
--text-invert-strong: #ededed;
--text-interactive-base: #9dbefe;
--text-on-brand-base: rgba(255, 255, 255, 0.603);
--text-on-interactive-base: #ededed;
--text-on-interactive-weak: rgba(255, 255, 255, 0.603);
--text-on-success-base: #12c905;
--text-on-critical-base: #fc533a;
--text-on-critical-weak: #b72d1a;
--text-on-critical-strong: #ffe0da;
--text-on-warning-base: rgba(255, 255, 255, 0.603);
--text-on-info-base: rgba(255, 255, 255, 0.603);
--text-diff-add-base: #9bcd97;
--text-diff-delete-base: #fc533a;
--text-diff-delete-strong: #ffe0da;
--text-diff-add-strong: #4a7348;
--text-on-info-weak: rgba(255, 255, 255, 0.404);
--text-on-info-strong: rgba(255, 255, 255, 0.928);
--text-on-warning-weak: rgba(255, 255, 255, 0.404);
--text-on-warning-strong: rgba(255, 255, 255, 0.928);
--text-on-success-weak: #127d0d;
--text-on-success-strong: #bafdb3;
--text-on-brand-weak: rgba(255, 255, 255, 0.404);
--text-on-brand-weaker: rgba(255, 255, 255, 0.266);
--text-on-brand-strong: rgba(255, 255, 255, 0.928);
--button-primary-base: #ededed;
--button-secondary-base: #1c1c1c;
--button-secondary-hover: rgba(255, 255, 255, 0.039);
--button-ghost-hover: rgba(255, 255, 255, 0.031);
--button-ghost-hover2: rgba(255, 255, 255, 0.059);
--border-base: rgba(255, 255, 255, 0.195);
--border-hover: rgba(255, 255, 255, 0.284);
--border-active: rgba(255, 255, 255, 0.418);
--border-selected: #9dbefe;
--border-disabled: rgba(255, 255, 255, 0.284);
--border-focus: rgba(255, 255, 255, 0.418);
--border-weak-base: #282828;
--border-strong-base: rgba(255, 255, 255, 0.266);
--border-strong-hover: rgba(255, 255, 255, 0.266);
--border-strong-active: rgba(255, 255, 255, 0.266);
--border-strong-selected: rgba(3, 76, 255, 0.62);
--border-strong-disabled: rgba(255, 255, 255, 0.138);
--border-strong-focus: rgba(255, 255, 255, 0.266);
--border-weak-hover: rgba(255, 255, 255, 0.181);
--border-weak-active: rgba(255, 255, 255, 0.266);
--border-weak-selected: rgba(3, 76, 255, 0.62);
--border-weak-disabled: rgba(255, 255, 255, 0.138);
--border-weak-focus: rgba(255, 255, 255, 0.266);
--border-weaker-base: #202020;
--border-weaker-hover: rgba(255, 255, 255, 0.084);
--border-weaker-active: rgba(255, 255, 255, 0.138);
--border-weaker-selected: rgba(3, 76, 255, 0.32);
--border-weaker-disabled: rgba(255, 255, 255, 0.034);
--border-weaker-focus: rgba(255, 255, 255, 0.138);
--border-interactive-base: #a3c1fd;
--border-interactive-hover: #7ea9ff;
--border-interactive-active: #034cff;
--border-interactive-selected: #034cff;
--border-interactive-disabled: #505050;
--border-interactive-focus: #034cff;
--border-success-base: #96ec8e;
--border-success-hover: #7add71;
--border-success-selected: #12c905;
--border-warning-base: #e9d282;
--border-warning-hover: #dac063;
--border-warning-selected: #fcd53a;
--border-critical-base: #6a1206;
--border-critical-hover: #952414;
--border-critical-selected: #fc533a;
--border-info-base: #eac5ec;
--border-info-hover: #dab1dd;
--border-info-selected: #edb2f1;
--border-color: #ffffff;
--icon-base: #7e7e7e;
--icon-hover: #a0a0a0;
--icon-active: #ededed;
--icon-selected: #ededed;
--icon-disabled: #3e3e3e;
--icon-focus: #ededed;
--icon-invert-base: #161616;
--icon-weak-base: #343434;
--icon-weak-hover: #d9d9d9;
--icon-weak-active: #c8c8c8;
--icon-weak-selected: #707070;
--icon-weak-disabled: #ededed;
--icon-weak-focus: #707070;
--icon-strong-base: #ededed;
--icon-strong-hover: #f6f3f3;
--icon-strong-active: #fcfcfc;
--icon-strong-selected: #fdfcfc;
--icon-strong-disabled: var(--smoke-dark-8);
--icon-strong-disabled: #3e3e3e;
--icon-strong-focus: #fdfcfc;
--icon-brand-base: var(--white);
--icon-interactive-base: var(--cobalt-dark-11);
--icon-success-base: var(--apple-dark-7);
--icon-success-hover: var(--apple-dark-8);
--icon-success-active: var(--apple-dark-11);
--icon-warning-base: var(--amber-dark-9);
--icon-warning-hover: var(--amber-dark-8);
--icon-warning-active: var(--amber-dark-11);
--icon-critical-base: var(--ember-dark-9);
--icon-critical-hover: var(--ember-dark-11);
--icon-critical-active: var(--ember-dark-12);
--icon-info-base: var(--lilac-dark-7);
--icon-info-hover: var(--lilac-dark-8);
--icon-info-active: var(--lilac-dark-11);
--icon-on-brand-base: var(--smoke-light-alpha-11);
--icon-on-brand-hover: var(--smoke-light-alpha-12);
--icon-on-brand-selected: var(--smoke-light-alpha-12);
--icon-on-interactive-base: var(--smoke-dark-12);
--icon-agent-plan-base: var(--purple-dark-9);
--icon-agent-docs-base: var(--amber-dark-9);
--icon-agent-ask-base: var(--cyan-dark-9);
--icon-agent-build-base: var(--cobalt-dark-11);
--icon-on-success-base: var(--apple-dark-alpha-9);
--icon-on-success-hover: var(--apple-dark-alpha-10);
--icon-on-success-selected: var(--apple-dark-alpha-11);
--icon-on-warning-base: var(--amber-darkalpha-9);
--icon-on-warning-hover: var(--amber-darkalpha-10);
--icon-on-warning-selected: var(--amber-darkalpha-11);
--icon-on-critical-base: var(--ember-dark-alpha-9);
--icon-on-critical-hover: var(--ember-dark-alpha-10);
--icon-on-critical-selected: var(--ember-dark-alpha-11);
--icon-on-info-base: var(--lilac-dark-9);
--icon-on-info-hover: var(--lilac-dark-alpha-10);
--icon-on-info-selected: var(--lilac-dark-alpha-11);
--icon-diff-add-base: var(--mint-dark-11);
--icon-diff-add-hover: var(--mint-dark-10);
--icon-diff-add-active: var(--mint-dark-11);
--icon-diff-delete-base: var(--ember-dark-9);
--icon-diff-delete-hover: var(--ember-dark-10);
--icon-brand-base: #ffffff;
--icon-interactive-base: #034cff;
--icon-success-base: #12c905;
--icon-success-hover: #35c02d;
--icon-success-active: #4de144;
--icon-warning-base: #fbb73c;
--icon-warning-hover: #885e08;
--icon-warning-active: #f1b13f;
--icon-critical-base: #fc533a;
--icon-critical-hover: #faa494;
--icon-critical-active: #ffe0da;
--icon-info-base: #68446b;
--icon-info-hover: #815484;
--icon-info-active: #dfa7e3;
--icon-on-brand-base: rgba(255, 255, 255, 0.603);
--icon-on-brand-hover: rgba(255, 255, 255, 0.928);
--icon-on-brand-selected: rgba(255, 255, 255, 0.928);
--icon-on-interactive-base: #ededed;
--icon-agent-plan-base: #edb2f1;
--icon-agent-docs-base: #fbb73c;
--icon-agent-ask-base: #2090f5;
--icon-agent-build-base: #9dbefe;
--icon-on-success-base: rgba(18, 201, 5, 0.9);
--icon-on-success-hover: rgba(53, 192, 45, 0.9);
--icon-on-success-selected: rgba(77, 225, 68, 0.9);
--icon-on-warning-base: rgba(251, 183, 60, 0.9);
--icon-on-warning-hover: rgba(245, 178, 56, 0.9);
--icon-on-warning-selected: rgba(241, 177, 63, 0.9);
--icon-on-critical-base: rgba(252, 83, 58, 0.9);
--icon-on-critical-hover: rgba(245, 79, 54, 0.9);
--icon-on-critical-selected: rgba(250, 164, 148, 0.9);
--icon-on-info-base: #edb2f1;
--icon-on-info-hover: rgba(231, 173, 235, 0.9);
--icon-on-info-selected: rgba(223, 167, 227, 0.9);
--icon-diff-add-base: #9bcd97;
--icon-diff-add-hover: #c3f9bf;
--icon-diff-add-active: #9bcd97;
--icon-diff-delete-base: #fc533a;
--icon-diff-delete-hover: #f54f36;
--icon-diff-modified-base: #ffba92;
--syntax-comment: var(--text-weak);
--syntax-regexp: var(--text-base);
@@ -559,12 +570,12 @@
--syntax-constant: #93e9f6;
--syntax-punctuation: var(--text-weak);
--syntax-object: var(--text-strong);
--syntax-success: var(--apple-dark-10);
--syntax-warning: var(--amber-dark-10);
--syntax-critical: var(--ember-dark-10);
--syntax-success: #35c02d;
--syntax-warning: #f5b238;
--syntax-critical: #f54f36;
--syntax-info: #93e9f6;
--syntax-diff-add: var(--mint-dark-11);
--syntax-diff-delete: var(--ember-dark-11);
--syntax-diff-add: #9bcd97;
--syntax-diff-delete: #faa494;
--syntax-diff-unknown: #ff0000;
--markdown-heading: #9d7cd8;
--markdown-text: #eeeeee;
@@ -580,10 +591,6 @@
--markdown-image: #fab283;
--markdown-image-text: #56b6c2;
--markdown-code-block: #eeeeee;
--border-color: #ffffff;
--border-weaker-base: var(--smoke-dark-alpha-2);
--button-ghost-hover: var(--smoke-dark-alpha-2);
--button-ghost-hover2: var(--smoke-dark-alpha-3);
--avatar-background-pink: #501b3f;
--avatar-background-mint: #033a34;
--avatar-background-orange: #5f2a06;
@@ -596,5 +603,6 @@
--avatar-text-purple: #9d5bd2;
--avatar-text-cyan: #369eff;
--avatar-text-lime: #c4f042;
--text-stronger: rgba(255, 255, 255, 0.936);
}
}

View File

@@ -1,16 +1,25 @@
import type { HexColor, OklchColor } from "./types"
function clamp(v: number, min: number, max: number) {
return Math.max(min, Math.min(max, v))
}
function hue(v: number) {
return ((v % 360) + 360) % 360
}
export function hexToRgb(hex: HexColor): { r: number; g: number; b: number } {
const h = hex.replace("#", "")
const full =
h.length === 3
h.length === 3 || h.length === 4
? h
.split("")
.map((c) => c + c)
.join("")
: h
const rgb = full.length === 8 ? full.slice(0, 6) : full
const num = parseInt(full, 16)
const num = parseInt(rgb, 16)
return {
r: ((num >> 16) & 255) / 255,
g: ((num >> 8) & 255) / 255,
@@ -20,7 +29,7 @@ export function hexToRgb(hex: HexColor): { r: number; g: number; b: number } {
export function rgbToHex(r: number, g: number, b: number): HexColor {
const toHex = (v: number) => {
const clamped = Math.max(0, Math.min(1, v))
const clamped = clamp(v, 0, 1)
const int = Math.round(clamped * 255)
return int.toString(16).padStart(2, "0")
}
@@ -91,8 +100,33 @@ export function hexToOklch(hex: HexColor): OklchColor {
return rgbToOklch(r, g, b)
}
export function fitOklch(oklch: OklchColor): OklchColor {
const base = {
l: clamp(oklch.l, 0, 1),
c: Math.max(0, oklch.c),
h: hue(oklch.h),
}
const rgb = oklchToRgb(base)
if (rgb.r >= 0 && rgb.r <= 1 && rgb.g >= 0 && rgb.g <= 1 && rgb.b >= 0 && rgb.b <= 1) {
return base
}
let c = base.c
for (let i = 0; i < 24; i++) {
c *= 0.9
const next = { ...base, c }
const out = oklchToRgb(next)
if (out.r >= 0 && out.r <= 1 && out.g >= 0 && out.g <= 1 && out.b >= 0 && out.b <= 1) {
return next
}
}
return { ...base, c: 0 }
}
export function oklchToHex(oklch: OklchColor): HexColor {
const { r, g, b } = oklchToRgb(oklch)
const { r, g, b } = oklchToRgb(fitOklch(oklch))
return rgbToHex(r, g, b)
}
@@ -101,12 +135,12 @@ export function generateScale(seed: HexColor, isDark: boolean): HexColor[] {
const scale: HexColor[] = []
const lightSteps = isDark
? [0.15, 0.18, 0.22, 0.26, 0.32, 0.38, 0.46, 0.56, base.l, base.l - 0.05, 0.75, 0.93]
: [0.99, 0.97, 0.94, 0.9, 0.85, 0.79, 0.72, 0.64, base.l, base.l + 0.05, 0.45, 0.25]
? [0.182, 0.21, 0.261, 0.302, 0.341, 0.387, 0.443, 0.514, base.l, Math.max(0, base.l - 0.017), 0.8, 0.93]
: [0.993, 0.983, 0.962, 0.936, 0.906, 0.866, 0.811, 0.74, base.l, Math.max(0, base.l - 0.036), 0.548, 0.33]
const chromaMultipliers = isDark
? [0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.85, 1, 1, 0.9, 0.6]
: [0.1, 0.15, 0.25, 0.35, 0.45, 0.55, 0.7, 0.85, 1, 1, 0.95, 0.85]
? [0.205, 0.275, 0.46, 0.62, 0.71, 0.79, 0.87, 0.97, 1.04, 1.03, 1, 0.58]
: [0.045, 0.128, 0.34, 0.5, 0.61, 0.69, 0.77, 0.89, 1, 1, 0.97, 0.56]
for (let i = 0; i < 12; i++) {
scale.push(
@@ -127,8 +161,8 @@ export function generateNeutralScale(seed: HexColor, isDark: boolean): HexColor[
const neutralChroma = Math.min(base.c, 0.02)
const lightSteps = isDark
? [0.13, 0.16, 0.2, 0.24, 0.28, 0.33, 0.4, 0.52, 0.58, 0.66, 0.82, 0.96]
: [0.995, 0.98, 0.96, 0.94, 0.91, 0.88, 0.84, 0.78, 0.62, 0.56, 0.46, 0.2]
? [0.2, 0.226, 0.256, 0.277, 0.301, 0.325, 0.364, 0.431, base.l, 0.593, 0.706, 0.946]
: [0.991, 0.979, 0.964, 0.946, 0.931, 0.913, 0.891, 0.83, base.l, 0.617, 0.542, 0.205]
for (let i = 0; i < 12; i++) {
scale.push(
@@ -164,19 +198,39 @@ export function generateAlphaScale(scale: HexColor[], isDark: boolean): HexColor
export function mixColors(color1: HexColor, color2: HexColor, amount: number): HexColor {
const c1 = hexToOklch(color1)
const c2 = hexToOklch(color2)
const delta = ((((c2.h - c1.h) % 360) + 540) % 360) - 180
return oklchToHex({
l: c1.l + (c2.l - c1.l) * amount,
c: c1.c + (c2.c - c1.c) * amount,
h: c1.h + (c2.h - c1.h) * amount,
h: c1.h + delta * amount,
})
}
export function shift(color: HexColor, value: { l?: number; c?: number; h?: number }): HexColor {
const base = hexToOklch(color)
return oklchToHex({
l: base.l + (value.l ?? 0),
c: base.c * (value.c ?? 1),
h: base.h + (value.h ?? 0),
})
}
export function blend(color: HexColor, background: HexColor, alpha: number): HexColor {
const fg = hexToRgb(color)
const bg = hexToRgb(background)
return rgbToHex(
fg.r * alpha + bg.r * (1 - alpha),
fg.g * alpha + bg.g * (1 - alpha),
fg.b * alpha + bg.b * (1 - alpha),
)
}
export function lighten(color: HexColor, amount: number): HexColor {
const oklch = hexToOklch(color)
return oklchToHex({
...oklch,
l: Math.min(1, oklch.l + amount),
l: clamp(oklch.l + amount, 0, 1),
})
}
@@ -184,7 +238,7 @@ export function darken(color: HexColor, amount: number): HexColor {
const oklch = hexToOklch(color)
return oklchToHex({
...oklch,
l: Math.max(0, oklch.l - amount),
l: clamp(oklch.l - amount, 0, 1),
})
}

View File

@@ -35,7 +35,7 @@ function applyThemeCss(theme: DesktopTheme, themeId: string, mode: "light" | "da
const tokens = resolveThemeVariant(variant, isDark)
const css = themeToCss(tokens)
if (themeId !== "oc-1") {
if (themeId !== "oc-2") {
try {
localStorage.setItem(isDark ? STORAGE_KEYS.THEME_CSS_DARK : STORAGE_KEYS.THEME_CSS_LIGHT, css)
} catch {}
@@ -54,7 +54,7 @@ function applyThemeCss(theme: DesktopTheme, themeId: string, mode: "light" | "da
}
function cacheThemeVariants(theme: DesktopTheme, themeId: string) {
if (themeId === "oc-1") return
if (themeId === "oc-2") return
for (const mode of ["light", "dark"] as const) {
const isDark = mode === "dark"
const variant = isDark ? theme.dark : theme.light
@@ -71,7 +71,7 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
init: (props: { defaultTheme?: string }) => {
const [store, setStore] = createStore({
themes: DEFAULT_THEMES as Record<string, DesktopTheme>,
themeId: props.defaultTheme ?? "oc-1",
themeId: props.defaultTheme ?? "oc-2",
colorScheme: "system" as ColorScheme,
mode: getSystemMode(),
previewThemeId: null as string | null,

View File

@@ -34,8 +34,8 @@ export const gruvboxTheme = gruvboxThemeJson as DesktopTheme
export const auraTheme = auraThemeJson as DesktopTheme
export const DEFAULT_THEMES: Record<string, DesktopTheme> = {
"oc-1": oc1Theme,
"oc-2": oc2Theme,
"oc-1": oc1Theme,
aura: auraTheme,
ayu: ayuTheme,
carbonfox: carbonfoxTheme,

View File

@@ -36,12 +36,13 @@
},
"ColorValue": {
"type": "string",
"pattern": "^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|var\(--[a-z0-9-]+\))$",
"pattern": "^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|var\\(--[a-z0-9-]+\\))$",
"description": "Either a hex color value (#rgb/#rgba/#rrggbb/#rrggbbaa) or a CSS variable reference"
},
"ThemeSeedColors": {
"type": "object",
"description": "The minimum set of colors needed to generate a theme",
"description": "The legacy semantic seed set used to generate a theme",
"additionalProperties": false,
"required": ["neutral", "primary", "success", "warning", "error", "info", "interactive", "diffAdd", "diffDelete"],
"properties": {
"neutral": {
@@ -82,14 +83,70 @@
}
}
},
"ThemePaletteColors": {
"type": "object",
"description": "A compact semantic palette used to derive the full theme programmatically",
"additionalProperties": false,
"required": ["neutral", "primary", "success", "warning", "error", "info"],
"properties": {
"neutral": {
"$ref": "#/definitions/HexColor",
"description": "Base neutral color for generating the gray scale"
},
"ink": {
"$ref": "#/definitions/HexColor",
"description": "Optional foreground or chrome color used to derive text and border tones"
},
"primary": {
"$ref": "#/definitions/HexColor",
"description": "Primary brand color used for brand surfaces and strong emphasis"
},
"success": {
"$ref": "#/definitions/HexColor",
"description": "Success state color"
},
"warning": {
"$ref": "#/definitions/HexColor",
"description": "Warning state color"
},
"error": {
"$ref": "#/definitions/HexColor",
"description": "Error or critical state color"
},
"info": {
"$ref": "#/definitions/HexColor",
"description": "Informational state color"
},
"accent": {
"$ref": "#/definitions/HexColor",
"description": "Optional extra expressive accent for syntax and rich content"
},
"interactive": {
"$ref": "#/definitions/HexColor",
"description": "Optional dedicated interactive color; falls back to primary"
},
"diffAdd": {
"$ref": "#/definitions/HexColor",
"description": "Optional diff-add seed; falls back to a softened success color"
},
"diffDelete": {
"$ref": "#/definitions/HexColor",
"description": "Optional diff-delete seed; falls back to error"
}
}
},
"ThemeVariant": {
"type": "object",
"description": "A theme variant (light or dark) with seed colors and optional overrides",
"required": ["seeds"],
"description": "A theme variant (light or dark) with either a compact palette or legacy seeds and optional overrides",
"oneOf": [{ "required": ["seeds"] }, { "required": ["palette"] }],
"properties": {
"seeds": {
"$ref": "#/definitions/ThemeSeedColors",
"description": "Seed colors used to generate the full palette"
"description": "Legacy seed colors used to generate the full palette"
},
"palette": {
"$ref": "#/definitions/ThemePaletteColors",
"description": "Compact palette used to derive the full token set"
},
"overrides": {
"type": "object",

View File

@@ -1,5 +1,6 @@
export type {
DesktopTheme,
ThemePaletteColors,
ThemeSeedColors,
ThemeVariant,
HexColor,
@@ -19,7 +20,10 @@ export {
generateScale,
generateNeutralScale,
generateAlphaScale,
fitOklch,
blend,
mixColors,
shift,
lighten,
darken,
withAlpha,

View File

@@ -27,7 +27,7 @@ export function applyTheme(theme: DesktopTheme, themeId?: string): void {
}
function buildThemeCss(light: ResolvedTheme, dark: ResolvedTheme, themeId: string): string {
const isDefaultTheme = themeId === "oc-1"
const isDefaultTheme = themeId === "oc-2"
const lightCss = themeToCss(light)
const darkCss = themeToCss(dark)

View File

@@ -1,27 +1,131 @@
import type { ColorValue, DesktopTheme, HexColor, ResolvedTheme, ThemeVariant } from "./types"
import { generateNeutralScale, generateScale, hexToOklch, oklchToHex, withAlpha } from "./color"
import { blend, generateNeutralScale, generateScale, hexToOklch, oklchToHex, shift, withAlpha } from "./color"
export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): ResolvedTheme {
const { seeds, overrides = {} } = variant
const colors = getColors(variant)
const { overrides = {} } = variant
const neutral = generateNeutralScale(seeds.neutral, isDark)
const primary = generateScale(seeds.primary, isDark)
const success = generateScale(seeds.success, isDark)
const warning = generateScale(seeds.warning, isDark)
const error = generateScale(seeds.error, isDark)
const info = generateScale(seeds.info, isDark)
const interactive = generateScale(seeds.interactive, isDark)
const diffAdd = generateScale(seeds.diffAdd, isDark)
const diffDelete = generateScale(seeds.diffDelete, isDark)
const neutral = generateNeutralScale(colors.neutral, isDark)
const primary = generateScale(colors.primary, isDark)
const accent = generateScale(colors.accent, isDark)
const success = generateScale(colors.success, isDark)
const warning = generateScale(colors.warning, isDark)
const error = generateScale(colors.error, isDark)
const info = generateScale(colors.info, isDark)
const interactive = generateScale(colors.interactive, isDark)
const hasInk = colors.compact && Boolean(colors.ink)
const noInk = colors.compact && !hasInk
const shadow = noInk && !isDark ? generateNeutralScale(colors.neutral, true) : neutral
const amber = generateScale(
shift(colors.warning, isDark ? { h: -16, l: -0.058, c: 1.14 } : { h: -22, l: -0.082, c: 0.94 }),
isDark,
)
const blue = generateScale(shift(colors.interactive, { h: -12, l: 0.128, c: 1.12 }), isDark)
const brandl = noInk && isDark ? generateScale(colors.primary, false) : primary
const successl = noInk && isDark ? generateScale(colors.success, false) : success
const warningl = noInk && isDark ? generateScale(colors.warning, false) : warning
const infol = noInk && isDark ? generateScale(colors.info, false) : info
const interl = noInk && isDark ? generateScale(colors.interactive, false) : interactive
const diffAdd = generateScale(
colors.diffAdd ??
(noInk
? shift(colors.success, { c: isDark ? 0.54 : 0.6, l: isDark ? 0.22 : 0.16 })
: shift(colors.success, { c: isDark ? 0.7 : 0.55, l: isDark ? -0.18 : 0.14 })),
isDark,
)
const diffDelete = generateScale(
colors.diffDelete ??
(noInk ? colors.error : shift(colors.error, { c: isDark ? 0.82 : 0.7, l: isDark ? -0.08 : 0.08 })),
isDark,
)
const ink = colors.ink ?? colors.neutral
const backgroundOverride = overrides["background-base"]
const backgroundHex = getHex(backgroundOverride)
const overlay = noInk || (Boolean(backgroundOverride) && !backgroundHex)
const content = (seed: HexColor, scale: HexColor[]) => {
const value = isDark ? seed : hexToOklch(seed).l > 0.82 ? scale[10] : seed
return shift(value, { c: isDark ? 1.16 : 1.1 })
}
const modified = () => {
if (!colors.compact) return isDark ? "#ffba92" : "#FF8C00"
if (!hasInk) return isDark ? "#ffba92" : "#FF8C00"
const warningHue = hexToOklch(colors.warning).h
const deleteHue = hexToOklch(colors.diffDelete ?? colors.error).h
const delta = Math.abs(((((deleteHue - warningHue) % 360) + 540) % 360) - 180)
if (delta < 48) return isDark ? "#ffba92" : "#FF8C00"
return content(colors.warning, warning)
}
const surface = (
seed: HexColor,
alpha: { base: number; weak: number; weaker: number; strong: number; stronger: number },
) => {
const base = alphaTone(seed, alpha.base)
return {
base,
weak: alphaTone(seed, alpha.weak),
weaker: alphaTone(seed, alpha.weaker),
strong: alphaTone(seed, alpha.strong),
stronger: alphaTone(seed, alpha.stronger),
}
}
const compactBackground =
colors.compact && !hasInk
? isDark
? {
base: shift(blend(colors.neutral, "#000000", 0.145), { c: 0 }),
weak: shift(blend(colors.neutral, "#000000", 0.27), { c: 0 }),
strong: shift(blend(colors.neutral, "#000000", 0.165), { c: 0 }),
stronger: shift(blend(colors.neutral, "#000000", 0.19), { c: 0 }),
}
: {
base: blend(colors.neutral, "#ffffff", 0.066),
weak: blend(colors.neutral, "#ffffff", 0.11),
strong: blend(colors.neutral, "#ffffff", 0.024),
stronger: blend(colors.neutral, "#ffffff", 0.024),
}
: undefined
const compactInkBackground =
colors.compact && hasInk && isDark
? {
base: neutral[2],
weak: neutral[3],
strong: neutral[1],
stronger: neutral[2],
}
: undefined
const neutralAlpha = generateNeutralAlphaScale(neutral, isDark)
const background = backgroundHex ?? compactInkBackground?.base ?? compactBackground?.base ?? neutral[0]
const alphaTone = (color: HexColor, alpha: number) =>
overlay ? (withAlpha(color, alpha) as ColorValue) : blend(color, background, alpha)
const borderTone = (light: number, dark: number) =>
alphaTone(
ink,
isDark ? Math.min(1, dark + 0.024 + (colors.compact && hasInk ? 0.08 : 0)) : Math.min(1, light + 0.024),
)
const diffHiddenSurface = noInk
? {
base: blue[isDark ? 1 : 2],
weak: blue[isDark ? 0 : 1],
weaker: blue[isDark ? 2 : 0],
strong: blue[4],
stronger: blue[isDark ? 10 : 8],
}
: surface(
isDark ? shift(colors.interactive, { c: 0.55, l: 0 }) : shift(colors.interactive, { c: 0.45, l: 0.08 }),
isDark
? { base: 0.14, weak: 0.08, weaker: 0.18, strong: 0.26, stronger: 0.42 }
: { base: 0.12, weak: 0.08, weaker: 0.16, strong: 0.24, stronger: 0.36 },
)
const neutralAlpha = noInk ? generateNeutralOverlayScale(neutral, isDark) : generateNeutralAlphaScale(neutral, isDark)
const tokens: ResolvedTheme = {}
tokens["background-base"] = neutral[0]
tokens["background-weak"] = neutral[2]
tokens["background-strong"] = neutral[0]
tokens["background-stronger"] = isDark ? neutral[1] : "#fcfcfc"
tokens["background-base"] = compactInkBackground?.base ?? compactBackground?.base ?? neutral[0]
tokens["background-weak"] = compactInkBackground?.weak ?? compactBackground?.weak ?? neutral[2]
tokens["background-strong"] = compactInkBackground?.strong ?? compactBackground?.strong ?? neutral[0]
tokens["background-stronger"] =
compactInkBackground?.stronger ?? compactBackground?.stronger ?? (isDark ? neutral[1] : "#fcfcfc")
tokens["surface-base"] = neutralAlpha[1]
tokens["base"] = neutralAlpha[1]
@@ -37,8 +141,8 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
: (withAlpha(neutral[3], 0.09) as ColorValue)
tokens["surface-inset-strong-hover"] = tokens["surface-inset-strong"]
tokens["surface-raised-base"] = neutralAlpha[0]
tokens["surface-float-base"] = isDark ? neutral[0] : neutral[11]
tokens["surface-float-base-hover"] = isDark ? neutral[1] : neutral[10]
tokens["surface-float-base"] = isDark ? neutral[0] : noInk ? shadow[0] : neutral[11]
tokens["surface-float-base-hover"] = isDark ? neutral[1] : noInk ? shadow[1] : neutral[10]
tokens["surface-raised-base-hover"] = neutralAlpha[1]
tokens["surface-raised-base-active"] = neutralAlpha[2]
tokens["surface-raised-strong"] = isDark ? neutralAlpha[3] : neutral[0]
@@ -50,34 +154,34 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
tokens["surface-strong"] = isDark ? neutralAlpha[6] : "#ffffff"
tokens["surface-raised-stronger-non-alpha"] = isDark ? neutral[2] : "#ffffff"
tokens["surface-brand-base"] = primary[8]
tokens["surface-brand-hover"] = primary[9]
tokens["surface-brand-base"] = brandl[8]
tokens["surface-brand-hover"] = brandl[9]
tokens["surface-interactive-base"] = interactive[2]
tokens["surface-interactive-hover"] = interactive[3]
tokens["surface-interactive-weak"] = interactive[1]
tokens["surface-interactive-weak-hover"] = interactive[2]
tokens["surface-interactive-base"] = interactive[isDark ? 4 : 3]
tokens["surface-interactive-hover"] = interactive[isDark ? 5 : 4]
tokens["surface-interactive-weak"] = interactive[isDark ? 3 : 2]
tokens["surface-interactive-weak-hover"] = noInk && isDark ? interl[4] : interactive[isDark ? 4 : 3]
tokens["surface-success-base"] = success[2]
tokens["surface-success-weak"] = success[1]
tokens["surface-success-strong"] = success[8]
tokens["surface-warning-base"] = warning[2]
tokens["surface-warning-weak"] = warning[1]
tokens["surface-warning-strong"] = warning[8]
tokens["surface-critical-base"] = error[2]
tokens["surface-critical-weak"] = error[1]
tokens["surface-critical-strong"] = error[8]
tokens["surface-info-base"] = info[2]
tokens["surface-info-weak"] = info[1]
tokens["surface-info-strong"] = info[8]
tokens["surface-success-base"] = success[isDark ? 4 : 3]
tokens["surface-success-weak"] = success[isDark ? 3 : 2]
tokens["surface-success-strong"] = success[9]
tokens["surface-warning-base"] = (noInk && isDark ? warningl : warning)[isDark ? 4 : 3]
tokens["surface-warning-weak"] = (noInk && isDark ? warningl : warning)[isDark ? 3 : 2]
tokens["surface-warning-strong"] = (noInk && isDark ? warningl : warning)[9]
tokens["surface-critical-base"] = error[isDark ? 4 : 3]
tokens["surface-critical-weak"] = error[isDark ? 3 : 2]
tokens["surface-critical-strong"] = error[9]
tokens["surface-info-base"] = (noInk && isDark ? infol : info)[isDark ? 4 : 3]
tokens["surface-info-weak"] = (noInk && isDark ? infol : info)[isDark ? 3 : 2]
tokens["surface-info-strong"] = (noInk && isDark ? infol : info)[9]
tokens["surface-diff-unchanged-base"] = isDark ? neutral[0] : "#ffffff00"
tokens["surface-diff-skip-base"] = isDark ? neutralAlpha[0] : neutral[1]
tokens["surface-diff-hidden-base"] = interactive[isDark ? 1 : 2]
tokens["surface-diff-hidden-weak"] = interactive[isDark ? 0 : 1]
tokens["surface-diff-hidden-weaker"] = interactive[isDark ? 2 : 0]
tokens["surface-diff-hidden-strong"] = interactive[4]
tokens["surface-diff-hidden-stronger"] = interactive[isDark ? 10 : 8]
tokens["surface-diff-hidden-base"] = diffHiddenSurface.base
tokens["surface-diff-hidden-weak"] = diffHiddenSurface.weak
tokens["surface-diff-hidden-weaker"] = diffHiddenSurface.weaker
tokens["surface-diff-hidden-strong"] = diffHiddenSurface.strong
tokens["surface-diff-hidden-stronger"] = diffHiddenSurface.stronger
tokens["surface-diff-add-base"] = diffAdd[2]
tokens["surface-diff-add-weak"] = diffAdd[isDark ? 3 : 1]
tokens["surface-diff-add-weaker"] = diffAdd[isDark ? 2 : 0]
@@ -96,10 +200,36 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
tokens["input-focus"] = interactive[0]
tokens["input-disabled"] = neutral[3]
tokens["text-base"] = neutral[10]
tokens["text-weak"] = neutral[8]
tokens["text-weaker"] = neutral[7]
tokens["text-strong"] = neutral[11]
tokens["text-base"] = hasInk ? ink : noInk ? (isDark ? neutralAlpha[10] : neutral[10]) : neutral[10]
tokens["text-weak"] = hasInk
? shift(ink, { l: isDark ? -0.18 : 0.16, c: 0.88 })
: noInk
? isDark
? neutralAlpha[8]
: neutral[8]
: neutral[8]
tokens["text-weaker"] = hasInk
? shift(ink, { l: isDark ? -0.3 : 0.26, c: isDark ? 0.74 : 0.68 })
: noInk
? isDark
? neutralAlpha[7]
: neutral[7]
: neutral[7]
tokens["text-strong"] = hasInk
? isDark && colors.compact
? blend("#ffffff", ink, 0.82)
: shift(ink, { l: isDark ? 0.06 : -0.09, c: 1 })
: noInk
? isDark
? neutralAlpha[11]
: neutral[11]
: neutral[11]
if (noInk && isDark) {
tokens["text-base"] = withAlpha("#ffffff", 0.618) as ColorValue
tokens["text-weak"] = withAlpha("#ffffff", 0.422) as ColorValue
tokens["text-weaker"] = withAlpha("#ffffff", 0.284) as ColorValue
tokens["text-strong"] = withAlpha("#ffffff", 0.936) as ColorValue
}
tokens["text-invert-base"] = isDark ? neutral[10] : neutral[1]
tokens["text-invert-weak"] = isDark ? neutral[8] : neutral[2]
tokens["text-invert-weaker"] = isDark ? neutral[7] : neutral[3]
@@ -128,79 +258,166 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
tokens["text-on-brand-weaker"] = neutralAlpha[7]
tokens["text-on-brand-strong"] = neutralAlpha[11]
tokens["button-secondary-base"] = isDark ? neutral[2] : neutral[0]
tokens["button-secondary-hover"] = isDark ? neutral[3] : neutral[1]
tokens["button-primary-base"] = neutral[11]
tokens["button-secondary-base"] = noInk ? (isDark ? neutral[1] : neutral[0]) : isDark ? neutral[2] : neutral[0]
tokens["button-secondary-hover"] = noInk ? (isDark ? neutral[1] : neutral[1]) : isDark ? neutral[3] : neutral[1]
tokens["button-ghost-hover"] = neutralAlpha[1]
tokens["button-ghost-hover2"] = neutralAlpha[2]
tokens["border-base"] = neutralAlpha[6]
tokens["border-hover"] = neutralAlpha[7]
tokens["border-active"] = neutralAlpha[8]
tokens["border-selected"] = withAlpha(interactive[8], isDark ? 0.9 : 0.99) as ColorValue
tokens["border-disabled"] = neutralAlpha[7]
tokens["border-focus"] = neutralAlpha[8]
tokens["border-weak-base"] = neutralAlpha[isDark ? 5 : 4]
tokens["border-strong-base"] = neutralAlpha[isDark ? 7 : 6]
tokens["border-strong-hover"] = neutralAlpha[7]
tokens["border-strong-active"] = neutralAlpha[isDark ? 7 : 6]
tokens["border-strong-selected"] = withAlpha(interactive[5], 0.6) as ColorValue
tokens["border-strong-disabled"] = neutralAlpha[5]
tokens["border-strong-focus"] = neutralAlpha[isDark ? 7 : 6]
tokens["border-weak-hover"] = neutralAlpha[isDark ? 6 : 5]
tokens["border-weak-active"] = neutralAlpha[isDark ? 7 : 6]
tokens["border-weak-selected"] = withAlpha(interactive[4], isDark ? 0.6 : 0.5) as ColorValue
tokens["border-weak-disabled"] = neutralAlpha[5]
tokens["border-weak-focus"] = neutralAlpha[isDark ? 7 : 6]
tokens["border-weaker-base"] = neutralAlpha[2]
if (noInk) {
const tone = (alpha: number) => alphaTone((isDark ? "#ffffff" : "#000000") as HexColor, alpha)
if (isDark) {
tokens["surface-base"] = tone(0.031)
tokens["surface-base-hover"] = tone(0.039)
tokens["surface-base-active"] = tone(0.059)
tokens["surface-raised-base"] = tone(0.059)
tokens["surface-raised-base-hover"] = tone(0.078)
tokens["surface-raised-base-active"] = tone(0.102)
tokens["surface-raised-strong"] = tone(0.078)
tokens["surface-raised-strong-hover"] = tone(0.129)
tokens["surface-raised-stronger"] = tone(0.129)
tokens["surface-raised-stronger-hover"] = tone(0.169)
tokens["surface-weak"] = tone(0.078)
tokens["surface-weaker"] = tone(0.102)
tokens["surface-strong"] = tone(0.169)
tokens["surface-raised-stronger-non-alpha"] = neutral[1]
tokens["surface-inset-base"] = withAlpha("#000000", 0.5) as ColorValue
tokens["surface-inset-base-hover"] = tokens["surface-inset-base"]
tokens["surface-inset-strong"] = withAlpha("#000000", 0.8) as ColorValue
tokens["surface-inset-strong-hover"] = tokens["surface-inset-strong"]
tokens["button-secondary-hover"] = tone(0.039)
tokens["button-ghost-hover"] = tone(0.031)
tokens["button-ghost-hover2"] = tone(0.059)
tokens["input-base"] = neutral[1]
tokens["input-hover"] = neutral[1]
tokens["input-selected"] = interactive[1]
tokens["surface-diff-skip-base"] = "#00000000"
}
tokens["border-interactive-base"] = interactive[6]
tokens["border-interactive-hover"] = interactive[7]
tokens["border-interactive-active"] = interactive[8]
tokens["border-interactive-selected"] = interactive[8]
if (!isDark) {
tokens["surface-base"] = tone(0.031)
tokens["surface-base-hover"] = tone(0.059)
tokens["surface-base-active"] = tone(0.051)
tokens["surface-raised-base"] = tone(0.031)
tokens["surface-raised-base-hover"] = tone(0.051)
tokens["surface-raised-base-active"] = tone(0.09)
tokens["surface-raised-strong"] = neutral[0]
tokens["surface-raised-strong-hover"] = "#ffffff"
tokens["surface-raised-stronger"] = "#ffffff"
tokens["surface-raised-stronger-hover"] = "#ffffff"
tokens["surface-weak"] = tone(0.051)
tokens["surface-weaker"] = tone(0.071)
tokens["surface-strong"] = "#ffffff"
tokens["surface-raised-stronger-non-alpha"] = "#ffffff"
tokens["surface-inset-strong"] = tone(0.09)
tokens["surface-inset-strong-hover"] = tokens["surface-inset-strong"]
tokens["button-secondary-hover"] = blend("#ffffff", background, 0.04)
tokens["button-ghost-hover"] = tone(0.031)
tokens["button-ghost-hover2"] = tone(0.051)
tokens["input-base"] = neutral[0]
tokens["input-hover"] = neutral[1]
}
tokens["surface-base-interactive-active"] = withAlpha(colors.interactive, isDark ? 0.125 : 0.09) as ColorValue
}
tokens["border-base"] = hasInk ? borderTone(0.22, 0.16) : neutralAlpha[6]
tokens["border-hover"] = hasInk ? borderTone(0.28, 0.2) : neutralAlpha[7]
tokens["border-active"] = hasInk ? borderTone(0.34, 0.24) : neutralAlpha[8]
tokens["border-selected"] = noInk
? isDark
? interactive[10]
: (withAlpha(colors.interactive, 0.99) as ColorValue)
: (withAlpha(interactive[8], isDark ? 0.9 : 0.99) as ColorValue)
tokens["border-disabled"] = hasInk ? borderTone(0.18, 0.12) : neutralAlpha[7]
tokens["border-focus"] = hasInk ? borderTone(0.34, 0.24) : neutralAlpha[8]
tokens["border-weak-base"] = hasInk
? borderTone(0.1, 0.08)
: noInk
? isDark
? neutral[3]
: blend(neutral[4], neutral[5], 0.5)
: neutralAlpha[isDark ? 5 : 4]
tokens["border-strong-base"] = hasInk ? borderTone(0.34, 0.24) : neutralAlpha[isDark ? 7 : 6]
tokens["border-strong-hover"] = hasInk ? borderTone(0.4, 0.28) : neutralAlpha[7]
tokens["border-strong-active"] = hasInk ? borderTone(0.46, 0.32) : neutralAlpha[isDark ? 7 : 6]
tokens["border-strong-selected"] = noInk
? (withAlpha(colors.interactive, isDark ? 0.62 : 0.31) as ColorValue)
: (withAlpha(interactive[5], 0.6) as ColorValue)
tokens["border-strong-disabled"] = hasInk ? borderTone(0.14, 0.1) : neutralAlpha[5]
tokens["border-strong-focus"] = hasInk ? borderTone(0.46, 0.32) : neutralAlpha[isDark ? 7 : 6]
tokens["border-weak-hover"] = hasInk ? borderTone(0.16, 0.12) : neutralAlpha[isDark ? 6 : 5]
tokens["border-weak-active"] = hasInk ? borderTone(0.22, 0.16) : neutralAlpha[isDark ? 7 : 6]
tokens["border-weak-selected"] = noInk
? (withAlpha(colors.interactive, isDark ? 0.62 : 0.24) as ColorValue)
: (withAlpha(interactive[4], isDark ? 0.6 : 0.5) as ColorValue)
tokens["border-weak-disabled"] = hasInk ? borderTone(0.08, 0.06) : neutralAlpha[5]
tokens["border-weak-focus"] = hasInk ? borderTone(0.22, 0.16) : neutralAlpha[isDark ? 7 : 6]
tokens["border-weaker-base"] = hasInk
? borderTone(0.06, 0.04)
: noInk
? isDark
? blend(neutral[1], neutral[2], 0.5)
: blend(neutral[2], neutral[3], 0.5)
: neutralAlpha[2]
if (noInk) {
const line = (l: number, d: number) => alphaTone((isDark ? "#ffffff" : "#000000") as HexColor, isDark ? d : l)
tokens["border-base"] = line(0.162, 0.195)
tokens["border-hover"] = line(0.236, 0.284)
tokens["border-active"] = line(0.46, 0.418)
tokens["border-disabled"] = tokens["border-hover"]
tokens["border-focus"] = tokens["border-active"]
}
tokens["border-interactive-base"] = (noInk && isDark ? interl : interactive)[6]
tokens["border-interactive-hover"] = (noInk && isDark ? interl : interactive)[7]
tokens["border-interactive-active"] = (noInk && isDark ? interl : interactive)[8]
tokens["border-interactive-selected"] = (noInk && isDark ? interl : interactive)[8]
tokens["border-interactive-disabled"] = neutral[7]
tokens["border-interactive-focus"] = interactive[8]
tokens["border-interactive-focus"] = (noInk && isDark ? interl : interactive)[8]
tokens["border-success-base"] = success[5]
tokens["border-success-hover"] = success[6]
tokens["border-success-selected"] = success[8]
tokens["border-warning-base"] = warning[5]
tokens["border-warning-hover"] = warning[6]
tokens["border-warning-selected"] = warning[8]
tokens["border-success-base"] = (noInk && isDark ? successl : success)[5]
tokens["border-success-hover"] = (noInk && isDark ? successl : success)[6]
tokens["border-success-selected"] = (noInk && isDark ? successl : success)[8]
tokens["border-warning-base"] = (noInk && isDark ? warningl : warning)[5]
tokens["border-warning-hover"] = (noInk && isDark ? warningl : warning)[6]
tokens["border-warning-selected"] = (noInk && isDark ? warningl : warning)[8]
tokens["border-critical-base"] = error[isDark ? 4 : 5]
tokens["border-critical-hover"] = error[6]
tokens["border-critical-selected"] = error[8]
tokens["border-info-base"] = info[5]
tokens["border-info-hover"] = info[6]
tokens["border-info-selected"] = info[8]
tokens["border-info-base"] = (noInk && isDark ? infol : info)[5]
tokens["border-info-hover"] = (noInk && isDark ? infol : info)[6]
tokens["border-info-selected"] = (noInk && isDark ? infol : info)[8]
tokens["border-color"] = "#ffffff"
tokens["icon-base"] = neutral[8]
tokens["icon-hover"] = neutral[isDark ? 9 : 10]
tokens["icon-active"] = neutral[isDark ? 10 : 11]
tokens["icon-selected"] = neutral[11]
tokens["icon-base"] = hasInk && !isDark ? tokens["text-weak"] : neutral[isDark ? 9 : 8]
tokens["icon-hover"] = hasInk && !isDark ? tokens["text-base"] : neutral[10]
tokens["icon-active"] = hasInk && !isDark ? tokens["text-strong"] : neutral[11]
tokens["icon-selected"] = hasInk && !isDark ? tokens["text-strong"] : neutral[11]
tokens["icon-disabled"] = neutral[isDark ? 6 : 7]
tokens["icon-focus"] = neutral[11]
tokens["icon-focus"] = hasInk && !isDark ? tokens["text-strong"] : neutral[11]
tokens["icon-invert-base"] = isDark ? neutral[0] : "#ffffff"
tokens["icon-weak-base"] = neutral[isDark ? 5 : 6]
tokens["icon-weak-hover"] = neutral[6]
tokens["icon-weak-active"] = neutral[7]
tokens["icon-weak-selected"] = neutral[8]
tokens["icon-weak-disabled"] = neutral[isDark ? 3 : 5]
tokens["icon-weak-hover"] = noInk && isDark ? blend(neutral[11], neutral[10], 0.74) : neutral[isDark ? 11 : 7]
tokens["icon-weak-active"] = noInk && isDark ? blend(neutral[11], neutral[10], 0.52) : neutral[8]
tokens["icon-weak-selected"] = neutral[isDark ? 8 : 9]
tokens["icon-weak-disabled"] = noInk && isDark ? neutral[11] : neutral[isDark ? 3 : 5]
tokens["icon-weak-focus"] = neutral[8]
tokens["icon-strong-base"] = neutral[11]
tokens["icon-strong-hover"] = isDark ? "#f6f3f3" : "#151313"
tokens["icon-strong-active"] = isDark ? "#fcfcfc" : "#020202"
tokens["icon-strong-selected"] = isDark ? "#fdfcfc" : "#020202"
tokens["icon-strong-disabled"] = neutral[7]
tokens["icon-strong-disabled"] = noInk && isDark ? neutral[6] : neutral[7]
tokens["icon-strong-focus"] = isDark ? "#fdfcfc" : "#020202"
tokens["icon-brand-base"] = isDark ? "#ffffff" : neutral[11]
tokens["icon-interactive-base"] = interactive[8]
tokens["icon-success-base"] = success[isDark ? 6 : 6]
tokens["icon-success-hover"] = success[7]
tokens["icon-success-base"] = success[isDark ? 8 : 6]
tokens["icon-success-hover"] = success[isDark ? 9 : 7]
tokens["icon-success-active"] = success[10]
tokens["icon-warning-base"] = warning[6]
tokens["icon-warning-hover"] = warning[7]
tokens["icon-warning-active"] = warning[10]
tokens["icon-warning-base"] = amber[isDark ? 8 : 6]
tokens["icon-warning-hover"] = amber[7]
tokens["icon-warning-active"] = amber[10]
tokens["icon-critical-base"] = error[isDark ? 8 : 9]
tokens["icon-critical-hover"] = error[10]
tokens["icon-critical-active"] = error[11]
@@ -213,16 +430,16 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
tokens["icon-on-interactive-base"] = isDark ? neutral[11] : neutral[0]
tokens["icon-agent-plan-base"] = info[8]
tokens["icon-agent-docs-base"] = warning[8]
tokens["icon-agent-ask-base"] = interactive[8]
tokens["icon-agent-docs-base"] = amber[8]
tokens["icon-agent-ask-base"] = blue[8]
tokens["icon-agent-build-base"] = interactive[isDark ? 10 : 8]
tokens["icon-on-success-base"] = withAlpha(success[8], 0.9) as ColorValue
tokens["icon-on-success-hover"] = withAlpha(success[9], 0.9) as ColorValue
tokens["icon-on-success-selected"] = withAlpha(success[10], 0.9) as ColorValue
tokens["icon-on-warning-base"] = withAlpha(warning[8], 0.9) as ColorValue
tokens["icon-on-warning-hover"] = withAlpha(warning[9], 0.9) as ColorValue
tokens["icon-on-warning-selected"] = withAlpha(warning[10], 0.9) as ColorValue
tokens["icon-on-warning-base"] = withAlpha(amber[8], 0.9) as ColorValue
tokens["icon-on-warning-hover"] = withAlpha(amber[9], 0.9) as ColorValue
tokens["icon-on-warning-selected"] = withAlpha(amber[10], 0.9) as ColorValue
tokens["icon-on-critical-base"] = withAlpha(error[8], 0.9) as ColorValue
tokens["icon-on-critical-hover"] = withAlpha(error[9], 0.9) as ColorValue
tokens["icon-on-critical-selected"] = withAlpha(error[10], 0.9) as ColorValue
@@ -235,42 +452,120 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
tokens["icon-diff-add-active"] = diffAdd[isDark ? 10 : 11]
tokens["icon-diff-delete-base"] = diffDelete[isDark ? 8 : 9]
tokens["icon-diff-delete-hover"] = diffDelete[isDark ? 9 : 10]
tokens["icon-diff-modified-base"] = isDark ? "#ffba92" : "#FF8C00"
tokens["icon-diff-modified-base"] = modified()
tokens["syntax-comment"] = "var(--text-weak)"
tokens["syntax-regexp"] = "var(--text-base)"
tokens["syntax-string"] = isDark ? "#00ceb9" : "#006656"
tokens["syntax-keyword"] = "var(--text-weak)"
tokens["syntax-primitive"] = isDark ? "#ffba92" : "#fb4804"
tokens["syntax-operator"] = isDark ? "var(--text-weak)" : "var(--text-base)"
tokens["syntax-variable"] = "var(--text-strong)"
tokens["syntax-property"] = isDark ? "#ff9ae2" : "#ed6dc8"
tokens["syntax-type"] = isDark ? "#ecf58c" : "#596600"
tokens["syntax-constant"] = isDark ? "#93e9f6" : "#007b80"
tokens["syntax-punctuation"] = isDark ? "var(--text-weak)" : "var(--text-base)"
tokens["syntax-object"] = "var(--text-strong)"
tokens["syntax-success"] = success[9]
tokens["syntax-warning"] = warning[9]
tokens["syntax-critical"] = error[isDark ? 9 : 9]
tokens["syntax-info"] = isDark ? "#93e9f6" : "#0092a8"
tokens["syntax-diff-add"] = diffAdd[10]
tokens["syntax-diff-delete"] = diffDelete[10]
tokens["syntax-diff-unknown"] = "#ff0000"
if (colors.compact) {
if (!hasInk) {
tokens["syntax-comment"] = "var(--text-weak)"
tokens["syntax-regexp"] = "var(--text-base)"
tokens["syntax-string"] = isDark ? "#00ceb9" : "#006656"
tokens["syntax-keyword"] = "var(--text-weak)"
tokens["syntax-primitive"] = isDark ? "#ffba92" : "#fb4804"
tokens["syntax-operator"] = isDark ? "var(--text-weak)" : "var(--text-base)"
tokens["syntax-variable"] = "var(--text-strong)"
tokens["syntax-property"] = isDark ? "#ff9ae2" : "#ed6dc8"
tokens["syntax-type"] = isDark ? "#ecf58c" : "#596600"
tokens["syntax-constant"] = isDark ? "#93e9f6" : "#007b80"
tokens["syntax-punctuation"] = isDark ? "var(--text-weak)" : "var(--text-base)"
tokens["syntax-object"] = "var(--text-strong)"
tokens["syntax-success"] = success[9]
tokens["syntax-warning"] = amber[9]
tokens["syntax-critical"] = error[9]
tokens["syntax-info"] = isDark ? "#93e9f6" : "#0092a8"
tokens["syntax-diff-add"] = diffAdd[10]
tokens["syntax-diff-delete"] = diffDelete[10]
tokens["syntax-diff-unknown"] = "#ff0000"
tokens["markdown-heading"] = isDark ? "#9d7cd8" : "#d68c27"
tokens["markdown-text"] = isDark ? "#eeeeee" : "#1a1a1a"
tokens["markdown-link"] = isDark ? "#fab283" : "#3b7dd8"
tokens["markdown-link-text"] = isDark ? "#56b6c2" : "#318795"
tokens["markdown-code"] = isDark ? "#7fd88f" : "#3d9a57"
tokens["markdown-block-quote"] = isDark ? "#e5c07b" : "#b0851f"
tokens["markdown-emph"] = isDark ? "#e5c07b" : "#b0851f"
tokens["markdown-strong"] = isDark ? "#f5a742" : "#d68c27"
tokens["markdown-horizontal-rule"] = isDark ? "#808080" : "#8a8a8a"
tokens["markdown-list-item"] = isDark ? "#fab283" : "#3b7dd8"
tokens["markdown-list-enumeration"] = isDark ? "#56b6c2" : "#318795"
tokens["markdown-image"] = isDark ? "#fab283" : "#3b7dd8"
tokens["markdown-image-text"] = isDark ? "#56b6c2" : "#318795"
tokens["markdown-code-block"] = isDark ? "#eeeeee" : "#1a1a1a"
tokens["markdown-heading"] = isDark ? "#9d7cd8" : "#d68c27"
tokens["markdown-text"] = isDark ? "#eeeeee" : "#1a1a1a"
tokens["markdown-link"] = isDark ? "#fab283" : "#3b7dd8"
tokens["markdown-link-text"] = isDark ? "#56b6c2" : "#318795"
tokens["markdown-code"] = isDark ? "#7fd88f" : "#3d9a57"
tokens["markdown-block-quote"] = isDark ? "#e5c07b" : "#b0851f"
tokens["markdown-emph"] = isDark ? "#e5c07b" : "#b0851f"
tokens["markdown-strong"] = isDark ? "#f5a742" : "#d68c27"
tokens["markdown-horizontal-rule"] = isDark ? "#808080" : "#8a8a8a"
tokens["markdown-list-item"] = isDark ? "#fab283" : "#3b7dd8"
tokens["markdown-list-enumeration"] = isDark ? "#56b6c2" : "#318795"
tokens["markdown-image"] = isDark ? "#fab283" : "#3b7dd8"
tokens["markdown-image-text"] = isDark ? "#56b6c2" : "#318795"
tokens["markdown-code-block"] = isDark ? "#eeeeee" : "#1a1a1a"
}
if (hasInk) {
tokens["syntax-comment"] = "var(--text-weak)"
tokens["syntax-regexp"] = "var(--text-base)"
tokens["syntax-string"] = content(colors.success, success)
tokens["syntax-keyword"] = "var(--text-weak)"
tokens["syntax-primitive"] = content(colors.accent, accent)
tokens["syntax-operator"] = isDark ? "var(--text-weak)" : "var(--text-base)"
tokens["syntax-variable"] = "var(--text-strong)"
tokens["syntax-property"] = content(colors.primary, primary)
tokens["syntax-type"] = content(colors.warning, warning)
tokens["syntax-constant"] = content(colors.info, info)
tokens["syntax-punctuation"] = isDark ? "var(--text-weak)" : "var(--text-base)"
tokens["syntax-object"] = "var(--text-strong)"
tokens["syntax-success"] = success[9]
tokens["syntax-warning"] = amber[9]
tokens["syntax-critical"] = error[9]
tokens["syntax-info"] = content(colors.info, info)
tokens["syntax-diff-add"] = diffAdd[10]
tokens["syntax-diff-delete"] = diffDelete[10]
tokens["syntax-diff-unknown"] = "#ff0000"
tokens["markdown-heading"] = content(colors.primary, primary)
tokens["markdown-text"] = tokens["text-base"]
tokens["markdown-link"] = content(colors.interactive, interactive)
tokens["markdown-link-text"] = content(colors.info, info)
tokens["markdown-code"] = content(colors.success, success)
tokens["markdown-block-quote"] = content(colors.warning, warning)
tokens["markdown-emph"] = content(colors.warning, warning)
tokens["markdown-strong"] = content(colors.accent, accent)
tokens["markdown-horizontal-rule"] = tokens["border-base"]
tokens["markdown-list-item"] = content(colors.interactive, interactive)
tokens["markdown-list-enumeration"] = content(colors.info, info)
tokens["markdown-image"] = content(colors.interactive, interactive)
tokens["markdown-image-text"] = content(colors.info, info)
tokens["markdown-code-block"] = tokens["text-base"]
}
}
if (!colors.compact) {
tokens["syntax-comment"] = "var(--text-weak)"
tokens["syntax-regexp"] = "var(--text-base)"
tokens["syntax-string"] = isDark ? "#00ceb9" : "#006656"
tokens["syntax-keyword"] = "var(--text-weak)"
tokens["syntax-primitive"] = isDark ? "#ffba92" : "#fb4804"
tokens["syntax-operator"] = isDark ? "var(--text-weak)" : "var(--text-base)"
tokens["syntax-variable"] = "var(--text-strong)"
tokens["syntax-property"] = isDark ? "#ff9ae2" : "#ed6dc8"
tokens["syntax-type"] = isDark ? "#ecf58c" : "#596600"
tokens["syntax-constant"] = isDark ? "#93e9f6" : "#007b80"
tokens["syntax-punctuation"] = isDark ? "var(--text-weak)" : "var(--text-base)"
tokens["syntax-object"] = "var(--text-strong)"
tokens["syntax-success"] = success[9]
tokens["syntax-warning"] = amber[9]
tokens["syntax-critical"] = error[9]
tokens["syntax-info"] = isDark ? "#93e9f6" : "#0092a8"
tokens["syntax-diff-add"] = diffAdd[10]
tokens["syntax-diff-delete"] = diffDelete[10]
tokens["syntax-diff-unknown"] = "#ff0000"
tokens["markdown-heading"] = isDark ? "#9d7cd8" : "#d68c27"
tokens["markdown-text"] = isDark ? "#eeeeee" : "#1a1a1a"
tokens["markdown-link"] = isDark ? "#fab283" : "#3b7dd8"
tokens["markdown-link-text"] = isDark ? "#56b6c2" : "#318795"
tokens["markdown-code"] = isDark ? "#7fd88f" : "#3d9a57"
tokens["markdown-block-quote"] = isDark ? "#e5c07b" : "#b0851f"
tokens["markdown-emph"] = isDark ? "#e5c07b" : "#b0851f"
tokens["markdown-strong"] = isDark ? "#f5a742" : "#d68c27"
tokens["markdown-horizontal-rule"] = isDark ? "#808080" : "#8a8a8a"
tokens["markdown-list-item"] = isDark ? "#fab283" : "#3b7dd8"
tokens["markdown-list-enumeration"] = isDark ? "#56b6c2" : "#318795"
tokens["markdown-image"] = isDark ? "#fab283" : "#3b7dd8"
tokens["markdown-image-text"] = isDark ? "#56b6c2" : "#318795"
tokens["markdown-code-block"] = isDark ? "#eeeeee" : "#1a1a1a"
}
tokens["avatar-background-pink"] = isDark ? "#501b3f" : "#feeef8"
tokens["avatar-background-mint"] = isDark ? "#033a34" : "#e1fbf4"
@@ -289,13 +584,101 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
tokens[key] = value
}
if (hasInk && "text-weak" in overrides && !("text-weaker" in overrides)) {
const weak = tokens["text-weak"]
if (weak.startsWith("#")) {
tokens["text-weaker"] = shift(weak as HexColor, { l: isDark ? -0.12 : 0.12, c: 0.75 })
} else {
tokens["text-weaker"] = weak
}
}
if (colors.compact && hasInk) {
if (!("markdown-text" in overrides)) {
tokens["markdown-text"] = tokens["text-base"]
}
if (!("markdown-code-block" in overrides)) {
tokens["markdown-code-block"] = tokens["text-base"]
}
}
if (!("text-stronger" in overrides)) {
tokens["text-stronger"] = tokens["text-strong"]
}
return tokens
}
interface ThemeColors {
compact: boolean
neutral: HexColor
ink?: HexColor
primary: HexColor
accent: HexColor
success: HexColor
warning: HexColor
error: HexColor
info: HexColor
interactive: HexColor
diffAdd?: HexColor
diffDelete?: HexColor
}
function getColors(variant: ThemeVariant): ThemeColors {
const input = variant as { palette?: unknown; seeds?: unknown }
if (input.palette && input.seeds) {
throw new Error("Theme variant cannot define both `palette` and `seeds`")
}
if (variant.palette) {
return {
compact: true,
neutral: variant.palette.neutral,
ink: variant.palette.ink,
primary: variant.palette.primary,
accent: variant.palette.accent ?? variant.palette.info,
success: variant.palette.success,
warning: variant.palette.warning,
error: variant.palette.error,
info: variant.palette.info,
interactive: variant.palette.interactive ?? variant.palette.primary,
diffAdd: variant.palette.diffAdd,
diffDelete: variant.palette.diffDelete,
}
}
if (variant.seeds) {
return {
compact: false,
neutral: variant.seeds.neutral,
ink: undefined,
primary: variant.seeds.primary,
accent: variant.seeds.info,
success: variant.seeds.success,
warning: variant.seeds.warning,
error: variant.seeds.error,
info: variant.seeds.info,
interactive: variant.seeds.interactive,
diffAdd: variant.seeds.diffAdd,
diffDelete: variant.seeds.diffDelete,
}
}
throw new Error("Theme variant requires `palette` or `seeds`")
}
function generateNeutralOverlayScale(neutralScale: HexColor[], isDark: boolean): ColorValue[] {
const alphas = isDark
? [0, 0.034, 0.063, 0.084, 0.109, 0.138, 0.181, 0.266, 0.404, 0.468, 0.603, 0.928]
: [0.014, 0.034, 0.055, 0.075, 0.096, 0.118, 0.151, 0.232, 0.453, 0.492, 0.574, 0.915]
const color = (isDark ? "#ffffff" : "#000000") as HexColor
return alphas.map((alpha) => withAlpha(color, alpha) as ColorValue)
}
function generateNeutralAlphaScale(neutralScale: HexColor[], isDark: boolean): HexColor[] {
const alphas = isDark
? [0.02, 0.04, 0.08, 0.12, 0.16, 0.2, 0.26, 0.36, 0.44, 0.52, 0.72, 0.94]
: [0.01, 0.03, 0.06, 0.09, 0.12, 0.15, 0.2, 0.27, 0.46, 0.61, 0.5, 0.87]
? [0.024, 0.048, 0.088, 0.128, 0.17, 0.215, 0.275, 0.38, 0.46, 0.54, 0.74, 0.95]
: [0.014, 0.034, 0.066, 0.098, 0.128, 0.158, 0.208, 0.282, 0.47, 0.625, 0.515, 0.88]
return neutralScale.map((hex, i) => {
const baseOklch = hexToOklch(hex)
@@ -307,6 +690,11 @@ function generateNeutralAlphaScale(neutralScale: HexColor[], isDark: boolean): H
})
}
function getHex(value: ColorValue | undefined): HexColor | undefined {
if (!value?.startsWith("#")) return
return value as HexColor
}
export function resolveTheme(theme: DesktopTheme): { light: ResolvedTheme; dark: ResolvedTheme } {
return {
light: resolveThemeVariant(theme.light, false),

View File

@@ -3,129 +3,37 @@
"name": "Aura",
"id": "aura",
"light": {
"seeds": {
"palette": {
"neutral": "#f5f0ff",
"ink": "#2d2640",
"primary": "#a277ff",
"accent": "#d94f4f",
"success": "#40bf7a",
"warning": "#d9a24a",
"error": "#d94f4f",
"info": "#5bb8d9",
"interactive": "#a277ff",
"diffAdd": "#b3e6cc",
"diffDelete": "#f5b3b3"
},
"overrides": {
"background-base": "#f5f0ff",
"background-weak": "#efe8fc",
"background-strong": "#faf7ff",
"background-stronger": "#fdfcff",
"border-weak-base": "#e0d6f2",
"border-weak-hover": "#d5c9eb",
"border-weak-active": "#cbbee3",
"border-weak-selected": "#c0b3dc",
"border-weak-disabled": "#f9f6ff",
"border-weak-focus": "#c5b8df",
"border-base": "#b5a6d4",
"border-hover": "#aa99cc",
"border-active": "#9f8dc4",
"border-selected": "#9480bc",
"border-disabled": "#ede7f9",
"border-focus": "#a593c8",
"border-strong-base": "#8068a8",
"border-strong-hover": "#735a9c",
"border-strong-active": "#664d90",
"border-strong-selected": "#5a4184",
"border-strong-disabled": "#d4c8ed",
"border-strong-focus": "#6d5396",
"surface-diff-add-base": "#e8f5ed",
"surface-diff-delete-base": "#fae8e8",
"surface-diff-hidden-base": "#e8e4f5",
"text-base": "#2d2640",
"text-weak": "#5c5270",
"text-strong": "#15101f",
"syntax-string": "#40bf7a",
"syntax-primitive": "#d94f4f",
"syntax-property": "#a277ff",
"syntax-type": "#d9a24a",
"syntax-constant": "#5bb8d9",
"syntax-info": "#5bb8d9",
"markdown-heading": "#a277ff",
"markdown-text": "#2d2640",
"markdown-link": "#c17ac8",
"markdown-link-text": "#a277ff",
"markdown-code": "#40bf7a",
"markdown-block-quote": "#6d6d6d",
"markdown-emph": "#d9a24a",
"markdown-strong": "#a277ff",
"markdown-horizontal-rule": "#d4c8ed",
"markdown-list-item": "#a277ff",
"markdown-list-enumeration": "#a277ff",
"markdown-image": "#c17ac8",
"markdown-image-text": "#a277ff",
"markdown-code-block": "#5bb8d9"
"syntax-keyword": "#7b5ae0"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#15141b",
"ink": "#edecee",
"primary": "#a277ff",
"accent": "#ff6767",
"success": "#61ffca",
"warning": "#ffca85",
"error": "#ff6767",
"info": "#82e2ff",
"interactive": "#a277ff",
"diffAdd": "#61ffca",
"diffDelete": "#ff6767"
},
"overrides": {
"background-base": "#15141b",
"background-weak": "#1a1921",
"background-strong": "#121118",
"background-stronger": "#0f0e14",
"border-weak-base": "#2d2b38",
"border-weak-hover": "#332f42",
"border-weak-active": "#38354c",
"border-weak-selected": "#3e3a56",
"border-weak-disabled": "#1a1921",
"border-weak-focus": "#363350",
"border-base": "#433f5a",
"border-hover": "#4a4565",
"border-active": "#514c70",
"border-selected": "#58527b",
"border-disabled": "#1f1e28",
"border-focus": "#4e496c",
"border-strong-base": "#635c8a",
"border-strong-hover": "#6d6597",
"border-strong-active": "#776fa4",
"border-strong-selected": "#8179b1",
"border-strong-disabled": "#2a283a",
"border-strong-focus": "#716a9e",
"surface-diff-add-base": "#162620",
"surface-diff-delete-base": "#26161a",
"surface-diff-hidden-base": "#1e1d2a",
"text-base": "#edecee",
"text-weak": "#6d6d6d",
"text-strong": "#ffffff",
"syntax-string": "#61ffca",
"syntax-primitive": "#ff6767",
"syntax-property": "#a277ff",
"syntax-type": "#ffca85",
"syntax-constant": "#82e2ff",
"syntax-info": "#82e2ff",
"markdown-heading": "#a277ff",
"markdown-text": "#edecee",
"markdown-link": "#f694ff",
"markdown-link-text": "#a277ff",
"markdown-code": "#61ffca",
"markdown-block-quote": "#6d6d6d",
"markdown-emph": "#ffca85",
"markdown-strong": "#a277ff",
"markdown-horizontal-rule": "#2d2b38",
"markdown-list-item": "#a277ff",
"markdown-list-enumeration": "#a277ff",
"markdown-image": "#f694ff",
"markdown-image-text": "#a277ff",
"markdown-code-block": "#edecee"
"syntax-keyword": "#a277ff"
}
}
}

View File

@@ -3,131 +3,37 @@
"name": "Ayu",
"id": "ayu",
"light": {
"seeds": {
"palette": {
"neutral": "#fdfaf4",
"ink": "#4f5964",
"primary": "#4aa8c8",
"accent": "#ef7d71",
"success": "#5fb978",
"warning": "#ea9f41",
"error": "#e6656a",
"info": "#2f9bce",
"interactive": "#4aa8c8",
"diffAdd": "#b1d780",
"diffDelete": "#e6656a"
},
"overrides": {
"background-base": "#fdfaf4",
"background-weak": "#fcf9f3",
"background-strong": "#fbf8f2",
"background-stronger": "#faf7f1",
"surface-raised-base-hover": "#f4f0e9",
"border-weak-base": "#e6ddcf",
"border-weak-hover": "#dcd3c5",
"border-weak-active": "#d1c9ba",
"border-weak-selected": "#c6bfaf",
"border-weak-disabled": "#f7f0e6",
"border-weak-focus": "#cbc4b6",
"border-base": "#bfb3a3",
"border-hover": "#b4a898",
"border-active": "#a99e8e",
"border-selected": "#9e9383",
"border-disabled": "#efe5d8",
"border-focus": "#b09f8f",
"border-strong-base": "#837765",
"border-strong-hover": "#7a6f5f",
"border-strong-active": "#716655",
"border-strong-selected": "#685e4e",
"border-strong-disabled": "#d8cabc",
"border-strong-focus": "#766b5c",
"surface-diff-add-base": "#eef5e4",
"surface-diff-delete-base": "#fde5e5",
"surface-diff-hidden-base": "#e3edf3",
"text-base": "#4f5964",
"text-weak": "#77818d",
"text-strong": "#1b232b",
"syntax-string": "#7fad00",
"syntax-primitive": "#ef7d71",
"syntax-property": "#4aa8c8",
"syntax-type": "#ed982e",
"syntax-constant": "#2f9bce",
"syntax-info": "#2f9bce",
"markdown-heading": "#4aa8c8",
"markdown-text": "#4f5964",
"markdown-link": "#4aa8c8",
"markdown-link-text": "#2f9bce",
"markdown-code": "#7fad00",
"markdown-block-quote": "#ed982e",
"markdown-emph": "#ed982e",
"markdown-strong": "#f07f72",
"markdown-horizontal-rule": "#d7cec0",
"markdown-list-item": "#4aa8c8",
"markdown-list-enumeration": "#2f9bce",
"markdown-image": "#4aa8c8",
"markdown-image-text": "#2f9bce",
"markdown-code-block": "#4aa8c8"
"syntax-keyword": "#ea9f41"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#0f1419",
"ink": "#d6dae0",
"primary": "#3fb7e3",
"accent": "#f2856f",
"success": "#78d05c",
"warning": "#e4a75c",
"error": "#f58572",
"info": "#66c6f1",
"interactive": "#3fb7e3",
"diffAdd": "#59c57c",
"diffDelete": "#f58572"
},
"overrides": {
"background-base": "#0f1419",
"background-weak": "#18222c",
"background-strong": "#0b1015",
"background-stronger": "#080c10",
"surface-raised-base-hover": "#0f1419",
"border-weak-base": "#2b3440",
"border-weak-hover": "#323c49",
"border-weak-active": "#394454",
"border-weak-selected": "#415063",
"border-weak-disabled": "#0a0e12",
"border-weak-focus": "#374453",
"border-base": "#475367",
"border-hover": "#515f75",
"border-active": "#5d6b83",
"border-selected": "#687795",
"border-disabled": "#11161d",
"border-focus": "#56647c",
"border-strong-base": "#73819b",
"border-strong-hover": "#7f8da8",
"border-strong-active": "#8b99b5",
"border-strong-selected": "#98a6c3",
"border-strong-disabled": "#1b222c",
"border-strong-focus": "#8391ad",
"surface-diff-add-base": "#132f27",
"surface-diff-delete-base": "#361d20",
"surface-diff-hidden-base": "#1b2632",
"text-base": "#d6dae0",
"text-weak": "#a3adba",
"text-strong": "#fbfbfd",
"syntax-string": "#b1c74a",
"syntax-primitive": "#f2856f",
"syntax-property": "#3fb7e3",
"syntax-type": "#e4a75c",
"syntax-constant": "#66c6f1",
"syntax-info": "#66c6f1",
"markdown-heading": "#3fb7e3",
"markdown-text": "#d6dae0",
"markdown-link": "#3fb7e3",
"markdown-link-text": "#66c6f1",
"markdown-code": "#b1c74a",
"markdown-block-quote": "#e4a75c",
"markdown-emph": "#e4a75c",
"markdown-strong": "#f2856f",
"markdown-horizontal-rule": "#2b3542",
"markdown-list-item": "#3fb7e3",
"markdown-list-enumeration": "#66c6f1",
"markdown-image": "#3fb7e3",
"markdown-image-text": "#66c6f1",
"markdown-code-block": "#d6dae0"
"syntax-keyword": "#ffad66"
}
}
}

View File

@@ -3,9 +3,11 @@
"name": "Carbonfox",
"id": "carbonfox",
"light": {
"seeds": {
"palette": {
"neutral": "#8e8e8e",
"ink": "#161616",
"primary": "#0072c3",
"accent": "#da1e28",
"success": "#198038",
"warning": "#f1c21b",
"error": "#da1e28",
@@ -15,44 +17,15 @@
"diffDelete": "#da1e28"
},
"overrides": {
"background-base": "#ffffff",
"background-weak": "#f4f4f4",
"background-strong": "#e8e8e8",
"background-stronger": "#dcdcdc",
"surface-raised-strong": "#ffffff",
"surface-raised-stronger": "#ffffff",
"surface-float-base": "#161616",
"surface-float-base-hover": "#262626",
"text-base": "#161616",
"text-weak": "#525252",
"text-strong": "#000000",
"syntax-string": "#198038",
"syntax-primitive": "#da1e28",
"syntax-property": "#0043ce",
"syntax-type": "#007d79",
"syntax-constant": "#6929c4",
"syntax-keyword": "#525252",
"syntax-info": "#0043ce",
"markdown-heading": "#0043ce",
"markdown-text": "#161616",
"markdown-link": "#0043ce",
"markdown-link-text": "#0072c3",
"markdown-code": "#198038",
"markdown-block-quote": "#525252",
"markdown-emph": "#6929c4",
"markdown-strong": "#161616",
"markdown-horizontal-rule": "#c6c6c6",
"markdown-list-item": "#0072c3",
"markdown-list-enumeration": "#0072c3",
"markdown-image": "#0043ce",
"markdown-image-text": "#0072c3",
"markdown-code-block": "#393939"
"syntax-keyword": "#8a3ffc"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#393939",
"ink": "#f2f4f8",
"primary": "#33b1ff",
"accent": "#ff8389",
"success": "#42be65",
"warning": "#f1c21b",
"error": "#ff8389",
@@ -62,61 +35,7 @@
"diffDelete": "#ff8389"
},
"overrides": {
"background-base": "#161616",
"background-weak": "#262626",
"background-strong": "#0d0d0d",
"background-stronger": "#000000",
"surface-raised-base": "#1c1c1c",
"surface-raised-base-hover": "#262626",
"surface-raised-strong": "#262626",
"surface-raised-strong-hover": "#303030",
"surface-raised-stronger": "#303030",
"surface-raised-stronger-hover": "#393939",
"surface-raised-stronger-non-alpha": "#303030",
"surface-float-base": "#0d0d0d",
"surface-float-base-hover": "#1a1a1a",
"surface-inset-base": "#0d0d0d",
"surface-inset-strong": "#000000",
"surface-base": "#1e1e1e",
"surface-base-hover": "#262626",
"surface-diff-add-base": "#0e3a22",
"surface-diff-delete-base": "#4d1a1f",
"input-base": "#262626",
"input-hover": "#303030",
"button-secondary-base": "#393939",
"button-secondary-hover": "#4c4c4c",
"border-weak-base": "#393939",
"border-weak-hover": "#4c4c4c",
"border-base": "#525252",
"border-hover": "#636363",
"border-strong-base": "#6f6f6f",
"text-base": "#f2f4f8",
"text-weak": "#8d8d8d",
"text-weaker": "#6f6f6f",
"text-strong": "#ffffff",
"icon-base": "#8d8d8d",
"icon-weak-base": "#6f6f6f",
"syntax-string": "#42be65",
"syntax-primitive": "#ff8389",
"syntax-property": "#78a9ff",
"syntax-type": "#08bdba",
"syntax-constant": "#be95ff",
"syntax-keyword": "#8d8d8d",
"syntax-info": "#78a9ff",
"markdown-heading": "#82cfff",
"markdown-text": "#f2f4f8",
"markdown-link": "#78a9ff",
"markdown-link-text": "#33b1ff",
"markdown-code": "#42be65",
"markdown-block-quote": "#8d8d8d",
"markdown-emph": "#be95ff",
"markdown-strong": "#ffffff",
"markdown-horizontal-rule": "#393939",
"markdown-list-item": "#33b1ff",
"markdown-list-enumeration": "#33b1ff",
"markdown-image": "#78a9ff",
"markdown-image-text": "#33b1ff",
"markdown-code-block": "#c6c6c6"
"syntax-keyword": "#be95ff"
}
}
}

View File

@@ -3,129 +3,39 @@
"name": "Catppuccin",
"id": "catppuccin",
"light": {
"seeds": {
"palette": {
"neutral": "#f5e0dc",
"ink": "#4c4f69",
"primary": "#7287fd",
"accent": "#d20f39",
"success": "#40a02b",
"warning": "#df8e1d",
"error": "#d20f39",
"info": "#04a5e5",
"interactive": "#7287fd",
"diffAdd": "#a6d189",
"diffDelete": "#e78284"
},
"overrides": {
"background-base": "#f5e0dc",
"background-weak": "#f2d8d4",
"background-strong": "#f9e8e4",
"background-stronger": "#fdeeee",
"border-weak-base": "#e0cfd3",
"border-weak-hover": "#d6c4c8",
"border-weak-active": "#cdb9be",
"border-weak-selected": "#c2aeb4",
"border-weak-disabled": "#fbeff2",
"border-weak-focus": "#c7b4ba",
"border-base": "#bca6b2",
"border-hover": "#b19ca8",
"border-active": "#a6929e",
"border-selected": "#9a8894",
"border-disabled": "#f3e4e7",
"border-focus": "#ab97a1",
"border-strong-base": "#83677f",
"border-strong-hover": "#775b73",
"border-strong-active": "#6b5068",
"border-strong-selected": "#5f465d",
"border-strong-disabled": "#d9c5cf",
"border-strong-focus": "#714f66",
"surface-diff-add-base": "#edf5e6",
"surface-diff-delete-base": "#fde1e3",
"surface-diff-hidden-base": "#e4e2f6",
"text-base": "#4c4f69",
"text-weak": "#6c6f85",
"text-strong": "#1f1f2a",
"syntax-string": "#40a02b",
"syntax-primitive": "#d20f39",
"syntax-property": "#7287fd",
"syntax-type": "#df8e1d",
"syntax-constant": "#04a5e5",
"syntax-info": "#04a5e5",
"markdown-heading": "#7287fd",
"markdown-text": "#4c4f69",
"markdown-link": "#7287fd",
"markdown-link-text": "#04a5e5",
"markdown-code": "#40a02b",
"markdown-block-quote": "#df8e1d",
"markdown-emph": "#df8e1d",
"markdown-strong": "#d20f39",
"markdown-horizontal-rule": "#d4c5cf",
"markdown-list-item": "#7287fd",
"markdown-list-enumeration": "#04a5e5",
"markdown-image": "#7287fd",
"markdown-image-text": "#04a5e5",
"markdown-code-block": "#7287fd"
"syntax-keyword": "#8839ef",
"syntax-primitive": "#fe640b"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#1e1e2e",
"ink": "#cdd6f4",
"primary": "#b4befe",
"accent": "#f38ba8",
"success": "#a6d189",
"warning": "#f4b8e4",
"error": "#f38ba8",
"info": "#89dceb",
"interactive": "#b4befe",
"diffAdd": "#94e2d5",
"diffDelete": "#f38ba8"
},
"overrides": {
"background-base": "#1e1e2e",
"background-weak": "#211f31",
"background-strong": "#1c1c29",
"background-stronger": "#191926",
"border-weak-base": "#35324a",
"border-weak-hover": "#393655",
"border-weak-active": "#403c61",
"border-weak-selected": "#47436d",
"border-weak-disabled": "#141426",
"border-weak-focus": "#3d3a63",
"border-base": "#4a4763",
"border-hover": "#524f70",
"border-active": "#5a577d",
"border-selected": "#625f8a",
"border-disabled": "#1b1a2c",
"border-focus": "#575379",
"border-strong-base": "#6e6a8c",
"border-strong-hover": "#787497",
"border-strong-active": "#8380a2",
"border-strong-selected": "#8d8bad",
"border-strong-disabled": "#232237",
"border-strong-focus": "#7b779b",
"surface-diff-add-base": "#1d2c30",
"surface-diff-delete-base": "#2c1f2a",
"surface-diff-hidden-base": "#232538",
"text-base": "#cdd6f4",
"text-weak": "#a6adc8",
"text-strong": "#f4f2ff",
"syntax-string": "#a6e3a1",
"syntax-primitive": "#f38ba8",
"syntax-property": "#b4befe",
"syntax-type": "#f9e2af",
"syntax-constant": "#89dceb",
"syntax-info": "#89dceb",
"markdown-heading": "#b4befe",
"markdown-text": "#cdd6f4",
"markdown-link": "#b4befe",
"markdown-link-text": "#89dceb",
"markdown-code": "#a6e3a1",
"markdown-block-quote": "#f9e2af",
"markdown-emph": "#f9e2af",
"markdown-strong": "#f38ba8",
"markdown-horizontal-rule": "#2e2d45",
"markdown-list-item": "#b4befe",
"markdown-list-enumeration": "#89dceb",
"markdown-image": "#b4befe",
"markdown-image-text": "#89dceb",
"markdown-code-block": "#cdd6f4"
"syntax-keyword": "#cba6f7",
"syntax-primitive": "#fab387"
}
}
}

View File

@@ -3,129 +3,41 @@
"name": "Dracula",
"id": "dracula",
"light": {
"seeds": {
"palette": {
"neutral": "#f8f8f2",
"ink": "#1f1f2f",
"primary": "#7c6bf5",
"accent": "#d16090",
"success": "#2fbf71",
"warning": "#f7a14d",
"error": "#d9536f",
"info": "#1d7fc5",
"interactive": "#7c6bf5",
"diffAdd": "#9fe3b3",
"diffDelete": "#f8a1b8"
},
"overrides": {
"background-base": "#f8f8f2",
"background-weak": "#f1f2ed",
"background-strong": "#f6f6f1",
"background-stronger": "#f2f2ec",
"border-weak-base": "#e2e3da",
"border-weak-hover": "#d8d9d0",
"border-weak-active": "#cfd0c7",
"border-weak-selected": "#c4c6bc",
"border-weak-disabled": "#eceee3",
"border-weak-focus": "#c9cabf",
"border-base": "#c4c6ba",
"border-hover": "#b8baae",
"border-active": "#abada3",
"border-selected": "#979a90",
"border-disabled": "#e5e7dd",
"border-focus": "#b0b2a7",
"border-strong-base": "#9fa293",
"border-strong-hover": "#8e9185",
"border-strong-active": "#7e8176",
"border-strong-selected": "#6f7268",
"border-strong-disabled": "#c7c9be",
"border-strong-focus": "#878b7f",
"surface-diff-add-base": "#e4f5e6",
"surface-diff-delete-base": "#fae4eb",
"surface-diff-hidden-base": "#dedfe9",
"text-base": "#1f1f2f",
"text-weak": "#52526b",
"text-strong": "#05040c",
"syntax-string": "#2fbf71",
"syntax-primitive": "#d16090",
"syntax-property": "#7c6bf5",
"syntax-type": "#f7a14d",
"syntax-constant": "#1d7fc5",
"syntax-info": "#1d7fc5",
"markdown-heading": "#7c6bf5",
"markdown-text": "#1f1f2f",
"markdown-link": "#7c6bf5",
"markdown-link-text": "#1d7fc5",
"markdown-code": "#2fbf71",
"markdown-block-quote": "#f7a14d",
"markdown-emph": "#f7a14d",
"markdown-strong": "#d16090",
"markdown-horizontal-rule": "#c3c5d4",
"markdown-list-item": "#7c6bf5",
"markdown-list-enumeration": "#1d7fc5",
"markdown-image": "#7c6bf5",
"markdown-image-text": "#1d7fc5",
"markdown-code-block": "#1d7fc5"
"syntax-keyword": "#d16090",
"syntax-string": "#596600",
"syntax-primitive": "#7c6bf5"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#1d1e28",
"ink": "#f8f8f2",
"primary": "#bd93f9",
"accent": "#ff79c6",
"success": "#50fa7b",
"warning": "#ffb86c",
"error": "#ff5555",
"info": "#8be9fd",
"interactive": "#bd93f9",
"diffAdd": "#2fb27d",
"diffDelete": "#ff6b81"
},
"overrides": {
"background-base": "#14151f",
"background-weak": "#181926",
"background-strong": "#161722",
"background-stronger": "#191a26",
"border-weak-base": "#2d2f3c",
"border-weak-hover": "#303244",
"border-weak-active": "#35364c",
"border-weak-selected": "#3b3d55",
"border-weak-disabled": "#1e1f2b",
"border-weak-focus": "#383a50",
"border-base": "#3f415a",
"border-hover": "#464967",
"border-active": "#4d5073",
"border-selected": "#55587f",
"border-disabled": "#272834",
"border-focus": "#4a4d6d",
"border-strong-base": "#606488",
"border-strong-hover": "#6a6e96",
"border-strong-active": "#7378a3",
"border-strong-selected": "#7d82b1",
"border-strong-disabled": "#343649",
"border-strong-focus": "#6f739c",
"surface-diff-add-base": "#1f2a2f",
"surface-diff-delete-base": "#2d1f27",
"surface-diff-hidden-base": "#24253a",
"text-base": "#f8f8f2",
"text-weak": "#b6b9e4",
"text-strong": "#ffffff",
"syntax-string": "#50fa7b",
"syntax-primitive": "#ff79c6",
"syntax-property": "#bd93f9",
"syntax-type": "#ffb86c",
"syntax-constant": "#8be9fd",
"syntax-info": "#8be9fd",
"markdown-heading": "#bd93f9",
"markdown-text": "#f8f8f2",
"markdown-link": "#bd93f9",
"markdown-link-text": "#8be9fd",
"markdown-code": "#50fa7b",
"markdown-block-quote": "#ffb86c",
"markdown-emph": "#ffb86c",
"markdown-strong": "#ff79c6",
"markdown-horizontal-rule": "#44475a",
"markdown-list-item": "#bd93f9",
"markdown-list-enumeration": "#8be9fd",
"markdown-image": "#bd93f9",
"markdown-image-text": "#8be9fd",
"markdown-code-block": "#f8f8f2"
"syntax-keyword": "#ff79c6",
"syntax-string": "#f1fa8c",
"syntax-primitive": "#bd93f9"
}
}
}

View File

@@ -3,130 +3,39 @@
"name": "Gruvbox",
"id": "gruvbox",
"light": {
"seeds": {
"palette": {
"neutral": "#fbf1c7",
"ink": "#3c3836",
"primary": "#076678",
"accent": "#9d0006",
"success": "#79740e",
"warning": "#b57614",
"error": "#9d0006",
"info": "#8f3f71",
"interactive": "#076678",
"diffAdd": "#79740e",
"diffDelete": "#9d0006"
},
"overrides": {
"background-base": "#fbf1c7",
"background-weak": "#f2e5bc",
"background-strong": "#f9f5d7",
"background-stronger": "#fdf9e8",
"surface-raised-stronger-non-alpha": "#fbfaf5",
"border-weak-base": "#d5c4a1",
"border-weak-hover": "#c9b897",
"border-weak-active": "#bdae93",
"border-weak-selected": "#b0a285",
"border-weak-disabled": "#f0e4b8",
"border-weak-focus": "#c4b590",
"border-base": "#bdae93",
"border-hover": "#b0a285",
"border-active": "#a89984",
"border-selected": "#928374",
"border-disabled": "#e5d9ad",
"border-focus": "#a89984",
"border-strong-base": "#7c6f64",
"border-strong-hover": "#6e6259",
"border-strong-active": "#665c54",
"border-strong-selected": "#5a524b",
"border-strong-disabled": "#c9bda1",
"border-strong-focus": "#665c54",
"surface-diff-add-base": "#dde3b1",
"surface-diff-delete-base": "#e8c7c3",
"surface-diff-hidden-base": "#ebdfb5",
"text-base": "#3c3836",
"text-weak": "#7c6f64",
"text-strong": "#282828",
"syntax-string": "#79740e",
"syntax-primitive": "#9d0006",
"syntax-property": "#076678",
"syntax-type": "#b57614",
"syntax-constant": "#8f3f71",
"syntax-info": "#427b58",
"markdown-heading": "#076678",
"markdown-text": "#3c3836",
"markdown-link": "#076678",
"markdown-link-text": "#427b58",
"markdown-code": "#79740e",
"markdown-block-quote": "#928374",
"markdown-emph": "#8f3f71",
"markdown-strong": "#af3a03",
"markdown-horizontal-rule": "#d5c4a1",
"markdown-list-item": "#076678",
"markdown-list-enumeration": "#427b58",
"markdown-image": "#076678",
"markdown-image-text": "#427b58",
"markdown-code-block": "#3c3836"
"syntax-keyword": "#9d0006",
"syntax-primitive": "#8f3f71"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#282828",
"ink": "#ebdbb2",
"primary": "#83a598",
"accent": "#fb4934",
"success": "#b8bb26",
"warning": "#fabd2f",
"error": "#fb4934",
"info": "#d3869b",
"interactive": "#83a598",
"diffAdd": "#b8bb26",
"diffDelete": "#fb4934"
},
"overrides": {
"background-base": "#282828",
"background-weak": "#32302f",
"background-strong": "#1d2021",
"background-stronger": "#141617",
"border-weak-base": "#504945",
"border-weak-hover": "#5a524b",
"border-weak-active": "#665c54",
"border-weak-selected": "#70665d",
"border-weak-disabled": "#1e1d1c",
"border-weak-focus": "#5e5650",
"border-base": "#665c54",
"border-hover": "#70665d",
"border-active": "#7c6f64",
"border-selected": "#928374",
"border-disabled": "#2a2827",
"border-focus": "#7c6f64",
"border-strong-base": "#928374",
"border-strong-hover": "#9d8e7f",
"border-strong-active": "#a89984",
"border-strong-selected": "#b3a48f",
"border-strong-disabled": "#3c3836",
"border-strong-focus": "#a89984",
"surface-diff-add-base": "#2a3325",
"surface-diff-delete-base": "#3c2222",
"surface-diff-hidden-base": "#32302f",
"text-base": "#ebdbb2",
"text-weak": "#a89984",
"text-strong": "#fbf1c7",
"syntax-string": "#b8bb26",
"syntax-primitive": "#fb4934",
"syntax-property": "#83a598",
"syntax-type": "#fabd2f",
"syntax-constant": "#d3869b",
"syntax-info": "#8ec07c",
"markdown-heading": "#83a598",
"markdown-text": "#ebdbb2",
"markdown-link": "#83a598",
"markdown-link-text": "#8ec07c",
"markdown-code": "#b8bb26",
"markdown-block-quote": "#928374",
"markdown-emph": "#d3869b",
"markdown-strong": "#fe8019",
"markdown-horizontal-rule": "#504945",
"markdown-list-item": "#83a598",
"markdown-list-enumeration": "#8ec07c",
"markdown-image": "#83a598",
"markdown-image-text": "#8ec07c",
"markdown-code-block": "#ebdbb2"
"syntax-keyword": "#fb4934",
"syntax-primitive": "#d3869b"
}
}
}

View File

@@ -3,129 +3,41 @@
"name": "Monokai",
"id": "monokai",
"light": {
"seeds": {
"palette": {
"neutral": "#fdf8ec",
"ink": "#292318",
"primary": "#bf7bff",
"accent": "#d9487c",
"success": "#4fb54b",
"warning": "#f1a948",
"error": "#e54b4b",
"info": "#2d9ad7",
"interactive": "#bf7bff",
"diffAdd": "#bfe7a3",
"diffDelete": "#f6a3ae"
},
"overrides": {
"background-base": "#fdf8ec",
"background-weak": "#f8f2e6",
"background-strong": "#fbf5e8",
"background-stronger": "#f7efdd",
"border-weak-base": "#e9e0cf",
"border-weak-hover": "#dfd5c3",
"border-weak-active": "#d5cab7",
"border-weak-selected": "#cabfad",
"border-weak-disabled": "#f3ebdd",
"border-weak-focus": "#d0c2b1",
"border-base": "#c7b9a5",
"border-hover": "#bcae98",
"border-active": "#b0a28c",
"border-selected": "#a49781",
"border-disabled": "#efe5d6",
"border-focus": "#b6a893",
"border-strong-base": "#998b76",
"border-strong-hover": "#8a7c67",
"border-strong-active": "#7a6d58",
"border-strong-selected": "#6c604c",
"border-strong-disabled": "#d7cabc",
"border-strong-focus": "#82745f",
"surface-diff-add-base": "#e8f7e1",
"surface-diff-delete-base": "#fde5e4",
"surface-diff-hidden-base": "#e9e0d0",
"text-base": "#292318",
"text-weak": "#6d5c40",
"text-strong": "#1c150c",
"syntax-string": "#4fb54b",
"syntax-primitive": "#d9487c",
"syntax-property": "#bf7bff",
"syntax-type": "#f1a948",
"syntax-constant": "#2d9ad7",
"syntax-info": "#2d9ad7",
"markdown-heading": "#bf7bff",
"markdown-text": "#292318",
"markdown-link": "#bf7bff",
"markdown-link-text": "#2d9ad7",
"markdown-code": "#4fb54b",
"markdown-block-quote": "#f1a948",
"markdown-emph": "#f1a948",
"markdown-strong": "#d9487c",
"markdown-horizontal-rule": "#cdbdab",
"markdown-list-item": "#bf7bff",
"markdown-list-enumeration": "#2d9ad7",
"markdown-image": "#bf7bff",
"markdown-image-text": "#2d9ad7",
"markdown-code-block": "#2d9ad7"
"syntax-keyword": "#d9487c",
"syntax-string": "#8a6500",
"syntax-primitive": "#bf7bff"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#272822",
"ink": "#f8f8f2",
"primary": "#ae81ff",
"accent": "#f92672",
"success": "#a6e22e",
"warning": "#fd971f",
"error": "#f92672",
"info": "#66d9ef",
"interactive": "#ae81ff",
"diffAdd": "#4d7f2a",
"diffDelete": "#f4477c"
},
"overrides": {
"background-base": "#23241e",
"background-weak": "#27281f",
"background-strong": "#25261f",
"background-stronger": "#292a23",
"border-weak-base": "#343528",
"border-weak-hover": "#393a2d",
"border-weak-active": "#3f4033",
"border-weak-selected": "#454639",
"border-weak-disabled": "#1d1e16",
"border-weak-focus": "#414235",
"border-base": "#494a3a",
"border-hover": "#50523f",
"border-active": "#585a45",
"border-selected": "#60624b",
"border-disabled": "#23241b",
"border-focus": "#555741",
"border-strong-base": "#6a6c55",
"border-strong-hover": "#73755d",
"border-strong-active": "#7d7f66",
"border-strong-selected": "#878970",
"border-strong-disabled": "#2c2d23",
"border-strong-focus": "#7a7c63",
"surface-diff-add-base": "#1e2a1d",
"surface-diff-delete-base": "#301c24",
"surface-diff-hidden-base": "#2f2f24",
"text-base": "#f8f8f2",
"text-weak": "#c5c5c0",
"text-strong": "#ffffff",
"syntax-string": "#a6e22e",
"syntax-primitive": "#f92672",
"syntax-property": "#ae81ff",
"syntax-type": "#fd971f",
"syntax-constant": "#66d9ef",
"syntax-info": "#66d9ef",
"markdown-heading": "#ae81ff",
"markdown-text": "#f8f8f2",
"markdown-link": "#ae81ff",
"markdown-link-text": "#66d9ef",
"markdown-code": "#a6e22e",
"markdown-block-quote": "#fd971f",
"markdown-emph": "#fd971f",
"markdown-strong": "#f92672",
"markdown-horizontal-rule": "#3b3c34",
"markdown-list-item": "#ae81ff",
"markdown-list-enumeration": "#66d9ef",
"markdown-image": "#ae81ff",
"markdown-image-text": "#66d9ef",
"markdown-code-block": "#f8f8f2"
"syntax-keyword": "#f92672",
"syntax-string": "#e6db74",
"syntax-primitive": "#ae81ff"
}
}
}

View File

@@ -3,129 +3,40 @@
"name": "Night Owl",
"id": "nightowl",
"light": {
"seeds": {
"palette": {
"neutral": "#f0f0f0",
"ink": "#403f53",
"primary": "#4876d6",
"accent": "#aa0982",
"success": "#2aa298",
"warning": "#c96765",
"error": "#de3d3b",
"info": "#4876d6",
"interactive": "#4876d6",
"diffAdd": "#2aa298",
"diffDelete": "#de3d3b"
},
"overrides": {
"background-base": "#fbfbfb",
"background-weak": "#f0f0f0",
"background-strong": "#ffffff",
"background-stronger": "#ffffff",
"border-weak-base": "#d9d9d9",
"border-weak-hover": "#cccccc",
"border-weak-active": "#bfbfbf",
"border-weak-selected": "#4876d6",
"border-weak-disabled": "#e6e6e6",
"border-weak-focus": "#4876d6",
"border-base": "#c0c0c0",
"border-hover": "#b3b3b3",
"border-active": "#a6a6a6",
"border-selected": "#4876d6",
"border-disabled": "#d9d9d9",
"border-focus": "#4876d6",
"border-strong-base": "#90a7b2",
"border-strong-hover": "#7d9aa6",
"border-strong-active": "#6a8d9a",
"border-strong-selected": "#4876d6",
"border-strong-disabled": "#c0c0c0",
"border-strong-focus": "#4876d6",
"surface-diff-add-base": "#eaf8f6",
"surface-diff-delete-base": "#fbe9e9",
"surface-diff-hidden-base": "#e8f0fc",
"text-base": "#403f53",
"text-weak": "#7a8181",
"text-strong": "#1a1a1a",
"syntax-string": "#c96765",
"syntax-primitive": "#aa0982",
"syntax-property": "#4876d6",
"syntax-type": "#994cc3",
"syntax-constant": "#2aa298",
"syntax-info": "#4876d6",
"markdown-heading": "#4876d6",
"markdown-text": "#403f53",
"markdown-link": "#4876d6",
"markdown-link-text": "#2aa298",
"markdown-code": "#2aa298",
"markdown-block-quote": "#7a8181",
"markdown-emph": "#994cc3",
"markdown-strong": "#c96765",
"markdown-horizontal-rule": "#90a7b2",
"markdown-list-item": "#4876d6",
"markdown-list-enumeration": "#2aa298",
"markdown-image": "#4876d6",
"markdown-image-text": "#2aa298",
"markdown-code-block": "#403f53"
"syntax-keyword": "#994cc3"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#011627",
"ink": "#d6deeb",
"primary": "#82aaff",
"accent": "#f78c6c",
"success": "#c5e478",
"warning": "#ecc48d",
"error": "#ef5350",
"info": "#82aaff",
"interactive": "#82aaff",
"diffAdd": "#c5e478",
"diffDelete": "#ef5350"
},
"overrides": {
"background-base": "#011627",
"background-weak": "#0b253a",
"background-strong": "#001122",
"background-stronger": "#000c17",
"border-weak-base": "#1d3b53",
"border-weak-hover": "#234561",
"border-weak-active": "#2a506f",
"border-weak-selected": "#82aaff",
"border-weak-disabled": "#0f2132",
"border-weak-focus": "#82aaff",
"border-base": "#3a5a75",
"border-hover": "#456785",
"border-active": "#507494",
"border-selected": "#82aaff",
"border-disabled": "#1a3347",
"border-focus": "#82aaff",
"border-strong-base": "#5f7e97",
"border-strong-hover": "#6e8da6",
"border-strong-active": "#7d9cb5",
"border-strong-selected": "#82aaff",
"border-strong-disabled": "#2c4a63",
"border-strong-focus": "#82aaff",
"surface-diff-add-base": "#0a2e1a",
"surface-diff-delete-base": "#2d1b1b",
"surface-diff-hidden-base": "#0b253a",
"text-base": "#d6deeb",
"text-weak": "#5f7e97",
"text-strong": "#ffffff",
"syntax-comment": "#637777",
"syntax-keyword": "#c792ea",
"syntax-string": "#ecc48d",
"syntax-primitive": "#f78c6c",
"syntax-property": "#82aaff",
"syntax-type": "#c5e478",
"syntax-constant": "#7fdbca",
"syntax-info": "#82aaff",
"markdown-heading": "#82aaff",
"markdown-text": "#d6deeb",
"markdown-link": "#82aaff",
"markdown-link-text": "#7fdbca",
"markdown-code": "#c5e478",
"markdown-block-quote": "#5f7e97",
"markdown-emph": "#c792ea",
"markdown-strong": "#ecc48d",
"markdown-horizontal-rule": "#5f7e97",
"markdown-list-item": "#82aaff",
"markdown-list-enumeration": "#7fdbca",
"markdown-image": "#82aaff",
"markdown-image-text": "#7fdbca",
"markdown-code-block": "#d6deeb"
"syntax-primitive": "#f78c6c"
}
}
}

View File

@@ -3,129 +3,40 @@
"name": "Nord",
"id": "nord",
"light": {
"seeds": {
"palette": {
"neutral": "#eceff4",
"ink": "#2e3440",
"primary": "#5e81ac",
"accent": "#bf616a",
"success": "#8fbcbb",
"warning": "#d08770",
"error": "#bf616a",
"info": "#81a1c1",
"interactive": "#5e81ac",
"diffAdd": "#a3be8c",
"diffDelete": "#bf616a"
},
"overrides": {
"background-base": "#eceff4",
"background-weak": "#e4e8f0",
"background-strong": "#f1f3f8",
"background-stronger": "#f6f8fc",
"border-weak-base": "#d5dbe7",
"border-weak-hover": "#c9d0de",
"border-weak-active": "#bec5d4",
"border-weak-selected": "#b2bacc",
"border-weak-disabled": "#f0f3fa",
"border-weak-focus": "#b9bfd0",
"border-base": "#afb7cb",
"border-hover": "#a3abc1",
"border-active": "#979fb7",
"border-selected": "#8b94ad",
"border-disabled": "#e5e9f2",
"border-focus": "#9ca4ba",
"border-strong-base": "#757f97",
"border-strong-hover": "#69718a",
"border-strong-active": "#5d647d",
"border-strong-selected": "#525970",
"border-strong-disabled": "#c9cedc",
"border-strong-focus": "#636c84",
"surface-diff-add-base": "#e4f0e4",
"surface-diff-delete-base": "#f4e1e4",
"surface-diff-hidden-base": "#dfe6f2",
"text-base": "#2e3440",
"text-weak": "#4c566a",
"text-strong": "#1f2530",
"syntax-keyword": "#5e81ac",
"syntax-string": "#a3be8c",
"syntax-primitive": "#bf616a",
"syntax-property": "#5e81ac",
"syntax-type": "#d08770",
"syntax-constant": "#81a1c1",
"syntax-info": "#81a1c1",
"markdown-heading": "#5e81ac",
"markdown-text": "#2e3440",
"markdown-link": "#5e81ac",
"markdown-link-text": "#81a1c1",
"markdown-code": "#a3be8c",
"markdown-block-quote": "#d08770",
"markdown-emph": "#d08770",
"markdown-strong": "#bf616a",
"markdown-horizontal-rule": "#cbd3e1",
"markdown-list-item": "#5e81ac",
"markdown-list-enumeration": "#81a1c1",
"markdown-image": "#5e81ac",
"markdown-image-text": "#81a1c1",
"markdown-code-block": "#5e81ac"
"syntax-primitive": "#b48ead"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#2e3440",
"ink": "#e5e9f0",
"primary": "#88c0d0",
"accent": "#d57780",
"success": "#a3be8c",
"warning": "#d08770",
"error": "#bf616a",
"info": "#81a1c1",
"interactive": "#88c0d0",
"diffAdd": "#81a1c1",
"diffDelete": "#bf616a"
},
"overrides": {
"background-base": "#1f2430",
"background-weak": "#222938",
"background-strong": "#1c202a",
"background-stronger": "#181c24",
"border-weak-base": "#343a47",
"border-weak-hover": "#383f50",
"border-weak-active": "#3d4458",
"border-weak-selected": "#434a62",
"border-weak-disabled": "#151923",
"border-weak-focus": "#3f4359",
"border-base": "#4a5163",
"border-hover": "#515870",
"border-active": "#585f7c",
"border-selected": "#606889",
"border-disabled": "#1b202a",
"border-focus": "#545b78",
"border-strong-base": "#6a7492",
"border-strong-hover": "#747e9f",
"border-strong-active": "#7e88ac",
"border-strong-selected": "#8993b9",
"border-strong-disabled": "#232836",
"border-strong-focus": "#76819f",
"surface-diff-add-base": "#1f2e33",
"surface-diff-delete-base": "#2e212a",
"surface-diff-hidden-base": "#222b3a",
"text-base": "#e5e9f0",
"text-weak": "#a4adbf",
"text-strong": "#f8fafc",
"syntax-string": "#a3be8c",
"syntax-primitive": "#d57780",
"syntax-property": "#88c0d0",
"syntax-type": "#eac196",
"syntax-constant": "#81a1c1",
"syntax-info": "#81a1c1",
"markdown-heading": "#88c0d0",
"markdown-text": "#e5e9f0",
"markdown-link": "#88c0d0",
"markdown-link-text": "#81a1c1",
"markdown-code": "#a3be8c",
"markdown-block-quote": "#d08770",
"markdown-emph": "#d08770",
"markdown-strong": "#bf616a",
"markdown-horizontal-rule": "#2f384a",
"markdown-list-item": "#88c0d0",
"markdown-list-enumeration": "#81a1c1",
"markdown-image": "#88c0d0",
"markdown-image-text": "#81a1c1",
"markdown-code-block": "#cbd3e1"
"syntax-keyword": "#81a1c1",
"syntax-primitive": "#b48ead"
}
}
}

View File

@@ -3,9 +3,11 @@
"name": "OC-1",
"id": "oc-1",
"light": {
"seeds": {
"palette": {
"neutral": "#8e8b8b",
"ink": "#656363",
"primary": "#dcde8d",
"accent": "#fb4804",
"success": "#12c905",
"warning": "#ffdc17",
"error": "#fc533a",
@@ -13,260 +15,14 @@
"interactive": "#034cff",
"diffAdd": "#9ff29a",
"diffDelete": "#fc533a"
},
"overrides": {
"background-base": "#f8f7f7",
"background-weak": "var(--smoke-light-3)",
"background-strong": "var(--smoke-light-1)",
"background-stronger": "#fcfcfc",
"surface-base": "var(--smoke-light-alpha-2)",
"base": "var(--smoke-light-alpha-2)",
"surface-base-hover": "#0500000f",
"surface-base-active": "var(--smoke-light-alpha-3)",
"surface-base-interactive-active": "var(--cobalt-light-alpha-3)",
"base2": "var(--smoke-light-alpha-2)",
"base3": "var(--smoke-light-alpha-2)",
"surface-inset-base": "var(--smoke-light-alpha-2)",
"surface-inset-base-hover": "var(--smoke-light-alpha-3)",
"surface-inset-strong": "#1f000017",
"surface-inset-strong-hover": "#1f000017",
"surface-raised-base": "var(--smoke-light-alpha-2)",
"surface-float-base": "var(--smoke-dark-1)",
"surface-float-base-hover": "var(--smoke-dark-2)",
"surface-raised-base-hover": "var(--smoke-light-alpha-3)",
"surface-raised-base-active": "var(--smoke-light-alpha-4)",
"surface-raised-strong": "var(--smoke-light-1)",
"surface-raised-strong-hover": "var(--white)",
"surface-raised-stronger": "var(--white)",
"surface-raised-stronger-hover": "var(--white)",
"surface-weak": "var(--smoke-light-alpha-3)",
"surface-weaker": "var(--smoke-light-alpha-4)",
"surface-strong": "#ffffff",
"surface-raised-stronger-non-alpha": "var(--white)",
"surface-brand-base": "var(--yuzu-light-9)",
"surface-brand-hover": "var(--yuzu-light-10)",
"surface-interactive-base": "var(--cobalt-light-3)",
"surface-interactive-hover": "#E5F0FF",
"surface-interactive-weak": "var(--cobalt-light-2)",
"surface-interactive-weak-hover": "var(--cobalt-light-3)",
"surface-success-base": "var(--apple-light-3)",
"surface-success-weak": "var(--apple-light-2)",
"surface-success-strong": "var(--apple-light-9)",
"surface-warning-base": "var(--solaris-light-3)",
"surface-warning-weak": "var(--solaris-light-2)",
"surface-warning-strong": "var(--solaris-light-9)",
"surface-critical-base": "var(--ember-light-3)",
"surface-critical-weak": "var(--ember-light-2)",
"surface-critical-strong": "var(--ember-light-9)",
"surface-info-base": "var(--lilac-light-3)",
"surface-info-weak": "var(--lilac-light-2)",
"surface-info-strong": "var(--lilac-light-9)",
"surface-diff-unchanged-base": "#ffffff00",
"surface-diff-skip-base": "var(--smoke-light-2)",
"surface-diff-hidden-base": "var(--blue-light-3)",
"surface-diff-hidden-weak": "var(--blue-light-2)",
"surface-diff-hidden-weaker": "var(--blue-light-1)",
"surface-diff-hidden-strong": "var(--blue-light-5)",
"surface-diff-hidden-stronger": "var(--blue-light-9)",
"surface-diff-add-base": "#dafbe0",
"surface-diff-add-weak": "var(--mint-light-2)",
"surface-diff-add-weaker": "var(--mint-light-1)",
"surface-diff-add-strong": "var(--mint-light-5)",
"surface-diff-add-stronger": "var(--mint-light-9)",
"surface-diff-delete-base": "var(--ember-light-3)",
"surface-diff-delete-weak": "var(--ember-light-2)",
"surface-diff-delete-weaker": "var(--ember-light-1)",
"surface-diff-delete-strong": "var(--ember-light-6)",
"surface-diff-delete-stronger": "var(--ember-light-9)",
"input-base": "var(--smoke-light-1)",
"input-hover": "var(--smoke-light-2)",
"input-active": "var(--cobalt-light-1)",
"input-selected": "var(--cobalt-light-4)",
"input-focus": "var(--cobalt-light-1)",
"input-disabled": "var(--smoke-light-4)",
"text-base": "var(--smoke-light-11)",
"text-weak": "var(--smoke-light-9)",
"text-weaker": "var(--smoke-light-8)",
"text-strong": "var(--smoke-light-12)",
"text-invert-base": "var(--smoke-dark-alpha-11)",
"text-invert-weak": "var(--smoke-dark-alpha-9)",
"text-invert-weaker": "var(--smoke-dark-alpha-8)",
"text-invert-strong": "var(--smoke-dark-alpha-12)",
"text-interactive-base": "var(--cobalt-light-9)",
"text-on-brand-base": "var(--smoke-light-alpha-11)",
"text-on-interactive-base": "var(--smoke-light-1)",
"text-on-interactive-weak": "var(--smoke-dark-alpha-11)",
"text-on-success-base": "var(--apple-light-10)",
"text-on-critical-base": "var(--ember-light-10)",
"text-on-critical-weak": "var(--ember-light-8)",
"text-on-critical-strong": "var(--ember-light-12)",
"text-on-warning-base": "var(--smoke-dark-alpha-11)",
"text-on-info-base": "var(--smoke-dark-alpha-11)",
"text-diff-add-base": "var(--mint-light-11)",
"text-diff-delete-base": "var(--ember-light-10)",
"text-diff-delete-strong": "var(--ember-light-12)",
"text-diff-add-strong": "var(--mint-light-12)",
"text-on-info-weak": "var(--smoke-dark-alpha-9)",
"text-on-info-strong": "var(--smoke-dark-alpha-12)",
"text-on-warning-weak": "var(--smoke-dark-alpha-9)",
"text-on-warning-strong": "var(--smoke-dark-alpha-12)",
"text-on-success-weak": "var(--apple-light-6)",
"text-on-success-strong": "var(--apple-light-12)",
"text-on-brand-weak": "var(--smoke-light-alpha-9)",
"text-on-brand-weaker": "var(--smoke-light-alpha-8)",
"text-on-brand-strong": "var(--smoke-light-alpha-12)",
"button-primary-base": "var(--smoke-light-12)",
"button-secondary-base": "#fdfcfc",
"button-secondary-hover": "#faf9f9",
"border-base": "var(--smoke-light-alpha-7)",
"border-hover": "var(--smoke-light-alpha-8)",
"border-active": "var(--smoke-light-alpha-9)",
"border-selected": "var(--cobalt-light-alpha-9)",
"border-disabled": "var(--smoke-light-alpha-8)",
"border-focus": "var(--smoke-light-alpha-9)",
"border-weak-base": "var(--smoke-light-alpha-5)",
"border-strong-base": "var(--smoke-light-alpha-7)",
"border-strong-hover": "var(--smoke-light-alpha-8)",
"border-strong-active": "var(--smoke-light-alpha-7)",
"border-strong-selected": "var(--cobalt-light-alpha-6)",
"border-strong-disabled": "var(--smoke-light-alpha-6)",
"border-strong-focus": "var(--smoke-light-alpha-7)",
"border-weak-hover": "var(--smoke-light-alpha-6)",
"border-weak-active": "var(--smoke-light-alpha-7)",
"border-weak-selected": "var(--cobalt-light-alpha-5)",
"border-weak-disabled": "var(--smoke-light-alpha-6)",
"border-weak-focus": "var(--smoke-light-alpha-7)",
"border-interactive-base": "var(--cobalt-light-7)",
"border-interactive-hover": "var(--cobalt-light-8)",
"border-interactive-active": "var(--cobalt-light-9)",
"border-interactive-selected": "var(--cobalt-light-9)",
"border-interactive-disabled": "var(--smoke-light-8)",
"border-interactive-focus": "var(--cobalt-light-9)",
"border-success-base": "var(--apple-light-6)",
"border-success-hover": "var(--apple-light-7)",
"border-success-selected": "var(--apple-light-9)",
"border-warning-base": "var(--solaris-light-6)",
"border-warning-hover": "var(--solaris-light-7)",
"border-warning-selected": "var(--solaris-light-9)",
"border-critical-base": "var(--ember-light-6)",
"border-critical-hover": "var(--ember-light-7)",
"border-critical-selected": "var(--ember-light-9)",
"border-info-base": "var(--lilac-light-6)",
"border-info-hover": "var(--lilac-light-7)",
"border-info-selected": "var(--lilac-light-9)",
"icon-base": "var(--smoke-light-9)",
"icon-hover": "var(--smoke-light-11)",
"icon-active": "var(--smoke-light-12)",
"icon-selected": "var(--smoke-light-12)",
"icon-disabled": "var(--smoke-light-8)",
"icon-focus": "var(--smoke-light-12)",
"icon-invert-base": "#ffffff",
"icon-weak-base": "var(--smoke-light-7)",
"icon-weak-hover": "var(--smoke-light-8)",
"icon-weak-active": "var(--smoke-light-9)",
"icon-weak-selected": "var(--smoke-light-10)",
"icon-weak-disabled": "var(--smoke-light-6)",
"icon-weak-focus": "var(--smoke-light-9)",
"icon-strong-base": "var(--smoke-light-12)",
"icon-strong-hover": "#151313",
"icon-strong-active": "#020202",
"icon-strong-selected": "#020202",
"icon-strong-disabled": "var(--smoke-light-8)",
"icon-strong-focus": "#020202",
"icon-brand-base": "var(--smoke-light-12)",
"icon-interactive-base": "var(--cobalt-light-9)",
"icon-success-base": "var(--apple-light-7)",
"icon-success-hover": "var(--apple-light-8)",
"icon-success-active": "var(--apple-light-11)",
"icon-warning-base": "var(--amber-light-7)",
"icon-warning-hover": "var(--amber-light-8)",
"icon-warning-active": "var(--amber-light-11)",
"icon-critical-base": "var(--ember-light-10)",
"icon-critical-hover": "var(--ember-light-11)",
"icon-critical-active": "var(--ember-light-12)",
"icon-info-base": "var(--lilac-light-7)",
"icon-info-hover": "var(--lilac-light-8)",
"icon-info-active": "var(--lilac-light-11)",
"icon-on-brand-base": "var(--smoke-light-alpha-11)",
"icon-on-brand-hover": "var(--smoke-light-alpha-12)",
"icon-on-brand-selected": "var(--smoke-light-alpha-12)",
"icon-on-interactive-base": "var(--smoke-light-1)",
"icon-agent-plan-base": "var(--purple-light-9)",
"icon-agent-docs-base": "var(--amber-light-9)",
"icon-agent-ask-base": "var(--cyan-light-9)",
"icon-agent-build-base": "var(--cobalt-light-9)",
"icon-on-success-base": "var(--apple-light-alpha-9)",
"icon-on-success-hover": "var(--apple-light-alpha-10)",
"icon-on-success-selected": "var(--apple-light-alpha-11)",
"icon-on-warning-base": "var(--amber-lightalpha-9)",
"icon-on-warning-hover": "var(--amber-lightalpha-10)",
"icon-on-warning-selected": "var(--amber-lightalpha-11)",
"icon-on-critical-base": "var(--ember-light-alpha-9)",
"icon-on-critical-hover": "var(--ember-light-alpha-10)",
"icon-on-critical-selected": "var(--ember-light-alpha-11)",
"icon-on-info-base": "var(--lilac-light-9)",
"icon-on-info-hover": "var(--lilac-light-alpha-10)",
"icon-on-info-selected": "var(--lilac-light-alpha-11)",
"icon-diff-add-base": "var(--mint-light-11)",
"icon-diff-add-hover": "var(--mint-light-12)",
"icon-diff-add-active": "var(--mint-light-12)",
"icon-diff-delete-base": "var(--ember-light-10)",
"icon-diff-delete-hover": "var(--ember-light-11)",
"syntax-comment": "var(--text-weak)",
"syntax-regexp": "var(--text-base)",
"syntax-string": "#006656",
"syntax-keyword": "var(--text-weak)",
"syntax-primitive": "#fb4804",
"syntax-operator": "var(--text-base)",
"syntax-variable": "var(--text-strong)",
"syntax-property": "#ed6dc8",
"syntax-type": "#596600",
"syntax-constant": "#007b80",
"syntax-punctuation": "var(--text-base)",
"syntax-object": "var(--text-strong)",
"syntax-success": "var(--apple-light-10)",
"syntax-warning": "var(--amber-light-10)",
"syntax-critical": "var(--ember-light-10)",
"syntax-info": "#0092a8",
"syntax-diff-add": "var(--mint-light-11)",
"syntax-diff-delete": "var(--ember-light-11)",
"syntax-diff-unknown": "#ff0000",
"markdown-heading": "#d68c27",
"markdown-text": "#1a1a1a",
"markdown-link": "#3b7dd8",
"markdown-link-text": "#318795",
"markdown-code": "#3d9a57",
"markdown-block-quote": "#b0851f",
"markdown-emph": "#b0851f",
"markdown-strong": "#d68c27",
"markdown-horizontal-rule": "#8a8a8a",
"markdown-list-item": "#3b7dd8",
"markdown-list-enumeration": "#318795",
"markdown-image": "#3b7dd8",
"markdown-image-text": "#318795",
"markdown-code-block": "#1a1a1a",
"border-color": "#ffffff",
"border-weaker-base": "var(--smoke-light-alpha-3)",
"button-ghost-hover": "var(--smoke-light-alpha-2)",
"button-ghost-hover2": "var(--smoke-light-alpha-3)",
"avatar-background-pink": "#feeef8",
"avatar-background-mint": "#e1fbf4",
"avatar-background-orange": "#fff1e7",
"avatar-background-purple": "#f9f1fe",
"avatar-background-cyan": "#e7f9fb",
"avatar-background-lime": "#eefadc",
"avatar-text-pink": "#cd1d8d",
"avatar-text-mint": "#147d6f",
"avatar-text-orange": "#ed5f00",
"avatar-text-purple": "#8445bc",
"avatar-text-cyan": "#0894b3",
"avatar-text-lime": "#5d770d"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#716c6b",
"ink": "#b7b1b1",
"primary": "#fab283",
"accent": "#ffba92",
"success": "#12c905",
"warning": "#fcd53a",
"error": "#fc533a",
@@ -274,254 +30,6 @@
"interactive": "#034cff",
"diffAdd": "#c8ffc4",
"diffDelete": "#fc533a"
},
"overrides": {
"background-base": "var(--smoke-dark-1)",
"background-weak": "#1c1717",
"background-strong": "#151313",
"background-stronger": "#191515",
"surface-base": "var(--smoke-dark-alpha-2)",
"base": "var(--smoke-dark-alpha-2)",
"surface-base-hover": "#e0b7b716",
"surface-base-active": "var(--smoke-dark-alpha-3)",
"surface-base-interactive-active": "var(--cobalt-dark-alpha-2)",
"base2": "var(--smoke-dark-alpha-2)",
"base3": "var(--smoke-dark-alpha-2)",
"surface-inset-base": "#0e0b0b7f",
"surface-inset-base-hover": "#0e0b0b7f",
"surface-inset-strong": "#060505cc",
"surface-inset-strong-hover": "#060505cc",
"surface-raised-base": "var(--smoke-dark-alpha-3)",
"surface-float-base": "var(--smoke-dark-1)",
"surface-float-base-hover": "var(--smoke-dark-2)",
"surface-raised-base-hover": "var(--smoke-dark-alpha-4)",
"surface-raised-base-active": "var(--smoke-dark-alpha-5)",
"surface-raised-strong": "var(--smoke-dark-alpha-4)",
"surface-raised-strong-hover": "var(--smoke-dark-alpha-6)",
"surface-raised-stronger": "var(--smoke-dark-alpha-6)",
"surface-raised-stronger-hover": "var(--smoke-dark-alpha-7)",
"surface-weak": "var(--smoke-dark-alpha-4)",
"surface-weaker": "var(--smoke-dark-alpha-5)",
"surface-strong": "var(--smoke-dark-alpha-7)",
"surface-raised-stronger-non-alpha": "var(--smoke-dark-3)",
"surface-brand-base": "var(--yuzu-light-9)",
"surface-brand-hover": "var(--yuzu-light-10)",
"surface-interactive-base": "var(--cobalt-dark-3)",
"surface-interactive-hover": "#0A1D4D",
"surface-interactive-weak": "var(--cobalt-dark-2)",
"surface-interactive-weak-hover": "var(--cobalt-light-3)",
"surface-success-base": "var(--apple-dark-3)",
"surface-success-weak": "var(--apple-dark-2)",
"surface-success-strong": "var(--apple-dark-9)",
"surface-warning-base": "var(--solaris-light-3)",
"surface-warning-weak": "var(--solaris-light-2)",
"surface-warning-strong": "var(--solaris-light-9)",
"surface-critical-base": "var(--ember-dark-3)",
"surface-critical-weak": "var(--ember-dark-2)",
"surface-critical-strong": "var(--ember-dark-9)",
"surface-info-base": "var(--lilac-light-3)",
"surface-info-weak": "var(--lilac-light-2)",
"surface-info-strong": "var(--lilac-light-9)",
"surface-diff-unchanged-base": "var(--smoke-dark-1)",
"surface-diff-skip-base": "var(--smoke-dark-alpha-1)",
"surface-diff-hidden-base": "var(--blue-dark-2)",
"surface-diff-hidden-weak": "var(--blue-dark-1)",
"surface-diff-hidden-weaker": "var(--blue-dark-3)",
"surface-diff-hidden-strong": "var(--blue-dark-5)",
"surface-diff-hidden-stronger": "var(--blue-dark-11)",
"surface-diff-add-base": "var(--mint-dark-3)",
"surface-diff-add-weak": "var(--mint-dark-4)",
"surface-diff-add-weaker": "var(--mint-dark-3)",
"surface-diff-add-strong": "var(--mint-dark-5)",
"surface-diff-add-stronger": "var(--mint-dark-11)",
"surface-diff-delete-base": "var(--ember-dark-3)",
"surface-diff-delete-weak": "var(--ember-dark-4)",
"surface-diff-delete-weaker": "var(--ember-dark-3)",
"surface-diff-delete-strong": "var(--ember-dark-5)",
"surface-diff-delete-stronger": "var(--ember-dark-11)",
"input-base": "var(--smoke-dark-2)",
"input-hover": "var(--smoke-dark-2)",
"input-active": "var(--cobalt-dark-1)",
"input-selected": "var(--cobalt-dark-2)",
"input-focus": "var(--cobalt-dark-1)",
"input-disabled": "var(--smoke-dark-4)",
"text-base": "var(--smoke-dark-alpha-11)",
"text-weak": "var(--smoke-dark-alpha-9)",
"text-weaker": "var(--smoke-dark-alpha-8)",
"text-strong": "var(--smoke-dark-alpha-12)",
"text-invert-base": "var(--smoke-dark-alpha-11)",
"text-invert-weak": "var(--smoke-dark-alpha-9)",
"text-invert-weaker": "var(--smoke-dark-alpha-8)",
"text-invert-strong": "var(--smoke-dark-alpha-12)",
"text-interactive-base": "var(--cobalt-dark-11)",
"text-on-brand-base": "var(--smoke-dark-alpha-11)",
"text-on-interactive-base": "var(--smoke-dark-12)",
"text-on-interactive-weak": "var(--smoke-dark-alpha-11)",
"text-on-success-base": "var(--apple-dark-9)",
"text-on-critical-base": "var(--ember-dark-9)",
"text-on-critical-weak": "var(--ember-dark-8)",
"text-on-critical-strong": "var(--ember-dark-12)",
"text-on-warning-base": "var(--smoke-dark-alpha-11)",
"text-on-info-base": "var(--smoke-dark-alpha-11)",
"text-diff-add-base": "var(--mint-dark-11)",
"text-diff-delete-base": "var(--ember-dark-9)",
"text-diff-delete-strong": "var(--ember-dark-12)",
"text-diff-add-strong": "var(--mint-dark-8)",
"text-on-info-weak": "var(--smoke-dark-alpha-9)",
"text-on-info-strong": "var(--smoke-dark-alpha-12)",
"text-on-warning-weak": "var(--smoke-dark-alpha-9)",
"text-on-warning-strong": "var(--smoke-dark-alpha-12)",
"text-on-success-weak": "var(--apple-dark-8)",
"text-on-success-strong": "var(--apple-dark-12)",
"text-on-brand-weak": "var(--smoke-dark-alpha-9)",
"text-on-brand-weaker": "var(--smoke-dark-alpha-8)",
"text-on-brand-strong": "var(--smoke-dark-alpha-12)",
"button-primary-base": "var(--smoke-dark-12)",
"button-secondary-base": "#231f1f",
"button-secondary-hover": "#2a2727",
"border-base": "var(--smoke-dark-alpha-7)",
"border-hover": "var(--smoke-dark-alpha-8)",
"border-active": "var(--smoke-dark-alpha-9)",
"border-selected": "var(--cobalt-dark-alpha-11)",
"border-disabled": "var(--smoke-dark-alpha-8)",
"border-focus": "var(--smoke-dark-alpha-9)",
"border-weak-base": "var(--smoke-dark-alpha-6)",
"border-strong-base": "var(--smoke-dark-alpha-8)",
"border-strong-hover": "var(--smoke-dark-alpha-7)",
"border-strong-active": "var(--smoke-dark-alpha-8)",
"border-strong-selected": "var(--cobalt-dark-alpha-6)",
"border-strong-disabled": "var(--smoke-dark-alpha-6)",
"border-strong-focus": "var(--smoke-dark-alpha-8)",
"border-weak-hover": "var(--smoke-dark-alpha-7)",
"border-weak-active": "var(--smoke-dark-alpha-8)",
"border-weak-selected": "var(--cobalt-dark-alpha-6)",
"border-weak-disabled": "var(--smoke-dark-alpha-6)",
"border-weak-focus": "var(--smoke-dark-alpha-8)",
"border-interactive-base": "var(--cobalt-light-7)",
"border-interactive-hover": "var(--cobalt-light-8)",
"border-interactive-active": "var(--cobalt-light-9)",
"border-interactive-selected": "var(--cobalt-light-9)",
"border-interactive-disabled": "var(--smoke-light-8)",
"border-interactive-focus": "var(--cobalt-light-9)",
"border-success-base": "var(--apple-light-6)",
"border-success-hover": "var(--apple-light-7)",
"border-success-selected": "var(--apple-light-9)",
"border-warning-base": "var(--solaris-light-6)",
"border-warning-hover": "var(--solaris-light-7)",
"border-warning-selected": "var(--solaris-light-9)",
"border-critical-base": "var(--ember-dark-5)",
"border-critical-hover": "var(--ember-dark-7)",
"border-critical-selected": "var(--ember-dark-9)",
"border-info-base": "var(--lilac-light-6)",
"border-info-hover": "var(--lilac-light-7)",
"border-info-selected": "var(--lilac-light-9)",
"icon-base": "var(--smoke-dark-9)",
"icon-hover": "var(--smoke-dark-10)",
"icon-active": "var(--smoke-dark-11)",
"icon-selected": "var(--smoke-dark-12)",
"icon-disabled": "var(--smoke-dark-7)",
"icon-focus": "var(--smoke-dark-12)",
"icon-invert-base": "var(--smoke-dark-1)",
"icon-weak-base": "var(--smoke-dark-6)",
"icon-weak-hover": "var(--smoke-light-7)",
"icon-weak-active": "var(--smoke-light-8)",
"icon-weak-selected": "var(--smoke-light-9)",
"icon-weak-disabled": "var(--smoke-light-4)",
"icon-weak-focus": "var(--smoke-light-9)",
"icon-strong-base": "var(--smoke-dark-12)",
"icon-strong-hover": "#f6f3f3",
"icon-strong-active": "#fcfcfc",
"icon-strong-selected": "#fdfcfc",
"icon-strong-disabled": "var(--smoke-dark-8)",
"icon-strong-focus": "#fdfcfc",
"icon-brand-base": "var(--white)",
"icon-interactive-base": "var(--cobalt-dark-11)",
"icon-success-base": "var(--apple-dark-9)",
"icon-success-hover": "var(--apple-dark-10)",
"icon-success-active": "var(--apple-dark-11)",
"icon-warning-base": "var(--amber-dark-9)",
"icon-warning-hover": "var(--amber-dark-8)",
"icon-warning-active": "var(--amber-dark-11)",
"icon-critical-base": "var(--ember-dark-9)",
"icon-critical-hover": "var(--ember-dark-11)",
"icon-critical-active": "var(--ember-dark-12)",
"icon-info-base": "var(--lilac-dark-7)",
"icon-info-hover": "var(--lilac-dark-8)",
"icon-info-active": "var(--lilac-dark-11)",
"icon-on-brand-base": "var(--smoke-light-alpha-11)",
"icon-on-brand-hover": "var(--smoke-light-alpha-12)",
"icon-on-brand-selected": "var(--smoke-light-alpha-12)",
"icon-on-interactive-base": "var(--smoke-dark-12)",
"icon-agent-plan-base": "var(--purple-dark-9)",
"icon-agent-docs-base": "var(--amber-dark-9)",
"icon-agent-ask-base": "var(--cyan-dark-9)",
"icon-agent-build-base": "var(--cobalt-dark-11)",
"icon-on-success-base": "var(--apple-dark-alpha-9)",
"icon-on-success-hover": "var(--apple-dark-alpha-10)",
"icon-on-success-selected": "var(--apple-dark-alpha-11)",
"icon-on-warning-base": "var(--amber-darkalpha-9)",
"icon-on-warning-hover": "var(--amber-darkalpha-10)",
"icon-on-warning-selected": "var(--amber-darkalpha-11)",
"icon-on-critical-base": "var(--ember-dark-alpha-9)",
"icon-on-critical-hover": "var(--ember-dark-alpha-10)",
"icon-on-critical-selected": "var(--ember-dark-alpha-11)",
"icon-on-info-base": "var(--lilac-dark-9)",
"icon-on-info-hover": "var(--lilac-dark-alpha-10)",
"icon-on-info-selected": "var(--lilac-dark-alpha-11)",
"icon-diff-add-base": "var(--mint-dark-11)",
"icon-diff-add-hover": "var(--mint-dark-10)",
"icon-diff-add-active": "var(--mint-dark-11)",
"icon-diff-delete-base": "var(--ember-dark-9)",
"icon-diff-delete-hover": "var(--ember-dark-10)",
"syntax-comment": "var(--text-weak)",
"syntax-regexp": "var(--text-base)",
"syntax-string": "#00ceb9",
"syntax-keyword": "var(--text-weak)",
"syntax-primitive": "#ffba92",
"syntax-operator": "var(--text-weak)",
"syntax-variable": "var(--text-strong)",
"syntax-property": "#ff9ae2",
"syntax-type": "#ecf58c",
"syntax-constant": "#93e9f6",
"syntax-punctuation": "var(--text-weak)",
"syntax-object": "var(--text-strong)",
"syntax-success": "var(--apple-dark-10)",
"syntax-warning": "var(--amber-dark-10)",
"syntax-critical": "var(--ember-dark-10)",
"syntax-info": "#93e9f6",
"syntax-diff-add": "var(--mint-dark-11)",
"syntax-diff-delete": "var(--ember-dark-11)",
"syntax-diff-unknown": "#ff0000",
"markdown-heading": "#9d7cd8",
"markdown-text": "#eeeeee",
"markdown-link": "#fab283",
"markdown-link-text": "#56b6c2",
"markdown-code": "#7fd88f",
"markdown-block-quote": "#e5c07b",
"markdown-emph": "#e5c07b",
"markdown-strong": "#f5a742",
"markdown-horizontal-rule": "#808080",
"markdown-list-item": "#fab283",
"markdown-list-enumeration": "#56b6c2",
"markdown-image": "#fab283",
"markdown-image-text": "#56b6c2",
"markdown-code-block": "#eeeeee",
"border-color": "#ffffff",
"border-weaker-base": "var(--smoke-dark-alpha-3)",
"button-ghost-hover": "var(--smoke-dark-alpha-2)",
"button-ghost-hover2": "var(--smoke-dark-alpha-3)",
"avatar-background-pink": "#501b3f",
"avatar-background-mint": "#033a34",
"avatar-background-orange": "#5f2a06",
"avatar-background-purple": "#432155",
"avatar-background-cyan": "#0f3058",
"avatar-background-lime": "#2b3711",
"avatar-text-pink": "#e34ba9",
"avatar-text-mint": "#95f3d9",
"avatar-text-orange": "#ff802b",
"avatar-text-purple": "#9d5bd2",
"avatar-text-cyan": "#369eff",
"avatar-text-lime": "#c4f042"
}
}
}

View File

@@ -3,7 +3,7 @@
"name": "OC-2",
"id": "oc-2",
"light": {
"seeds": {
"palette": {
"neutral": "#8f8f8f",
"primary": "#dcde8d",
"success": "#12c905",
@@ -13,258 +13,10 @@
"interactive": "#034cff",
"diffAdd": "#9ff29a",
"diffDelete": "#fc533a"
},
"overrides": {
"background-base": "#f8f8f8",
"background-weak": "#f3f3f3",
"background-strong": "#fcfcfc",
"background-stronger": "#fcfcfc",
"surface-base": "#00000008",
"base": "#00000008",
"surface-base-hover": "#0000000f",
"surface-base-active": "#0000000d",
"surface-base-interactive-active": "var(--cobalt-light-alpha-3)",
"base2": "#00000008",
"base3": "#00000008",
"surface-inset-base": "#00000008",
"surface-inset-base-hover": "#0000000d",
"surface-inset-strong": "#00000017",
"surface-inset-strong-hover": "#00000017",
"surface-raised-base": "#00000008",
"surface-float-base": "#161616",
"surface-float-base-hover": "#1c1c1c",
"surface-raised-base-hover": "#0000000d",
"surface-raised-base-active": "#00000017",
"surface-raised-strong": "#fcfcfc",
"surface-raised-strong-hover": "var(--white)",
"surface-raised-stronger": "var(--white)",
"surface-raised-stronger-hover": "var(--white)",
"surface-weak": "#0000000d",
"surface-weaker": "#00000012",
"surface-strong": "#ffffff",
"surface-raised-stronger-non-alpha": "var(--white)",
"surface-brand-base": "var(--yuzu-light-9)",
"surface-brand-hover": "var(--yuzu-light-10)",
"surface-interactive-base": "var(--cobalt-light-3)",
"surface-interactive-hover": "#E5F0FF",
"surface-interactive-weak": "var(--cobalt-light-2)",
"surface-interactive-weak-hover": "var(--cobalt-light-3)",
"surface-success-base": "var(--apple-light-3)",
"surface-success-weak": "var(--apple-light-2)",
"surface-success-strong": "var(--apple-light-9)",
"surface-warning-base": "var(--solaris-light-3)",
"surface-warning-weak": "var(--solaris-light-2)",
"surface-warning-strong": "var(--solaris-light-9)",
"surface-critical-base": "var(--ember-light-3)",
"surface-critical-weak": "var(--ember-light-2)",
"surface-critical-strong": "var(--ember-light-9)",
"surface-info-base": "var(--lilac-light-3)",
"surface-info-weak": "var(--lilac-light-2)",
"surface-info-strong": "var(--lilac-light-9)",
"surface-diff-unchanged-base": "#ffffff00",
"surface-diff-skip-base": "#f8f8f8",
"surface-diff-hidden-base": "var(--blue-light-3)",
"surface-diff-hidden-weak": "var(--blue-light-2)",
"surface-diff-hidden-weaker": "var(--blue-light-1)",
"surface-diff-hidden-strong": "var(--blue-light-5)",
"surface-diff-hidden-stronger": "var(--blue-light-9)",
"surface-diff-add-base": "#dafbe0",
"surface-diff-add-weak": "var(--mint-light-2)",
"surface-diff-add-weaker": "var(--mint-light-1)",
"surface-diff-add-strong": "var(--mint-light-5)",
"surface-diff-add-stronger": "var(--mint-light-9)",
"surface-diff-delete-base": "var(--ember-light-3)",
"surface-diff-delete-weak": "var(--ember-light-2)",
"surface-diff-delete-weaker": "var(--ember-light-1)",
"surface-diff-delete-strong": "var(--ember-light-6)",
"surface-diff-delete-stronger": "var(--ember-light-9)",
"input-base": "#fcfcfc",
"input-hover": "#f8f8f8",
"input-active": "var(--cobalt-light-1)",
"input-selected": "var(--cobalt-light-4)",
"input-focus": "var(--cobalt-light-1)",
"input-disabled": "#ededed",
"text-base": "#6f6f6f",
"text-weak": "#8f8f8f",
"text-weaker": "#c7c7c7",
"text-strong": "#171717",
"text-invert-base": "#ffffff96",
"text-invert-weak": "#ffffff63",
"text-invert-weaker": "#ffffff40",
"text-invert-strong": "#ffffffeb",
"text-interactive-base": "var(--cobalt-light-9)",
"text-on-brand-base": "#0000008f",
"text-on-interactive-base": "#fcfcfc",
"text-on-interactive-weak": "#ffffff96",
"text-on-success-base": "var(--apple-light-10)",
"text-on-critical-base": "var(--ember-light-10)",
"text-on-critical-weak": "var(--ember-light-8)",
"text-on-critical-strong": "var(--ember-light-12)",
"text-on-warning-base": "#ffffff96",
"text-on-info-base": "#ffffff96",
"text-diff-add-base": "var(--mint-light-11)",
"text-diff-delete-base": "var(--ember-light-10)",
"text-diff-delete-strong": "var(--ember-light-12)",
"text-diff-add-strong": "var(--mint-light-12)",
"text-on-info-weak": "#ffffff63",
"text-on-info-strong": "#ffffffeb",
"text-on-warning-weak": "#ffffff63",
"text-on-warning-strong": "#ffffffeb",
"text-on-success-weak": "var(--apple-light-6)",
"text-on-success-strong": "var(--apple-light-12)",
"text-on-brand-weak": "#00000070",
"text-on-brand-weaker": "#00000038",
"text-on-brand-strong": "#000000e8",
"button-primary-base": "#171717",
"button-secondary-base": "#fcfcfc",
"button-secondary-hover": "FFFFFF0A",
"border-base": "#00000024",
"border-hover": "#00000038",
"border-active": "#00000070",
"border-selected": "var(--cobalt-light-alpha-9)",
"border-disabled": "#00000038",
"border-focus": "#00000070",
"border-weak-base": "#e5e5e5",
"border-strong-base": "#00000024",
"border-strong-hover": "#00000038",
"border-strong-active": "#00000024",
"border-strong-selected": "var(--cobalt-light-alpha-6)",
"border-strong-disabled": "#0000001c",
"border-strong-focus": "#00000024",
"border-weak-hover": "#0000001c",
"border-weak-active": "#00000024",
"border-weak-selected": "var(--cobalt-light-alpha-5)",
"border-weak-disabled": "#0000001c",
"border-weak-focus": "#00000024",
"border-interactive-base": "var(--cobalt-light-7)",
"border-interactive-hover": "var(--cobalt-light-8)",
"border-interactive-active": "var(--cobalt-light-9)",
"border-interactive-selected": "var(--cobalt-light-9)",
"border-interactive-disabled": "#c7c7c7",
"border-interactive-focus": "var(--cobalt-light-9)",
"border-success-base": "var(--apple-light-6)",
"border-success-hover": "var(--apple-light-7)",
"border-success-selected": "var(--apple-light-9)",
"border-warning-base": "var(--solaris-light-6)",
"border-warning-hover": "var(--solaris-light-7)",
"border-warning-selected": "var(--solaris-light-9)",
"border-critical-base": "var(--ember-light-6)",
"border-critical-hover": "var(--ember-light-7)",
"border-critical-selected": "var(--ember-light-9)",
"border-info-base": "var(--lilac-light-6)",
"border-info-hover": "var(--lilac-light-7)",
"border-info-selected": "var(--lilac-light-9)",
"icon-base": "#8f8f8f",
"icon-hover": "#6f6f6f",
"icon-active": "#171717",
"icon-selected": "#171717",
"icon-disabled": "#c7c7c7",
"icon-focus": "#171717",
"icon-invert-base": "#ffffff",
"icon-weak-base": "#dbdbdb",
"icon-weak-hover": "#c7c7c7",
"icon-weak-active": "#8f8f8f",
"icon-weak-selected": "#858585",
"icon-weak-disabled": "#e2e2e2",
"icon-weak-focus": "#8f8f8f",
"icon-strong-base": "#171717",
"icon-strong-hover": "#151515",
"icon-strong-active": "#020202",
"icon-strong-selected": "#020202",
"icon-strong-disabled": "#e2e2e2",
"icon-strong-focus": "#020202",
"icon-brand-base": "#171717",
"icon-interactive-base": "var(--cobalt-light-9)",
"icon-success-base": "var(--apple-light-7)",
"icon-success-hover": "var(--apple-light-8)",
"icon-success-active": "var(--apple-light-11)",
"icon-warning-base": "var(--amber-light-7)",
"icon-warning-hover": "var(--amber-light-8)",
"icon-warning-active": "var(--amber-light-11)",
"icon-critical-base": "var(--ember-light-10)",
"icon-critical-hover": "var(--ember-light-11)",
"icon-critical-active": "var(--ember-light-12)",
"icon-info-base": "var(--lilac-light-7)",
"icon-info-hover": "var(--lilac-light-8)",
"icon-info-active": "var(--lilac-light-11)",
"icon-on-brand-base": "#0000008f",
"icon-on-brand-hover": "#000000e8",
"icon-on-brand-selected": "#000000e8",
"icon-on-interactive-base": "#fcfcfc",
"icon-agent-plan-base": "var(--purple-light-9)",
"icon-agent-docs-base": "var(--amber-light-9)",
"icon-agent-ask-base": "var(--cyan-light-9)",
"icon-agent-build-base": "var(--cobalt-light-9)",
"icon-on-success-base": "var(--apple-light-alpha-9)",
"icon-on-success-hover": "var(--apple-light-alpha-10)",
"icon-on-success-selected": "var(--apple-light-alpha-11)",
"icon-on-warning-base": "var(--amber-lightalpha-9)",
"icon-on-warning-hover": "var(--amber-lightalpha-10)",
"icon-on-warning-selected": "var(--amber-lightalpha-11)",
"icon-on-critical-base": "var(--ember-light-alpha-9)",
"icon-on-critical-hover": "var(--ember-light-alpha-10)",
"icon-on-critical-selected": "var(--ember-light-alpha-11)",
"icon-on-info-base": "var(--lilac-light-9)",
"icon-on-info-hover": "var(--lilac-light-alpha-10)",
"icon-on-info-selected": "var(--lilac-light-alpha-11)",
"icon-diff-add-base": "var(--mint-light-11)",
"icon-diff-add-hover": "var(--mint-light-12)",
"icon-diff-add-active": "var(--mint-light-12)",
"icon-diff-delete-base": "var(--ember-light-10)",
"icon-diff-delete-hover": "var(--ember-light-11)",
"syntax-comment": "var(--text-weak)",
"syntax-regexp": "var(--text-base)",
"syntax-string": "#006656",
"syntax-keyword": "var(--text-weak)",
"syntax-primitive": "#fb4804",
"syntax-operator": "var(--text-base)",
"syntax-variable": "var(--text-strong)",
"syntax-property": "#ed6dc8",
"syntax-type": "#596600",
"syntax-constant": "#007b80",
"syntax-punctuation": "var(--text-base)",
"syntax-object": "var(--text-strong)",
"syntax-success": "var(--apple-light-10)",
"syntax-warning": "var(--amber-light-10)",
"syntax-critical": "var(--ember-light-10)",
"syntax-info": "#0092a8",
"syntax-diff-add": "var(--mint-light-11)",
"syntax-diff-delete": "var(--ember-light-11)",
"syntax-diff-unknown": "#ff0000",
"markdown-heading": "#d68c27",
"markdown-text": "#1a1a1a",
"markdown-link": "#3b7dd8",
"markdown-link-text": "#318795",
"markdown-code": "#3d9a57",
"markdown-block-quote": "#b0851f",
"markdown-emph": "#b0851f",
"markdown-strong": "#d68c27",
"markdown-horizontal-rule": "#8a8a8a",
"markdown-list-item": "#3b7dd8",
"markdown-list-enumeration": "#318795",
"markdown-image": "#3b7dd8",
"markdown-image-text": "#318795",
"markdown-code-block": "#1a1a1a",
"border-color": "#ffffff",
"border-weaker-base": "#efefef",
"button-ghost-hover": "#00000008",
"button-ghost-hover2": "#0000000d",
"avatar-background-pink": "#feeef8",
"avatar-background-mint": "#e1fbf4",
"avatar-background-orange": "#fff1e7",
"avatar-background-purple": "#f9f1fe",
"avatar-background-cyan": "#e7f9fb",
"avatar-background-lime": "#eefadc",
"avatar-text-pink": "#cd1d8d",
"avatar-text-mint": "#147d6f",
"avatar-text-orange": "#ed5f00",
"avatar-text-purple": "#8445bc",
"avatar-text-cyan": "#0894b3",
"avatar-text-lime": "#5d770d"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#707070",
"primary": "#fab283",
"success": "#12c905",
@@ -274,249 +26,6 @@
"interactive": "#034cff",
"diffAdd": "#c8ffc4",
"diffDelete": "#fc533a"
},
"overrides": {
"base": "#ffffff08",
"base2": "#ffffff08",
"base3": "#ffffff08",
"background-base": "#101010",
"background-weak": "#1E1E1E",
"background-strong": "#121212",
"background-stronger": "#151515",
"surface-base": "#ffffff08",
"surface-base-hover": "#FFFFFF0A",
"surface-base-active": "#ffffff0f",
"surface-base-interactive-active": "var(--cobalt-dark-alpha-2)",
"surface-inset-base": "#0000007f",
"surface-inset-base-hover": "#0000007f",
"surface-inset-strong": "#000000cc",
"surface-inset-strong-hover": "#000000cc",
"surface-raised-base": "#ffffff0f",
"surface-float-base": "#161616",
"surface-float-base-hover": "#1c1c1c",
"surface-raised-base-hover": "#ffffff14",
"surface-raised-base-active": "#ffffff1a",
"surface-raised-strong": "#ffffff14",
"surface-raised-strong-hover": "#ffffff21",
"surface-raised-stronger": "#ffffff21",
"surface-raised-stronger-hover": "#ffffff2b",
"surface-weak": "#ffffff14",
"surface-weaker": "#ffffff1a",
"surface-strong": "#ffffff2b",
"surface-raised-stronger-non-alpha": "#1B1B1B",
"surface-brand-base": "var(--yuzu-light-9)",
"surface-brand-hover": "var(--yuzu-light-10)",
"surface-interactive-base": "var(--cobalt-dark-3)",
"surface-interactive-hover": "#0A1D4D",
"surface-interactive-weak": "var(--cobalt-dark-2)",
"surface-interactive-weak-hover": "var(--cobalt-light-3)",
"surface-success-base": "var(--apple-dark-3)",
"surface-success-weak": "var(--apple-dark-2)",
"surface-success-strong": "var(--apple-dark-9)",
"surface-warning-base": "var(--solaris-light-3)",
"surface-warning-weak": "var(--solaris-light-2)",
"surface-warning-strong": "var(--solaris-light-9)",
"surface-critical-base": "var(--ember-dark-3)",
"surface-critical-weak": "var(--ember-dark-2)",
"surface-critical-strong": "var(--ember-dark-9)",
"surface-info-base": "var(--lilac-light-3)",
"surface-info-weak": "var(--lilac-light-2)",
"surface-info-strong": "var(--lilac-light-9)",
"surface-diff-unchanged-base": "#161616",
"surface-diff-skip-base": "#00000000",
"surface-diff-hidden-base": "var(--blue-dark-2)",
"surface-diff-hidden-weak": "var(--blue-dark-1)",
"surface-diff-hidden-weaker": "var(--blue-dark-3)",
"surface-diff-hidden-strong": "var(--blue-dark-5)",
"surface-diff-hidden-stronger": "var(--blue-dark-11)",
"surface-diff-add-base": "var(--mint-dark-3)",
"surface-diff-add-weak": "var(--mint-dark-4)",
"surface-diff-add-weaker": "var(--mint-dark-3)",
"surface-diff-add-strong": "var(--mint-dark-5)",
"surface-diff-add-stronger": "var(--mint-dark-11)",
"surface-diff-delete-base": "var(--ember-dark-3)",
"surface-diff-delete-weak": "var(--ember-dark-4)",
"surface-diff-delete-weaker": "var(--ember-dark-3)",
"surface-diff-delete-strong": "var(--ember-dark-5)",
"surface-diff-delete-stronger": "var(--ember-dark-11)",
"input-base": "#1c1c1c",
"input-hover": "#1c1c1c",
"input-active": "var(--cobalt-dark-1)",
"input-selected": "var(--cobalt-dark-2)",
"input-focus": "var(--cobalt-dark-1)",
"input-disabled": "#282828",
"text-base": "#ffffff96",
"text-weak": "#ffffff63",
"text-weaker": "#ffffff40",
"text-strong": "#ffffffeb",
"text-invert-base": "#ffffff96",
"text-invert-weak": "#ffffff63",
"text-invert-weaker": "#ffffff40",
"text-invert-strong": "#ffffffeb",
"text-interactive-base": "var(--cobalt-dark-11)",
"text-on-brand-base": "#ffffff96",
"text-on-interactive-base": "#ededed",
"text-on-interactive-weak": "#ffffff96",
"text-on-success-base": "var(--apple-dark-9)",
"text-on-critical-base": "var(--ember-dark-9)",
"text-on-critical-weak": "var(--ember-dark-8)",
"text-on-critical-strong": "var(--ember-dark-12)",
"text-on-warning-base": "#ffffff96",
"text-on-info-base": "#ffffff96",
"text-diff-add-base": "var(--mint-dark-11)",
"text-diff-delete-base": "var(--ember-dark-9)",
"text-diff-delete-strong": "var(--ember-dark-12)",
"text-diff-add-strong": "var(--mint-dark-8)",
"text-on-info-weak": "#ffffff63",
"text-on-info-strong": "#ffffffeb",
"text-on-warning-weak": "#ffffff63",
"text-on-warning-strong": "#ffffffeb",
"text-on-success-weak": "var(--apple-dark-8)",
"text-on-success-strong": "var(--apple-dark-12)",
"text-on-brand-weak": "#ffffff63",
"text-on-brand-weaker": "#ffffff40",
"text-on-brand-strong": "#ffffffeb",
"button-primary-base": "#ededed",
"button-secondary-base": "#1c1c1c",
"button-secondary-hover": "#FFFFFF0A",
"border-base": "#ffffff2b",
"border-hover": "#ffffff40",
"border-active": "#ffffff63",
"border-selected": "var(--cobalt-dark-alpha-11)",
"border-disabled": "#ffffff40",
"border-focus": "#ffffff63",
"border-weak-base": "#282828",
"border-weak-hover": "#ffffff2b",
"border-weak-active": "#ffffff40",
"border-weak-selected": "var(--cobalt-dark-alpha-6)",
"border-weak-disabled": "#ffffff21",
"border-weak-focus": "#ffffff40",
"border-strong-base": "#ffffff40",
"border-interactive-base": "var(--cobalt-light-7)",
"border-interactive-hover": "var(--cobalt-light-8)",
"border-interactive-active": "var(--cobalt-light-9)",
"border-interactive-selected": "var(--cobalt-light-9)",
"border-interactive-disabled": "#c7c7c7",
"border-interactive-focus": "var(--cobalt-light-9)",
"border-success-base": "var(--apple-light-6)",
"border-success-hover": "var(--apple-light-7)",
"border-success-selected": "var(--apple-light-9)",
"border-warning-base": "var(--solaris-light-6)",
"border-warning-hover": "var(--solaris-light-7)",
"border-warning-selected": "var(--solaris-light-9)",
"border-critical-base": "var(--ember-dark-5)",
"border-critical-hover": "var(--ember-dark-7)",
"border-critical-selected": "var(--ember-dark-9)",
"border-info-base": "var(--lilac-light-6)",
"border-info-hover": "var(--lilac-light-7)",
"border-info-selected": "var(--lilac-light-9)",
"icon-base": "#7e7e7e",
"icon-hover": "#a0a0a0",
"icon-active": "#ededed",
"icon-selected": "#ededed",
"icon-disabled": "#505050",
"icon-focus": "#ededed",
"icon-invert-base": "#161616",
"icon-weak-base": "#343434",
"icon-weak-hover": "#dbdbdb",
"icon-weak-active": "#c7c7c7",
"icon-weak-selected": "#8f8f8f",
"icon-weak-disabled": "#ededed",
"icon-weak-focus": "#8f8f8f",
"icon-strong-base": "#ededed",
"icon-strong-hover": "#F3F3F3",
"icon-strong-active": "#EBEBEB",
"icon-strong-selected": "#FCFCFC",
"icon-strong-disabled": "#3e3e3e",
"icon-strong-focus": "#FCFCFC",
"icon-brand-base": "var(--white)",
"icon-interactive-base": "var(--cobalt-dark-11)",
"icon-success-base": "var(--apple-dark-9)",
"icon-success-hover": "var(--apple-dark-10)",
"icon-success-active": "var(--apple-dark-11)",
"icon-warning-base": "var(--amber-dark-9)",
"icon-warning-hover": "var(--amber-dark-8)",
"icon-warning-active": "var(--amber-dark-11)",
"icon-critical-base": "var(--ember-dark-9)",
"icon-critical-hover": "var(--ember-dark-11)",
"icon-critical-active": "var(--ember-dark-12)",
"icon-info-base": "var(--lilac-dark-7)",
"icon-info-hover": "var(--lilac-dark-8)",
"icon-info-active": "var(--lilac-dark-11)",
"icon-on-brand-base": "#0000008f",
"icon-on-brand-hover": "#000000e8",
"icon-on-brand-selected": "#000000e8",
"icon-on-interactive-base": "#ededed",
"icon-agent-plan-base": "var(--purple-dark-9)",
"icon-agent-docs-base": "var(--amber-dark-9)",
"icon-agent-ask-base": "var(--cyan-dark-9)",
"icon-agent-build-base": "var(--cobalt-dark-11)",
"icon-on-success-base": "var(--apple-dark-alpha-9)",
"icon-on-success-hover": "var(--apple-dark-alpha-10)",
"icon-on-success-selected": "var(--apple-dark-alpha-11)",
"icon-on-warning-base": "var(--amber-darkalpha-9)",
"icon-on-warning-hover": "var(--amber-darkalpha-10)",
"icon-on-warning-selected": "var(--amber-darkalpha-11)",
"icon-on-critical-base": "var(--ember-dark-alpha-9)",
"icon-on-critical-hover": "var(--ember-dark-alpha-10)",
"icon-on-critical-selected": "var(--ember-dark-alpha-11)",
"icon-on-info-base": "var(--lilac-dark-9)",
"icon-on-info-hover": "var(--lilac-dark-alpha-10)",
"icon-on-info-selected": "var(--lilac-dark-alpha-11)",
"icon-diff-add-base": "var(--mint-dark-11)",
"icon-diff-add-hover": "var(--mint-dark-10)",
"icon-diff-add-active": "var(--mint-dark-11)",
"icon-diff-delete-base": "var(--ember-dark-9)",
"icon-diff-delete-hover": "var(--ember-dark-10)",
"syntax-comment": "var(--text-weak)",
"syntax-regexp": "var(--text-base)",
"syntax-string": "#00ceb9",
"syntax-keyword": "var(--text-weak)",
"syntax-primitive": "#ffba92",
"syntax-operator": "var(--text-weak)",
"syntax-variable": "var(--text-strong)",
"syntax-property": "#ff9ae2",
"syntax-type": "#ecf58c",
"syntax-constant": "#93e9f6",
"syntax-punctuation": "var(--text-weak)",
"syntax-object": "var(--text-strong)",
"syntax-success": "var(--apple-dark-10)",
"syntax-warning": "var(--amber-dark-10)",
"syntax-critical": "var(--ember-dark-10)",
"syntax-info": "#93e9f6",
"syntax-diff-add": "var(--mint-dark-11)",
"syntax-diff-delete": "var(--ember-dark-11)",
"syntax-diff-unknown": "#ff0000",
"markdown-heading": "#9d7cd8",
"markdown-text": "#eeeeee",
"markdown-link": "#fab283",
"markdown-link-text": "#56b6c2",
"markdown-code": "#7fd88f",
"markdown-block-quote": "#e5c07b",
"markdown-emph": "#e5c07b",
"markdown-strong": "#f5a742",
"markdown-horizontal-rule": "#808080",
"markdown-list-item": "#fab283",
"markdown-list-enumeration": "#56b6c2",
"markdown-image": "#fab283",
"markdown-image-text": "#56b6c2",
"markdown-code-block": "#eeeeee",
"border-color": "#ffffff",
"border-weaker-base": "#1e1e1e",
"button-ghost-hover": "#ffffff08",
"button-ghost-hover2": "#ffffff0f",
"avatar-background-pink": "#501b3f",
"avatar-background-mint": "#033a34",
"avatar-background-orange": "#5f2a06",
"avatar-background-purple": "#432155",
"avatar-background-cyan": "#0f3058",
"avatar-background-lime": "#2b3711",
"avatar-text-pink": "#e34ba9",
"avatar-text-mint": "#95f3d9",
"avatar-text-orange": "#ff802b",
"avatar-text-purple": "#9d5bd2",
"avatar-text-cyan": "#369eff",
"avatar-text-lime": "#c4f042"
}
}
}

View File

@@ -3,129 +3,39 @@
"name": "One Dark Pro",
"id": "onedarkpro",
"light": {
"seeds": {
"palette": {
"neutral": "#f5f6f8",
"ink": "#2b303b",
"primary": "#528bff",
"accent": "#d85462",
"success": "#4fa66d",
"warning": "#d19a66",
"error": "#e06c75",
"info": "#61afef",
"interactive": "#528bff",
"diffAdd": "#c2ebcf",
"diffDelete": "#f7c1c5"
},
"overrides": {
"background-base": "#f5f6f8",
"background-weak": "#eef0f4",
"background-strong": "#fafbfc",
"background-stronger": "#ffffff",
"border-weak-base": "#dee2eb",
"border-weak-hover": "#d4d9e3",
"border-weak-active": "#caced6",
"border-weak-selected": "#bec4d0",
"border-weak-disabled": "#f4f6fb",
"border-weak-focus": "#c4cada",
"border-base": "#b5bccd",
"border-hover": "#aab1c2",
"border-active": "#a0a7b8",
"border-selected": "#959cae",
"border-disabled": "#eceef4",
"border-focus": "#a6adbf",
"border-strong-base": "#747c92",
"border-strong-hover": "#6a7287",
"border-strong-active": "#60687c",
"border-strong-selected": "#565e71",
"border-strong-disabled": "#cbd0dd",
"border-strong-focus": "#666d82",
"surface-diff-add-base": "#e5f4ea",
"surface-diff-delete-base": "#fde7ea",
"surface-diff-hidden-base": "#e4e8f4",
"text-base": "#2b303b",
"text-weak": "#6b717f",
"text-strong": "#0e1118",
"syntax-string": "#4fa66d",
"syntax-primitive": "#d85462",
"syntax-property": "#528bff",
"syntax-type": "#d19a66",
"syntax-constant": "#61afef",
"syntax-info": "#61afef",
"markdown-heading": "#528bff",
"markdown-text": "#2b303b",
"markdown-link": "#528bff",
"markdown-link-text": "#61afef",
"markdown-code": "#4fa66d",
"markdown-block-quote": "#d19a66",
"markdown-emph": "#d19a66",
"markdown-strong": "#d85462",
"markdown-horizontal-rule": "#d3d7e4",
"markdown-list-item": "#528bff",
"markdown-list-enumeration": "#61afef",
"markdown-image": "#528bff",
"markdown-image-text": "#61afef",
"markdown-code-block": "#528bff"
"syntax-keyword": "#a626a4",
"syntax-primitive": "#986801"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#1e222a",
"ink": "#abb2bf",
"primary": "#61afef",
"accent": "#e06c75",
"success": "#98c379",
"warning": "#e5c07b",
"error": "#e06c75",
"info": "#56b6c2",
"interactive": "#61afef",
"diffAdd": "#4b815a",
"diffDelete": "#b2555f"
},
"overrides": {
"background-base": "#1e222a",
"background-weak": "#212631",
"background-strong": "#1b1f27",
"background-stronger": "#171b23",
"border-weak-base": "#323848",
"border-weak-hover": "#363d52",
"border-weak-active": "#3c435c",
"border-weak-selected": "#424967",
"border-weak-disabled": "#141720",
"border-weak-focus": "#3f4560",
"border-base": "#4a5164",
"border-hover": "#515871",
"border-active": "#585f7e",
"border-selected": "#60688a",
"border-disabled": "#1a1e27",
"border-focus": "#555c79",
"border-strong-base": "#6a7390",
"border-strong-hover": "#737c9d",
"border-strong-active": "#7d87ab",
"border-strong-selected": "#8791b8",
"border-strong-disabled": "#212533",
"border-strong-focus": "#7680a2",
"surface-diff-add-base": "#1c2a26",
"surface-diff-delete-base": "#2a1c22",
"surface-diff-hidden-base": "#232836",
"text-base": "#abb2bf",
"text-weak": "#818899",
"text-strong": "#f6f7fb",
"syntax-string": "#98c379",
"syntax-primitive": "#e06c75",
"syntax-property": "#61afef",
"syntax-type": "#e5c07b",
"syntax-constant": "#56b6c2",
"syntax-info": "#56b6c2",
"markdown-heading": "#61afef",
"markdown-text": "#abb2bf",
"markdown-link": "#61afef",
"markdown-link-text": "#56b6c2",
"markdown-code": "#98c379",
"markdown-block-quote": "#e5c07b",
"markdown-emph": "#e5c07b",
"markdown-strong": "#e06c75",
"markdown-horizontal-rule": "#2d3444",
"markdown-list-item": "#61afef",
"markdown-list-enumeration": "#56b6c2",
"markdown-image": "#61afef",
"markdown-image-text": "#56b6c2",
"markdown-code-block": "#abb2bf"
"syntax-keyword": "#c678dd",
"syntax-primitive": "#d19a66"
}
}
}

View File

@@ -3,129 +3,37 @@
"name": "Shades of Purple",
"id": "shadesofpurple",
"light": {
"seeds": {
"palette": {
"neutral": "#f7ebff",
"ink": "#3b2c59",
"primary": "#7a5af8",
"accent": "#ff6bd5",
"success": "#3dd598",
"warning": "#f7c948",
"error": "#ff6bd5",
"info": "#62d4ff",
"interactive": "#7a5af8",
"diffAdd": "#c8f8da",
"diffDelete": "#ffc3ef"
},
"overrides": {
"background-base": "#f7ebff",
"background-weak": "#f2e2ff",
"background-strong": "#fbf2ff",
"background-stronger": "#fff7ff",
"border-weak-base": "#e5d3ff",
"border-weak-hover": "#dac8f5",
"border-weak-active": "#d1bdeb",
"border-weak-selected": "#c6b3e1",
"border-weak-disabled": "#fcf6ff",
"border-weak-focus": "#ccb9e7",
"border-base": "#baa4d5",
"border-hover": "#b098cb",
"border-active": "#a68dc2",
"border-selected": "#9b82b8",
"border-disabled": "#f1e7ff",
"border-focus": "#a692c6",
"border-strong-base": "#8769a9",
"border-strong-hover": "#7b5c9d",
"border-strong-active": "#704f91",
"border-strong-selected": "#664587",
"border-strong-disabled": "#d8c4f0",
"border-strong-focus": "#755495",
"surface-diff-add-base": "#edf8f1",
"surface-diff-delete-base": "#ffe4f4",
"surface-diff-hidden-base": "#e9e4ff",
"text-base": "#3b2c59",
"text-weak": "#6c568f",
"text-strong": "#1c1033",
"syntax-string": "#3dd598",
"syntax-primitive": "#ff6bd5",
"syntax-property": "#7a5af8",
"syntax-type": "#f7c948",
"syntax-constant": "#62d4ff",
"syntax-info": "#62d4ff",
"markdown-heading": "#7a5af8",
"markdown-text": "#3b2c59",
"markdown-link": "#7a5af8",
"markdown-link-text": "#62d4ff",
"markdown-code": "#3dd598",
"markdown-block-quote": "#f7c948",
"markdown-emph": "#f7c948",
"markdown-strong": "#ff6bd5",
"markdown-horizontal-rule": "#decbed",
"markdown-list-item": "#7a5af8",
"markdown-list-enumeration": "#62d4ff",
"markdown-image": "#7a5af8",
"markdown-image-text": "#62d4ff",
"markdown-code-block": "#7a5af8"
"syntax-keyword": "#ff6bd5"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#1a102b",
"ink": "#f5f0ff",
"primary": "#c792ff",
"accent": "#ff7ac6",
"success": "#7be0b0",
"warning": "#ffd580",
"error": "#ff7ac6",
"info": "#7dd4ff",
"interactive": "#c792ff",
"diffAdd": "#53c39f",
"diffDelete": "#d85aa0"
},
"overrides": {
"background-base": "#1a102b",
"background-weak": "#1f1434",
"background-strong": "#1c122f",
"background-stronger": "#170e26",
"border-weak-base": "#352552",
"border-weak-hover": "#3a2a5d",
"border-weak-active": "#402f68",
"border-weak-selected": "#463674",
"border-weak-disabled": "#10091b",
"border-weak-focus": "#3d2d65",
"border-base": "#4d3a73",
"border-hover": "#553f7f",
"border-active": "#5d468c",
"border-selected": "#654c99",
"border-disabled": "#150d21",
"border-focus": "#594283",
"border-strong-base": "#7659b0",
"border-strong-hover": "#8262be",
"border-strong-active": "#8e6ccc",
"border-strong-selected": "#9a77da",
"border-strong-disabled": "#1c122c",
"border-strong-focus": "#8666c4",
"surface-diff-add-base": "#142c27",
"surface-diff-delete-base": "#2d1424",
"surface-diff-hidden-base": "#231737",
"text-base": "#f5f0ff",
"text-weak": "#c9b6ff",
"text-strong": "#ffffff",
"syntax-string": "#7be0b0",
"syntax-primitive": "#ff7ac6",
"syntax-property": "#c792ff",
"syntax-type": "#ffd580",
"syntax-constant": "#7dd4ff",
"syntax-info": "#7dd4ff",
"markdown-heading": "#c792ff",
"markdown-text": "#f5f0ff",
"markdown-link": "#c792ff",
"markdown-link-text": "#7dd4ff",
"markdown-code": "#7be0b0",
"markdown-block-quote": "#ffd580",
"markdown-emph": "#ffd580",
"markdown-strong": "#ff7ac6",
"markdown-horizontal-rule": "#2d1d41",
"markdown-list-item": "#c792ff",
"markdown-list-enumeration": "#7dd4ff",
"markdown-image": "#c792ff",
"markdown-image-text": "#7dd4ff",
"markdown-code-block": "#f5f0ff"
"syntax-keyword": "#ff7ac6"
}
}
}

View File

@@ -3,129 +3,39 @@
"name": "Solarized",
"id": "solarized",
"light": {
"seeds": {
"palette": {
"neutral": "#fdf6e3",
"ink": "#586e75",
"primary": "#268bd2",
"accent": "#d33682",
"success": "#859900",
"warning": "#b58900",
"error": "#dc322f",
"info": "#2aa198",
"interactive": "#268bd2",
"diffAdd": "#c6dc7a",
"diffDelete": "#f2a1a1"
},
"overrides": {
"background-base": "#fdf6e3",
"background-weak": "#f6efda",
"background-strong": "#faf3dc",
"background-stronger": "#f6edd4",
"border-weak-base": "#e3e0cd",
"border-weak-hover": "#d9d4c2",
"border-weak-active": "#cfcab7",
"border-weak-selected": "#c5c0ad",
"border-weak-disabled": "#f2edda",
"border-weak-focus": "#cbc6b2",
"border-base": "#bcb5a0",
"border-hover": "#b1aa96",
"border-active": "#a59f8c",
"border-selected": "#999382",
"border-disabled": "#ede7d4",
"border-focus": "#aca58f",
"border-strong-base": "#8c8572",
"border-strong-hover": "#7f7866",
"border-strong-active": "#716b5b",
"border-strong-selected": "#645f50",
"border-strong-disabled": "#d5cdb8",
"border-strong-focus": "#78715f",
"surface-diff-add-base": "#eef5d6",
"surface-diff-delete-base": "#fde4dd",
"surface-diff-hidden-base": "#e3ecf3",
"text-base": "#586e75",
"text-weak": "#7a8c8e",
"text-strong": "#073642",
"syntax-string": "#859900",
"syntax-primitive": "#d33682",
"syntax-property": "#268bd2",
"syntax-type": "#b58900",
"syntax-constant": "#2aa198",
"syntax-info": "#2aa198",
"markdown-heading": "#268bd2",
"markdown-text": "#586e75",
"markdown-link": "#268bd2",
"markdown-link-text": "#2aa198",
"markdown-code": "#859900",
"markdown-block-quote": "#b58900",
"markdown-emph": "#b58900",
"markdown-strong": "#d33682",
"markdown-horizontal-rule": "#cfd1bf",
"markdown-list-item": "#268bd2",
"markdown-list-enumeration": "#2aa198",
"markdown-image": "#268bd2",
"markdown-image-text": "#2aa198",
"markdown-code-block": "#2aa198"
"syntax-keyword": "#859900",
"syntax-string": "#2aa198"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#002b36",
"ink": "#93a1a1",
"primary": "#6c71c4",
"accent": "#d33682",
"success": "#859900",
"warning": "#b58900",
"error": "#dc322f",
"info": "#2aa198",
"interactive": "#6c71c4",
"diffAdd": "#4c7654",
"diffDelete": "#c34b4b"
},
"overrides": {
"background-base": "#001f27",
"background-weak": "#022733",
"background-strong": "#01222b",
"background-stronger": "#032830",
"border-weak-base": "#20373f",
"border-weak-hover": "#243e47",
"border-weak-active": "#28434f",
"border-weak-selected": "#2d4958",
"border-weak-disabled": "#0f2026",
"border-weak-focus": "#2a4552",
"border-base": "#31505b",
"border-hover": "#365765",
"border-active": "#3c5e70",
"border-selected": "#42657a",
"border-disabled": "#13272e",
"border-focus": "#3a5a6b",
"border-strong-base": "#4a7887",
"border-strong-hover": "#528294",
"border-strong-active": "#5a8ca1",
"border-strong-selected": "#6396ae",
"border-strong-disabled": "#1b323b",
"border-strong-focus": "#56879a",
"surface-diff-add-base": "#0f2f29",
"surface-diff-delete-base": "#321c1c",
"surface-diff-hidden-base": "#0f3844",
"text-base": "#93a1a1",
"text-weak": "#6c7f80",
"text-strong": "#fdf6e3",
"syntax-string": "#859900",
"syntax-primitive": "#d33682",
"syntax-property": "#6c71c4",
"syntax-type": "#b58900",
"syntax-constant": "#2aa198",
"syntax-info": "#2aa198",
"markdown-heading": "#6c71c4",
"markdown-text": "#93a1a1",
"markdown-link": "#6c71c4",
"markdown-link-text": "#2aa198",
"markdown-code": "#859900",
"markdown-block-quote": "#b58900",
"markdown-emph": "#b58900",
"markdown-strong": "#d33682",
"markdown-horizontal-rule": "#0e3b46",
"markdown-list-item": "#6c71c4",
"markdown-list-enumeration": "#2aa198",
"markdown-image": "#6c71c4",
"markdown-image-text": "#2aa198",
"markdown-code-block": "#93a1a1"
"syntax-keyword": "#859900",
"syntax-string": "#2aa198"
}
}
}

View File

@@ -3,153 +3,37 @@
"name": "Tokyonight",
"id": "tokyonight",
"light": {
"seeds": {
"palette": {
"neutral": "#e1e2e7",
"ink": "#273153",
"primary": "#2e7de9",
"accent": "#b15c00",
"success": "#587539",
"warning": "#8c6c3e",
"error": "#c94060",
"info": "#007197",
"interactive": "#2e7de9",
"diffAdd": "#4f8f7b",
"diffDelete": "#d05f7c"
},
"overrides": {
"background-base": "#e1e2e7",
"background-weak": "#dee0ea",
"background-strong": "#e5e6ee",
"background-stronger": "#e9eaf1",
"border-weak-base": "#cdd0dc",
"border-weak-hover": "#c3c6d2",
"border-weak-active": "#b9bcc8",
"border-weak-selected": "#aeb2bf",
"border-weak-disabled": "#e6e7ef",
"border-weak-focus": "#b3b6c3",
"border-base": "#a7abbb",
"border-hover": "#9ba0b1",
"border-active": "#9095a8",
"border-selected": "#83889e",
"border-disabled": "#dedfe6",
"border-focus": "#9599a8",
"border-strong-base": "#757b90",
"border-strong-hover": "#6a7084",
"border-strong-active": "#5f6578",
"border-strong-selected": "#545a6d",
"border-strong-disabled": "#c4c6d0",
"border-strong-focus": "#666b7f",
"surface-diff-add-base": "#dfe7da",
"surface-diff-delete-base": "#f4dadd",
"surface-diff-hidden-base": "#cfd1dd",
"text-base": "#273153",
"text-weak": "#5c6390",
"text-strong": "#1c2544",
"syntax-string": "#587539",
"syntax-primitive": "#b15c00",
"syntax-property": "#9854f1",
"syntax-type": "#3760bf",
"syntax-constant": "#007197",
"syntax-info": "#007197",
"markdown-heading": "#9854f1",
"markdown-text": "#273153",
"markdown-link": "#2e7de9",
"markdown-link-text": "#007197",
"markdown-code": "#587539",
"markdown-block-quote": "#8c6c3e",
"markdown-emph": "#8c6c3e",
"markdown-strong": "#b15c00",
"markdown-horizontal-rule": "#a1a6c5",
"markdown-list-item": "#2e7de9",
"markdown-list-enumeration": "#007197",
"markdown-image": "#2e7de9",
"markdown-image-text": "#007197",
"markdown-code-block": "#3760bf"
"syntax-keyword": "#9854f1"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#1a1b26",
"ink": "#c0caf5",
"primary": "#7aa2f7",
"accent": "#ff9e64",
"success": "#9ece6a",
"warning": "#e0af68",
"error": "#f7768e",
"info": "#7dcfff",
"interactive": "#7aa2f7",
"diffAdd": "#41a6b5",
"diffDelete": "#c34043"
},
"overrides": {
"background-base": "#0f111a",
"background-weak": "#111428",
"background-strong": "#101324",
"background-stronger": "#13172a",
"border-weak-base": "#25283b",
"border-weak-hover": "#292c43",
"border-weak-active": "#2e314b",
"border-weak-selected": "#343755",
"border-weak-disabled": "#151727",
"border-weak-focus": "#30324f",
"border-base": "#3a3e57",
"border-hover": "#414264",
"border-active": "#474972",
"border-selected": "#4f507f",
"border-disabled": "#1c1d2d",
"border-focus": "#45496f",
"border-strong-base": "#5a5f82",
"border-strong-hover": "#646994",
"border-strong-active": "#6f74a6",
"border-strong-selected": "#7a7fb8",
"border-strong-disabled": "#23243a",
"border-strong-focus": "#6a6f9f",
"surface-base": "#1f2335",
"base": "#1f2335",
"surface-base-hover": "#232840",
"surface-base-active": "#262c46",
"surface-base-interactive-active": "#2b3357",
"base2": "#1f2335",
"base3": "#1f2335",
"surface-inset-base": "#161a2ab3",
"surface-inset-base-hover": "#161a2acc",
"surface-inset-strong": "#0d111fcc",
"surface-inset-strong-hover": "#0d111fcc",
"surface-raised-base": "#242a42",
"surface-float-base": "#242b45",
"surface-float-base-hover": "#2a3154",
"surface-raised-base-hover": "#272e49",
"surface-raised-base-active": "#2c3353",
"surface-raised-strong": "#31385a",
"surface-raised-strong-hover": "#373f6b",
"surface-raised-stronger": "#3b4261",
"surface-raised-stronger-hover": "#444c82",
"surface-weak": "#1b2033",
"surface-weaker": "#181d2d",
"surface-strong": "#323858",
"surface-raised-stronger-non-alpha": "#2b3150",
"surface-diff-add-base": "#1c2a38",
"surface-diff-delete-base": "#2a1f32",
"surface-diff-hidden-base": "#24283b",
"text-base": "#c0caf5",
"text-weak": "#7a88cf",
"text-strong": "#eaeaff",
"syntax-string": "#9ece6a",
"syntax-primitive": "#ff9e64",
"syntax-property": "#bb9af7",
"syntax-type": "#e0af68",
"syntax-constant": "#7dcfff",
"syntax-info": "#7dcfff",
"markdown-heading": "#bb9af7",
"markdown-text": "#c0caf5",
"markdown-link": "#7aa2f7",
"markdown-link-text": "#7dcfff",
"markdown-code": "#9ece6a",
"markdown-block-quote": "#e0af68",
"markdown-emph": "#e0af68",
"markdown-strong": "#ff9e64",
"markdown-horizontal-rule": "#3b4261",
"markdown-list-item": "#7aa2f7",
"markdown-list-enumeration": "#7dcfff",
"markdown-image": "#7aa2f7",
"markdown-image-text": "#7dcfff",
"markdown-code-block": "#c0caf5"
"syntax-keyword": "#bb9af7"
}
}
}

View File

@@ -3,129 +3,38 @@
"name": "Vesper",
"id": "vesper",
"light": {
"seeds": {
"palette": {
"neutral": "#F0F0F0",
"ink": "#101010",
"primary": "#FFC799",
"accent": "#B30000",
"success": "#99FFE4",
"warning": "#FFC799",
"error": "#FF8080",
"info": "#FFC799",
"interactive": "#FFC799",
"diffAdd": "#99FFE4",
"diffDelete": "#FF8080"
},
"overrides": {
"background-base": "#FFF",
"background-weak": "#F8F8F8",
"background-strong": "#F0F0F0",
"background-stronger": "#FBFBFB",
"border-weak-hover": "#E0E0E0",
"border-weak-active": "#D8D8D8",
"border-weak-selected": "#D0D0D0",
"border-weak-disabled": "#F0F0F0",
"border-weak-focus": "#D8D8D8",
"border-base": "#D0D0D0",
"border-hover": "#C8C8C8",
"border-active": "#C0C0C0",
"border-selected": "#B8B8B8",
"border-disabled": "#E8E8E8",
"border-focus": "#C0C0C0",
"border-strong-base": "#A0A0A0",
"border-strong-hover": "#989898",
"border-strong-active": "#909090",
"border-strong-selected": "#888888",
"border-strong-disabled": "#D0D0D0",
"border-strong-focus": "#909090",
"surface-diff-add-base": "#e8f5e8",
"surface-diff-delete-base": "#f5e8e8",
"surface-diff-hidden-base": "#F0F0F0",
"text-base": "#101010",
"text-invert-strong": "var(--smoke-dark-alpha-12)",
"text-weak": "#606060",
"text-strong": "#000000",
"syntax-string": "#0D5C4F",
"syntax-primitive": "#B30000",
"syntax-property": "#C66C00",
"syntax-type": "#9C5C12",
"syntax-constant": "#404040",
"syntax-info": "#606060",
"markdown-heading": "#FFC799",
"markdown-text": "#101010",
"markdown-link": "#FFC799",
"markdown-link-text": "#A0A0A0",
"markdown-code": "#A0A0A0",
"markdown-block-quote": "#101010",
"markdown-emph": "#101010",
"markdown-strong": "#101010",
"markdown-horizontal-rule": "#65737E",
"markdown-list-item": "#101010",
"markdown-list-enumeration": "#101010",
"markdown-image": "#FFC799",
"markdown-image-text": "#A0A0A0",
"markdown-code-block": "#FFC799"
"syntax-keyword": "#b30000"
}
},
"dark": {
"seeds": {
"palette": {
"neutral": "#101010",
"ink": "#FFF",
"primary": "#FFC799",
"accent": "#FF8080",
"success": "#99FFE4",
"warning": "#FFC799",
"error": "#FF8080",
"info": "#FFC799",
"interactive": "#FFC799",
"diffAdd": "#99FFE4",
"diffDelete": "#FF8080"
},
"overrides": {
"background-base": "#101010",
"background-weak": "#141414",
"background-strong": "#0C0C0C",
"background-stronger": "#080808",
"border-weak-base": "#1C1C1C",
"border-weak-hover": "#202020",
"border-weak-active": "#242424",
"border-weak-selected": "#282828",
"border-weak-disabled": "#141414",
"border-weak-focus": "#242424",
"border-base": "#282828",
"border-hover": "#303030",
"border-active": "#383838",
"border-selected": "#404040",
"border-disabled": "#181818",
"border-focus": "#383838",
"border-strong-base": "#505050",
"border-strong-hover": "#585858",
"border-strong-active": "#606060",
"border-strong-selected": "#686868",
"border-strong-disabled": "#202020",
"border-strong-focus": "#606060",
"surface-diff-add-base": "#0d2818",
"surface-diff-delete-base": "#281a1a",
"surface-diff-hidden-base": "#141414",
"text-base": "#FFF",
"text-weak": "#A0A0A0",
"text-strong": "#FFFFFF",
"syntax-string": "#99FFE4",
"syntax-primitive": "#FF8080",
"syntax-property": "#FFC799",
"syntax-type": "#FFC799",
"syntax-constant": "#A0A0A0",
"syntax-info": "#8b8b8b",
"markdown-heading": "#FFC799",
"markdown-text": "#FFF",
"markdown-link": "#FFC799",
"markdown-link-text": "#A0A0A0",
"markdown-code": "#A0A0A0",
"markdown-block-quote": "#FFF",
"markdown-emph": "#FFF",
"markdown-strong": "#FFF",
"markdown-horizontal-rule": "#65737E",
"markdown-list-item": "#FFF",
"markdown-list-enumeration": "#FFF",
"markdown-image": "#FFC799",
"markdown-image-text": "#A0A0A0",
"markdown-code-block": "#FFF"
"syntax-keyword": "#ff8080",
"syntax-primitive": "#ffc799"
}
}
}

View File

@@ -18,11 +18,28 @@ export interface ThemeSeedColors {
diffDelete: HexColor
}
export interface ThemeVariant {
seeds: ThemeSeedColors
export interface ThemePaletteColors {
neutral: HexColor
ink?: HexColor
primary: HexColor
success: HexColor
warning: HexColor
error: HexColor
info: HexColor
accent?: HexColor
interactive?: HexColor
diffAdd?: HexColor
diffDelete?: HexColor
}
type ThemeVariantBase = {
overrides?: Record<string, ColorValue>
}
export type ThemeVariant =
| ({ seeds: ThemeSeedColors; palette?: never } & ThemeVariantBase)
| ({ palette: ThemePaletteColors; seeds?: never } & ThemeVariantBase)
export interface DesktopTheme {
$schema?: string
name: string

View File

@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/util",
"version": "1.2.21",
"version": "1.2.22",
"private": true,
"type": "module",
"license": "MIT",

View File

@@ -2,7 +2,7 @@
"name": "@opencode-ai/web",
"type": "module",
"license": "MIT",
"version": "1.2.21",
"version": "1.2.22",
"scripts": {
"dev": "astro dev",
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",

View File

@@ -28,6 +28,7 @@ description: خصّص اختصارات لوحة المفاتيح.
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ OpenCode ima listu veza tipki koje možete prilagoditi putem `tui.json`.
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ OpenCode har en liste over nøglebindinger, som du kan tilpasse gennem `tui.json
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -3,11 +3,11 @@ title: Tastenkombinationen
description: Passen Sie Ihre Tastenkombinationen an.
---
OpenCode verfügt über eine Liste von Tastenkombinationen, die Sie über die OpenCode-Konfiguration anpassen können.
OpenCode verfügt über eine Liste von Tastenkombinationen, die Sie über `tui.json` anpassen können.
```json title="opencode.json"
```json title="tui.json"
{
"$schema": "https://opencode.ai/config.json",
"$schema": "https://opencode.ai/tui.json",
"keybinds": {
"leader": "ctrl+x",
"app_exit": "ctrl+c,ctrl+d,<leader>q",
@@ -28,6 +28,7 @@ OpenCode verfügt über eine Liste von Tastenkombinationen, die Sie über die Op
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",
@@ -117,11 +118,11 @@ Sie müssen für Ihre Keybinds keinen Leader Key verwenden, wir empfehlen jedoch
## Keybind deaktivieren
Sie können eine Keybind deaktivieren, indem Sie den Schlüssel mit dem Wert „none“ zu Ihrer Konfiguration hinzufügen.
Sie können eine Keybind deaktivieren, indem Sie den Schlüssel mit dem Wert „none“ zu `tui.json` hinzufügen.
```json title="opencode.json"
```json title="tui.json"
{
"$schema": "https://opencode.ai/config.json",
"$schema": "https://opencode.ai/tui.json",
"keybinds": {
"session_compact": "none"
}

View File

@@ -28,6 +28,7 @@ OpenCode tiene una lista de combinaciones de teclas que puede personalizar a tra
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ OpenCode a une liste de raccourcis clavier que vous pouvez personnaliser via la
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ OpenCode ha una lista di scorciatoie che puoi personalizzare tramite `tui.json`.
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ OpenCode には、`tui.json` を通じてカスタマイズできるキーバイ
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ OpenCode has a list of keybinds that you can customize through `tui.json`.
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ OpenCode에는 `tui.json`을 통해 커스터마이즈할 수 있는 키바인
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ OpenCode har en liste over tastebindinger som du kan tilpasse gjennom `tui.json`
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ OpenCode zawiera listę skrótów klawiszowych, które można dostosować za pom
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ O opencode tem uma lista de atalhos de teclado que você pode personalizar atrav
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ opencode имеет список сочетаний клавиш, которые
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ OpenCode มีรายการปุ่มลัดที่คุณปร
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ opencode, `tui.json` aracılığıyla özelleştirebileceğiniz bir tuş bağlan
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",
@@ -134,21 +135,21 @@ Anahtarı yapılandırmanıza "none" değeriyle ekleyerek bir tuş atamasını d
opencode masaüstü uygulaması bilgi istemi girişi, metni düzenlemek için yaygın Readline/Emacs tarzı kısayolları destekler. Bunlar yerleşiktir ve şu anda `opencode.json` aracılığıyla yapılandırılamaz.
| Shortcut | Action |
| -------- | ---------------------------------------- |
| `ctrl+a` | Geçerli satırın başına git |
| `ctrl+e` | Move to end of current line |
| `ctrl+b` | Move cursor back one character |
| `ctrl+f` | Move cursor forward one character |
| `alt+b` | Move cursor back one word |
| `alt+f` | Move cursor forward one word |
| `ctrl+d` | Delete character under cursor |
| `ctrl+k` | Kill to end of line |
| `ctrl+u` | Satırın başına kadar öldür |
| `ctrl+w` | Kill previous word |
| `alt+d` | Kill next word |
| `ctrl+t` | Transpose characters |
| `ctrl+g` | Cancel popovers / abort running response |
| Shortcut | Action |
| -------- | --------------------------------------------------- |
| `ctrl+a` | Geçerli satırın başına git |
| `ctrl+e` | Geçerli satırın sonuna git |
| `ctrl+b` | İmleci bir karakter geri taşı |
| `ctrl+f` | İmleci bir karakter ileri taşı |
| `alt+b` | İmleci bir kelime geri taşı |
| `alt+f` | İmleci bir kelime ileri taşı |
| `ctrl+d` | İmleç altındaki karakteri sil |
| `ctrl+k` | Satırın sonuna kadar sil |
| `ctrl+u` | Satırın başına kadar sil |
| `ctrl+w` | Önceki kelimeyi sil |
| `alt+d` | Sonraki kelimeyi sil |
| `ctrl+t` | Karakterlerin yerini değiştir |
| `ctrl+g` | ılır pencereleri iptal et / çalışan yanıtı durdur |
---
@@ -158,7 +159,7 @@ Bazı terminaller varsayılan olarak Enter ile değiştirici tuşlar göndermez.
### Windows Terminali
`settings.json` cihazınızı şu adresteın:
`settings.json` dosyasını şuradaın:
```
%LOCALAPPDATA%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json

View File

@@ -28,6 +28,7 @@ OpenCode 提供了一系列快捷键,您可以通过 `tui.json` 进行自定
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

View File

@@ -28,6 +28,7 @@ OpenCode 提供了一系列快捷鍵,您可以透過 `tui.json` 進行自訂
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "<leader>c",
"session_child_first": "<leader>down",
"session_child_cycle": "<leader>right",
"session_child_cycle_reverse": "<leader>left",
"session_parent": "<leader>up",

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