mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
Merge branch 'dev' into opentui
This commit is contained in:
commit
1ba238d149
28 changed files with 465 additions and 286 deletions
1
.github/workflows/publish.yml
vendored
1
.github/workflows/publish.yml
vendored
|
|
@ -58,6 +58,7 @@ jobs:
|
|||
./script/publish.ts
|
||||
env:
|
||||
OPENCODE_BUMP: ${{ inputs.bump }}
|
||||
OPENCODE_TAG: latest
|
||||
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||
AUR_KEY: ${{ secrets.AUR_KEY }}
|
||||
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
|
|
|||
2
.github/workflows/snapshot.yml
vendored
2
.github/workflows/snapshot.yml
vendored
|
|
@ -30,7 +30,5 @@ jobs:
|
|||
run: |
|
||||
./packages/opencode/script/publish.ts
|
||||
env:
|
||||
OPENCODE_SNAPSHOT: true
|
||||
OPENCODE_TAG: ${{ github.ref_name }}
|
||||
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
|
|
|||
14
bun.lock
14
bun.lock
|
|
@ -4,6 +4,7 @@
|
|||
"": {
|
||||
"name": "opencode",
|
||||
"dependencies": {
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -114,7 +115,7 @@
|
|||
},
|
||||
"packages/css": {
|
||||
"name": "@opencode-ai/css",
|
||||
"version": "0.15.0",
|
||||
"version": "0.15.2",
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
|
|
@ -220,6 +221,7 @@
|
|||
"@ai-sdk/google-vertex": "3.0.16",
|
||||
"@babel/core": "7.28.4",
|
||||
"@octokit/webhooks-types": "7.6.1",
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@parcel/watcher-win32-x64": "2.5.1",
|
||||
"@standard-schema/spec": "1.0.0",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
|
|
@ -246,6 +248,12 @@
|
|||
"typescript": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/script": {
|
||||
"name": "@opencode-ai/script",
|
||||
"devDependencies": {
|
||||
"@types/bun": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "0.15.2",
|
||||
|
|
@ -270,7 +278,7 @@
|
|||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "0.15.0",
|
||||
"version": "0.15.2",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/css": "workspace:*",
|
||||
|
|
@ -902,6 +910,8 @@
|
|||
|
||||
"@opencode-ai/plugin": ["@opencode-ai/plugin@workspace:packages/plugin"],
|
||||
|
||||
"@opencode-ai/script": ["@opencode-ai/script@workspace:packages/script"],
|
||||
|
||||
"@opencode-ai/sdk": ["@opencode-ai/sdk@workspace:packages/sdk/js"],
|
||||
|
||||
"@opencode-ai/slack": ["@opencode-ai/slack@workspace:packages/slack"],
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@
|
|||
"turbo": "2.5.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*"
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opencode-ai/script": "workspace:*"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { Show } from "solid-js"
|
||||
import { query, createAsync, RouteSectionProps, useParams, A } from "@solidjs/router"
|
||||
import "./workspace.css"
|
||||
import { IconWorkspaceLogo } from "../component/icon"
|
||||
|
|
@ -7,7 +6,6 @@ import { UserMenu } from "./user-menu"
|
|||
import { withActor } from "~/context/auth.withActor"
|
||||
import { User } from "@opencode-ai/console-core/user.js"
|
||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||
import { querySessionInfo } from "./workspace/common"
|
||||
|
||||
const getUserEmail = query(async (workspaceID: string) => {
|
||||
"use server"
|
||||
|
|
@ -21,7 +19,6 @@ const getUserEmail = query(async (workspaceID: string) => {
|
|||
export default function WorkspaceLayout(props: RouteSectionProps) {
|
||||
const params = useParams()
|
||||
const userEmail = createAsync(() => getUserEmail(params.id))
|
||||
const sessionInfo = createAsync(() => querySessionInfo(params.id))
|
||||
return (
|
||||
<main data-page="workspace">
|
||||
<header data-component="workspace-header">
|
||||
|
|
@ -29,9 +26,7 @@ export default function WorkspaceLayout(props: RouteSectionProps) {
|
|||
<A href="/" data-component="site-title">
|
||||
<IconWorkspaceLogo />
|
||||
</A>
|
||||
<Show when={sessionInfo()?.isBeta}>
|
||||
<WorkspacePicker />
|
||||
</Show>
|
||||
<WorkspacePicker />
|
||||
</div>
|
||||
<div data-slot="header-actions">
|
||||
<UserMenu email={userEmail()} />
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import type { KVNamespace } from "@cloudflare/workers-types"
|
||||
import { z } from "zod"
|
||||
import { issuer } from "@openauthjs/openauth"
|
||||
import type { Theme } from "@openauthjs/openauth/ui/theme"
|
||||
|
|
@ -94,6 +95,7 @@ export default {
|
|||
// }),
|
||||
},
|
||||
storage: CloudflareStorage({
|
||||
// @ts-ignore
|
||||
namespace: env.AuthStorage,
|
||||
}),
|
||||
subjects,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
{
|
||||
"name": "@opencode-ai/css",
|
||||
"version": "0.15.0",
|
||||
"version": "0.15.2",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.css",
|
||||
"./*": "./src/*"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "bun run build.ts",
|
||||
"dev": "bun run dev.ts"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,4 @@
|
|||
[data-component="select"] {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: calc(var(--spacing) * 2);
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: var(--radius-md);
|
||||
border-color: var(--color-smoke-4);
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--text-base--line-height);
|
||||
font-weight: var(--font-weight-normal);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
text-decoration: none;
|
||||
user-select: none;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
|
|
@ -28,38 +12,35 @@
|
|||
[data-slot="section"] {
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--text-xs--line-height);
|
||||
font-weight: var(--font-weight-normal);
|
||||
font-weight: var(--font-weight-light);
|
||||
text-transform: uppercase;
|
||||
color: var(--text-default-text-weak);
|
||||
opacity: 0.6;
|
||||
margin-top: calc(var(--spacing) * 3);
|
||||
margin-left: calc(var(--spacing) * 2);
|
||||
&:first-child {
|
||||
margin-top: calc(var(--spacing) * 0);
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="item"] {
|
||||
/* "relative flex cursor-pointer select-none items-center": true, */
|
||||
/* "rounded-sm px-2 py-0.5 text-xs outline-none text-text": true, */
|
||||
/* "transition-colors data-[disabled]:pointer-events-none": true, */
|
||||
/* "data-[highlighted]:bg-background-element data-[disabled]:opacity-50": true, */
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: calc(var(--spacing) * 2);
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: var(--radius-md);
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--text-base--line-height);
|
||||
font-weight: var(--font-weight-normal);
|
||||
padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2);
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--text-xs--line-height);
|
||||
color: var(--text-default-text);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
background-color 0.2s ease-in-out,
|
||||
color 0.2s ease-in-out;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
|
||||
&[data-highlighted] {
|
||||
background-color: var(--color-background-element);
|
||||
background-color: var(--surface-default-surface);
|
||||
}
|
||||
|
||||
&[data-disabled] {
|
||||
|
|
@ -67,16 +48,7 @@
|
|||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* [data-slot="item-label"] { */
|
||||
/* font-size: var(--text-xs); */
|
||||
/* line-height: var(--text-xs--line-height); */
|
||||
/* font-weight: var(--font-weight-normal); */
|
||||
/* } */
|
||||
|
||||
[data-slot="item-indicator"] {
|
||||
/* display: flex; */
|
||||
/* align-items: center; */
|
||||
/* gap: calc(var(--spacing) * 1); */
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
|
@ -88,46 +60,61 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
[data-slot="icon"] {
|
||||
/* "group size-fit shrink-0 text-text-muted transition-transform duration-100": true, */
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
flex-shrink: 0;
|
||||
color: var(--text-default-text-weak);
|
||||
transition: transform 0.1s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="select-content"] {
|
||||
/* "min-w-32 overflow-hidden rounded-md border border-border-subtle/40": true, */
|
||||
/* "bg-background-panel p-1 shadow-md z-50": true, */
|
||||
/* "data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95": true, */
|
||||
/* "data-[expanded]:animate-in data-[expanded]:fade-in-0 data-[expanded]:zoom-in-95": true, */
|
||||
min-width: 8rem;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-md);
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: var(--color-smoke-4);
|
||||
background-color: var(--color-smoke-2);
|
||||
border-color: var(--border-default-border-weak);
|
||||
background-color: var(--surface-raised-surface-raised);
|
||||
padding: calc(var(--spacing) * 1);
|
||||
box-shadow: var(--shadow-md);
|
||||
z-index: 50;
|
||||
|
||||
/* &[data-closed] { */
|
||||
/* animation: fade-out-0 0.2s ease-out; */
|
||||
/* animation-fill-mode: forwards; */
|
||||
/* animation-delay: 0.2s; */
|
||||
/* opacity: 0; */
|
||||
/* } */
|
||||
/* &[data-expanded] { */
|
||||
/* animation: fade-in-0 0.2s ease-out; */
|
||||
/* animation-fill-mode: forwards; */
|
||||
/* animation-delay: 0.2s; */
|
||||
/* opacity: 1; */
|
||||
/* } */
|
||||
&[data-closed] {
|
||||
animation: select-close 0.15s ease-out;
|
||||
}
|
||||
|
||||
&[data-expanded] {
|
||||
animation: select-open 0.15s ease-out;
|
||||
}
|
||||
|
||||
[data-slot="list"] {
|
||||
/* overflow-y-auto max-h-48 whitespace-nowrap overflow-x-hidden */
|
||||
overflow-y: auto;
|
||||
max-height: 12rem;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes select-open {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes select-close {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
98
packages/css/src/components/tabs.css
Normal file
98
packages/css/src/components/tabs.css
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
[data-component="tabs"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
& [data-slot="list"] {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: var(--surface-default-surface);
|
||||
overflow-x: auto;
|
||||
|
||||
/* Hide scrollbar */
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Divider between tabs */
|
||||
& > [data-slot="trigger"]:not(:first-child) {
|
||||
border-left: 1px solid var(--border-default-border-weak);
|
||||
}
|
||||
|
||||
/* After element to fill remaining space */
|
||||
&::after {
|
||||
content: "";
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
height: calc(var(--spacing) * 8);
|
||||
border-left: 1px solid var(--border-default-border-weak);
|
||||
border-bottom: 1px solid var(--border-default-border-weak);
|
||||
}
|
||||
|
||||
&:empty::after {
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
|
||||
& [data-slot="trigger"] {
|
||||
position: relative;
|
||||
padding: 0 calc(var(--spacing) * 3);
|
||||
height: calc(var(--spacing) * 8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--text-sm);
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--text-default-text-weak);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid var(--border-default-border-weak);
|
||||
background-color: transparent;
|
||||
transition:
|
||||
background-color 0.15s ease,
|
||||
color 0.15s ease;
|
||||
|
||||
&:disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px var(--border-default-border-focus);
|
||||
}
|
||||
|
||||
&[data-selected] {
|
||||
color: var(--text-default-text);
|
||||
background-color: var(--surface-panel-surface);
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled):not([data-selected]) {
|
||||
color: var(--text-default-text);
|
||||
}
|
||||
}
|
||||
|
||||
& [data-slot="content"] {
|
||||
background-color: var(--surface-panel-surface);
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
|
||||
/* Hide scrollbar */
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px var(--border-default-border-focus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,5 +8,6 @@
|
|||
@import "./components/button.css" layer(components);
|
||||
@import "./components/icon.css" layer(components);
|
||||
@import "./components/select.css" layer(components);
|
||||
@import "./components/tabs.css" layer(components);
|
||||
|
||||
@import "./utilities.css" layer(utilities);
|
||||
|
|
|
|||
|
|
@ -213,11 +213,22 @@ export default function Page() {
|
|||
})
|
||||
}
|
||||
|
||||
const plus = (
|
||||
<IconButton
|
||||
class="text-text-muted/60 peer-data-[selected]/tab:opacity-100 peer-data-[selected]/tab:text-text peer-data-[selected]/tab:hover:bg-border-subtle hover:opacity-100 peer-hover/tab:opacity-100"
|
||||
size="xs"
|
||||
variant="secondary"
|
||||
onClick={() => setStore("fileSelectOpen", true)}
|
||||
>
|
||||
<Icon name="plus" size={12} />
|
||||
</IconButton>
|
||||
)
|
||||
|
||||
return (
|
||||
<div class="relative h-screen flex flex-col">
|
||||
<header class="h-10 shrink-0 bg-background-panel"></header>
|
||||
<main class="h-[calc(100vh-2.5rem)] flex">
|
||||
<div class="shrink-0 w-64">
|
||||
<header class="h-12 shrink-0"></header>
|
||||
<main class="h-[calc(100vh-3rem)] flex">
|
||||
<div class="hidden shrink-0 w-64">
|
||||
<SessionList />
|
||||
</div>
|
||||
<div class="grow w-full min-w-0 overflow-y-auto flex justify-center">
|
||||
|
|
@ -256,92 +267,15 @@ export default function Page() {
|
|||
<div class="hidden grow min-w-0">
|
||||
<EditorPane onFileClick={handleFileClick} />
|
||||
</div>
|
||||
<div class="absolute bottom-4 right-4 border border-border-subtle/60 p-2 rounded-xl bg-background w-xl flex flex-col gap-2 z-50">
|
||||
<div class="flex items-center gap-2">
|
||||
<Select
|
||||
options={sync.data.session}
|
||||
current={local.session.active()}
|
||||
placeholder="New Session"
|
||||
value={(x) => x.id}
|
||||
label={(x) => x.title}
|
||||
onSelect={(s) => local.session.setActive(s?.id)}
|
||||
class="bg-transparent! max-w-48 pl-0! text-text-muted!"
|
||||
/>
|
||||
<Show when={local.session.active()}>
|
||||
<>
|
||||
<div>/</div>
|
||||
<Select
|
||||
options={sync.data.message[local.session.active()!.id]?.filter((m) => m.role === "user") ?? []}
|
||||
label={(m) => sync.data.part[m.id].find((p) => p.type === "text")!.text}
|
||||
class="bg-transparent! max-w-48 pl-0! text-text-muted!"
|
||||
/>
|
||||
</>
|
||||
</Show>
|
||||
</div>
|
||||
<div class="h-72 text-xs overflow-x-scroll no-scrollbar w-full min-w-0">
|
||||
<Tabs
|
||||
class="relative grow w-full flex flex-col gap-1 h-full"
|
||||
value={local.context.activeFile()?.path}
|
||||
onChange={local.context.setActiveFile}
|
||||
>
|
||||
<div class="sticky top-0 shrink-0 flex items-center gap-1">
|
||||
<IconButton
|
||||
class="text-text-muted/60 peer-data-[selected]/tab:opacity-100 peer-data-[selected]/tab:text-text peer-data-[selected]/tab:hover:bg-border-subtle hover:opacity-100 peer-hover/tab:opacity-100"
|
||||
size="xs"
|
||||
variant="secondary"
|
||||
onClick={() => setStore("fileSelectOpen", true)}
|
||||
>
|
||||
<Icon name="plus" size={12} />
|
||||
</IconButton>
|
||||
<Tabs.List class="grow after:hidden! h-full divide-none! gap-1">
|
||||
<For each={local.context.files()}>
|
||||
{(file) => (
|
||||
<KobalteTabs.Trigger
|
||||
value={file.path}
|
||||
class="h-full"
|
||||
// onClick={() => props.onTabClick(props.file)}
|
||||
>
|
||||
<div class="flex items-center gap-x-1 rounded-md bg-background-panel px-2 h-full">
|
||||
<FileIcon node={file} class="shrink-0 size-3!" />
|
||||
<span class="text-xs text-text whitespace-nowrap">{getFilename(file.path)}</span>
|
||||
</div>
|
||||
</KobalteTabs.Trigger>
|
||||
)}
|
||||
</For>
|
||||
</Tabs.List>
|
||||
</div>
|
||||
<For each={local.context.files()}>
|
||||
{(file) => (
|
||||
<Tabs.Content value={file.path} class="grow h-full pt-1 select-text rounded-md">
|
||||
<Code path={file.path} code={file.content?.content ?? ""} />
|
||||
</Tabs.Content>
|
||||
)}
|
||||
</For>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div class="absolute bottom-4 inset-x-0 p-2 flex flex-col justify-center items-center gap-2 z-50">
|
||||
<PromptForm
|
||||
class="w-xl"
|
||||
onSubmit={handlePromptSubmit}
|
||||
onOpenModelSelect={() => setStore("modelSelectOpen", true)}
|
||||
onInputRefChange={(element: HTMLTextAreaElement | undefined) => {
|
||||
inputRef = element ?? undefined
|
||||
}}
|
||||
/>
|
||||
<div class="hidden relative flex-1 min-h-0 overflow-y-auto overflow-x-hidden">
|
||||
<Show when={local.session.active()}>
|
||||
{(activeSession) => (
|
||||
<div class="relative">
|
||||
<div class="sticky top-0 bg-background z-50 px-2 h-8 border-b border-border-subtle/30">
|
||||
<div class="h-full flex items-center gap-2">
|
||||
<h2 class="text-sm font-medium text-text truncate">
|
||||
{activeSession().title || "Untitled Session"}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<SessionTimeline session={activeSession().id} />
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Show when={store.modelSelectOpen}>
|
||||
|
|
|
|||
|
|
@ -31,8 +31,13 @@
|
|||
"@types/yargs": "17.0.33",
|
||||
"typescript": "catalog:",
|
||||
"vscode-languageserver-types": "3.17.5",
|
||||
<<<<<<< HEAD
|
||||
"why-is-node-running": "3.2.2",
|
||||
"zod-to-json-schema": "3.24.5"
|
||||
=======
|
||||
"zod-to-json-schema": "3.24.5",
|
||||
"@opencode-ai/script": "workspace:*"
|
||||
>>>>>>> dev
|
||||
},
|
||||
"dependencies": {
|
||||
"@clack/prompts": "1.0.0-alpha.1",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const dir = new URL("..", import.meta.url).pathname
|
|||
process.chdir(dir)
|
||||
|
||||
import pkg from "../package.json"
|
||||
import { Script } from "@opencode-ai/script"
|
||||
|
||||
const singleFlag = process.argv.includes("--single")
|
||||
|
||||
|
|
@ -28,7 +29,6 @@ const targets = singleFlag
|
|||
await $`rm -rf dist`
|
||||
|
||||
const binaries: Record<string, string> = {}
|
||||
const version = process.env["OPENCODE_VERSION"] ?? "dev"
|
||||
for (const [os, arch] of targets) {
|
||||
console.log(`building ${os}-${arch}`)
|
||||
const name = `${pkg.name}-${os}-${arch}`
|
||||
|
|
@ -51,7 +51,7 @@ for (const [os, arch] of targets) {
|
|||
compile: {
|
||||
target: `bun-${os}-${arch}` as any,
|
||||
outfile: `dist/${name}/bin/opencode`,
|
||||
execArgv: [`--user-agent=opencode/${version}`, `--env-file=""`, `--`],
|
||||
execArgv: [`--user-agent=opencode/${Script.version}`, `--env-file=""`, `--`],
|
||||
windows: {},
|
||||
},
|
||||
entrypoints: [
|
||||
|
|
@ -60,7 +60,7 @@ for (const [os, arch] of targets) {
|
|||
"./src/cli/cmd/tui/worker.ts",
|
||||
],
|
||||
define: {
|
||||
OPENCODE_VERSION: `'${version}'`,
|
||||
OPENCODE_VERSION: `'${Script.version}'`,
|
||||
OTUI_TREE_SITTER_WORKER_PATH: "/$bunfs/root/../../node_modules/@opentui/core/parser.worker.js",
|
||||
},
|
||||
})
|
||||
|
|
@ -70,7 +70,7 @@ for (const [os, arch] of targets) {
|
|||
JSON.stringify(
|
||||
{
|
||||
name,
|
||||
version,
|
||||
version: Script.version,
|
||||
os: [os === "windows" ? "win32" : os],
|
||||
cpu: [arch],
|
||||
},
|
||||
|
|
@ -78,7 +78,7 @@ for (const [os, arch] of targets) {
|
|||
2,
|
||||
),
|
||||
)
|
||||
binaries[name] = version
|
||||
binaries[name] = Script.version
|
||||
}
|
||||
|
||||
export { binaries }
|
||||
|
|
|
|||
|
|
@ -1,20 +1,10 @@
|
|||
#!/usr/bin/env bun
|
||||
import { $ } from "bun"
|
||||
import pkg from "../package.json"
|
||||
import { Script } from "@opencode-ai/script"
|
||||
|
||||
const dir = new URL("..", import.meta.url).pathname
|
||||
process.chdir(dir)
|
||||
import { $ } from "bun"
|
||||
|
||||
import pkg from "../package.json"
|
||||
|
||||
const snapshot = process.env["OPENCODE_SNAPSHOT"] === "true"
|
||||
let version = process.env["OPENCODE_VERSION"]
|
||||
const tag = process.env["OPENCODE_TAG"] ?? (snapshot ? "snapshot" : "latest")
|
||||
if (!version && snapshot) {
|
||||
version = `0.0.0-${tag}-${new Date().toISOString().slice(0, 16).replace(/[-:T]/g, "")}`
|
||||
process.env["OPENCODE_VERSION"] = version
|
||||
}
|
||||
if (!version) throw new Error("OPENCODE_VERSION is required")
|
||||
|
||||
console.log(`publishing ${version}`)
|
||||
|
||||
const { binaries } = await import("./build.ts")
|
||||
{
|
||||
|
|
@ -38,7 +28,7 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write(
|
|||
preinstall: "node ./preinstall.mjs",
|
||||
postinstall: "node ./postinstall.mjs",
|
||||
},
|
||||
version,
|
||||
version: Script.version,
|
||||
optionalDependencies: binaries,
|
||||
},
|
||||
null,
|
||||
|
|
@ -46,11 +36,11 @@ 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 ${tag}`
|
||||
await $`cd dist/${name} && chmod 777 -R . && bun publish --access public --tag ${Script.tag}`
|
||||
}
|
||||
await $`cd ./dist/${pkg.name} && bun publish --access public --tag ${tag}`
|
||||
await $`cd ./dist/${pkg.name} && bun publish --access public --tag ${Script.tag}`
|
||||
|
||||
if (!snapshot) {
|
||||
if (!Script.preview) {
|
||||
for (const key of Object.keys(binaries)) {
|
||||
await $`cd dist/${key}/bin && zip -r ../../${key}.zip *`
|
||||
}
|
||||
|
|
@ -67,7 +57,7 @@ if (!snapshot) {
|
|||
"# Maintainer: adam",
|
||||
"",
|
||||
"pkgname='opencode-bin'",
|
||||
`pkgver=${version.split("-")[0]}`,
|
||||
`pkgver=${Script.version.split("-")[0]}`,
|
||||
"options=('!debug' '!strip')",
|
||||
"pkgrel=1",
|
||||
"pkgdesc='The AI coding agent built for the terminal.'",
|
||||
|
|
@ -78,10 +68,10 @@ if (!snapshot) {
|
|||
"conflicts=('opencode')",
|
||||
"depends=('fzf' 'ripgrep')",
|
||||
"",
|
||||
`source_aarch64=("\${pkgname}_\${pkgver}_aarch64.zip::https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-arm64.zip")`,
|
||||
`source_aarch64=("\${pkgname}_\${pkgver}_aarch64.zip::https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-arm64.zip")`,
|
||||
`sha256sums_aarch64=('${arm64Sha}')`,
|
||||
"",
|
||||
`source_x86_64=("\${pkgname}_\${pkgver}_x86_64.zip::https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-x64.zip")`,
|
||||
`source_x86_64=("\${pkgname}_\${pkgver}_x86_64.zip::https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-x64.zip")`,
|
||||
`sha256sums_x86_64=('${x64Sha}')`,
|
||||
"",
|
||||
"package() {",
|
||||
|
|
@ -96,7 +86,7 @@ if (!snapshot) {
|
|||
"# Maintainer: adam",
|
||||
"",
|
||||
"pkgname='opencode'",
|
||||
`pkgver=${version.split("-")[0]}`,
|
||||
`pkgver=${Script.version.split("-")[0]}`,
|
||||
"options=('!debug' '!strip')",
|
||||
"pkgrel=1",
|
||||
"pkgdesc='The AI coding agent built for the terminal.'",
|
||||
|
|
@ -108,7 +98,7 @@ if (!snapshot) {
|
|||
"depends=('fzf' 'ripgrep')",
|
||||
"makedepends=('git' 'bun-bin' 'go')",
|
||||
"",
|
||||
`source=("opencode-\${pkgver}.tar.gz::https://github.com/sst/opencode/archive/v${version}.tar.gz")`,
|
||||
`source=("opencode-\${pkgver}.tar.gz::https://github.com/sst/opencode/archive/v${Script.version}.tar.gz")`,
|
||||
`sha256sums=('SKIP')`,
|
||||
"",
|
||||
"build() {",
|
||||
|
|
@ -139,7 +129,7 @@ if (!snapshot) {
|
|||
await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild)
|
||||
await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO`
|
||||
await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO`
|
||||
await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${version}"`
|
||||
await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${Script.version}"`
|
||||
await $`cd ./dist/aur-${pkg} && git push`
|
||||
break
|
||||
} catch (e) {
|
||||
|
|
@ -157,11 +147,11 @@ if (!snapshot) {
|
|||
"class Opencode < Formula",
|
||||
` desc "The AI coding agent built for the terminal."`,
|
||||
` homepage "https://github.com/sst/opencode"`,
|
||||
` version "${version.split("-")[0]}"`,
|
||||
` version "${Script.version.split("-")[0]}"`,
|
||||
"",
|
||||
" on_macos do",
|
||||
" if Hardware::CPU.intel?",
|
||||
` url "https://github.com/sst/opencode/releases/download/v${version}/opencode-darwin-x64.zip"`,
|
||||
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-x64.zip"`,
|
||||
` sha256 "${macX64Sha}"`,
|
||||
"",
|
||||
" def install",
|
||||
|
|
@ -169,7 +159,7 @@ if (!snapshot) {
|
|||
" end",
|
||||
" end",
|
||||
" if Hardware::CPU.arm?",
|
||||
` url "https://github.com/sst/opencode/releases/download/v${version}/opencode-darwin-arm64.zip"`,
|
||||
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-arm64.zip"`,
|
||||
` sha256 "${macArm64Sha}"`,
|
||||
"",
|
||||
" def install",
|
||||
|
|
@ -180,14 +170,14 @@ if (!snapshot) {
|
|||
"",
|
||||
" on_linux do",
|
||||
" if Hardware::CPU.intel? and Hardware::CPU.is_64_bit?",
|
||||
` url "https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-x64.zip"`,
|
||||
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-x64.zip"`,
|
||||
` sha256 "${x64Sha}"`,
|
||||
" def install",
|
||||
' bin.install "opencode"',
|
||||
" end",
|
||||
" end",
|
||||
" if Hardware::CPU.arm? and Hardware::CPU.is_64_bit?",
|
||||
` url "https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-arm64.zip"`,
|
||||
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-arm64.zip"`,
|
||||
` sha256 "${arm64Sha}"`,
|
||||
" def install",
|
||||
' bin.install "opencode"',
|
||||
|
|
@ -203,6 +193,6 @@ if (!snapshot) {
|
|||
await $`git clone https://${process.env["GITHUB_TOKEN"]}@github.com/sst/homebrew-tap.git ./dist/homebrew-tap`
|
||||
await Bun.file("./dist/homebrew-tap/opencode.rb").write(homebrewFormula)
|
||||
await $`cd ./dist/homebrew-tap && git add opencode.rb`
|
||||
await $`cd ./dist/homebrew-tap && git commit -m "Update to v${version}"`
|
||||
await $`cd ./dist/homebrew-tap && git commit -m "Update to v${Script.version}"`
|
||||
await $`cd ./dist/homebrew-tap && git push`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,19 @@ export const UpgradeCommand = {
|
|||
const detectedMethod = await Installation.method()
|
||||
const method = (args.method as Installation.Method) ?? detectedMethod
|
||||
if (method === "unknown") {
|
||||
prompts.log.error(`opencode is installed to ${process.execPath} and seems to be managed by a package manager`)
|
||||
prompts.outro("Done")
|
||||
return
|
||||
prompts.log.error(`opencode is installed to ${process.execPath} and may be managed by a package manager`)
|
||||
const install = await prompts.select({
|
||||
message: "Install anyways?",
|
||||
options: [
|
||||
{ label: "Yes", value: true },
|
||||
{ label: "No", value: false },
|
||||
],
|
||||
initialValue: false,
|
||||
})
|
||||
if (!install) {
|
||||
prompts.outro("Done")
|
||||
return
|
||||
}
|
||||
}
|
||||
prompts.log.info("Using method: " + method)
|
||||
const target = args.target ? args.target.replace(/^v/, "") : await Installation.latest()
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ export namespace Installation {
|
|||
|
||||
export async function method() {
|
||||
if (process.execPath.includes(path.join(".opencode", "bin"))) return "curl"
|
||||
if (process.execPath.includes(path.join(".local", "bin"))) return "curl"
|
||||
const exec = process.execPath.toLowerCase()
|
||||
|
||||
const checks = [
|
||||
|
|
|
|||
10
packages/script/package.json
Normal file
10
packages/script/package.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package",
|
||||
"name": "@opencode-ai/script",
|
||||
"devDependencies": {
|
||||
"@types/bun": "catalog:"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
}
|
||||
}
|
||||
35
packages/script/src/index.ts
Normal file
35
packages/script/src/index.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { $ } from "bun"
|
||||
|
||||
if (process.versions.bun !== "1.3.0") {
|
||||
throw new Error("This script requires bun@1.3.0")
|
||||
}
|
||||
|
||||
const TAG = process.env["OPENCODE_TAG"] ?? (await $`git branch --show-current`.text().then((x) => x.trim()))
|
||||
const IS_PREVIEW = TAG !== "latest"
|
||||
const VERSION = await (async () => {
|
||||
if (IS_PREVIEW) return `0.0.0-${new Date().toISOString().slice(0, 16).replace(/[-:T]/g, "")}`
|
||||
const version = await fetch("https://registry.npmjs.org/opencode-ai/latest")
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error(res.statusText)
|
||||
return res.json()
|
||||
})
|
||||
.then((data: any) => data.version)
|
||||
const [major, minor, patch] = version.split(".").map((x: string) => Number(x) || 0)
|
||||
const t = process.env["OPENCODE_BUMP"]?.toLowerCase()
|
||||
if (t === "major") return `${major + 1}.0.0`
|
||||
if (t === "minor") return `${major}.${minor + 1}.0`
|
||||
return `${major}.${minor}.${patch + 1}`
|
||||
})()
|
||||
|
||||
export const Script = {
|
||||
get tag() {
|
||||
return TAG
|
||||
},
|
||||
get version() {
|
||||
return VERSION
|
||||
},
|
||||
get preview() {
|
||||
return IS_PREVIEW
|
||||
},
|
||||
}
|
||||
console.log(`opencode script`, JSON.stringify(Script, null, 2))
|
||||
8
packages/script/tsconfig.json
Normal file
8
packages/script/tsconfig.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@tsconfig/bun/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"noUncheckedIndexedAccess": false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<!doctype html>
|
||||
<html lang="en" class="light">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "0.15.0",
|
||||
"version": "0.15.2",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/components/index.ts",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,100 @@
|
|||
import type { Component } from "solid-js"
|
||||
import { Button } from "./components/button"
|
||||
import { Select } from "./components"
|
||||
import { Button, Select, Tabs } from "./components"
|
||||
import "@opencode-ai/css"
|
||||
import "./index.css"
|
||||
|
||||
const App: Component = () => {
|
||||
const Content = (props: { dark?: boolean }) => (
|
||||
<div class={`${props.dark ? "dark" : ""}`}>
|
||||
<h3>Buttons</h3>
|
||||
<section>
|
||||
<Button variant="primary" size="normal">
|
||||
Normal Primary
|
||||
</Button>
|
||||
<Button variant="secondary" size="normal">
|
||||
Normal Secondary
|
||||
</Button>
|
||||
<Button variant="ghost" size="normal">
|
||||
Normal Ghost
|
||||
</Button>
|
||||
<Button variant="primary" size="large">
|
||||
Large Primary
|
||||
</Button>
|
||||
<Button variant="secondary" size="large">
|
||||
Large Secondary
|
||||
</Button>
|
||||
<Button variant="ghost" size="large">
|
||||
Large Ghost
|
||||
</Button>
|
||||
</section>
|
||||
<h3>Select</h3>
|
||||
<section>
|
||||
<Select
|
||||
// we have to pass dark bc of the portal,
|
||||
// normally wouldn't be needed bc root element
|
||||
// would have theme class
|
||||
class={props.dark ? "dark" : ""}
|
||||
variant="primary"
|
||||
options={["Option 1", "Option 2", "Option 3"]}
|
||||
placeholder="Select Primary"
|
||||
/>
|
||||
<Select
|
||||
variant="secondary"
|
||||
class={props.dark ? "dark" : ""}
|
||||
options={["Option 1", "Option 2", "Option 3"]}
|
||||
placeholder="Select Secondary"
|
||||
/>
|
||||
<Select
|
||||
variant="ghost"
|
||||
class={props.dark ? "dark" : ""}
|
||||
options={["Option 1", "Option 2", "Option 3"]}
|
||||
placeholder="Select Ghost"
|
||||
/>
|
||||
</section>
|
||||
<h3>Tabs</h3>
|
||||
<section>
|
||||
<Tabs defaultValue="tab1" style={{ width: "100%" }}>
|
||||
<Tabs.List>
|
||||
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
||||
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
||||
<Tabs.Trigger value="tab3">Tab 3</Tabs.Trigger>
|
||||
<Tabs.Trigger value="tab4" disabled>
|
||||
Disabled Tab
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<Tabs.Content value="tab1">
|
||||
<div style={{ padding: "16px" }}>
|
||||
<h4>Tab 1 Content</h4>
|
||||
<p>This is the content for the first tab.</p>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="tab2">
|
||||
<div style={{ padding: "16px" }}>
|
||||
<h4>Tab 2 Content</h4>
|
||||
<p>This is the content for the second tab.</p>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="tab3">
|
||||
<div style={{ padding: "16px" }}>
|
||||
<h4>Tab 3 Content</h4>
|
||||
<p>This is the content for the third tab.</p>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="tab4">
|
||||
<div style={{ padding: "16px" }}>
|
||||
<h4>Tab 4 Content</h4>
|
||||
<p>This tab should be disabled.</p>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
</Tabs>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div class="light">
|
||||
<h3>Buttons</h3>
|
||||
<section>
|
||||
<Button variant="primary" size="normal">
|
||||
Normal Primary
|
||||
</Button>
|
||||
<Button variant="secondary" size="normal">
|
||||
Normal Secondary
|
||||
</Button>
|
||||
<Button variant="ghost" size="normal">
|
||||
Normal Ghost
|
||||
</Button>
|
||||
<Button variant="primary" size="large">
|
||||
Large Primary
|
||||
</Button>
|
||||
<Button variant="secondary" size="large">
|
||||
Large Secondary
|
||||
</Button>
|
||||
<Button variant="ghost" size="large">
|
||||
Large Ghost
|
||||
</Button>
|
||||
</section>
|
||||
<h3>Select</h3>
|
||||
<section>
|
||||
<Select options={["a", "b", "c"]} onSelect={(x) => console.log(x)} placeholder="Select" />
|
||||
</section>
|
||||
</div>
|
||||
<div class="dark">
|
||||
<h3>Buttons</h3>
|
||||
<section>
|
||||
<Button variant="primary" size="normal">
|
||||
Normal Primary
|
||||
</Button>
|
||||
<Button variant="secondary" size="normal">
|
||||
Normal Secondary
|
||||
</Button>
|
||||
<Button variant="ghost" size="normal">
|
||||
Normal Ghost
|
||||
</Button>
|
||||
<Button variant="primary" size="large">
|
||||
Large Primary
|
||||
</Button>
|
||||
<Button variant="secondary" size="large">
|
||||
Large Secondary
|
||||
</Button>
|
||||
<Button variant="ghost" size="large">
|
||||
Large Ghost
|
||||
</Button>
|
||||
</section>
|
||||
<h3>Select</h3>
|
||||
<section>
|
||||
<Select options={["a", "b", "c"]} onSelect={(x) => console.log(x)} placeholder="Select" />
|
||||
</section>
|
||||
</div>
|
||||
<Content />
|
||||
<Content dark />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export * from "./button"
|
||||
export * from "./icon"
|
||||
export * from "./select"
|
||||
export * from "./tabs"
|
||||
|
|
|
|||
|
|
@ -79,11 +79,17 @@ export function Select<T>(props: SelectProps<T> & ButtonProps) {
|
|||
}}
|
||||
</Kobalte.Value>
|
||||
<Kobalte.Icon data-slot="icon">
|
||||
<Icon name="chevron-down" size={16} class="-my-2 group-data-[expanded]:rotate-180" />
|
||||
<Icon name="chevron-down" size={16} />
|
||||
</Kobalte.Icon>
|
||||
</Kobalte.Trigger>
|
||||
<Kobalte.Portal>
|
||||
<Kobalte.Content data-component="select-content">
|
||||
<Kobalte.Content
|
||||
classList={{
|
||||
...(props.classList ?? {}),
|
||||
[props.class ?? ""]: !!props.class,
|
||||
}}
|
||||
data-component="select-content"
|
||||
>
|
||||
<Kobalte.Listbox data-slot="list" />
|
||||
</Kobalte.Content>
|
||||
</Kobalte.Portal>
|
||||
|
|
|
|||
74
packages/ui/src/components/tabs.tsx
Normal file
74
packages/ui/src/components/tabs.tsx
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { Tabs as Kobalte } from "@kobalte/core/tabs"
|
||||
import { splitProps } 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 TabsContentProps extends ComponentProps<typeof Kobalte.Content> {}
|
||||
|
||||
function TabsRoot(props: TabsProps) {
|
||||
const [split, rest] = splitProps(props, ["class", "classList"])
|
||||
return (
|
||||
<Kobalte
|
||||
{...rest}
|
||||
data-component="tabs"
|
||||
classList={{
|
||||
...(split.classList ?? {}),
|
||||
[split.class ?? ""]: !!split.class,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsList(props: TabsListProps) {
|
||||
const [split, rest] = splitProps(props, ["class", "classList"])
|
||||
return (
|
||||
<Kobalte.List
|
||||
{...rest}
|
||||
data-slot="list"
|
||||
classList={{
|
||||
...(split.classList ?? {}),
|
||||
[split.class ?? ""]: !!split.class,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsTrigger(props: ParentProps<TabsTriggerProps>) {
|
||||
const [split, rest] = splitProps(props, ["class", "classList", "children"])
|
||||
return (
|
||||
<Kobalte.Trigger
|
||||
{...rest}
|
||||
data-slot="trigger"
|
||||
classList={{
|
||||
...(split.classList ?? {}),
|
||||
[split.class ?? ""]: !!split.class,
|
||||
}}
|
||||
>
|
||||
{split.children}
|
||||
</Kobalte.Trigger>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsContent(props: ParentProps<TabsContentProps>) {
|
||||
const [split, rest] = splitProps(props, ["class", "classList", "children"])
|
||||
return (
|
||||
<Kobalte.Content
|
||||
{...rest}
|
||||
data-slot="content"
|
||||
classList={{
|
||||
...(split.classList ?? {}),
|
||||
[split.class ?? ""]: !!split.class,
|
||||
}}
|
||||
>
|
||||
{split.children}
|
||||
</Kobalte.Content>
|
||||
)
|
||||
}
|
||||
|
||||
export const Tabs = Object.assign(TabsRoot, {
|
||||
List: TabsList,
|
||||
Trigger: TabsTrigger,
|
||||
Content: TabsContent,
|
||||
})
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
}
|
||||
main > div {
|
||||
flex: 1;
|
||||
padding: 3rem;
|
||||
padding: 2rem;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 1rem 0;
|
||||
margin-bottom: -1rem;
|
||||
}
|
||||
section {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
/* @refresh reload */
|
||||
import { render } from "solid-js/web"
|
||||
import "solid-devtools"
|
||||
|
||||
import App from "./app"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,34 +2,13 @@
|
|||
|
||||
import { $ } from "bun"
|
||||
import { createOpencode } from "@opencode-ai/sdk"
|
||||
if (process.versions.bun !== "1.3.0") {
|
||||
throw new Error("This script requires bun@1.3.0")
|
||||
}
|
||||
import { Script } from "@opencode-ai/script"
|
||||
|
||||
const notes = [] as string[]
|
||||
|
||||
console.log("=== publishing ===\n")
|
||||
|
||||
const snapshot = process.env["OPENCODE_SNAPSHOT"] === "true"
|
||||
const version = await (async () => {
|
||||
if (snapshot) return `0.0.0-${new Date().toISOString().slice(0, 16).replace(/[-:T]/g, "")}`
|
||||
if (process.env["OPENCODE_VERSION"]) return process.env["OPENCODE_VERSION"]
|
||||
const npmVersion = await fetch("https://registry.npmjs.org/opencode-ai/latest")
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error(res.statusText)
|
||||
return res.json()
|
||||
})
|
||||
.then((data: any) => data.version)
|
||||
const [major, minor, patch] = npmVersion.split(".").map((x: string) => Number(x) || 0)
|
||||
const t = process.env["OPENCODE_BUMP"]?.toLowerCase()
|
||||
if (t === "major") return `${major + 1}.0.0`
|
||||
if (t === "minor") return `${major}.${minor + 1}.0`
|
||||
return `${major}.${minor}.${patch + 1}`
|
||||
})()
|
||||
process.env["OPENCODE_VERSION"] = version
|
||||
console.log("version:", version)
|
||||
|
||||
if (!snapshot) {
|
||||
if (!Script.preview) {
|
||||
const previous = await fetch("https://registry.npmjs.org/opencode-ai/latest")
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error(res.statusText)
|
||||
|
|
@ -97,7 +76,7 @@ const pkgjsons = await Array.fromAsync(
|
|||
|
||||
for (const file of pkgjsons) {
|
||||
let pkg = await Bun.file(file).text()
|
||||
pkg = pkg.replaceAll(/"version": "[^"]+"/g, `"version": "${version}"`)
|
||||
pkg = pkg.replaceAll(/"version": "[^"]+"/g, `"version": "${Script.version}"`)
|
||||
console.log("updated:", file)
|
||||
await Bun.file(file).write(pkg)
|
||||
}
|
||||
|
|
@ -115,12 +94,12 @@ await import(`../packages/plugin/script/publish.ts`)
|
|||
const dir = new URL("..", import.meta.url).pathname
|
||||
process.chdir(dir)
|
||||
|
||||
if (!snapshot) {
|
||||
await $`git commit -am "release: v${version}"`
|
||||
await $`git tag v${version}`
|
||||
if (!Script.preview) {
|
||||
await $`git commit -am "release: v${Script.version}"`
|
||||
await $`git tag v${Script.version}`
|
||||
await $`git fetch origin`
|
||||
await $`git cherry-pick HEAD..origin/dev`.nothrow()
|
||||
await $`git push origin HEAD --tags --no-verify --force`
|
||||
|
||||
await $`gh release create v${version} --title "v${version}" --notes ${notes.join("\n") ?? "No notable changes"} ./packages/opencode/dist/*.zip`
|
||||
await $`gh release create v${Script.version} --title "v${Script.version}" --notes ${notes.join("\n") ?? "No notable changes"} ./packages/opencode/dist/*.zip`
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue