mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
Merge dev into agent-loop, keeping clean loop implementation
This commit is contained in:
commit
cd61185cba
67 changed files with 1283 additions and 609 deletions
2
.github/workflows/snapshot.yml
vendored
2
.github/workflows/snapshot.yml
vendored
|
|
@ -4,7 +4,7 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- dev
|
||||
- opentui
|
||||
- windows
|
||||
- v0
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
|
|
|||
1
STATS.md
1
STATS.md
|
|
@ -138,3 +138,4 @@
|
|||
| 2025-11-10 | 722,288 (+8,826) | 668,225 (+7,766) | 1,390,513 (+16,592) |
|
||||
| 2025-11-11 | 729,769 (+7,481) | 677,501 (+9,276) | 1,407,270 (+16,757) |
|
||||
| 2025-11-12 | 740,180 (+10,411) | 686,454 (+8,953) | 1,426,634 (+19,364) |
|
||||
| 2025-11-13 | 749,905 (+9,725) | 696,157 (+9,703) | 1,446,062 (+19,428) |
|
||||
|
|
|
|||
22
bun.lock
22
bun.lock
|
|
@ -40,7 +40,7 @@
|
|||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
|
|
@ -155,7 +155,7 @@
|
|||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "22.0.0",
|
||||
|
|
@ -171,7 +171,7 @@
|
|||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
|
|
@ -249,7 +249,7 @@
|
|||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
|
|
@ -269,7 +269,7 @@
|
|||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "0.81.0",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
|
|
@ -280,7 +280,7 @@
|
|||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
|
|
@ -293,7 +293,7 @@
|
|||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
|
|
@ -323,7 +323,7 @@
|
|||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
|
||||
"build": "./script/generate-sitemap.ts && vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
||||
"start": "vinxi start",
|
||||
"version": "1.0.62"
|
||||
"version": "1.0.65"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
|
|
|||
|
|
@ -8,14 +8,12 @@
|
|||
<title>OpenCode</title>
|
||||
</head>
|
||||
<body class="antialiased overscroll-none select-none text-12-regular">
|
||||
<!-- <script> -->
|
||||
<!-- ;(function () { -->
|
||||
<!-- const savedTheme = localStorage.getItem("theme") || "opencode" -->
|
||||
<!-- const savedDarkMode = localStorage.getItem("darkMode") !== "false" -->
|
||||
<!-- document.documentElement.setAttribute("data-theme", savedTheme) -->
|
||||
<!-- document.documentElement.setAttribute("data-dark", savedDarkMode.toString()) -->
|
||||
<!-- })() -->
|
||||
<!-- </script> -->
|
||||
<script>
|
||||
;(function () {
|
||||
const savedTheme = localStorage.getItem("theme") || "oc-1"
|
||||
document.documentElement.setAttribute("data-theme", savedTheme)
|
||||
})()
|
||||
</script>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script src="/src/index.tsx" type="module"></script>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||
<Show when={store.popoverIsOpen}>
|
||||
<div
|
||||
class="absolute inset-x-0 -top-3 -translate-y-full origin-bottom-left max-h-[252px] min-h-10
|
||||
overflow-auto no-scrollbar flex flex-col p-2 pb-0 rounded-2xl
|
||||
overflow-auto no-scrollbar flex flex-col p-2 pb-0 rounded-md
|
||||
border border-border-base bg-surface-raised-stronger-non-alpha shadow-md"
|
||||
>
|
||||
<Show when={flat().length > 0} fallback={<div class="text-text-weak px-2">No matching files</div>}>
|
||||
|
|
@ -382,7 +382,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||
onSubmit={handleSubmit}
|
||||
classList={{
|
||||
"bg-surface-raised-stronger-non-alpha border border-border-strong-base": true,
|
||||
"rounded-2xl overflow-clip focus-within:border-transparent focus-within:shadow-xs-border-select": true,
|
||||
"rounded-md overflow-clip focus-within:border-transparent focus-within:shadow-xs-border-select": true,
|
||||
[props.class ?? ""]: !!props.class,
|
||||
}}
|
||||
>
|
||||
|
|
@ -396,17 +396,17 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||
onInput={handleInput}
|
||||
onKeyDown={handleKeyDown}
|
||||
classList={{
|
||||
"w-full p-3 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true,
|
||||
"w-full px-5 py-3 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true,
|
||||
"[&>[data-type=file]]:text-icon-info-active": true,
|
||||
}}
|
||||
/>
|
||||
<Show when={!session.prompt.dirty()}>
|
||||
<div class="absolute top-0 left-0 p-3 text-14-regular text-text-weak pointer-events-none">
|
||||
<div class="absolute top-0 left-0 px-5 py-3 text-14-regular text-text-weak pointer-events-none">
|
||||
Plan and build anything
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<div class="p-3 flex items-center justify-between">
|
||||
<div class="relative p-3 flex items-center justify-between">
|
||||
<div class="flex items-center justify-start gap-1">
|
||||
<Select
|
||||
options={local.agent.list().map((agent) => agent.name)}
|
||||
|
|
@ -489,7 +489,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||
disabled={!session.prompt.dirty() && !session.working()}
|
||||
icon={session.working() ? "stop" : "arrow-up"}
|
||||
variant="primary"
|
||||
class="rounded-full"
|
||||
class="h-10 w-8 absolute right-2 bottom-2"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
|
|
|||
104
packages/desktop/src/components/session-review.tsx
Normal file
104
packages/desktop/src/components/session-review.tsx
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import { useLocal } from "@/context/local"
|
||||
import { useSession } from "@/context/session"
|
||||
import { FileIcon } from "@/ui"
|
||||
import { getDirectory, getFilename } from "@/utils"
|
||||
import { Accordion, Button, Diff, DiffChanges, Icon, IconButton, Tooltip } from "@opencode-ai/ui"
|
||||
import { For, Match, Show, Switch } from "solid-js"
|
||||
import { StickyAccordionHeader } from "./sticky-accordion-header"
|
||||
import { createStore } from "solid-js/store"
|
||||
|
||||
export const SessionReview = (props: { split?: boolean; class?: string; hideExpand?: boolean }) => {
|
||||
const local = useLocal()
|
||||
const session = useSession()
|
||||
const [store, setStore] = createStore({
|
||||
open: session.diffs().map((d) => d.file),
|
||||
})
|
||||
|
||||
const handleChange = (open: string[]) => {
|
||||
setStore("open", open)
|
||||
}
|
||||
|
||||
const handleExpandOrCollapseAll = () => {
|
||||
if (store.open.length > 0) {
|
||||
setStore("open", [])
|
||||
} else {
|
||||
setStore(
|
||||
"open",
|
||||
session.diffs().map((d) => d.file),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
classList={{
|
||||
"flex flex-col gap-3 h-full overflow-y-auto no-scrollbar": true,
|
||||
[props.class ?? ""]: !!props.class,
|
||||
}}
|
||||
>
|
||||
<div class="sticky top-0 z-20 bg-background-stronger h-8 shrink-0 flex justify-between items-center self-stretch">
|
||||
<div class="text-14-medium text-text-strong">Session changes</div>
|
||||
<div class="flex items-center gap-x-4 pr-px">
|
||||
<Button size="normal" icon="chevron-grabber-vertical" onClick={handleExpandOrCollapseAll}>
|
||||
<Switch>
|
||||
<Match when={store.open.length > 0}>Collapse all</Match>
|
||||
<Match when={true}>Expand all</Match>
|
||||
</Switch>
|
||||
</Button>
|
||||
<Show when={!props.hideExpand}>
|
||||
<Tooltip value="Open in tab">
|
||||
<IconButton
|
||||
icon="expand"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
local.layout.review.tab()
|
||||
session.layout.setActiveTab("review")
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<Accordion multiple value={store.open} onChange={handleChange}>
|
||||
<For each={session.diffs()}>
|
||||
{(diff) => (
|
||||
<Accordion.Item value={diff.file}>
|
||||
<StickyAccordionHeader class="top-11 data-expanded:before:-top-11">
|
||||
<Accordion.Trigger class="bg-background-stronger">
|
||||
<div class="flex items-center justify-between w-full gap-5">
|
||||
<div class="grow flex items-center gap-5 min-w-0">
|
||||
<FileIcon node={{ path: diff.file, type: "file" }} class="shrink-0 size-4" />
|
||||
<div class="flex grow min-w-0">
|
||||
<Show when={diff.file.includes("/")}>
|
||||
<span class="text-text-base truncate-start">{getDirectory(diff.file)}‎</span>
|
||||
</Show>
|
||||
<span class="text-text-strong shrink-0">{getFilename(diff.file)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shrink-0 flex gap-4 items-center justify-end">
|
||||
<DiffChanges changes={diff} />
|
||||
<Icon name="chevron-grabber-vertical" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</Accordion.Trigger>
|
||||
</StickyAccordionHeader>
|
||||
<Accordion.Content>
|
||||
<Diff
|
||||
diffStyle={props.split ? "split" : "unified"}
|
||||
before={{
|
||||
name: diff.file!,
|
||||
contents: diff.before!,
|
||||
}}
|
||||
after={{
|
||||
name: diff.file!,
|
||||
contents: diff.after!,
|
||||
}}
|
||||
/>
|
||||
</Accordion.Content>
|
||||
</Accordion.Item>
|
||||
)}
|
||||
</For>
|
||||
</Accordion>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
17
packages/desktop/src/components/sticky-accordion-header.tsx
Normal file
17
packages/desktop/src/components/sticky-accordion-header.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { Accordion } from "@opencode-ai/ui"
|
||||
import { ParentProps } from "solid-js"
|
||||
|
||||
export function StickyAccordionHeader(props: ParentProps<{ class?: string }>) {
|
||||
return (
|
||||
<Accordion.Header
|
||||
classList={{
|
||||
"sticky top-0 data-expanded:z-10": true,
|
||||
"data-expanded:before:content-[''] data-expanded:before:z-[-10]": true,
|
||||
"data-expanded:before:absolute data-expanded:before:inset-0 data-expanded:before:bg-background-stronger": true,
|
||||
[props.class ?? ""]: !!props.class,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</Accordion.Header>
|
||||
)
|
||||
}
|
||||
|
|
@ -465,11 +465,11 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||
width: 240,
|
||||
},
|
||||
review: {
|
||||
state: "closed" as "open" | "closed" | "tab",
|
||||
state: "pane" as "pane" | "tab",
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: "default-layout",
|
||||
name: "_default-layout",
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -492,11 +492,8 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||
},
|
||||
review: {
|
||||
state: createMemo(() => store.review?.state ?? "closed"),
|
||||
open() {
|
||||
setStore("review", "state", "open")
|
||||
},
|
||||
close() {
|
||||
setStore("review", "state", "closed")
|
||||
pane() {
|
||||
setStore("review", "state", "pane")
|
||||
},
|
||||
tab() {
|
||||
setStore("review", "state", "tab")
|
||||
|
|
|
|||
|
|
@ -10,11 +10,6 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
|||
const sdk = createOpencodeClient({
|
||||
baseUrl: props.url,
|
||||
signal: abort.signal,
|
||||
fetch: (req) => {
|
||||
// @ts-ignore
|
||||
req.timeout = false
|
||||
return fetch(req)
|
||||
},
|
||||
})
|
||||
|
||||
const emitter = createGlobalEmitter<{
|
||||
|
|
|
|||
|
|
@ -44,12 +44,14 @@ import {
|
|||
useDragDropContext,
|
||||
} from "@thisbeyond/solid-dnd"
|
||||
import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd"
|
||||
import type { JSX, ParentProps } from "solid-js"
|
||||
import type { JSX } from "solid-js"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk"
|
||||
import { Markdown } from "@opencode-ai/ui"
|
||||
import { Spinner } from "@/components/spinner"
|
||||
import { useSession } from "@/context/session"
|
||||
import { StickyAccordionHeader } from "@/components/sticky-accordion-header"
|
||||
import { SessionReview } from "@/components/session-review"
|
||||
|
||||
export default function Page() {
|
||||
const local = useLocal()
|
||||
|
|
@ -83,6 +85,15 @@ export default function Page() {
|
|||
setStore("fileSelectOpen", true)
|
||||
return
|
||||
}
|
||||
if (event.ctrlKey && event.key.toLowerCase() === "t") {
|
||||
event.preventDefault()
|
||||
const currentTheme = localStorage.getItem("theme") ?? "oc-1"
|
||||
const themes = ["oc-1", "oc-2-paper"]
|
||||
const nextTheme = themes[(themes.indexOf(currentTheme) + 1) % themes.length]
|
||||
localStorage.setItem("theme", nextTheme)
|
||||
document.documentElement.setAttribute("data-theme", nextTheme)
|
||||
return
|
||||
}
|
||||
|
||||
const focused = document.activeElement === inputRef
|
||||
if (focused) {
|
||||
|
|
@ -216,18 +227,15 @@ export default function Page() {
|
|||
// @ts-ignore
|
||||
<div use:sortable classList={{ "h-full": true, "opacity-0": sortable.isActiveDraggable }}>
|
||||
<div class="relative h-full">
|
||||
<Tabs.Trigger value={props.tab} class="group/tab pl-3 pr-1" onClick={() => props.onTabClick(props.tab)}>
|
||||
<Tabs.Trigger
|
||||
value={props.tab}
|
||||
closeButton={<IconButton icon="close" variant="ghost" onClick={() => props.onTabClose(props.tab)} />}
|
||||
hideCloseButton
|
||||
onClick={() => props.onTabClick(props.tab)}
|
||||
>
|
||||
<Switch>
|
||||
<Match when={file()}>{(f) => <FileVisual file={f()} />}</Match>
|
||||
</Switch>
|
||||
<IconButton
|
||||
icon="close"
|
||||
class="mt-0.5 opacity-0 group-data-[selected]/tab:opacity-100
|
||||
hover:bg-transparent
|
||||
hover:opacity-100 group-hover/tab:opacity-100"
|
||||
variant="ghost"
|
||||
onClick={() => props.onTabClose(props.tab)}
|
||||
/>
|
||||
</Tabs.Trigger>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -277,38 +285,40 @@ export default function Page() {
|
|||
<Tabs value={session.layout.tabs.active ?? "chat"} onChange={session.layout.openTab}>
|
||||
<div class="sticky top-0 shrink-0 flex">
|
||||
<Tabs.List>
|
||||
<Tabs.Trigger value="chat" class="flex gap-x-4 items-center">
|
||||
<div>Chat</div>
|
||||
<Tooltip
|
||||
value={`${new Intl.NumberFormat("en-US", {
|
||||
notation: "compact",
|
||||
compactDisplay: "short",
|
||||
}).format(session.usage.tokens() ?? 0)} Tokens`}
|
||||
class="flex items-center gap-1.5"
|
||||
>
|
||||
<ProgressCircle percentage={session.usage.context() ?? 0} />
|
||||
<div class="text-14-regular text-text-weak text-left w-7">{session.usage.context() ?? 0}%</div>
|
||||
</Tooltip>
|
||||
<Tabs.Trigger value="chat">
|
||||
<div class="flex gap-x-[17px] items-center">
|
||||
<div>Chat</div>
|
||||
<Tooltip
|
||||
value={`${new Intl.NumberFormat("en-US", {
|
||||
notation: "compact",
|
||||
compactDisplay: "short",
|
||||
}).format(session.usage.tokens() ?? 0)} Tokens`}
|
||||
class="flex items-center gap-1.5"
|
||||
>
|
||||
<ProgressCircle percentage={session.usage.context() ?? 0} />
|
||||
<div class="text-14-regular text-text-weak text-left w-7">{session.usage.context() ?? 0}%</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Tabs.Trigger>
|
||||
<Show when={local.layout.review.state() === "tab" && session.diffs().length}>
|
||||
<Tabs.Trigger value="review" class="flex gap-3 items-center group/tab pr-1">
|
||||
<Show when={session.diffs()}>
|
||||
<DiffChanges changes={session.diffs()} variant="bars" />
|
||||
</Show>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div>Review</div>
|
||||
<Show when={session.info()?.summary?.files}>
|
||||
<div class="text-12-medium text-text-strong h-4 px-2 flex flex-col items-center justify-center rounded-full bg-surface-base">
|
||||
{session.info()?.summary?.files ?? 0}
|
||||
</div>
|
||||
<Tabs.Trigger
|
||||
value="review"
|
||||
closeButton={
|
||||
<IconButton icon="collapse" size="normal" variant="ghost" onClick={local.layout.review.pane} />
|
||||
}
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<Show when={session.diffs()}>
|
||||
<DiffChanges changes={session.diffs()} variant="bars" />
|
||||
</Show>
|
||||
<IconButton
|
||||
icon="close"
|
||||
class="mt-0.5 -ml-1 opacity-0 group-data-[selected]/tab:opacity-100
|
||||
hover:bg-transparent hover:opacity-100 group-hover/tab:opacity-100"
|
||||
variant="ghost"
|
||||
onClick={local.layout.review.close}
|
||||
/>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div>Review</div>
|
||||
<Show when={session.info()?.summary?.files}>
|
||||
<div class="text-12-medium text-text-strong h-4 px-2 flex flex-col items-center justify-center rounded-full bg-surface-base">
|
||||
{session.info()?.summary?.files ?? 0}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Trigger>
|
||||
</Show>
|
||||
|
|
@ -333,24 +343,17 @@ export default function Page() {
|
|||
<div
|
||||
classList={{
|
||||
"w-full flex-1 min-h-0": true,
|
||||
grid: local.layout.review.state() !== "open",
|
||||
flex: local.layout.review.state() === "open",
|
||||
grid: local.layout.review.state() === "tab",
|
||||
flex: local.layout.review.state() === "pane",
|
||||
}}
|
||||
>
|
||||
<div class="relative shrink-0 px-6 py-2 flex flex-col gap-6 flex-1 min-h-0 w-full max-w-xl mx-auto">
|
||||
<div class="relative shrink-0 px-6 py-3 flex flex-col gap-6 flex-1 min-h-0 w-full max-w-xl mx-auto">
|
||||
<Switch>
|
||||
<Match when={session.id}>
|
||||
<div class="h-8 flex shrink-0 self-stretch items-center justify-end">
|
||||
<Show when={local.layout.review.state() === "closed" && session.diffs().length}>
|
||||
<Button icon="layout-right" onClick={local.layout.review.open}>
|
||||
Review
|
||||
</Button>
|
||||
</Show>
|
||||
</div>
|
||||
<div
|
||||
classList={{
|
||||
"flex-1 min-h-0 pb-20": true,
|
||||
"flex items-start justify-start": local.layout.review.state() === "open",
|
||||
"flex items-start justify-start": local.layout.review.state() === "pane",
|
||||
}}
|
||||
>
|
||||
<Show when={session.messages.user().length > 1}>
|
||||
|
|
@ -358,8 +361,8 @@ export default function Page() {
|
|||
role="list"
|
||||
classList={{
|
||||
"mr-8 shrink-0 flex flex-col items-start": true,
|
||||
"absolute right-full w-60 @7xl:gap-2": local.layout.review.state() !== "open",
|
||||
"mt-1": local.layout.review.state() === "open",
|
||||
"absolute right-full w-60 mt-3 @7xl:gap-2 @7xl:mt-1": local.layout.review.state() === "tab",
|
||||
"mt-3": local.layout.review.state() === "pane",
|
||||
}}
|
||||
>
|
||||
<For each={session.messages.user()}>
|
||||
|
|
@ -379,7 +382,7 @@ export default function Page() {
|
|||
<li
|
||||
classList={{
|
||||
"group/li flex items-center self-stretch justify-end": true,
|
||||
"@7xl:justify-start": local.layout.review.state() !== "open",
|
||||
"@7xl:justify-start": local.layout.review.state() === "tab",
|
||||
}}
|
||||
>
|
||||
<Tooltip
|
||||
|
|
@ -398,7 +401,7 @@ export default function Page() {
|
|||
classList={{
|
||||
"group/tick flex items-center justify-start h-2 w-8 -mr-3": true,
|
||||
"data-[active=true]:[&>div]:bg-icon-strong-base data-[active=true]:[&>div]:w-full": true,
|
||||
"@7xl:hidden": local.layout.review.state() !== "open",
|
||||
"@7xl:hidden": local.layout.review.state() === "tab",
|
||||
}}
|
||||
>
|
||||
<div class="h-px w-5 bg-icon-base group-hover/tick:w-full group-hover/tick:bg-icon-strong-base" />
|
||||
|
|
@ -407,7 +410,7 @@ export default function Page() {
|
|||
<button
|
||||
classList={{
|
||||
"hidden items-center self-stretch w-full gap-x-2 cursor-default": true,
|
||||
"@7xl:flex": local.layout.review.state() !== "open",
|
||||
"@7xl:flex": local.layout.review.state() === "tab",
|
||||
}}
|
||||
onClick={handleClick}
|
||||
>
|
||||
|
|
@ -477,7 +480,7 @@ export default function Page() {
|
|||
class="flex flex-col items-start self-stretch gap-8 pb-20"
|
||||
>
|
||||
{/* Title */}
|
||||
<div class="flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger z-20 pb-1">
|
||||
<div class="flex items-center gap-2 self-stretch sticky top-0 bg-background-stronger z-20 h-8">
|
||||
<div class="w-full text-14-medium text-text-strong">
|
||||
<Show
|
||||
when={titled()}
|
||||
|
|
@ -495,9 +498,7 @@ export default function Page() {
|
|||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<div class="-mt-9">
|
||||
<Message message={message} parts={parts()} />
|
||||
</div>
|
||||
<Message message={message} parts={parts()} />
|
||||
{/* Summary */}
|
||||
<Show when={completed()}>
|
||||
<div class="w-full flex flex-col gap-6 items-start self-stretch">
|
||||
|
|
@ -524,7 +525,7 @@ export default function Page() {
|
|||
<For each={message.summary?.diffs ?? []}>
|
||||
{(diff) => (
|
||||
<Accordion.Item value={diff.file}>
|
||||
<StickyAccordionHeader class="top-10 data-expanded:before:-top-10 ">
|
||||
<StickyAccordionHeader class="top-10 data-expanded:before:-top-10">
|
||||
<Accordion.Trigger>
|
||||
<div class="flex items-center justify-between w-full gap-5">
|
||||
<div class="grow flex items-center gap-5 min-w-0">
|
||||
|
|
@ -653,127 +654,25 @@ export default function Page() {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={local.layout.review.state() === "open"}>
|
||||
<Show when={local.layout.review.state() === "pane" && session.diffs().length}>
|
||||
<div
|
||||
classList={{
|
||||
"relative grow px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0 border-l border-border-weak-base": true,
|
||||
"relative grow px-6 py-3 flex-1 min-h-0 border-l border-border-weak-base": true,
|
||||
}}
|
||||
>
|
||||
<div class="h-8 w-full flex items-center justify-between shrink-0 self-stretch">
|
||||
<div class="flex items-center gap-x-3">
|
||||
<Tooltip value="Close">
|
||||
<IconButton icon="align-right" variant="ghost" onClick={local.layout.review.close} />
|
||||
</Tooltip>
|
||||
<Tooltip value="Open in tab">
|
||||
<IconButton
|
||||
icon="expand"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
local.layout.review.tab()
|
||||
session.layout.setActiveTab("review")
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-14-medium text-text-strong">All changes</div>
|
||||
<div class="h-full pb-40 overflow-y-auto no-scrollbar">
|
||||
<Accordion class="w-full" multiple>
|
||||
<For each={session.diffs()}>
|
||||
{(diff) => (
|
||||
<Accordion.Item value={diff.file} defaultOpen>
|
||||
<StickyAccordionHeader>
|
||||
<Accordion.Trigger>
|
||||
<div class="flex items-center justify-between w-full gap-5">
|
||||
<div class="grow flex items-center gap-5 min-w-0">
|
||||
<FileIcon node={{ path: diff.file, type: "file" }} class="shrink-0 size-4" />
|
||||
<div class="flex grow min-w-0">
|
||||
<Show when={diff.file.includes("/")}>
|
||||
<span class="text-text-base truncate-start">
|
||||
{getDirectory(diff.file)}‎
|
||||
</span>
|
||||
</Show>
|
||||
<span class="text-text-strong shrink-0">{getFilename(diff.file)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shrink-0 flex gap-4 items-center justify-end">
|
||||
<DiffChanges changes={diff} />
|
||||
<Icon name="chevron-grabber-vertical" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</Accordion.Trigger>
|
||||
</StickyAccordionHeader>
|
||||
<Accordion.Content>
|
||||
<Diff
|
||||
before={{
|
||||
name: diff.file!,
|
||||
contents: diff.before!,
|
||||
}}
|
||||
after={{
|
||||
name: diff.file!,
|
||||
contents: diff.after!,
|
||||
}}
|
||||
/>
|
||||
</Accordion.Content>
|
||||
</Accordion.Item>
|
||||
)}
|
||||
</For>
|
||||
</Accordion>
|
||||
</div>
|
||||
<SessionReview />
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
<Show when={local.layout.review.state() === "tab" && session.diffs().length}>
|
||||
<Tabs.Content value="review" class="select-text flex flex-col h-full overflow-hidden mt-8">
|
||||
<Tabs.Content value="review" class="select-text flex flex-col h-full overflow-hidden">
|
||||
<div
|
||||
classList={{
|
||||
"relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0 overflow-hidden": true,
|
||||
"relative px-6 py-3 flex-1 min-h-0 overflow-hidden": true,
|
||||
}}
|
||||
>
|
||||
<div class="text-14-medium text-text-strong shrink-0">All changes</div>
|
||||
<div class="flex-1 min-h-0 pb-40 overflow-y-auto no-scrollbar">
|
||||
<Accordion class="w-full" multiple>
|
||||
<For each={session.diffs()}>
|
||||
{(diff) => (
|
||||
<Accordion.Item value={diff.file} defaultOpen>
|
||||
<StickyAccordionHeader>
|
||||
<Accordion.Trigger>
|
||||
<div class="flex items-center justify-between w-full gap-5">
|
||||
<div class="grow flex items-center gap-5 min-w-0">
|
||||
<FileIcon node={{ path: diff.file, type: "file" }} class="shrink-0 size-4" />
|
||||
<div class="flex grow min-w-0">
|
||||
<Show when={diff.file.includes("/")}>
|
||||
<span class="text-text-base truncate-start">{getDirectory(diff.file)}‎</span>
|
||||
</Show>
|
||||
<span class="text-text-strong shrink-0">{getFilename(diff.file)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shrink-0 flex gap-4 items-center justify-end">
|
||||
<DiffChanges changes={diff} />
|
||||
<Icon name="chevron-grabber-vertical" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</Accordion.Trigger>
|
||||
</StickyAccordionHeader>
|
||||
<Accordion.Content>
|
||||
<Diff
|
||||
diffStyle="split"
|
||||
before={{
|
||||
name: diff.file!,
|
||||
contents: diff.before!,
|
||||
}}
|
||||
after={{
|
||||
name: diff.file!,
|
||||
contents: diff.after!,
|
||||
}}
|
||||
/>
|
||||
</Accordion.Content>
|
||||
</Accordion.Item>
|
||||
)}
|
||||
</For>
|
||||
</Accordion>
|
||||
</div>
|
||||
<SessionReview split hideExpand class="pb-40" />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
</Show>
|
||||
|
|
@ -828,7 +727,7 @@ export default function Page() {
|
|||
</DragOverlay>
|
||||
</DragDropProvider>
|
||||
<Show when={session.layout.tabs.active}>
|
||||
<div class="absolute inset-x-0 px-6 max-w-2xl flex flex-col justify-center items-center z-50 mx-auto bottom-8">
|
||||
<div class="absolute inset-x-0 px-6 max-w-2xl flex flex-col justify-center items-center z-50 mx-auto bottom-6">
|
||||
<PromptInput
|
||||
ref={(el) => {
|
||||
inputRef = el
|
||||
|
|
@ -895,18 +794,3 @@ export default function Page() {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function StickyAccordionHeader(props: ParentProps<{ class?: string }>) {
|
||||
return (
|
||||
<Accordion.Header
|
||||
classList={{
|
||||
"sticky top-0 data-expanded:z-10": true,
|
||||
"data-expanded:before:content-[''] data-expanded:before:z-[-10]": true,
|
||||
"data-expanded:before:absolute data-expanded:before:inset-0 data-expanded:before:bg-background-stronger": true,
|
||||
[props.class ?? ""]: !!props.class,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</Accordion.Header>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
id = "opencode"
|
||||
name = "OpenCode"
|
||||
description = "The AI coding agent built for the terminal"
|
||||
version = "1.0.62"
|
||||
version = "1.0.65"
|
||||
schema_version = 1
|
||||
authors = ["Anomaly"]
|
||||
repository = "https://github.com/sst/opencode"
|
||||
|
|
@ -11,26 +11,26 @@ name = "OpenCode"
|
|||
icon = "./icons/opencode.svg"
|
||||
|
||||
[agent_servers.opencode.targets.darwin-aarch64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.62/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.65/opencode-darwin-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.darwin-x86_64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.62/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.65/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.62/opencode-linux-arm64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.65/opencode-linux-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-x86_64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.62/opencode-linux-x64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.65/opencode-linux-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.windows-x86_64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.62/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.65/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -1,61 +1,84 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
#!/usr/bin/env node
|
||||
|
||||
if [ -n "$OPENCODE_BIN_PATH" ]; then
|
||||
resolved="$OPENCODE_BIN_PATH"
|
||||
else
|
||||
# Get the real path of this script, resolving any symlinks
|
||||
script_path="$0"
|
||||
while [ -L "$script_path" ]; do
|
||||
link_target="$(readlink "$script_path")"
|
||||
case "$link_target" in
|
||||
/*) script_path="$link_target" ;;
|
||||
*) script_path="$(dirname "$script_path")/$link_target" ;;
|
||||
esac
|
||||
done
|
||||
script_dir="$(dirname "$script_path")"
|
||||
script_dir="$(cd "$script_dir" && pwd)"
|
||||
|
||||
# Map platform names
|
||||
case "$(uname -s)" in
|
||||
Darwin) platform="darwin" ;;
|
||||
Linux) platform="linux" ;;
|
||||
MINGW*|CYGWIN*|MSYS*) platform="win32" ;;
|
||||
*) platform="$(uname -s | tr '[:upper:]' '[:lower:]')" ;;
|
||||
esac
|
||||
|
||||
# Map architecture names
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64) arch="x64" ;;
|
||||
aarch64) arch="arm64" ;;
|
||||
armv7l) arch="arm" ;;
|
||||
*) arch="$(uname -m)" ;;
|
||||
esac
|
||||
|
||||
name="opencode-${platform}-${arch}"
|
||||
binary="opencode"
|
||||
[ "$platform" = "win32" ] && binary="opencode.exe"
|
||||
|
||||
# Search for the binary starting from real script location
|
||||
resolved=""
|
||||
current_dir="$script_dir"
|
||||
while [ "$current_dir" != "/" ]; do
|
||||
candidate="$current_dir/node_modules/$name/bin/$binary"
|
||||
if [ -f "$candidate" ]; then
|
||||
resolved="$candidate"
|
||||
break
|
||||
fi
|
||||
current_dir="$(dirname "$current_dir")"
|
||||
done
|
||||
|
||||
if [ -z "$resolved" ]; then
|
||||
printf "It seems that your package manager failed to install the right version of the opencode CLI for your platform. You can try manually installing the \"%s\" package\n" "$name" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
const childProcess = require("child_process")
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const os = require("os")
|
||||
|
||||
# Handle SIGINT gracefully
|
||||
trap '' INT
|
||||
function run(target) {
|
||||
const result = childProcess.spawnSync(target, process.argv.slice(2), {
|
||||
stdio: "inherit",
|
||||
})
|
||||
if (result.error) {
|
||||
console.error(result.error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
const code = typeof result.status === "number" ? result.status : 0
|
||||
process.exit(code)
|
||||
}
|
||||
|
||||
# Execute the binary with all arguments
|
||||
exec "$resolved" "$@"
|
||||
const envPath = process.env.OPENCODE_BIN_PATH
|
||||
if (envPath) {
|
||||
run(envPath)
|
||||
}
|
||||
|
||||
const scriptPath = fs.realpathSync(__filename)
|
||||
const scriptDir = path.dirname(scriptPath)
|
||||
|
||||
const platformMap = {
|
||||
darwin: "darwin",
|
||||
linux: "linux",
|
||||
win32: "windows",
|
||||
}
|
||||
const archMap = {
|
||||
x64: "x64",
|
||||
arm64: "arm64",
|
||||
arm: "arm",
|
||||
}
|
||||
|
||||
let platform = platformMap[os.platform()]
|
||||
if (!platform) {
|
||||
platform = os.platform()
|
||||
}
|
||||
let arch = archMap[os.arch()]
|
||||
if (!arch) {
|
||||
arch = os.arch()
|
||||
}
|
||||
const base = "opencode-" + platform + "-" + arch
|
||||
const binary = platform === "windows" ? "opencode.exe" : "opencode"
|
||||
|
||||
function findBinary(startDir) {
|
||||
let current = startDir
|
||||
for (;;) {
|
||||
const modules = path.join(current, "node_modules")
|
||||
if (fs.existsSync(modules)) {
|
||||
const entries = fs.readdirSync(modules)
|
||||
for (const entry of entries) {
|
||||
if (!entry.startsWith(base)) {
|
||||
continue
|
||||
}
|
||||
const candidate = path.join(modules, entry, "bin", binary)
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
const parent = path.dirname(current)
|
||||
if (parent === current) {
|
||||
return
|
||||
}
|
||||
current = parent
|
||||
}
|
||||
}
|
||||
|
||||
const resolved = findBinary(scriptDir)
|
||||
if (!resolved) {
|
||||
console.error(
|
||||
'It seems that your package manager failed to install the right version of the opencode CLI for your platform. You can try manually installing the "' +
|
||||
base +
|
||||
'" package',
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
run(resolved)
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
if defined OPENCODE_BIN_PATH (
|
||||
set "resolved=%OPENCODE_BIN_PATH%"
|
||||
goto :execute
|
||||
)
|
||||
|
||||
rem Get the directory of this script
|
||||
set "script_dir=%~dp0"
|
||||
set "script_dir=%script_dir:~0,-1%"
|
||||
|
||||
rem Detect platform and architecture
|
||||
set "platform=windows"
|
||||
|
||||
rem Detect architecture
|
||||
if "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
|
||||
set "arch=x64"
|
||||
) else if "%PROCESSOR_ARCHITECTURE%"=="ARM64" (
|
||||
set "arch=arm64"
|
||||
) else if "%PROCESSOR_ARCHITECTURE%"=="x86" (
|
||||
set "arch=x86"
|
||||
) else (
|
||||
set "arch=x64"
|
||||
)
|
||||
|
||||
set "name=opencode-!platform!-!arch!"
|
||||
set "binary=opencode.exe"
|
||||
|
||||
rem Search for the binary starting from script location
|
||||
set "resolved="
|
||||
set "current_dir=%script_dir%"
|
||||
|
||||
:search_loop
|
||||
set "candidate=%current_dir%\node_modules\%name%\bin\%binary%"
|
||||
if exist "%candidate%" (
|
||||
set "resolved=%candidate%"
|
||||
goto :execute
|
||||
)
|
||||
|
||||
rem Move up one directory
|
||||
for %%i in ("%current_dir%") do set "parent_dir=%%~dpi"
|
||||
set "parent_dir=%parent_dir:~0,-1%"
|
||||
|
||||
rem Check if we've reached the root
|
||||
if "%current_dir%"=="%parent_dir%" goto :not_found
|
||||
set "current_dir=%parent_dir%"
|
||||
goto :search_loop
|
||||
|
||||
:not_found
|
||||
echo It seems that your package manager failed to install the right version of the opencode CLI for your platform. You can try manually installing the "%name%" package >&2
|
||||
exit /b 1
|
||||
|
||||
:execute
|
||||
rem Execute the binary with all arguments in the same console window
|
||||
rem Use start /b /wait to ensure it runs in the current shell context for all shells
|
||||
start /b /wait "" "%resolved%" %*
|
||||
exit /b %ERRORLEVEL%
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
|
|
|
|||
|
|
@ -66,11 +66,11 @@ const allTargets: {
|
|||
avx2: false,
|
||||
},
|
||||
{
|
||||
os: "windows",
|
||||
os: "win32",
|
||||
arch: "x64",
|
||||
},
|
||||
{
|
||||
os: "windows",
|
||||
os: "win32",
|
||||
arch: "x64",
|
||||
avx2: false,
|
||||
},
|
||||
|
|
@ -88,7 +88,8 @@ await $`bun install --os="*" --cpu="*" @parcel/watcher@${pkg.dependencies["@parc
|
|||
for (const item of targets) {
|
||||
const name = [
|
||||
pkg.name,
|
||||
item.os,
|
||||
// changing to win32 flags npm for some reason
|
||||
item.os === "win32" ? "windows" : item.os,
|
||||
item.arch,
|
||||
item.avx2 === false ? "baseline" : undefined,
|
||||
item.abi === undefined ? undefined : item.abi,
|
||||
|
|
@ -115,7 +116,7 @@ for (const item of targets) {
|
|||
entrypoints: ["./src/index.ts", parserWorker, workerPath],
|
||||
define: {
|
||||
OPENCODE_VERSION: `'${Script.version}'`,
|
||||
OTUI_TREE_SITTER_WORKER_PATH: "/$bunfs/root/" + path.relative(dir, parserWorker),
|
||||
OTUI_TREE_SITTER_WORKER_PATH: "/$bunfs/root/" + path.relative(dir, parserWorker).replaceAll("\\", "/"),
|
||||
OPENCODE_WORKER_PATH: workerPath,
|
||||
OPENCODE_CHANNEL: `'${Script.channel}'`,
|
||||
},
|
||||
|
|
@ -127,7 +128,7 @@ for (const item of targets) {
|
|||
{
|
||||
name,
|
||||
version: Script.version,
|
||||
os: [item.os === "windows" ? "win32" : item.os],
|
||||
os: [item.os],
|
||||
cpu: [item.arch],
|
||||
},
|
||||
null,
|
||||
|
|
|
|||
|
|
@ -50,79 +50,66 @@ function detectPlatformAndArch() {
|
|||
function findBinary() {
|
||||
const { platform, arch } = detectPlatformAndArch()
|
||||
const packageName = `opencode-${platform}-${arch}`
|
||||
const binary = platform === "windows" ? "opencode.exe" : "opencode"
|
||||
const binaryName = platform === "windows" ? "opencode.exe" : "opencode"
|
||||
|
||||
try {
|
||||
// Use require.resolve to find the package
|
||||
const packageJsonPath = require.resolve(`${packageName}/package.json`)
|
||||
const packageDir = path.dirname(packageJsonPath)
|
||||
const binaryPath = path.join(packageDir, "bin", binary)
|
||||
const binaryPath = path.join(packageDir, "bin", binaryName)
|
||||
|
||||
if (!fs.existsSync(binaryPath)) {
|
||||
throw new Error(`Binary not found at ${binaryPath}`)
|
||||
}
|
||||
|
||||
return binaryPath
|
||||
return { binaryPath, binaryName }
|
||||
} catch (error) {
|
||||
throw new Error(`Could not find package ${packageName}: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function regenerateWindowsCmdWrappers() {
|
||||
console.log("Windows + npm detected: Forcing npm to rebuild bin links")
|
||||
function prepareBinDirectory(binaryName) {
|
||||
const binDir = path.join(__dirname, "bin")
|
||||
const targetPath = path.join(binDir, binaryName)
|
||||
|
||||
try {
|
||||
const { execSync } = require("child_process")
|
||||
const pkgPath = path.join(__dirname, "..")
|
||||
// Ensure bin directory exists
|
||||
if (!fs.existsSync(binDir)) {
|
||||
fs.mkdirSync(binDir, { recursive: true })
|
||||
}
|
||||
|
||||
// npm_config_global is string | undefined
|
||||
// if it exists, the value is true
|
||||
const isGlobal = process.env.npm_config_global === "true" || pkgPath.includes(path.join("npm", "node_modules"))
|
||||
// Remove existing binary/symlink if it exists
|
||||
if (fs.existsSync(targetPath)) {
|
||||
fs.unlinkSync(targetPath)
|
||||
}
|
||||
|
||||
// The npm rebuild command does 2 things - Execute lifecycle scripts and rebuild bin links
|
||||
// We want to skip lifecycle scripts to avoid infinite loops, so we use --ignore-scripts
|
||||
const cmd = `npm rebuild opencode-ai --ignore-scripts${isGlobal ? " -g" : ""}`
|
||||
const opts = {
|
||||
stdio: "inherit",
|
||||
shell: true,
|
||||
...(isGlobal ? {} : { cwd: path.join(pkgPath, "..", "..") }), // For local, run from project root
|
||||
}
|
||||
return { binDir, targetPath }
|
||||
}
|
||||
|
||||
console.log(`Running: ${cmd}`)
|
||||
execSync(cmd, opts)
|
||||
console.log("Successfully rebuilt npm bin links")
|
||||
} catch (error) {
|
||||
console.error("Error rebuilding npm links:", error.message)
|
||||
console.error("npm rebuild failed. You may need to manually run: npm rebuild opencode-ai --ignore-scripts")
|
||||
function symlinkBinary(sourcePath, binaryName) {
|
||||
const { targetPath } = prepareBinDirectory(binaryName)
|
||||
|
||||
fs.symlinkSync(sourcePath, targetPath)
|
||||
console.log(`opencode binary symlinked: ${targetPath} -> ${sourcePath}`)
|
||||
|
||||
// Verify the file exists after operation
|
||||
if (!fs.existsSync(targetPath)) {
|
||||
throw new Error(`Failed to symlink binary to ${targetPath}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
if (os.platform() === "win32") {
|
||||
// NPM eg format - npm/11.4.2 node/v24.4.1 win32 x64
|
||||
// Bun eg format - bun/1.2.19 npm/? node/v24.3.0 win32 x64
|
||||
if (process.env.npm_config_user_agent.startsWith("npm")) {
|
||||
await regenerateWindowsCmdWrappers()
|
||||
} else {
|
||||
console.log("Windows detected but not npm, skipping postinstall")
|
||||
}
|
||||
// On Windows, the .exe is already included in the package and bin field points to it
|
||||
// No postinstall setup needed
|
||||
console.log("Windows detected: binary setup not needed (using packaged .exe)")
|
||||
return
|
||||
}
|
||||
|
||||
const binaryPath = findBinary()
|
||||
const binScript = path.join(__dirname, "bin", "opencode")
|
||||
|
||||
// Remove existing bin script if it exists
|
||||
if (fs.existsSync(binScript)) {
|
||||
fs.unlinkSync(binScript)
|
||||
}
|
||||
|
||||
// Create symlink to the actual binary
|
||||
fs.symlinkSync(binaryPath, binScript)
|
||||
console.log(`opencode binary symlinked: ${binScript} -> ${binaryPath}`)
|
||||
const { binaryPath, binaryName } = findBinary()
|
||||
symlinkBinary(binaryPath, binaryName)
|
||||
} catch (error) {
|
||||
console.error("Failed to create opencode binary symlink:", error.message)
|
||||
console.error("Failed to setup opencode binary:", error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import os from "os"
|
||||
import { fileURLToPath } from "url"
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
function main() {
|
||||
if (os.platform() !== "win32") {
|
||||
console.log("Non-Windows platform detected, skipping preinstall")
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Windows detected: Modifying package.json bin entry")
|
||||
|
||||
// Read package.json
|
||||
const packageJsonPath = path.join(__dirname, "package.json")
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"))
|
||||
|
||||
// Modify bin to point to .cmd file on Windows
|
||||
packageJson.bin = {
|
||||
opencode: "./bin/opencode.cmd",
|
||||
}
|
||||
|
||||
// Write it back
|
||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))
|
||||
console.log("Updated package.json bin to use opencode.cmd")
|
||||
|
||||
// Now you can also remove the Unix script if you want
|
||||
const unixScript = path.join(__dirname, "bin", "opencode")
|
||||
if (fs.existsSync(unixScript)) {
|
||||
console.log("Removing Unix shell script")
|
||||
fs.unlinkSync(unixScript)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
main()
|
||||
} catch (error) {
|
||||
console.error("Preinstall script error:", error.message)
|
||||
process.exit(0)
|
||||
}
|
||||
|
|
@ -2,8 +2,9 @@
|
|||
import { $ } from "bun"
|
||||
import pkg from "../package.json"
|
||||
import { Script } from "@opencode-ai/script"
|
||||
import { fileURLToPath } from "url"
|
||||
|
||||
const dir = new URL("..", import.meta.url).pathname
|
||||
const dir = fileURLToPath(new URL("..", import.meta.url))
|
||||
process.chdir(dir)
|
||||
|
||||
const { binaries } = await import("./build.ts")
|
||||
|
|
@ -15,8 +16,8 @@ const { binaries } = await import("./build.ts")
|
|||
|
||||
await $`mkdir -p ./dist/${pkg.name}`
|
||||
await $`cp -r ./bin ./dist/${pkg.name}/bin`
|
||||
await $`cp ./script/preinstall.mjs ./dist/${pkg.name}/preinstall.mjs`
|
||||
await $`cp ./script/postinstall.mjs ./dist/${pkg.name}/postinstall.mjs`
|
||||
|
||||
await Bun.file(`./dist/${pkg.name}/package.json`).write(
|
||||
JSON.stringify(
|
||||
{
|
||||
|
|
@ -25,7 +26,6 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write(
|
|||
[pkg.name]: `./bin/${pkg.name}`,
|
||||
},
|
||||
scripts: {
|
||||
preinstall: "bun ./preinstall.mjs || node ./preinstall.mjs",
|
||||
postinstall: "bun ./postinstall.mjs || node ./postinstall.mjs",
|
||||
},
|
||||
version: Script.version,
|
||||
|
|
@ -36,7 +36,15 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write(
|
|||
),
|
||||
)
|
||||
for (const [name] of Object.entries(binaries)) {
|
||||
await $`cd dist/${name} && chmod 777 -R . && bun publish --access public --tag ${Script.channel}`
|
||||
try {
|
||||
process.chdir(`./dist/${name}`)
|
||||
if (process.platform !== "win32") {
|
||||
await $`chmod 755 -R .`
|
||||
}
|
||||
await $`bun publish --access public --tag ${Script.channel}`
|
||||
} finally {
|
||||
process.chdir(dir)
|
||||
}
|
||||
}
|
||||
await $`cd ./dist/${pkg.name} && bun publish --access public --tag ${Script.channel}`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
import { useRenderer } from "@opentui/solid"
|
||||
import { createSimpleContext } from "./helper"
|
||||
import { FormatError } from "@/cli/error"
|
||||
|
||||
export const { use: useExit, provider: ExitProvider } = createSimpleContext({
|
||||
name: "Exit",
|
||||
init: (input: { onExit?: () => Promise<void> }) => {
|
||||
const renderer = useRenderer()
|
||||
return async () => {
|
||||
return async (reason?: any) => {
|
||||
renderer.destroy()
|
||||
await input.onExit?.()
|
||||
if (reason) {
|
||||
const formatted = FormatError(reason) ?? JSON.stringify(reason)
|
||||
process.stderr.write(formatted + "\n")
|
||||
}
|
||||
process.exit(0)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,11 +10,6 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
|||
const sdk = createOpencodeClient({
|
||||
baseUrl: props.url,
|
||||
signal: abort.signal,
|
||||
fetch: (req) => {
|
||||
// @ts-ignore
|
||||
req.timeout = false
|
||||
return fetch(req)
|
||||
},
|
||||
})
|
||||
|
||||
const emitter = createGlobalEmitter<{
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import { useSDK } from "@tui/context/sdk"
|
|||
import { Binary } from "@/util/binary"
|
||||
import { createSimpleContext } from "./helper"
|
||||
import type { Snapshot } from "@/snapshot"
|
||||
import { useExit } from "./exit"
|
||||
import { onMount } from "solid-js"
|
||||
|
||||
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
name: "Sync",
|
||||
|
|
@ -226,29 +228,37 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||
}
|
||||
})
|
||||
|
||||
// blocking
|
||||
Promise.all([
|
||||
sdk.client.config.providers({ throwOnError: true }).then((x) => setStore("provider", x.data!.providers)),
|
||||
sdk.client.app.agents({ throwOnError: true }).then((x) => setStore("agent", x.data ?? [])),
|
||||
sdk.client.config.get({ throwOnError: true }).then((x) => setStore("config", x.data!)),
|
||||
]).then(() => {
|
||||
setStore("status", "partial")
|
||||
// non-blocking
|
||||
const exit = useExit()
|
||||
|
||||
onMount(() => {
|
||||
// blocking
|
||||
Promise.all([
|
||||
sdk.client.session.list().then((x) =>
|
||||
setStore(
|
||||
"session",
|
||||
(x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)),
|
||||
),
|
||||
),
|
||||
sdk.client.command.list().then((x) => setStore("command", x.data ?? [])),
|
||||
sdk.client.lsp.status().then((x) => setStore("lsp", x.data!)),
|
||||
sdk.client.mcp.status().then((x) => setStore("mcp", x.data!)),
|
||||
sdk.client.formatter.status().then((x) => setStore("formatter", x.data!)),
|
||||
sdk.client.session.status().then((x) => setStore("session_status", x.data!)),
|
||||
]).then(() => {
|
||||
setStore("status", "complete")
|
||||
})
|
||||
sdk.client.config.providers({ throwOnError: true }).then((x) => setStore("provider", x.data!.providers)),
|
||||
sdk.client.app.agents({ throwOnError: true }).then((x) => setStore("agent", x.data ?? [])),
|
||||
sdk.client.config.get({ throwOnError: true }).then((x) => setStore("config", x.data!)),
|
||||
])
|
||||
.then(() => {
|
||||
setStore("status", "partial")
|
||||
// non-blocking
|
||||
Promise.all([
|
||||
sdk.client.session.list().then((x) =>
|
||||
setStore(
|
||||
"session",
|
||||
(x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)),
|
||||
),
|
||||
),
|
||||
sdk.client.command.list().then((x) => setStore("command", x.data ?? [])),
|
||||
sdk.client.lsp.status().then((x) => setStore("lsp", x.data!)),
|
||||
sdk.client.mcp.status().then((x) => setStore("mcp", x.data!)),
|
||||
sdk.client.formatter.status().then((x) => setStore("formatter", x.data!)),
|
||||
sdk.client.session.status().then((x) => setStore("session_status", x.data!)),
|
||||
]).then(() => {
|
||||
setStore("status", "complete")
|
||||
})
|
||||
})
|
||||
.catch(async (e) => {
|
||||
await exit(e)
|
||||
})
|
||||
})
|
||||
|
||||
const result = {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,14 @@ import { useRoute, useRouteData } from "@tui/context/route"
|
|||
import { useSync } from "@tui/context/sync"
|
||||
import { SplitBorder } from "@tui/component/border"
|
||||
import { useTheme } from "@tui/context/theme"
|
||||
import { BoxRenderable, ScrollBoxRenderable, addDefaultParsers } from "@opentui/core"
|
||||
import {
|
||||
BoxRenderable,
|
||||
ScrollBoxRenderable,
|
||||
TextAttributes,
|
||||
addDefaultParsers,
|
||||
MacOSScrollAccel,
|
||||
type ScrollAcceleration,
|
||||
} from "@opentui/core"
|
||||
import { Prompt, type PromptRef } from "@tui/component/prompt"
|
||||
import type { AssistantMessage, Part, ToolPart, UserMessage, TextPart, ReasoningPart } from "@opencode-ai/sdk"
|
||||
import { useLocal } from "@tui/context/local"
|
||||
|
|
@ -61,6 +68,16 @@ import stripAnsi from "strip-ansi"
|
|||
|
||||
addDefaultParsers(parsers.parsers)
|
||||
|
||||
class CustomSpeedScroll implements ScrollAcceleration {
|
||||
constructor(private speed: number) {}
|
||||
|
||||
tick(_now?: number): number {
|
||||
return this.speed
|
||||
}
|
||||
|
||||
reset(): void {}
|
||||
}
|
||||
|
||||
const context = createContext<{
|
||||
width: number
|
||||
conceal: () => boolean
|
||||
|
|
@ -99,6 +116,17 @@ export function Session() {
|
|||
const sidebarVisible = createMemo(() => sidebar() === "show" || (sidebar() === "auto" && wide()))
|
||||
const contentWidth = createMemo(() => dimensions().width - (sidebarVisible() ? 42 : 0) - 4)
|
||||
|
||||
const scrollAcceleration = createMemo(() => {
|
||||
const tui = sync.data.config.tui
|
||||
if (tui?.scroll_acceleration?.enabled) {
|
||||
return new MacOSScrollAccel()
|
||||
}
|
||||
if (tui?.scroll_speed) {
|
||||
return new CustomSpeedScroll(tui.scroll_speed)
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
|
||||
createEffect(async () => {
|
||||
await sync.session
|
||||
.sync(route.sessionID)
|
||||
|
|
@ -695,6 +723,7 @@ export function Session() {
|
|||
stickyScroll={true}
|
||||
stickyStart="bottom"
|
||||
flexGrow={1}
|
||||
scrollAcceleration={scrollAcceleration()}
|
||||
>
|
||||
<For each={messages()}>
|
||||
{(message, index) => (
|
||||
|
|
@ -1229,7 +1258,7 @@ ToolRegistry.register<typeof WriteTool>({
|
|||
<For each={numbers()}>{(value) => <text style={{ fg: theme.textMuted }}>{value}</text>}</For>
|
||||
</box>
|
||||
<box paddingLeft={1} flexGrow={1}>
|
||||
<code filetype={filetype(props.input.filePath!)} syntaxStyle={syntax()} content={code()} />
|
||||
<code fg={theme.text} filetype={filetype(props.input.filePath!)} syntaxStyle={syntax()} content={code()} />
|
||||
</box>
|
||||
</box>
|
||||
<Show when={diagnostics().length}>
|
||||
|
|
@ -1439,16 +1468,16 @@ ToolRegistry.register<typeof EditTool>({
|
|||
<Match when={diff() && style() === "split"}>
|
||||
<box paddingLeft={1} flexDirection="row" gap={2}>
|
||||
<box flexGrow={1} flexBasis={0}>
|
||||
<code filetype={ft()} syntaxStyle={syntax()} content={diff()!.oldContent} />
|
||||
<code fg={theme.text} filetype={ft()} syntaxStyle={syntax()} content={diff()!.oldContent} />
|
||||
</box>
|
||||
<box flexGrow={1} flexBasis={0}>
|
||||
<code filetype={ft()} syntaxStyle={syntax()} content={diff()!.newContent} />
|
||||
<code fg={theme.text} filetype={ft()} syntaxStyle={syntax()} content={diff()!.newContent} />
|
||||
</box>
|
||||
</box>
|
||||
</Match>
|
||||
<Match when={code()}>
|
||||
<box paddingLeft={1}>
|
||||
<code filetype={ft()} syntaxStyle={syntax()} content={code()} />
|
||||
<code fg={theme.text} filetype={ft()} syntaxStyle={syntax()} content={code()} />
|
||||
</box>
|
||||
</Match>
|
||||
</Switch>
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ export function Toast() {
|
|||
customBorderChars={SplitBorder.customBorderChars}
|
||||
>
|
||||
<Show when={current().title}>
|
||||
<text attributes={TextAttributes.BOLD} marginBottom={1}>
|
||||
<text attributes={TextAttributes.BOLD} marginBottom={1} fg={theme.text}>
|
||||
{current().title}
|
||||
</text>
|
||||
</Show>
|
||||
<text>{current().message}</text>
|
||||
<text fg={theme.text}>{current().message}</text>
|
||||
</box>
|
||||
)}
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export const rpc = {
|
|||
}
|
||||
},
|
||||
async shutdown() {
|
||||
Log.Default.info("worker shutting down")
|
||||
await Instance.disposeAll()
|
||||
await server.stop(true)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -437,7 +437,13 @@ export namespace Config {
|
|||
})
|
||||
|
||||
export const TUI = z.object({
|
||||
scroll_speed: z.number().min(1).optional().default(2).describe("TUI scroll speed"),
|
||||
scroll_speed: z.number().min(1).optional().default(1).describe("TUI scroll speed"),
|
||||
scroll_acceleration: z
|
||||
.object({
|
||||
enabled: z.boolean().describe("Enable scroll acceleration"),
|
||||
})
|
||||
.optional()
|
||||
.describe("Scroll acceleration settings"),
|
||||
})
|
||||
|
||||
export const Layout = z.enum(["auto", "stretch"]).meta({
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ export namespace Format {
|
|||
extensions: [],
|
||||
...item,
|
||||
})
|
||||
|
||||
if (result.command.length === 0) continue
|
||||
|
||||
result.enabled = async () => true
|
||||
result.name = name
|
||||
formatters[name] = result
|
||||
|
|
|
|||
|
|
@ -53,10 +53,14 @@ export const Instance = {
|
|||
await State.dispose(Instance.directory)
|
||||
},
|
||||
async disposeAll() {
|
||||
Log.Default.info("disposing all instances")
|
||||
for (const [_key, value] of cache) {
|
||||
await context.provide(await value, async () => {
|
||||
await Instance.dispose()
|
||||
})
|
||||
const awaited = await value.catch(() => {})
|
||||
if (awaited) {
|
||||
await context.provide(await value, async () => {
|
||||
await Instance.dispose()
|
||||
})
|
||||
}
|
||||
}
|
||||
cache.clear()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ export namespace ProviderTransform {
|
|||
}
|
||||
|
||||
export function maxOutputTokens(
|
||||
providerID: string,
|
||||
npm: string,
|
||||
options: Record<string, any>,
|
||||
modelLimit: number,
|
||||
globalLimit: number,
|
||||
|
|
@ -184,7 +184,7 @@ export namespace ProviderTransform {
|
|||
const modelCap = modelLimit || globalLimit
|
||||
const standardLimit = Math.min(modelCap, globalLimit)
|
||||
|
||||
if (providerID === "anthropic") {
|
||||
if (npm === "@ai-sdk/anthropic") {
|
||||
const thinking = options?.["thinking"]
|
||||
const budgetTokens = typeof thinking?.["budgetTokens"] === "number" ? thinking["budgetTokens"] : 0
|
||||
const enabled = thinking?.["type"] === "enabled"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { Provider } from "../provider/provider"
|
|||
import { Identifier } from "../id/id"
|
||||
import { Permission } from "../permission"
|
||||
import { Agent } from "@/agent/agent"
|
||||
import { iife } from "@/util/iife"
|
||||
|
||||
const DEFAULT_READ_LIMIT = 2000
|
||||
const MAX_LINE_LENGTH = 2000
|
||||
|
|
@ -48,6 +49,19 @@ export const ReadTool = Tool.define("read", {
|
|||
}
|
||||
}
|
||||
|
||||
const block = (() => {
|
||||
const whitelist = [".env.example", ".env.sample"]
|
||||
|
||||
if (whitelist.some((w) => filepath.endsWith(w))) return false
|
||||
if (filepath.includes(".env")) return true
|
||||
|
||||
return false
|
||||
})()
|
||||
|
||||
if (block) {
|
||||
throw new Error(`The user has blocked you from reading ${filepath}, DO NOT make further attempts to read it`)
|
||||
}
|
||||
|
||||
const file = Bun.file(filepath)
|
||||
if (!(await file.exists())) {
|
||||
const dir = path.dirname(filepath)
|
||||
|
|
|
|||
98
packages/opencode/test/provider/transform.test.ts
Normal file
98
packages/opencode/test/provider/transform.test.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import { ProviderTransform } from "../../src/provider/transform"
|
||||
|
||||
const OUTPUT_TOKEN_MAX = 32000
|
||||
|
||||
describe("ProviderTransform.maxOutputTokens", () => {
|
||||
test("returns 32k when modelLimit > 32k", () => {
|
||||
const modelLimit = 100000
|
||||
const result = ProviderTransform.maxOutputTokens("@ai-sdk/openai", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
||||
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
||||
})
|
||||
|
||||
test("returns modelLimit when modelLimit < 32k", () => {
|
||||
const modelLimit = 16000
|
||||
const result = ProviderTransform.maxOutputTokens("@ai-sdk/openai", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
||||
expect(result).toBe(16000)
|
||||
})
|
||||
|
||||
describe("azure", () => {
|
||||
test("returns 32k when modelLimit > 32k", () => {
|
||||
const modelLimit = 100000
|
||||
const result = ProviderTransform.maxOutputTokens("@ai-sdk/azure", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
||||
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
||||
})
|
||||
|
||||
test("returns modelLimit when modelLimit < 32k", () => {
|
||||
const modelLimit = 16000
|
||||
const result = ProviderTransform.maxOutputTokens("@ai-sdk/azure", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
||||
expect(result).toBe(16000)
|
||||
})
|
||||
})
|
||||
|
||||
describe("bedrock", () => {
|
||||
test("returns 32k when modelLimit > 32k", () => {
|
||||
const modelLimit = 100000
|
||||
const result = ProviderTransform.maxOutputTokens("@ai-sdk/amazon-bedrock", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
||||
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
||||
})
|
||||
|
||||
test("returns modelLimit when modelLimit < 32k", () => {
|
||||
const modelLimit = 16000
|
||||
const result = ProviderTransform.maxOutputTokens("@ai-sdk/amazon-bedrock", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
||||
expect(result).toBe(16000)
|
||||
})
|
||||
})
|
||||
|
||||
describe("anthropic without thinking options", () => {
|
||||
test("returns 32k when modelLimit > 32k", () => {
|
||||
const modelLimit = 100000
|
||||
const result = ProviderTransform.maxOutputTokens("@ai-sdk/anthropic", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
||||
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
||||
})
|
||||
|
||||
test("returns modelLimit when modelLimit < 32k", () => {
|
||||
const modelLimit = 16000
|
||||
const result = ProviderTransform.maxOutputTokens("@ai-sdk/anthropic", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
||||
expect(result).toBe(16000)
|
||||
})
|
||||
})
|
||||
|
||||
describe("anthropic with thinking options", () => {
|
||||
test("returns 32k when budgetTokens + 32k <= modelLimit", () => {
|
||||
const modelLimit = 100000
|
||||
const options = {
|
||||
thinking: {
|
||||
type: "enabled",
|
||||
budgetTokens: 10000,
|
||||
},
|
||||
}
|
||||
const result = ProviderTransform.maxOutputTokens("@ai-sdk/anthropic", options, modelLimit, OUTPUT_TOKEN_MAX)
|
||||
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
||||
})
|
||||
|
||||
test("returns modelLimit - budgetTokens when budgetTokens + 32k > modelLimit", () => {
|
||||
const modelLimit = 50000
|
||||
const options = {
|
||||
thinking: {
|
||||
type: "enabled",
|
||||
budgetTokens: 30000,
|
||||
},
|
||||
}
|
||||
const result = ProviderTransform.maxOutputTokens("@ai-sdk/anthropic", options, modelLimit, OUTPUT_TOKEN_MAX)
|
||||
expect(result).toBe(20000)
|
||||
})
|
||||
|
||||
test("returns 32k when thinking type is not enabled", () => {
|
||||
const modelLimit = 100000
|
||||
const options = {
|
||||
thinking: {
|
||||
type: "disabled",
|
||||
budgetTokens: 10000,
|
||||
},
|
||||
}
|
||||
const result = ProviderTransform.maxOutputTokens("@ai-sdk/anthropic", options, modelLimit, OUTPUT_TOKEN_MAX)
|
||||
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,17 @@ import { type Config } from "./gen/client/types.gen.js"
|
|||
import { OpencodeClient } from "./gen/sdk.gen.js"
|
||||
|
||||
export function createOpencodeClient(config?: Config) {
|
||||
if (!config?.fetch) {
|
||||
config = {
|
||||
...config,
|
||||
fetch: (req) => {
|
||||
// @ts-ignore
|
||||
req.timeout = false
|
||||
return fetch(req)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const client = createClient(config)
|
||||
return new OpencodeClient({ client })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,6 +301,15 @@ export type Config = {
|
|||
* TUI scroll speed
|
||||
*/
|
||||
scroll_speed?: number
|
||||
/**
|
||||
* Scroll acceleration settings
|
||||
*/
|
||||
scroll_acceleration?: {
|
||||
/**
|
||||
* Enable scroll acceleration
|
||||
*/
|
||||
enabled: boolean
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Command configuration, see https://opencode.ai/docs/commands
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bun run src/index.ts",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/components/index.ts",
|
||||
|
|
|
|||
|
|
@ -44,9 +44,9 @@
|
|||
--surface-info-base: var(--lilac-light-3);
|
||||
--surface-info-weak: var(--lilac-light-2);
|
||||
--surface-info-strong: var(--lilac-light-9);
|
||||
--surface-diff-unchanged-base: #FFFFFF00;
|
||||
--surface-diff-skip-base: var(--smoke-light-2);
|
||||
--surface-diff-hidden-base: var(--blue-light-3);
|
||||
--surface-diff-unchanged-base: #FFFFFF00;
|
||||
--surface-diff-hidden-weak: var(--blue-light-2);
|
||||
--surface-diff-hidden-weaker: var(--blue-light-1);
|
||||
--surface-diff-hidden-strong: var(--blue-light-5);
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
--text-on-brand-weaker: var(--smoke-light-alpha-8);
|
||||
--text-on-brand-strong: var(--smoke-light-alpha-12);
|
||||
--button-secondary-base: #FDFCFC;
|
||||
--button-secondary-base-hover: #FAF9F9;
|
||||
--button-secondary-hover: #FAF9F9;
|
||||
--border-base: var(--smoke-light-alpha-7);
|
||||
--border-hover: var(--smoke-light-alpha-8);
|
||||
--border-active: var(--smoke-light-alpha-9);
|
||||
|
|
@ -190,20 +190,25 @@
|
|||
--icon-diff-add-active: var(--mint-light-12);
|
||||
--icon-diff-delete-base: var(--ember-light-10);
|
||||
--icon-diff-delete-hover: var(--ember-light-11);
|
||||
--syntax-comment: #8A8A8A;
|
||||
--syntax-string: #D68C27;
|
||||
--syntax-keyword: #3B7DD8;
|
||||
--syntax-function: #D1383D;
|
||||
--syntax-number: #3D9A57;
|
||||
--syntax-operator: #D68C27;
|
||||
--syntax-variable: #B0851F;
|
||||
--syntax-type: #318795;
|
||||
--syntax-constant: #953170;
|
||||
--syntax-punctuation: #1A1A1A;
|
||||
--syntax-success: var(--apple-dark-10);
|
||||
--syntax-comment: var(--text-weaker);
|
||||
--syntax-regexp: var(--text-base);
|
||||
--syntax-string: #007663;
|
||||
--syntax-keyword: var(--text-weak);
|
||||
--syntax-primitive: #FB7F51;
|
||||
--syntax-operator: var(--text-weak);
|
||||
--syntax-variable: var(--text-strong);
|
||||
--syntax-property: #EC6CC8;
|
||||
--syntax-type: #738400;
|
||||
--syntax-constant: #00B2B9;
|
||||
--syntax-punctuation: var(--text-weaker);
|
||||
--syntax-object: var(--text-strong);
|
||||
--syntax-success: var(--apple-light-10);
|
||||
--syntax-warning: var(--amber-light-10);
|
||||
--syntax-critical: var(--ember-dark-9);
|
||||
--syntax-info: var(--lilac-dark-11);
|
||||
--syntax-critical: var(--ember-light-9);
|
||||
--syntax-info: #0091A7;
|
||||
--syntax-diff-add: var(--mint-light-11);
|
||||
--syntax-diff-delete: var(--ember-light-11);
|
||||
--syntax-diff-unknown: #FF0000;
|
||||
--markdown-heading: #D68C27;
|
||||
--markdown-text: #1A1A1A;
|
||||
--markdown-link: #3B7DD8;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ for (const line of colors.split("\n")) {
|
|||
}
|
||||
|
||||
const output = `
|
||||
/* Generated by script/colors.ts */
|
||||
/* Generated by script/tailwind.ts */
|
||||
/* Do not edit this file manually */
|
||||
|
||||
@theme {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
[data-slot="accordion-trigger"] {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
height: 32px;
|
||||
padding: 8px 12px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
|
@ -63,23 +63,24 @@
|
|||
margin-bottom: 8px;
|
||||
|
||||
[data-slot="accordion-trigger"] {
|
||||
border-radius: 8px 8px 0 0;
|
||||
border-top-left-radius: var(--radius-md);
|
||||
border-top-right-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
[data-slot="accordion-content"] {
|
||||
border: 1px solid var(--border-weak-base);
|
||||
border-top: none;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: var(--radius-md);
|
||||
border-bottom-right-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
[data-slot="accordion-item"]:has(+ &) {
|
||||
&[data-closed] {
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: var(--radius-md);
|
||||
border-bottom-right-radius: var(--radius-md);
|
||||
[data-slot="accordion-trigger"] {
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: var(--radius-md);
|
||||
border-bottom-right-radius: var(--radius-md);
|
||||
}
|
||||
}
|
||||
margin-bottom: 8px;
|
||||
|
|
@ -89,8 +90,8 @@
|
|||
margin-top: 8px;
|
||||
|
||||
[data-slot="accordion-trigger"] {
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
border-top-left-radius: var(--radius-md);
|
||||
border-top-right-radius: var(--radius-md);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -106,8 +107,8 @@
|
|||
|
||||
&[data-closed] {
|
||||
[data-slot="accordion-trigger"] {
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
border-top-left-radius: var(--radius-md);
|
||||
border-top-right-radius: var(--radius-md);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -117,8 +118,8 @@
|
|||
|
||||
&[data-closed] {
|
||||
[data-slot="accordion-trigger"] {
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: var(--radius-md);
|
||||
border-bottom-right-radius: var(--radius-md);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import { Accordion as Kobalte } from "@kobalte/core/accordion"
|
||||
import { createSignal, splitProps } from "solid-js"
|
||||
import { splitProps } from "solid-js"
|
||||
import type { ComponentProps, ParentProps } from "solid-js"
|
||||
|
||||
export interface AccordionProps extends ComponentProps<typeof Kobalte> {}
|
||||
export interface AccordionItemProps extends ComponentProps<typeof Kobalte.Item> {
|
||||
defaultOpen?: boolean
|
||||
}
|
||||
export interface AccordionItemProps extends ComponentProps<typeof Kobalte.Item> {}
|
||||
export interface AccordionHeaderProps extends ComponentProps<typeof Kobalte.Header> {}
|
||||
export interface AccordionTriggerProps extends ComponentProps<typeof Kobalte.Trigger> {}
|
||||
export interface AccordionContentProps extends ComponentProps<typeof Kobalte.Content> {}
|
||||
|
|
@ -25,14 +23,11 @@ function AccordionRoot(props: AccordionProps) {
|
|||
}
|
||||
|
||||
function AccordionItem(props: AccordionItemProps) {
|
||||
const [split, rest] = splitProps(props, ["class", "classList", "defaultOpen"])
|
||||
const [open, setOpen] = createSignal(split.defaultOpen ?? false)
|
||||
const [split, rest] = splitProps(props, ["class", "classList"])
|
||||
return (
|
||||
<Kobalte.Item
|
||||
{...rest}
|
||||
data-slot="accordion-item"
|
||||
onOpenChange={setOpen}
|
||||
open={open()}
|
||||
classList={{
|
||||
...(split.classList ?? {}),
|
||||
[split.class ?? ""]: !!split.class,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
justify-content: center;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
text-decoration: none;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
background-color: var(--surface-inset-base);
|
||||
border: 1px solid var(--border-weaker-base);
|
||||
transition: background-color 0.15s ease;
|
||||
border-radius: 8px;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 6px 12px;
|
||||
overflow: clip;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
padding: 2px;
|
||||
aspect-ratio: 1;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border-weak-base);
|
||||
/* background-color: var(--surface-weak); */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
background-color: var(--surface-inset-base);
|
||||
border: 1px solid var(--border-weaker-base);
|
||||
transition: background-color 0.15s ease;
|
||||
border-radius: 8px;
|
||||
border-radius: var(--radius-md);
|
||||
overflow: clip;
|
||||
|
||||
[data-slot="collapsible-trigger"] {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
/* padding: 8px; */
|
||||
padding: 8px 8px 0 8px;
|
||||
border: 1px solid var(--border-base);
|
||||
border-radius: 16px;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface-raised-stronger-non-alpha);
|
||||
box-shadow:
|
||||
0 15px 45px 0 rgba(19, 16, 16, 0.22),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
text-decoration: none;
|
||||
user-select: none;
|
||||
aspect-ratio: 1;
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ const newIcons = {
|
|||
"speech-bubble": `<path d="M18.3334 10.0003C18.3334 5.57324 15.0927 2.91699 10.0001 2.91699C4.90749 2.91699 1.66675 5.57324 1.66675 10.0003C1.66675 11.1497 2.45578 13.1016 2.5771 13.3949C2.5878 13.4207 2.59839 13.4444 2.60802 13.4706C2.69194 13.6996 3.04282 14.9364 1.66675 16.7684C3.5186 17.6538 5.48526 16.1982 5.48526 16.1982C6.84592 16.9202 8.46491 17.0837 10.0001 17.0837C15.0927 17.0837 18.3334 14.4274 18.3334 10.0003Z" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"align-right": `<path d="M12.292 6.04167L16.2503 9.99998L12.292 13.9583M2.91699 9.99998H15.6253M17.0837 3.75V16.25" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
expand: `<path d="M4.58301 10.4163V15.4163H9.58301M10.4163 4.58301H15.4163V9.58301" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
collapse: `<path d="M16.666 8.33398H11.666V3.33398" stroke="currentColor" stroke-linecap="square"/><path d="M8.33398 16.666V11.666H3.33398" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
}
|
||||
|
||||
export interface IconProps extends ComponentProps<"svg"> {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
padding: 4px 12px;
|
||||
text-align: left;
|
||||
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-md);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
&[data-active="true"] {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
gap: 12px;
|
||||
align-self: stretch;
|
||||
|
||||
border-radius: 8px;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface-base);
|
||||
|
||||
[data-slot="input-container"] {
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
align-items: center;
|
||||
|
||||
&[data-active="true"] {
|
||||
border-radius: 8px;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface-raised-base-hover);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
[data-component="select-content"] {
|
||||
min-width: 4rem;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
border-radius: var(--radius-md);
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: var(--border-weak-base);
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 6px 0 6px;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
|
||||
/* text-12-medium */
|
||||
font-family: var(--font-family-sans);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
background-color: var(--background-stronger);
|
||||
overflow: clip;
|
||||
|
||||
[data-slot="list"] {
|
||||
[data-slot="tabs-list"] {
|
||||
height: 48px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
|
@ -36,12 +36,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
[data-slot="trigger"] {
|
||||
[data-slot="tabs-trigger-wrapper"] {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
padding: 14px 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
color: var(--text-base);
|
||||
|
||||
/* text-14-medium */
|
||||
|
|
@ -58,6 +58,23 @@
|
|||
border-right: 1px solid var(--border-weak-base);
|
||||
background-color: var(--background-base);
|
||||
|
||||
[data-slot="tabs-trigger"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 14px 24px;
|
||||
}
|
||||
|
||||
[data-slot="tabs-trigger-close-button"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
[data-component="icon-button"] {
|
||||
margin: -0.25rem;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
pointer-events: none;
|
||||
color: var(--text-weaker);
|
||||
|
|
@ -66,17 +83,38 @@
|
|||
outline: none;
|
||||
box-shadow: 0 0 0 2px var(--border-focus);
|
||||
}
|
||||
&[data-selected] {
|
||||
&:has([data-hidden]) {
|
||||
[data-slot="tabs-trigger-close-button"] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
[data-slot="tabs-trigger-close-button"] {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:has([data-selected]) {
|
||||
color: var(--text-strong);
|
||||
background-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
[data-slot="tabs-trigger-close-button"] {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&:hover:not(:disabled):not([data-selected]) {
|
||||
color: var(--text-strong);
|
||||
}
|
||||
&:has([data-slot="tabs-trigger-close-button"]) {
|
||||
padding-right: 12px;
|
||||
|
||||
[data-slot="tabs-trigger"] {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="content"] {
|
||||
[data-slot="tabs-content"] {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import { Tabs as Kobalte } from "@kobalte/core/tabs"
|
||||
import { splitProps } from "solid-js"
|
||||
import { Show, splitProps, type JSX } from "solid-js"
|
||||
import type { ComponentProps, ParentProps } from "solid-js"
|
||||
|
||||
export interface TabsProps extends ComponentProps<typeof Kobalte> {}
|
||||
export interface TabsListProps extends ComponentProps<typeof Kobalte.List> {}
|
||||
export interface TabsTriggerProps extends ComponentProps<typeof Kobalte.Trigger> {}
|
||||
export interface TabsTriggerProps extends ComponentProps<typeof Kobalte.Trigger> {
|
||||
hideCloseButton?: boolean
|
||||
closeButton?: JSX.Element
|
||||
}
|
||||
export interface TabsContentProps extends ComponentProps<typeof Kobalte.Content> {}
|
||||
|
||||
function TabsRoot(props: TabsProps) {
|
||||
|
|
@ -26,7 +29,7 @@ function TabsList(props: TabsListProps) {
|
|||
return (
|
||||
<Kobalte.List
|
||||
{...rest}
|
||||
data-slot="list"
|
||||
data-slot="tabs-list"
|
||||
classList={{
|
||||
...(split.classList ?? {}),
|
||||
[split.class ?? ""]: !!split.class,
|
||||
|
|
@ -36,18 +39,26 @@ function TabsList(props: TabsListProps) {
|
|||
}
|
||||
|
||||
function TabsTrigger(props: ParentProps<TabsTriggerProps>) {
|
||||
const [split, rest] = splitProps(props, ["class", "classList", "children"])
|
||||
const [split, rest] = splitProps(props, ["class", "classList", "children", "closeButton", "hideCloseButton"])
|
||||
return (
|
||||
<Kobalte.Trigger
|
||||
{...rest}
|
||||
data-slot="trigger"
|
||||
<div
|
||||
data-slot="tabs-trigger-wrapper"
|
||||
classList={{
|
||||
...(split.classList ?? {}),
|
||||
[split.class ?? ""]: !!split.class,
|
||||
}}
|
||||
>
|
||||
{split.children}
|
||||
</Kobalte.Trigger>
|
||||
<Kobalte.Trigger {...rest} data-slot="tabs-trigger" class="group/tab">
|
||||
{split.children}
|
||||
</Kobalte.Trigger>
|
||||
<Show when={split.closeButton}>
|
||||
{(closeButton) => (
|
||||
<div data-slot="tabs-trigger-close-button" data-hidden={split.hideCloseButton}>
|
||||
{closeButton()}
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +67,7 @@ function TabsContent(props: ParentProps<TabsContentProps>) {
|
|||
return (
|
||||
<Kobalte.Content
|
||||
{...rest}
|
||||
data-slot="content"
|
||||
data-slot="tabs-content"
|
||||
classList={{
|
||||
...(split.classList ?? {}),
|
||||
[split.class ?? ""]: !!split.class,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
[data-component="tooltip"] {
|
||||
z-index: 1000;
|
||||
max-width: 320px;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-md);
|
||||
background-color: var(--surface-float-base);
|
||||
color: rgba(253, 252, 252, 0.94);
|
||||
padding: 2px 8px;
|
||||
|
|
|
|||
|
|
@ -493,4 +493,52 @@
|
|||
--blue-light-alpha-10: #007feff9;
|
||||
--blue-light-alpha-11: #0066dbf9;
|
||||
--blue-light-alpha-12: #002047f9;
|
||||
--ink-dark-1: #101313;
|
||||
--ink-dark-2: #181b1b;
|
||||
--ink-dark-3: #212525;
|
||||
--ink-dark-4: #282d2d;
|
||||
--ink-dark-5: #303434;
|
||||
--ink-dark-6: #393e3e;
|
||||
--ink-dark-7: #464b4b;
|
||||
--ink-dark-8: #5f6464;
|
||||
--ink-dark-9: #6b7171;
|
||||
--ink-dark-10: #797f7f;
|
||||
--ink-dark-11: #b1b7b7;
|
||||
--ink-dark-12: #ecf1f1;
|
||||
--ink-light-1: #fcfdfd;
|
||||
--ink-light-2: #f8f9f9;
|
||||
--ink-light-3: #f0f1f1;
|
||||
--ink-light-4: #e8e9e9;
|
||||
--ink-light-5: #e0e2e2;
|
||||
--ink-light-6: #d9dada;
|
||||
--ink-light-7: #cdcfcf;
|
||||
--ink-light-8: #bbbcbc;
|
||||
--ink-light-9: #8b8e8e;
|
||||
--ink-light-10: #818484;
|
||||
--ink-light-11: #636565;
|
||||
--ink-light-12: #1e2121;
|
||||
--ink-dark-alpha-1: #38828203;
|
||||
--ink-dark-alpha-2: #c6e6e60b;
|
||||
--ink-dark-alpha-3: #d5eded16;
|
||||
--ink-dark-alpha-4: #e1f2f21e;
|
||||
--ink-dark-alpha-5: #e8f5f526;
|
||||
--ink-dark-alpha-6: #e8f5f531;
|
||||
--ink-dark-alpha-7: #ecf7f73f;
|
||||
--ink-dark-alpha-8: #f5fafa59;
|
||||
--ink-dark-alpha-9: #f4fafa67;
|
||||
--ink-dark-alpha-10: #f5fbfb76;
|
||||
--ink-dark-alpha-11: #f9fcfcb2;
|
||||
--ink-dark-alpha-12: #fbfdfdf0;
|
||||
--ink-light-alpha-1: #00555503;
|
||||
--ink-light-alpha-2: #00252507;
|
||||
--ink-light-alpha-3: #0011110f;
|
||||
--ink-light-alpha-4: #000c0c17;
|
||||
--ink-light-alpha-5: #0011111f;
|
||||
--ink-light-alpha-6: #00070726;
|
||||
--ink-light-alpha-7: #000b0b32;
|
||||
--ink-light-alpha-8: #00040444;
|
||||
--ink-light-alpha-9: #00070774;
|
||||
--ink-light-alpha-10: #0004049c;
|
||||
--ink-light-alpha-11: #0007077e;
|
||||
--ink-light-alpha-12: #000202df;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,9 +49,9 @@
|
|||
--color-surface-info-base: var(--surface-info-base);
|
||||
--color-surface-info-weak: var(--surface-info-weak);
|
||||
--color-surface-info-strong: var(--surface-info-strong);
|
||||
--color-surface-diff-unchanged-base: var(--surface-diff-unchanged-base);
|
||||
--color-surface-diff-skip-base: var(--surface-diff-skip-base);
|
||||
--color-surface-diff-hidden-base: var(--surface-diff-hidden-base);
|
||||
--color-surface-diff-unchanged-base: var(--surface-diff-unchanged-base);
|
||||
--color-surface-diff-hidden-weak: var(--surface-diff-hidden-weak);
|
||||
--color-surface-diff-hidden-weaker: var(--surface-diff-hidden-weaker);
|
||||
--color-surface-diff-hidden-strong: var(--surface-diff-hidden-strong);
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
--color-text-on-brand-weaker: var(--text-on-brand-weaker);
|
||||
--color-text-on-brand-strong: var(--text-on-brand-strong);
|
||||
--color-button-secondary-base: var(--button-secondary-base);
|
||||
--color-button-secondary-base-hover: var(--button-secondary-base-hover);
|
||||
--color-button-secondary-hover: var(--button-secondary-hover);
|
||||
--color-border-base: var(--border-base);
|
||||
--color-border-hover: var(--border-hover);
|
||||
--color-border-active: var(--border-active);
|
||||
|
|
@ -199,21 +199,21 @@
|
|||
--color-syntax-regexp: var(--syntax-regexp);
|
||||
--color-syntax-string: var(--syntax-string);
|
||||
--color-syntax-keyword: var(--syntax-keyword);
|
||||
--color-syntax-function: var(--syntax-function);
|
||||
--color-syntax-number: var(--syntax-number);
|
||||
--color-syntax-primitive: var(--syntax-primitive);
|
||||
--color-syntax-operator: var(--syntax-operator);
|
||||
--color-syntax-variable: var(--syntax-variable);
|
||||
--color-syntax-property: var(--syntax-property);
|
||||
--color-syntax-parameter: var(--syntax-parameter);
|
||||
--color-syntax-type: var(--syntax-type);
|
||||
--color-syntax-constant: var(--syntax-constant);
|
||||
--color-syntax-punctuation: var(--syntax-punctuation);
|
||||
--color-syntax-namespace: var(--syntax-namespace);
|
||||
--color-syntax-enum: var(--syntax-enum);
|
||||
--color-syntax-object: var(--syntax-object);
|
||||
--color-syntax-success: var(--syntax-success);
|
||||
--color-syntax-warning: var(--syntax-warning);
|
||||
--color-syntax-critical: var(--syntax-critical);
|
||||
--color-syntax-info: var(--syntax-info);
|
||||
--color-syntax-diff-add: var(--syntax-diff-add);
|
||||
--color-syntax-diff-delete: var(--syntax-diff-delete);
|
||||
--color-syntax-diff-unknown: var(--syntax-diff-unknown);
|
||||
--color-markdown-heading: var(--markdown-heading);
|
||||
--color-markdown-text: var(--markdown-text);
|
||||
--color-markdown-link: var(--markdown-link);
|
||||
|
|
|
|||
|
|
@ -40,14 +40,8 @@
|
|||
--container-6xl: 72rem;
|
||||
--container-7xl: 80rem;
|
||||
|
||||
--radius-xs: 0.125rem;
|
||||
--radius-sm: 0.25rem;
|
||||
--radius-md: 0.375rem;
|
||||
--radius-lg: 0.5rem;
|
||||
--radius-xl: 0.75rem;
|
||||
--radius-2xl: 1rem;
|
||||
--radius-3xl: 1.5rem;
|
||||
--radius-4xl: 2rem;
|
||||
|
||||
--shadow-xs:
|
||||
0 1px 2px -1px rgba(19, 16, 16, 0.04), 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08);
|
||||
|
|
@ -65,19 +59,15 @@
|
|||
0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12), 0 0 0 2px var(--background-weak, #f1f0f0),
|
||||
0 0 0 3px var(--border-selected, rgba(0, 74, 255, 0.99));
|
||||
|
||||
--text-mix-blend-mode: multiply;
|
||||
}
|
||||
|
||||
:root {
|
||||
/* OC-1-Light */
|
||||
--text-mix-blend-mode: multiply;
|
||||
|
||||
color-scheme: light;
|
||||
--text-mix-blend-mode: multiply;
|
||||
|
||||
/* OC-1-light */
|
||||
--background-base: #f8f7f7;
|
||||
--background-weak: var(--smoke-light-3);
|
||||
--background-strong: var(--smoke-light-1);
|
||||
--background-stronger: #fcfcfc;
|
||||
--surface-base: var(--smoke-light-3);
|
||||
--surface-base: var(--smoke-light-alpha-2);
|
||||
--base: var(--smoke-light-alpha-2);
|
||||
--surface-base-hover: #0500000f;
|
||||
--surface-base-active: var(--smoke-light-alpha-3);
|
||||
|
|
@ -119,9 +109,9 @@
|
|||
--surface-info-base: var(--lilac-light-3);
|
||||
--surface-info-weak: var(--lilac-light-2);
|
||||
--surface-info-strong: var(--lilac-light-9);
|
||||
--surface-diff-unchanged-base: #ffffff00;
|
||||
--surface-diff-skip-base: var(--smoke-light-2);
|
||||
--surface-diff-hidden-base: var(--blue-light-3);
|
||||
--surface-diff-unchanged-base: #ffffff00;
|
||||
--surface-diff-hidden-weak: var(--blue-light-2);
|
||||
--surface-diff-hidden-weaker: var(--blue-light-1);
|
||||
--surface-diff-hidden-strong: var(--blue-light-5);
|
||||
|
|
@ -170,7 +160,7 @@
|
|||
--text-on-brand-weaker: var(--smoke-light-alpha-8);
|
||||
--text-on-brand-strong: var(--smoke-light-alpha-12);
|
||||
--button-secondary-base: #fdfcfc;
|
||||
--button-secondary-hover: #fafafa;
|
||||
--button-secondary-hover: #faf9f9;
|
||||
--border-base: var(--smoke-light-alpha-7);
|
||||
--border-hover: var(--smoke-light-alpha-8);
|
||||
--border-active: var(--smoke-light-alpha-9);
|
||||
|
|
@ -269,21 +259,21 @@
|
|||
--syntax-regexp: var(--text-base);
|
||||
--syntax-string: #007663;
|
||||
--syntax-keyword: var(--text-weak);
|
||||
--syntax-primitive: #f6916b;
|
||||
--syntax-primitive: #fb7f51;
|
||||
--syntax-operator: var(--text-weak);
|
||||
--syntax-variable: var(--text-strong);
|
||||
--syntax-property: #ed6dc8;
|
||||
--syntax-type: #b2cc00;
|
||||
--syntax-constant: #0092a8;
|
||||
--syntax-property: #ec6cc8;
|
||||
--syntax-type: #738400;
|
||||
--syntax-constant: #00b2b9;
|
||||
--syntax-punctuation: var(--text-weaker);
|
||||
--syntax-object: var(--text-strong);
|
||||
--syntax-success: var(--apple-dark-10);
|
||||
--syntax-warning: var(--amber-dark-10);
|
||||
--syntax-critical: var(--ember-dark-9);
|
||||
--syntax-info: #0092a8;
|
||||
--syntax-diff-add: var(--mint-light-alpha-8);
|
||||
--syntax-diff-delete: var(--ember-light-alpha-8);
|
||||
--syntax-unknown: var(--text-weak);
|
||||
--syntax-success: var(--apple-light-10);
|
||||
--syntax-warning: var(--amber-light-10);
|
||||
--syntax-critical: var(--ember-light-9);
|
||||
--syntax-info: #0091a7;
|
||||
--syntax-diff-add: var(--mint-light-11);
|
||||
--syntax-diff-delete: var(--ember-light-11);
|
||||
--syntax-diff-unknown: #ff0000;
|
||||
--markdown-heading: #d68c27;
|
||||
--markdown-text: #1a1a1a;
|
||||
--markdown-link: #3b7dd8;
|
||||
|
|
@ -309,15 +299,15 @@
|
|||
--button-ghost-hover2: var(--smoke-light-alpha-3);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
color-scheme: dark;
|
||||
--text-mix-blend-mode: plus-lighter;
|
||||
|
||||
/* OC-1-Dark */
|
||||
color-scheme: dark;
|
||||
/* OC-1-dark */
|
||||
--background-base: var(--smoke-dark-1);
|
||||
--background-weak: #1b1818;
|
||||
--background-weak: #1c1717;
|
||||
--background-strong: #151313;
|
||||
--background-stronger: #191515;
|
||||
--surface-base: var(--smoke-dark-3);
|
||||
--surface-base: var(--smoke-dark-alpha-2);
|
||||
--base: var(--smoke-dark-alpha-2);
|
||||
--surface-base-hover: #e0b7b716;
|
||||
--surface-base-active: var(--smoke-dark-alpha-3);
|
||||
|
|
@ -359,9 +349,9 @@
|
|||
--surface-info-base: var(--lilac-light-3);
|
||||
--surface-info-weak: var(--lilac-light-2);
|
||||
--surface-info-strong: var(--lilac-light-9);
|
||||
--surface-diff-unchanged-base: var(--smoke-dark-1);
|
||||
--surface-diff-skip-base: var(--smoke-dark-alpha-1);
|
||||
--surface-diff-hidden-base: var(--blue-dark-2);
|
||||
--surface-diff-unchanged-base: var(--smoke-dark-1);
|
||||
--surface-diff-hidden-weak: var(--blue-dark-1);
|
||||
--surface-diff-hidden-weaker: var(--blue-dark-3);
|
||||
--surface-diff-hidden-strong: var(--blue-dark-5);
|
||||
|
|
@ -417,7 +407,7 @@
|
|||
--border-selected: var(--cobalt-dark-alpha-11);
|
||||
--border-disabled: var(--smoke-dark-alpha-8);
|
||||
--border-focus: var(--smoke-dark-alpha-9);
|
||||
--border-weak-base: var(--smoke-dark-alpha-4);
|
||||
--border-weak-base: var(--smoke-dark-alpha-6);
|
||||
--border-strong-base: var(--smoke-dark-alpha-8);
|
||||
--border-strong-hover: var(--smoke-dark-alpha-7);
|
||||
--border-strong-active: var(--smoke-dark-alpha-8);
|
||||
|
|
@ -519,11 +509,11 @@
|
|||
--syntax-object: var(--text-strong);
|
||||
--syntax-success: var(--apple-dark-10);
|
||||
--syntax-warning: var(--amber-dark-10);
|
||||
--syntax-critical: var(--ember-dark-9);
|
||||
--syntax-info: #ff9ae2;
|
||||
--syntax-diff-add: var(--mint-dark-alpha-6);
|
||||
--syntax-diff-delete: var(--ember-dark-alpha-4);
|
||||
--syntax-unknown: var(--text-weak);
|
||||
--syntax-critical: var(--ember-dark-10);
|
||||
--syntax-info: #93e9f6;
|
||||
--syntax-diff-add: var(--mint-dark-11);
|
||||
--syntax-diff-delete: var(--ember-dark-11);
|
||||
--syntax-diff-unknown: #ff0000;
|
||||
--markdown-heading: #9d7cd8;
|
||||
--markdown-text: #eeeeee;
|
||||
--markdown-link: #fab283;
|
||||
|
|
@ -549,3 +539,478 @@
|
|||
--button-ghost-hover2: var(--smoke-dark-alpha-3);
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme="oc-2-paper"] {
|
||||
/* OC-2-paper */
|
||||
--background-base: #f7f8f8;
|
||||
--background-weak: var(--ink-light-3);
|
||||
--background-strong: var(--ink-light-1);
|
||||
--background-stronger: #fcfcfc;
|
||||
--surface-base: var(--ink-light-alpha-2);
|
||||
--base: var(--ink-light-alpha-2);
|
||||
--surface-base-hover: #0005050f;
|
||||
--surface-base-active: var(--ink-light-alpha-3);
|
||||
--surface-base-interactive-active: var(--cobalt-light-alpha-3);
|
||||
--base2: var(--ink-light-alpha-2);
|
||||
--base3: var(--ink-light-alpha-2);
|
||||
--surface-inset-base: var(--ink-light-alpha-2);
|
||||
--surface-inset-base-hover: var(--ink-light-alpha-3);
|
||||
--surface-inset-strong: #001f1f17;
|
||||
--surface-inset-strong-hover: #001f1f17;
|
||||
--surface-raised-base: var(--ink-light-alpha-1);
|
||||
--surface-float-base: var(--ink-dark-1);
|
||||
--surface-float-base-hover: var(--ink-dark-2);
|
||||
--surface-raised-base-hover: var(--ink-light-alpha-2);
|
||||
--surface-raised-base-active: var(--ink-light-alpha-3);
|
||||
--surface-raised-strong: var(--ink-light-1);
|
||||
--surface-raised-strong-hover: var(--white);
|
||||
--surface-raised-stronger: var(--white);
|
||||
--surface-raised-stronger-hover: var(--white);
|
||||
--surface-weak: var(--ink-light-alpha-3);
|
||||
--surface-weaker: var(--ink-light-alpha-4);
|
||||
--surface-strong: #ffffff;
|
||||
--surface-raised-stronger-non-alpha: var(--white);
|
||||
--surface-brand-base: var(--yuzu-light-9);
|
||||
--surface-brand-hover: var(--yuzu-light-10);
|
||||
--surface-interactive-base: var(--cobalt-light-3);
|
||||
--surface-interactive-hover: var(--cobalt-light-4);
|
||||
--surface-interactive-weak: var(--cobalt-light-2);
|
||||
--surface-interactive-weak-hover: var(--cobalt-light-3);
|
||||
--surface-success-base: var(--apple-light-3);
|
||||
--surface-success-weak: var(--apple-light-2);
|
||||
--surface-success-strong: var(--apple-light-9);
|
||||
--surface-warning-base: var(--solaris-light-3);
|
||||
--surface-warning-weak: var(--solaris-light-2);
|
||||
--surface-warning-strong: var(--solaris-light-9);
|
||||
--surface-critical-base: var(--ember-light-3);
|
||||
--surface-critical-weak: var(--ember-light-2);
|
||||
--surface-critical-strong: var(--ember-light-9);
|
||||
--surface-info-base: var(--lilac-light-3);
|
||||
--surface-info-weak: var(--lilac-light-2);
|
||||
--surface-info-strong: var(--lilac-light-9);
|
||||
--surface-diff-unchanged-base: #ffffff00;
|
||||
--surface-diff-skip-base: var(--ink-light-2);
|
||||
--surface-diff-hidden-base: var(--blue-light-3);
|
||||
--surface-diff-hidden-weak: var(--blue-light-2);
|
||||
--surface-diff-hidden-weaker: var(--blue-light-1);
|
||||
--surface-diff-hidden-strong: var(--blue-light-5);
|
||||
--surface-diff-hidden-stronger: var(--blue-light-9);
|
||||
--surface-diff-add-base: var(--mint-light-3);
|
||||
--surface-diff-add-weak: var(--mint-light-2);
|
||||
--surface-diff-add-weaker: var(--mint-light-1);
|
||||
--surface-diff-add-strong: var(--mint-light-5);
|
||||
--surface-diff-add-stronger: var(--mint-light-9);
|
||||
--surface-diff-delete-base: var(--ember-light-3);
|
||||
--surface-diff-delete-weak: var(--ember-light-2);
|
||||
--surface-diff-delete-weaker: var(--ember-light-1);
|
||||
--surface-diff-delete-strong: var(--ember-light-6);
|
||||
--surface-diff-delete-stronger: var(--ember-light-9);
|
||||
--text-base: var(--ink-light-11);
|
||||
--input-base: var(--ink-light-1);
|
||||
--input-hover: var(--ink-light-2);
|
||||
--input-active: var(--cobalt-light-1);
|
||||
--input-selected: var(--cobalt-light-4);
|
||||
--input-focus: var(--cobalt-light-1);
|
||||
--input-disabled: var(--ink-light-4);
|
||||
--text-weak: var(--ink-light-9);
|
||||
--text-weaker: var(--ink-light-8);
|
||||
--text-strong: var(--ink-light-12);
|
||||
--text-interactive-base: var(--cobalt-light-9);
|
||||
--text-on-brand-base: var(--ink-light-alpha-11);
|
||||
--text-on-interactive-base: var(--ink-light-1);
|
||||
--text-on-interactive-weak: var(--ink-light-alpha-11);
|
||||
--text-on-success-base: var(--apple-light-10);
|
||||
--text-on-critical-base: var(--ember-light-10);
|
||||
--text-on-critical-weak: var(--ember-light-8);
|
||||
--text-on-critical-strong: var(--ember-light-12);
|
||||
--text-on-warning-base: var(--ink-dark-alpha-11);
|
||||
--text-on-info-base: var(--ink-dark-alpha-11);
|
||||
--text-diff-add-base: var(--mint-light-11);
|
||||
--text-diff-delete-base: var(--ember-light-10);
|
||||
--text-diff-delete-strong: var(--ember-light-12);
|
||||
--text-diff-add-strong: var(--mint-light-12);
|
||||
--text-on-info-weak: var(--ink-dark-alpha-9);
|
||||
--text-on-info-strong: var(--ink-dark-alpha-12);
|
||||
--text-on-warning-weak: var(--ink-dark-alpha-9);
|
||||
--text-on-warning-strong: var(--ink-dark-alpha-12);
|
||||
--text-on-success-weak: var(--apple-light-6);
|
||||
--text-on-success-strong: var(--apple-light-12);
|
||||
--text-on-brand-weak: var(--ink-light-alpha-9);
|
||||
--text-on-brand-weaker: var(--ink-light-alpha-8);
|
||||
--text-on-brand-strong: var(--ink-light-alpha-12);
|
||||
--button-secondary-base: #fcfdfd;
|
||||
--button-secondary-hover: #f9fafa;
|
||||
--border-base: var(--ink-light-alpha-7);
|
||||
--border-hover: var(--ink-light-alpha-8);
|
||||
--border-active: var(--ink-light-alpha-9);
|
||||
--border-selected: var(--cobalt-light-alpha-9);
|
||||
--border-disabled: var(--ink-light-alpha-8);
|
||||
--border-focus: var(--ink-light-alpha-9);
|
||||
--border-weak-base: var(--ink-light-alpha-5);
|
||||
--border-strong-base: var(--ink-light-alpha-7);
|
||||
--border-strong-hover: var(--ink-light-alpha-8);
|
||||
--border-strong-active: var(--ink-light-alpha-7);
|
||||
--border-strong-selected: var(--cobalt-light-alpha-6);
|
||||
--border-strong-disabled: var(--ink-light-alpha-6);
|
||||
--border-strong-focus: var(--ink-light-alpha-7);
|
||||
--border-weak-hover: var(--ink-light-alpha-6);
|
||||
--border-weak-active: var(--ink-light-alpha-7);
|
||||
--border-weak-selected: var(--cobalt-light-alpha-5);
|
||||
--border-weak-disabled: var(--ink-light-alpha-6);
|
||||
--border-weak-focus: var(--ink-light-alpha-7);
|
||||
--border-interactive-base: var(--cobalt-light-7);
|
||||
--border-interactive-hover: var(--cobalt-light-8);
|
||||
--border-interactive-active: var(--cobalt-light-9);
|
||||
--border-interactive-selected: var(--cobalt-light-9);
|
||||
--border-interactive-disabled: var(--ink-light-8);
|
||||
--border-interactive-focus: var(--cobalt-light-9);
|
||||
--border-success-base: var(--apple-light-6);
|
||||
--border-success-hover: var(--apple-light-7);
|
||||
--border-success-selected: var(--apple-light-9);
|
||||
--border-warning-base: var(--solaris-light-6);
|
||||
--border-warning-hover: var(--solaris-light-7);
|
||||
--border-warning-selected: var(--solaris-light-9);
|
||||
--border-critical-base: var(--ember-light-6);
|
||||
--border-critical-hover: var(--ember-light-7);
|
||||
--border-critical-selected: var(--ember-light-9);
|
||||
--border-info-base: var(--lilac-light-6);
|
||||
--border-info-hover: var(--lilac-light-7);
|
||||
--border-info-selected: var(--lilac-light-9);
|
||||
--icon-base: var(--ink-light-9);
|
||||
--icon-hover: var(--ink-light-11);
|
||||
--icon-active: var(--ink-light-12);
|
||||
--icon-selected: var(--ink-light-12);
|
||||
--icon-disabled: var(--ink-light-8);
|
||||
--icon-focus: var(--ink-light-12);
|
||||
--icon-invert-base: #ffffff;
|
||||
--icon-weak-base: var(--ink-light-7);
|
||||
--icon-weak-hover: var(--ink-light-8);
|
||||
--icon-weak-active: var(--ink-light-9);
|
||||
--icon-weak-selected: var(--ink-light-10);
|
||||
--icon-weak-disabled: var(--ink-light-6);
|
||||
--icon-weak-focus: var(--ink-light-9);
|
||||
--icon-strong-base: var(--ink-light-12);
|
||||
--icon-strong-hover: #131515;
|
||||
--icon-strong-active: #020202;
|
||||
--icon-strong-selected: #020202;
|
||||
--icon-strong-disabled: var(--ink-light-8);
|
||||
--icon-strong-focus: #020202;
|
||||
--icon-brand-base: var(--ink-light-12);
|
||||
--icon-interactive-base: var(--cobalt-light-9);
|
||||
--icon-success-base: var(--apple-light-7);
|
||||
--icon-success-hover: var(--apple-light-8);
|
||||
--icon-success-active: var(--apple-light-11);
|
||||
--icon-warning-base: var(--amber-light-7);
|
||||
--icon-warning-hover: var(--amber-light-8);
|
||||
--icon-warning-active: var(--amber-light-11);
|
||||
--icon-critical-base: var(--ember-light-10);
|
||||
--icon-critical-hover: var(--ember-light-11);
|
||||
--icon-critical-active: var(--ember-light-12);
|
||||
--icon-info-base: var(--lilac-light-7);
|
||||
--icon-info-hover: var(--lilac-light-8);
|
||||
--icon-info-active: var(--lilac-light-11);
|
||||
--icon-on-brand-base: var(--ink-light-alpha-11);
|
||||
--icon-on-brand-hover: var(--ink-light-alpha-12);
|
||||
--icon-on-brand-selected: var(--ink-light-alpha-12);
|
||||
--icon-on-interactive-base: var(--ink-light-1);
|
||||
--icon-agent-plan-base: var(--purple-light-9);
|
||||
--icon-agent-docs-base: var(--amber-light-9);
|
||||
--icon-agent-ask-base: var(--cyan-light-9);
|
||||
--icon-agent-build-base: var(--cobalt-light-9);
|
||||
--icon-on-success-base: var(--apple-light-alpha-9);
|
||||
--icon-on-success-hover: var(--apple-light-alpha-10);
|
||||
--icon-on-success-selected: var(--apple-light-alpha-11);
|
||||
--icon-on-warning-base: var(--amber-lightalpha-9);
|
||||
--icon-on-warning-hover: var(--amber-lightalpha-10);
|
||||
--icon-on-warning-selected: var(--amber-lightalpha-11);
|
||||
--icon-on-critical-base: var(--ember-light-alpha-9);
|
||||
--icon-on-critical-hover: var(--ember-light-alpha-10);
|
||||
--icon-on-critical-selected: var(--ember-light-alpha-11);
|
||||
--icon-on-info-base: var(--lilac-light-9);
|
||||
--icon-on-info-hover: var(--lilac-light-alpha-10);
|
||||
--icon-on-info-selected: var(--lilac-light-alpha-11);
|
||||
--icon-diff-add-base: var(--mint-light-11);
|
||||
--icon-diff-add-hover: var(--mint-light-12);
|
||||
--icon-diff-add-active: var(--mint-light-12);
|
||||
--icon-diff-delete-base: var(--ember-light-10);
|
||||
--icon-diff-delete-hover: var(--ember-light-11);
|
||||
--syntax-comment: var(--text-weaker);
|
||||
--syntax-regexp: var(--text-base);
|
||||
--syntax-string: #007663;
|
||||
--syntax-keyword: var(--text-weak);
|
||||
--syntax-primitive: #fb7f51;
|
||||
--syntax-operator: var(--text-weak);
|
||||
--syntax-variable: var(--text-strong);
|
||||
--syntax-property: #ec6cc8;
|
||||
--syntax-type: #738400;
|
||||
--syntax-constant: #00b2b9;
|
||||
--syntax-punctuation: var(--text-weaker);
|
||||
--syntax-object: var(--text-strong);
|
||||
--syntax-success: var(--apple-light-10);
|
||||
--syntax-warning: var(--amber-light-10);
|
||||
--syntax-critical: var(--ember-light-9);
|
||||
--syntax-info: #0091a7;
|
||||
--syntax-diff-add: var(--mint-light-11);
|
||||
--syntax-diff-delete: var(--ember-light-11);
|
||||
--syntax-diff-unknown: #ff0000;
|
||||
--markdown-heading: #d68c27;
|
||||
--markdown-text: #1a1a1a;
|
||||
--markdown-link: #3b7dd8;
|
||||
--markdown-link-text: #318795;
|
||||
--markdown-code: #3d9a57;
|
||||
--markdown-block-quote: #b0851f;
|
||||
--markdown-emph: #b0851f;
|
||||
--markdown-strong: #d68c27;
|
||||
--markdown-horizontal-rule: #8a8a8a;
|
||||
--markdown-list-item: #3b7dd8;
|
||||
--markdown-list-enumeration: #318795;
|
||||
--markdown-image: #3b7dd8;
|
||||
--markdown-image-text: #318795;
|
||||
--markdown-code-block: #1a1a1a;
|
||||
--border-color: #ffffff;
|
||||
--border-weaker-base: var(--ink-light-alpha-3);
|
||||
--border-weaker-hover: var(--ink-light-alpha-4);
|
||||
--border-weaker-active: var(--ink-light-alpha-6);
|
||||
--border-weaker-selected: var(--cobalt-light-alpha-4);
|
||||
--border-weaker-disabled: var(--ink-light-alpha-2);
|
||||
--border-weaker-focus: var(--ink-light-alpha-6);
|
||||
--button-ghost-hover: var(--ink-light-alpha-2);
|
||||
--button-ghost-hover2: var(--ink-light-alpha-3);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
--background-base: var(--ink-dark-1);
|
||||
--background-weak: #171c1c;
|
||||
--background-strong: #131515;
|
||||
--background-stronger: #151919;
|
||||
--surface-base: var(--ink-dark-alpha-2);
|
||||
--base: var(--ink-dark-alpha-2);
|
||||
--surface-base-hover: #b8e0e00f;
|
||||
--surface-base-active: var(--ink-dark-alpha-3);
|
||||
--surface-base-interactive-active: var(--cobalt-light-alpha-3);
|
||||
--base2: var(--ink-dark-alpha-2);
|
||||
--base3: var(--ink-dark-alpha-2);
|
||||
--surface-inset-base: #0b0e0e7f;
|
||||
--surface-inset-base-hover: #0b0e0e7f;
|
||||
--surface-inset-strong: #050606cc;
|
||||
--surface-inset-strong-hover: #050606cc;
|
||||
--surface-raised-base: var(--ink-light-alpha-1);
|
||||
--surface-float-base: var(--ink-dark-1);
|
||||
--surface-float-base-hover: var(--ink-dark-2);
|
||||
--surface-raised-base-hover: var(--ink-light-alpha-2);
|
||||
--surface-raised-base-active: var(--ink-light-alpha-3);
|
||||
--surface-raised-strong: var(--ink-light-1);
|
||||
--surface-raised-strong-hover: var(--white);
|
||||
--surface-raised-stronger: var(--white);
|
||||
--surface-raised-stronger-hover: var(--white);
|
||||
--surface-weak: var(--ink-dark-alpha-4);
|
||||
--surface-weaker: var(--ink-dark-alpha-5);
|
||||
--surface-strong: var(--ink-dark-alpha-7);
|
||||
--surface-raised-stronger-non-alpha: var(--white);
|
||||
--surface-brand-base: var(--yuzu-light-9);
|
||||
--surface-brand-hover: var(--yuzu-light-10);
|
||||
--surface-interactive-base: var(--cobalt-light-3);
|
||||
--surface-interactive-hover: var(--cobalt-light-4);
|
||||
--surface-interactive-weak: var(--cobalt-light-2);
|
||||
--surface-interactive-weak-hover: var(--cobalt-light-3);
|
||||
--surface-success-base: var(--apple-light-3);
|
||||
--surface-success-weak: var(--apple-light-2);
|
||||
--surface-success-strong: var(--apple-light-9);
|
||||
--surface-warning-base: var(--solaris-light-3);
|
||||
--surface-warning-weak: var(--solaris-light-2);
|
||||
--surface-warning-strong: var(--solaris-light-9);
|
||||
--surface-critical-base: var(--ember-light-3);
|
||||
--surface-critical-weak: var(--ember-light-2);
|
||||
--surface-critical-strong: var(--ember-light-9);
|
||||
--surface-info-base: var(--lilac-light-3);
|
||||
--surface-info-weak: var(--lilac-light-2);
|
||||
--surface-info-strong: var(--lilac-light-9);
|
||||
--surface-diff-unchanged-base: #ffffff00;
|
||||
--surface-diff-skip-base: var(--ink-light-2);
|
||||
--surface-diff-hidden-base: var(--blue-light-3);
|
||||
--surface-diff-hidden-weak: var(--blue-light-2);
|
||||
--surface-diff-hidden-weaker: var(--blue-light-1);
|
||||
--surface-diff-hidden-strong: var(--blue-light-5);
|
||||
--surface-diff-hidden-stronger: var(--blue-light-9);
|
||||
--surface-diff-add-base: var(--mint-light-3);
|
||||
--surface-diff-add-weak: var(--mint-light-2);
|
||||
--surface-diff-add-weaker: var(--mint-light-1);
|
||||
--surface-diff-add-strong: var(--mint-light-5);
|
||||
--surface-diff-add-stronger: var(--mint-light-9);
|
||||
--surface-diff-delete-base: var(--ember-light-3);
|
||||
--surface-diff-delete-weak: var(--ember-light-2);
|
||||
--surface-diff-delete-weaker: var(--ember-light-1);
|
||||
--surface-diff-delete-strong: var(--ember-light-6);
|
||||
--surface-diff-delete-stronger: var(--ember-light-9);
|
||||
--text-base: var(--ink-light-11);
|
||||
--input-base: var(--ink-light-1);
|
||||
--input-hover: var(--ink-light-2);
|
||||
--input-active: var(--cobalt-light-1);
|
||||
--input-selected: var(--cobalt-light-4);
|
||||
--input-focus: var(--cobalt-light-1);
|
||||
--input-disabled: var(--ink-light-4);
|
||||
--text-weak: var(--ink-light-9);
|
||||
--text-weaker: var(--ink-light-8);
|
||||
--text-strong: var(--ink-light-12);
|
||||
--text-interactive-base: var(--cobalt-light-9);
|
||||
--text-on-brand-base: var(--ink-light-alpha-11);
|
||||
--text-on-interactive-base: var(--ink-light-1);
|
||||
--text-on-interactive-weak: var(--ink-light-alpha-11);
|
||||
--text-on-success-base: var(--apple-light-10);
|
||||
--text-on-critical-base: var(--ember-light-10);
|
||||
--text-on-critical-weak: var(--ember-light-8);
|
||||
--text-on-critical-strong: var(--ember-light-12);
|
||||
--text-on-warning-base: var(--ink-dark-alpha-11);
|
||||
--text-on-info-base: var(--ink-dark-alpha-11);
|
||||
--text-diff-add-base: var(--mint-light-11);
|
||||
--text-diff-delete-base: var(--ember-light-10);
|
||||
--text-diff-delete-strong: var(--ember-light-12);
|
||||
--text-diff-add-strong: var(--mint-light-12);
|
||||
--text-on-info-weak: var(--ink-dark-alpha-9);
|
||||
--text-on-info-strong: var(--ink-dark-alpha-12);
|
||||
--text-on-warning-weak: var(--ink-dark-alpha-9);
|
||||
--text-on-warning-strong: var(--ink-dark-alpha-12);
|
||||
--text-on-success-weak: var(--apple-light-6);
|
||||
--text-on-success-strong: var(--apple-light-12);
|
||||
--text-on-brand-weak: var(--ink-light-alpha-9);
|
||||
--text-on-brand-weaker: var(--ink-light-alpha-8);
|
||||
--text-on-brand-strong: var(--ink-light-alpha-12);
|
||||
--button-secondary-base: #fcfdfd;
|
||||
--button-secondary-hover: #f9fafa;
|
||||
--border-base: var(--ink-light-alpha-7);
|
||||
--border-hover: var(--ink-light-alpha-8);
|
||||
--border-active: var(--ink-light-alpha-9);
|
||||
--border-selected: var(--cobalt-light-alpha-9);
|
||||
--border-disabled: var(--ink-light-alpha-8);
|
||||
--border-focus: var(--ink-light-alpha-9);
|
||||
--border-weak-base: var(--ink-light-alpha-5);
|
||||
--border-strong-base: var(--ink-light-alpha-7);
|
||||
--border-strong-hover: var(--ink-light-alpha-8);
|
||||
--border-strong-active: var(--ink-light-alpha-7);
|
||||
--border-strong-selected: var(--cobalt-light-alpha-6);
|
||||
--border-strong-disabled: var(--ink-light-alpha-6);
|
||||
--border-strong-focus: var(--ink-light-alpha-7);
|
||||
--border-weak-hover: var(--ink-light-alpha-6);
|
||||
--border-weak-active: var(--ink-light-alpha-7);
|
||||
--border-weak-selected: var(--cobalt-light-alpha-5);
|
||||
--border-weak-disabled: var(--ink-light-alpha-6);
|
||||
--border-weak-focus: var(--ink-light-alpha-7);
|
||||
--border-interactive-base: var(--cobalt-light-7);
|
||||
--border-interactive-hover: var(--cobalt-light-8);
|
||||
--border-interactive-active: var(--cobalt-light-9);
|
||||
--border-interactive-selected: var(--cobalt-light-9);
|
||||
--border-interactive-disabled: var(--ink-light-8);
|
||||
--border-interactive-focus: var(--cobalt-light-9);
|
||||
--border-success-base: var(--apple-light-6);
|
||||
--border-success-hover: var(--apple-light-7);
|
||||
--border-success-selected: var(--apple-light-9);
|
||||
--border-warning-base: var(--solaris-light-6);
|
||||
--border-warning-hover: var(--solaris-light-7);
|
||||
--border-warning-selected: var(--solaris-light-9);
|
||||
--border-critical-base: var(--ember-light-6);
|
||||
--border-critical-hover: var(--ember-light-7);
|
||||
--border-critical-selected: var(--ember-light-9);
|
||||
--border-info-base: var(--lilac-light-6);
|
||||
--border-info-hover: var(--lilac-light-7);
|
||||
--border-info-selected: var(--lilac-light-9);
|
||||
--icon-base: var(--ink-light-9);
|
||||
--icon-hover: var(--ink-light-11);
|
||||
--icon-active: var(--ink-light-12);
|
||||
--icon-selected: var(--ink-light-12);
|
||||
--icon-disabled: var(--ink-light-8);
|
||||
--icon-focus: var(--ink-light-12);
|
||||
--icon-invert-base: #ffffff;
|
||||
--icon-weak-base: var(--ink-light-7);
|
||||
--icon-weak-hover: var(--ink-light-8);
|
||||
--icon-weak-active: var(--ink-light-9);
|
||||
--icon-weak-selected: var(--ink-light-10);
|
||||
--icon-weak-disabled: var(--ink-light-6);
|
||||
--icon-weak-focus: var(--ink-light-9);
|
||||
--icon-strong-base: var(--ink-light-12);
|
||||
--icon-strong-hover: #131515;
|
||||
--icon-strong-active: #020202;
|
||||
--icon-strong-selected: #020202;
|
||||
--icon-strong-disabled: var(--ink-light-8);
|
||||
--icon-strong-focus: #020202;
|
||||
--icon-brand-base: var(--ink-light-12);
|
||||
--icon-interactive-base: var(--cobalt-light-9);
|
||||
--icon-success-base: var(--apple-light-7);
|
||||
--icon-success-hover: var(--apple-light-8);
|
||||
--icon-success-active: var(--apple-light-11);
|
||||
--icon-warning-base: var(--amber-light-7);
|
||||
--icon-warning-hover: var(--amber-light-8);
|
||||
--icon-warning-active: var(--amber-light-11);
|
||||
--icon-critical-base: var(--ember-light-10);
|
||||
--icon-critical-hover: var(--ember-light-11);
|
||||
--icon-critical-active: var(--ember-light-12);
|
||||
--icon-info-base: var(--lilac-light-7);
|
||||
--icon-info-hover: var(--lilac-light-8);
|
||||
--icon-info-active: var(--lilac-light-11);
|
||||
--icon-on-brand-base: var(--ink-light-alpha-11);
|
||||
--icon-on-brand-hover: var(--ink-light-alpha-12);
|
||||
--icon-on-brand-selected: var(--ink-light-alpha-12);
|
||||
--icon-on-interactive-base: var(--ink-light-1);
|
||||
--icon-agent-plan-base: var(--purple-light-9);
|
||||
--icon-agent-docs-base: var(--amber-light-9);
|
||||
--icon-agent-ask-base: var(--cyan-light-9);
|
||||
--icon-agent-build-base: var(--cobalt-light-9);
|
||||
--icon-on-success-base: var(--apple-light-alpha-9);
|
||||
--icon-on-success-hover: var(--apple-light-alpha-10);
|
||||
--icon-on-success-selected: var(--apple-light-alpha-11);
|
||||
--icon-on-warning-base: var(--amber-lightalpha-9);
|
||||
--icon-on-warning-hover: var(--amber-lightalpha-10);
|
||||
--icon-on-warning-selected: var(--amber-lightalpha-11);
|
||||
--icon-on-critical-base: var(--ember-light-alpha-9);
|
||||
--icon-on-critical-hover: var(--ember-light-alpha-10);
|
||||
--icon-on-critical-selected: var(--ember-light-alpha-11);
|
||||
--icon-on-info-base: var(--lilac-light-9);
|
||||
--icon-on-info-hover: var(--lilac-light-alpha-10);
|
||||
--icon-on-info-selected: var(--lilac-light-alpha-11);
|
||||
--icon-diff-add-base: var(--mint-light-11);
|
||||
--icon-diff-add-hover: var(--mint-light-12);
|
||||
--icon-diff-add-active: var(--mint-light-12);
|
||||
--icon-diff-delete-base: var(--ember-light-10);
|
||||
--icon-diff-delete-hover: var(--ember-light-11);
|
||||
--syntax-comment: var(--text-weaker);
|
||||
--syntax-regexp: var(--text-base);
|
||||
--syntax-string: #007663;
|
||||
--syntax-keyword: var(--text-weak);
|
||||
--syntax-primitive: #fb7f51;
|
||||
--syntax-operator: var(--text-weak);
|
||||
--syntax-variable: var(--text-strong);
|
||||
--syntax-property: #ec6cc8;
|
||||
--syntax-type: #738400;
|
||||
--syntax-constant: #00b2b9;
|
||||
--syntax-punctuation: var(--text-weaker);
|
||||
--syntax-object: var(--text-strong);
|
||||
--syntax-success: var(--apple-light-10);
|
||||
--syntax-warning: var(--amber-light-10);
|
||||
--syntax-critical: var(--ember-light-9);
|
||||
--syntax-info: #0091a7;
|
||||
--syntax-diff-add: var(--mint-light-11);
|
||||
--syntax-diff-delete: var(--ember-light-11);
|
||||
--syntax-diff-unknown: #ff0000;
|
||||
--markdown-heading: #d68c27;
|
||||
--markdown-text: #1a1a1a;
|
||||
--markdown-link: #3b7dd8;
|
||||
--markdown-link-text: #318795;
|
||||
--markdown-code: #3d9a57;
|
||||
--markdown-block-quote: #b0851f;
|
||||
--markdown-emph: #b0851f;
|
||||
--markdown-strong: #d68c27;
|
||||
--markdown-horizontal-rule: #8a8a8a;
|
||||
--markdown-list-item: #3b7dd8;
|
||||
--markdown-list-enumeration: #318795;
|
||||
--markdown-image: #3b7dd8;
|
||||
--markdown-image-text: #318795;
|
||||
--markdown-code-block: #1a1a1a;
|
||||
--border-color: #ffffff;
|
||||
--border-weaker-base: var(--ink-light-alpha-3);
|
||||
--border-weaker-hover: var(--ink-light-alpha-4);
|
||||
--border-weaker-active: var(--ink-light-alpha-6);
|
||||
--border-weaker-selected: var(--cobalt-light-alpha-4);
|
||||
--border-weaker-disabled: var(--ink-light-alpha-2);
|
||||
--border-weaker-focus: var(--ink-light-alpha-6);
|
||||
--button-ghost-hover: var(--ink-light-alpha-2);
|
||||
--button-ghost-hover2: var(--ink-light-alpha-3);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--theme-border-subtle);
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
* {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
|
|
|||
|
|
@ -93,11 +93,19 @@ You can configure TUI-specific settings through the `tui` option.
|
|||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"tui": {
|
||||
"scroll_speed": 3
|
||||
"scroll_speed": 3,
|
||||
"scroll_acceleration": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Available options:
|
||||
|
||||
- `scroll_acceleration.enabled` - Enable macOS-style scroll acceleration. **Takes precedence over `scroll_speed`.**
|
||||
- `scroll_speed` - Custom scroll speed multiplier (default: `1`, minimum: `1`). Ignored if `scroll_acceleration.enabled` is `true`.
|
||||
|
||||
[Learn more about using the TUI here](/docs/tui).
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -336,11 +336,15 @@ You can customize TUI behavior through your OpenCode config file.
|
|||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"tui": {
|
||||
"scroll_speed": 3
|
||||
"scroll_speed": 3,
|
||||
"scroll_acceleration": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
- `scroll_speed` - Controls how fast the TUI scrolls when using scroll commands (default: `2`, minimum: `1`)
|
||||
- `scroll_acceleration` - Enable macOS-style scroll acceleration for smooth, natural scrolling. When enabled, scroll speed increases with rapid scrolling gestures and stays precise for slower movements. **This setting takes precedence over `scroll_speed` and overrides it when enabled.**
|
||||
- `scroll_speed` - Controls how fast the TUI scrolls when using scroll commands (default: `1`, minimum: `1`). **Note: This is ignored if `scroll_acceleration.enabled` is set to `true`.**
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.0.62",
|
||||
"version": "1.0.65",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue