Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9356b6c35a | ||
|
|
29a6603a89 | ||
|
|
a454ba8895 | ||
|
|
5eae7aef0e | ||
|
|
1031bceef7 | ||
|
|
653965ef59 | ||
|
|
ca0ea3f94d | ||
|
|
98bd5109c2 | ||
|
|
78f65e4789 | ||
|
|
75dd2f75aa | ||
|
|
fe86e58bbb | ||
|
|
ae339015fc | ||
|
|
cce2e4ad75 | ||
|
|
a1ce35c208 | ||
|
|
69d6709a19 | ||
|
|
52ec134b2d | ||
|
|
db88bede05 | ||
|
|
d4d218d7d6 | ||
|
|
3e086e3ab9 | ||
|
|
2f5faae34b | ||
|
|
e3ad6a0698 | ||
|
|
b536b45536 | ||
|
|
81c245035f | ||
|
|
dda7059e57 | ||
|
|
0cca75ef48 | ||
|
|
ee1f55dbe2 | ||
|
|
2fa50190e5 | ||
|
|
662b6b1258 | ||
|
|
f0dbe40522 | ||
|
|
41c54f629c | ||
|
|
4503201b15 | ||
|
|
120151ee38 | ||
|
|
4d2e556713 |
@@ -1,8 +1,10 @@
|
||||
name: release
|
||||
name: publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- dontlook
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
@@ -13,7 +15,7 @@ permissions:
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -24,7 +26,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ">=1.23.2"
|
||||
go-version: ">=1.24.0"
|
||||
cache: true
|
||||
cache-dependency-path: go.sum
|
||||
|
||||
@@ -32,8 +34,23 @@ jobs:
|
||||
with:
|
||||
bun-version: 1.2.16
|
||||
|
||||
- run: ./script/publish.ts
|
||||
- name: Setup SSH for AUR
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
bun install
|
||||
if [ "${{ startsWith(github.ref, 'refs/tags/') }}" = "true" ]; then
|
||||
./script/publish.ts
|
||||
else
|
||||
./script/publish.ts --snapshot
|
||||
fi
|
||||
working-directory: ./packages/opencode
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||
AUR_KEY: ${{ secrets.AUR_KEY }}
|
||||
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
4
bun.lock
@@ -425,7 +425,7 @@
|
||||
|
||||
"@types/babel__traverse": ["@types/babel__traverse@7.20.7", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.2.15", "", { "dependencies": { "bun-types": "1.2.15" } }, "sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA=="],
|
||||
"@types/bun": ["@types/bun@1.2.16", "", { "dependencies": { "bun-types": "1.2.16" } }, "sha512-1aCZJ/6nSiViw339RsaNhkNoEloLaPzZhxMOYEa7OzRzO41IGg5n/7I43/ZIAW/c+Q6cT12Vf7fOZOoVIzb5BQ=="],
|
||||
|
||||
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
||||
|
||||
@@ -561,7 +561,7 @@
|
||||
|
||||
"buffer": ["buffer@4.9.2", "", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" } }, "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.15", "", { "dependencies": { "@types/node": "*" } }, "sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w=="],
|
||||
"bun-types": ["bun-types@1.2.16", "", { "dependencies": { "@types/node": "*" } }, "sha512-ciXLrHV4PXax9vHvUrkvun9VPVGOVwbbbBF/Ev1cXz12lyEZMoJpIJABOfPcN9gDJRaiKF9MVbSygLg4NXu3/A=="],
|
||||
|
||||
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ for (const [os, arch] of targets) {
|
||||
),
|
||||
)
|
||||
if (!dry)
|
||||
await $`cd dist/${name} && npm publish --access public --tag ${npmTag}`
|
||||
await $`cd dist/${name} && bun publish --access public --tag ${npmTag}`
|
||||
optionalDependencies[name] = version
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write(
|
||||
),
|
||||
)
|
||||
if (!dry)
|
||||
await $`cd ./dist/${pkg.name} && npm publish --access public --tag ${npmTag}`
|
||||
await $`cd ./dist/${pkg.name} && bun publish --access public --tag ${npmTag}`
|
||||
|
||||
if (!snapshot) {
|
||||
// Github Release
|
||||
@@ -144,18 +144,11 @@ if (!snapshot) {
|
||||
)
|
||||
|
||||
await $`rm -rf ./dist/aur-opencode-bin`
|
||||
const gitEnv: Record<string, string> = process.env["AUR_KEY"]
|
||||
? { GIT_SSH_COMMAND: `ssh -i ${process.env["AUR_KEY"]}` }
|
||||
: {}
|
||||
|
||||
await $`git clone ssh://aur@aur.archlinux.org/opencode-bin.git ./dist/aur-opencode-bin`.env(
|
||||
gitEnv,
|
||||
)
|
||||
await $`git clone ssh://aur@aur.archlinux.org/opencode-bin.git ./dist/aur-opencode-bin`
|
||||
await Bun.file("./dist/aur-opencode-bin/PKGBUILD").write(pkgbuild)
|
||||
await $`cd ./dist/aur-opencode-bin && makepkg --printsrcinfo > .SRCINFO`
|
||||
await $`cd ./dist/aur-opencode-bin && git add PKGBUILD .SRCINFO`.env(gitEnv)
|
||||
await $`cd ./dist/aur-opencode-bin && git commit -m "Update to v${version}"`.env(
|
||||
gitEnv,
|
||||
)
|
||||
if (!dry) await $`cd ./dist/aur-opencode-bin && git push`.env(gitEnv)
|
||||
await $`cd ./dist/aur-opencode-bin && git add PKGBUILD .SRCINFO`
|
||||
await $`cd ./dist/aur-opencode-bin && git commit -m "Update to v${version}"`
|
||||
if (!dry) await $`cd ./dist/aur-opencode-bin && git push`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import path from "path"
|
||||
import { Log } from "../util/log"
|
||||
export namespace BunProc {
|
||||
const log = Log.create({ service: "bun" })
|
||||
@@ -13,6 +12,8 @@ export namespace BunProc {
|
||||
})
|
||||
const result = Bun.spawn([which(), ...cmd], {
|
||||
...options,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...process.env,
|
||||
...options?.env,
|
||||
@@ -21,15 +22,12 @@ export namespace BunProc {
|
||||
})
|
||||
const code = await result.exited
|
||||
if (code !== 0) {
|
||||
console.error(result.stderr?.toString("utf8") ?? "")
|
||||
throw new Error(`Command failed with exit code ${result.exitCode}`)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function which() {
|
||||
return process.argv0 !== "bun"
|
||||
? path.resolve(process.cwd(), process.argv0)
|
||||
: "bun"
|
||||
return process.execPath
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ export const RunCommand = {
|
||||
}
|
||||
|
||||
Bus.subscribe(Message.Event.PartUpdated, async (evt) => {
|
||||
if (evt.properties.sessionID !== session.id) return
|
||||
const part = evt.properties.part
|
||||
const message = await Session.getMessage(
|
||||
evt.properties.sessionID,
|
||||
|
||||
@@ -35,12 +35,19 @@ const cli = yargs(hideBin(process.argv))
|
||||
})
|
||||
.usage("\n" + UI.logo())
|
||||
.command({
|
||||
command: "$0",
|
||||
command: "$0 [project]",
|
||||
describe: "Start OpenCode TUI",
|
||||
builder: (yargs) =>
|
||||
yargs.positional("project", {
|
||||
type: "string",
|
||||
describe: "path to start opencode in",
|
||||
}),
|
||||
handler: async (args) => {
|
||||
while (true) {
|
||||
const cwd = args.project ? path.resolve(args.project) : process.cwd()
|
||||
process.chdir(cwd)
|
||||
const result = await App.provide(
|
||||
{ cwd: process.cwd(), version: VERSION },
|
||||
{ cwd, version: VERSION },
|
||||
async () => {
|
||||
const providers = await Provider.list()
|
||||
if (Object.keys(providers).length === 0) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import { AuthAnthropic } from "../auth/anthropic"
|
||||
import { ModelsDev } from "./models"
|
||||
import { NamedError } from "../util/error"
|
||||
import { Auth } from "../auth"
|
||||
import { TaskTool } from "../tool/task"
|
||||
|
||||
export namespace Provider {
|
||||
const log = Log.create({ service: "provider" })
|
||||
@@ -298,6 +299,7 @@ export namespace Provider {
|
||||
// MultiEditTool,
|
||||
WriteTool,
|
||||
TodoWriteTool,
|
||||
TaskTool,
|
||||
TodoReadTool,
|
||||
]
|
||||
const TOOL_MAPPING: Record<string, Tool.Info[]> = {
|
||||
|
||||
@@ -12,24 +12,21 @@ import {
|
||||
tool,
|
||||
type Tool as AITool,
|
||||
type LanguageModelUsage,
|
||||
type UIMessage,
|
||||
} from "ai"
|
||||
import { z, ZodSchema } from "zod"
|
||||
import { Decimal } from "decimal.js"
|
||||
|
||||
import PROMPT_ANTHROPIC from "./prompt/anthropic.txt"
|
||||
import PROMPT_ANTHROPIC_SPOOF from "./prompt/anthropic_spoof.txt"
|
||||
import PROMPT_TITLE from "./prompt/title.txt"
|
||||
import PROMPT_SUMMARIZE from "./prompt/summarize.txt"
|
||||
import PROMPT_INITIALIZE from "../session/prompt/initialize.txt"
|
||||
|
||||
import { Share } from "../share/share"
|
||||
import { Message } from "./message"
|
||||
import { Bus } from "../bus"
|
||||
import { Provider } from "../provider/provider"
|
||||
import { SessionContext } from "./context"
|
||||
import { ListTool } from "../tool/ls"
|
||||
import { MCP } from "../mcp"
|
||||
import { NamedError } from "../util/error"
|
||||
import type { Tool } from "../tool/tool"
|
||||
import { SystemPrompt } from "./system"
|
||||
|
||||
export namespace Session {
|
||||
const log = Log.create({ service: "session" })
|
||||
@@ -37,6 +34,7 @@ export namespace Session {
|
||||
export const Info = z
|
||||
.object({
|
||||
id: Identifier.schema("session"),
|
||||
parentID: Identifier.schema("session").optional(),
|
||||
share: z
|
||||
.object({
|
||||
secret: z.string(),
|
||||
@@ -79,10 +77,13 @@ export namespace Session {
|
||||
}
|
||||
})
|
||||
|
||||
export async function create() {
|
||||
export async function create(parentID?: string) {
|
||||
const result: Info = {
|
||||
id: Identifier.descending("session"),
|
||||
title: "New Session - " + new Date().toISOString(),
|
||||
parentID,
|
||||
title:
|
||||
(parentID ? "Child session - " : "New Session - ") +
|
||||
new Date().toISOString(),
|
||||
time: {
|
||||
created: Date.now(),
|
||||
updated: Date.now(),
|
||||
@@ -91,11 +92,12 @@ export namespace Session {
|
||||
log.info("created", result)
|
||||
state().sessions.set(result.id, result)
|
||||
await Storage.writeJSON("session/info/" + result.id, result)
|
||||
share(result.id).then((share) => {
|
||||
update(result.id, (draft) => {
|
||||
draft.share = share
|
||||
if (!result.parentID)
|
||||
share(result.id).then((share) => {
|
||||
update(result.id, (draft) => {
|
||||
draft.share = share
|
||||
})
|
||||
})
|
||||
})
|
||||
Bus.publish(Event.Updated, {
|
||||
info: result,
|
||||
})
|
||||
@@ -186,12 +188,16 @@ export namespace Session {
|
||||
providerID: string
|
||||
modelID: string
|
||||
parts: Message.Part[]
|
||||
system?: string[]
|
||||
tools?: Tool.Info[]
|
||||
}) {
|
||||
const l = log.clone().tag("session", input.sessionID)
|
||||
l.info("chatting")
|
||||
const model = await Provider.getModel(input.providerID, input.modelID)
|
||||
let msgs = await messages(input.sessionID)
|
||||
const previous = msgs.at(-1)
|
||||
|
||||
// auto summarize if too long
|
||||
if (previous?.metadata.assistant) {
|
||||
const tokens =
|
||||
previous.metadata.assistant.tokens.input +
|
||||
@@ -214,95 +220,26 @@ export namespace Session {
|
||||
const lastSummary = msgs.findLast(
|
||||
(msg) => msg.metadata.assistant?.summary === true,
|
||||
)
|
||||
if (lastSummary)
|
||||
msgs = msgs.filter(
|
||||
(msg) => msg.role === "system" || msg.id >= lastSummary.id,
|
||||
)
|
||||
if (lastSummary) msgs = msgs.filter((msg) => msg.id >= lastSummary.id)
|
||||
|
||||
if (msgs.length === 0) {
|
||||
const app = App.info()
|
||||
if (input.providerID === "anthropic") {
|
||||
const claude: Message.Info = {
|
||||
id: Identifier.ascending("message"),
|
||||
role: "system",
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: PROMPT_ANTHROPIC_SPOOF.trim(),
|
||||
},
|
||||
],
|
||||
metadata: {
|
||||
sessionID: input.sessionID,
|
||||
time: {
|
||||
created: Date.now(),
|
||||
},
|
||||
tool: {},
|
||||
},
|
||||
}
|
||||
await updateMessage(claude)
|
||||
msgs.push(claude)
|
||||
}
|
||||
const system: Message.Info = {
|
||||
id: Identifier.ascending("message"),
|
||||
role: "system",
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: PROMPT_ANTHROPIC,
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
text: [
|
||||
`Here is some useful information about the environment you are running in:`,
|
||||
`<env>`,
|
||||
`Working directory: ${app.path.cwd}`,
|
||||
`Is directory a git repo: ${app.git ? "yes" : "no"}`,
|
||||
`Platform: ${process.platform}`,
|
||||
`Today's date: ${new Date().toISOString()}`,
|
||||
`</env>`,
|
||||
`<project>`,
|
||||
`${app.git ? await ListTool.execute({ path: app.path.cwd, ignore: [] }, { sessionID: input.sessionID, abort: abort.signal }).then((x) => x.output) : ""}`,
|
||||
`</project>`,
|
||||
].join("\n"),
|
||||
},
|
||||
],
|
||||
metadata: {
|
||||
sessionID: input.sessionID,
|
||||
time: {
|
||||
created: Date.now(),
|
||||
},
|
||||
tool: {},
|
||||
},
|
||||
}
|
||||
const context = await SessionContext.find()
|
||||
if (context) {
|
||||
system.parts.push({
|
||||
type: "text",
|
||||
text: context,
|
||||
})
|
||||
}
|
||||
msgs.push(system)
|
||||
const app = App.info()
|
||||
const session = await get(input.sessionID)
|
||||
if (msgs.length === 0 && !session.parentID) {
|
||||
generateText({
|
||||
maxOutputTokens: 20,
|
||||
messages: convertToModelMessages([
|
||||
{
|
||||
role: "system",
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: PROMPT_ANTHROPIC_SPOOF.trim(),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "system",
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: PROMPT_TITLE,
|
||||
},
|
||||
],
|
||||
},
|
||||
...SystemPrompt.title(input.providerID).map(
|
||||
(x): UIMessage => ({
|
||||
id: Identifier.ascending("message"),
|
||||
role: "system",
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: x,
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
{
|
||||
role: "user",
|
||||
parts: input.parts,
|
||||
@@ -317,7 +254,6 @@ export namespace Session {
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
await updateMessage(system)
|
||||
}
|
||||
const msg: Message.Info = {
|
||||
role: "user",
|
||||
@@ -334,12 +270,21 @@ export namespace Session {
|
||||
await updateMessage(msg)
|
||||
msgs.push(msg)
|
||||
|
||||
const system = input.system ?? SystemPrompt.provider(input.providerID)
|
||||
system.push(...(await SystemPrompt.environment(input.sessionID)))
|
||||
system.push(...(await SystemPrompt.custom()))
|
||||
|
||||
const next: Message.Info = {
|
||||
id: Identifier.ascending("message"),
|
||||
role: "assistant",
|
||||
parts: [],
|
||||
metadata: {
|
||||
assistant: {
|
||||
system,
|
||||
path: {
|
||||
cwd: app.path.cwd,
|
||||
root: app.path.root,
|
||||
},
|
||||
cost: 0,
|
||||
tokens: {
|
||||
input: 0,
|
||||
@@ -358,6 +303,7 @@ export namespace Session {
|
||||
}
|
||||
await updateMessage(next)
|
||||
const tools: Record<string, AITool> = {}
|
||||
|
||||
for (const item of await Provider.tools(input.providerID)) {
|
||||
tools[item.id.replaceAll(".", "_")] = tool({
|
||||
id: item.id as any,
|
||||
@@ -369,6 +315,7 @@ export namespace Session {
|
||||
const result = await item.execute(args, {
|
||||
sessionID: input.sessionID,
|
||||
abort: abort.signal,
|
||||
messageID: next.id,
|
||||
})
|
||||
next.metadata!.tool![opts.toolCallId] = {
|
||||
...result.metadata,
|
||||
@@ -395,6 +342,7 @@ export namespace Session {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
for (const [key, item] of Object.entries(await MCP.tools())) {
|
||||
const execute = item.execute
|
||||
if (!execute) continue
|
||||
@@ -576,7 +524,21 @@ export namespace Session {
|
||||
toolCallStreaming: true,
|
||||
abortSignal: abort.signal,
|
||||
stopWhen: stepCountIs(1000),
|
||||
messages: convertToModelMessages(msgs),
|
||||
messages: convertToModelMessages([
|
||||
...system.map(
|
||||
(x): UIMessage => ({
|
||||
id: Identifier.ascending("message"),
|
||||
role: "system",
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: x,
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
...msgs,
|
||||
]),
|
||||
temperature: model.info.id === "codex-mini-latest" ? undefined : 0,
|
||||
tools: {
|
||||
...(await MCP.tools()),
|
||||
@@ -618,10 +580,11 @@ export namespace Session {
|
||||
const lastSummary = msgs.findLast(
|
||||
(msg) => msg.metadata.assistant?.summary === true,
|
||||
)?.id
|
||||
const filtered = msgs.filter(
|
||||
(msg) => msg.role !== "system" && (!lastSummary || msg.id >= lastSummary),
|
||||
)
|
||||
const filtered = msgs.filter((msg) => !lastSummary || msg.id >= lastSummary)
|
||||
const model = await Provider.getModel(input.providerID, input.modelID)
|
||||
const app = App.info()
|
||||
const system = SystemPrompt.summarize(input.providerID)
|
||||
|
||||
const next: Message.Info = {
|
||||
id: Identifier.ascending("message"),
|
||||
role: "assistant",
|
||||
@@ -630,6 +593,11 @@ export namespace Session {
|
||||
tool: {},
|
||||
sessionID: input.sessionID,
|
||||
assistant: {
|
||||
system,
|
||||
path: {
|
||||
cwd: app.path.cwd,
|
||||
root: app.path.root,
|
||||
},
|
||||
summary: true,
|
||||
cost: 0,
|
||||
modelID: input.modelID,
|
||||
@@ -650,15 +618,18 @@ export namespace Session {
|
||||
abortSignal: abort.signal,
|
||||
model: model.language,
|
||||
messages: convertToModelMessages([
|
||||
{
|
||||
role: "system",
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: PROMPT_SUMMARIZE,
|
||||
},
|
||||
],
|
||||
},
|
||||
...system.map(
|
||||
(x): UIMessage => ({
|
||||
id: Identifier.ascending("message"),
|
||||
role: "system",
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: x,
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
...filtered,
|
||||
{
|
||||
role: "user",
|
||||
|
||||
@@ -133,7 +133,7 @@ export namespace Message {
|
||||
export const Info = z
|
||||
.object({
|
||||
id: z.string(),
|
||||
role: z.enum(["system", "user", "assistant"]),
|
||||
role: z.enum(["user", "assistant"]),
|
||||
parts: z.array(Part),
|
||||
metadata: z.object({
|
||||
time: z.object({
|
||||
@@ -161,8 +161,13 @@ export namespace Message {
|
||||
),
|
||||
assistant: z
|
||||
.object({
|
||||
system: z.string().array(),
|
||||
modelID: z.string(),
|
||||
providerID: z.string(),
|
||||
path: z.object({
|
||||
cwd: z.string(),
|
||||
root: z.string(),
|
||||
}),
|
||||
cost: z.number(),
|
||||
summary: z.boolean().optional(),
|
||||
tokens: z.object({
|
||||
|
||||
75
packages/opencode/src/session/system.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { App } from "../app/app"
|
||||
import { ListTool } from "../tool/ls"
|
||||
import { Filesystem } from "../util/filesystem"
|
||||
|
||||
import PROMPT_ANTHROPIC from "./prompt/anthropic.txt"
|
||||
import PROMPT_ANTHROPIC_SPOOF from "./prompt/anthropic_spoof.txt"
|
||||
import PROMPT_SUMMARIZE from "./prompt/summarize.txt"
|
||||
import PROMPT_TITLE from "./prompt/title.txt"
|
||||
|
||||
export namespace SystemPrompt {
|
||||
export function provider(providerID: string) {
|
||||
const result = []
|
||||
switch (providerID) {
|
||||
case "anthropic":
|
||||
result.push(PROMPT_ANTHROPIC_SPOOF.trim())
|
||||
result.push(PROMPT_ANTHROPIC)
|
||||
break
|
||||
default:
|
||||
result.push(PROMPT_ANTHROPIC)
|
||||
break
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export async function environment(sessionID: string) {
|
||||
const app = App.info()
|
||||
return [
|
||||
[
|
||||
`Here is some useful information about the environment you are running in:`,
|
||||
`<env>`,
|
||||
` Working directory: ${app.path.cwd}`,
|
||||
` Is directory a git repo: ${app.git ? "yes" : "no"}`,
|
||||
` Platform: ${process.platform}`,
|
||||
` Today's date: ${new Date().toDateString()}`,
|
||||
`</env>`,
|
||||
`<project>`,
|
||||
` ${app.git ? await ListTool.execute({ path: app.path.cwd, ignore: [] }, { sessionID: sessionID, messageID: "", abort: AbortSignal.any([]) }).then((x) => x.output) : ""}`,
|
||||
`</project>`,
|
||||
].join("\n"),
|
||||
]
|
||||
}
|
||||
|
||||
const CUSTOM_FILES = [
|
||||
"AGENTS.md",
|
||||
"CLAUDE.md",
|
||||
"CONTEXT.md", // deprecated
|
||||
]
|
||||
export async function custom() {
|
||||
const { cwd, root } = App.info().path
|
||||
const found = []
|
||||
for (const item of CUSTOM_FILES) {
|
||||
const matches = await Filesystem.findUp(item, cwd, root)
|
||||
found.push(...matches.map((x) => Bun.file(x).text()))
|
||||
}
|
||||
return Promise.all(found)
|
||||
}
|
||||
|
||||
export function summarize(providerID: string) {
|
||||
switch (providerID) {
|
||||
case "anthropic":
|
||||
return [PROMPT_ANTHROPIC_SPOOF.trim(), PROMPT_SUMMARIZE]
|
||||
default:
|
||||
return [PROMPT_SUMMARIZE]
|
||||
}
|
||||
}
|
||||
|
||||
export function title(providerID: string) {
|
||||
switch (providerID) {
|
||||
case "anthropic":
|
||||
return [PROMPT_ANTHROPIC_SPOOF.trim(), PROMPT_TITLE]
|
||||
default:
|
||||
return [PROMPT_TITLE]
|
||||
}
|
||||
}
|
||||
}
|
||||
39
packages/opencode/src/tool/task.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Tool } from "./tool"
|
||||
import DESCRIPTION from "./task.txt"
|
||||
import { z } from "zod"
|
||||
import { Session } from "../session"
|
||||
|
||||
export const TaskTool = Tool.define({
|
||||
id: "opencode.task",
|
||||
description: DESCRIPTION,
|
||||
parameters: z.object({
|
||||
description: z
|
||||
.string()
|
||||
.describe("A short (3-5 words) description of the task"),
|
||||
prompt: z.string().describe("The task for the agent to perform"),
|
||||
}),
|
||||
async execute(params, ctx) {
|
||||
const session = await Session.create(ctx.sessionID)
|
||||
const msg = await Session.getMessage(ctx.sessionID, ctx.messageID)
|
||||
const metadata = msg.metadata.assistant!
|
||||
|
||||
const result = await Session.chat({
|
||||
sessionID: session.id,
|
||||
modelID: metadata.modelID,
|
||||
providerID: metadata.providerID,
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: params.prompt,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
return {
|
||||
metadata: {
|
||||
title: params.description,
|
||||
},
|
||||
output: result.parts.findLast((x) => x.type === "text")!.text,
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -7,6 +7,7 @@ export namespace Tool {
|
||||
}
|
||||
export type Context = {
|
||||
sessionID: string
|
||||
messageID: string
|
||||
abort: AbortSignal
|
||||
}
|
||||
export interface Info<
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
zone "github.com/lrstanley/bubblezone"
|
||||
"github.com/sst/opencode/internal/app"
|
||||
"github.com/sst/opencode/internal/pubsub"
|
||||
@@ -65,6 +65,8 @@ func main() {
|
||||
zone.NewGlobal()
|
||||
program := tea.NewProgram(
|
||||
tui.NewModel(app_),
|
||||
// tea.WithMouseCellMotion(),
|
||||
tea.WithKeyboardEnhancements(),
|
||||
tea.WithAltScreen(),
|
||||
)
|
||||
|
||||
|
||||
@@ -4,13 +4,12 @@ go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.5.0
|
||||
github.com/alecthomas/chroma/v2 v2.15.0
|
||||
github.com/alecthomas/chroma/v2 v2.18.0
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1
|
||||
github.com/catppuccin/go v0.3.0
|
||||
github.com/charmbracelet/bubbles v0.21.0
|
||||
github.com/charmbracelet/bubbletea v1.3.4
|
||||
github.com/charmbracelet/glamour v0.9.1
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1
|
||||
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3
|
||||
github.com/charmbracelet/glamour v0.10.0
|
||||
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta1
|
||||
github.com/charmbracelet/x/ansi v0.8.0
|
||||
github.com/lithammer/fuzzysearch v1.1.8
|
||||
github.com/lrstanley/bubblezone v0.0.0-20250315020633-c249a3fe1231
|
||||
@@ -28,6 +27,11 @@ require (
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/atombender/go-jsonschema v0.20.0 // indirect
|
||||
github.com/charmbracelet/bubbletea v1.3.4 // indirect
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 // indirect
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
|
||||
github.com/charmbracelet/x/input v0.3.5-0.20250424101541-abb4d9a9b197 // indirect
|
||||
github.com/charmbracelet/x/windows v0.2.1 // indirect
|
||||
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.127.0 // indirect
|
||||
@@ -57,11 +61,11 @@ require (
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/colorprofile v0.3.1 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250501183327-ad3bc78c6a81 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
|
||||
@@ -7,8 +7,8 @@ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc=
|
||||
github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio=
|
||||
github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4=
|
||||
github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
@@ -26,26 +26,34 @@ github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd3
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
|
||||
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
|
||||
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
||||
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
|
||||
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1 h1:swACzss0FjnyPz1enfX56GKkLiuKg5FlyVmOLIlU2kE=
|
||||
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
|
||||
github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI=
|
||||
github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
github.com/charmbracelet/glamour v0.9.1 h1:11dEfiGP8q1BEqvGoIjivuc2rBk+5qEXdPtaQ2WoiCM=
|
||||
github.com/charmbracelet/glamour v0.9.1/go.mod h1:+SHvIS8qnwhgTpVMiXwn7OfGomSqff1cHBCI8jLOetk=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3 h1:5A2e3myxXMpCES+kjEWgGsaf9VgZXjZbLi5iMTH7j40=
|
||||
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3/go.mod h1:ZFDg5oPjyRYrPAa3iFrtP1DO8xy+LUQxd9JFHEcuwJY=
|
||||
github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
|
||||
github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0=
|
||||
github.com/charmbracelet/glamour v0.10.0 h1:MtZvfwsYCx8jEPFJm3rIBFIMZUfUJ765oX8V6kXldcY=
|
||||
github.com/charmbracelet/glamour v0.10.0/go.mod h1:f+uf+I/ChNmqo087elLnVdCiVgjSKWuXa/l6NU2ndYk=
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
|
||||
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta1 h1:SOylT6+BQzPHEjn15TIzawBPVD0QmhKXbcb3jY0ZIKU=
|
||||
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta1/go.mod h1:tRlx/Hu0lo/j9viunCN2H+Ze6JrmdjQlXUQvvArgaOc=
|
||||
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
|
||||
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250501183327-ad3bc78c6a81 h1:iGrflaL5jQW6crML+pZx/ulWAVZQR3CQoRGvFsr2Tyg=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250501183327-ad3bc78c6a81/go.mod h1:poPFOXFTsJsnLbkV3H2KxAAXT7pdjxxLujLocWjkyzM=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a h1:FsHEJ52OC4VuTzU8t+n5frMjLvpYWEznSr/u8tnkCYw=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
|
||||
github.com/charmbracelet/x/input v0.3.5-0.20250424101541-abb4d9a9b197 h1:fsWj8NF5njyMVzELc7++HsvRDvgz3VcgGAUgWBDWWWM=
|
||||
github.com/charmbracelet/x/input v0.3.5-0.20250424101541-abb4d9a9b197/go.mod h1:xseGeVftoP9rVI+/8WKYrJFH6ior6iERGvklwwHz5+s=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/charmbracelet/x/windows v0.2.1 h1:3x7vnbpQrjpuq/4L+I4gNsG5htYoCiA5oe9hLjAij5I=
|
||||
github.com/charmbracelet/x/windows v0.2.1/go.mod h1:ptZp16h40gDYqs5TSawSVW+yiLB13j4kSMA0lSCHL0M=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -56,8 +64,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58=
|
||||
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w=
|
||||
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q=
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"log/slog"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/sst/opencode/internal/config"
|
||||
"github.com/sst/opencode/internal/fileutil"
|
||||
"github.com/sst/opencode/internal/state"
|
||||
|
||||
@@ -5,15 +5,13 @@ import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"slices"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/spinner"
|
||||
"github.com/charmbracelet/bubbles/textarea"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
"github.com/charmbracelet/bubbles/v2/spinner"
|
||||
"github.com/charmbracelet/bubbles/v2/textarea"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/app"
|
||||
"github.com/sst/opencode/internal/components/dialog"
|
||||
"github.com/sst/opencode/internal/image"
|
||||
@@ -58,12 +56,12 @@ type DeleteAttachmentKeyMaps struct {
|
||||
|
||||
var editorMaps = EditorKeyMaps{
|
||||
Send: key.NewBinding(
|
||||
key.WithKeys("enter", "ctrl+s"),
|
||||
key.WithKeys("enter"),
|
||||
key.WithHelp("enter", "send message"),
|
||||
),
|
||||
OpenEditor: key.NewBinding(
|
||||
key.WithKeys("ctrl+e"),
|
||||
key.WithHelp("ctrl+e", "open editor"),
|
||||
key.WithKeys("f12"),
|
||||
key.WithHelp("f12", "open editor"),
|
||||
),
|
||||
Paste: key.NewBinding(
|
||||
key.WithKeys("ctrl+v"),
|
||||
@@ -120,6 +118,20 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
m.attachments = append(m.attachments, msg.Attachment)
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
if m.textarea.Value() != "" {
|
||||
m.textarea.Reset()
|
||||
return m, func() tea.Msg {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
case "shift+enter":
|
||||
value := m.textarea.Value()
|
||||
m.textarea.SetValue(value + "\n")
|
||||
return m, nil
|
||||
}
|
||||
|
||||
if key.Matches(msg, DeleteKeyMaps.AttachmentDeleteMode) {
|
||||
m.deleteMode = true
|
||||
return m, nil
|
||||
@@ -129,18 +141,18 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.attachments = nil
|
||||
return m, nil
|
||||
}
|
||||
if m.deleteMode && len(msg.Runes) > 0 && unicode.IsDigit(msg.Runes[0]) {
|
||||
num := int(msg.Runes[0] - '0')
|
||||
m.deleteMode = false
|
||||
if num < 10 && len(m.attachments) > num {
|
||||
if num == 0 {
|
||||
m.attachments = m.attachments[num+1:]
|
||||
} else {
|
||||
m.attachments = slices.Delete(m.attachments, num, num+1)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
// if m.deleteMode && len(msg.Runes) > 0 && unicode.IsDigit(msg.Runes[0]) {
|
||||
// num := int(msg.Runes[0] - '0')
|
||||
// m.deleteMode = false
|
||||
// if num < 10 && len(m.attachments) > num {
|
||||
// if num == 0 {
|
||||
// m.attachments = m.attachments[num+1:]
|
||||
// } else {
|
||||
// m.attachments = slices.Delete(m.attachments, num, num+1)
|
||||
// }
|
||||
// return m, nil
|
||||
// }
|
||||
// }
|
||||
if key.Matches(msg, messageKeys.PageUp) || key.Matches(msg, messageKeys.PageDown) ||
|
||||
key.Matches(msg, messageKeys.HalfPageUp) || key.Matches(msg, messageKeys.HalfPageDown) {
|
||||
return m, nil
|
||||
@@ -258,9 +270,11 @@ func (m *editorComponent) View() string {
|
||||
m.textarea.View(),
|
||||
)
|
||||
textarea = styles.BaseStyle().
|
||||
Width(m.width-2).
|
||||
Border(lipgloss.NormalBorder(), true, true).
|
||||
BorderForeground(t.Border()).
|
||||
Width(m.width).
|
||||
Background(t.BackgroundElement()).
|
||||
Border(lipgloss.ThickBorder(), false, true).
|
||||
BorderForeground(t.BorderActive()).
|
||||
BorderBackground(t.Background()).
|
||||
Render(textarea)
|
||||
|
||||
hint := base("enter") + muted(" send ") + base("shift") + muted("+") + base("enter") + muted(" newline")
|
||||
@@ -282,21 +296,19 @@ func (m *editorComponent) View() string {
|
||||
content := lipgloss.JoinVertical(
|
||||
lipgloss.Top,
|
||||
// m.attachmentsContent(),
|
||||
"",
|
||||
textarea,
|
||||
info,
|
||||
)
|
||||
|
||||
return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
content,
|
||||
t.Background(),
|
||||
)
|
||||
return content
|
||||
}
|
||||
|
||||
func (m *editorComponent) SetSize(width, height int) tea.Cmd {
|
||||
m.width = width
|
||||
m.height = height
|
||||
m.textarea.SetWidth(width - 5) // account for the prompt and padding right
|
||||
m.textarea.SetHeight(height - 3) // account for info underneath
|
||||
m.textarea.SetHeight(height - 2) // account for info underneath
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -407,21 +419,21 @@ func (m *editorComponent) attachmentsContent() string {
|
||||
|
||||
func createTextArea(existing *textarea.Model) textarea.Model {
|
||||
t := theme.CurrentTheme()
|
||||
bgColor := t.Background()
|
||||
bgColor := t.BackgroundElement()
|
||||
textColor := t.Text()
|
||||
textMutedColor := t.TextMuted()
|
||||
|
||||
ta := textarea.New()
|
||||
ta.Placeholder = "It's prompting time..."
|
||||
|
||||
ta.BlurredStyle.Base = styles.BaseStyle().Background(bgColor).Foreground(textColor)
|
||||
ta.BlurredStyle.CursorLine = styles.BaseStyle().Background(bgColor)
|
||||
ta.BlurredStyle.Placeholder = styles.BaseStyle().Background(bgColor).Foreground(textMutedColor)
|
||||
ta.BlurredStyle.Text = styles.BaseStyle().Background(bgColor).Foreground(textColor)
|
||||
ta.FocusedStyle.Base = styles.BaseStyle().Background(bgColor).Foreground(textColor)
|
||||
ta.FocusedStyle.CursorLine = styles.BaseStyle().Background(bgColor)
|
||||
ta.FocusedStyle.Placeholder = styles.BaseStyle().Background(bgColor).Foreground(textMutedColor)
|
||||
ta.FocusedStyle.Text = styles.BaseStyle().Background(bgColor).Foreground(textColor)
|
||||
ta.Styles.Blurred.Base = lipgloss.NewStyle().Background(bgColor).Foreground(textColor)
|
||||
ta.Styles.Blurred.CursorLine = lipgloss.NewStyle().Background(bgColor)
|
||||
ta.Styles.Blurred.Placeholder = lipgloss.NewStyle().Background(bgColor).Foreground(textMutedColor)
|
||||
ta.Styles.Blurred.Text = lipgloss.NewStyle().Background(bgColor).Foreground(textColor)
|
||||
ta.Styles.Focused.Base = lipgloss.NewStyle().Background(bgColor).Foreground(textColor)
|
||||
ta.Styles.Focused.CursorLine = lipgloss.NewStyle().Background(bgColor)
|
||||
ta.Styles.Focused.Placeholder = lipgloss.NewStyle().Background(bgColor).Foreground(textMutedColor)
|
||||
ta.Styles.Focused.Text = lipgloss.NewStyle().Background(bgColor).Foreground(textColor)
|
||||
ta.Styles.Cursor.Color = t.Primary()
|
||||
|
||||
ta.Prompt = " "
|
||||
ta.ShowLineNumbers = false
|
||||
@@ -437,7 +449,7 @@ func createTextArea(existing *textarea.Model) textarea.Model {
|
||||
return ta
|
||||
}
|
||||
|
||||
func NewEditorComponent(app *app.App) tea.Model {
|
||||
func NewEditorComponent(app *app.App) layout.ModelWithView {
|
||||
s := spinner.New(spinner.WithSpinner(spinner.Ellipsis), spinner.WithStyle(styles.Muted().Width(3)))
|
||||
ta := createTextArea(nil)
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ import (
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2/compat"
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
"github.com/sst/opencode/internal/app"
|
||||
"github.com/sst/opencode/internal/components/diff"
|
||||
@@ -21,8 +22,8 @@ import (
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func toMarkdown(content string, width int) string {
|
||||
r := styles.GetMarkdownRenderer(width)
|
||||
func toMarkdown(content string, width int, backgroundColor compat.AdaptiveColor) string {
|
||||
r := styles.GetMarkdownRenderer(width, backgroundColor)
|
||||
content = strings.ReplaceAll(content, app.Info.Path.Root+"/", "")
|
||||
rendered, _ := r.Render(content)
|
||||
lines := strings.Split(rendered, "\n")
|
||||
@@ -50,7 +51,7 @@ func toMarkdown(content string, width int) string {
|
||||
|
||||
type blockRenderer struct {
|
||||
align *lipgloss.Position
|
||||
borderColor *lipgloss.AdaptiveColor
|
||||
borderColor *compat.AdaptiveColor
|
||||
fullWidth bool
|
||||
paddingTop int
|
||||
paddingBottom int
|
||||
@@ -70,7 +71,7 @@ func WithAlign(align lipgloss.Position) renderingOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithBorderColor(color lipgloss.AdaptiveColor) renderingOption {
|
||||
func WithBorderColor(color compat.AdaptiveColor) renderingOption {
|
||||
return func(c *blockRenderer) {
|
||||
c.borderColor = &color
|
||||
}
|
||||
@@ -137,9 +138,8 @@ func renderContentBlock(content string, options ...renderingOption) string {
|
||||
BorderLeftBackground(t.Background())
|
||||
}
|
||||
|
||||
content = styles.ForceReplaceBackgroundWithLipgloss(content, t.BackgroundSubtle())
|
||||
if renderer.fullWidth {
|
||||
style = style.Width(layout.Current.Container.Width - 2)
|
||||
style = style.Width(layout.Current.Container.Width)
|
||||
}
|
||||
content = style.Render(content)
|
||||
if renderer.paddingTop > 0 {
|
||||
@@ -152,13 +152,11 @@ func renderContentBlock(content string, options ...renderingOption) string {
|
||||
layout.Current.Container.Width,
|
||||
align,
|
||||
content,
|
||||
lipgloss.WithWhitespaceBackground(t.Background()),
|
||||
)
|
||||
content = lipgloss.PlaceHorizontal(
|
||||
layout.Current.Viewport.Width,
|
||||
lipgloss.Center,
|
||||
content,
|
||||
lipgloss.WithWhitespaceBackground(t.Background()),
|
||||
)
|
||||
return content
|
||||
}
|
||||
@@ -181,9 +179,7 @@ func renderText(message client.MessageInfo, text string, author string) string {
|
||||
// don't show the date if it's today
|
||||
timestamp = timestamp[12:]
|
||||
}
|
||||
info := styles.BaseStyle().
|
||||
Foreground(t.TextMuted()).
|
||||
Render(fmt.Sprintf("%s (%s)", author, timestamp))
|
||||
info := fmt.Sprintf("%s (%s)", author, timestamp)
|
||||
|
||||
align := lipgloss.Left
|
||||
switch message.Role {
|
||||
@@ -195,7 +191,7 @@ func renderText(message client.MessageInfo, text string, author string) string {
|
||||
|
||||
textWidth := lipgloss.Width(text)
|
||||
markdownWidth := min(textWidth, width-padding-4) // -4 for the border and padding
|
||||
content := toMarkdown(text, markdownWidth)
|
||||
content := toMarkdown(text, markdownWidth, t.BackgroundSubtle())
|
||||
content = lipgloss.JoinVertical(align, content, info)
|
||||
|
||||
switch message.Role {
|
||||
@@ -262,16 +258,18 @@ func renderToolInvocation(
|
||||
}
|
||||
|
||||
body := ""
|
||||
error := ""
|
||||
finished := result != nil && *result != ""
|
||||
if finished {
|
||||
body = *result
|
||||
}
|
||||
|
||||
if metadata["error"] != nil && metadata["message"] != nil {
|
||||
body = styles.BaseStyle().
|
||||
Width(outerWidth).
|
||||
body = ""
|
||||
error = styles.BaseStyle().
|
||||
Foreground(t.Error()).
|
||||
Render(metadata["message"].(string))
|
||||
error = renderContentBlock(error, WithBorderColor(t.Error()), WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
|
||||
}
|
||||
|
||||
elapsed := ""
|
||||
@@ -294,8 +292,8 @@ func renderToolInvocation(
|
||||
toolArgs = renderArgs(&toolArgsMap, "filePath")
|
||||
title = fmt.Sprintf("Read: %s %s", toolArgs, elapsed)
|
||||
body = ""
|
||||
filename := toolArgsMap["filePath"].(string)
|
||||
if metadata["preview"] != nil {
|
||||
if metadata["preview"] != nil && toolArgsMap["filePath"] != nil {
|
||||
filename := toolArgsMap["filePath"].(string)
|
||||
body = metadata["preview"].(string)
|
||||
body = renderFile(filename, body, WithTruncate(6))
|
||||
}
|
||||
@@ -313,7 +311,7 @@ func renderToolInvocation(
|
||||
lipgloss.Center,
|
||||
lipgloss.Center,
|
||||
body,
|
||||
lipgloss.WithWhitespaceBackground(t.Background()),
|
||||
lipgloss.WithWhitespaceStyle(lipgloss.NewStyle().Background(t.Background())),
|
||||
)
|
||||
}
|
||||
case "opencode_write":
|
||||
@@ -328,7 +326,7 @@ func renderToolInvocation(
|
||||
command := toolArgsMap["command"].(string)
|
||||
stdout := metadata["stdout"].(string)
|
||||
body = fmt.Sprintf("```console\n> %s\n%s```", command, stdout)
|
||||
body = toMarkdown(body, innerWidth)
|
||||
body = toMarkdown(body, innerWidth, t.BackgroundSubtle())
|
||||
body = renderContentBlock(body, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
|
||||
}
|
||||
case "opencode_webfetch":
|
||||
@@ -336,7 +334,7 @@ func renderToolInvocation(
|
||||
format := toolArgsMap["format"].(string)
|
||||
body = truncateHeight(body, 10)
|
||||
if format == "html" || format == "markdown" {
|
||||
body = toMarkdown(body, innerWidth)
|
||||
body = toMarkdown(body, innerWidth, t.BackgroundSubtle())
|
||||
}
|
||||
body = renderContentBlock(body, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
|
||||
case "opencode_todowrite":
|
||||
@@ -356,7 +354,7 @@ func renderToolInvocation(
|
||||
body += fmt.Sprintf("- [ ] %s\n", content)
|
||||
}
|
||||
}
|
||||
body = toMarkdown(body, innerWidth)
|
||||
body = toMarkdown(body, innerWidth, t.BackgroundSubtle())
|
||||
body = renderContentBlock(body, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
|
||||
}
|
||||
default:
|
||||
@@ -368,10 +366,12 @@ func renderToolInvocation(
|
||||
|
||||
content := style.Render(title)
|
||||
content = lipgloss.PlaceHorizontal(layout.Current.Viewport.Width, lipgloss.Center, content)
|
||||
content = styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
|
||||
if showResult && body != "" {
|
||||
if showResult && body != "" && error == "" {
|
||||
content += "\n" + body
|
||||
}
|
||||
if showResult && error != "" {
|
||||
content += "\n" + error
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
@@ -411,6 +411,7 @@ func WithTruncate(height int) fileRenderingOption {
|
||||
}
|
||||
|
||||
func renderFile(filename string, content string, options ...fileRenderingOption) string {
|
||||
t := theme.CurrentTheme()
|
||||
renderer := &fileRenderer{
|
||||
filename: filename,
|
||||
content: content,
|
||||
@@ -419,7 +420,6 @@ func renderFile(filename string, content string, options ...fileRenderingOption)
|
||||
option(renderer)
|
||||
}
|
||||
|
||||
// TODO: is this even needed?
|
||||
lines := []string{}
|
||||
for line := range strings.SplitSeq(content, "\n") {
|
||||
line = strings.TrimRightFunc(line, unicode.IsSpace)
|
||||
@@ -428,23 +428,12 @@ func renderFile(filename string, content string, options ...fileRenderingOption)
|
||||
}
|
||||
content = strings.Join(lines, "\n")
|
||||
|
||||
width := layout.Current.Container.Width - 6
|
||||
width := layout.Current.Container.Width - 8
|
||||
if renderer.height > 0 {
|
||||
content = truncateHeight(content, renderer.height)
|
||||
}
|
||||
content = fmt.Sprintf("```%s\n%s\n```", extension(renderer.filename), content)
|
||||
content = toMarkdown(content, width)
|
||||
|
||||
// ensure no line is wider than the width
|
||||
// truncated := []string{}
|
||||
// for line := range strings.SplitSeq(content, "\n") {
|
||||
// line = strings.TrimRightFunc(line, unicode.IsSpace)
|
||||
// // if lipgloss.Width(line) > width-3 {
|
||||
// line = ansi.Truncate(line, width-3, "")
|
||||
// // }
|
||||
// truncated = append(truncated, line)
|
||||
// }
|
||||
// content = strings.Join(truncated, "\n")
|
||||
content = toMarkdown(content, width, t.BackgroundSubtle())
|
||||
|
||||
return renderContentBlock(content, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
|
||||
}
|
||||
@@ -484,6 +473,9 @@ func renderArgs(args *map[string]any, titleKey string) string {
|
||||
title := ""
|
||||
parts := []string{}
|
||||
for key, value := range *args {
|
||||
if value == nil {
|
||||
continue
|
||||
}
|
||||
if key == "filePath" || key == "path" {
|
||||
value = relative(value.(string))
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/spinner"
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
"github.com/charmbracelet/bubbles/v2/spinner"
|
||||
"github.com/charmbracelet/bubbles/v2/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/app"
|
||||
"github.com/sst/opencode/internal/components/dialog"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
@@ -122,6 +122,7 @@ const (
|
||||
userTextBlock
|
||||
assistantTextBlock
|
||||
toolInvocationBlock
|
||||
errorBlock
|
||||
)
|
||||
|
||||
func (m *messagesComponent) renderView() {
|
||||
@@ -129,6 +130,7 @@ func (m *messagesComponent) renderView() {
|
||||
return
|
||||
}
|
||||
|
||||
t := theme.CurrentTheme()
|
||||
blocks := make([]string, 0)
|
||||
previousBlockType := none
|
||||
for _, message := range m.app.Messages {
|
||||
@@ -211,21 +213,31 @@ func (m *messagesComponent) renderView() {
|
||||
previousBlockType = toolInvocationBlock
|
||||
}
|
||||
}
|
||||
|
||||
error := ""
|
||||
errorValue, _ := message.Metadata.Error.ValueByDiscriminator()
|
||||
switch errorValue.(type) {
|
||||
case client.UnknownError:
|
||||
clientError := errorValue.(client.UnknownError)
|
||||
error = clientError.Data.Message
|
||||
error = renderContentBlock(error, WithBorderColor(t.Error()), WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
|
||||
blocks = append(blocks, error)
|
||||
previousBlockType = errorBlock
|
||||
}
|
||||
}
|
||||
|
||||
t := theme.CurrentTheme()
|
||||
centered := []string{}
|
||||
for _, block := range blocks {
|
||||
centered = append(centered, lipgloss.PlaceHorizontal(
|
||||
m.width,
|
||||
lipgloss.Center,
|
||||
block,
|
||||
lipgloss.WithWhitespaceBackground(t.Background()),
|
||||
lipgloss.WithWhitespaceStyle(lipgloss.NewStyle().Background(t.Background())),
|
||||
))
|
||||
}
|
||||
|
||||
m.viewport.Height = m.height - lipgloss.Height(m.header())
|
||||
m.viewport.SetContent(strings.Join(centered, "\n"))
|
||||
m.viewport.SetHeight(m.height - lipgloss.Height(m.header()))
|
||||
m.viewport.SetContent("\n" + strings.Join(centered, "\n") + "\n")
|
||||
}
|
||||
|
||||
func (m *messagesComponent) header() string {
|
||||
@@ -238,7 +250,7 @@ func (m *messagesComponent) header() string {
|
||||
base := styles.BaseStyle().Render
|
||||
muted := styles.Muted().Render
|
||||
headerLines := []string{}
|
||||
headerLines = append(headerLines, toMarkdown("# "+m.app.Session.Title, width))
|
||||
headerLines = append(headerLines, toMarkdown("# "+m.app.Session.Title, width, t.Background()))
|
||||
if m.app.Session.Share != nil && m.app.Session.Share.Url != "" {
|
||||
headerLines = append(headerLines, muted(m.app.Session.Share.Url))
|
||||
} else {
|
||||
@@ -255,7 +267,7 @@ func (m *messagesComponent) header() string {
|
||||
Background(t.Background()).
|
||||
Render(header)
|
||||
|
||||
return styles.ForceReplaceBackgroundWithLipgloss(header, t.Background())
|
||||
return header
|
||||
}
|
||||
|
||||
func (m *messagesComponent) View() string {
|
||||
@@ -269,73 +281,8 @@ func (m *messagesComponent) View() string {
|
||||
)
|
||||
}
|
||||
|
||||
// func hasToolsWithoutResponse(messages []message.Message) bool {
|
||||
// toolCalls := make([]message.ToolCall, 0)
|
||||
// toolResults := make([]message.ToolResult, 0)
|
||||
// for _, m := range messages {
|
||||
// toolCalls = append(toolCalls, m.ToolCalls()...)
|
||||
// toolResults = append(toolResults, m.ToolResults()...)
|
||||
// }
|
||||
//
|
||||
// for _, v := range toolCalls {
|
||||
// found := false
|
||||
// for _, r := range toolResults {
|
||||
// if v.ID == r.ToolCallID {
|
||||
// found = true
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// if !found && v.Finished {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
|
||||
// func hasUnfinishedToolCalls(messages []message.Message) bool {
|
||||
// toolCalls := make([]message.ToolCall, 0)
|
||||
// for _, m := range messages {
|
||||
// toolCalls = append(toolCalls, m.ToolCalls()...)
|
||||
// }
|
||||
// for _, v := range toolCalls {
|
||||
// if !v.Finished {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
|
||||
func (m *messagesComponent) help() string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
text := ""
|
||||
|
||||
if m.app.IsBusy() {
|
||||
text += lipgloss.JoinHorizontal(
|
||||
lipgloss.Left,
|
||||
baseStyle.Foreground(t.TextMuted()).Bold(true).Render("press "),
|
||||
baseStyle.Foreground(t.Text()).Bold(true).Render("esc"),
|
||||
baseStyle.Foreground(t.TextMuted()).Bold(true).Render(" to interrupt"),
|
||||
)
|
||||
} else {
|
||||
text += lipgloss.JoinHorizontal(
|
||||
lipgloss.Left,
|
||||
baseStyle.Foreground(t.Text()).Bold(true).Render("enter"),
|
||||
baseStyle.Foreground(t.TextMuted()).Bold(true).Render(" to send,"),
|
||||
baseStyle.Foreground(t.Text()).Bold(true).Render(" \\"),
|
||||
baseStyle.Foreground(t.TextMuted()).Bold(true).Render("+"),
|
||||
baseStyle.Foreground(t.Text()).Bold(true).Render("enter"),
|
||||
baseStyle.Foreground(t.TextMuted()).Bold(true).Render(" for newline"),
|
||||
)
|
||||
}
|
||||
return baseStyle.
|
||||
Width(m.width).
|
||||
Render(text)
|
||||
}
|
||||
|
||||
func (m *messagesComponent) home() string {
|
||||
t := theme.CurrentTheme()
|
||||
// t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
base := baseStyle.Render
|
||||
muted := styles.Muted().Render
|
||||
@@ -398,16 +345,13 @@ func (m *messagesComponent) home() string {
|
||||
lines = append(lines, "")
|
||||
}
|
||||
|
||||
return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center,
|
||||
baseStyle.Width(lipgloss.Width(logoAndVersion)).Render(
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Top,
|
||||
lines...,
|
||||
),
|
||||
)),
|
||||
t.Background(),
|
||||
)
|
||||
return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center,
|
||||
baseStyle.Width(lipgloss.Width(logoAndVersion)).Render(
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Top,
|
||||
lines...,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
func (m *messagesComponent) SetSize(width, height int) tea.Cmd {
|
||||
@@ -420,10 +364,10 @@ func (m *messagesComponent) SetSize(width, height int) tea.Cmd {
|
||||
}
|
||||
m.width = width
|
||||
m.height = height
|
||||
m.viewport.Width = width
|
||||
m.viewport.Height = height - lipgloss.Height(m.header())
|
||||
m.attachments.Width = width + 40
|
||||
m.attachments.Height = 3
|
||||
m.viewport.SetWidth(width)
|
||||
m.viewport.SetHeight(height - lipgloss.Height(m.header()))
|
||||
m.attachments.SetWidth(width + 40)
|
||||
m.attachments.SetHeight(3)
|
||||
m.renderView()
|
||||
return nil
|
||||
}
|
||||
@@ -449,15 +393,15 @@ func (m *messagesComponent) BindingKeys() []key.Binding {
|
||||
}
|
||||
}
|
||||
|
||||
func NewMessagesComponent(app *app.App) tea.Model {
|
||||
func NewMessagesComponent(app *app.App) layout.ModelWithView {
|
||||
customSpinner := spinner.Spinner{
|
||||
Frames: []string{" ", "┃", "┃"},
|
||||
FPS: time.Second / 3,
|
||||
}
|
||||
s := spinner.New(spinner.WithSpinner(customSpinner))
|
||||
|
||||
vp := viewport.New(0, 0)
|
||||
attachments := viewport.New(0, 0)
|
||||
vp := viewport.New() //(0, 0)
|
||||
attachments := viewport.New() //(0, 0)
|
||||
vp.KeyMap.PageUp = messageKeys.PageUp
|
||||
vp.KeyMap.PageDown = messageKeys.PageDown
|
||||
vp.KeyMap.HalfPageUp = messageKeys.HalfPageUp
|
||||
|
||||
@@ -5,20 +5,21 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/app"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/pubsub"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
)
|
||||
|
||||
type StatusCmp interface {
|
||||
tea.Model
|
||||
type StatusComponent interface {
|
||||
layout.ModelWithView
|
||||
}
|
||||
|
||||
type statusCmp struct {
|
||||
type statusComponent struct {
|
||||
app *app.App
|
||||
queue []status.StatusMessage
|
||||
width int
|
||||
@@ -27,7 +28,7 @@ type statusCmp struct {
|
||||
}
|
||||
|
||||
// clearMessageCmd is a command that clears status messages after a timeout
|
||||
func (m statusCmp) clearMessageCmd() tea.Cmd {
|
||||
func (m statusComponent) clearMessageCmd() tea.Cmd {
|
||||
return tea.Tick(time.Second, func(t time.Time) tea.Msg {
|
||||
return statusCleanupMsg{time: t}
|
||||
})
|
||||
@@ -38,11 +39,11 @@ type statusCleanupMsg struct {
|
||||
time time.Time
|
||||
}
|
||||
|
||||
func (m statusCmp) Init() tea.Cmd {
|
||||
func (m statusComponent) Init() tea.Cmd {
|
||||
return m.clearMessageCmd()
|
||||
}
|
||||
|
||||
func (m statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (m statusComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
m.width = msg.Width
|
||||
@@ -100,14 +101,15 @@ func (m statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
func logo() string {
|
||||
t := theme.CurrentTheme()
|
||||
mark := styles.Bold().Foreground(t.Primary()).Render("◧ ")
|
||||
open := styles.Muted().Render("open")
|
||||
code := styles.BaseStyle().Bold(true).Render("code")
|
||||
version := styles.Muted().Render(app.Info.Version)
|
||||
return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
styles.Padded().Render(mark+open+code+" "+version),
|
||||
t.BackgroundElement(),
|
||||
)
|
||||
base := lipgloss.NewStyle().Background(t.BackgroundElement()).Foreground(t.TextMuted()).Render
|
||||
emphasis := lipgloss.NewStyle().Bold(true).Background(t.BackgroundElement()).Foreground(t.Text()).Render
|
||||
|
||||
open := base("open")
|
||||
code := emphasis("code ")
|
||||
version := base(app.Info.Version)
|
||||
return styles.Padded().
|
||||
Background(t.BackgroundElement()).
|
||||
Render(open + code + version)
|
||||
}
|
||||
|
||||
func formatTokensAndCost(tokens float32, contextWindow float32, cost float32) string {
|
||||
@@ -137,7 +139,7 @@ func formatTokensAndCost(tokens float32, contextWindow float32, cost float32) st
|
||||
return fmt.Sprintf("Tokens: %s (%d%%), Cost: %s", formattedTokens, int(percentage), formattedCost)
|
||||
}
|
||||
|
||||
func (m statusCmp) View() string {
|
||||
func (m statusComponent) View() string {
|
||||
if m.app.Session.Id == "" {
|
||||
return styles.BaseStyle().
|
||||
Width(m.width).
|
||||
@@ -250,107 +252,8 @@ func (m statusCmp) View() string {
|
||||
// }
|
||||
}
|
||||
|
||||
func (m *statusCmp) projectDiagnostics() string {
|
||||
t := theme.CurrentTheme()
|
||||
|
||||
// Check if any LSP server is still initializing
|
||||
initializing := false
|
||||
// for _, client := range m.app.LSPClients {
|
||||
// if client.GetServerState() == lsp.StateStarting {
|
||||
// initializing = true
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
// If any server is initializing, show that status
|
||||
if initializing {
|
||||
return lipgloss.NewStyle().
|
||||
Foreground(t.Warning()).
|
||||
Render(fmt.Sprintf("%s Initializing LSP...", styles.SpinnerIcon))
|
||||
}
|
||||
|
||||
// errorDiagnostics := []protocol.Diagnostic{}
|
||||
// warnDiagnostics := []protocol.Diagnostic{}
|
||||
// hintDiagnostics := []protocol.Diagnostic{}
|
||||
// infoDiagnostics := []protocol.Diagnostic{}
|
||||
// for _, client := range m.app.LSPClients {
|
||||
// for _, d := range client.GetDiagnostics() {
|
||||
// for _, diag := range d {
|
||||
// switch diag.Severity {
|
||||
// case protocol.SeverityError:
|
||||
// errorDiagnostics = append(errorDiagnostics, diag)
|
||||
// case protocol.SeverityWarning:
|
||||
// warnDiagnostics = append(warnDiagnostics, diag)
|
||||
// case protocol.SeverityHint:
|
||||
// hintDiagnostics = append(hintDiagnostics, diag)
|
||||
// case protocol.SeverityInformation:
|
||||
// infoDiagnostics = append(infoDiagnostics, diag)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
styles.Padded().Render("No diagnostics"),
|
||||
t.BackgroundElement(),
|
||||
)
|
||||
|
||||
// if len(errorDiagnostics) == 0 &&
|
||||
// len(warnDiagnostics) == 0 &&
|
||||
// len(infoDiagnostics) == 0 &&
|
||||
// len(hintDiagnostics) == 0 {
|
||||
// return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
// styles.Padded().Render("No diagnostics"),
|
||||
// t.BackgroundDarker(),
|
||||
// )
|
||||
// }
|
||||
|
||||
// diagnostics := []string{}
|
||||
//
|
||||
// errStr := lipgloss.NewStyle().
|
||||
// Background(t.BackgroundDarker()).
|
||||
// Foreground(t.Error()).
|
||||
// Render(fmt.Sprintf("%s %d", styles.ErrorIcon, len(errorDiagnostics)))
|
||||
// diagnostics = append(diagnostics, errStr)
|
||||
//
|
||||
// warnStr := lipgloss.NewStyle().
|
||||
// Background(t.BackgroundDarker()).
|
||||
// Foreground(t.Warning()).
|
||||
// Render(fmt.Sprintf("%s %d", styles.WarningIcon, len(warnDiagnostics)))
|
||||
// diagnostics = append(diagnostics, warnStr)
|
||||
//
|
||||
// infoStr := lipgloss.NewStyle().
|
||||
// Background(t.BackgroundDarker()).
|
||||
// Foreground(t.Info()).
|
||||
// Render(fmt.Sprintf("%s %d", styles.InfoIcon, len(infoDiagnostics)))
|
||||
// diagnostics = append(diagnostics, infoStr)
|
||||
//
|
||||
// hintStr := lipgloss.NewStyle().
|
||||
// Background(t.BackgroundDarker()).
|
||||
// Foreground(t.Text()).
|
||||
// Render(fmt.Sprintf("%s %d", styles.HintIcon, len(hintDiagnostics)))
|
||||
// diagnostics = append(diagnostics, hintStr)
|
||||
//
|
||||
// return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
// styles.Padded().Render(strings.Join(diagnostics, " ")),
|
||||
// t.BackgroundDarker(),
|
||||
// )
|
||||
}
|
||||
|
||||
func (m statusCmp) model() string {
|
||||
t := theme.CurrentTheme()
|
||||
model := "None"
|
||||
if m.app.Model != nil {
|
||||
model = *m.app.Model.Name
|
||||
}
|
||||
|
||||
return styles.Padded().
|
||||
Background(t.Secondary()).
|
||||
Foreground(t.Background()).
|
||||
Render(model)
|
||||
}
|
||||
|
||||
func NewStatusCmp(app *app.App) StatusCmp {
|
||||
statusComponent := &statusCmp{
|
||||
func NewStatusCmp(app *app.App) StatusComponent {
|
||||
statusComponent := &statusComponent{
|
||||
app: app,
|
||||
queue: []status.StatusMessage{},
|
||||
messageTTL: 4 * time.Second,
|
||||
|
||||
@@ -2,10 +2,10 @@ package dialog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
"github.com/charmbracelet/bubbles/v2/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
@@ -70,17 +70,24 @@ func NewMultiArgumentsDialogCmp(commandID, content string, argNames []string) Mu
|
||||
for i, name := range argNames {
|
||||
ti := textinput.New()
|
||||
ti.Placeholder = fmt.Sprintf("Enter value for %s...", name)
|
||||
ti.Width = 40
|
||||
ti.SetWidth(40)
|
||||
ti.Prompt = ""
|
||||
ti.PlaceholderStyle = ti.PlaceholderStyle.Background(t.Background())
|
||||
ti.PromptStyle = ti.PromptStyle.Background(t.Background())
|
||||
ti.TextStyle = ti.TextStyle.Background(t.Background())
|
||||
ti.Styles.Blurred.Placeholder = ti.Styles.Blurred.Placeholder.Background(t.Background())
|
||||
ti.Styles.Blurred.Text = ti.Styles.Blurred.Text.Background(t.Background())
|
||||
ti.Styles.Blurred.Prompt = ti.Styles.Blurred.Prompt.Foreground(t.Primary())
|
||||
|
||||
ti.Styles.Focused.Placeholder = ti.Styles.Focused.Placeholder.Background(t.Background())
|
||||
ti.Styles.Focused.Text = ti.Styles.Focused.Text.Background(t.Background())
|
||||
ti.Styles.Focused.Prompt = ti.Styles.Focused.Prompt.Foreground(t.Primary())
|
||||
|
||||
// ti.PromptStyle = ti.PromptStyle.Background(t.Background())
|
||||
// ti.TextStyle = ti.TextStyle.Background(t.Background())
|
||||
|
||||
// Only focus the first input initially
|
||||
if i == 0 {
|
||||
ti.Focus()
|
||||
ti.PromptStyle = ti.PromptStyle.Foreground(t.Primary())
|
||||
ti.TextStyle = ti.TextStyle.Foreground(t.Primary())
|
||||
// ti.PromptStyle = ti.PromptStyle.Foreground(t.Primary())
|
||||
// ti.TextStyle = ti.TextStyle.Foreground(t.Primary())
|
||||
} else {
|
||||
ti.Blur()
|
||||
}
|
||||
@@ -115,7 +122,7 @@ func (m MultiArgumentsDialogCmp) Init() tea.Cmd {
|
||||
// Update implements tea.Model.
|
||||
func (m MultiArgumentsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
t := theme.CurrentTheme()
|
||||
// t := theme.CurrentTheme()
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
@@ -145,22 +152,22 @@ func (m MultiArgumentsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.inputs[m.focusIndex].Blur()
|
||||
m.focusIndex++
|
||||
m.inputs[m.focusIndex].Focus()
|
||||
m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
|
||||
m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
|
||||
// m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
|
||||
// m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
|
||||
case key.Matches(msg, key.NewBinding(key.WithKeys("tab"))):
|
||||
// Move to the next input
|
||||
m.inputs[m.focusIndex].Blur()
|
||||
m.focusIndex = (m.focusIndex + 1) % len(m.inputs)
|
||||
m.inputs[m.focusIndex].Focus()
|
||||
m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
|
||||
m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
|
||||
// m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
|
||||
// m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
|
||||
case key.Matches(msg, key.NewBinding(key.WithKeys("shift+tab"))):
|
||||
// Move to the previous input
|
||||
m.inputs[m.focusIndex].Blur()
|
||||
m.focusIndex = (m.focusIndex - 1 + len(m.inputs)) % len(m.inputs)
|
||||
m.inputs[m.focusIndex].Focus()
|
||||
m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
|
||||
m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
|
||||
// m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
|
||||
// m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
m.width = msg.Width
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package dialog
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
utilComponents "github.com/sst/opencode/internal/components/util"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
@@ -56,12 +56,12 @@ type CloseCommandDialogMsg struct{}
|
||||
|
||||
// CommandDialog interface for the command selection dialog
|
||||
type CommandDialog interface {
|
||||
tea.Model
|
||||
layout.ModelWithView
|
||||
layout.Bindings
|
||||
SetCommands(commands []Command)
|
||||
}
|
||||
|
||||
type commandDialogCmp struct {
|
||||
type commandDialogComponent struct {
|
||||
listView utilComponents.SimpleList[Command]
|
||||
width int
|
||||
height int
|
||||
@@ -83,11 +83,11 @@ var commandKeys = commandKeyMap{
|
||||
),
|
||||
}
|
||||
|
||||
func (c *commandDialogCmp) Init() tea.Cmd {
|
||||
func (c *commandDialogComponent) Init() tea.Cmd {
|
||||
return c.listView.Init()
|
||||
}
|
||||
|
||||
func (c *commandDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (c *commandDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
@@ -114,7 +114,7 @@ func (c *commandDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return c, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
func (c *commandDialogCmp) View() string {
|
||||
func (c *commandDialogComponent) View() string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
@@ -158,11 +158,11 @@ func (c *commandDialogCmp) View() string {
|
||||
Render(content)
|
||||
}
|
||||
|
||||
func (c *commandDialogCmp) BindingKeys() []key.Binding {
|
||||
func (c *commandDialogComponent) BindingKeys() []key.Binding {
|
||||
return layout.KeyMapToSlice(commandKeys)
|
||||
}
|
||||
|
||||
func (c *commandDialogCmp) SetCommands(commands []Command) {
|
||||
func (c *commandDialogComponent) SetCommands(commands []Command) {
|
||||
c.listView.SetItems(commands)
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ func NewCommandDialogCmp() CommandDialog {
|
||||
"No commands available",
|
||||
true,
|
||||
)
|
||||
return &commandDialogCmp{
|
||||
return &commandDialogComponent{
|
||||
listView: listView,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package dialog
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/textarea"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
"github.com/charmbracelet/bubbles/v2/textarea"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
utilComponents "github.com/sst/opencode/internal/components/util"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
"github.com/sst/opencode/internal/util"
|
||||
@@ -77,12 +77,12 @@ type CompletionDialogCompleteItemMsg struct {
|
||||
type CompletionDialogCloseMsg struct{}
|
||||
|
||||
type CompletionDialog interface {
|
||||
tea.Model
|
||||
layout.ModelWithView
|
||||
layout.Bindings
|
||||
SetWidth(width int)
|
||||
}
|
||||
|
||||
type completionDialogCmp struct {
|
||||
type completionDialogComponent struct {
|
||||
query string
|
||||
completionProvider CompletionProvider
|
||||
width int
|
||||
@@ -105,11 +105,11 @@ var completionDialogKeys = completionDialogKeyMap{
|
||||
),
|
||||
}
|
||||
|
||||
func (c *completionDialogCmp) Init() tea.Cmd {
|
||||
func (c *completionDialogComponent) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *completionDialogCmp) complete(item CompletionItemI) tea.Cmd {
|
||||
func (c *completionDialogComponent) complete(item CompletionItemI) tea.Cmd {
|
||||
value := c.pseudoSearchTextArea.Value()
|
||||
|
||||
if value == "" {
|
||||
@@ -125,7 +125,7 @@ func (c *completionDialogCmp) complete(item CompletionItemI) tea.Cmd {
|
||||
)
|
||||
}
|
||||
|
||||
func (c *completionDialogCmp) close() tea.Cmd {
|
||||
func (c *completionDialogComponent) close() tea.Cmd {
|
||||
c.listView.SetItems([]CompletionItemI{})
|
||||
c.pseudoSearchTextArea.Reset()
|
||||
c.pseudoSearchTextArea.Blur()
|
||||
@@ -133,7 +133,7 @@ func (c *completionDialogCmp) close() tea.Cmd {
|
||||
return util.CmdHandler(CompletionDialogCloseMsg{})
|
||||
}
|
||||
|
||||
func (c *completionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (c *completionDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
@@ -203,7 +203,7 @@ func (c *completionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return c, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
func (c *completionDialogCmp) View() string {
|
||||
func (c *completionDialogComponent) View() string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
@@ -231,15 +231,15 @@ func (c *completionDialogCmp) View() string {
|
||||
Render(c.listView.View())
|
||||
}
|
||||
|
||||
func (c *completionDialogCmp) SetWidth(width int) {
|
||||
func (c *completionDialogComponent) SetWidth(width int) {
|
||||
c.width = width
|
||||
}
|
||||
|
||||
func (c *completionDialogCmp) BindingKeys() []key.Binding {
|
||||
func (c *completionDialogComponent) BindingKeys() []key.Binding {
|
||||
return layout.KeyMapToSlice(completionDialogKeys)
|
||||
}
|
||||
|
||||
func NewCompletionDialogCmp(completionProvider CompletionProvider) CompletionDialog {
|
||||
func NewCompletionDialogComponent(completionProvider CompletionProvider) CompletionDialog {
|
||||
ti := textarea.New()
|
||||
|
||||
items, err := completionProvider.GetChildEntries("")
|
||||
@@ -254,7 +254,7 @@ func NewCompletionDialogCmp(completionProvider CompletionProvider) CompletionDia
|
||||
false,
|
||||
)
|
||||
|
||||
return &completionDialogCmp{
|
||||
return &completionDialogComponent{
|
||||
query: "",
|
||||
completionProvider: completionProvider,
|
||||
pseudoSearchTextArea: ti,
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/sst/opencode/internal/app"
|
||||
"github.com/sst/opencode/internal/util"
|
||||
)
|
||||
|
||||
@@ -12,13 +12,14 @@ import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/atotto/clipboard"
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
"github.com/charmbracelet/bubbles/v2/textinput"
|
||||
"github.com/charmbracelet/bubbles/v2/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/app"
|
||||
"github.com/sst/opencode/internal/image"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
@@ -82,7 +83,7 @@ var filePickerKeyMap = FilePrickerKeyMap{
|
||||
),
|
||||
}
|
||||
|
||||
type filepickerCmp struct {
|
||||
type filepickerComponent struct {
|
||||
basePath string
|
||||
width int
|
||||
height int
|
||||
@@ -118,18 +119,18 @@ type AttachmentAddedMsg struct {
|
||||
Attachment app.Attachment
|
||||
}
|
||||
|
||||
func (f *filepickerCmp) Init() tea.Cmd {
|
||||
func (f *filepickerComponent) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *filepickerCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (f *filepickerComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmd tea.Cmd
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
f.width = 60
|
||||
f.height = 20
|
||||
f.viewport.Width = 80
|
||||
f.viewport.Height = 22
|
||||
f.viewport.SetWidth(80)
|
||||
f.viewport.SetHeight(22)
|
||||
f.cursor = 0
|
||||
f.getCurrentFileBelowCursor()
|
||||
case tea.KeyMsg:
|
||||
@@ -236,7 +237,7 @@ func (f *filepickerCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return f, cmd
|
||||
}
|
||||
|
||||
func (f *filepickerCmp) addAttachmentToMessage() (tea.Model, tea.Cmd) {
|
||||
func (f *filepickerComponent) addAttachmentToMessage() (tea.Model, tea.Cmd) {
|
||||
// modeInfo := GetSelectedModel(config.Get())
|
||||
// if !modeInfo.SupportsAttachments {
|
||||
// status.Error(fmt.Sprintf("Model %s doesn't support attachments", modeInfo.Name))
|
||||
@@ -273,7 +274,7 @@ func (f *filepickerCmp) addAttachmentToMessage() (tea.Model, tea.Cmd) {
|
||||
return f, util.CmdHandler(AttachmentAddedMsg{attachment})
|
||||
}
|
||||
|
||||
func (f *filepickerCmp) View() string {
|
||||
func (f *filepickerComponent) View() string {
|
||||
t := theme.CurrentTheme()
|
||||
const maxVisibleDirs = 20
|
||||
const maxWidth = 80
|
||||
@@ -333,7 +334,7 @@ func (f *filepickerCmp) View() string {
|
||||
Render(f.cwd.View())
|
||||
|
||||
viewportstyle := lipgloss.NewStyle().
|
||||
Width(f.viewport.Width).
|
||||
Width(f.viewport.Width()).
|
||||
Background(t.Background()).
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderForeground(t.TextMuted()).
|
||||
@@ -366,21 +367,21 @@ func (f *filepickerCmp) View() string {
|
||||
return lipgloss.JoinHorizontal(lipgloss.Center, contentStyle.Render(content), viewportstyle)
|
||||
}
|
||||
|
||||
type FilepickerCmp interface {
|
||||
tea.Model
|
||||
type FilepickerComponent interface {
|
||||
layout.ModelWithView
|
||||
ToggleFilepicker(showFilepicker bool)
|
||||
IsCWDFocused() bool
|
||||
}
|
||||
|
||||
func (f *filepickerCmp) ToggleFilepicker(showFilepicker bool) {
|
||||
func (f *filepickerComponent) ToggleFilepicker(showFilepicker bool) {
|
||||
f.ShowFilePicker = showFilepicker
|
||||
}
|
||||
|
||||
func (f *filepickerCmp) IsCWDFocused() bool {
|
||||
func (f *filepickerComponent) IsCWDFocused() bool {
|
||||
return f.cwd.Focused()
|
||||
}
|
||||
|
||||
func NewFilepickerCmp(app *app.App) FilepickerCmp {
|
||||
func NewFilepickerCmp(app *app.App) FilepickerComponent {
|
||||
homepath, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
slog.Error("error loading user files")
|
||||
@@ -388,16 +389,16 @@ func NewFilepickerCmp(app *app.App) FilepickerCmp {
|
||||
}
|
||||
baseDir := DirNode{parent: nil, directory: homepath}
|
||||
dirs := readDir(homepath, false)
|
||||
viewport := viewport.New(0, 0)
|
||||
viewport := viewport.New() // viewport.New(0, 0)
|
||||
currentDirectory := textinput.New()
|
||||
currentDirectory.CharLimit = 200
|
||||
currentDirectory.Width = 44
|
||||
currentDirectory.Cursor.Blink = true
|
||||
currentDirectory.SetWidth(44)
|
||||
// currentDirectory.Cursor.Blink = true
|
||||
currentDirectory.SetValue(baseDir.directory)
|
||||
return &filepickerCmp{cwdDetails: &baseDir, dirs: dirs, cursorChain: make(stack, 0), viewport: viewport, cwd: currentDirectory, app: app}
|
||||
return &filepickerComponent{cwdDetails: &baseDir, dirs: dirs, cursorChain: make(stack, 0), viewport: viewport, cwd: currentDirectory, app: app}
|
||||
}
|
||||
|
||||
func (f *filepickerCmp) getCurrentFileBelowCursor() {
|
||||
func (f *filepickerComponent) getCurrentFileBelowCursor() {
|
||||
if len(f.dirs) == 0 || f.cursor < 0 || f.cursor >= len(f.dirs) {
|
||||
slog.Error(fmt.Sprintf("Invalid cursor position. Dirs length: %d, Cursor: %d", len(f.dirs), f.cursor))
|
||||
f.viewport.SetContent("Preview unavailable")
|
||||
@@ -410,7 +411,7 @@ func (f *filepickerCmp) getCurrentFileBelowCursor() {
|
||||
fullPath := f.cwdDetails.directory + "/" + dir.Name()
|
||||
|
||||
go func() {
|
||||
imageString, err := image.ImagePreview(f.viewport.Width-4, fullPath)
|
||||
imageString, err := image.ImagePreview(f.viewport.Width()-4, fullPath)
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
f.viewport.SetContent("Preview unavailable")
|
||||
|
||||
@@ -3,198 +3,118 @@ package dialog
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/components/modal"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
)
|
||||
|
||||
type helpCmp struct {
|
||||
width int
|
||||
height int
|
||||
keys []key.Binding
|
||||
type helpDialog struct {
|
||||
width int
|
||||
height int
|
||||
modal *modal.Modal
|
||||
bindings []key.Binding
|
||||
}
|
||||
|
||||
func (h *helpCmp) Init() tea.Cmd {
|
||||
// func (i bindingItem) Render(selected bool, width int) string {
|
||||
// t := theme.CurrentTheme()
|
||||
// baseStyle := styles.BaseStyle().
|
||||
// Width(width - 2).
|
||||
// Background(t.BackgroundElement())
|
||||
//
|
||||
// if selected {
|
||||
// baseStyle = baseStyle.
|
||||
// Background(t.Primary()).
|
||||
// Foreground(t.BackgroundElement()).
|
||||
// Bold(true)
|
||||
// } else {
|
||||
// baseStyle = baseStyle.
|
||||
// Foreground(t.Text())
|
||||
// }
|
||||
//
|
||||
// return baseStyle.Padding(0, 1).Render(i.binding.Help().Desc)
|
||||
// }
|
||||
|
||||
func (h *helpDialog) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *helpCmp) SetBindings(k []key.Binding) {
|
||||
h.keys = k
|
||||
}
|
||||
|
||||
func (h *helpCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (h *helpDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
h.width = 90
|
||||
h.width = msg.Width
|
||||
h.height = msg.Height
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func removeDuplicateBindings(bindings []key.Binding) []key.Binding {
|
||||
seen := make(map[string]struct{})
|
||||
result := make([]key.Binding, 0, len(bindings))
|
||||
// func removeDuplicateBindings(bindings []key.Binding) []key.Binding {
|
||||
// seen := make(map[string]struct{})
|
||||
// result := make([]key.Binding, 0, len(bindings))
|
||||
//
|
||||
// // Process bindings in reverse order
|
||||
// for i := len(bindings) - 1; i >= 0; i-- {
|
||||
// b := bindings[i]
|
||||
// k := strings.Join(b.Keys(), " ")
|
||||
// if _, ok := seen[k]; ok {
|
||||
// // duplicate, skip
|
||||
// continue
|
||||
// }
|
||||
// seen[k] = struct{}{}
|
||||
// // Add to the beginning of result to maintain original order
|
||||
// result = append([]key.Binding{b}, result...)
|
||||
// }
|
||||
//
|
||||
// return result
|
||||
// }
|
||||
|
||||
// Process bindings in reverse order
|
||||
for i := len(bindings) - 1; i >= 0; i-- {
|
||||
b := bindings[i]
|
||||
k := strings.Join(b.Keys(), " ")
|
||||
if _, ok := seen[k]; ok {
|
||||
// duplicate, skip
|
||||
continue
|
||||
}
|
||||
seen[k] = struct{}{}
|
||||
// Add to the beginning of result to maintain original order
|
||||
result = append([]key.Binding{b}, result...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (h *helpCmp) render() string {
|
||||
func (h *helpDialog) View() string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
helpKeyStyle := styles.Bold().
|
||||
Background(t.Background()).
|
||||
keyStyle := lipgloss.NewStyle().
|
||||
Background(t.BackgroundElement()).
|
||||
Foreground(t.Text()).
|
||||
Padding(0, 1, 0, 0)
|
||||
|
||||
helpDescStyle := styles.Regular().
|
||||
Background(t.Background()).
|
||||
Bold(true)
|
||||
descStyle := lipgloss.NewStyle().
|
||||
Background(t.BackgroundElement()).
|
||||
Foreground(t.TextMuted())
|
||||
|
||||
// Compile list of bindings to render
|
||||
bindings := removeDuplicateBindings(h.keys)
|
||||
|
||||
// Enumerate through each group of bindings, populating a series of
|
||||
// pairs of columns, one for keys, one for descriptions
|
||||
var (
|
||||
pairs []string
|
||||
width int
|
||||
rows = 12 - 2
|
||||
)
|
||||
|
||||
for i := 0; i < len(bindings); i += rows {
|
||||
var (
|
||||
keys []string
|
||||
descs []string
|
||||
)
|
||||
for j := i; j < min(i+rows, len(bindings)); j++ {
|
||||
keys = append(keys, helpKeyStyle.Render(bindings[j].Help().Key))
|
||||
descs = append(descs, helpDescStyle.Render(bindings[j].Help().Desc))
|
||||
}
|
||||
|
||||
// Render pair of columns; beyond the first pair, render a three space
|
||||
// left margin, in order to visually separate the pairs.
|
||||
var cols []string
|
||||
if len(pairs) > 0 {
|
||||
cols = []string{baseStyle.Render(" ")}
|
||||
}
|
||||
|
||||
maxDescWidth := 0
|
||||
for _, desc := range descs {
|
||||
if maxDescWidth < lipgloss.Width(desc) {
|
||||
maxDescWidth = lipgloss.Width(desc)
|
||||
}
|
||||
}
|
||||
for i := range descs {
|
||||
remainingWidth := maxDescWidth - lipgloss.Width(descs[i])
|
||||
if remainingWidth > 0 {
|
||||
descs[i] = descs[i] + baseStyle.Render(strings.Repeat(" ", remainingWidth))
|
||||
}
|
||||
}
|
||||
maxKeyWidth := 0
|
||||
for _, key := range keys {
|
||||
if maxKeyWidth < lipgloss.Width(key) {
|
||||
maxKeyWidth = lipgloss.Width(key)
|
||||
}
|
||||
}
|
||||
for i := range keys {
|
||||
remainingWidth := maxKeyWidth - lipgloss.Width(keys[i])
|
||||
if remainingWidth > 0 {
|
||||
keys[i] = keys[i] + baseStyle.Render(strings.Repeat(" ", remainingWidth))
|
||||
lines := []string{}
|
||||
for _, b := range h.bindings {
|
||||
content := keyStyle.Render(b.Help().Key)
|
||||
content += descStyle.Render(" " + b.Help().Desc)
|
||||
for i, key := range b.Keys() {
|
||||
if i == 0 {
|
||||
keyString := " (" + strings.ToUpper(key) + ")"
|
||||
// space := max(h.width-lipgloss.Width(content)-lipgloss.Width(keyString), 0)
|
||||
// spacer := strings.Repeat(" ", space)
|
||||
// content += descStyle.Render(spacer)
|
||||
content += descStyle.Render(keyString)
|
||||
}
|
||||
}
|
||||
|
||||
cols = append(cols,
|
||||
strings.Join(keys, "\n"),
|
||||
strings.Join(descs, "\n"),
|
||||
)
|
||||
|
||||
pair := baseStyle.Render(lipgloss.JoinHorizontal(lipgloss.Top, cols...))
|
||||
// check whether it exceeds the maximum width avail (the width of the
|
||||
// terminal, subtracting 2 for the borders).
|
||||
width += lipgloss.Width(pair)
|
||||
if width > h.width-2 {
|
||||
break
|
||||
}
|
||||
pairs = append(pairs, pair)
|
||||
lines = append(lines, content)
|
||||
}
|
||||
|
||||
// https://github.com/charmbracelet/lipgloss/issues/209
|
||||
if len(pairs) > 1 {
|
||||
prefix := pairs[:len(pairs)-1]
|
||||
lastPair := pairs[len(pairs)-1]
|
||||
prefix = append(prefix, lipgloss.Place(
|
||||
lipgloss.Width(lastPair), // width
|
||||
lipgloss.Height(prefix[0]), // height
|
||||
lipgloss.Left, // x
|
||||
lipgloss.Top, // y
|
||||
lastPair, // content
|
||||
lipgloss.WithWhitespaceBackground(t.Background()),
|
||||
))
|
||||
content := baseStyle.Width(h.width).Render(
|
||||
lipgloss.JoinHorizontal(
|
||||
lipgloss.Top,
|
||||
prefix...,
|
||||
),
|
||||
)
|
||||
return content
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func (h *helpDialog) Render(background string) string {
|
||||
return h.modal.Render(h.View(), background)
|
||||
}
|
||||
|
||||
func (h *helpDialog) Close() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
type HelpDialog interface {
|
||||
layout.Modal
|
||||
}
|
||||
|
||||
func NewHelpDialog(bindings ...key.Binding) HelpDialog {
|
||||
return &helpDialog{
|
||||
bindings: bindings,
|
||||
modal: modal.New(),
|
||||
}
|
||||
|
||||
// Join pairs of columns and enclose in a border
|
||||
content := baseStyle.Width(h.width).Render(
|
||||
lipgloss.JoinHorizontal(
|
||||
lipgloss.Top,
|
||||
pairs...,
|
||||
),
|
||||
)
|
||||
return content
|
||||
}
|
||||
|
||||
func (h *helpCmp) View() string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
content := h.render()
|
||||
header := baseStyle.
|
||||
Bold(true).
|
||||
Width(lipgloss.Width(content)).
|
||||
Foreground(t.Primary()).
|
||||
Render("Keyboard Shortcuts")
|
||||
|
||||
return baseStyle.Padding(1).
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderForeground(t.TextMuted()).
|
||||
Width(h.width).
|
||||
BorderBackground(t.Background()).
|
||||
Render(
|
||||
lipgloss.JoinVertical(lipgloss.Center,
|
||||
header,
|
||||
baseStyle.Render(strings.Repeat(" ", lipgloss.Width(header))),
|
||||
content,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
type HelpCmp interface {
|
||||
tea.Model
|
||||
SetBindings([]key.Binding)
|
||||
}
|
||||
|
||||
func NewHelpCmp() HelpCmp {
|
||||
return &helpCmp{}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package dialog
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
|
||||
@@ -7,11 +7,13 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/app"
|
||||
"github.com/sst/opencode/internal/components/modal"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/state"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
"github.com/sst/opencode/internal/util"
|
||||
@@ -19,25 +21,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
numVisibleModels = 10
|
||||
numVisibleModels = 6
|
||||
maxDialogWidth = 40
|
||||
)
|
||||
|
||||
// CloseModelDialogMsg is sent when a model is selected
|
||||
type CloseModelDialogMsg struct {
|
||||
Provider *client.ProviderInfo
|
||||
Model *client.ProviderModel
|
||||
}
|
||||
|
||||
// ModelDialog interface for the model selection dialog
|
||||
type ModelDialog interface {
|
||||
tea.Model
|
||||
layout.Bindings
|
||||
|
||||
SetProviders(providers []client.ProviderInfo)
|
||||
layout.Modal
|
||||
}
|
||||
|
||||
type modelDialogCmp struct {
|
||||
type modelDialog struct {
|
||||
app *app.App
|
||||
availableProviders []client.ProviderInfo
|
||||
provider client.ProviderInfo
|
||||
@@ -48,6 +41,8 @@ type modelDialogCmp struct {
|
||||
scrollOffset int
|
||||
hScrollOffset int
|
||||
hScrollPossible bool
|
||||
|
||||
modal *modal.Modal
|
||||
}
|
||||
|
||||
type modelKeyMap struct {
|
||||
@@ -57,27 +52,23 @@ type modelKeyMap struct {
|
||||
Right key.Binding
|
||||
Enter key.Binding
|
||||
Escape key.Binding
|
||||
J key.Binding
|
||||
K key.Binding
|
||||
H key.Binding
|
||||
L key.Binding
|
||||
}
|
||||
|
||||
var modelKeys = modelKeyMap{
|
||||
Up: key.NewBinding(
|
||||
key.WithKeys("up"),
|
||||
key.WithKeys("up", "k"),
|
||||
key.WithHelp("↑", "previous model"),
|
||||
),
|
||||
Down: key.NewBinding(
|
||||
key.WithKeys("down"),
|
||||
key.WithKeys("down", "j"),
|
||||
key.WithHelp("↓", "next model"),
|
||||
),
|
||||
Left: key.NewBinding(
|
||||
key.WithKeys("left"),
|
||||
key.WithKeys("left", "h"),
|
||||
key.WithHelp("←", "scroll left"),
|
||||
),
|
||||
Right: key.NewBinding(
|
||||
key.WithKeys("right"),
|
||||
key.WithKeys("right", "l"),
|
||||
key.WithHelp("→", "scroll right"),
|
||||
),
|
||||
Enter: key.NewBinding(
|
||||
@@ -88,25 +79,9 @@ var modelKeys = modelKeyMap{
|
||||
key.WithKeys("esc"),
|
||||
key.WithHelp("esc", "close"),
|
||||
),
|
||||
J: key.NewBinding(
|
||||
key.WithKeys("j"),
|
||||
key.WithHelp("j", "next model"),
|
||||
),
|
||||
K: key.NewBinding(
|
||||
key.WithKeys("k"),
|
||||
key.WithHelp("k", "previous model"),
|
||||
),
|
||||
H: key.NewBinding(
|
||||
key.WithKeys("h"),
|
||||
key.WithHelp("h", "scroll left"),
|
||||
),
|
||||
L: key.NewBinding(
|
||||
key.WithKeys("l"),
|
||||
key.WithHelp("l", "scroll right"),
|
||||
),
|
||||
}
|
||||
|
||||
func (m *modelDialogCmp) Init() tea.Cmd {
|
||||
func (m *modelDialog) Init() tea.Cmd {
|
||||
// cfg := config.Get()
|
||||
// modelInfo := GetSelectedModel(cfg)
|
||||
// m.availableProviders = getEnabledProviders(cfg)
|
||||
@@ -116,40 +91,31 @@ func (m *modelDialogCmp) Init() tea.Cmd {
|
||||
// m.hScrollOffset = findProviderIndex(m.availableProviders, m.provider)
|
||||
|
||||
// m.setupModelsForProvider(m.provider)
|
||||
|
||||
m.availableProviders, _ = m.app.ListProviders(context.Background())
|
||||
m.hScrollOffset = 0
|
||||
m.hScrollPossible = len(m.availableProviders) > 1
|
||||
m.provider = m.availableProviders[m.hScrollOffset]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *modelDialogCmp) SetProviders(providers []client.ProviderInfo) {
|
||||
m.availableProviders = providers
|
||||
}
|
||||
|
||||
func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (m *modelDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
case key.Matches(msg, modelKeys.Up) || key.Matches(msg, modelKeys.K):
|
||||
case key.Matches(msg, modelKeys.Up):
|
||||
m.moveSelectionUp()
|
||||
case key.Matches(msg, modelKeys.Down) || key.Matches(msg, modelKeys.J):
|
||||
case key.Matches(msg, modelKeys.Down):
|
||||
m.moveSelectionDown()
|
||||
case key.Matches(msg, modelKeys.Left) || key.Matches(msg, modelKeys.H):
|
||||
case key.Matches(msg, modelKeys.Left):
|
||||
if m.hScrollPossible {
|
||||
m.switchProvider(-1)
|
||||
}
|
||||
case key.Matches(msg, modelKeys.Right) || key.Matches(msg, modelKeys.L):
|
||||
case key.Matches(msg, modelKeys.Right):
|
||||
if m.hScrollPossible {
|
||||
m.switchProvider(1)
|
||||
}
|
||||
case key.Matches(msg, modelKeys.Enter):
|
||||
models := m.models()
|
||||
return m, util.CmdHandler(CloseModelDialogMsg{Provider: &m.provider, Model: &models[m.selectedIdx]})
|
||||
cmd := util.CmdHandler(state.ModelSelectedMsg{Provider: m.provider, Model: models[m.selectedIdx]})
|
||||
return m, tea.Batch(cmd, util.CmdHandler(modal.CloseModalMsg{}))
|
||||
case key.Matches(msg, modelKeys.Escape):
|
||||
return m, util.CmdHandler(CloseModelDialogMsg{})
|
||||
return m, util.CmdHandler(modal.CloseModalMsg{})
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
m.width = msg.Width
|
||||
@@ -159,7 +125,7 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *modelDialogCmp) models() []client.ProviderModel {
|
||||
func (m *modelDialog) models() []client.ProviderModel {
|
||||
models := slices.SortedFunc(maps.Values(m.provider.Models), func(a, b client.ProviderModel) int {
|
||||
return strings.Compare(*a.Name, *b.Name)
|
||||
})
|
||||
@@ -167,7 +133,7 @@ func (m *modelDialogCmp) models() []client.ProviderModel {
|
||||
}
|
||||
|
||||
// moveSelectionUp moves the selection up or wraps to bottom
|
||||
func (m *modelDialogCmp) moveSelectionUp() {
|
||||
func (m *modelDialog) moveSelectionUp() {
|
||||
if m.selectedIdx > 0 {
|
||||
m.selectedIdx--
|
||||
} else {
|
||||
@@ -182,7 +148,7 @@ func (m *modelDialogCmp) moveSelectionUp() {
|
||||
}
|
||||
|
||||
// moveSelectionDown moves the selection down or wraps to top
|
||||
func (m *modelDialogCmp) moveSelectionDown() {
|
||||
func (m *modelDialog) moveSelectionDown() {
|
||||
if m.selectedIdx < len(m.provider.Models)-1 {
|
||||
m.selectedIdx++
|
||||
} else {
|
||||
@@ -196,7 +162,7 @@ func (m *modelDialogCmp) moveSelectionDown() {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *modelDialogCmp) switchProvider(offset int) {
|
||||
func (m *modelDialog) switchProvider(offset int) {
|
||||
newOffset := m.hScrollOffset + offset
|
||||
|
||||
// Ensure we stay within bounds
|
||||
@@ -212,9 +178,11 @@ func (m *modelDialogCmp) switchProvider(offset int) {
|
||||
m.setupModelsForProvider(m.provider.Id)
|
||||
}
|
||||
|
||||
func (m *modelDialogCmp) View() string {
|
||||
func (m *modelDialog) View() string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
baseStyle := lipgloss.NewStyle().
|
||||
Background(t.BackgroundElement()).
|
||||
Foreground(t.Text())
|
||||
|
||||
// Capitalize first letter of provider name
|
||||
title := baseStyle.
|
||||
@@ -232,8 +200,10 @@ func (m *modelDialogCmp) View() string {
|
||||
for i := m.scrollOffset; i < endIdx; i++ {
|
||||
itemStyle := baseStyle.Width(maxDialogWidth)
|
||||
if i == m.selectedIdx {
|
||||
itemStyle = itemStyle.Background(t.Primary()).
|
||||
Foreground(t.Background()).Bold(true)
|
||||
itemStyle = itemStyle.
|
||||
Background(t.Primary()).
|
||||
Foreground(t.BackgroundElement()).
|
||||
Bold(true)
|
||||
}
|
||||
modelItems = append(modelItems, itemStyle.Render(*models[i].Name))
|
||||
}
|
||||
@@ -247,15 +217,10 @@ func (m *modelDialogCmp) View() string {
|
||||
scrollIndicator,
|
||||
)
|
||||
|
||||
return baseStyle.Padding(1, 2).
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderBackground(t.Background()).
|
||||
BorderForeground(t.TextMuted()).
|
||||
Width(lipgloss.Width(content) + 4).
|
||||
Render(content)
|
||||
return content
|
||||
}
|
||||
|
||||
func (m *modelDialogCmp) getScrollIndicators(maxWidth int) string {
|
||||
func (m *modelDialog) getScrollIndicators(maxWidth int) string {
|
||||
var indicator string
|
||||
|
||||
if len(m.provider.Models) > numVisibleModels {
|
||||
@@ -291,10 +256,6 @@ func (m *modelDialogCmp) getScrollIndicators(maxWidth int) string {
|
||||
Render(indicator)
|
||||
}
|
||||
|
||||
func (m *modelDialogCmp) BindingKeys() []key.Binding {
|
||||
return layout.KeyMapToSlice(modelKeys)
|
||||
}
|
||||
|
||||
// findProviderIndex returns the index of the provider in the list, or -1 if not found
|
||||
// func findProviderIndex(providers []string, provider string) int {
|
||||
// for i, p := range providers {
|
||||
@@ -305,7 +266,7 @@ func (m *modelDialogCmp) BindingKeys() []key.Binding {
|
||||
// return -1
|
||||
// }
|
||||
|
||||
func (m *modelDialogCmp) setupModelsForProvider(_ string) {
|
||||
func (m *modelDialog) setupModelsForProvider(_ string) {
|
||||
m.selectedIdx = 0
|
||||
m.scrollOffset = 0
|
||||
|
||||
@@ -331,8 +292,22 @@ func (m *modelDialogCmp) setupModelsForProvider(_ string) {
|
||||
// }
|
||||
}
|
||||
|
||||
func NewModelDialogCmp(app *app.App) ModelDialog {
|
||||
return &modelDialogCmp{
|
||||
app: app,
|
||||
func (m *modelDialog) Render(background string) string {
|
||||
return m.modal.Render(m.View(), background)
|
||||
}
|
||||
|
||||
func (s *modelDialog) Close() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewModelDialog(app *app.App) ModelDialog {
|
||||
availableProviders, _ := app.ListProviders(context.Background())
|
||||
|
||||
return &modelDialog{
|
||||
availableProviders: availableProviders,
|
||||
hScrollOffset: 0,
|
||||
hScrollPossible: len(availableProviders) > 1,
|
||||
provider: availableProviders[0],
|
||||
modal: modal.New(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package dialog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
"github.com/charmbracelet/bubbles/v2/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
@@ -28,9 +28,9 @@ type PermissionResponseMsg struct {
|
||||
Action PermissionAction
|
||||
}
|
||||
|
||||
// PermissionDialogCmp interface for permission dialog component
|
||||
type PermissionDialogCmp interface {
|
||||
tea.Model
|
||||
// PermissionDialogComponent interface for permission dialog component
|
||||
type PermissionDialogComponent interface {
|
||||
layout.ModelWithView
|
||||
layout.Bindings
|
||||
// SetPermissions(permission permission.PermissionRequest) tea.Cmd
|
||||
}
|
||||
@@ -76,8 +76,8 @@ var permissionsKeys = permissionsMapping{
|
||||
),
|
||||
}
|
||||
|
||||
// permissionDialogCmp is the implementation of PermissionDialog
|
||||
type permissionDialogCmp struct {
|
||||
// permissionDialogComponent is the implementation of PermissionDialog
|
||||
type permissionDialogComponent struct {
|
||||
width int
|
||||
height int
|
||||
// permission permission.PermissionRequest
|
||||
@@ -89,11 +89,11 @@ type permissionDialogCmp struct {
|
||||
markdownCache map[string]string
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) Init() tea.Cmd {
|
||||
func (p *permissionDialogComponent) Init() tea.Cmd {
|
||||
return p.contentViewPort.Init()
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (p *permissionDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
|
||||
switch msg := msg.(type) {
|
||||
@@ -129,7 +129,7 @@ func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return p, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) selectCurrentOption() tea.Cmd {
|
||||
func (p *permissionDialogComponent) selectCurrentOption() tea.Cmd {
|
||||
var action PermissionAction
|
||||
|
||||
switch p.selectedOption {
|
||||
@@ -144,7 +144,7 @@ func (p *permissionDialogCmp) selectCurrentOption() tea.Cmd {
|
||||
return util.CmdHandler(PermissionResponseMsg{Action: action}) // , Permission: p.permission})
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) renderButtons() string {
|
||||
func (p *permissionDialogComponent) renderButtons() string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
@@ -190,7 +190,7 @@ func (p *permissionDialogCmp) renderButtons() string {
|
||||
return content
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) renderHeader() string {
|
||||
func (p *permissionDialogComponent) renderHeader() string {
|
||||
return "NOT IMPLEMENTED"
|
||||
// t := theme.CurrentTheme()
|
||||
// baseStyle := styles.BaseStyle()
|
||||
@@ -246,7 +246,7 @@ func (p *permissionDialogCmp) renderHeader() string {
|
||||
// return lipgloss.NewStyle().Background(t.Background()).Render(lipgloss.JoinVertical(lipgloss.Left, headerParts...))
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) renderBashContent() string {
|
||||
func (p *permissionDialogComponent) renderBashContent() string {
|
||||
// t := theme.CurrentTheme()
|
||||
// baseStyle := styles.BaseStyle()
|
||||
//
|
||||
@@ -257,7 +257,7 @@ func (p *permissionDialogCmp) renderBashContent() string {
|
||||
// renderedContent := p.GetOrSetMarkdown(p.permission.ID, func() (string, error) {
|
||||
// r := styles.GetMarkdownRenderer(p.width - 10)
|
||||
// s, err := r.Render(content)
|
||||
// return styles.ForceReplaceBackgroundWithLipgloss(s, t.Background()), err
|
||||
// return s
|
||||
// })
|
||||
//
|
||||
// finalContent := baseStyle.
|
||||
@@ -269,7 +269,7 @@ func (p *permissionDialogCmp) renderBashContent() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) renderEditContent() string {
|
||||
func (p *permissionDialogComponent) renderEditContent() string {
|
||||
// if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok {
|
||||
// diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) {
|
||||
// return diff.FormatDiff(pr.Diff, diff.WithTotalWidth(p.contentViewPort.Width))
|
||||
@@ -281,7 +281,7 @@ func (p *permissionDialogCmp) renderEditContent() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) renderPatchContent() string {
|
||||
func (p *permissionDialogComponent) renderPatchContent() string {
|
||||
// if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok {
|
||||
// diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) {
|
||||
// return diff.FormatDiff(pr.Diff, diff.WithTotalWidth(p.contentViewPort.Width))
|
||||
@@ -293,7 +293,7 @@ func (p *permissionDialogCmp) renderPatchContent() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) renderWriteContent() string {
|
||||
func (p *permissionDialogComponent) renderWriteContent() string {
|
||||
// if pr, ok := p.permission.Params.(tools.WritePermissionsParams); ok {
|
||||
// // Use the cache for diff rendering
|
||||
// diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) {
|
||||
@@ -306,7 +306,7 @@ func (p *permissionDialogCmp) renderWriteContent() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) renderFetchContent() string {
|
||||
func (p *permissionDialogComponent) renderFetchContent() string {
|
||||
// t := theme.CurrentTheme()
|
||||
// baseStyle := styles.BaseStyle()
|
||||
//
|
||||
@@ -317,7 +317,7 @@ func (p *permissionDialogCmp) renderFetchContent() string {
|
||||
// renderedContent := p.GetOrSetMarkdown(p.permission.ID, func() (string, error) {
|
||||
// r := styles.GetMarkdownRenderer(p.width - 10)
|
||||
// s, err := r.Render(content)
|
||||
// return styles.ForceReplaceBackgroundWithLipgloss(s, t.Background()), err
|
||||
// return s
|
||||
// })
|
||||
//
|
||||
// finalContent := baseStyle.
|
||||
@@ -329,7 +329,7 @@ func (p *permissionDialogCmp) renderFetchContent() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) renderDefaultContent() string {
|
||||
func (p *permissionDialogComponent) renderDefaultContent() string {
|
||||
// t := theme.CurrentTheme()
|
||||
// baseStyle := styles.BaseStyle()
|
||||
//
|
||||
@@ -339,7 +339,7 @@ func (p *permissionDialogCmp) renderDefaultContent() string {
|
||||
// renderedContent := p.GetOrSetMarkdown(p.permission.ID, func() (string, error) {
|
||||
// r := styles.GetMarkdownRenderer(p.width - 10)
|
||||
// s, err := r.Render(content)
|
||||
// return styles.ForceReplaceBackgroundWithLipgloss(s, t.Background()), err
|
||||
// return s
|
||||
// })
|
||||
//
|
||||
// finalContent := baseStyle.
|
||||
@@ -354,7 +354,7 @@ func (p *permissionDialogCmp) renderDefaultContent() string {
|
||||
return p.styleViewport()
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) styleViewport() string {
|
||||
func (p *permissionDialogComponent) styleViewport() string {
|
||||
t := theme.CurrentTheme()
|
||||
contentStyle := lipgloss.NewStyle().
|
||||
Background(t.Background())
|
||||
@@ -362,7 +362,7 @@ func (p *permissionDialogCmp) styleViewport() string {
|
||||
return contentStyle.Render(p.contentViewPort.View())
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) render() string {
|
||||
func (p *permissionDialogComponent) render() string {
|
||||
return "NOT IMPLEMENTED"
|
||||
// t := theme.CurrentTheme()
|
||||
// baseStyle := styles.BaseStyle()
|
||||
@@ -420,15 +420,15 @@ func (p *permissionDialogCmp) render() string {
|
||||
// )
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) View() string {
|
||||
func (p *permissionDialogComponent) View() string {
|
||||
return p.render()
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) BindingKeys() []key.Binding {
|
||||
func (p *permissionDialogComponent) BindingKeys() []key.Binding {
|
||||
return layout.KeyMapToSlice(permissionsKeys)
|
||||
}
|
||||
|
||||
func (p *permissionDialogCmp) SetSize() tea.Cmd {
|
||||
func (p *permissionDialogComponent) SetSize() tea.Cmd {
|
||||
// if p.permission.ID == "" {
|
||||
// return nil
|
||||
// }
|
||||
@@ -458,7 +458,7 @@ func (p *permissionDialogCmp) SetSize() tea.Cmd {
|
||||
// }
|
||||
|
||||
// Helper to get or set cached diff content
|
||||
func (c *permissionDialogCmp) GetOrSetDiff(key string, generator func() (string, error)) string {
|
||||
func (c *permissionDialogComponent) GetOrSetDiff(key string, generator func() (string, error)) string {
|
||||
if cached, ok := c.diffCache[key]; ok {
|
||||
return cached
|
||||
}
|
||||
@@ -474,7 +474,7 @@ func (c *permissionDialogCmp) GetOrSetDiff(key string, generator func() (string,
|
||||
}
|
||||
|
||||
// Helper to get or set cached markdown content
|
||||
func (c *permissionDialogCmp) GetOrSetMarkdown(key string, generator func() (string, error)) string {
|
||||
func (c *permissionDialogComponent) GetOrSetMarkdown(key string, generator func() (string, error)) string {
|
||||
if cached, ok := c.markdownCache[key]; ok {
|
||||
return cached
|
||||
}
|
||||
@@ -489,11 +489,11 @@ func (c *permissionDialogCmp) GetOrSetMarkdown(key string, generator func() (str
|
||||
return content
|
||||
}
|
||||
|
||||
func NewPermissionDialogCmp() PermissionDialogCmp {
|
||||
func NewPermissionDialogCmp() PermissionDialogComponent {
|
||||
// Create viewport for content
|
||||
contentViewport := viewport.New(0, 0)
|
||||
contentViewport := viewport.New() // (0, 0)
|
||||
|
||||
return &permissionDialogCmp{
|
||||
return &permissionDialogComponent{
|
||||
contentViewPort: contentViewport,
|
||||
selectedOption: 0, // Default to "Allow"
|
||||
diffCache: make(map[string]string),
|
||||
|
||||
@@ -3,9 +3,10 @@ package dialog
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/components/modal"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
@@ -14,14 +15,17 @@ import (
|
||||
|
||||
const question = "Are you sure you want to quit?"
|
||||
|
||||
type CloseQuitMsg struct{}
|
||||
|
||||
// QuitDialog interface for the quit confirmation dialog
|
||||
type QuitDialog interface {
|
||||
tea.Model
|
||||
layout.Bindings
|
||||
layout.Modal
|
||||
IsQuitDialog() bool
|
||||
}
|
||||
|
||||
type quitDialogCmp struct {
|
||||
type quitDialog struct {
|
||||
width int
|
||||
height int
|
||||
|
||||
modal *modal.Modal
|
||||
selectedNo bool
|
||||
}
|
||||
|
||||
@@ -30,12 +34,11 @@ type helpMapping struct {
|
||||
EnterSpace key.Binding
|
||||
Yes key.Binding
|
||||
No key.Binding
|
||||
Tab key.Binding
|
||||
}
|
||||
|
||||
var helpKeys = helpMapping{
|
||||
LeftRight: key.NewBinding(
|
||||
key.WithKeys("left", "right"),
|
||||
key.WithKeys("left", "right", "h", "l", "tab"),
|
||||
key.WithHelp("←/→", "switch options"),
|
||||
),
|
||||
EnterSpace: key.NewBinding(
|
||||
@@ -43,58 +46,61 @@ var helpKeys = helpMapping{
|
||||
key.WithHelp("enter/space", "confirm"),
|
||||
),
|
||||
Yes: key.NewBinding(
|
||||
key.WithKeys("y", "Y"),
|
||||
key.WithKeys("y", "Y", "ctrl+c"),
|
||||
key.WithHelp("y/Y", "yes"),
|
||||
),
|
||||
No: key.NewBinding(
|
||||
key.WithKeys("n", "N"),
|
||||
key.WithHelp("n/N", "no"),
|
||||
),
|
||||
Tab: key.NewBinding(
|
||||
key.WithKeys("tab"),
|
||||
key.WithHelp("tab", "switch options"),
|
||||
),
|
||||
}
|
||||
|
||||
func (q *quitDialogCmp) Init() tea.Cmd {
|
||||
func (q *quitDialog) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *quitDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (q *quitDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
q.width = msg.Width
|
||||
q.height = msg.Height
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
case key.Matches(msg, helpKeys.LeftRight) || key.Matches(msg, helpKeys.Tab):
|
||||
case key.Matches(msg, helpKeys.LeftRight):
|
||||
q.selectedNo = !q.selectedNo
|
||||
return q, nil
|
||||
case key.Matches(msg, helpKeys.EnterSpace):
|
||||
if !q.selectedNo {
|
||||
return q, tea.Quit
|
||||
}
|
||||
return q, util.CmdHandler(CloseQuitMsg{})
|
||||
return q, tea.Batch(
|
||||
util.CmdHandler(modal.CloseModalMsg{}),
|
||||
)
|
||||
case key.Matches(msg, helpKeys.Yes):
|
||||
return q, tea.Quit
|
||||
case key.Matches(msg, helpKeys.No):
|
||||
return q, util.CmdHandler(CloseQuitMsg{})
|
||||
return q, tea.Batch(
|
||||
util.CmdHandler(modal.CloseModalMsg{}),
|
||||
)
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (q *quitDialogCmp) View() string {
|
||||
func (q *quitDialog) Render(background string) string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
yesStyle := baseStyle
|
||||
noStyle := baseStyle
|
||||
spacerStyle := baseStyle.Background(t.Background())
|
||||
spacerStyle := baseStyle.Background(t.BackgroundElement())
|
||||
|
||||
if q.selectedNo {
|
||||
noStyle = noStyle.Background(t.Primary()).Foreground(t.Background())
|
||||
yesStyle = yesStyle.Background(t.Background()).Foreground(t.Primary())
|
||||
noStyle = noStyle.Background(t.Primary()).Foreground(t.BackgroundElement())
|
||||
yesStyle = yesStyle.Background(t.BackgroundElement()).Foreground(t.Primary())
|
||||
} else {
|
||||
yesStyle = yesStyle.Background(t.Primary()).Foreground(t.Background())
|
||||
noStyle = noStyle.Background(t.Background()).Foreground(t.Primary())
|
||||
yesStyle = yesStyle.Background(t.Primary()).Foreground(t.BackgroundElement())
|
||||
noStyle = noStyle.Background(t.BackgroundElement()).Foreground(t.Primary())
|
||||
}
|
||||
|
||||
yesButton := yesStyle.Padding(0, 1).Render("Yes")
|
||||
@@ -117,20 +123,21 @@ func (q *quitDialogCmp) View() string {
|
||||
),
|
||||
)
|
||||
|
||||
return baseStyle.Padding(1, 2).
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderBackground(t.Background()).
|
||||
BorderForeground(t.TextMuted()).
|
||||
Width(lipgloss.Width(content) + 4).
|
||||
Render(content)
|
||||
return q.modal.Render(content, background)
|
||||
}
|
||||
|
||||
func (q *quitDialogCmp) BindingKeys() []key.Binding {
|
||||
return layout.KeyMapToSlice(helpKeys)
|
||||
func (q *quitDialog) Close() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewQuitCmp() QuitDialog {
|
||||
return &quitDialogCmp{
|
||||
func (q *quitDialog) IsQuitDialog() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// NewQuitDialog creates a new quit confirmation dialog
|
||||
func NewQuitDialog() QuitDialog {
|
||||
return &quitDialog{
|
||||
selectedNo: true,
|
||||
modal: modal.New(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,230 +1,112 @@
|
||||
package dialog
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"context"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/sst/opencode/internal/app"
|
||||
"github.com/sst/opencode/internal/components/modal"
|
||||
components "github.com/sst/opencode/internal/components/util"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/state"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
"github.com/sst/opencode/internal/util"
|
||||
"github.com/sst/opencode/pkg/client"
|
||||
)
|
||||
|
||||
// CloseSessionDialogMsg is sent when the session dialog is closed
|
||||
type CloseSessionDialogMsg struct {
|
||||
Session *client.SessionInfo
|
||||
}
|
||||
|
||||
// SessionDialog interface for the session switching dialog
|
||||
type SessionDialog interface {
|
||||
tea.Model
|
||||
layout.Bindings
|
||||
SetSessions(sessions []client.SessionInfo)
|
||||
SetSelectedSession(sessionID string)
|
||||
layout.Modal
|
||||
}
|
||||
|
||||
type sessionDialogCmp struct {
|
||||
sessions []client.SessionInfo
|
||||
selectedIdx int
|
||||
type sessionItem struct {
|
||||
session client.SessionInfo
|
||||
}
|
||||
|
||||
func (s sessionItem) Render(selected bool, width int) string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle().
|
||||
Width(width - 2).
|
||||
Background(t.BackgroundElement())
|
||||
|
||||
if selected {
|
||||
baseStyle = baseStyle.
|
||||
Background(t.Primary()).
|
||||
Foreground(t.BackgroundElement()).
|
||||
Bold(true)
|
||||
} else {
|
||||
baseStyle = baseStyle.
|
||||
Foreground(t.Text())
|
||||
}
|
||||
|
||||
return baseStyle.Padding(0, 1).Render(s.session.Title)
|
||||
}
|
||||
|
||||
type sessionDialog struct {
|
||||
width int
|
||||
height int
|
||||
modal *modal.Modal
|
||||
selectedSessionID string
|
||||
list components.SimpleList[sessionItem]
|
||||
}
|
||||
|
||||
type sessionKeyMap struct {
|
||||
Up key.Binding
|
||||
Down key.Binding
|
||||
Enter key.Binding
|
||||
Escape key.Binding
|
||||
J key.Binding
|
||||
K key.Binding
|
||||
}
|
||||
|
||||
var sessionKeys = sessionKeyMap{
|
||||
Up: key.NewBinding(
|
||||
key.WithKeys("up"),
|
||||
key.WithHelp("↑", "previous session"),
|
||||
),
|
||||
Down: key.NewBinding(
|
||||
key.WithKeys("down"),
|
||||
key.WithHelp("↓", "next session"),
|
||||
),
|
||||
Enter: key.NewBinding(
|
||||
key.WithKeys("enter"),
|
||||
key.WithHelp("enter", "select session"),
|
||||
),
|
||||
Escape: key.NewBinding(
|
||||
key.WithKeys("esc"),
|
||||
key.WithHelp("esc", "close"),
|
||||
),
|
||||
J: key.NewBinding(
|
||||
key.WithKeys("j"),
|
||||
key.WithHelp("j", "next session"),
|
||||
),
|
||||
K: key.NewBinding(
|
||||
key.WithKeys("k"),
|
||||
key.WithHelp("k", "previous session"),
|
||||
),
|
||||
}
|
||||
|
||||
func (s *sessionDialogCmp) Init() tea.Cmd {
|
||||
func (s *sessionDialog) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sessionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (s *sessionDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
s.width = msg.Width
|
||||
s.height = msg.Height
|
||||
s.list.SetMaxWidth(layout.Current.Container.Width - 12)
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
case key.Matches(msg, sessionKeys.Up) || key.Matches(msg, sessionKeys.K):
|
||||
if s.selectedIdx > 0 {
|
||||
s.selectedIdx--
|
||||
}
|
||||
return s, nil
|
||||
case key.Matches(msg, sessionKeys.Down) || key.Matches(msg, sessionKeys.J):
|
||||
if s.selectedIdx < len(s.sessions)-1 {
|
||||
s.selectedIdx++
|
||||
}
|
||||
return s, nil
|
||||
case key.Matches(msg, sessionKeys.Enter):
|
||||
if len(s.sessions) > 0 {
|
||||
selectedSession := s.sessions[s.selectedIdx]
|
||||
switch msg.String() {
|
||||
case "enter":
|
||||
if item, idx := s.list.GetSelectedItem(); idx >= 0 {
|
||||
selectedSession := item.session
|
||||
s.selectedSessionID = selectedSession.Id
|
||||
|
||||
return s, util.CmdHandler(CloseSessionDialogMsg{
|
||||
Session: &selectedSession,
|
||||
})
|
||||
return s, tea.Batch(
|
||||
util.CmdHandler(state.SessionSelectedMsg(&selectedSession)),
|
||||
util.CmdHandler(modal.CloseModalMsg{}),
|
||||
)
|
||||
}
|
||||
case key.Matches(msg, sessionKeys.Escape):
|
||||
return s, util.CmdHandler(CloseSessionDialogMsg{})
|
||||
}
|
||||
}
|
||||
return s, nil
|
||||
|
||||
var cmd tea.Cmd
|
||||
listModel, cmd := s.list.Update(msg)
|
||||
s.list = listModel.(components.SimpleList[sessionItem])
|
||||
return s, cmd
|
||||
}
|
||||
|
||||
func (s *sessionDialogCmp) View() string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
func (s *sessionDialog) Render(background string) string {
|
||||
return s.modal.Render(s.list.View(), background)
|
||||
}
|
||||
|
||||
if len(s.sessions) == 0 {
|
||||
return baseStyle.Padding(1, 2).
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderBackground(t.Background()).
|
||||
BorderForeground(t.TextMuted()).
|
||||
Width(40).
|
||||
Render("No sessions available")
|
||||
func (s *sessionDialog) Close() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSessionDialog creates a new session switching dialog
|
||||
func NewSessionDialog(app *app.App) SessionDialog {
|
||||
sessions, _ := app.ListSessions(context.Background())
|
||||
|
||||
var sessionItems []sessionItem
|
||||
for _, sess := range sessions {
|
||||
sessionItems = append(sessionItems, sessionItem{session: sess})
|
||||
}
|
||||
|
||||
// Calculate max width needed for session titles
|
||||
maxWidth := 40 // Minimum width
|
||||
for _, sess := range s.sessions {
|
||||
if len(sess.Title) > maxWidth-4 { // Account for padding
|
||||
maxWidth = len(sess.Title) + 4
|
||||
}
|
||||
}
|
||||
|
||||
maxWidth = max(30, min(maxWidth, s.width-15)) // Limit width to avoid overflow
|
||||
|
||||
// Limit height to avoid taking up too much screen space
|
||||
maxVisibleSessions := min(10, len(s.sessions))
|
||||
|
||||
// Build the session list
|
||||
sessionItems := make([]string, 0, maxVisibleSessions)
|
||||
startIdx := 0
|
||||
|
||||
// If we have more sessions than can be displayed, adjust the start index
|
||||
if len(s.sessions) > maxVisibleSessions {
|
||||
// Center the selected item when possible
|
||||
halfVisible := maxVisibleSessions / 2
|
||||
if s.selectedIdx >= halfVisible && s.selectedIdx < len(s.sessions)-halfVisible {
|
||||
startIdx = s.selectedIdx - halfVisible
|
||||
} else if s.selectedIdx >= len(s.sessions)-halfVisible {
|
||||
startIdx = len(s.sessions) - maxVisibleSessions
|
||||
}
|
||||
}
|
||||
|
||||
endIdx := min(startIdx+maxVisibleSessions, len(s.sessions))
|
||||
|
||||
for i := startIdx; i < endIdx; i++ {
|
||||
sess := s.sessions[i]
|
||||
itemStyle := baseStyle.Width(maxWidth)
|
||||
|
||||
if i == s.selectedIdx {
|
||||
itemStyle = itemStyle.
|
||||
Background(t.Primary()).
|
||||
Foreground(t.Background()).
|
||||
Bold(true)
|
||||
}
|
||||
|
||||
sessionItems = append(sessionItems, itemStyle.Padding(0, 1).Render(sess.Title))
|
||||
}
|
||||
|
||||
title := baseStyle.
|
||||
Foreground(t.Primary()).
|
||||
Bold(true).
|
||||
Width(maxWidth).
|
||||
Padding(0, 1).
|
||||
Render("Switch Session")
|
||||
|
||||
content := lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
title,
|
||||
baseStyle.Width(maxWidth).Render(""),
|
||||
baseStyle.Width(maxWidth).Render(lipgloss.JoinVertical(lipgloss.Left, sessionItems...)),
|
||||
baseStyle.Width(maxWidth).Render(""),
|
||||
list := components.NewSimpleList(
|
||||
sessionItems,
|
||||
10, // maxVisibleSessions
|
||||
"No sessions available",
|
||||
true, // useAlphaNumericKeys
|
||||
)
|
||||
|
||||
return baseStyle.Padding(1, 2).
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderBackground(t.Background()).
|
||||
BorderForeground(t.TextMuted()).
|
||||
Width(lipgloss.Width(content) + 4).
|
||||
Render(content)
|
||||
}
|
||||
|
||||
func (s *sessionDialogCmp) BindingKeys() []key.Binding {
|
||||
return layout.KeyMapToSlice(sessionKeys)
|
||||
}
|
||||
|
||||
func (s *sessionDialogCmp) SetSessions(sessions []client.SessionInfo) {
|
||||
s.sessions = sessions
|
||||
|
||||
// If we have a selected session ID, find its index
|
||||
if s.selectedSessionID != "" {
|
||||
for i, sess := range sessions {
|
||||
if sess.Id == s.selectedSessionID {
|
||||
s.selectedIdx = i
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default to first session if selected not found
|
||||
s.selectedIdx = 0
|
||||
}
|
||||
|
||||
func (s *sessionDialogCmp) SetSelectedSession(sessionID string) {
|
||||
s.selectedSessionID = sessionID
|
||||
|
||||
// Update the selected index if sessions are already loaded
|
||||
if len(s.sessions) > 0 {
|
||||
for i, sess := range s.sessions {
|
||||
if sess.Id == sessionID {
|
||||
s.selectedIdx = i
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewSessionDialogCmp creates a new session switching dialog
|
||||
func NewSessionDialogCmp() SessionDialog {
|
||||
return &sessionDialogCmp{
|
||||
sessions: []client.SessionInfo{},
|
||||
selectedIdx: 0,
|
||||
selectedSessionID: "",
|
||||
return &sessionDialog{
|
||||
list: list,
|
||||
modal: modal.New(modal.WithTitle("Switch Session"), modal.WithMaxWidth(80)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package dialog
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/sst/opencode/internal/components/modal"
|
||||
components "github.com/sst/opencode/internal/components/util"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
@@ -16,184 +16,113 @@ type ThemeChangedMsg struct {
|
||||
ThemeName string
|
||||
}
|
||||
|
||||
// CloseThemeDialogMsg is sent when the theme dialog is closed
|
||||
type CloseThemeDialogMsg struct{}
|
||||
|
||||
// ThemeDialog interface for the theme switching dialog
|
||||
type ThemeDialog interface {
|
||||
tea.Model
|
||||
layout.Bindings
|
||||
layout.Modal
|
||||
}
|
||||
|
||||
type themeDialogCmp struct {
|
||||
themes []string
|
||||
selectedIdx int
|
||||
width int
|
||||
height int
|
||||
currentTheme string
|
||||
type themeItem struct {
|
||||
name string
|
||||
}
|
||||
|
||||
type themeKeyMap struct {
|
||||
Up key.Binding
|
||||
Down key.Binding
|
||||
Enter key.Binding
|
||||
Escape key.Binding
|
||||
J key.Binding
|
||||
K key.Binding
|
||||
}
|
||||
func (t themeItem) Render(selected bool, width int) string {
|
||||
th := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle().
|
||||
Width(width - 2).
|
||||
Background(th.BackgroundElement())
|
||||
|
||||
var themeKeys = themeKeyMap{
|
||||
Up: key.NewBinding(
|
||||
key.WithKeys("up"),
|
||||
key.WithHelp("↑", "previous theme"),
|
||||
),
|
||||
Down: key.NewBinding(
|
||||
key.WithKeys("down"),
|
||||
key.WithHelp("↓", "next theme"),
|
||||
),
|
||||
Enter: key.NewBinding(
|
||||
key.WithKeys("enter"),
|
||||
key.WithHelp("enter", "select theme"),
|
||||
),
|
||||
Escape: key.NewBinding(
|
||||
key.WithKeys("esc"),
|
||||
key.WithHelp("esc", "close"),
|
||||
),
|
||||
J: key.NewBinding(
|
||||
key.WithKeys("j"),
|
||||
key.WithHelp("j", "next theme"),
|
||||
),
|
||||
K: key.NewBinding(
|
||||
key.WithKeys("k"),
|
||||
key.WithHelp("k", "previous theme"),
|
||||
),
|
||||
}
|
||||
|
||||
func (t *themeDialogCmp) Init() tea.Cmd {
|
||||
// Load available themes and update selectedIdx based on current theme
|
||||
t.themes = theme.AvailableThemes()
|
||||
t.currentTheme = theme.CurrentThemeName()
|
||||
|
||||
// Find the current theme in the list
|
||||
for i, name := range t.themes {
|
||||
if name == t.currentTheme {
|
||||
t.selectedIdx = i
|
||||
break
|
||||
}
|
||||
if selected {
|
||||
baseStyle = baseStyle.
|
||||
Background(th.Primary()).
|
||||
Foreground(th.BackgroundElement()).
|
||||
Bold(true)
|
||||
} else {
|
||||
baseStyle = baseStyle.
|
||||
Foreground(th.Text())
|
||||
}
|
||||
|
||||
return baseStyle.Padding(0, 1).Render(t.name)
|
||||
}
|
||||
|
||||
type themeDialog struct {
|
||||
width int
|
||||
height int
|
||||
|
||||
modal *modal.Modal
|
||||
list components.SimpleList[themeItem]
|
||||
}
|
||||
|
||||
func (t *themeDialog) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *themeDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (t *themeDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
t.width = msg.Width
|
||||
t.height = msg.Height
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
case key.Matches(msg, themeKeys.Up) || key.Matches(msg, themeKeys.K):
|
||||
if t.selectedIdx > 0 {
|
||||
t.selectedIdx--
|
||||
}
|
||||
return t, nil
|
||||
case key.Matches(msg, themeKeys.Down) || key.Matches(msg, themeKeys.J):
|
||||
if t.selectedIdx < len(t.themes)-1 {
|
||||
t.selectedIdx++
|
||||
}
|
||||
return t, nil
|
||||
case key.Matches(msg, themeKeys.Enter):
|
||||
if len(t.themes) > 0 {
|
||||
switch msg.String() {
|
||||
case "enter":
|
||||
if item, idx := t.list.GetSelectedItem(); idx >= 0 {
|
||||
previousTheme := theme.CurrentThemeName()
|
||||
selectedTheme := t.themes[t.selectedIdx]
|
||||
selectedTheme := item.name
|
||||
if previousTheme == selectedTheme {
|
||||
return t, util.CmdHandler(CloseThemeDialogMsg{})
|
||||
return t, util.CmdHandler(modal.CloseModalMsg{})
|
||||
}
|
||||
if err := theme.SetTheme(selectedTheme); err != nil {
|
||||
status.Error(err.Error())
|
||||
return t, nil
|
||||
}
|
||||
return t, util.CmdHandler(ThemeChangedMsg{
|
||||
ThemeName: selectedTheme,
|
||||
})
|
||||
return t, tea.Batch(
|
||||
util.CmdHandler(ThemeChangedMsg{ThemeName: selectedTheme}),
|
||||
util.CmdHandler(modal.CloseModalMsg{}),
|
||||
)
|
||||
}
|
||||
case key.Matches(msg, themeKeys.Escape):
|
||||
return t, util.CmdHandler(CloseThemeDialogMsg{})
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
t.width = msg.Width
|
||||
t.height = msg.Height
|
||||
}
|
||||
return t, nil
|
||||
|
||||
var cmd tea.Cmd
|
||||
listModel, cmd := t.list.Update(msg)
|
||||
t.list = listModel.(components.SimpleList[themeItem])
|
||||
return t, cmd
|
||||
}
|
||||
|
||||
func (t *themeDialogCmp) View() string {
|
||||
currentTheme := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
func (t *themeDialog) Render(background string) string {
|
||||
return t.modal.Render(t.list.View(), background)
|
||||
}
|
||||
|
||||
if len(t.themes) == 0 {
|
||||
return baseStyle.Padding(1, 2).
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderBackground(currentTheme.Background()).
|
||||
BorderForeground(currentTheme.TextMuted()).
|
||||
Width(40).
|
||||
Render("No themes available")
|
||||
}
|
||||
func (t *themeDialog) Close() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Calculate max width needed for theme names
|
||||
maxWidth := 40 // Minimum width
|
||||
for _, themeName := range t.themes {
|
||||
if len(themeName) > maxWidth-4 { // Account for padding
|
||||
maxWidth = len(themeName) + 4
|
||||
// NewThemeDialog creates a new theme switching dialog
|
||||
func NewThemeDialog() ThemeDialog {
|
||||
themes := theme.AvailableThemes()
|
||||
currentTheme := theme.CurrentThemeName()
|
||||
|
||||
var themeItems []themeItem
|
||||
var selectedIdx int
|
||||
for i, name := range themes {
|
||||
themeItems = append(themeItems, themeItem{name: name})
|
||||
if name == currentTheme {
|
||||
selectedIdx = i
|
||||
}
|
||||
}
|
||||
|
||||
maxWidth = max(30, min(maxWidth, t.width-15)) // Limit width to avoid overflow
|
||||
|
||||
// Build the theme list
|
||||
themeItems := make([]string, 0, len(t.themes))
|
||||
for i, themeName := range t.themes {
|
||||
itemStyle := baseStyle.Width(maxWidth)
|
||||
|
||||
if i == t.selectedIdx {
|
||||
itemStyle = itemStyle.
|
||||
Background(currentTheme.Primary()).
|
||||
Foreground(currentTheme.Background()).
|
||||
Bold(true)
|
||||
}
|
||||
|
||||
themeItems = append(themeItems, itemStyle.Padding(0, 1).Render(themeName))
|
||||
}
|
||||
|
||||
title := baseStyle.
|
||||
Foreground(currentTheme.Primary()).
|
||||
Bold(true).
|
||||
Width(maxWidth).
|
||||
Padding(0, 1).
|
||||
Render("Select Theme")
|
||||
|
||||
content := lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
title,
|
||||
baseStyle.Width(maxWidth).Render(""),
|
||||
baseStyle.Width(maxWidth).Render(lipgloss.JoinVertical(lipgloss.Left, themeItems...)),
|
||||
baseStyle.Width(maxWidth).Render(""),
|
||||
list := components.NewSimpleList(
|
||||
themeItems,
|
||||
10, // maxVisibleThemes
|
||||
"No themes available",
|
||||
true,
|
||||
)
|
||||
|
||||
return baseStyle.Padding(1, 2).
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderBackground(currentTheme.Background()).
|
||||
BorderForeground(currentTheme.TextMuted()).
|
||||
Width(lipgloss.Width(content) + 4).
|
||||
Render(content)
|
||||
}
|
||||
// Set the initial selection to the current theme
|
||||
list.SetSelectedIndex(selectedIdx)
|
||||
|
||||
func (t *themeDialogCmp) BindingKeys() []key.Binding {
|
||||
return layout.KeyMapToSlice(themeKeys)
|
||||
}
|
||||
|
||||
// NewThemeDialogCmp creates a new theme switching dialog
|
||||
func NewThemeDialogCmp() ThemeDialog {
|
||||
return &themeDialogCmp{
|
||||
themes: []string{},
|
||||
selectedIdx: 0,
|
||||
currentTheme: "",
|
||||
return &themeDialog{
|
||||
list: list,
|
||||
modal: modal.New(modal.WithTitle("Select Theme"), modal.WithMaxWidth(40)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package dialog
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
utilComponents "github.com/sst/opencode/internal/components/util"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
@@ -17,7 +17,7 @@ const (
|
||||
|
||||
// ToolsDialog interface for the tools list dialog
|
||||
type ToolsDialog interface {
|
||||
tea.Model
|
||||
layout.ModelWithView
|
||||
layout.Bindings
|
||||
SetTools(tools []string)
|
||||
}
|
||||
@@ -39,7 +39,7 @@ func (t toolItem) Render(selected bool, width int) string {
|
||||
baseStyle := styles.BaseStyle().
|
||||
Width(width).
|
||||
Background(th.Background())
|
||||
|
||||
|
||||
if selected {
|
||||
baseStyle = baseStyle.
|
||||
Background(th.Primary()).
|
||||
@@ -49,15 +49,15 @@ func (t toolItem) Render(selected bool, width int) string {
|
||||
baseStyle = baseStyle.
|
||||
Foreground(th.Text())
|
||||
}
|
||||
|
||||
|
||||
return baseStyle.Render(t.name)
|
||||
}
|
||||
|
||||
type toolsDialogCmp struct {
|
||||
tools []toolItem
|
||||
width int
|
||||
height int
|
||||
list utilComponents.SimpleList[toolItem]
|
||||
type toolsDialogComponent struct {
|
||||
tools []toolItem
|
||||
width int
|
||||
height int
|
||||
list utilComponents.SimpleList[toolItem]
|
||||
}
|
||||
|
||||
type toolsKeyMap struct {
|
||||
@@ -91,21 +91,21 @@ var toolsKeys = toolsKeyMap{
|
||||
),
|
||||
}
|
||||
|
||||
func (m *toolsDialogCmp) Init() tea.Cmd {
|
||||
func (m *toolsDialogComponent) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *toolsDialogCmp) SetTools(tools []string) {
|
||||
func (m *toolsDialogComponent) SetTools(tools []string) {
|
||||
var toolItems []toolItem
|
||||
for _, name := range tools {
|
||||
toolItems = append(toolItems, toolItem{name: name})
|
||||
}
|
||||
|
||||
|
||||
m.tools = toolItems
|
||||
m.list.SetItems(toolItems)
|
||||
}
|
||||
|
||||
func (m *toolsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (m *toolsDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
@@ -130,7 +130,7 @@ func (m *toolsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m *toolsDialogCmp) View() string {
|
||||
func (m *toolsDialogComponent) View() string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle().Background(t.Background())
|
||||
|
||||
@@ -144,7 +144,7 @@ func (m *toolsDialogCmp) View() string {
|
||||
// Calculate dialog width based on content
|
||||
dialogWidth := min(maxToolsDialogWidth, m.width/2)
|
||||
m.list.SetMaxWidth(dialogWidth)
|
||||
|
||||
|
||||
content := lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
title,
|
||||
@@ -160,7 +160,7 @@ func (m *toolsDialogCmp) View() string {
|
||||
Render(content)
|
||||
}
|
||||
|
||||
func (m *toolsDialogCmp) BindingKeys() []key.Binding {
|
||||
func (m *toolsDialogComponent) BindingKeys() []key.Binding {
|
||||
return layout.KeyMapToSlice(toolsKeys)
|
||||
}
|
||||
|
||||
@@ -171,8 +171,8 @@ func NewToolsDialogCmp() ToolsDialog {
|
||||
"No tools available",
|
||||
true,
|
||||
)
|
||||
|
||||
return &toolsDialogCmp{
|
||||
|
||||
return &toolsDialogComponent{
|
||||
list: list,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package diff
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image/color"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -12,9 +13,11 @@ import (
|
||||
"github.com/alecthomas/chroma/v2/formatters"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2/compat"
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
stylesi "github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
)
|
||||
|
||||
@@ -300,7 +303,7 @@ func pairLines(lines []DiffLine) []linePair {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// SyntaxHighlight applies syntax highlighting to text based on file extension
|
||||
func SyntaxHighlight(w io.Writer, source, fileName, formatter string, bg lipgloss.TerminalColor) error {
|
||||
func SyntaxHighlight(w io.Writer, source, fileName, formatter string, bg color.Color) error {
|
||||
t := theme.CurrentTheme()
|
||||
|
||||
// Determine the language lexer to use
|
||||
@@ -509,15 +512,12 @@ func SyntaxHighlight(w io.Writer, source, fileName, formatter string, bg lipglos
|
||||
}
|
||||
|
||||
// getColor returns the appropriate hex color string based on terminal background
|
||||
func getColor(adaptiveColor lipgloss.AdaptiveColor) string {
|
||||
if lipgloss.HasDarkBackground() {
|
||||
return adaptiveColor.Dark
|
||||
}
|
||||
return adaptiveColor.Light
|
||||
func getColor(adaptiveColor compat.AdaptiveColor) string {
|
||||
return stylesi.AdaptiveColorToString(adaptiveColor)
|
||||
}
|
||||
|
||||
// highlightLine applies syntax highlighting to a single line
|
||||
func highlightLine(fileName string, line string, bg lipgloss.TerminalColor) string {
|
||||
func highlightLine(fileName string, line string, bg color.Color) string {
|
||||
var buf bytes.Buffer
|
||||
err := SyntaxHighlight(&buf, line, fileName, "terminal16m", bg)
|
||||
if err != nil {
|
||||
@@ -540,7 +540,7 @@ func createStyles(t theme.Theme) (removedLineStyle, addedLineStyle, contextLineS
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// applyHighlighting applies intra-line highlighting to a piece of text
|
||||
func applyHighlighting(content string, segments []Segment, segmentType LineType, highlightBg lipgloss.AdaptiveColor) string {
|
||||
func applyHighlighting(content string, segments []Segment, segmentType LineType, highlightBg compat.AdaptiveColor) string {
|
||||
// Find all ANSI sequences in the content
|
||||
ansiRegex := regexp.MustCompile(`\x1b(?:[@-Z\\-_]|\[[0-9?]*(?:;[0-9?]*)*[@-~])`)
|
||||
ansiMatches := ansiRegex.FindAllStringIndex(content, -1)
|
||||
@@ -662,7 +662,7 @@ func renderDiffColumnLine(
|
||||
var bgStyle lipgloss.Style
|
||||
var lineNum string
|
||||
var highlightType LineType
|
||||
var highlightColor lipgloss.AdaptiveColor
|
||||
var highlightColor compat.AdaptiveColor
|
||||
|
||||
if isLeftColumn {
|
||||
// Left column logic
|
||||
|
||||
143
packages/tui/internal/components/modal/modal.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package modal
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
)
|
||||
|
||||
// CloseModalMsg is a message to signal that the active modal should be closed.
|
||||
type CloseModalMsg struct{}
|
||||
|
||||
// Modal is a reusable modal component that handles frame rendering and overlay placement
|
||||
type Modal struct {
|
||||
width int
|
||||
height int
|
||||
title string
|
||||
maxWidth int
|
||||
maxHeight int
|
||||
fitContent bool
|
||||
}
|
||||
|
||||
// ModalOption is a function that configures a Modal
|
||||
type ModalOption func(*Modal)
|
||||
|
||||
// WithTitle sets the modal title
|
||||
func WithTitle(title string) ModalOption {
|
||||
return func(m *Modal) {
|
||||
m.title = title
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxWidth sets the maximum width
|
||||
func WithMaxWidth(width int) ModalOption {
|
||||
return func(m *Modal) {
|
||||
m.maxWidth = width
|
||||
m.fitContent = false
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxHeight sets the maximum height
|
||||
func WithMaxHeight(height int) ModalOption {
|
||||
return func(m *Modal) {
|
||||
m.maxHeight = height
|
||||
}
|
||||
}
|
||||
|
||||
func WithFitContent(fit bool) ModalOption {
|
||||
return func(m *Modal) {
|
||||
m.fitContent = fit
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Modal with the given options
|
||||
func New(opts ...ModalOption) *Modal {
|
||||
m := &Modal{
|
||||
maxWidth: 0,
|
||||
maxHeight: 0,
|
||||
fitContent: true,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(m)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Render renders the modal centered on the screen
|
||||
func (m *Modal) Render(contentView string, background string) string {
|
||||
t := theme.CurrentTheme()
|
||||
|
||||
outerWidth := layout.Current.Container.Width - 8
|
||||
if m.maxWidth > 0 && outerWidth > m.maxWidth {
|
||||
outerWidth = m.maxWidth
|
||||
}
|
||||
|
||||
if m.fitContent {
|
||||
titleWidth := lipgloss.Width(m.title)
|
||||
contentWidth := lipgloss.Width(contentView)
|
||||
largestWidth := max(titleWidth+2, contentWidth)
|
||||
outerWidth = largestWidth + 6
|
||||
}
|
||||
|
||||
innerWidth := outerWidth - 4
|
||||
|
||||
// Base style for the modal
|
||||
baseStyle := styles.BaseStyle().
|
||||
Background(t.BackgroundElement()).
|
||||
Foreground(t.TextMuted())
|
||||
|
||||
// Add title if provided
|
||||
var finalContent string
|
||||
if m.title != "" {
|
||||
titleStyle := baseStyle.
|
||||
Foreground(t.Primary()).
|
||||
Bold(true).
|
||||
Width(innerWidth).
|
||||
Padding(0, 1)
|
||||
|
||||
titleView := titleStyle.Render(m.title)
|
||||
finalContent = lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
titleView,
|
||||
contentView,
|
||||
)
|
||||
} else {
|
||||
finalContent = contentView
|
||||
}
|
||||
|
||||
modalStyle := baseStyle.
|
||||
PaddingTop(1).
|
||||
PaddingBottom(1).
|
||||
PaddingLeft(2).
|
||||
PaddingRight(2).
|
||||
BorderStyle(lipgloss.ThickBorder()).
|
||||
BorderLeft(true).
|
||||
BorderRight(true).
|
||||
BorderLeftForeground(t.BackgroundSubtle()).
|
||||
BorderLeftBackground(t.Background()).
|
||||
BorderRightForeground(t.BackgroundSubtle()).
|
||||
BorderRightBackground(t.Background())
|
||||
|
||||
modalView := modalStyle.
|
||||
Width(outerWidth).
|
||||
Render(finalContent)
|
||||
|
||||
// Calculate position for centering
|
||||
bgHeight := lipgloss.Height(background)
|
||||
bgWidth := lipgloss.Width(background)
|
||||
modalHeight := lipgloss.Height(modalView)
|
||||
modalWidth := lipgloss.Width(modalView)
|
||||
|
||||
row := (bgHeight - modalHeight) / 2
|
||||
col := (bgWidth - modalWidth) / 2
|
||||
|
||||
return layout.PlaceOverlay(
|
||||
col,
|
||||
row,
|
||||
modalView,
|
||||
background,
|
||||
)
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package qr
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
"rsc.io/qr"
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package utilComponents
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
@@ -14,15 +14,16 @@ type SimpleListItem interface {
|
||||
}
|
||||
|
||||
type SimpleList[T SimpleListItem] interface {
|
||||
tea.Model
|
||||
layout.ModelWithView
|
||||
layout.Bindings
|
||||
SetMaxWidth(maxWidth int)
|
||||
GetSelectedItem() (item T, idx int)
|
||||
SetItems(items []T)
|
||||
GetItems() []T
|
||||
SetSelectedIndex(idx int)
|
||||
}
|
||||
|
||||
type simpleListCmp[T SimpleListItem] struct {
|
||||
type simpleListComponent[T SimpleListItem] struct {
|
||||
fallbackMsg string
|
||||
items []T
|
||||
selectedIdx int
|
||||
@@ -59,11 +60,11 @@ var simpleListKeys = simpleListKeyMap{
|
||||
),
|
||||
}
|
||||
|
||||
func (c *simpleListCmp[T]) Init() tea.Cmd {
|
||||
func (c *simpleListComponent[T]) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *simpleListCmp[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (c *simpleListComponent[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
@@ -83,11 +84,11 @@ func (c *simpleListCmp[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *simpleListCmp[T]) BindingKeys() []key.Binding {
|
||||
func (c *simpleListComponent[T]) BindingKeys() []key.Binding {
|
||||
return layout.KeyMapToSlice(simpleListKeys)
|
||||
}
|
||||
|
||||
func (c *simpleListCmp[T]) GetSelectedItem() (T, int) {
|
||||
func (c *simpleListComponent[T]) GetSelectedItem() (T, int) {
|
||||
if len(c.items) > 0 {
|
||||
return c.items[c.selectedIdx], c.selectedIdx
|
||||
}
|
||||
@@ -96,20 +97,26 @@ func (c *simpleListCmp[T]) GetSelectedItem() (T, int) {
|
||||
return zero, -1
|
||||
}
|
||||
|
||||
func (c *simpleListCmp[T]) SetItems(items []T) {
|
||||
func (c *simpleListComponent[T]) SetItems(items []T) {
|
||||
c.selectedIdx = 0
|
||||
c.items = items
|
||||
}
|
||||
|
||||
func (c *simpleListCmp[T]) GetItems() []T {
|
||||
func (c *simpleListComponent[T]) GetItems() []T {
|
||||
return c.items
|
||||
}
|
||||
|
||||
func (c *simpleListCmp[T]) SetMaxWidth(width int) {
|
||||
func (c *simpleListComponent[T]) SetMaxWidth(width int) {
|
||||
c.maxWidth = width
|
||||
}
|
||||
|
||||
func (c *simpleListCmp[T]) View() string {
|
||||
func (c *simpleListComponent[T]) SetSelectedIndex(idx int) {
|
||||
if idx >= 0 && idx < len(c.items) {
|
||||
c.selectedIdx = idx
|
||||
}
|
||||
}
|
||||
|
||||
func (c *simpleListComponent[T]) View() string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
@@ -149,7 +156,7 @@ func (c *simpleListCmp[T]) View() string {
|
||||
}
|
||||
|
||||
func NewSimpleList[T SimpleListItem](items []T, maxVisibleItems int, fallbackMsg string, useAlphaNumericKeys bool) SimpleList[T] {
|
||||
return &simpleListCmp[T]{
|
||||
return &simpleListComponent[T]{
|
||||
fallbackMsg: fallbackMsg,
|
||||
items: items,
|
||||
maxVisibleItems: maxVisibleItems,
|
||||
|
||||
@@ -4,11 +4,12 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/lucasb-eyer/go-colorful"
|
||||
_ "golang.org/x/image/webp"
|
||||
@@ -39,7 +40,7 @@ func ToString(width int, img image.Image) string {
|
||||
c1, _ := colorful.MakeColor(img.At(x, heightCounter))
|
||||
color1 := lipgloss.Color(c1.Hex())
|
||||
|
||||
var color2 lipgloss.Color
|
||||
var color2 color.Color
|
||||
if heightCounter+1 < h {
|
||||
c2, _ := colorful.MakeColor(img.At(x, heightCounter+1))
|
||||
color2 = lipgloss.Color(c2.Hex())
|
||||
@@ -76,10 +77,10 @@ func ImagePreview(width int, filename string) (string, error) {
|
||||
|
||||
func ImageToBytes(image image.Image) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
err := png.Encode(buf, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
err := png.Encode(buf, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
package layout
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
)
|
||||
|
||||
type Container interface {
|
||||
type ModelWithView interface {
|
||||
tea.Model
|
||||
tea.ViewModel
|
||||
}
|
||||
|
||||
type Container interface {
|
||||
ModelWithView
|
||||
Sizeable
|
||||
Bindings
|
||||
Focus()
|
||||
@@ -21,7 +26,7 @@ type container struct {
|
||||
width int
|
||||
height int
|
||||
|
||||
content tea.Model
|
||||
content ModelWithView
|
||||
|
||||
paddingTop int
|
||||
paddingRight int
|
||||
@@ -46,7 +51,7 @@ func (c *container) Init() tea.Cmd {
|
||||
|
||||
func (c *container) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
u, cmd := c.content.Update(msg)
|
||||
c.content = u
|
||||
c.content = u.(ModelWithView)
|
||||
return c, cmd
|
||||
}
|
||||
|
||||
@@ -175,7 +180,7 @@ func (c *container) Blur() {
|
||||
|
||||
type ContainerOption func(*container)
|
||||
|
||||
func NewContainer(content tea.Model, options ...ContainerOption) Container {
|
||||
func NewContainer(content ModelWithView, options ...ContainerOption) Container {
|
||||
c := &container{
|
||||
content: content,
|
||||
borderStyle: lipgloss.NormalBorder(),
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package layout
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
)
|
||||
|
||||
type FlexDirection int
|
||||
@@ -26,7 +25,7 @@ func FlexPaneSizeFixed(size int) FlexPaneSize {
|
||||
}
|
||||
|
||||
type FlexLayout interface {
|
||||
tea.Model
|
||||
ModelWithView
|
||||
Sizeable
|
||||
Bindings
|
||||
SetPanes(panes []Container) tea.Cmd
|
||||
@@ -75,8 +74,6 @@ func (f *flexLayout) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
func (f *flexLayout) View() string {
|
||||
t := theme.CurrentTheme()
|
||||
|
||||
if len(f.panes) == 0 {
|
||||
return ""
|
||||
}
|
||||
@@ -94,7 +91,6 @@ func (f *flexLayout) View() string {
|
||||
paneWidth,
|
||||
pane.Alignment(),
|
||||
pane.View(),
|
||||
lipgloss.WithWhitespaceBackground(t.Background()),
|
||||
)
|
||||
views = append(views, view)
|
||||
} else {
|
||||
@@ -105,7 +101,6 @@ func (f *flexLayout) View() string {
|
||||
lipgloss.Center,
|
||||
pane.Alignment(),
|
||||
pane.View(),
|
||||
lipgloss.WithWhitespaceBackground(t.Background()),
|
||||
)
|
||||
views = append(views, view)
|
||||
}
|
||||
@@ -245,4 +240,3 @@ func WithPaneSizes(sizes ...FlexPaneSize) FlexLayoutOption {
|
||||
f.sizes = sizes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ package layout
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
)
|
||||
|
||||
var Current *LayoutInfo
|
||||
@@ -36,6 +36,12 @@ type LayoutInfo struct {
|
||||
Container Dimensions
|
||||
}
|
||||
|
||||
type Modal interface {
|
||||
tea.Model
|
||||
Render(background string) string
|
||||
Close() tea.Cmd
|
||||
}
|
||||
|
||||
type Focusable interface {
|
||||
Focus() tea.Cmd
|
||||
Blur() tea.Cmd
|
||||
|
||||
@@ -3,20 +3,14 @@ package layout
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
chAnsi "github.com/charmbracelet/x/ansi"
|
||||
"github.com/muesli/ansi"
|
||||
"github.com/muesli/reflow/truncate"
|
||||
"github.com/muesli/termenv"
|
||||
"github.com/sst/opencode/internal/styles"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
"github.com/sst/opencode/internal/util"
|
||||
)
|
||||
|
||||
// Most of this code is borrowed from
|
||||
// https://github.com/charmbracelet/lipgloss/pull/102
|
||||
// as well as the lipgloss library, with some modification for what I needed.
|
||||
|
||||
// Split a string into lines, additionally returning the size of the widest line.
|
||||
func getLines(s string) (lines []string, widest int) {
|
||||
lines = strings.Split(s, "\n")
|
||||
@@ -33,40 +27,18 @@ func getLines(s string) (lines []string, widest int) {
|
||||
func PlaceOverlay(
|
||||
x, y int,
|
||||
fg, bg string,
|
||||
shadow bool, opts ...WhitespaceOption,
|
||||
opts ...WhitespaceOption,
|
||||
) string {
|
||||
fgLines, fgWidth := getLines(fg)
|
||||
bgLines, bgWidth := getLines(bg)
|
||||
bgHeight := len(bgLines)
|
||||
fgHeight := len(fgLines)
|
||||
|
||||
if shadow {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
var shadowbg string = ""
|
||||
shadowchar := lipgloss.NewStyle().
|
||||
Background(t.BackgroundElement()).
|
||||
Foreground(t.Background()).
|
||||
Render("░")
|
||||
bgchar := baseStyle.Render(" ")
|
||||
for i := 0; i <= fgHeight; i++ {
|
||||
if i == 0 {
|
||||
shadowbg += bgchar + strings.Repeat(bgchar, fgWidth) + "\n"
|
||||
} else {
|
||||
shadowbg += bgchar + strings.Repeat(shadowchar, fgWidth) + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
fg = PlaceOverlay(0, 0, fg, shadowbg, false, opts...)
|
||||
fgLines, fgWidth = getLines(fg)
|
||||
fgHeight = len(fgLines)
|
||||
}
|
||||
|
||||
if fgWidth >= bgWidth && fgHeight >= bgHeight {
|
||||
// FIXME: return fg or bg?
|
||||
return fg
|
||||
}
|
||||
|
||||
// TODO: allow placement outside of the bg box?
|
||||
x = util.Clamp(x, 0, bgWidth-fgWidth)
|
||||
y = util.Clamp(y, 0, bgHeight-fgHeight)
|
||||
@@ -120,13 +92,6 @@ func cutLeft(s string, cutWidth int) string {
|
||||
return chAnsi.Cut(s, cutWidth, lipgloss.Width(s))
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
type whitespace struct {
|
||||
style termenv.Style
|
||||
chars string
|
||||
|
||||
@@ -4,18 +4,16 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sst/opencode/internal/app"
|
||||
"github.com/sst/opencode/internal/completions"
|
||||
"github.com/sst/opencode/internal/components/chat"
|
||||
"github.com/sst/opencode/internal/components/dialog"
|
||||
"github.com/sst/opencode/internal/layout"
|
||||
"github.com/sst/opencode/internal/state"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
"github.com/sst/opencode/internal/util"
|
||||
"github.com/sst/opencode/pkg/client"
|
||||
)
|
||||
|
||||
var ChatPage PageID = "chat"
|
||||
@@ -30,17 +28,12 @@ type chatPage struct {
|
||||
}
|
||||
|
||||
type ChatKeyMap struct {
|
||||
NewSession key.Binding
|
||||
Cancel key.Binding
|
||||
ToggleTools key.Binding
|
||||
ShowCompletionDialog key.Binding
|
||||
}
|
||||
|
||||
var keyMap = ChatKeyMap{
|
||||
NewSession: key.NewBinding(
|
||||
key.WithKeys("ctrl+n"),
|
||||
key.WithHelp("ctrl+n", "new session"),
|
||||
),
|
||||
Cancel: key.NewBinding(
|
||||
key.WithKeys("esc"),
|
||||
key.WithHelp("esc", "cancel"),
|
||||
@@ -101,17 +94,19 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
p.showCompletionDialog = false
|
||||
p.app.SetCompletionDialogOpen(false)
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
_, cmd := p.editor.Update(msg)
|
||||
if cmd != nil {
|
||||
return p, cmd
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case key.Matches(msg, keyMap.ShowCompletionDialog):
|
||||
p.showCompletionDialog = true
|
||||
p.app.SetCompletionDialogOpen(true)
|
||||
// Continue sending keys to layout->chat
|
||||
case key.Matches(msg, keyMap.NewSession):
|
||||
p.app.Session = &client.SessionInfo{}
|
||||
p.app.Messages = []client.MessageInfo{}
|
||||
return p, tea.Batch(
|
||||
util.CmdHandler(state.SessionClearedMsg{}),
|
||||
)
|
||||
case key.Matches(msg, keyMap.Cancel):
|
||||
if p.app.Session.Id != "" {
|
||||
// Cancel the current session's generation process
|
||||
@@ -173,7 +168,6 @@ func (p *chatPage) View() string {
|
||||
layoutHeight-editorHeight-lipgloss.Height(overlay),
|
||||
overlay,
|
||||
layoutView,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -187,9 +181,9 @@ func (p *chatPage) BindingKeys() []key.Binding {
|
||||
return bindings
|
||||
}
|
||||
|
||||
func NewChatPage(app *app.App) tea.Model {
|
||||
func NewChatPage(app *app.App) layout.ModelWithView {
|
||||
cg := completions.NewFileAndFolderContextGroup()
|
||||
completionDialog := dialog.NewCompletionDialogCmp(cg)
|
||||
completionDialog := dialog.NewCompletionDialogComponent(cg)
|
||||
messagesContainer := layout.NewContainer(
|
||||
chat.NewMessagesComponent(app),
|
||||
)
|
||||
@@ -208,7 +202,7 @@ func NewChatPage(app *app.App) tea.Model {
|
||||
layout.WithDirection(layout.FlexDirectionVertical),
|
||||
layout.WithPaneSizes(
|
||||
layout.FlexPaneSizeGrow,
|
||||
layout.FlexPaneSizeFixed(6),
|
||||
layout.FlexPaneSizeFixed(5),
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -1,123 +1,13 @@
|
||||
package styles
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
type TerminalInfo struct {
|
||||
BackgroundIsDark bool
|
||||
}
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
var Terminal *TerminalInfo
|
||||
|
||||
var ansiEscape = regexp.MustCompile("\x1b\\[[0-9;]*m")
|
||||
|
||||
func getColorRGB(c lipgloss.TerminalColor) (uint8, uint8, uint8) {
|
||||
r, g, b, a := c.RGBA()
|
||||
|
||||
// Un-premultiply alpha if needed
|
||||
if a > 0 && a < 0xffff {
|
||||
r = (r * 0xffff) / a
|
||||
g = (g * 0xffff) / a
|
||||
b = (b * 0xffff) / a
|
||||
func init() {
|
||||
Terminal = &TerminalInfo{
|
||||
BackgroundIsDark: false,
|
||||
}
|
||||
|
||||
// Convert from 16-bit to 8-bit color
|
||||
return uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)
|
||||
}
|
||||
|
||||
// ForceReplaceBackgroundWithLipgloss replaces any ANSI background color codes
|
||||
// in `input` with a single 24‑bit background (48;2;R;G;B).
|
||||
func ForceReplaceBackgroundWithLipgloss(input string, newBgColor lipgloss.TerminalColor) string {
|
||||
// Precompute our new-bg sequence once
|
||||
r, g, b := getColorRGB(newBgColor)
|
||||
newBg := fmt.Sprintf("48;2;%d;%d;%d", r, g, b)
|
||||
|
||||
return ansiEscape.ReplaceAllStringFunc(input, func(seq string) string {
|
||||
const (
|
||||
escPrefixLen = 2 // "\x1b["
|
||||
escSuffixLen = 1 // "m"
|
||||
)
|
||||
|
||||
raw := seq
|
||||
start := escPrefixLen
|
||||
end := len(raw) - escSuffixLen
|
||||
|
||||
var sb strings.Builder
|
||||
// reserve enough space: original content minus bg codes + our newBg
|
||||
sb.Grow((end - start) + len(newBg) + 2)
|
||||
|
||||
// scan from start..end, token by token
|
||||
for i := start; i < end; {
|
||||
// find the next ';' or end
|
||||
j := i
|
||||
for j < end && raw[j] != ';' {
|
||||
j++
|
||||
}
|
||||
token := raw[i:j]
|
||||
|
||||
// fast‑path: skip "48;5;N" or "48;2;R;G;B"
|
||||
if len(token) == 2 && token[0] == '4' && token[1] == '8' {
|
||||
k := j + 1
|
||||
if k < end {
|
||||
// find next token
|
||||
l := k
|
||||
for l < end && raw[l] != ';' {
|
||||
l++
|
||||
}
|
||||
next := raw[k:l]
|
||||
if next == "5" {
|
||||
// skip "48;5;N"
|
||||
m := l + 1
|
||||
for m < end && raw[m] != ';' {
|
||||
m++
|
||||
}
|
||||
i = m + 1
|
||||
continue
|
||||
} else if next == "2" {
|
||||
// skip "48;2;R;G;B"
|
||||
m := l + 1
|
||||
for count := 0; count < 3 && m < end; count++ {
|
||||
for m < end && raw[m] != ';' {
|
||||
m++
|
||||
}
|
||||
m++
|
||||
}
|
||||
i = m
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decide whether to keep this token
|
||||
// manually parse ASCII digits to int
|
||||
isNum := true
|
||||
val := 0
|
||||
for p := i; p < j; p++ {
|
||||
c := raw[p]
|
||||
if c < '0' || c > '9' {
|
||||
isNum = false
|
||||
break
|
||||
}
|
||||
val = val*10 + int(c-'0')
|
||||
}
|
||||
keep := !isNum ||
|
||||
((val < 40 || val > 47) && (val < 100 || val > 107) && val != 49)
|
||||
|
||||
if keep {
|
||||
if sb.Len() > 0 {
|
||||
sb.WriteByte(';')
|
||||
}
|
||||
sb.WriteString(token)
|
||||
}
|
||||
// advance past this token (and the semicolon)
|
||||
i = j + 1
|
||||
}
|
||||
|
||||
// append our new background
|
||||
if sb.Len() > 0 {
|
||||
sb.WriteByte(';')
|
||||
}
|
||||
sb.WriteString(newBg)
|
||||
|
||||
return "\x1b[" + sb.String() + "m"
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ package styles
|
||||
import (
|
||||
"github.com/charmbracelet/glamour"
|
||||
"github.com/charmbracelet/glamour/ansi"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/lipgloss/v2/compat"
|
||||
"github.com/lucasb-eyer/go-colorful"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
)
|
||||
|
||||
@@ -15,234 +16,267 @@ func stringPtr(s string) *string { return &s }
|
||||
func uintPtr(u uint) *uint { return &u }
|
||||
|
||||
// returns a glamour TermRenderer configured with the current theme
|
||||
func GetMarkdownRenderer(width int) *glamour.TermRenderer {
|
||||
func GetMarkdownRenderer(width int, backgroundColor compat.AdaptiveColor) *glamour.TermRenderer {
|
||||
r, _ := glamour.NewTermRenderer(
|
||||
glamour.WithStyles(generateMarkdownStyleConfig()),
|
||||
glamour.WithStyles(generateMarkdownStyleConfig(backgroundColor)),
|
||||
glamour.WithWordWrap(width),
|
||||
glamour.WithChromaFormatter("terminal16m"),
|
||||
)
|
||||
return r
|
||||
}
|
||||
|
||||
// creates an ansi.StyleConfig for markdown rendering
|
||||
// using adaptive colors from the provided theme.
|
||||
func generateMarkdownStyleConfig() ansi.StyleConfig {
|
||||
func generateMarkdownStyleConfig(backgroundColor compat.AdaptiveColor) ansi.StyleConfig {
|
||||
t := theme.CurrentTheme()
|
||||
background := stringPtr(AdaptiveColorToString(backgroundColor))
|
||||
|
||||
return ansi.StyleConfig{
|
||||
Document: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
BlockPrefix: "",
|
||||
BlockSuffix: "",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownText())),
|
||||
BlockPrefix: "",
|
||||
BlockSuffix: "",
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownText())),
|
||||
},
|
||||
},
|
||||
BlockQuote: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownBlockQuote())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownBlockQuote())),
|
||||
Italic: boolPtr(true),
|
||||
Prefix: "┃ ",
|
||||
},
|
||||
Indent: uintPtr(1),
|
||||
IndentToken: stringPtr(BaseStyle().Render(" ")),
|
||||
IndentToken: stringPtr(" "),
|
||||
},
|
||||
List: ansi.StyleList{
|
||||
LevelIndent: defaultMargin,
|
||||
StyleBlock: ansi.StyleBlock{
|
||||
IndentToken: stringPtr(BaseStyle().Render(" ")),
|
||||
IndentToken: stringPtr(" "),
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownText())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownText())),
|
||||
},
|
||||
},
|
||||
},
|
||||
Heading: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
BlockSuffix: "\n",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
},
|
||||
H1: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: "# ",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
},
|
||||
H2: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: "## ",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
},
|
||||
H3: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: "### ",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
},
|
||||
H4: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: "#### ",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
},
|
||||
H5: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: "##### ",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
},
|
||||
H6: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: "###### ",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
},
|
||||
Strikethrough: ansi.StylePrimitive{
|
||||
CrossedOut: boolPtr(true),
|
||||
Color: stringPtr(adaptiveColorToString(t.TextMuted())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.TextMuted())),
|
||||
},
|
||||
Emph: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownEmph())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownEmph())),
|
||||
Italic: boolPtr(true),
|
||||
},
|
||||
Strong: ansi.StylePrimitive{
|
||||
Bold: boolPtr(true),
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownStrong())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownStrong())),
|
||||
},
|
||||
HorizontalRule: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownHorizontalRule())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownHorizontalRule())),
|
||||
Format: "\n─────────────────────────────────────────\n",
|
||||
},
|
||||
Item: ansi.StylePrimitive{
|
||||
BlockPrefix: "• ",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownListItem())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownListItem())),
|
||||
},
|
||||
Enumeration: ansi.StylePrimitive{
|
||||
BlockPrefix: ". ",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownListEnumeration())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownListEnumeration())),
|
||||
},
|
||||
Task: ansi.StyleTask{
|
||||
StylePrimitive: ansi.StylePrimitive{},
|
||||
Ticked: "[✓] ",
|
||||
Unticked: "[ ] ",
|
||||
Ticked: "[✓] ",
|
||||
Unticked: "[ ] ",
|
||||
},
|
||||
Link: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownLink())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownLink())),
|
||||
Underline: boolPtr(true),
|
||||
},
|
||||
LinkText: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownLinkText())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownLinkText())),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
Image: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownImage())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownImage())),
|
||||
Underline: boolPtr(true),
|
||||
Format: "🖼 {{.text}}",
|
||||
},
|
||||
ImageText: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownImageText())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownImageText())),
|
||||
Format: "{{.text}}",
|
||||
},
|
||||
Code: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownCode())),
|
||||
Prefix: "",
|
||||
Suffix: "",
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownCode())),
|
||||
Prefix: "",
|
||||
Suffix: "",
|
||||
},
|
||||
},
|
||||
CodeBlock: ansi.StyleCodeBlock{
|
||||
StyleBlock: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Prefix: " ",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownCodeBlock())),
|
||||
BackgroundColor: background,
|
||||
Prefix: " ",
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownCodeBlock())),
|
||||
},
|
||||
},
|
||||
Chroma: &ansi.Chroma{
|
||||
Background: ansi.StylePrimitive{
|
||||
BackgroundColor: background,
|
||||
},
|
||||
Text: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownText())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownText())),
|
||||
},
|
||||
Error: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.Error())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.Error())),
|
||||
},
|
||||
Comment: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxComment())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxComment())),
|
||||
},
|
||||
CommentPreproc: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
|
||||
},
|
||||
Keyword: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
|
||||
},
|
||||
KeywordReserved: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
|
||||
},
|
||||
KeywordNamespace: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
|
||||
},
|
||||
KeywordType: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxType())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxType())),
|
||||
},
|
||||
Operator: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxOperator())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxOperator())),
|
||||
},
|
||||
Punctuation: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxPunctuation())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxPunctuation())),
|
||||
},
|
||||
Name: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxVariable())),
|
||||
},
|
||||
NameBuiltin: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxVariable())),
|
||||
},
|
||||
NameTag: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
|
||||
},
|
||||
NameAttribute: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxFunction())),
|
||||
},
|
||||
NameClass: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxType())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxType())),
|
||||
},
|
||||
NameConstant: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxVariable())),
|
||||
},
|
||||
NameDecorator: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxFunction())),
|
||||
},
|
||||
NameFunction: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxFunction())),
|
||||
},
|
||||
LiteralNumber: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxNumber())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxNumber())),
|
||||
},
|
||||
LiteralString: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxString())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxString())),
|
||||
},
|
||||
LiteralStringEscape: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
|
||||
},
|
||||
GenericDeleted: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.DiffRemoved())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.DiffRemoved())),
|
||||
},
|
||||
GenericEmph: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownEmph())),
|
||||
Italic: boolPtr(true),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownEmph())),
|
||||
Italic: boolPtr(true),
|
||||
},
|
||||
GenericInserted: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.DiffAdded())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.DiffAdded())),
|
||||
},
|
||||
GenericStrong: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownStrong())),
|
||||
Bold: boolPtr(true),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownStrong())),
|
||||
Bold: boolPtr(true),
|
||||
},
|
||||
GenericSubheading: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
|
||||
BackgroundColor: background,
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -259,24 +293,26 @@ func generateMarkdownStyleConfig() ansi.StyleConfig {
|
||||
},
|
||||
DefinitionDescription: ansi.StylePrimitive{
|
||||
BlockPrefix: "\n ❯ ",
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownLinkText())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownLinkText())),
|
||||
},
|
||||
Text: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownText())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownText())),
|
||||
},
|
||||
Paragraph: ansi.StyleBlock{
|
||||
StylePrimitive: ansi.StylePrimitive{
|
||||
Color: stringPtr(adaptiveColorToString(t.MarkdownText())),
|
||||
Color: stringPtr(AdaptiveColorToString(t.MarkdownText())),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// adaptiveColorToString converts a lipgloss.AdaptiveColor to the appropriate
|
||||
// AdaptiveColorToString converts a compat.AdaptiveColor to the appropriate
|
||||
// hex color string based on the current terminal background
|
||||
func adaptiveColorToString(color lipgloss.AdaptiveColor) string {
|
||||
if lipgloss.HasDarkBackground() {
|
||||
return color.Dark
|
||||
func AdaptiveColorToString(color compat.AdaptiveColor) string {
|
||||
if Terminal.BackgroundIsDark {
|
||||
c1, _ := colorful.MakeColor(color.Dark)
|
||||
return c1.Hex()
|
||||
}
|
||||
return color.Light
|
||||
c1, _ := colorful.MakeColor(color.Light)
|
||||
return c1.Hex()
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package styles
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2/compat"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
)
|
||||
|
||||
// BaseStyle returns the base style with background and foreground colors
|
||||
func BaseStyle() lipgloss.Style {
|
||||
t := theme.CurrentTheme()
|
||||
return lipgloss.NewStyle().
|
||||
Background(t.Background()).
|
||||
Foreground(t.Text())
|
||||
return lipgloss.NewStyle().Foreground(t.Text())
|
||||
}
|
||||
|
||||
func Panel() lipgloss.Style {
|
||||
@@ -29,7 +28,7 @@ func Regular() lipgloss.Style {
|
||||
|
||||
func Muted() lipgloss.Style {
|
||||
t := theme.CurrentTheme()
|
||||
return lipgloss.NewStyle().Background(t.Background()).Foreground(t.TextMuted())
|
||||
return lipgloss.NewStyle().Foreground(t.TextMuted())
|
||||
}
|
||||
|
||||
// Bold returns a bold style
|
||||
@@ -83,76 +82,76 @@ func DimBorder() lipgloss.Style {
|
||||
}
|
||||
|
||||
// PrimaryColor returns the primary color from the current theme
|
||||
func PrimaryColor() lipgloss.AdaptiveColor {
|
||||
func PrimaryColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().Primary()
|
||||
}
|
||||
|
||||
// SecondaryColor returns the secondary color from the current theme
|
||||
func SecondaryColor() lipgloss.AdaptiveColor {
|
||||
func SecondaryColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().Secondary()
|
||||
}
|
||||
|
||||
// AccentColor returns the accent color from the current theme
|
||||
func AccentColor() lipgloss.AdaptiveColor {
|
||||
func AccentColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().Accent()
|
||||
}
|
||||
|
||||
// ErrorColor returns the error color from the current theme
|
||||
func ErrorColor() lipgloss.AdaptiveColor {
|
||||
func ErrorColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().Error()
|
||||
}
|
||||
|
||||
// WarningColor returns the warning color from the current theme
|
||||
func WarningColor() lipgloss.AdaptiveColor {
|
||||
func WarningColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().Warning()
|
||||
}
|
||||
|
||||
// SuccessColor returns the success color from the current theme
|
||||
func SuccessColor() lipgloss.AdaptiveColor {
|
||||
func SuccessColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().Success()
|
||||
}
|
||||
|
||||
// InfoColor returns the info color from the current theme
|
||||
func InfoColor() lipgloss.AdaptiveColor {
|
||||
func InfoColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().Info()
|
||||
}
|
||||
|
||||
// TextColor returns the text color from the current theme
|
||||
func TextColor() lipgloss.AdaptiveColor {
|
||||
func TextColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().Text()
|
||||
}
|
||||
|
||||
// TextMutedColor returns the muted text color from the current theme
|
||||
func TextMutedColor() lipgloss.AdaptiveColor {
|
||||
func TextMutedColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().TextMuted()
|
||||
}
|
||||
|
||||
// BackgroundColor returns the background color from the current theme
|
||||
func BackgroundColor() lipgloss.AdaptiveColor {
|
||||
func BackgroundColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().Background()
|
||||
}
|
||||
|
||||
// BackgroundSubtleColor returns the subtle background color from the current theme
|
||||
func BackgroundSubtleColor() lipgloss.AdaptiveColor {
|
||||
func BackgroundSubtleColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().BackgroundSubtle()
|
||||
}
|
||||
|
||||
// BackgroundElementColor returns the darker background color from the current theme
|
||||
func BackgroundElementColor() lipgloss.AdaptiveColor {
|
||||
func BackgroundElementColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().BackgroundElement()
|
||||
}
|
||||
|
||||
// BorderColor returns the border color from the current theme
|
||||
func BorderColor() lipgloss.AdaptiveColor {
|
||||
func BorderColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().Border()
|
||||
}
|
||||
|
||||
// BorderActiveColor returns the active border color from the current theme
|
||||
func BorderActiveColor() lipgloss.AdaptiveColor {
|
||||
func BorderActiveColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().BorderActive()
|
||||
}
|
||||
|
||||
// BorderSubtleColor returns the subtle border color from the current theme
|
||||
func BorderSubtleColor() lipgloss.AdaptiveColor {
|
||||
func BorderSubtleColor() compat.AdaptiveColor {
|
||||
return theme.CurrentTheme().BorderSubtle()
|
||||
}
|
||||
|
||||
@@ -1,276 +0,0 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// AyuDarkTheme implements the Theme interface with Ayu Dark colors.
|
||||
type AyuDarkTheme struct {
|
||||
BaseTheme
|
||||
}
|
||||
|
||||
// AyuLightTheme implements the Theme interface with Ayu Light colors.
|
||||
type AyuLightTheme struct {
|
||||
BaseTheme
|
||||
}
|
||||
|
||||
// AyuMirageTheme implements the Theme interface with Ayu Mirage colors.
|
||||
type AyuMirageTheme struct {
|
||||
BaseTheme
|
||||
}
|
||||
|
||||
// NewAyuDarkTheme creates a new instance of the Ayu Dark theme.
|
||||
func NewAyuDarkTheme() *AyuDarkTheme {
|
||||
// Ayu Dark color palette
|
||||
darkBackground := "#0f1419"
|
||||
darkCurrentLine := "#191f26"
|
||||
darkSelection := "#253340"
|
||||
darkForeground := "#b3b1ad"
|
||||
darkComment := "#5c6773"
|
||||
darkBlue := "#53bdfa"
|
||||
darkCyan := "#90e1c6"
|
||||
darkGreen := "#91b362"
|
||||
darkOrange := "#f9af4f"
|
||||
darkPurple := "#fae994"
|
||||
darkRed := "#ea6c73"
|
||||
darkBorder := "#253340"
|
||||
|
||||
// Light mode approximation for terminal compatibility
|
||||
lightBackground := "#fafafa"
|
||||
lightCurrentLine := "#f0f0f0"
|
||||
lightSelection := "#d1d1d1"
|
||||
lightForeground := "#5c6773"
|
||||
lightComment := "#828c99"
|
||||
lightBlue := "#3199e1"
|
||||
lightCyan := "#46ba94"
|
||||
lightGreen := "#7c9f32"
|
||||
lightOrange := "#f29718"
|
||||
lightPurple := "#9e75c7"
|
||||
lightRed := "#f07171"
|
||||
lightBorder := "#d1d1d1"
|
||||
|
||||
theme := &AyuDarkTheme{}
|
||||
|
||||
// Base colors
|
||||
theme.PrimaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.SecondaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.AccentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
|
||||
// Status colors
|
||||
theme.ErrorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
}
|
||||
theme.WarningColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.SuccessColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.InfoColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
|
||||
// Text colors
|
||||
theme.TextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.TextMutedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
|
||||
// Background colors
|
||||
theme.BackgroundColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBackground,
|
||||
Light: lightBackground,
|
||||
}
|
||||
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCurrentLine,
|
||||
Light: lightCurrentLine,
|
||||
}
|
||||
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#0b0e14", // Darker than background
|
||||
Light: "#ffffff", // Lighter than background
|
||||
}
|
||||
|
||||
// Border colors
|
||||
theme.BorderColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBorder,
|
||||
Light: lightBorder,
|
||||
}
|
||||
theme.BorderActiveColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkSelection,
|
||||
Light: lightSelection,
|
||||
}
|
||||
|
||||
// Diff view colors
|
||||
theme.DiffAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
}
|
||||
theme.DiffContextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#91b362",
|
||||
Light: "#a5d6a7",
|
||||
}
|
||||
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#ea6c73",
|
||||
Light: "#ef9a9a",
|
||||
}
|
||||
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#1f2c1f",
|
||||
Light: "#e8f5e9",
|
||||
}
|
||||
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#2c1f1f",
|
||||
Light: "#ffebee",
|
||||
}
|
||||
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBackground,
|
||||
Light: lightBackground,
|
||||
}
|
||||
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#1a261a",
|
||||
Light: "#c8e6c9",
|
||||
}
|
||||
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#261a1a",
|
||||
Light: "#ffcdd2",
|
||||
}
|
||||
|
||||
// Markdown colors
|
||||
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
|
||||
// Syntax highlighting colors
|
||||
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
|
||||
return theme
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register all three Ayu theme variants with the theme manager
|
||||
RegisterTheme("ayu", NewAyuDarkTheme())
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
catppuccin "github.com/catppuccin/go"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// CatppuccinTheme implements the Theme interface with Catppuccin colors.
|
||||
// It provides both dark (Mocha) and light (Latte) variants.
|
||||
type CatppuccinTheme struct {
|
||||
BaseTheme
|
||||
}
|
||||
|
||||
// NewCatppuccinTheme creates a new instance of the Catppuccin theme.
|
||||
func NewCatppuccinTheme() *CatppuccinTheme {
|
||||
// Get the Catppuccin palettes
|
||||
mocha := catppuccin.Mocha
|
||||
latte := catppuccin.Latte
|
||||
|
||||
theme := &CatppuccinTheme{}
|
||||
|
||||
// Base colors
|
||||
theme.PrimaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Blue().Hex,
|
||||
Light: latte.Blue().Hex,
|
||||
}
|
||||
theme.SecondaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Mauve().Hex,
|
||||
Light: latte.Mauve().Hex,
|
||||
}
|
||||
theme.AccentColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Peach().Hex,
|
||||
Light: latte.Peach().Hex,
|
||||
}
|
||||
|
||||
// Status colors
|
||||
theme.ErrorColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Red().Hex,
|
||||
Light: latte.Red().Hex,
|
||||
}
|
||||
theme.WarningColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Peach().Hex,
|
||||
Light: latte.Peach().Hex,
|
||||
}
|
||||
theme.SuccessColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Green().Hex,
|
||||
Light: latte.Green().Hex,
|
||||
}
|
||||
theme.InfoColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Blue().Hex,
|
||||
Light: latte.Blue().Hex,
|
||||
}
|
||||
|
||||
// Text colors
|
||||
theme.TextColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Text().Hex,
|
||||
Light: latte.Text().Hex,
|
||||
}
|
||||
theme.TextMutedColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Subtext0().Hex,
|
||||
Light: latte.Subtext0().Hex,
|
||||
}
|
||||
|
||||
// Background colors
|
||||
theme.BackgroundColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#212121", // From existing styles
|
||||
Light: "#EEEEEE", // Light equivalent
|
||||
}
|
||||
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#2c2c2c", // From existing styles
|
||||
Light: "#E0E0E0", // Light equivalent
|
||||
}
|
||||
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#181818", // From existing styles
|
||||
Light: "#F5F5F5", // Light equivalent
|
||||
}
|
||||
|
||||
// Border colors
|
||||
theme.BorderColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#4b4c5c", // From existing styles
|
||||
Light: "#BDBDBD", // Light equivalent
|
||||
}
|
||||
theme.BorderActiveColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Blue().Hex,
|
||||
Light: latte.Blue().Hex,
|
||||
}
|
||||
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Surface0().Hex,
|
||||
Light: latte.Surface0().Hex,
|
||||
}
|
||||
|
||||
// Diff view colors
|
||||
theme.DiffAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#478247", // From existing diff.go
|
||||
Light: "#2E7D32", // Light equivalent
|
||||
}
|
||||
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#7C4444", // From existing diff.go
|
||||
Light: "#C62828", // Light equivalent
|
||||
}
|
||||
theme.DiffContextColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#a0a0a0", // From existing diff.go
|
||||
Light: "#757575", // Light equivalent
|
||||
}
|
||||
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#a0a0a0", // From existing diff.go
|
||||
Light: "#757575", // Light equivalent
|
||||
}
|
||||
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#DAFADA", // From existing diff.go
|
||||
Light: "#A5D6A7", // Light equivalent
|
||||
}
|
||||
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#FADADD", // From existing diff.go
|
||||
Light: "#EF9A9A", // Light equivalent
|
||||
}
|
||||
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#303A30", // From existing diff.go
|
||||
Light: "#E8F5E9", // Light equivalent
|
||||
}
|
||||
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#3A3030", // From existing diff.go
|
||||
Light: "#FFEBEE", // Light equivalent
|
||||
}
|
||||
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#212121", // From existing diff.go
|
||||
Light: "#F5F5F5", // Light equivalent
|
||||
}
|
||||
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#888888", // From existing diff.go
|
||||
Light: "#9E9E9E", // Light equivalent
|
||||
}
|
||||
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#293229", // From existing diff.go
|
||||
Light: "#C8E6C9", // Light equivalent
|
||||
}
|
||||
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#332929", // From existing diff.go
|
||||
Light: "#FFCDD2", // Light equivalent
|
||||
}
|
||||
|
||||
// Markdown colors
|
||||
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Text().Hex,
|
||||
Light: latte.Text().Hex,
|
||||
}
|
||||
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Mauve().Hex,
|
||||
Light: latte.Mauve().Hex,
|
||||
}
|
||||
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Sky().Hex,
|
||||
Light: latte.Sky().Hex,
|
||||
}
|
||||
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Pink().Hex,
|
||||
Light: latte.Pink().Hex,
|
||||
}
|
||||
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Green().Hex,
|
||||
Light: latte.Green().Hex,
|
||||
}
|
||||
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Yellow().Hex,
|
||||
Light: latte.Yellow().Hex,
|
||||
}
|
||||
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Yellow().Hex,
|
||||
Light: latte.Yellow().Hex,
|
||||
}
|
||||
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Peach().Hex,
|
||||
Light: latte.Peach().Hex,
|
||||
}
|
||||
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Overlay0().Hex,
|
||||
Light: latte.Overlay0().Hex,
|
||||
}
|
||||
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Blue().Hex,
|
||||
Light: latte.Blue().Hex,
|
||||
}
|
||||
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Sky().Hex,
|
||||
Light: latte.Sky().Hex,
|
||||
}
|
||||
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Sapphire().Hex,
|
||||
Light: latte.Sapphire().Hex,
|
||||
}
|
||||
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Pink().Hex,
|
||||
Light: latte.Pink().Hex,
|
||||
}
|
||||
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Text().Hex,
|
||||
Light: latte.Text().Hex,
|
||||
}
|
||||
|
||||
// Syntax highlighting colors
|
||||
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Overlay1().Hex,
|
||||
Light: latte.Overlay1().Hex,
|
||||
}
|
||||
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Pink().Hex,
|
||||
Light: latte.Pink().Hex,
|
||||
}
|
||||
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Green().Hex,
|
||||
Light: latte.Green().Hex,
|
||||
}
|
||||
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Sky().Hex,
|
||||
Light: latte.Sky().Hex,
|
||||
}
|
||||
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Yellow().Hex,
|
||||
Light: latte.Yellow().Hex,
|
||||
}
|
||||
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Teal().Hex,
|
||||
Light: latte.Teal().Hex,
|
||||
}
|
||||
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Sky().Hex,
|
||||
Light: latte.Sky().Hex,
|
||||
}
|
||||
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Pink().Hex,
|
||||
Light: latte.Pink().Hex,
|
||||
}
|
||||
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
|
||||
Dark: mocha.Text().Hex,
|
||||
Light: latte.Text().Hex,
|
||||
}
|
||||
|
||||
return theme
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register the Catppuccin theme with the theme manager
|
||||
RegisterTheme("catppuccin", NewCatppuccinTheme())
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// DraculaTheme implements the Theme interface with Dracula colors.
|
||||
// It provides both dark and light variants, though Dracula is primarily a dark theme.
|
||||
type DraculaTheme struct {
|
||||
BaseTheme
|
||||
}
|
||||
|
||||
// NewDraculaTheme creates a new instance of the Dracula theme.
|
||||
func NewDraculaTheme() *DraculaTheme {
|
||||
// Dracula color palette
|
||||
// Official colors from https://draculatheme.com/
|
||||
darkBackground := "#282a36"
|
||||
darkCurrentLine := "#44475a"
|
||||
darkSelection := "#44475a"
|
||||
darkForeground := "#f8f8f2"
|
||||
darkComment := "#6272a4"
|
||||
darkCyan := "#8be9fd"
|
||||
darkGreen := "#50fa7b"
|
||||
darkOrange := "#ffb86c"
|
||||
darkPink := "#ff79c6"
|
||||
darkPurple := "#bd93f9"
|
||||
darkRed := "#ff5555"
|
||||
darkYellow := "#f1fa8c"
|
||||
darkBorder := "#44475a"
|
||||
|
||||
// Light mode approximation (Dracula is primarily a dark theme)
|
||||
lightBackground := "#f8f8f2"
|
||||
lightCurrentLine := "#e6e6e6"
|
||||
lightSelection := "#d8d8d8"
|
||||
lightForeground := "#282a36"
|
||||
lightComment := "#6272a4"
|
||||
lightCyan := "#0097a7"
|
||||
lightGreen := "#388e3c"
|
||||
lightOrange := "#f57c00"
|
||||
lightPink := "#d81b60"
|
||||
lightPurple := "#7e57c2"
|
||||
lightRed := "#e53935"
|
||||
lightYellow := "#fbc02d"
|
||||
lightBorder := "#d8d8d8"
|
||||
|
||||
theme := &DraculaTheme{}
|
||||
|
||||
// Base colors
|
||||
theme.PrimaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.SecondaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPink,
|
||||
Light: lightPink,
|
||||
}
|
||||
theme.AccentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
|
||||
// Status colors
|
||||
theme.ErrorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
}
|
||||
theme.WarningColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.SuccessColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.InfoColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
|
||||
// Text colors
|
||||
theme.TextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.TextMutedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
|
||||
// Background colors
|
||||
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBackground,
|
||||
Light: lightBackground,
|
||||
}
|
||||
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCurrentLine,
|
||||
Light: lightCurrentLine,
|
||||
}
|
||||
theme.BackgroundColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#21222c", // Slightly darker than background
|
||||
Light: "#ffffff", // Slightly lighter than background
|
||||
}
|
||||
|
||||
// Border colors
|
||||
theme.BorderColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBorder,
|
||||
Light: lightBorder,
|
||||
}
|
||||
theme.BorderActiveColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkSelection,
|
||||
Light: lightSelection,
|
||||
}
|
||||
|
||||
// Diff view colors
|
||||
theme.DiffAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
}
|
||||
theme.DiffContextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCurrentLine,
|
||||
Light: lightCurrentLine,
|
||||
}
|
||||
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#50fa7b",
|
||||
Light: "#a5d6a7",
|
||||
}
|
||||
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#ff5555",
|
||||
Light: "#ef9a9a",
|
||||
}
|
||||
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#2c3b2c",
|
||||
Light: "#e8f5e9",
|
||||
}
|
||||
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#3b2c2c",
|
||||
Light: "#ffebee",
|
||||
}
|
||||
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBackground,
|
||||
Light: lightBackground,
|
||||
}
|
||||
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#253025",
|
||||
Light: "#c8e6c9",
|
||||
}
|
||||
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#302525",
|
||||
Light: "#ffcdd2",
|
||||
}
|
||||
|
||||
// Markdown colors
|
||||
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPink,
|
||||
Light: lightPink,
|
||||
}
|
||||
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
|
||||
// Syntax highlighting colors
|
||||
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPink,
|
||||
Light: lightPink,
|
||||
}
|
||||
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPink,
|
||||
Light: lightPink,
|
||||
}
|
||||
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
|
||||
return theme
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register the Dracula theme with the theme manager
|
||||
RegisterTheme("dracula", NewDraculaTheme())
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// Flexoki color palette constants
|
||||
const (
|
||||
// Base colors
|
||||
flexokiPaper = "#FFFCF0" // Paper (lightest)
|
||||
flexokiBase50 = "#F2F0E5" // bg-2 (light)
|
||||
flexokiBase100 = "#E6E4D9" // ui (light)
|
||||
flexokiBase150 = "#DAD8CE" // ui-2 (light)
|
||||
flexokiBase200 = "#CECDC3" // ui-3 (light)
|
||||
flexokiBase300 = "#B7B5AC" // tx-3 (light)
|
||||
flexokiBase500 = "#878580" // tx-2 (light)
|
||||
flexokiBase600 = "#6F6E69" // tx (light)
|
||||
flexokiBase700 = "#575653" // tx-3 (dark)
|
||||
flexokiBase800 = "#403E3C" // ui-3 (dark)
|
||||
flexokiBase850 = "#343331" // ui-2 (dark)
|
||||
flexokiBase900 = "#282726" // ui (dark)
|
||||
flexokiBase950 = "#1C1B1A" // bg-2 (dark)
|
||||
flexokiBlack = "#100F0F" // bg (darkest)
|
||||
|
||||
// Accent colors - Light theme (600)
|
||||
flexokiRed600 = "#AF3029"
|
||||
flexokiOrange600 = "#BC5215"
|
||||
flexokiYellow600 = "#AD8301"
|
||||
flexokiGreen600 = "#66800B"
|
||||
flexokiCyan600 = "#24837B"
|
||||
flexokiBlue600 = "#205EA6"
|
||||
flexokiPurple600 = "#5E409D"
|
||||
flexokiMagenta600 = "#A02F6F"
|
||||
|
||||
// Accent colors - Dark theme (400)
|
||||
flexokiRed400 = "#D14D41"
|
||||
flexokiOrange400 = "#DA702C"
|
||||
flexokiYellow400 = "#D0A215"
|
||||
flexokiGreen400 = "#879A39"
|
||||
flexokiCyan400 = "#3AA99F"
|
||||
flexokiBlue400 = "#4385BE"
|
||||
flexokiPurple400 = "#8B7EC8"
|
||||
flexokiMagenta400 = "#CE5D97"
|
||||
)
|
||||
|
||||
// FlexokiTheme implements the Theme interface with Flexoki colors.
|
||||
// It provides both dark and light variants.
|
||||
type FlexokiTheme struct {
|
||||
BaseTheme
|
||||
}
|
||||
|
||||
// NewFlexokiTheme creates a new instance of the Flexoki theme.
|
||||
func NewFlexokiTheme() *FlexokiTheme {
|
||||
theme := &FlexokiTheme{}
|
||||
|
||||
// Base colors
|
||||
theme.PrimaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBlue400,
|
||||
Light: flexokiBlue600,
|
||||
}
|
||||
theme.SecondaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiPurple400,
|
||||
Light: flexokiPurple600,
|
||||
}
|
||||
theme.AccentColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiOrange400,
|
||||
Light: flexokiOrange600,
|
||||
}
|
||||
|
||||
// Status colors
|
||||
theme.ErrorColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiRed400,
|
||||
Light: flexokiRed600,
|
||||
}
|
||||
theme.WarningColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiYellow400,
|
||||
Light: flexokiYellow600,
|
||||
}
|
||||
theme.SuccessColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiGreen400,
|
||||
Light: flexokiGreen600,
|
||||
}
|
||||
theme.InfoColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiCyan400,
|
||||
Light: flexokiCyan600,
|
||||
}
|
||||
|
||||
// Text colors
|
||||
theme.TextColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase300,
|
||||
Light: flexokiBase600,
|
||||
}
|
||||
theme.TextMutedColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase700,
|
||||
Light: flexokiBase500,
|
||||
}
|
||||
|
||||
// Background colors
|
||||
theme.BackgroundColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBlack,
|
||||
Light: flexokiPaper,
|
||||
}
|
||||
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase950,
|
||||
Light: flexokiBase50,
|
||||
}
|
||||
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase900,
|
||||
Light: flexokiBase100,
|
||||
}
|
||||
|
||||
// Border colors
|
||||
theme.BorderColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase900,
|
||||
Light: flexokiBase100,
|
||||
}
|
||||
theme.BorderActiveColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBlue400,
|
||||
Light: flexokiBlue600,
|
||||
}
|
||||
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase850,
|
||||
Light: flexokiBase150,
|
||||
}
|
||||
|
||||
// Diff view colors
|
||||
theme.DiffAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiGreen400,
|
||||
Light: flexokiGreen600,
|
||||
}
|
||||
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiRed400,
|
||||
Light: flexokiRed600,
|
||||
}
|
||||
theme.DiffContextColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase700,
|
||||
Light: flexokiBase500,
|
||||
}
|
||||
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase700,
|
||||
Light: flexokiBase500,
|
||||
}
|
||||
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiGreen400,
|
||||
Light: flexokiGreen600,
|
||||
}
|
||||
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiRed400,
|
||||
Light: flexokiRed600,
|
||||
}
|
||||
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#1D2419", // Darker green background
|
||||
Light: "#EFF2E2", // Light green background
|
||||
}
|
||||
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#241919", // Darker red background
|
||||
Light: "#F2E2E2", // Light red background
|
||||
}
|
||||
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBlack,
|
||||
Light: flexokiPaper,
|
||||
}
|
||||
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase700,
|
||||
Light: flexokiBase500,
|
||||
}
|
||||
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#1A2017", // Slightly darker green
|
||||
Light: "#E5EBD9", // Light green
|
||||
}
|
||||
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#201717", // Slightly darker red
|
||||
Light: "#EBD9D9", // Light red
|
||||
}
|
||||
|
||||
// Markdown colors
|
||||
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase300,
|
||||
Light: flexokiBase600,
|
||||
}
|
||||
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiYellow400,
|
||||
Light: flexokiYellow600,
|
||||
}
|
||||
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiCyan400,
|
||||
Light: flexokiCyan600,
|
||||
}
|
||||
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiMagenta400,
|
||||
Light: flexokiMagenta600,
|
||||
}
|
||||
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiGreen400,
|
||||
Light: flexokiGreen600,
|
||||
}
|
||||
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiCyan400,
|
||||
Light: flexokiCyan600,
|
||||
}
|
||||
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiYellow400,
|
||||
Light: flexokiYellow600,
|
||||
}
|
||||
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiOrange400,
|
||||
Light: flexokiOrange600,
|
||||
}
|
||||
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase800,
|
||||
Light: flexokiBase200,
|
||||
}
|
||||
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBlue400,
|
||||
Light: flexokiBlue600,
|
||||
}
|
||||
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBlue400,
|
||||
Light: flexokiBlue600,
|
||||
}
|
||||
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiPurple400,
|
||||
Light: flexokiPurple600,
|
||||
}
|
||||
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiMagenta400,
|
||||
Light: flexokiMagenta600,
|
||||
}
|
||||
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase300,
|
||||
Light: flexokiBase600,
|
||||
}
|
||||
|
||||
// Syntax highlighting colors (based on Flexoki's mappings)
|
||||
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase700, // tx-3
|
||||
Light: flexokiBase300, // tx-3
|
||||
}
|
||||
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiGreen400, // gr
|
||||
Light: flexokiGreen600, // gr
|
||||
}
|
||||
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiOrange400, // or
|
||||
Light: flexokiOrange600, // or
|
||||
}
|
||||
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBlue400, // bl
|
||||
Light: flexokiBlue600, // bl
|
||||
}
|
||||
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiCyan400, // cy
|
||||
Light: flexokiCyan600, // cy
|
||||
}
|
||||
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiPurple400, // pu
|
||||
Light: flexokiPurple600, // pu
|
||||
}
|
||||
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiYellow400, // ye
|
||||
Light: flexokiYellow600, // ye
|
||||
}
|
||||
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase500, // tx-2
|
||||
Light: flexokiBase500, // tx-2
|
||||
}
|
||||
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
|
||||
Dark: flexokiBase500, // tx-2
|
||||
Light: flexokiBase500, // tx-2
|
||||
}
|
||||
|
||||
return theme
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register the Flexoki theme with the theme manager
|
||||
RegisterTheme("flexoki", NewFlexokiTheme())
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// Gruvbox color palette constants
|
||||
const (
|
||||
// Dark theme colors
|
||||
gruvboxDarkBg0 = "#282828"
|
||||
gruvboxDarkBg0Soft = "#32302f"
|
||||
gruvboxDarkBg1 = "#3c3836"
|
||||
gruvboxDarkBg2 = "#504945"
|
||||
gruvboxDarkBg3 = "#665c54"
|
||||
gruvboxDarkBg4 = "#7c6f64"
|
||||
gruvboxDarkFg0 = "#fbf1c7"
|
||||
gruvboxDarkFg1 = "#ebdbb2"
|
||||
gruvboxDarkFg2 = "#d5c4a1"
|
||||
gruvboxDarkFg3 = "#bdae93"
|
||||
gruvboxDarkFg4 = "#a89984"
|
||||
gruvboxDarkGray = "#928374"
|
||||
gruvboxDarkRed = "#cc241d"
|
||||
gruvboxDarkRedBright = "#fb4934"
|
||||
gruvboxDarkGreen = "#98971a"
|
||||
gruvboxDarkGreenBright = "#b8bb26"
|
||||
gruvboxDarkYellow = "#d79921"
|
||||
gruvboxDarkYellowBright = "#fabd2f"
|
||||
gruvboxDarkBlue = "#458588"
|
||||
gruvboxDarkBlueBright = "#83a598"
|
||||
gruvboxDarkPurple = "#b16286"
|
||||
gruvboxDarkPurpleBright = "#d3869b"
|
||||
gruvboxDarkAqua = "#689d6a"
|
||||
gruvboxDarkAquaBright = "#8ec07c"
|
||||
gruvboxDarkOrange = "#d65d0e"
|
||||
gruvboxDarkOrangeBright = "#fe8019"
|
||||
|
||||
// Light theme colors
|
||||
gruvboxLightBg0 = "#fbf1c7"
|
||||
gruvboxLightBg0Soft = "#f2e5bc"
|
||||
gruvboxLightBg1 = "#ebdbb2"
|
||||
gruvboxLightBg2 = "#d5c4a1"
|
||||
gruvboxLightBg3 = "#bdae93"
|
||||
gruvboxLightBg4 = "#a89984"
|
||||
gruvboxLightFg0 = "#282828"
|
||||
gruvboxLightFg1 = "#3c3836"
|
||||
gruvboxLightFg2 = "#504945"
|
||||
gruvboxLightFg3 = "#665c54"
|
||||
gruvboxLightFg4 = "#7c6f64"
|
||||
gruvboxLightGray = "#928374"
|
||||
gruvboxLightRed = "#9d0006"
|
||||
gruvboxLightRedBright = "#cc241d"
|
||||
gruvboxLightGreen = "#79740e"
|
||||
gruvboxLightGreenBright = "#98971a"
|
||||
gruvboxLightYellow = "#b57614"
|
||||
gruvboxLightYellowBright = "#d79921"
|
||||
gruvboxLightBlue = "#076678"
|
||||
gruvboxLightBlueBright = "#458588"
|
||||
gruvboxLightPurple = "#8f3f71"
|
||||
gruvboxLightPurpleBright = "#b16286"
|
||||
gruvboxLightAqua = "#427b58"
|
||||
gruvboxLightAquaBright = "#689d6a"
|
||||
gruvboxLightOrange = "#af3a03"
|
||||
gruvboxLightOrangeBright = "#d65d0e"
|
||||
)
|
||||
|
||||
// GruvboxTheme implements the Theme interface with Gruvbox colors.
|
||||
// It provides both dark and light variants.
|
||||
type GruvboxTheme struct {
|
||||
BaseTheme
|
||||
}
|
||||
|
||||
// NewGruvboxTheme creates a new instance of the Gruvbox theme.
|
||||
func NewGruvboxTheme() *GruvboxTheme {
|
||||
theme := &GruvboxTheme{}
|
||||
|
||||
// Base colors
|
||||
theme.PrimaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBlueBright,
|
||||
Light: gruvboxLightBlueBright,
|
||||
}
|
||||
theme.SecondaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkPurpleBright,
|
||||
Light: gruvboxLightPurpleBright,
|
||||
}
|
||||
theme.AccentColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkOrangeBright,
|
||||
Light: gruvboxLightOrangeBright,
|
||||
}
|
||||
|
||||
// Status colors
|
||||
theme.ErrorColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkRedBright,
|
||||
Light: gruvboxLightRedBright,
|
||||
}
|
||||
theme.WarningColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkYellowBright,
|
||||
Light: gruvboxLightYellowBright,
|
||||
}
|
||||
theme.SuccessColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkGreenBright,
|
||||
Light: gruvboxLightGreenBright,
|
||||
}
|
||||
theme.InfoColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBlueBright,
|
||||
Light: gruvboxLightBlueBright,
|
||||
}
|
||||
|
||||
// Text colors
|
||||
theme.TextColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkFg1,
|
||||
Light: gruvboxLightFg1,
|
||||
}
|
||||
theme.TextMutedColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkFg4,
|
||||
Light: gruvboxLightFg4,
|
||||
}
|
||||
|
||||
// Background colors
|
||||
theme.BackgroundColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBg0,
|
||||
Light: gruvboxLightBg0,
|
||||
}
|
||||
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBg1,
|
||||
Light: gruvboxLightBg1,
|
||||
}
|
||||
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBg0Soft,
|
||||
Light: gruvboxLightBg0Soft,
|
||||
}
|
||||
|
||||
// Border colors
|
||||
theme.BorderColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBg2,
|
||||
Light: gruvboxLightBg2,
|
||||
}
|
||||
theme.BorderActiveColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBlueBright,
|
||||
Light: gruvboxLightBlueBright,
|
||||
}
|
||||
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBg1,
|
||||
Light: gruvboxLightBg1,
|
||||
}
|
||||
|
||||
// Diff view colors
|
||||
theme.DiffAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkGreenBright,
|
||||
Light: gruvboxLightGreenBright,
|
||||
}
|
||||
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkRedBright,
|
||||
Light: gruvboxLightRedBright,
|
||||
}
|
||||
theme.DiffContextColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkFg4,
|
||||
Light: gruvboxLightFg4,
|
||||
}
|
||||
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkFg3,
|
||||
Light: gruvboxLightFg3,
|
||||
}
|
||||
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkGreenBright,
|
||||
Light: gruvboxLightGreenBright,
|
||||
}
|
||||
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkRedBright,
|
||||
Light: gruvboxLightRedBright,
|
||||
}
|
||||
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#3C4C3C", // Darker green background
|
||||
Light: "#E8F5E9", // Light green background
|
||||
}
|
||||
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#4C3C3C", // Darker red background
|
||||
Light: "#FFEBEE", // Light red background
|
||||
}
|
||||
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBg0,
|
||||
Light: gruvboxLightBg0,
|
||||
}
|
||||
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkFg4,
|
||||
Light: gruvboxLightFg4,
|
||||
}
|
||||
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#32432F", // Slightly darker green
|
||||
Light: "#C8E6C9", // Light green
|
||||
}
|
||||
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#43322F", // Slightly darker red
|
||||
Light: "#FFCDD2", // Light red
|
||||
}
|
||||
|
||||
// Markdown colors
|
||||
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkFg1,
|
||||
Light: gruvboxLightFg1,
|
||||
}
|
||||
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkYellowBright,
|
||||
Light: gruvboxLightYellowBright,
|
||||
}
|
||||
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBlueBright,
|
||||
Light: gruvboxLightBlueBright,
|
||||
}
|
||||
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkAquaBright,
|
||||
Light: gruvboxLightAquaBright,
|
||||
}
|
||||
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkGreenBright,
|
||||
Light: gruvboxLightGreenBright,
|
||||
}
|
||||
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkAquaBright,
|
||||
Light: gruvboxLightAquaBright,
|
||||
}
|
||||
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkYellowBright,
|
||||
Light: gruvboxLightYellowBright,
|
||||
}
|
||||
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkOrangeBright,
|
||||
Light: gruvboxLightOrangeBright,
|
||||
}
|
||||
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBg3,
|
||||
Light: gruvboxLightBg3,
|
||||
}
|
||||
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBlueBright,
|
||||
Light: gruvboxLightBlueBright,
|
||||
}
|
||||
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBlueBright,
|
||||
Light: gruvboxLightBlueBright,
|
||||
}
|
||||
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkPurpleBright,
|
||||
Light: gruvboxLightPurpleBright,
|
||||
}
|
||||
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkAquaBright,
|
||||
Light: gruvboxLightAquaBright,
|
||||
}
|
||||
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkFg1,
|
||||
Light: gruvboxLightFg1,
|
||||
}
|
||||
|
||||
// Syntax highlighting colors
|
||||
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkGray,
|
||||
Light: gruvboxLightGray,
|
||||
}
|
||||
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkRedBright,
|
||||
Light: gruvboxLightRedBright,
|
||||
}
|
||||
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkGreenBright,
|
||||
Light: gruvboxLightGreenBright,
|
||||
}
|
||||
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkBlueBright,
|
||||
Light: gruvboxLightBlueBright,
|
||||
}
|
||||
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkYellowBright,
|
||||
Light: gruvboxLightYellowBright,
|
||||
}
|
||||
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkPurpleBright,
|
||||
Light: gruvboxLightPurpleBright,
|
||||
}
|
||||
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkYellow,
|
||||
Light: gruvboxLightYellow,
|
||||
}
|
||||
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkAquaBright,
|
||||
Light: gruvboxLightAquaBright,
|
||||
}
|
||||
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
|
||||
Dark: gruvboxDarkFg1,
|
||||
Light: gruvboxLightFg1,
|
||||
}
|
||||
|
||||
return theme
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register the Gruvbox theme with the theme manager
|
||||
RegisterTheme("gruvbox", NewGruvboxTheme())
|
||||
}
|
||||
@@ -6,8 +6,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
// "github.com/alecthomas/chroma/v2/styles"
|
||||
)
|
||||
|
||||
// Manager handles theme registration, selection, and retrieval.
|
||||
@@ -46,7 +45,7 @@ func RegisterTheme(name string, theme Theme) {
|
||||
func SetTheme(name string) error {
|
||||
globalManager.mu.Lock()
|
||||
defer globalManager.mu.Unlock()
|
||||
delete(styles.Registry, "charm")
|
||||
// delete(styles.Registry, "charm")
|
||||
|
||||
// Handle custom theme
|
||||
// if name == "custom" {
|
||||
|
||||
@@ -1,269 +0,0 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// MonokaiProTheme implements the Theme interface with Monokai Pro colors.
|
||||
// It provides both dark and light variants.
|
||||
type MonokaiProTheme struct {
|
||||
BaseTheme
|
||||
}
|
||||
|
||||
// NewMonokaiProTheme creates a new instance of the Monokai Pro theme.
|
||||
func NewMonokaiProTheme() *MonokaiProTheme {
|
||||
// Monokai Pro color palette (dark mode)
|
||||
darkBackground := "#2d2a2e"
|
||||
darkCurrentLine := "#403e41"
|
||||
darkSelection := "#5b595c"
|
||||
darkForeground := "#fcfcfa"
|
||||
darkComment := "#727072"
|
||||
darkRed := "#ff6188"
|
||||
darkOrange := "#fc9867"
|
||||
darkYellow := "#ffd866"
|
||||
darkGreen := "#a9dc76"
|
||||
darkCyan := "#78dce8"
|
||||
darkBlue := "#ab9df2"
|
||||
darkPurple := "#ab9df2"
|
||||
darkBorder := "#403e41"
|
||||
|
||||
// Light mode colors (adapted from dark)
|
||||
lightBackground := "#fafafa"
|
||||
lightCurrentLine := "#f0f0f0"
|
||||
lightSelection := "#e5e5e6"
|
||||
lightForeground := "#2d2a2e"
|
||||
lightComment := "#939293"
|
||||
lightRed := "#f92672"
|
||||
lightOrange := "#fd971f"
|
||||
lightYellow := "#e6db74"
|
||||
lightGreen := "#9bca65"
|
||||
lightCyan := "#66d9ef"
|
||||
lightBlue := "#7e75db"
|
||||
lightPurple := "#ae81ff"
|
||||
lightBorder := "#d3d3d3"
|
||||
|
||||
theme := &MonokaiProTheme{}
|
||||
|
||||
// Base colors
|
||||
theme.PrimaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.SecondaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.AccentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
|
||||
// Status colors
|
||||
theme.ErrorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
}
|
||||
theme.WarningColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.SuccessColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.InfoColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
|
||||
// Text colors
|
||||
theme.TextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.TextMutedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
|
||||
// Background colors
|
||||
theme.BackgroundColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBackground,
|
||||
Light: lightBackground,
|
||||
}
|
||||
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCurrentLine,
|
||||
Light: lightCurrentLine,
|
||||
}
|
||||
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#221f22", // Slightly darker than background
|
||||
Light: "#ffffff", // Slightly lighter than background
|
||||
}
|
||||
|
||||
// Border colors
|
||||
theme.BorderColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBorder,
|
||||
Light: lightBorder,
|
||||
}
|
||||
theme.BorderActiveColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkSelection,
|
||||
Light: lightSelection,
|
||||
}
|
||||
|
||||
// Diff view colors
|
||||
theme.DiffAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#a9dc76",
|
||||
Light: "#9bca65",
|
||||
}
|
||||
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#ff6188",
|
||||
Light: "#f92672",
|
||||
}
|
||||
theme.DiffContextColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#a0a0a0",
|
||||
Light: "#757575",
|
||||
}
|
||||
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#a0a0a0",
|
||||
Light: "#757575",
|
||||
}
|
||||
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#c2e7a9",
|
||||
Light: "#c5e0b4",
|
||||
}
|
||||
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#ff8ca6",
|
||||
Light: "#ffb3c8",
|
||||
}
|
||||
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#3a4a35",
|
||||
Light: "#e8f5e9",
|
||||
}
|
||||
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#4a3439",
|
||||
Light: "#ffebee",
|
||||
}
|
||||
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBackground,
|
||||
Light: lightBackground,
|
||||
}
|
||||
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#888888",
|
||||
Light: "#9e9e9e",
|
||||
}
|
||||
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#2d3a28",
|
||||
Light: "#c8e6c9",
|
||||
}
|
||||
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#3d2a2e",
|
||||
Light: "#ffcdd2",
|
||||
}
|
||||
|
||||
// Markdown colors
|
||||
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
|
||||
// Syntax highlighting colors
|
||||
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
}
|
||||
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
|
||||
return theme
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register the Monokai Pro theme with the theme manager
|
||||
RegisterTheme("monokai", NewMonokaiProTheme())
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// OneDarkTheme implements the Theme interface with Atom's One Dark colors.
|
||||
// It provides both dark and light variants.
|
||||
type OneDarkTheme struct {
|
||||
BaseTheme
|
||||
}
|
||||
|
||||
// NewOneDarkTheme creates a new instance of the One Dark theme.
|
||||
func NewOneDarkTheme() *OneDarkTheme {
|
||||
// One Dark color palette
|
||||
// Dark mode colors from Atom One Dark
|
||||
darkBackground := "#282c34"
|
||||
darkCurrentLine := "#2c313c"
|
||||
darkSelection := "#3e4451"
|
||||
darkForeground := "#abb2bf"
|
||||
darkComment := "#5c6370"
|
||||
darkRed := "#e06c75"
|
||||
darkOrange := "#d19a66"
|
||||
darkYellow := "#e5c07b"
|
||||
darkGreen := "#98c379"
|
||||
darkCyan := "#56b6c2"
|
||||
darkBlue := "#61afef"
|
||||
darkPurple := "#c678dd"
|
||||
darkBorder := "#3b4048"
|
||||
|
||||
// Light mode colors from Atom One Light
|
||||
lightBackground := "#fafafa"
|
||||
lightCurrentLine := "#f0f0f0"
|
||||
lightSelection := "#e5e5e6"
|
||||
lightForeground := "#383a42"
|
||||
lightComment := "#a0a1a7"
|
||||
lightRed := "#e45649"
|
||||
lightOrange := "#da8548"
|
||||
lightYellow := "#c18401"
|
||||
lightGreen := "#50a14f"
|
||||
lightCyan := "#0184bc"
|
||||
lightBlue := "#4078f2"
|
||||
lightPurple := "#a626a4"
|
||||
lightBorder := "#d3d3d3"
|
||||
|
||||
theme := &OneDarkTheme{}
|
||||
|
||||
// Base colors
|
||||
theme.PrimaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.SecondaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.AccentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
|
||||
// Status colors
|
||||
theme.ErrorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
}
|
||||
theme.WarningColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.SuccessColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.InfoColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
|
||||
// Text colors
|
||||
theme.TextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.TextMutedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
|
||||
// Background colors
|
||||
theme.BackgroundColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBackground,
|
||||
Light: lightBackground,
|
||||
}
|
||||
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCurrentLine,
|
||||
Light: lightCurrentLine,
|
||||
}
|
||||
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#21252b", // Slightly darker than background
|
||||
Light: "#ffffff", // Slightly lighter than background
|
||||
}
|
||||
|
||||
// Border colors
|
||||
theme.BorderColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBorder,
|
||||
Light: lightBorder,
|
||||
}
|
||||
theme.BorderActiveColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkSelection,
|
||||
Light: lightSelection,
|
||||
}
|
||||
|
||||
// Diff view colors
|
||||
theme.DiffAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#478247",
|
||||
Light: "#2E7D32",
|
||||
}
|
||||
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#7C4444",
|
||||
Light: "#C62828",
|
||||
}
|
||||
theme.DiffContextColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#a0a0a0",
|
||||
Light: "#757575",
|
||||
}
|
||||
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#a0a0a0",
|
||||
Light: "#757575",
|
||||
}
|
||||
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#DAFADA",
|
||||
Light: "#A5D6A7",
|
||||
}
|
||||
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#FADADD",
|
||||
Light: "#EF9A9A",
|
||||
}
|
||||
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#303A30",
|
||||
Light: "#E8F5E9",
|
||||
}
|
||||
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#3A3030",
|
||||
Light: "#FFEBEE",
|
||||
}
|
||||
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBackground,
|
||||
Light: lightBackground,
|
||||
}
|
||||
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#888888",
|
||||
Light: "#9E9E9E",
|
||||
}
|
||||
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#293229",
|
||||
Light: "#C8E6C9",
|
||||
}
|
||||
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#332929",
|
||||
Light: "#FFCDD2",
|
||||
}
|
||||
|
||||
// Markdown colors
|
||||
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
|
||||
// Syntax highlighting colors
|
||||
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
}
|
||||
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
|
||||
return theme
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register the One Dark theme with the theme manager
|
||||
RegisterTheme("onedark", NewOneDarkTheme())
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2/compat"
|
||||
)
|
||||
|
||||
// OpenCodeTheme implements the Theme interface with OpenCode brand colors.
|
||||
@@ -72,219 +73,219 @@ func NewOpenCodeTheme() *OpenCodeTheme {
|
||||
theme := &OpenCodeTheme{}
|
||||
|
||||
// Base colors
|
||||
theme.PrimaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPrimary,
|
||||
Light: lightPrimary,
|
||||
theme.PrimaryColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkPrimary),
|
||||
Light: lipgloss.Color(lightPrimary),
|
||||
}
|
||||
theme.SecondaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkSecondary,
|
||||
Light: lightSecondary,
|
||||
theme.SecondaryColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkSecondary),
|
||||
Light: lipgloss.Color(lightSecondary),
|
||||
}
|
||||
theme.AccentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkAccent,
|
||||
Light: lightAccent,
|
||||
theme.AccentColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkAccent),
|
||||
Light: lipgloss.Color(lightAccent),
|
||||
}
|
||||
|
||||
// Status colors
|
||||
theme.ErrorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
theme.ErrorColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkRed),
|
||||
Light: lipgloss.Color(lightRed),
|
||||
}
|
||||
theme.WarningColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
theme.WarningColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkOrange),
|
||||
Light: lipgloss.Color(lightOrange),
|
||||
}
|
||||
theme.SuccessColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
theme.SuccessColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkGreen),
|
||||
Light: lipgloss.Color(lightGreen),
|
||||
}
|
||||
theme.InfoColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
theme.InfoColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkCyan),
|
||||
Light: lipgloss.Color(lightCyan),
|
||||
}
|
||||
|
||||
// Text colors
|
||||
theme.TextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep12,
|
||||
Light: lightStep12,
|
||||
theme.TextColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep12),
|
||||
Light: lipgloss.Color(lightStep12),
|
||||
}
|
||||
theme.TextMutedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep11,
|
||||
Light: lightStep11,
|
||||
theme.TextMutedColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep11),
|
||||
Light: lipgloss.Color(lightStep11),
|
||||
}
|
||||
|
||||
// Background colors
|
||||
theme.BackgroundColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep1,
|
||||
Light: lightStep1,
|
||||
theme.BackgroundColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep1),
|
||||
Light: lipgloss.Color(lightStep1),
|
||||
}
|
||||
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep2,
|
||||
Light: lightStep2,
|
||||
theme.BackgroundSubtleColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep2),
|
||||
Light: lipgloss.Color(lightStep2),
|
||||
}
|
||||
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep3,
|
||||
Light: lightStep3,
|
||||
theme.BackgroundElementColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep3),
|
||||
Light: lipgloss.Color(lightStep3),
|
||||
}
|
||||
|
||||
// Border colors
|
||||
theme.BorderColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep7,
|
||||
Light: lightStep7,
|
||||
theme.BorderColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep7),
|
||||
Light: lipgloss.Color(lightStep7),
|
||||
}
|
||||
theme.BorderActiveColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep8,
|
||||
Light: lightStep8,
|
||||
theme.BorderActiveColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep8),
|
||||
Light: lipgloss.Color(lightStep8),
|
||||
}
|
||||
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep6,
|
||||
Light: lightStep6,
|
||||
theme.BorderSubtleColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep6),
|
||||
Light: lipgloss.Color(lightStep6),
|
||||
}
|
||||
|
||||
// Diff view colors
|
||||
theme.DiffAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#478247",
|
||||
Light: "#2E7D32",
|
||||
theme.DiffAddedColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#478247"),
|
||||
Light: lipgloss.Color("#2E7D32"),
|
||||
}
|
||||
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#7C4444",
|
||||
Light: "#C62828",
|
||||
theme.DiffRemovedColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#7C4444"),
|
||||
Light: lipgloss.Color("#C62828"),
|
||||
}
|
||||
theme.DiffContextColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#a0a0a0",
|
||||
Light: "#757575",
|
||||
theme.DiffContextColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#a0a0a0"),
|
||||
Light: lipgloss.Color("#757575"),
|
||||
}
|
||||
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#a0a0a0",
|
||||
Light: "#757575",
|
||||
theme.DiffHunkHeaderColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#a0a0a0"),
|
||||
Light: lipgloss.Color("#757575"),
|
||||
}
|
||||
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#DAFADA",
|
||||
Light: "#A5D6A7",
|
||||
theme.DiffHighlightAddedColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#DAFADA"),
|
||||
Light: lipgloss.Color("#A5D6A7"),
|
||||
}
|
||||
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#FADADD",
|
||||
Light: "#EF9A9A",
|
||||
theme.DiffHighlightRemovedColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#FADADD"),
|
||||
Light: lipgloss.Color("#EF9A9A"),
|
||||
}
|
||||
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#303A30",
|
||||
Light: "#E8F5E9",
|
||||
theme.DiffAddedBgColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#303A30"),
|
||||
Light: lipgloss.Color("#E8F5E9"),
|
||||
}
|
||||
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#3A3030",
|
||||
Light: "#FFEBEE",
|
||||
theme.DiffRemovedBgColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#3A3030"),
|
||||
Light: lipgloss.Color("#FFEBEE"),
|
||||
}
|
||||
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep2,
|
||||
Light: lightStep2,
|
||||
theme.DiffContextBgColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep2),
|
||||
Light: lipgloss.Color(lightStep2),
|
||||
}
|
||||
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep3,
|
||||
Light: lightStep3,
|
||||
theme.DiffLineNumberColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep3),
|
||||
Light: lipgloss.Color(lightStep3),
|
||||
}
|
||||
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#293229",
|
||||
Light: "#C8E6C9",
|
||||
theme.DiffAddedLineNumberBgColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#293229"),
|
||||
Light: lipgloss.Color("#C8E6C9"),
|
||||
}
|
||||
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#332929",
|
||||
Light: "#FFCDD2",
|
||||
theme.DiffRemovedLineNumberBgColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#332929"),
|
||||
Light: lipgloss.Color("#FFCDD2"),
|
||||
}
|
||||
|
||||
// Markdown colors
|
||||
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep12,
|
||||
Light: lightStep12,
|
||||
theme.MarkdownTextColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep12),
|
||||
Light: lipgloss.Color(lightStep12),
|
||||
}
|
||||
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkSecondary,
|
||||
Light: lightSecondary,
|
||||
theme.MarkdownHeadingColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkSecondary),
|
||||
Light: lipgloss.Color(lightSecondary),
|
||||
}
|
||||
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPrimary,
|
||||
Light: lightPrimary,
|
||||
theme.MarkdownLinkColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkPrimary),
|
||||
Light: lipgloss.Color(lightPrimary),
|
||||
}
|
||||
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
theme.MarkdownLinkTextColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkCyan),
|
||||
Light: lipgloss.Color(lightCyan),
|
||||
}
|
||||
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
theme.MarkdownCodeColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkGreen),
|
||||
Light: lipgloss.Color(lightGreen),
|
||||
}
|
||||
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
theme.MarkdownBlockQuoteColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkYellow),
|
||||
Light: lipgloss.Color(lightYellow),
|
||||
}
|
||||
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
theme.MarkdownEmphColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkYellow),
|
||||
Light: lipgloss.Color(lightYellow),
|
||||
}
|
||||
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkAccent,
|
||||
Light: lightAccent,
|
||||
theme.MarkdownStrongColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkAccent),
|
||||
Light: lipgloss.Color(lightAccent),
|
||||
}
|
||||
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep11,
|
||||
Light: lightStep11,
|
||||
theme.MarkdownHorizontalRuleColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep11),
|
||||
Light: lipgloss.Color(lightStep11),
|
||||
}
|
||||
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPrimary,
|
||||
Light: lightPrimary,
|
||||
theme.MarkdownListItemColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkPrimary),
|
||||
Light: lipgloss.Color(lightPrimary),
|
||||
}
|
||||
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
theme.MarkdownListEnumerationColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkCyan),
|
||||
Light: lipgloss.Color(lightCyan),
|
||||
}
|
||||
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPrimary,
|
||||
Light: lightPrimary,
|
||||
theme.MarkdownImageColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkPrimary),
|
||||
Light: lipgloss.Color(lightPrimary),
|
||||
}
|
||||
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
theme.MarkdownImageTextColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkCyan),
|
||||
Light: lipgloss.Color(lightCyan),
|
||||
}
|
||||
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep12,
|
||||
Light: lightStep12,
|
||||
theme.MarkdownCodeBlockColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep12),
|
||||
Light: lipgloss.Color(lightStep12),
|
||||
}
|
||||
|
||||
// Syntax highlighting colors
|
||||
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep11,
|
||||
Light: lightStep11,
|
||||
theme.SyntaxCommentColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep11),
|
||||
Light: lipgloss.Color(lightStep11),
|
||||
}
|
||||
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkSecondary,
|
||||
Light: lightSecondary,
|
||||
theme.SyntaxKeywordColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkPrimary),
|
||||
Light: lipgloss.Color(lightPrimary),
|
||||
}
|
||||
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPrimary,
|
||||
Light: lightPrimary,
|
||||
theme.SyntaxFunctionColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkPrimary),
|
||||
Light: lipgloss.Color(lightPrimary),
|
||||
}
|
||||
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
theme.SyntaxVariableColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkRed),
|
||||
Light: lipgloss.Color(lightRed),
|
||||
}
|
||||
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
theme.SyntaxStringColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkGreen),
|
||||
Light: lipgloss.Color(lightGreen),
|
||||
}
|
||||
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkAccent,
|
||||
Light: lightAccent,
|
||||
theme.SyntaxNumberColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkAccent),
|
||||
Light: lipgloss.Color(lightAccent),
|
||||
}
|
||||
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
theme.SyntaxTypeColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkYellow),
|
||||
Light: lipgloss.Color(lightYellow),
|
||||
}
|
||||
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
theme.SyntaxOperatorColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkCyan),
|
||||
Light: lipgloss.Color(lightCyan),
|
||||
}
|
||||
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep12,
|
||||
Light: lightStep12,
|
||||
theme.SyntaxPunctuationColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep12),
|
||||
Light: lipgloss.Color(lightStep12),
|
||||
}
|
||||
|
||||
return theme
|
||||
|
||||
@@ -4,231 +4,232 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2/compat"
|
||||
)
|
||||
|
||||
// Theme defines the interface for all UI themes in the application.
|
||||
// All colors must be defined as lipgloss.AdaptiveColor to support
|
||||
// All colors must be defined as compat.AdaptiveColor to support
|
||||
// both light and dark terminal backgrounds.
|
||||
type Theme interface {
|
||||
// Background colors
|
||||
Background() lipgloss.AdaptiveColor // Radix 1
|
||||
BackgroundSubtle() lipgloss.AdaptiveColor // Radix 2
|
||||
BackgroundElement() lipgloss.AdaptiveColor // Radix 3
|
||||
Background() compat.AdaptiveColor // Radix 1
|
||||
BackgroundSubtle() compat.AdaptiveColor // Radix 2
|
||||
BackgroundElement() compat.AdaptiveColor // Radix 3
|
||||
|
||||
// Border colors
|
||||
BorderSubtle() lipgloss.AdaptiveColor // Radix 6
|
||||
Border() lipgloss.AdaptiveColor // Radix 7
|
||||
BorderActive() lipgloss.AdaptiveColor // Radix 8
|
||||
BorderSubtle() compat.AdaptiveColor // Radix 6
|
||||
Border() compat.AdaptiveColor // Radix 7
|
||||
BorderActive() compat.AdaptiveColor // Radix 8
|
||||
|
||||
// Brand colors
|
||||
Primary() lipgloss.AdaptiveColor // Radix 9
|
||||
Secondary() lipgloss.AdaptiveColor
|
||||
Accent() lipgloss.AdaptiveColor
|
||||
Primary() compat.AdaptiveColor // Radix 9
|
||||
Secondary() compat.AdaptiveColor
|
||||
Accent() compat.AdaptiveColor
|
||||
|
||||
// Text colors
|
||||
TextMuted() lipgloss.AdaptiveColor // Radix 11
|
||||
Text() lipgloss.AdaptiveColor // Radix 12
|
||||
TextMuted() compat.AdaptiveColor // Radix 11
|
||||
Text() compat.AdaptiveColor // Radix 12
|
||||
|
||||
// Status colors
|
||||
Error() lipgloss.AdaptiveColor
|
||||
Warning() lipgloss.AdaptiveColor
|
||||
Success() lipgloss.AdaptiveColor
|
||||
Info() lipgloss.AdaptiveColor
|
||||
Error() compat.AdaptiveColor
|
||||
Warning() compat.AdaptiveColor
|
||||
Success() compat.AdaptiveColor
|
||||
Info() compat.AdaptiveColor
|
||||
|
||||
// Diff view colors
|
||||
DiffAdded() lipgloss.AdaptiveColor
|
||||
DiffRemoved() lipgloss.AdaptiveColor
|
||||
DiffContext() lipgloss.AdaptiveColor
|
||||
DiffHunkHeader() lipgloss.AdaptiveColor
|
||||
DiffHighlightAdded() lipgloss.AdaptiveColor
|
||||
DiffHighlightRemoved() lipgloss.AdaptiveColor
|
||||
DiffAddedBg() lipgloss.AdaptiveColor
|
||||
DiffRemovedBg() lipgloss.AdaptiveColor
|
||||
DiffContextBg() lipgloss.AdaptiveColor
|
||||
DiffLineNumber() lipgloss.AdaptiveColor
|
||||
DiffAddedLineNumberBg() lipgloss.AdaptiveColor
|
||||
DiffRemovedLineNumberBg() lipgloss.AdaptiveColor
|
||||
DiffAdded() compat.AdaptiveColor
|
||||
DiffRemoved() compat.AdaptiveColor
|
||||
DiffContext() compat.AdaptiveColor
|
||||
DiffHunkHeader() compat.AdaptiveColor
|
||||
DiffHighlightAdded() compat.AdaptiveColor
|
||||
DiffHighlightRemoved() compat.AdaptiveColor
|
||||
DiffAddedBg() compat.AdaptiveColor
|
||||
DiffRemovedBg() compat.AdaptiveColor
|
||||
DiffContextBg() compat.AdaptiveColor
|
||||
DiffLineNumber() compat.AdaptiveColor
|
||||
DiffAddedLineNumberBg() compat.AdaptiveColor
|
||||
DiffRemovedLineNumberBg() compat.AdaptiveColor
|
||||
|
||||
// Markdown colors
|
||||
MarkdownText() lipgloss.AdaptiveColor
|
||||
MarkdownHeading() lipgloss.AdaptiveColor
|
||||
MarkdownLink() lipgloss.AdaptiveColor
|
||||
MarkdownLinkText() lipgloss.AdaptiveColor
|
||||
MarkdownCode() lipgloss.AdaptiveColor
|
||||
MarkdownBlockQuote() lipgloss.AdaptiveColor
|
||||
MarkdownEmph() lipgloss.AdaptiveColor
|
||||
MarkdownStrong() lipgloss.AdaptiveColor
|
||||
MarkdownHorizontalRule() lipgloss.AdaptiveColor
|
||||
MarkdownListItem() lipgloss.AdaptiveColor
|
||||
MarkdownListEnumeration() lipgloss.AdaptiveColor
|
||||
MarkdownImage() lipgloss.AdaptiveColor
|
||||
MarkdownImageText() lipgloss.AdaptiveColor
|
||||
MarkdownCodeBlock() lipgloss.AdaptiveColor
|
||||
MarkdownText() compat.AdaptiveColor
|
||||
MarkdownHeading() compat.AdaptiveColor
|
||||
MarkdownLink() compat.AdaptiveColor
|
||||
MarkdownLinkText() compat.AdaptiveColor
|
||||
MarkdownCode() compat.AdaptiveColor
|
||||
MarkdownBlockQuote() compat.AdaptiveColor
|
||||
MarkdownEmph() compat.AdaptiveColor
|
||||
MarkdownStrong() compat.AdaptiveColor
|
||||
MarkdownHorizontalRule() compat.AdaptiveColor
|
||||
MarkdownListItem() compat.AdaptiveColor
|
||||
MarkdownListEnumeration() compat.AdaptiveColor
|
||||
MarkdownImage() compat.AdaptiveColor
|
||||
MarkdownImageText() compat.AdaptiveColor
|
||||
MarkdownCodeBlock() compat.AdaptiveColor
|
||||
|
||||
// Syntax highlighting colors
|
||||
SyntaxComment() lipgloss.AdaptiveColor
|
||||
SyntaxKeyword() lipgloss.AdaptiveColor
|
||||
SyntaxFunction() lipgloss.AdaptiveColor
|
||||
SyntaxVariable() lipgloss.AdaptiveColor
|
||||
SyntaxString() lipgloss.AdaptiveColor
|
||||
SyntaxNumber() lipgloss.AdaptiveColor
|
||||
SyntaxType() lipgloss.AdaptiveColor
|
||||
SyntaxOperator() lipgloss.AdaptiveColor
|
||||
SyntaxPunctuation() lipgloss.AdaptiveColor
|
||||
SyntaxComment() compat.AdaptiveColor
|
||||
SyntaxKeyword() compat.AdaptiveColor
|
||||
SyntaxFunction() compat.AdaptiveColor
|
||||
SyntaxVariable() compat.AdaptiveColor
|
||||
SyntaxString() compat.AdaptiveColor
|
||||
SyntaxNumber() compat.AdaptiveColor
|
||||
SyntaxType() compat.AdaptiveColor
|
||||
SyntaxOperator() compat.AdaptiveColor
|
||||
SyntaxPunctuation() compat.AdaptiveColor
|
||||
}
|
||||
|
||||
// BaseTheme provides a default implementation of the Theme interface
|
||||
// that can be embedded in concrete theme implementations.
|
||||
type BaseTheme struct {
|
||||
// Background colors
|
||||
BackgroundColor lipgloss.AdaptiveColor
|
||||
BackgroundSubtleColor lipgloss.AdaptiveColor
|
||||
BackgroundElementColor lipgloss.AdaptiveColor
|
||||
BackgroundColor compat.AdaptiveColor
|
||||
BackgroundSubtleColor compat.AdaptiveColor
|
||||
BackgroundElementColor compat.AdaptiveColor
|
||||
|
||||
// Border colors
|
||||
BorderSubtleColor lipgloss.AdaptiveColor
|
||||
BorderColor lipgloss.AdaptiveColor
|
||||
BorderActiveColor lipgloss.AdaptiveColor
|
||||
BorderSubtleColor compat.AdaptiveColor
|
||||
BorderColor compat.AdaptiveColor
|
||||
BorderActiveColor compat.AdaptiveColor
|
||||
|
||||
// Brand colors
|
||||
PrimaryColor lipgloss.AdaptiveColor
|
||||
SecondaryColor lipgloss.AdaptiveColor
|
||||
AccentColor lipgloss.AdaptiveColor
|
||||
PrimaryColor compat.AdaptiveColor
|
||||
SecondaryColor compat.AdaptiveColor
|
||||
AccentColor compat.AdaptiveColor
|
||||
|
||||
// Text colors
|
||||
TextMutedColor lipgloss.AdaptiveColor
|
||||
TextColor lipgloss.AdaptiveColor
|
||||
TextMutedColor compat.AdaptiveColor
|
||||
TextColor compat.AdaptiveColor
|
||||
|
||||
// Status colors
|
||||
ErrorColor lipgloss.AdaptiveColor
|
||||
WarningColor lipgloss.AdaptiveColor
|
||||
SuccessColor lipgloss.AdaptiveColor
|
||||
InfoColor lipgloss.AdaptiveColor
|
||||
ErrorColor compat.AdaptiveColor
|
||||
WarningColor compat.AdaptiveColor
|
||||
SuccessColor compat.AdaptiveColor
|
||||
InfoColor compat.AdaptiveColor
|
||||
|
||||
// Diff view colors
|
||||
DiffAddedColor lipgloss.AdaptiveColor
|
||||
DiffRemovedColor lipgloss.AdaptiveColor
|
||||
DiffContextColor lipgloss.AdaptiveColor
|
||||
DiffHunkHeaderColor lipgloss.AdaptiveColor
|
||||
DiffHighlightAddedColor lipgloss.AdaptiveColor
|
||||
DiffHighlightRemovedColor lipgloss.AdaptiveColor
|
||||
DiffAddedBgColor lipgloss.AdaptiveColor
|
||||
DiffRemovedBgColor lipgloss.AdaptiveColor
|
||||
DiffContextBgColor lipgloss.AdaptiveColor
|
||||
DiffLineNumberColor lipgloss.AdaptiveColor
|
||||
DiffAddedLineNumberBgColor lipgloss.AdaptiveColor
|
||||
DiffRemovedLineNumberBgColor lipgloss.AdaptiveColor
|
||||
DiffAddedColor compat.AdaptiveColor
|
||||
DiffRemovedColor compat.AdaptiveColor
|
||||
DiffContextColor compat.AdaptiveColor
|
||||
DiffHunkHeaderColor compat.AdaptiveColor
|
||||
DiffHighlightAddedColor compat.AdaptiveColor
|
||||
DiffHighlightRemovedColor compat.AdaptiveColor
|
||||
DiffAddedBgColor compat.AdaptiveColor
|
||||
DiffRemovedBgColor compat.AdaptiveColor
|
||||
DiffContextBgColor compat.AdaptiveColor
|
||||
DiffLineNumberColor compat.AdaptiveColor
|
||||
DiffAddedLineNumberBgColor compat.AdaptiveColor
|
||||
DiffRemovedLineNumberBgColor compat.AdaptiveColor
|
||||
|
||||
// Markdown colors
|
||||
MarkdownTextColor lipgloss.AdaptiveColor
|
||||
MarkdownHeadingColor lipgloss.AdaptiveColor
|
||||
MarkdownLinkColor lipgloss.AdaptiveColor
|
||||
MarkdownLinkTextColor lipgloss.AdaptiveColor
|
||||
MarkdownCodeColor lipgloss.AdaptiveColor
|
||||
MarkdownBlockQuoteColor lipgloss.AdaptiveColor
|
||||
MarkdownEmphColor lipgloss.AdaptiveColor
|
||||
MarkdownStrongColor lipgloss.AdaptiveColor
|
||||
MarkdownHorizontalRuleColor lipgloss.AdaptiveColor
|
||||
MarkdownListItemColor lipgloss.AdaptiveColor
|
||||
MarkdownListEnumerationColor lipgloss.AdaptiveColor
|
||||
MarkdownImageColor lipgloss.AdaptiveColor
|
||||
MarkdownImageTextColor lipgloss.AdaptiveColor
|
||||
MarkdownCodeBlockColor lipgloss.AdaptiveColor
|
||||
MarkdownTextColor compat.AdaptiveColor
|
||||
MarkdownHeadingColor compat.AdaptiveColor
|
||||
MarkdownLinkColor compat.AdaptiveColor
|
||||
MarkdownLinkTextColor compat.AdaptiveColor
|
||||
MarkdownCodeColor compat.AdaptiveColor
|
||||
MarkdownBlockQuoteColor compat.AdaptiveColor
|
||||
MarkdownEmphColor compat.AdaptiveColor
|
||||
MarkdownStrongColor compat.AdaptiveColor
|
||||
MarkdownHorizontalRuleColor compat.AdaptiveColor
|
||||
MarkdownListItemColor compat.AdaptiveColor
|
||||
MarkdownListEnumerationColor compat.AdaptiveColor
|
||||
MarkdownImageColor compat.AdaptiveColor
|
||||
MarkdownImageTextColor compat.AdaptiveColor
|
||||
MarkdownCodeBlockColor compat.AdaptiveColor
|
||||
|
||||
// Syntax highlighting colors
|
||||
SyntaxCommentColor lipgloss.AdaptiveColor
|
||||
SyntaxKeywordColor lipgloss.AdaptiveColor
|
||||
SyntaxFunctionColor lipgloss.AdaptiveColor
|
||||
SyntaxVariableColor lipgloss.AdaptiveColor
|
||||
SyntaxStringColor lipgloss.AdaptiveColor
|
||||
SyntaxNumberColor lipgloss.AdaptiveColor
|
||||
SyntaxTypeColor lipgloss.AdaptiveColor
|
||||
SyntaxOperatorColor lipgloss.AdaptiveColor
|
||||
SyntaxPunctuationColor lipgloss.AdaptiveColor
|
||||
SyntaxCommentColor compat.AdaptiveColor
|
||||
SyntaxKeywordColor compat.AdaptiveColor
|
||||
SyntaxFunctionColor compat.AdaptiveColor
|
||||
SyntaxVariableColor compat.AdaptiveColor
|
||||
SyntaxStringColor compat.AdaptiveColor
|
||||
SyntaxNumberColor compat.AdaptiveColor
|
||||
SyntaxTypeColor compat.AdaptiveColor
|
||||
SyntaxOperatorColor compat.AdaptiveColor
|
||||
SyntaxPunctuationColor compat.AdaptiveColor
|
||||
}
|
||||
|
||||
// Implement the Theme interface for BaseTheme
|
||||
func (t *BaseTheme) Primary() lipgloss.AdaptiveColor { return t.PrimaryColor }
|
||||
func (t *BaseTheme) Secondary() lipgloss.AdaptiveColor { return t.SecondaryColor }
|
||||
func (t *BaseTheme) Accent() lipgloss.AdaptiveColor { return t.AccentColor }
|
||||
func (t *BaseTheme) Primary() compat.AdaptiveColor { return t.PrimaryColor }
|
||||
func (t *BaseTheme) Secondary() compat.AdaptiveColor { return t.SecondaryColor }
|
||||
func (t *BaseTheme) Accent() compat.AdaptiveColor { return t.AccentColor }
|
||||
|
||||
func (t *BaseTheme) Error() lipgloss.AdaptiveColor { return t.ErrorColor }
|
||||
func (t *BaseTheme) Warning() lipgloss.AdaptiveColor { return t.WarningColor }
|
||||
func (t *BaseTheme) Success() lipgloss.AdaptiveColor { return t.SuccessColor }
|
||||
func (t *BaseTheme) Info() lipgloss.AdaptiveColor { return t.InfoColor }
|
||||
func (t *BaseTheme) Error() compat.AdaptiveColor { return t.ErrorColor }
|
||||
func (t *BaseTheme) Warning() compat.AdaptiveColor { return t.WarningColor }
|
||||
func (t *BaseTheme) Success() compat.AdaptiveColor { return t.SuccessColor }
|
||||
func (t *BaseTheme) Info() compat.AdaptiveColor { return t.InfoColor }
|
||||
|
||||
func (t *BaseTheme) Text() lipgloss.AdaptiveColor { return t.TextColor }
|
||||
func (t *BaseTheme) TextMuted() lipgloss.AdaptiveColor { return t.TextMutedColor }
|
||||
func (t *BaseTheme) Text() compat.AdaptiveColor { return t.TextColor }
|
||||
func (t *BaseTheme) TextMuted() compat.AdaptiveColor { return t.TextMutedColor }
|
||||
|
||||
func (t *BaseTheme) Background() lipgloss.AdaptiveColor { return t.BackgroundColor }
|
||||
func (t *BaseTheme) BackgroundSubtle() lipgloss.AdaptiveColor { return t.BackgroundSubtleColor }
|
||||
func (t *BaseTheme) BackgroundElement() lipgloss.AdaptiveColor { return t.BackgroundElementColor }
|
||||
func (t *BaseTheme) Background() compat.AdaptiveColor { return t.BackgroundColor }
|
||||
func (t *BaseTheme) BackgroundSubtle() compat.AdaptiveColor { return t.BackgroundSubtleColor }
|
||||
func (t *BaseTheme) BackgroundElement() compat.AdaptiveColor { return t.BackgroundElementColor }
|
||||
|
||||
func (t *BaseTheme) Border() lipgloss.AdaptiveColor { return t.BorderColor }
|
||||
func (t *BaseTheme) BorderActive() lipgloss.AdaptiveColor { return t.BorderActiveColor }
|
||||
func (t *BaseTheme) BorderSubtle() lipgloss.AdaptiveColor { return t.BorderSubtleColor }
|
||||
func (t *BaseTheme) Border() compat.AdaptiveColor { return t.BorderColor }
|
||||
func (t *BaseTheme) BorderActive() compat.AdaptiveColor { return t.BorderActiveColor }
|
||||
func (t *BaseTheme) BorderSubtle() compat.AdaptiveColor { return t.BorderSubtleColor }
|
||||
|
||||
func (t *BaseTheme) DiffAdded() lipgloss.AdaptiveColor { return t.DiffAddedColor }
|
||||
func (t *BaseTheme) DiffRemoved() lipgloss.AdaptiveColor { return t.DiffRemovedColor }
|
||||
func (t *BaseTheme) DiffContext() lipgloss.AdaptiveColor { return t.DiffContextColor }
|
||||
func (t *BaseTheme) DiffHunkHeader() lipgloss.AdaptiveColor { return t.DiffHunkHeaderColor }
|
||||
func (t *BaseTheme) DiffHighlightAdded() lipgloss.AdaptiveColor { return t.DiffHighlightAddedColor }
|
||||
func (t *BaseTheme) DiffHighlightRemoved() lipgloss.AdaptiveColor { return t.DiffHighlightRemovedColor }
|
||||
func (t *BaseTheme) DiffAddedBg() lipgloss.AdaptiveColor { return t.DiffAddedBgColor }
|
||||
func (t *BaseTheme) DiffRemovedBg() lipgloss.AdaptiveColor { return t.DiffRemovedBgColor }
|
||||
func (t *BaseTheme) DiffContextBg() lipgloss.AdaptiveColor { return t.DiffContextBgColor }
|
||||
func (t *BaseTheme) DiffLineNumber() lipgloss.AdaptiveColor { return t.DiffLineNumberColor }
|
||||
func (t *BaseTheme) DiffAddedLineNumberBg() lipgloss.AdaptiveColor {
|
||||
func (t *BaseTheme) DiffAdded() compat.AdaptiveColor { return t.DiffAddedColor }
|
||||
func (t *BaseTheme) DiffRemoved() compat.AdaptiveColor { return t.DiffRemovedColor }
|
||||
func (t *BaseTheme) DiffContext() compat.AdaptiveColor { return t.DiffContextColor }
|
||||
func (t *BaseTheme) DiffHunkHeader() compat.AdaptiveColor { return t.DiffHunkHeaderColor }
|
||||
func (t *BaseTheme) DiffHighlightAdded() compat.AdaptiveColor { return t.DiffHighlightAddedColor }
|
||||
func (t *BaseTheme) DiffHighlightRemoved() compat.AdaptiveColor { return t.DiffHighlightRemovedColor }
|
||||
func (t *BaseTheme) DiffAddedBg() compat.AdaptiveColor { return t.DiffAddedBgColor }
|
||||
func (t *BaseTheme) DiffRemovedBg() compat.AdaptiveColor { return t.DiffRemovedBgColor }
|
||||
func (t *BaseTheme) DiffContextBg() compat.AdaptiveColor { return t.DiffContextBgColor }
|
||||
func (t *BaseTheme) DiffLineNumber() compat.AdaptiveColor { return t.DiffLineNumberColor }
|
||||
func (t *BaseTheme) DiffAddedLineNumberBg() compat.AdaptiveColor {
|
||||
return t.DiffAddedLineNumberBgColor
|
||||
}
|
||||
func (t *BaseTheme) DiffRemovedLineNumberBg() lipgloss.AdaptiveColor {
|
||||
func (t *BaseTheme) DiffRemovedLineNumberBg() compat.AdaptiveColor {
|
||||
return t.DiffRemovedLineNumberBgColor
|
||||
}
|
||||
|
||||
func (t *BaseTheme) MarkdownText() lipgloss.AdaptiveColor { return t.MarkdownTextColor }
|
||||
func (t *BaseTheme) MarkdownHeading() lipgloss.AdaptiveColor { return t.MarkdownHeadingColor }
|
||||
func (t *BaseTheme) MarkdownLink() lipgloss.AdaptiveColor { return t.MarkdownLinkColor }
|
||||
func (t *BaseTheme) MarkdownLinkText() lipgloss.AdaptiveColor { return t.MarkdownLinkTextColor }
|
||||
func (t *BaseTheme) MarkdownCode() lipgloss.AdaptiveColor { return t.MarkdownCodeColor }
|
||||
func (t *BaseTheme) MarkdownBlockQuote() lipgloss.AdaptiveColor { return t.MarkdownBlockQuoteColor }
|
||||
func (t *BaseTheme) MarkdownEmph() lipgloss.AdaptiveColor { return t.MarkdownEmphColor }
|
||||
func (t *BaseTheme) MarkdownStrong() lipgloss.AdaptiveColor { return t.MarkdownStrongColor }
|
||||
func (t *BaseTheme) MarkdownHorizontalRule() lipgloss.AdaptiveColor {
|
||||
func (t *BaseTheme) MarkdownText() compat.AdaptiveColor { return t.MarkdownTextColor }
|
||||
func (t *BaseTheme) MarkdownHeading() compat.AdaptiveColor { return t.MarkdownHeadingColor }
|
||||
func (t *BaseTheme) MarkdownLink() compat.AdaptiveColor { return t.MarkdownLinkColor }
|
||||
func (t *BaseTheme) MarkdownLinkText() compat.AdaptiveColor { return t.MarkdownLinkTextColor }
|
||||
func (t *BaseTheme) MarkdownCode() compat.AdaptiveColor { return t.MarkdownCodeColor }
|
||||
func (t *BaseTheme) MarkdownBlockQuote() compat.AdaptiveColor { return t.MarkdownBlockQuoteColor }
|
||||
func (t *BaseTheme) MarkdownEmph() compat.AdaptiveColor { return t.MarkdownEmphColor }
|
||||
func (t *BaseTheme) MarkdownStrong() compat.AdaptiveColor { return t.MarkdownStrongColor }
|
||||
func (t *BaseTheme) MarkdownHorizontalRule() compat.AdaptiveColor {
|
||||
return t.MarkdownHorizontalRuleColor
|
||||
}
|
||||
func (t *BaseTheme) MarkdownListItem() lipgloss.AdaptiveColor { return t.MarkdownListItemColor }
|
||||
func (t *BaseTheme) MarkdownListEnumeration() lipgloss.AdaptiveColor {
|
||||
func (t *BaseTheme) MarkdownListItem() compat.AdaptiveColor { return t.MarkdownListItemColor }
|
||||
func (t *BaseTheme) MarkdownListEnumeration() compat.AdaptiveColor {
|
||||
return t.MarkdownListEnumerationColor
|
||||
}
|
||||
func (t *BaseTheme) MarkdownImage() lipgloss.AdaptiveColor { return t.MarkdownImageColor }
|
||||
func (t *BaseTheme) MarkdownImageText() lipgloss.AdaptiveColor { return t.MarkdownImageTextColor }
|
||||
func (t *BaseTheme) MarkdownCodeBlock() lipgloss.AdaptiveColor { return t.MarkdownCodeBlockColor }
|
||||
func (t *BaseTheme) MarkdownImage() compat.AdaptiveColor { return t.MarkdownImageColor }
|
||||
func (t *BaseTheme) MarkdownImageText() compat.AdaptiveColor { return t.MarkdownImageTextColor }
|
||||
func (t *BaseTheme) MarkdownCodeBlock() compat.AdaptiveColor { return t.MarkdownCodeBlockColor }
|
||||
|
||||
func (t *BaseTheme) SyntaxComment() lipgloss.AdaptiveColor { return t.SyntaxCommentColor }
|
||||
func (t *BaseTheme) SyntaxKeyword() lipgloss.AdaptiveColor { return t.SyntaxKeywordColor }
|
||||
func (t *BaseTheme) SyntaxFunction() lipgloss.AdaptiveColor { return t.SyntaxFunctionColor }
|
||||
func (t *BaseTheme) SyntaxVariable() lipgloss.AdaptiveColor { return t.SyntaxVariableColor }
|
||||
func (t *BaseTheme) SyntaxString() lipgloss.AdaptiveColor { return t.SyntaxStringColor }
|
||||
func (t *BaseTheme) SyntaxNumber() lipgloss.AdaptiveColor { return t.SyntaxNumberColor }
|
||||
func (t *BaseTheme) SyntaxType() lipgloss.AdaptiveColor { return t.SyntaxTypeColor }
|
||||
func (t *BaseTheme) SyntaxOperator() lipgloss.AdaptiveColor { return t.SyntaxOperatorColor }
|
||||
func (t *BaseTheme) SyntaxPunctuation() lipgloss.AdaptiveColor { return t.SyntaxPunctuationColor }
|
||||
func (t *BaseTheme) SyntaxComment() compat.AdaptiveColor { return t.SyntaxCommentColor }
|
||||
func (t *BaseTheme) SyntaxKeyword() compat.AdaptiveColor { return t.SyntaxKeywordColor }
|
||||
func (t *BaseTheme) SyntaxFunction() compat.AdaptiveColor { return t.SyntaxFunctionColor }
|
||||
func (t *BaseTheme) SyntaxVariable() compat.AdaptiveColor { return t.SyntaxVariableColor }
|
||||
func (t *BaseTheme) SyntaxString() compat.AdaptiveColor { return t.SyntaxStringColor }
|
||||
func (t *BaseTheme) SyntaxNumber() compat.AdaptiveColor { return t.SyntaxNumberColor }
|
||||
func (t *BaseTheme) SyntaxType() compat.AdaptiveColor { return t.SyntaxTypeColor }
|
||||
func (t *BaseTheme) SyntaxOperator() compat.AdaptiveColor { return t.SyntaxOperatorColor }
|
||||
func (t *BaseTheme) SyntaxPunctuation() compat.AdaptiveColor { return t.SyntaxPunctuationColor }
|
||||
|
||||
// ParseAdaptiveColor parses a color value from the config file into a lipgloss.AdaptiveColor.
|
||||
// ParseAdaptiveColor parses a color value from the config file into a compat.AdaptiveColor.
|
||||
// It accepts either a string (hex color) or a map with "dark" and "light" keys.
|
||||
func ParseAdaptiveColor(value any) (lipgloss.AdaptiveColor, error) {
|
||||
func ParseAdaptiveColor(value any) (compat.AdaptiveColor, error) {
|
||||
// Regular expression to validate hex color format
|
||||
hexColorRegex := regexp.MustCompile(`^#[0-9a-fA-F]{6}$`)
|
||||
|
||||
// Case 1: String value (same color for both dark and light modes)
|
||||
if hexColor, ok := value.(string); ok {
|
||||
if !hexColorRegex.MatchString(hexColor) {
|
||||
return lipgloss.AdaptiveColor{}, fmt.Errorf("invalid hex color format: %s", hexColor)
|
||||
return compat.AdaptiveColor{}, fmt.Errorf("invalid hex color format: %s", hexColor)
|
||||
}
|
||||
return lipgloss.AdaptiveColor{
|
||||
Dark: hexColor,
|
||||
Light: hexColor,
|
||||
return compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(hexColor),
|
||||
Light: lipgloss.Color(hexColor),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -236,11 +237,11 @@ func ParseAdaptiveColor(value any) (lipgloss.AdaptiveColor, error) {
|
||||
if numericVal, ok := value.(float64); ok {
|
||||
intVal := int(numericVal)
|
||||
if intVal < 0 || intVal > 255 {
|
||||
return lipgloss.AdaptiveColor{}, fmt.Errorf("invalid int color value (must be between 0 and 255): %d", intVal)
|
||||
return compat.AdaptiveColor{}, fmt.Errorf("invalid int color value (must be between 0 and 255): %d", intVal)
|
||||
}
|
||||
return lipgloss.AdaptiveColor{
|
||||
Dark: fmt.Sprintf("%d", intVal),
|
||||
Light: fmt.Sprintf("%d", intVal),
|
||||
return compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(fmt.Sprintf("%d", intVal)),
|
||||
Light: lipgloss.Color(fmt.Sprintf("%d", intVal)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -250,7 +251,7 @@ func ParseAdaptiveColor(value any) (lipgloss.AdaptiveColor, error) {
|
||||
lightVal, lightOk := colorMap["light"]
|
||||
|
||||
if !darkOk || !lightOk {
|
||||
return lipgloss.AdaptiveColor{}, fmt.Errorf("color map must contain both 'dark' and 'light' keys")
|
||||
return compat.AdaptiveColor{}, fmt.Errorf("color map must contain both 'dark' and 'light' keys")
|
||||
}
|
||||
|
||||
darkHex, darkIsString := darkVal.(string)
|
||||
@@ -261,27 +262,27 @@ func ParseAdaptiveColor(value any) (lipgloss.AdaptiveColor, error) {
|
||||
lightVal, lightIsNumber := lightVal.(float64)
|
||||
|
||||
if !darkIsNumber || !lightIsNumber {
|
||||
return lipgloss.AdaptiveColor{}, fmt.Errorf("color map values must be strings or ints")
|
||||
return compat.AdaptiveColor{}, fmt.Errorf("color map values must be strings or ints")
|
||||
}
|
||||
|
||||
darkInt := int(darkVal)
|
||||
lightInt := int(lightVal)
|
||||
|
||||
return lipgloss.AdaptiveColor{
|
||||
Dark: fmt.Sprintf("%d", darkInt),
|
||||
Light: fmt.Sprintf("%d", lightInt),
|
||||
return compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(fmt.Sprintf("%d", darkInt)),
|
||||
Light: lipgloss.Color(fmt.Sprintf("%d", lightInt)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if !hexColorRegex.MatchString(darkHex) || !hexColorRegex.MatchString(lightHex) {
|
||||
return lipgloss.AdaptiveColor{}, fmt.Errorf("invalid hex color format")
|
||||
return compat.AdaptiveColor{}, fmt.Errorf("invalid hex color format")
|
||||
}
|
||||
|
||||
return lipgloss.AdaptiveColor{
|
||||
Dark: darkHex,
|
||||
Light: lightHex,
|
||||
return compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkHex),
|
||||
Light: lipgloss.Color(lightHex),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return lipgloss.AdaptiveColor{}, fmt.Errorf("color must be either a hex string or an object with dark/light keys")
|
||||
return compat.AdaptiveColor{}, fmt.Errorf("color must be either a hex string or an object with dark/light keys")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/charmbracelet/lipgloss/v2/compat"
|
||||
)
|
||||
|
||||
// TokyoNightTheme implements the Theme interface with Tokyo Night colors.
|
||||
@@ -70,219 +71,219 @@ func NewTokyoNightTheme() *TokyoNightTheme {
|
||||
theme := &TokyoNightTheme{}
|
||||
|
||||
// Base colors
|
||||
theme.PrimaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
theme.PrimaryColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkBlue),
|
||||
Light: lipgloss.Color(lightBlue),
|
||||
}
|
||||
theme.SecondaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
theme.SecondaryColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkPurple),
|
||||
Light: lipgloss.Color(lightPurple),
|
||||
}
|
||||
theme.AccentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
theme.AccentColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkOrange),
|
||||
Light: lipgloss.Color(lightOrange),
|
||||
}
|
||||
|
||||
// Status colors
|
||||
theme.ErrorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
theme.ErrorColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkRed),
|
||||
Light: lipgloss.Color(lightRed),
|
||||
}
|
||||
theme.WarningColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
theme.WarningColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkOrange),
|
||||
Light: lipgloss.Color(lightOrange),
|
||||
}
|
||||
theme.SuccessColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
theme.SuccessColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkGreen),
|
||||
Light: lipgloss.Color(lightGreen),
|
||||
}
|
||||
theme.InfoColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
theme.InfoColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkBlue),
|
||||
Light: lipgloss.Color(lightBlue),
|
||||
}
|
||||
|
||||
// Text colors
|
||||
theme.TextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep12,
|
||||
Light: lightStep12,
|
||||
theme.TextColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep12),
|
||||
Light: lipgloss.Color(lightStep12),
|
||||
}
|
||||
theme.TextMutedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep11,
|
||||
Light: lightStep11,
|
||||
theme.TextMutedColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep11),
|
||||
Light: lipgloss.Color(lightStep11),
|
||||
}
|
||||
|
||||
// Background colors
|
||||
theme.BackgroundColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep1,
|
||||
Light: lightStep1,
|
||||
theme.BackgroundColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep1),
|
||||
Light: lipgloss.Color(lightStep1),
|
||||
}
|
||||
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep2,
|
||||
Light: lightStep2,
|
||||
theme.BackgroundSubtleColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep2),
|
||||
Light: lipgloss.Color(lightStep2),
|
||||
}
|
||||
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep3,
|
||||
Light: lightStep3,
|
||||
theme.BackgroundElementColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep3),
|
||||
Light: lipgloss.Color(lightStep3),
|
||||
}
|
||||
|
||||
// Border colors
|
||||
theme.BorderColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep7,
|
||||
Light: lightStep7,
|
||||
theme.BorderColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep7),
|
||||
Light: lipgloss.Color(lightStep7),
|
||||
}
|
||||
theme.BorderActiveColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep8,
|
||||
Light: lightStep8,
|
||||
theme.BorderActiveColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep8),
|
||||
Light: lipgloss.Color(lightStep8),
|
||||
}
|
||||
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep6,
|
||||
Light: lightStep6,
|
||||
theme.BorderSubtleColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep6),
|
||||
Light: lipgloss.Color(lightStep6),
|
||||
}
|
||||
|
||||
// Diff view colors
|
||||
theme.DiffAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#4fd6be", // teal from palette
|
||||
Light: "#1e725c",
|
||||
theme.DiffAddedColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#4fd6be"), // teal from palette
|
||||
Light: lipgloss.Color("#1e725c"),
|
||||
}
|
||||
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#c53b53", // red1 from palette
|
||||
Light: "#c53b53",
|
||||
theme.DiffRemovedColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#c53b53"), // red1 from palette
|
||||
Light: lipgloss.Color("#c53b53"),
|
||||
}
|
||||
theme.DiffContextColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#828bb8", // fg_dark from palette
|
||||
Light: "#7086b5",
|
||||
theme.DiffContextColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#828bb8"), // fg_dark from palette
|
||||
Light: lipgloss.Color("#7086b5"),
|
||||
}
|
||||
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#828bb8", // fg_dark from palette
|
||||
Light: "#7086b5",
|
||||
theme.DiffHunkHeaderColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#828bb8"), // fg_dark from palette
|
||||
Light: lipgloss.Color("#7086b5"),
|
||||
}
|
||||
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#b8db87", // git.add from palette
|
||||
Light: "#4db380",
|
||||
theme.DiffHighlightAddedColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#b8db87"), // git.add from palette
|
||||
Light: lipgloss.Color("#4db380"),
|
||||
}
|
||||
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#e26a75", // git.delete from palette
|
||||
Light: "#f52a65",
|
||||
theme.DiffHighlightRemovedColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#e26a75"), // git.delete from palette
|
||||
Light: lipgloss.Color("#f52a65"),
|
||||
}
|
||||
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#20303b",
|
||||
Light: "#d5e5d5",
|
||||
theme.DiffAddedBgColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#20303b"),
|
||||
Light: lipgloss.Color("#d5e5d5"),
|
||||
}
|
||||
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#37222c",
|
||||
Light: "#f7d8db",
|
||||
theme.DiffRemovedBgColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#37222c"),
|
||||
Light: lipgloss.Color("#f7d8db"),
|
||||
}
|
||||
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep2,
|
||||
Light: lightStep2,
|
||||
theme.DiffContextBgColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep2),
|
||||
Light: lipgloss.Color(lightStep2),
|
||||
}
|
||||
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep3, // dark3 from palette
|
||||
Light: lightStep3,
|
||||
theme.DiffLineNumberColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep3), // dark3 from palette
|
||||
Light: lipgloss.Color(lightStep3),
|
||||
}
|
||||
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#1b2b34",
|
||||
Light: "#c5d5c5",
|
||||
theme.DiffAddedLineNumberBgColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#1b2b34"),
|
||||
Light: lipgloss.Color("#c5d5c5"),
|
||||
}
|
||||
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#2d1f26",
|
||||
Light: "#e7c8cb",
|
||||
theme.DiffRemovedLineNumberBgColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color("#2d1f26"),
|
||||
Light: lipgloss.Color("#e7c8cb"),
|
||||
}
|
||||
|
||||
// Markdown colors
|
||||
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep12,
|
||||
Light: lightStep12,
|
||||
theme.MarkdownTextColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep12),
|
||||
Light: lipgloss.Color(lightStep12),
|
||||
}
|
||||
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
theme.MarkdownHeadingColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkPurple),
|
||||
Light: lipgloss.Color(lightPurple),
|
||||
}
|
||||
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
theme.MarkdownLinkColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkBlue),
|
||||
Light: lipgloss.Color(lightBlue),
|
||||
}
|
||||
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
theme.MarkdownLinkTextColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkCyan),
|
||||
Light: lipgloss.Color(lightCyan),
|
||||
}
|
||||
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
theme.MarkdownCodeColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkGreen),
|
||||
Light: lipgloss.Color(lightGreen),
|
||||
}
|
||||
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
theme.MarkdownBlockQuoteColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkYellow),
|
||||
Light: lipgloss.Color(lightYellow),
|
||||
}
|
||||
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
theme.MarkdownEmphColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkYellow),
|
||||
Light: lipgloss.Color(lightYellow),
|
||||
}
|
||||
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
theme.MarkdownStrongColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkOrange),
|
||||
Light: lipgloss.Color(lightOrange),
|
||||
}
|
||||
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep11,
|
||||
Light: lightStep11,
|
||||
theme.MarkdownHorizontalRuleColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep11),
|
||||
Light: lipgloss.Color(lightStep11),
|
||||
}
|
||||
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
theme.MarkdownListItemColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkBlue),
|
||||
Light: lipgloss.Color(lightBlue),
|
||||
}
|
||||
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
theme.MarkdownListEnumerationColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkCyan),
|
||||
Light: lipgloss.Color(lightCyan),
|
||||
}
|
||||
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
theme.MarkdownImageColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkBlue),
|
||||
Light: lipgloss.Color(lightBlue),
|
||||
}
|
||||
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
theme.MarkdownImageTextColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkCyan),
|
||||
Light: lipgloss.Color(lightCyan),
|
||||
}
|
||||
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep12,
|
||||
Light: lightStep12,
|
||||
theme.MarkdownCodeBlockColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep12),
|
||||
Light: lipgloss.Color(lightStep12),
|
||||
}
|
||||
|
||||
// Syntax highlighting colors
|
||||
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep11,
|
||||
Light: lightStep11,
|
||||
theme.SyntaxCommentColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep11),
|
||||
Light: lipgloss.Color(lightStep11),
|
||||
}
|
||||
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
theme.SyntaxKeywordColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkPurple),
|
||||
Light: lipgloss.Color(lightPurple),
|
||||
}
|
||||
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
theme.SyntaxFunctionColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkBlue),
|
||||
Light: lipgloss.Color(lightBlue),
|
||||
}
|
||||
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
theme.SyntaxVariableColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkRed),
|
||||
Light: lipgloss.Color(lightRed),
|
||||
}
|
||||
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
theme.SyntaxStringColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkGreen),
|
||||
Light: lipgloss.Color(lightGreen),
|
||||
}
|
||||
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
theme.SyntaxNumberColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkOrange),
|
||||
Light: lipgloss.Color(lightOrange),
|
||||
}
|
||||
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
theme.SyntaxTypeColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkYellow),
|
||||
Light: lipgloss.Color(lightYellow),
|
||||
}
|
||||
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
theme.SyntaxOperatorColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkCyan),
|
||||
Light: lipgloss.Color(lightCyan),
|
||||
}
|
||||
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkStep12,
|
||||
Light: lightStep12,
|
||||
theme.SyntaxPunctuationColor = compat.AdaptiveColor{
|
||||
Dark: lipgloss.Color(darkStep12),
|
||||
Light: lipgloss.Color(lightStep12),
|
||||
}
|
||||
|
||||
return theme
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// TronTheme implements the Theme interface with Tron-inspired colors.
|
||||
// It provides both dark and light variants, though Tron is primarily a dark theme.
|
||||
type TronTheme struct {
|
||||
BaseTheme
|
||||
}
|
||||
|
||||
// NewTronTheme creates a new instance of the Tron theme.
|
||||
func NewTronTheme() *TronTheme {
|
||||
// Tron color palette
|
||||
// Inspired by the Tron movie's neon aesthetic
|
||||
darkBackground := "#0c141f"
|
||||
darkCurrentLine := "#1a2633"
|
||||
darkSelection := "#1a2633"
|
||||
darkForeground := "#caf0ff"
|
||||
darkComment := "#4d6b87"
|
||||
darkCyan := "#00d9ff"
|
||||
darkBlue := "#007fff"
|
||||
darkOrange := "#ff9000"
|
||||
darkPink := "#ff00a0"
|
||||
darkPurple := "#b73fff"
|
||||
darkRed := "#ff3333"
|
||||
darkYellow := "#ffcc00"
|
||||
darkGreen := "#00ff8f"
|
||||
darkBorder := "#1a2633"
|
||||
|
||||
// Light mode approximation
|
||||
lightBackground := "#f0f8ff"
|
||||
lightCurrentLine := "#e0f0ff"
|
||||
lightSelection := "#d0e8ff"
|
||||
lightForeground := "#0c141f"
|
||||
lightComment := "#4d6b87"
|
||||
lightCyan := "#0097b3"
|
||||
lightBlue := "#0066cc"
|
||||
lightOrange := "#cc7300"
|
||||
lightPink := "#cc0080"
|
||||
lightPurple := "#9932cc"
|
||||
lightRed := "#cc2929"
|
||||
lightYellow := "#cc9900"
|
||||
lightGreen := "#00cc72"
|
||||
lightBorder := "#d0e8ff"
|
||||
|
||||
theme := &TronTheme{}
|
||||
|
||||
// Base colors
|
||||
theme.PrimaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.SecondaryColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.AccentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
|
||||
// Status colors
|
||||
theme.ErrorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
}
|
||||
theme.WarningColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.SuccessColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.InfoColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
|
||||
// Text colors
|
||||
theme.TextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.TextMutedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
|
||||
// Background colors
|
||||
theme.BackgroundColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBackground,
|
||||
Light: lightBackground,
|
||||
}
|
||||
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCurrentLine,
|
||||
Light: lightCurrentLine,
|
||||
}
|
||||
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#070d14", // Slightly darker than background
|
||||
Light: "#ffffff", // Slightly lighter than background
|
||||
}
|
||||
|
||||
// Border colors
|
||||
theme.BorderColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBorder,
|
||||
Light: lightBorder,
|
||||
}
|
||||
theme.BorderActiveColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkSelection,
|
||||
Light: lightSelection,
|
||||
}
|
||||
|
||||
// Diff view colors
|
||||
theme.DiffAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkRed,
|
||||
Light: lightRed,
|
||||
}
|
||||
theme.DiffContextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#00ff8f",
|
||||
Light: "#a5d6a7",
|
||||
}
|
||||
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#ff3333",
|
||||
Light: "#ef9a9a",
|
||||
}
|
||||
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#0a2a1a",
|
||||
Light: "#e8f5e9",
|
||||
}
|
||||
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#2a0a0a",
|
||||
Light: "#ffebee",
|
||||
}
|
||||
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBackground,
|
||||
Light: lightBackground,
|
||||
}
|
||||
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#082015",
|
||||
Light: "#c8e6c9",
|
||||
}
|
||||
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
|
||||
Dark: "#200808",
|
||||
Light: "#ffcdd2",
|
||||
}
|
||||
|
||||
// Markdown colors
|
||||
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
|
||||
// Syntax highlighting colors
|
||||
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkComment,
|
||||
Light: lightComment,
|
||||
}
|
||||
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkCyan,
|
||||
Light: lightCyan,
|
||||
}
|
||||
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkGreen,
|
||||
Light: lightGreen,
|
||||
}
|
||||
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkOrange,
|
||||
Light: lightOrange,
|
||||
}
|
||||
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkYellow,
|
||||
Light: lightYellow,
|
||||
}
|
||||
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkBlue,
|
||||
Light: lightBlue,
|
||||
}
|
||||
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPurple,
|
||||
Light: lightPurple,
|
||||
}
|
||||
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkPink,
|
||||
Light: lightPink,
|
||||
}
|
||||
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
|
||||
Dark: darkForeground,
|
||||
Light: lightForeground,
|
||||
}
|
||||
|
||||
return theme
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register the Tron theme with the theme manager
|
||||
RegisterTheme("tron", NewTronTheme())
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
)
|
||||
|
||||
func CmdHandler(msg tea.Msg) tea.Cmd {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<svg width="29" height="29" viewBox="0 0 29 29" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.184 28.512C12.144 28.512 10.26 28.152 8.532 27.432C6.804 26.736 5.304 25.752 4.032 24.48C2.76 23.184 1.764 21.672 1.044 19.944C0.348 18.216 0 16.332 0 14.292C0 12.228 0.36 10.332 1.08 8.604C1.8 6.852 2.796 5.34 4.068 4.068C5.364 2.772 6.876 1.776 8.604 1.08C10.332 0.36 12.192 0 14.184 0C16.224 0 18.108 0.36 19.836 1.08C21.588 1.8 23.112 2.808 24.408 4.104C25.704 5.4 26.712 6.924 27.432 8.676C28.152 10.404 28.512 12.276 28.512 14.292C28.512 16.284 28.152 18.144 27.432 19.872C26.736 21.6 25.74 23.112 24.444 24.408C23.172 25.704 21.66 26.712 19.908 27.432C18.18 28.152 16.272 28.512 14.184 28.512ZM13.104 25.74V2.772L11.016 3.168V25.416L13.104 25.74ZM15.3 25.74L17.388 25.416V3.168L15.3 2.772V25.74ZM8.82 24.516V3.996C8.076 4.404 7.392 4.884 6.768 5.436V23.148C7.104 23.364 7.44 23.592 7.776 23.832C8.136 24.048 8.484 24.276 8.82 24.516ZM19.548 24.552L21.636 23.148V5.364L19.548 3.996V24.552ZM23.868 20.736C25.14 18.888 25.776 16.74 25.776 14.292C25.776 11.964 25.14 9.816 23.868 7.848V20.736ZM4.608 20.736V7.884C3.384 9.756 2.772 11.892 2.772 14.292C2.772 15.468 2.916 16.596 3.204 17.676C3.516 18.732 3.984 19.752 4.608 20.736Z" fill="black"/>
|
||||
<svg width="33" height="42" viewBox="0 0 33 42" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.5 0H32.5V41.5955H0.5V0ZM24.5 8.5H8.5V33H24.5V8.5Z" fill="black"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 221 B |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 17 KiB |
@@ -1,11 +1,12 @@
|
||||
<svg width="187" height="32" viewBox="0 0 187 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.184 28.512C12.144 28.512 10.26 28.152 8.532 27.432C6.804 26.736 5.304 25.752 4.032 24.48C2.76 23.184 1.764 21.672 1.044 19.944C0.348 18.216 0 16.332 0 14.292C0 12.228 0.36 10.332 1.08 8.604C1.8 6.852 2.796 5.34 4.068 4.068C5.364 2.772 6.876 1.776 8.604 1.08C10.332 0.36 12.192 0 14.184 0C16.224 0 18.108 0.36 19.836 1.08C21.588 1.8 23.112 2.808 24.408 4.104C25.704 5.4 26.712 6.924 27.432 8.676C28.152 10.404 28.512 12.276 28.512 14.292C28.512 16.284 28.152 18.144 27.432 19.872C26.736 21.6 25.74 23.112 24.444 24.408C23.172 25.704 21.66 26.712 19.908 27.432C18.18 28.152 16.272 28.512 14.184 28.512ZM13.104 25.74V2.772L11.016 3.168V25.416L13.104 25.74ZM15.3 25.74L17.388 25.416V3.168L15.3 2.772V25.74ZM8.82 24.516V3.996C8.076 4.404 7.392 4.884 6.768 5.436V23.148C7.104 23.364 7.44 23.592 7.776 23.832C8.136 24.048 8.484 24.276 8.82 24.516ZM19.548 24.552L21.636 23.148V5.364L19.548 3.996V24.552ZM23.868 20.736C25.14 18.888 25.776 16.74 25.776 14.292C25.776 11.964 25.14 9.816 23.868 7.848V20.736ZM4.608 20.736V7.884C3.384 9.756 2.772 11.892 2.772 14.292C2.772 15.468 2.916 16.596 3.204 17.676C3.516 18.732 3.984 19.752 4.608 20.736Z" fill="white"/>
|
||||
<path d="M45.628 25.432C42.556 25.432 40.252 24.688 38.716 23.2C37.204 21.688 36.448 19.552 36.448 16.792V9.62799C36.448 6.84399 37.204 4.70799 38.716 3.21999C40.252 1.73199 42.556 0.987986 45.628 0.987986C48.7 0.987986 50.992 1.73199 52.504 3.21999C54.04 4.70799 54.808 6.84399 54.808 9.62799V16.792C54.808 19.552 54.04 21.688 52.504 23.2C50.992 24.688 48.7 25.432 45.628 25.432ZM45.628 22.228C47.452 22.228 48.832 21.76 49.768 20.824C50.704 19.864 51.172 18.484 51.172 16.684V9.73599C51.172 7.91199 50.704 6.53199 49.768 5.59599C48.832 4.65999 47.452 4.19199 45.628 4.19199C43.828 4.19199 42.448 4.65999 41.488 5.59599C40.552 6.53199 40.084 7.91199 40.084 9.73599V16.684C40.084 18.484 40.552 19.864 41.488 20.824C42.448 21.76 43.828 22.228 45.628 22.228Z" fill="white"/>
|
||||
<path d="M67.0294 25.432C66.1654 25.432 65.2414 25.312 64.2574 25.072C63.2734 24.856 62.4694 24.544 61.8454 24.136L61.7734 21.184C62.4214 21.544 63.1414 21.832 63.9334 22.048C64.7254 22.24 65.4574 22.336 66.1294 22.336C67.4254 22.336 68.3614 21.964 68.9374 21.22C69.5374 20.452 69.8374 19.312 69.8374 17.8V13.912C69.8374 12.592 69.6094 11.632 69.1534 11.032C68.6974 10.408 68.0014 10.096 67.0654 10.096C66.2734 10.096 65.4694 10.336 64.6534 10.816C63.8374 11.272 62.8174 12.064 61.5934 13.192L61.4854 10.168C62.2534 9.44799 62.9854 8.84799 63.6814 8.36799C64.3774 7.88799 65.0854 7.52799 65.8054 7.28799C66.5254 7.04799 67.2934 6.92799 68.1094 6.92799C69.8614 6.92799 71.1814 7.46799 72.0694 8.54799C72.9814 9.62799 73.4374 11.272 73.4374 13.48V18.196C73.4374 20.572 72.8854 22.372 71.7814 23.596C70.7014 24.82 69.1174 25.432 67.0294 25.432ZM59.1094 31.66C58.8454 31.66 58.7134 31.528 58.7134 31.264V11.536C58.7134 10.936 58.6894 10.3 58.6414 9.62799C58.5934 8.95599 58.5334 8.35599 58.4614 7.82799C58.4374 7.51599 58.5694 7.35999 58.8574 7.35999H61.4134C61.6774 7.35999 61.8214 7.47999 61.8454 7.71999C61.8934 7.91199 61.9414 8.16399 61.9894 8.47599C62.0374 8.78799 62.0734 9.09999 62.0974 9.41199C62.1214 9.69999 62.1334 9.91599 62.1334 10.06L62.3134 11.968V31.264C62.3134 31.528 62.1814 31.66 61.9174 31.66H59.1094Z" fill="white"/>
|
||||
<path d="M84.0237 25.432C81.5757 25.432 79.7157 24.88 78.4437 23.776C77.1957 22.672 76.5717 21.028 76.5717 18.844V14.056C76.5717 11.728 77.1957 9.96399 78.4437 8.76399C79.6917 7.53999 81.5397 6.92799 83.9877 6.92799C86.4117 6.92799 88.2477 7.51599 89.4957 8.69199C90.7437 9.86799 91.3677 11.584 91.3677 13.84V16.648C91.3677 16.912 91.2477 17.044 91.0077 17.044H80.1717V18.196C80.1717 19.66 80.4837 20.728 81.1077 21.4C81.7317 22.072 82.7397 22.408 84.1317 22.408C85.1877 22.408 86.0037 22.24 86.5797 21.904C87.1557 21.568 87.4437 21.052 87.4437 20.356C87.4437 20.092 87.5877 19.96 87.8757 19.96H90.6477C90.8637 19.96 90.9957 20.08 91.0437 20.32C91.1397 21.928 90.5637 23.188 89.3157 24.1C88.0677 24.988 86.3037 25.432 84.0237 25.432ZM80.1717 14.308H87.8037V14.128C87.8037 12.688 87.4917 11.632 86.8677 10.96C86.2437 10.288 85.2957 9.95199 84.0237 9.95199C82.7277 9.95199 81.7557 10.3 81.1077 10.996C80.4837 11.692 80.1717 12.736 80.1717 14.128V14.308Z" fill="white"/>
|
||||
<path d="M106.647 25C106.383 25 106.251 24.868 106.251 24.604V13.3C106.251 12.196 106.023 11.392 105.567 10.888C105.135 10.36 104.451 10.096 103.515 10.096C102.627 10.096 101.751 10.36 100.887 10.888C100.023 11.392 98.9906 12.244 97.7906 13.444L97.7186 10.528C98.4866 9.71199 99.2306 9.03999 99.9506 8.51199C100.671 7.98399 101.403 7.58799 102.147 7.32399C102.915 7.05999 103.719 6.92799 104.559 6.92799C106.287 6.92799 107.595 7.43199 108.483 8.43999C109.395 9.42399 109.851 10.912 109.851 12.904V24.604C109.851 24.868 109.731 25 109.491 25H106.647ZM95.2346 25C94.9706 25 94.8386 24.868 94.8386 24.604V11.824C94.8386 11.152 94.8026 10.444 94.7306 9.69999C94.6826 8.93199 94.6346 8.31999 94.5866 7.86399C94.5386 7.52799 94.6706 7.35999 94.9826 7.35999H97.5386C97.7786 7.35999 97.9226 7.47999 97.9706 7.71999C98.0186 7.93599 98.0666 8.23599 98.1146 8.61999C98.1626 9.00399 98.2106 9.41199 98.2586 9.84399C98.3066 10.276 98.3306 10.648 98.3306 10.96L98.4386 12.112V24.604C98.4386 24.868 98.3066 25 98.0426 25H95.2346Z" fill="white"/>
|
||||
<path d="M122.87 25.432C119.894 25.432 117.626 24.7 116.066 23.236C114.506 21.748 113.726 19.6 113.726 16.792V9.66399C113.726 6.83199 114.506 4.68399 116.066 3.21999C117.65 1.73199 119.93 0.987986 122.906 0.987986C124.85 0.987986 126.542 1.31199 127.982 1.95999C129.422 2.60799 130.49 3.54399 131.186 4.76799C131.906 5.96799 132.146 7.40799 131.906 9.08799C131.882 9.23199 131.846 9.35199 131.798 9.44799C131.75 9.54399 131.654 9.59199 131.51 9.59199H128.63C128.366 9.59199 128.246 9.45999 128.27 9.19599C128.342 7.61199 127.922 6.37599 127.01 5.48799C126.098 4.59999 124.742 4.15599 122.942 4.15599C121.142 4.15599 119.762 4.62399 118.802 5.55999C117.842 6.49599 117.362 7.86399 117.362 9.66399V16.756C117.362 18.556 117.842 19.924 118.802 20.86C119.762 21.796 121.142 22.264 122.942 22.264C124.766 22.264 126.146 21.82 127.082 20.932C128.042 20.02 128.438 18.784 128.27 17.224C128.246 16.96 128.366 16.828 128.63 16.828H131.474C131.714 16.828 131.858 16.996 131.906 17.332C132.05 18.964 131.762 20.392 131.042 21.616C130.346 22.816 129.29 23.752 127.874 24.424C126.458 25.096 124.79 25.432 122.87 25.432Z" fill="white"/>
|
||||
<path d="M142.558 25.432C140.086 25.432 138.19 24.832 136.87 23.632C135.55 22.432 134.89 20.62 134.89 18.196V14.164C134.89 11.74 135.538 9.92799 136.834 8.72799C138.154 7.52799 140.062 6.92799 142.558 6.92799C145.03 6.92799 146.914 7.52799 148.21 8.72799C149.53 9.92799 150.19 11.74 150.19 14.164V18.196C150.19 20.62 149.542 22.432 148.246 23.632C146.95 24.832 145.054 25.432 142.558 25.432ZM142.558 22.3C143.974 22.3 144.994 21.94 145.618 21.22C146.266 20.5 146.59 19.384 146.59 17.872V14.488C146.59 13 146.266 11.896 145.618 11.176C144.994 10.432 143.974 10.06 142.558 10.06C141.118 10.06 140.074 10.432 139.426 11.176C138.802 11.896 138.49 13 138.49 14.488V17.872C138.49 19.384 138.802 20.5 139.426 21.22C140.074 21.94 141.118 22.3 142.558 22.3Z" fill="white"/>
|
||||
<path d="M158.473 25.432C156.793 25.432 155.509 24.892 154.621 23.812C153.733 22.708 153.289 21.064 153.289 18.88V14.2C153.289 11.848 153.853 10.048 154.981 8.79999C156.133 7.55199 157.729 6.92799 159.769 6.92799C160.657 6.92799 161.605 7.04799 162.613 7.28799C163.645 7.52799 164.449 7.83999 165.025 8.22399L165.169 11.212C164.449 10.828 163.657 10.54 162.793 10.348C161.953 10.132 161.209 10.024 160.561 10.024C159.337 10.024 158.413 10.408 157.789 11.176C157.189 11.92 156.889 13.048 156.889 14.56V18.772C156.889 19.948 157.117 20.836 157.573 21.436C158.029 22.012 158.713 22.3 159.625 22.3C160.201 22.3 160.765 22.192 161.317 21.976C161.869 21.736 162.469 21.376 163.117 20.896C163.765 20.392 164.497 19.72 165.313 18.88L165.349 21.832C164.581 22.6 163.837 23.26 163.117 23.812C162.397 24.34 161.665 24.736 160.921 25C160.177 25.288 159.361 25.432 158.473 25.432ZM165.601 25C165.313 25 165.157 24.868 165.133 24.604C165.085 24.244 165.025 23.8 164.953 23.272C164.881 22.72 164.857 22.252 164.881 21.868L164.629 20.104V0.807986C164.629 0.543987 164.761 0.411987 165.025 0.411987H167.833C168.097 0.411987 168.229 0.543987 168.229 0.807986V20.824C168.229 21.184 168.241 21.604 168.265 22.084C168.289 22.54 168.313 22.984 168.337 23.416C168.385 23.848 168.421 24.208 168.445 24.496C168.517 24.832 168.397 25 168.085 25H165.601Z" fill="white"/>
|
||||
<path d="M179.171 25.432C176.723 25.432 174.863 24.88 173.591 23.776C172.343 22.672 171.719 21.028 171.719 18.844V14.056C171.719 11.728 172.343 9.96399 173.591 8.76399C174.839 7.53999 176.687 6.92799 179.135 6.92799C181.559 6.92799 183.395 7.51599 184.643 8.69199C185.891 9.86799 186.515 11.584 186.515 13.84V16.648C186.515 16.912 186.395 17.044 186.155 17.044H175.319V18.196C175.319 19.66 175.631 20.728 176.255 21.4C176.879 22.072 177.887 22.408 179.279 22.408C180.335 22.408 181.151 22.24 181.727 21.904C182.303 21.568 182.591 21.052 182.591 20.356C182.591 20.092 182.735 19.96 183.023 19.96H185.795C186.011 19.96 186.143 20.08 186.191 20.32C186.287 21.928 185.711 23.188 184.463 24.1C183.215 24.988 181.451 25.432 179.171 25.432ZM175.319 14.308H182.951V14.128C182.951 12.688 182.639 11.632 182.015 10.96C181.391 10.288 180.443 9.95199 179.171 9.95199C177.875 9.95199 176.903 10.3 176.255 10.996C175.631 11.692 175.319 12.736 175.319 14.128V14.308Z" fill="white"/>
|
||||
<svg width="289" height="50" viewBox="0 0 289 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M264.5 0H288.5V8.5H272.5V16.5H288.5V25H272.5V33H288.5V41.5H264.5V0Z" fill="white"/>
|
||||
<path d="M248.5 0H224.5V41.5H248.5V33H232.5V8.5H248.5V0Z" fill="white"/>
|
||||
<path d="M256.5 8.5H248.5V33H256.5V8.5Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M184.5 0H216.5V41.5H184.5V0ZM208.5 8.5H192.5V33H208.5V8.5Z" fill="white"/>
|
||||
<path d="M144.5 8.5H136.5V41.5H144.5V8.5Z" fill="white"/>
|
||||
<path d="M136.5 0H112.5V41.5H120.5V8.5H136.5V0Z" fill="white"/>
|
||||
<path d="M80.5 0H104.5V8.5H88.5V16.5H104.5V25H88.5V33H104.5V41.5H80.5V0Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.5 0H72.5V41.5H48.5V49.5H40.5V0ZM64.5 8.5H48.5V33H64.5V8.5Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.5 0H32.5V41.5955H0.5V0ZM24.5 8.5H8.5V33H24.5V8.5Z" fill="white"/>
|
||||
<path d="M152.5 0H176.5V8.5H160.5V33H176.5V41.5H152.5V0Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 981 B |
@@ -1,11 +1,12 @@
|
||||
<svg width="187" height="32" viewBox="0 0 187 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.184 28.512C12.144 28.512 10.26 28.152 8.532 27.432C6.804 26.736 5.304 25.752 4.032 24.48C2.76 23.184 1.764 21.672 1.044 19.944C0.348 18.216 0 16.332 0 14.292C0 12.228 0.36 10.332 1.08 8.604C1.8 6.852 2.796 5.34 4.068 4.068C5.364 2.772 6.876 1.776 8.604 1.08C10.332 0.36 12.192 0 14.184 0C16.224 0 18.108 0.36 19.836 1.08C21.588 1.8 23.112 2.808 24.408 4.104C25.704 5.4 26.712 6.924 27.432 8.676C28.152 10.404 28.512 12.276 28.512 14.292C28.512 16.284 28.152 18.144 27.432 19.872C26.736 21.6 25.74 23.112 24.444 24.408C23.172 25.704 21.66 26.712 19.908 27.432C18.18 28.152 16.272 28.512 14.184 28.512ZM13.104 25.74V2.772L11.016 3.168V25.416L13.104 25.74ZM15.3 25.74L17.388 25.416V3.168L15.3 2.772V25.74ZM8.82 24.516V3.996C8.076 4.404 7.392 4.884 6.768 5.436V23.148C7.104 23.364 7.44 23.592 7.776 23.832C8.136 24.048 8.484 24.276 8.82 24.516ZM19.548 24.552L21.636 23.148V5.364L19.548 3.996V24.552ZM23.868 20.736C25.14 18.888 25.776 16.74 25.776 14.292C25.776 11.964 25.14 9.816 23.868 7.848V20.736ZM4.608 20.736V7.884C3.384 9.756 2.772 11.892 2.772 14.292C2.772 15.468 2.916 16.596 3.204 17.676C3.516 18.732 3.984 19.752 4.608 20.736Z" fill="black"/>
|
||||
<path d="M45.628 25.432C42.556 25.432 40.252 24.688 38.716 23.2C37.204 21.688 36.448 19.552 36.448 16.792V9.62799C36.448 6.84399 37.204 4.70799 38.716 3.21999C40.252 1.73199 42.556 0.987986 45.628 0.987986C48.7 0.987986 50.992 1.73199 52.504 3.21999C54.04 4.70799 54.808 6.84399 54.808 9.62799V16.792C54.808 19.552 54.04 21.688 52.504 23.2C50.992 24.688 48.7 25.432 45.628 25.432ZM45.628 22.228C47.452 22.228 48.832 21.76 49.768 20.824C50.704 19.864 51.172 18.484 51.172 16.684V9.73599C51.172 7.91199 50.704 6.53199 49.768 5.59599C48.832 4.65999 47.452 4.19199 45.628 4.19199C43.828 4.19199 42.448 4.65999 41.488 5.59599C40.552 6.53199 40.084 7.91199 40.084 9.73599V16.684C40.084 18.484 40.552 19.864 41.488 20.824C42.448 21.76 43.828 22.228 45.628 22.228Z" fill="black"/>
|
||||
<path d="M67.0294 25.432C66.1654 25.432 65.2414 25.312 64.2574 25.072C63.2734 24.856 62.4694 24.544 61.8454 24.136L61.7734 21.184C62.4214 21.544 63.1414 21.832 63.9334 22.048C64.7254 22.24 65.4574 22.336 66.1294 22.336C67.4254 22.336 68.3614 21.964 68.9374 21.22C69.5374 20.452 69.8374 19.312 69.8374 17.8V13.912C69.8374 12.592 69.6094 11.632 69.1534 11.032C68.6974 10.408 68.0014 10.096 67.0654 10.096C66.2734 10.096 65.4694 10.336 64.6534 10.816C63.8374 11.272 62.8174 12.064 61.5934 13.192L61.4854 10.168C62.2534 9.44799 62.9854 8.84799 63.6814 8.36799C64.3774 7.88799 65.0854 7.52799 65.8054 7.28799C66.5254 7.04799 67.2934 6.92799 68.1094 6.92799C69.8614 6.92799 71.1814 7.46799 72.0694 8.54799C72.9814 9.62799 73.4374 11.272 73.4374 13.48V18.196C73.4374 20.572 72.8854 22.372 71.7814 23.596C70.7014 24.82 69.1174 25.432 67.0294 25.432ZM59.1094 31.66C58.8454 31.66 58.7134 31.528 58.7134 31.264V11.536C58.7134 10.936 58.6894 10.3 58.6414 9.62799C58.5934 8.95599 58.5334 8.35599 58.4614 7.82799C58.4374 7.51599 58.5694 7.35999 58.8574 7.35999H61.4134C61.6774 7.35999 61.8214 7.47999 61.8454 7.71999C61.8934 7.91199 61.9414 8.16399 61.9894 8.47599C62.0374 8.78799 62.0734 9.09999 62.0974 9.41199C62.1214 9.69999 62.1334 9.91599 62.1334 10.06L62.3134 11.968V31.264C62.3134 31.528 62.1814 31.66 61.9174 31.66H59.1094Z" fill="black"/>
|
||||
<path d="M84.0237 25.432C81.5757 25.432 79.7157 24.88 78.4437 23.776C77.1957 22.672 76.5717 21.028 76.5717 18.844V14.056C76.5717 11.728 77.1957 9.96399 78.4437 8.76399C79.6917 7.53999 81.5397 6.92799 83.9877 6.92799C86.4117 6.92799 88.2477 7.51599 89.4957 8.69199C90.7437 9.86799 91.3677 11.584 91.3677 13.84V16.648C91.3677 16.912 91.2477 17.044 91.0077 17.044H80.1717V18.196C80.1717 19.66 80.4837 20.728 81.1077 21.4C81.7317 22.072 82.7397 22.408 84.1317 22.408C85.1877 22.408 86.0037 22.24 86.5797 21.904C87.1557 21.568 87.4437 21.052 87.4437 20.356C87.4437 20.092 87.5877 19.96 87.8757 19.96H90.6477C90.8637 19.96 90.9957 20.08 91.0437 20.32C91.1397 21.928 90.5637 23.188 89.3157 24.1C88.0677 24.988 86.3037 25.432 84.0237 25.432ZM80.1717 14.308H87.8037V14.128C87.8037 12.688 87.4917 11.632 86.8677 10.96C86.2437 10.288 85.2957 9.95199 84.0237 9.95199C82.7277 9.95199 81.7557 10.3 81.1077 10.996C80.4837 11.692 80.1717 12.736 80.1717 14.128V14.308Z" fill="black"/>
|
||||
<path d="M106.647 25C106.383 25 106.251 24.868 106.251 24.604V13.3C106.251 12.196 106.023 11.392 105.567 10.888C105.135 10.36 104.451 10.096 103.515 10.096C102.627 10.096 101.751 10.36 100.887 10.888C100.023 11.392 98.9906 12.244 97.7906 13.444L97.7186 10.528C98.4866 9.71199 99.2306 9.03999 99.9506 8.51199C100.671 7.98399 101.403 7.58799 102.147 7.32399C102.915 7.05999 103.719 6.92799 104.559 6.92799C106.287 6.92799 107.595 7.43199 108.483 8.43999C109.395 9.42399 109.851 10.912 109.851 12.904V24.604C109.851 24.868 109.731 25 109.491 25H106.647ZM95.2346 25C94.9706 25 94.8386 24.868 94.8386 24.604V11.824C94.8386 11.152 94.8026 10.444 94.7306 9.69999C94.6826 8.93199 94.6346 8.31999 94.5866 7.86399C94.5386 7.52799 94.6706 7.35999 94.9826 7.35999H97.5386C97.7786 7.35999 97.9226 7.47999 97.9706 7.71999C98.0186 7.93599 98.0666 8.23599 98.1146 8.61999C98.1626 9.00399 98.2106 9.41199 98.2586 9.84399C98.3066 10.276 98.3306 10.648 98.3306 10.96L98.4386 12.112V24.604C98.4386 24.868 98.3066 25 98.0426 25H95.2346Z" fill="black"/>
|
||||
<path d="M122.87 25.432C119.894 25.432 117.626 24.7 116.066 23.236C114.506 21.748 113.726 19.6 113.726 16.792V9.66399C113.726 6.83199 114.506 4.68399 116.066 3.21999C117.65 1.73199 119.93 0.987986 122.906 0.987986C124.85 0.987986 126.542 1.31199 127.982 1.95999C129.422 2.60799 130.49 3.54399 131.186 4.76799C131.906 5.96799 132.146 7.40799 131.906 9.08799C131.882 9.23199 131.846 9.35199 131.798 9.44799C131.75 9.54399 131.654 9.59199 131.51 9.59199H128.63C128.366 9.59199 128.246 9.45999 128.27 9.19599C128.342 7.61199 127.922 6.37599 127.01 5.48799C126.098 4.59999 124.742 4.15599 122.942 4.15599C121.142 4.15599 119.762 4.62399 118.802 5.55999C117.842 6.49599 117.362 7.86399 117.362 9.66399V16.756C117.362 18.556 117.842 19.924 118.802 20.86C119.762 21.796 121.142 22.264 122.942 22.264C124.766 22.264 126.146 21.82 127.082 20.932C128.042 20.02 128.438 18.784 128.27 17.224C128.246 16.96 128.366 16.828 128.63 16.828H131.474C131.714 16.828 131.858 16.996 131.906 17.332C132.05 18.964 131.762 20.392 131.042 21.616C130.346 22.816 129.29 23.752 127.874 24.424C126.458 25.096 124.79 25.432 122.87 25.432Z" fill="black"/>
|
||||
<path d="M142.558 25.432C140.086 25.432 138.19 24.832 136.87 23.632C135.55 22.432 134.89 20.62 134.89 18.196V14.164C134.89 11.74 135.538 9.92799 136.834 8.72799C138.154 7.52799 140.062 6.92799 142.558 6.92799C145.03 6.92799 146.914 7.52799 148.21 8.72799C149.53 9.92799 150.19 11.74 150.19 14.164V18.196C150.19 20.62 149.542 22.432 148.246 23.632C146.95 24.832 145.054 25.432 142.558 25.432ZM142.558 22.3C143.974 22.3 144.994 21.94 145.618 21.22C146.266 20.5 146.59 19.384 146.59 17.872V14.488C146.59 13 146.266 11.896 145.618 11.176C144.994 10.432 143.974 10.06 142.558 10.06C141.118 10.06 140.074 10.432 139.426 11.176C138.802 11.896 138.49 13 138.49 14.488V17.872C138.49 19.384 138.802 20.5 139.426 21.22C140.074 21.94 141.118 22.3 142.558 22.3Z" fill="black"/>
|
||||
<path d="M158.473 25.432C156.793 25.432 155.509 24.892 154.621 23.812C153.733 22.708 153.289 21.064 153.289 18.88V14.2C153.289 11.848 153.853 10.048 154.981 8.79999C156.133 7.55199 157.729 6.92799 159.769 6.92799C160.657 6.92799 161.605 7.04799 162.613 7.28799C163.645 7.52799 164.449 7.83999 165.025 8.22399L165.169 11.212C164.449 10.828 163.657 10.54 162.793 10.348C161.953 10.132 161.209 10.024 160.561 10.024C159.337 10.024 158.413 10.408 157.789 11.176C157.189 11.92 156.889 13.048 156.889 14.56V18.772C156.889 19.948 157.117 20.836 157.573 21.436C158.029 22.012 158.713 22.3 159.625 22.3C160.201 22.3 160.765 22.192 161.317 21.976C161.869 21.736 162.469 21.376 163.117 20.896C163.765 20.392 164.497 19.72 165.313 18.88L165.349 21.832C164.581 22.6 163.837 23.26 163.117 23.812C162.397 24.34 161.665 24.736 160.921 25C160.177 25.288 159.361 25.432 158.473 25.432ZM165.601 25C165.313 25 165.157 24.868 165.133 24.604C165.085 24.244 165.025 23.8 164.953 23.272C164.881 22.72 164.857 22.252 164.881 21.868L164.629 20.104V0.807986C164.629 0.543987 164.761 0.411987 165.025 0.411987H167.833C168.097 0.411987 168.229 0.543987 168.229 0.807986V20.824C168.229 21.184 168.241 21.604 168.265 22.084C168.289 22.54 168.313 22.984 168.337 23.416C168.385 23.848 168.421 24.208 168.445 24.496C168.517 24.832 168.397 25 168.085 25H165.601Z" fill="black"/>
|
||||
<path d="M179.171 25.432C176.723 25.432 174.863 24.88 173.591 23.776C172.343 22.672 171.719 21.028 171.719 18.844V14.056C171.719 11.728 172.343 9.96399 173.591 8.76399C174.839 7.53999 176.687 6.92799 179.135 6.92799C181.559 6.92799 183.395 7.51599 184.643 8.69199C185.891 9.86799 186.515 11.584 186.515 13.84V16.648C186.515 16.912 186.395 17.044 186.155 17.044H175.319V18.196C175.319 19.66 175.631 20.728 176.255 21.4C176.879 22.072 177.887 22.408 179.279 22.408C180.335 22.408 181.151 22.24 181.727 21.904C182.303 21.568 182.591 21.052 182.591 20.356C182.591 20.092 182.735 19.96 183.023 19.96H185.795C186.011 19.96 186.143 20.08 186.191 20.32C186.287 21.928 185.711 23.188 184.463 24.1C183.215 24.988 181.451 25.432 179.171 25.432ZM175.319 14.308H182.951V14.128C182.951 12.688 182.639 11.632 182.015 10.96C181.391 10.288 180.443 9.95199 179.171 9.95199C177.875 9.95199 176.903 10.3 176.255 10.996C175.631 11.692 175.319 12.736 175.319 14.128V14.308Z" fill="black"/>
|
||||
<svg width="289" height="50" viewBox="0 0 289 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M264.5 0H288.5V8.5H272.5V16.5H288.5V25H272.5V33H288.5V41.5H264.5V0Z" fill="black"/>
|
||||
<path d="M248.5 0H224.5V41.5H248.5V33H232.5V8.5H248.5V0Z" fill="black"/>
|
||||
<path d="M256.5 8.5H248.5V33H256.5V8.5Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M184.5 0H216.5V41.5H184.5V0ZM208.5 8.5H192.5V33H208.5V8.5Z" fill="black"/>
|
||||
<path d="M144.5 8.5H136.5V41.5H144.5V8.5Z" fill="black"/>
|
||||
<path d="M136.5 0H112.5V41.5H120.5V8.5H136.5V0Z" fill="black"/>
|
||||
<path d="M80.5 0H104.5V8.5H88.5V16.5H104.5V25H88.5V33H104.5V41.5H80.5V0Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.5 0H72.5V41.5H48.5V49.5H40.5V0ZM64.5 8.5H48.5V33H64.5V8.5Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.5 0H32.5V41.5955H0.5V0ZM24.5 8.5H8.5V33H24.5V8.5Z" fill="black"/>
|
||||
<path d="M152.5 0H176.5V8.5H160.5V33H176.5V41.5H152.5V0Z" fill="black"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 981 B |
@@ -167,6 +167,8 @@ export function getDiagnostics(
|
||||
): string[] {
|
||||
const result: string[] = []
|
||||
|
||||
if (diagnosticsByFile === undefined) return result
|
||||
|
||||
for (const diags of Object.values(diagnosticsByFile)) {
|
||||
for (const d of diags) {
|
||||
// Only keep diagnostics explicitly marked as Error (severity === 1)
|
||||
@@ -1395,6 +1397,7 @@ export default function Share(props: {
|
||||
part().toolInvocation.toolCallId
|
||||
],
|
||||
)
|
||||
const hasError = metadata()?.error
|
||||
const args = part().toolInvocation.args
|
||||
const filePath = args.filePath
|
||||
const diagnostics = createMemo(() =>
|
||||
@@ -1429,13 +1432,27 @@ export default function Share(props: {
|
||||
<span data-element-label>Edit</span>
|
||||
<b>{filePath}</b>
|
||||
</span>
|
||||
<div data-part-tool-edit>
|
||||
<DiffView
|
||||
class={styles["diff-code-block"]}
|
||||
diff={metadata()?.diff}
|
||||
lang={getFileType(filePath)}
|
||||
/>
|
||||
</div>
|
||||
<Switch>
|
||||
<Match when={hasError}>
|
||||
<div data-part-tool-result>
|
||||
<TextPart
|
||||
expand
|
||||
data-size="sm"
|
||||
data-color="dimmed"
|
||||
text={metadata()?.message}
|
||||
/>
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={metadata()?.diff}>
|
||||
<div data-part-tool-edit>
|
||||
<DiffView
|
||||
class={styles["diff-code-block"]}
|
||||
diff={metadata()?.diff}
|
||||
lang={getFileType(filePath)}
|
||||
/>
|
||||
</div>
|
||||
</Match>
|
||||
</Switch>
|
||||
<Show when={diagnostics().length > 0}>
|
||||
<TextPart
|
||||
data-size="sm"
|
||||
|
||||
@@ -12,8 +12,11 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
list-style-position: inside;
|
||||
padding-left: 0.75rem;
|
||||
}
|
||||
ul {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
|
||||