Compare commits

...

9 Commits

Author SHA1 Message Date
Dax Raad ae4d089c06 remove call to npm causing noticible delay when starting chat 2025-06-18 10:35:41 -04:00
Dax Raad 5110fbdaf9 fix issue when running opencode in empty directory 2025-06-18 10:29:09 -04:00
Dax Raad e6ddb474fc ignore: sync 2025-06-18 08:36:25 -04:00
SBSTN 0dc71774ce Add Everforest Theme (#170) 2025-06-18 05:55:38 -05:00
Dax Raad b470466e30 integrate cache read/write data 2025-06-17 20:51:39 -04:00
Jay V d1f9311931 ignore: share page polish 2025-06-17 20:26:12 -04:00
Dax Raad 1c58023df9 improve anthropic oauth token caching and authentication handling
🤖 Generated with [opencode](https://opencode.ai)

Co-Authored-By: opencode <noreply@opencode.ai>
2025-06-17 13:23:15 -04:00
Dax Raad 4e0aa58b7e ignore: fix 2025-06-17 13:04:26 -04:00
Dax Raad 23ee34b35f state 2025-06-17 12:29:28 -04:00
20 changed files with 715 additions and 633 deletions
+5 -2
View File
@@ -45,6 +45,7 @@
"zod-openapi": "4.2.4",
},
"devDependencies": {
"@ai-sdk/anthropic": "1.2.12",
"@tsconfig/bun": "1.0.7",
"@types/bun": "latest",
"@types/turndown": "5.0.5",
@@ -74,7 +75,7 @@
"sharp": "0.32.5",
"shiki": "3.4.2",
"solid-js": "1.9.7",
"toolbeam-docs-theme": "0.2.4",
"toolbeam-docs-theme": "0.3.0",
},
"devDependencies": {
"@types/node": "catalog:",
@@ -99,6 +100,8 @@
"zod": "3.24.2",
},
"packages": {
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@1.2.12", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ=="],
"@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="],
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="],
@@ -1491,7 +1494,7 @@
"token-types": ["token-types@6.0.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA=="],
"toolbeam-docs-theme": ["toolbeam-docs-theme@0.2.4", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-W5mdbcgRpTBDFyEdcU81USs3MFZoXMInpSznc/AFZCwqz8atk4iBNDIlhvihpGHY54Nf5crKmZwJjxVojkHFvA=="],
"toolbeam-docs-theme": ["toolbeam-docs-theme@0.3.0", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-qlBkKRp8HVYV7p7jaG9lT2lvQY7c8b9czZ0tnsJUrN2TBTtEyFJymCdkhhpZNC9U4oGZ7lLk0glRJHrndWvVsg=="],
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
+2 -1
View File
@@ -20,7 +20,8 @@
"@types/turndown": "5.0.5",
"@types/yargs": "17.0.33",
"typescript": "catalog:",
"zod-to-json-schema": "3.24.5"
"zod-to-json-schema": "3.24.5",
"@ai-sdk/anthropic": "1.2.12"
},
"dependencies": {
"@clack/prompts": "0.11.0",
+2
View File
@@ -18,6 +18,7 @@ export namespace App {
data: z.string(),
root: z.string(),
cwd: z.string(),
state: z.string(),
}),
time: z.object({
initialized: z.number().optional(),
@@ -68,6 +69,7 @@ export namespace App {
git: git !== undefined,
path: {
config: Global.Path.config,
state: Global.Path.state,
data,
root: git ?? input.cwd,
cwd: input.cwd,
+3
View File
@@ -48,6 +48,7 @@ export namespace AuthAnthropic {
await Auth.set("anthropic", {
type: "oauth",
refresh: json.refresh_token as string,
access: json.access_token as string,
expires: Date.now() + json.expires_in * 1000,
})
}
@@ -55,6 +56,7 @@ export namespace AuthAnthropic {
export async function access() {
const info = await Auth.get("anthropic")
if (!info || info.type !== "oauth") return
if (info.access && info.expires > Date.now()) return info.access
const response = await fetch(
"https://console.anthropic.com/v1/oauth/token",
{
@@ -74,6 +76,7 @@ export namespace AuthAnthropic {
await Auth.set("anthropic", {
type: "oauth",
refresh: json.refresh_token as string,
access: json.access_token as string,
expires: Date.now() + json.expires_in * 1000,
})
return json.access_token as string
+1
View File
@@ -7,6 +7,7 @@ export namespace Auth {
export const Oauth = z.object({
type: z.literal("oauth"),
refresh: z.string(),
access: z.string(),
expires: z.number(),
})
+2 -2
View File
@@ -5,10 +5,10 @@ import path from "path"
export namespace GlobalConfig {
export const Info = z.object({
autoupdate: z.boolean().optional(),
autoshare: z.boolean().optional(),
provider: z.string().optional(),
model: z.string().optional(),
autoupdate: z.boolean().optional(),
autoshare: z.boolean().optional(),
disabled_providers: z.array(z.string()).optional(),
})
export type Info = z.infer<typeof Info>
+7 -4
View File
@@ -1,5 +1,5 @@
import fs from "fs/promises"
import { xdgData, xdgCache, xdgConfig } from "xdg-basedir"
import { xdgData, xdgCache, xdgConfig, xdgState } from "xdg-basedir"
import path from "path"
const app = "opencode"
@@ -7,6 +7,7 @@ const app = "opencode"
const data = path.join(xdgData!, app)
const cache = path.join(xdgCache!, app)
const config = path.join(xdgConfig!, app)
const state = path.join(xdgState!, app)
export namespace Global {
export const Path = {
@@ -15,12 +16,14 @@ export namespace Global {
providers: path.join(config, "providers"),
cache,
config,
state,
} as const
}
await Promise.all([
fs.mkdir(data, { recursive: true }),
fs.mkdir(config, { recursive: true }),
fs.mkdir(cache, { recursive: true }),
fs.mkdir(Global.Path.data, { recursive: true }),
fs.mkdir(Global.Path.config, { recursive: true }),
fs.mkdir(Global.Path.cache, { recursive: true }),
fs.mkdir(Global.Path.providers, { recursive: true }),
fs.mkdir(Global.Path.state, { recursive: true }),
])
+3 -4
View File
@@ -127,14 +127,13 @@ const cli = yargs(hideBin(process.argv))
) {
cli.showHelp("log")
}
Log.Default.error(msg, {
err,
})
})
.strict()
try {
await cli.parse()
} catch (e) {
Log.Default.error(e)
Log.Default.error(e, {
stack: e instanceof Error ? e.stack : undefined,
})
}
+2 -28
View File
@@ -17,8 +17,8 @@ export namespace ModelsDev {
cost: z.object({
input: z.number(),
output: z.number(),
inputCached: z.number(),
outputCached: z.number(),
cache_read: z.number().optional(),
cache_write: z.number().optional(),
}),
limit: z.object({
context: z.number(),
@@ -64,30 +64,4 @@ export namespace ModelsDev {
throw new Error(`Failed to fetch models.dev: ${result.statusText}`)
await Bun.write(file, result)
}
const aisdk = lazy(async () => {
log.info("fetching ai-sdk")
const response = await fetch(
"https://registry.npmjs.org/-/v1/search?text=scope:@ai-sdk",
)
if (!response.ok)
throw new Error(
`Failed to fetch ai-sdk information: ${response.statusText}`,
)
const result = await response.json()
log.info("found ai-sdk", result.objects.length)
return result.objects
.filter((obj: any) => obj.package.name.startsWith("@ai-sdk/"))
.reduce((acc: any, obj: any) => {
acc[obj.package.name] = obj
return acc
}, {})
})
export async function pkg(providerID: string): Promise<[string, string]> {
const packages = await aisdk()
const match = packages[`@ai-sdk/${providerID}`]
if (match) return [match.package.name, "latest"]
return [providerID, "latest"]
}
}
+13 -6
View File
@@ -43,16 +43,23 @@ export namespace Provider {
for (const model of Object.values(provider.models)) {
model.cost = {
input: 0,
inputCached: 0,
output: 0,
outputCached: 0,
}
}
return {
apiKey: "",
headers: {
authorization: `Bearer ${access}`,
"anthropic-beta": "oauth-2025-04-20",
async fetch(input: any, init: any) {
const access = await AuthAnthropic.access()
const headers = {
...init.headers,
authorization: `Bearer ${access}`,
"anthropic-beta": "oauth-2025-04-20",
}
delete headers["x-api-key"]
return fetch(input, {
...init,
headers,
})
},
}
},
@@ -225,7 +232,7 @@ export namespace Provider {
const s = await state()
const existing = s.sdk.get(provider.id)
if (existing) return existing
const [pkg, version] = await ModelsDev.pkg(provider.npm ?? provider.id)
const [pkg, version] = provider.npm ?? provider.id
const mod = await import(await BunProc.install(pkg, version))
const fn = mod[Object.keys(mod).find((key) => key.startsWith("create"))!]
const loaded = fn(s.providers[provider.id]?.options)
+10
View File
@@ -759,6 +759,16 @@ export namespace Session {
cost: new Decimal(0)
.add(new Decimal(tokens.input).mul(model.cost.input).div(1_000_000))
.add(new Decimal(tokens.output).mul(model.cost.output).div(1_000_000))
.add(
new Decimal(tokens.cache.read)
.mul(model.cost.cache_read ?? 0)
.div(1_000_000),
)
.add(
new Decimal(tokens.cache.write)
.mul(model.cost.cache_write ?? 0)
.div(1_000_000),
)
.toNumber(),
tokens,
}
@@ -1,4 +1,4 @@
you will generate a short title based on the first message a user begins a conversation with
You will generate a short title based on the first message a user begins a conversation with
- ensure it is not more than 50 characters long
- the title should be a summary of the user's message
- it should be one line long
+298
View File
@@ -0,0 +1,298 @@
package theme
import (
"github.com/charmbracelet/lipgloss/v2"
"github.com/charmbracelet/lipgloss/v2/compat"
)
// EverforestTheme implements the Theme interface with Everforest colors.
// It provides both dark and light variants with Medium (default) contrast.
type EverforestTheme struct {
BaseTheme
}
// NewEverforestTheme creates a new instance of the Everforest Medium theme.
func NewEverforestTheme() *EverforestTheme {
// Everforest color palette - Medium variant
// Official colors from https://github.com/sainnhe/everforest/wiki
// Dark mode colors - using Everforest:Dark Medium contrast palette
darkStep1 := "#2d353b" // App background
darkStep2 := "#333c43" // Subtle background
darkStep3 := "#343f44" // UI element background
darkStep4 := "#3d484d" // Hovered UI element background
darkStep5 := "#475258" // Active/Selected UI element background
darkStep6 := "#7a8478" // Subtle borders and separators
darkStep7 := "#859289" // UI element border and focus rings
darkStep8 := "#9da9a0" // Hovered UI element border
darkStep9 := "#a7c080" // Solid backgrounds
darkStep10 := "#83c092" // Hovered solid backgrounds
darkStep11 := "#7a8478" // Low-contrast text
darkStep12 := "#d3c6aa" // High-contrast text
// Dark mode accent colors
darkPrimary := darkStep9 // Primary uses step 9 (green)
darkSecondary := "#7fbbb3" // Secondary (blue)
darkAccent := "#d699b6" // Accent (purple)
darkRed := "#e67e80" // Error (red)
darkOrange := "#e69875" // Warning (orange)
darkGreen := "#a7c080" // Success (green)
darkCyan := "#83c092" // Info (aqua)
darkYellow := "#dbbc7f" // Emphasized text
// Light mode colors for the Everforest:Light Medium contrast palette
lightStep1 := "#fdf6e3" // App background
lightStep2 := "#efebd4" // Subtle background
lightStep3 := "#f4f0d9" // UI element background
lightStep4 := "#efebd4" // Hovered UI element background
lightStep5 := "#e6e2cc" // Active/Selected UI element background
lightStep6 := "#a6b0a0" // Subtle borders and separators
lightStep7 := "#939f91" // UI element border and focus rings
lightStep8 := "#829181" // Hovered UI element border
lightStep9 := "#8da101" // Solid backgrounds
lightStep10 := "#35a77c" // Hovered solid backgrounds
lightStep11 := "#a6b0a0" // Low-contrast text
lightStep12 := "#5c6a72" // High-contrast text
// Light mode accent colors
lightPrimary := lightStep9 // Primary uses step 9 (green)
lightSecondary := "#3a94c5" // Secondary blue
lightAccent := "#df69ba" // Accent purple
lightRed := "#f85552" // Error red
lightOrange := "#f57d26" // Warning orange
lightGreen := "#8da101" // Success green
lightCyan := "#35a77c" // Info aqua
lightYellow := "#dfa000" // Emphasized text
// Unused variables. These could be used for hover states
_ = darkStep4
_ = darkStep5
_ = darkStep10
_ = lightStep4
_ = lightStep5
_ = lightStep10
theme := &EverforestTheme{}
// Base colors
theme.PrimaryColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkPrimary),
Light: lipgloss.Color(lightPrimary),
}
theme.SecondaryColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkSecondary),
Light: lipgloss.Color(lightSecondary),
}
theme.AccentColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkAccent),
Light: lipgloss.Color(lightAccent),
}
// Status colors
theme.ErrorColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkRed),
Light: lipgloss.Color(lightRed),
}
theme.WarningColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkOrange),
Light: lipgloss.Color(lightOrange),
}
theme.SuccessColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkGreen),
Light: lipgloss.Color(lightGreen),
}
theme.InfoColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkCyan),
Light: lipgloss.Color(lightCyan),
}
// Text colors
theme.TextColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep12),
Light: lipgloss.Color(lightStep12),
}
theme.TextMutedColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep11),
Light: lipgloss.Color(lightStep11),
}
// Background colors
theme.BackgroundColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep1),
Light: lipgloss.Color(lightStep1),
}
theme.BackgroundSubtleColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep2),
Light: lipgloss.Color(lightStep2),
}
theme.BackgroundElementColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep3),
Light: lipgloss.Color(lightStep3),
}
// Border colors
theme.BorderColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep7),
Light: lipgloss.Color(lightStep7),
}
theme.BorderActiveColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep8),
Light: lipgloss.Color(lightStep8),
}
theme.BorderSubtleColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep6),
Light: lipgloss.Color(lightStep6),
}
// Diff view colors
theme.DiffAddedColor = compat.AdaptiveColor{
Dark: lipgloss.Color("#A7C080"),
Light: lipgloss.Color("#8DA101"),
}
theme.DiffRemovedColor = compat.AdaptiveColor{
Dark: lipgloss.Color("#E67E80"),
Light: lipgloss.Color("#F85552"),
}
theme.DiffContextColor = compat.AdaptiveColor{
Dark: lipgloss.Color("#7A8478"),
Light: lipgloss.Color("#A6B0A0"),
}
theme.DiffHunkHeaderColor = compat.AdaptiveColor{
Dark: lipgloss.Color("#859289"),
Light: lipgloss.Color("#939F91"),
}
theme.DiffHighlightAddedColor = compat.AdaptiveColor{
Dark: lipgloss.Color("#A7C080"),
Light: lipgloss.Color("#8DA101"),
}
theme.DiffHighlightRemovedColor = compat.AdaptiveColor{
Dark: lipgloss.Color("#E67E80"),
Light: lipgloss.Color("#F85552"),
}
theme.DiffAddedBgColor = compat.AdaptiveColor{
Dark: lipgloss.Color("#425047"),
Light: lipgloss.Color("#F0F1D2"),
}
theme.DiffRemovedBgColor = compat.AdaptiveColor{
Dark: lipgloss.Color("#543A48"),
Light: lipgloss.Color("#FBE3DA"),
}
theme.DiffContextBgColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep2),
Light: lipgloss.Color(lightStep2),
}
theme.DiffLineNumberColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep3),
Light: lipgloss.Color(lightStep3),
}
theme.DiffAddedLineNumberBgColor = compat.AdaptiveColor{
Dark: lipgloss.Color("#3A4A3F"),
Light: lipgloss.Color("#E8F2D1"),
}
theme.DiffRemovedLineNumberBgColor = compat.AdaptiveColor{
Dark: lipgloss.Color("#4A3A40"),
Light: lipgloss.Color("#FBDAD2"),
}
// Markdown colors
theme.MarkdownTextColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep12),
Light: lipgloss.Color(lightStep12),
}
theme.MarkdownHeadingColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkSecondary),
Light: lipgloss.Color(lightSecondary),
}
theme.MarkdownLinkColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkPrimary),
Light: lipgloss.Color(lightPrimary),
}
theme.MarkdownLinkTextColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkCyan),
Light: lipgloss.Color(lightCyan),
}
theme.MarkdownCodeColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkGreen),
Light: lipgloss.Color(lightGreen),
}
theme.MarkdownBlockQuoteColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkYellow),
Light: lipgloss.Color(lightYellow),
}
theme.MarkdownEmphColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkYellow),
Light: lipgloss.Color(lightYellow),
}
theme.MarkdownStrongColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkAccent),
Light: lipgloss.Color(lightAccent),
}
theme.MarkdownHorizontalRuleColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep11),
Light: lipgloss.Color(lightStep11),
}
theme.MarkdownListItemColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkPrimary),
Light: lipgloss.Color(lightPrimary),
}
theme.MarkdownListEnumerationColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkCyan),
Light: lipgloss.Color(lightCyan),
}
theme.MarkdownImageColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkPrimary),
Light: lipgloss.Color(lightPrimary),
}
theme.MarkdownImageTextColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkCyan),
Light: lipgloss.Color(lightCyan),
}
theme.MarkdownCodeBlockColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep12),
Light: lipgloss.Color(lightStep12),
}
// Syntax highlighting colors
theme.SyntaxCommentColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep11),
Light: lipgloss.Color(lightStep11),
}
theme.SyntaxKeywordColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkPrimary),
Light: lipgloss.Color(lightPrimary),
}
theme.SyntaxFunctionColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkSecondary),
Light: lipgloss.Color(lightSecondary),
}
theme.SyntaxVariableColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkRed),
Light: lipgloss.Color(lightRed),
}
theme.SyntaxStringColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkGreen),
Light: lipgloss.Color(lightGreen),
}
theme.SyntaxNumberColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkAccent),
Light: lipgloss.Color(lightAccent),
}
theme.SyntaxTypeColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkYellow),
Light: lipgloss.Color(lightYellow),
}
theme.SyntaxOperatorColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkCyan),
Light: lipgloss.Color(lightCyan),
}
theme.SyntaxPunctuationColor = compat.AdaptiveColor{
Dark: lipgloss.Color(darkStep12),
Light: lipgloss.Color(lightStep12),
}
return theme
}
func init() {
// Register the Everforest theme with the theme manager
RegisterTheme("everforest", NewEverforestTheme())
}
+1 -1
View File
@@ -27,7 +27,7 @@
"sharp": "0.32.5",
"shiki": "3.4.2",
"solid-js": "1.9.7",
"toolbeam-docs-theme": "0.2.4"
"toolbeam-docs-theme": "0.3.0"
},
"devDependencies": {
"@types/node": "catalog:",
+3 -3
View File
@@ -18,9 +18,9 @@ function CodeBlock(props: CodeBlockProps) {
const [local, rest] = splitProps(props, ["code", "lang", "onRendered"])
let containerRef!: HTMLDivElement
const [html] = createResource(async () => {
return (await codeToHtml(local.code, {
lang: local.lang || "text",
const [html] = createResource(() => [local.code, local.lang], async ([code, lang]) => {
return (await codeToHtml(code || "", {
lang: lang || "text",
themes: {
light: "github-light",
dark: "github-dark",
-6
View File
@@ -54,9 +54,3 @@ const links = config.social || [];
}
}
</style>
<style is:global>
body > div.page > header {
border-color: var(--sl-color-divider);
}
</style>
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,7 @@
.codeblock {
pre {
--shiki-dark-bg: var(--sl-color-bg) !important;
background-color: var(--sl-color-bg) !important;
--shiki-dark-bg: var(--sl-color-bg-surface) !important;
background-color: var(--sl-color-bg-surface) !important;
}
}
@@ -12,6 +12,10 @@
margin-bottom: 1rem;
}
strong {
font-weight: 600;
}
ol {
list-style-position: inside;
padding-left: 0.75rem;
+53 -68
View File
@@ -5,6 +5,10 @@
gap: 2.5rem;
line-height: 1;
--sm-tool-width: 28rem;
--md-tool-width: 40rem;
--lg-tool-width: 56rem;
--term-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2060%2016'%20preserveAspectRatio%3D'xMidYMid%20meet'%3E%3Ccircle%20cx%3D'8'%20cy%3D'8'%20r%3D'8'%2F%3E%3Ccircle%20cx%3D'30'%20cy%3D'8'%20r%3D'8'%2F%3E%3Ccircle%20cx%3D'52'%20cy%3D'8'%20r%3D'8'%2F%3E%3C%2Fsvg%3E");
}
@@ -37,7 +41,7 @@
[data-element-label] {
text-transform: uppercase;
letter-spacing: 0.05em;
letter-spacing: -0.5px;
color: var(--sl-color-text-dimmed);
}
@@ -164,30 +168,6 @@
}
}
}
[data-section="system-prompt"] {
display: flex;
gap: 0.3125rem;
[data-section="icon"] {
flex: 0 0 auto;
color: var(--sl-color-text-dimmed);
opacity: 0.85;
svg {
display: block;
}
}
[data-section="content"] {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
button {
line-height: 1rem;
font-size: 0.875rem;
}
}
}
.parts {
@@ -227,6 +207,7 @@
}
& > [data-section="content"] {
flex: 1 1 auto;
min-width: 0;
padding: 0 0 0.375rem;
display: flex;
@@ -236,20 +217,28 @@
[data-part-tool-body] {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.375rem;
}
span[data-part-title] {
[data-part-title] {
line-height: 18px;
font-size: 0.75rem;
font-size: 0.875rem;
color: var(--sl-color-text-secondary);
max-wdith: var(--sm-tool-width);
b {
word-break: break-all;
font-weight: 500;
display: flex;
align-items: flex-start;
gap: 0.375rem;
span[data-element-label] {
color: var(--sl-color-text-secondary);
}
&[data-size="md"] {
font-size: 0.875rem;
b {
color: var(--sl-color-text);
word-break: break-all;
font-weight: 500;
}
}
@@ -267,7 +256,7 @@
display: inline-grid;
align-items: center;
grid-template-columns: max-content max-content minmax(0, 1fr);
max-width: 100%;
max-width: var(--md-tool-width);
gap: 0.25rem 0.375rem;
& > div:nth-child(3n + 1) {
@@ -279,16 +268,14 @@
& > div:nth-child(3n + 2),
& > div:nth-child(3n + 3) {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 0.75rem;
line-height: 1.5;
}
& > div:nth-child(3n + 3) {
padding-left: 0.125rem;
color: var(--sl-color-text-dimmed);
word-break: break-word;
color: var(--sl-color-text-secondary);
}
}
@@ -302,6 +289,11 @@
font-size: 0.75rem;
}
}
[data-part-tool-edit] {
width: 100%;
max-width: var(--lg-tool-width);
}
}
}
@@ -325,16 +317,6 @@
& > [data-section="content"] > [data-part-tool-body] {
gap: 0.5rem;
}
[data-part-title] {
display: flex;
align-items: flex-start;
gap: 0.5rem;
b {
color: var(--sl-color-text);
word-break: break-all;
}
}
}
[data-part-type="tool-grep"] {
@@ -342,16 +324,6 @@
> [data-section="content"] > [data-part-tool-body] {
gap: 0.5rem;
}
[data-part-title] {
display: flex;
align-items: flex-start;
gap: 0.5rem;
b {
color: var(--sl-color-text);
word-break: break-all;
}
}
}
[data-part-type="tool-write"],
@@ -359,7 +331,9 @@
[data-part-type="tool-fetch"] {
[data-part-tool-result] {
[data-part-tool-code] {
width: var(--md-tool-width);
border: 1px solid var(--sl-color-divider);
background-color: var(--sl-color-bg-surface);
border-radius: 0.25rem;
padding: 0.5rem calc(0.5rem + 3px);
@@ -372,8 +346,6 @@
}
}
}
[data-part-type="tool-edit"] {
}
}
.message-text {
@@ -384,6 +356,8 @@
flex-direction: column;
align-items: flex-start;
gap: 1rem;
align-self: flex-start;
max-width: var(--md-tool-width);
&[data-size="sm"] {
pre {
@@ -411,7 +385,7 @@
font-size: 0.75rem;
}
&[data-highlight="true"] {
&[data-invert="true"] {
background-color: var(--sl-color-blue-high);
pre {
@@ -428,6 +402,10 @@
}
}
&[data-highlight="true"] {
background-color: var(--sl-color-blue-low);
}
&[data-expanded="true"] {
pre {
display: block;
@@ -450,6 +428,7 @@
gap: 0.5rem;
& > [data-section="body"] {
width: var(--sm-tool-width);
border: 1px solid var(--sl-color-divider);
border-radius: 0.25rem;
max-width: 100%;
@@ -460,7 +439,7 @@
width: 100%;
height: 1.625rem;
text-align: center;
padding: 0 0.75rem 0 3.25rem;
padding: 0 3.25rem;
& > span {
max-width: min(100%, 140ch);
@@ -491,12 +470,10 @@
[data-section="content"] {
padding: 0.5rem calc(0.5rem + 3px);
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
pre {
--shiki-dark-bg: var(--sl-color-bg) !important;
background-color: var(--sl-color-bg) !important;
line-height: 1.6;
font-size: 0.75rem;
white-space: pre-wrap;
@@ -533,6 +510,8 @@
flex-direction: column;
align-items: flex-start;
gap: 1rem;
align-self: flex-start;
max-width: var(--md-tool-width);
button {
flex: 0 0 auto;
@@ -540,6 +519,10 @@
font-size: 0.75rem;
}
&[data-highlight="true"] {
background-color: var(--sl-color-blue-low);
}
&[data-expanded="true"] {
[data-elment-markdown] {
display: block;
@@ -566,6 +549,7 @@
list-style-type: none;
padding: 0;
margin: 0;
width: var(--sm-tool-width);
border: 1px solid var(--sl-color-divider);
border-radius: 0.25rem;
@@ -577,6 +561,7 @@
padding: 0.375rem 0.625rem 0.375rem 1.75rem;
border-bottom: 1px solid var(--sl-color-divider);
line-height: 1.5;
word-break: break-word;
&:last-child {
border-bottom: none;
@@ -614,9 +599,9 @@
}
}
&[data-status="completed"] {
color: var(--sl-color-text-dimmed);
color: var(--sl-color-text-secondary);
& > span { border-color: var(--sl-color-hairline); }
& > span { border-color: var(--sl-color-green-low); }
& > span::before {
content: "";
position: absolute;
@@ -624,7 +609,7 @@
left: 2px;
width: calc(0.75rem - 2px - 4px);
height: calc(0.75rem - 2px - 4px);
box-shadow: inset 1rem 1rem var(--sl-color-divider);
box-shadow: inset 1rem 1rem var(--sl-color-green);
transform-origin: bottom left;
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);