diff options
| author | Frank <[email protected]> | 2025-10-08 00:03:34 -0400 |
|---|---|---|
| committer | Frank <[email protected]> | 2025-10-08 00:03:36 -0400 |
| commit | 99b72eb1ea136ef6569751061d50b6f691718564 (patch) | |
| tree | 98e93be1fe291cb884b5e83817613954c447649e /packages/console/app/src | |
| parent | 22a6849ff8f34ef9d3fab55a295fcfbb766f9d5a (diff) | |
| download | opencode-99b72eb1ea136ef6569751061d50b6f691718564.tar.gz opencode-99b72eb1ea136ef6569751061d50b6f691718564.zip | |
wip: zen
Diffstat (limited to 'packages/console/app/src')
3 files changed, 218 insertions, 0 deletions
diff --git a/packages/console/app/src/routes/workspace/[id].tsx b/packages/console/app/src/routes/workspace/[id].tsx index 09b14056a..952c1417d 100644 --- a/packages/console/app/src/routes/workspace/[id].tsx +++ b/packages/console/app/src/routes/workspace/[id].tsx @@ -7,6 +7,7 @@ import { UsageSection } from "./usage-section" import { KeySection } from "./key-section" import { MemberSection } from "./member-section" import { SettingsSection } from "./settings-section" +import { ModelSection } from "./model-section" import { Show } from "solid-js" import { createAsync, query, useParams } from "@solidjs/router" import { Actor } from "@opencode-ai/console-core/actor.js" @@ -50,6 +51,7 @@ export default function () { <Show when={isBeta()}> <SettingsSection /> <MemberSection /> + <ModelSection /> </Show> <BillingSection /> <MonthlyLimitSection /> diff --git a/packages/console/app/src/routes/workspace/model-section.module.css b/packages/console/app/src/routes/workspace/model-section.module.css new file mode 100644 index 000000000..5a98c9b15 --- /dev/null +++ b/packages/console/app/src/routes/workspace/model-section.module.css @@ -0,0 +1,122 @@ +.root {} + +[data-slot="section-title"] { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +[data-slot="section-title"] h2 { + margin: 0; + font-size: 1.25rem; + font-weight: 600; + color: var(--color-text); +} + +[data-slot="section-title"] p { + margin: 0; + color: var(--color-text-secondary); + font-size: 0.875rem; +} + +[data-slot="models-list"] { + display: flex; + flex-direction: column; +} + +[data-slot="models-table"] { + overflow-x: auto; +} + +[data-slot="models-table-element"] { + width: 100%; + border-collapse: collapse; + font-size: var(--font-size-sm); + + thead { + border-bottom: 1px solid var(--color-border); + } + + th { + padding: var(--space-3) var(--space-4); + text-align: left; + font-weight: normal; + color: var(--color-text-muted); + text-transform: uppercase; + } + + td { + padding: var(--space-3) var(--space-4); + border-bottom: 1px solid var(--color-border-muted); + color: var(--color-text-muted); + font-family: var(--font-mono); + + &[data-slot="model-name"] { + color: var(--color-text); + font-family: var(--font-mono); + font-weight: 500; + } + + &[data-slot="training-data"] { + text-align: center; + color: var(--color-text); + } + + &[data-slot="model-status"] { + text-align: left; + color: var(--color-text); + } + + &[data-slot="model-toggle"] { + text-align: left; + font-family: var(--font-sans); + } + } + + tbody tr { + &[data-enabled="false"] { + opacity: 0.6; + } + + &:last-child td { + border-bottom: none; + } + } + + @media (max-width: 40rem) { + + th, + td { + padding: var(--space-2) var(--space-3); + font-size: var(--font-size-xs); + } + + th { + &:nth-child(2) + + /* Training Data */ + { + display: none; + } + } + + td { + &:nth-child(2) + + /* Training Data */ + { + display: none; + } + } + } +} + + +[data-component="empty-state"] { + display: flex; + align-items: center; + justify-content: center; + padding: 3rem; + color: var(--color-text-secondary); + font-size: 0.875rem; +}
\ No newline at end of file diff --git a/packages/console/app/src/routes/workspace/model-section.tsx b/packages/console/app/src/routes/workspace/model-section.tsx new file mode 100644 index 000000000..078cbfa88 --- /dev/null +++ b/packages/console/app/src/routes/workspace/model-section.tsx @@ -0,0 +1,94 @@ +import { Model } from "@opencode-ai/console-core/model.js" +import { query, action, useParams, createAsync, json } from "@solidjs/router" +import { createMemo, For, Show } from "solid-js" +import { withActor } from "~/context/auth.withActor" +import { ZenModel } from "@opencode-ai/console-core/model.js" +import styles from "./model-section.module.css" + +const getModelsInfo = query(async (workspaceID: string) => { + "use server" + return withActor(async () => { + return { + all: Object.keys(ZenModel.list()) + .filter((model) => !["claude-3-5-haiku", "glm-4.6", "qwen3-max"].includes(model)) + .sort(([a], [b]) => a.localeCompare(b)), + disabled: await Model.listDisabled(), + } + }, workspaceID) +}, "model.info") + +const updateModel = action(async (form: FormData) => { + "use server" + const model = form.get("model")?.toString() + if (!model) return { error: "Model is required" } + const workspaceID = form.get("workspaceID")?.toString() + if (!workspaceID) return { error: "Workspace ID is required" } + const enabled = form.get("enabled")?.toString() === "true" + console.log({ model, workspaceID, enabled }) + return json( + withActor(async () => { + if (enabled) { + await Model.disable({ model }) + } else { + await Model.enable({ model }) + } + }, workspaceID), + { revalidate: getModelsInfo.key }, + ) +}, "model.toggle") + +export function ModelSection() { + const params = useParams() + const modelsInfo = createAsync(() => getModelsInfo(params.id)) + return ( + <section class={styles.root}> + <div data-slot="section-title"> + <h2>Models</h2> + <p>Manage models for your workspace.</p> + </div> + <div data-slot="models-list"> + <Show + when={modelsInfo()} + fallback={ + <div data-component="empty-state"> + <p>Loading models...</p> + </div> + } + > + <div data-slot="models-table"> + <table data-slot="models-table-element"> + <thead> + <tr> + <th>Model</th> + <th>Status</th> + <th>Action</th> + </tr> + </thead> + <tbody> + <For each={modelsInfo()!.all}> + {(modelId) => { + const isEnabled = createMemo(() => !modelsInfo()!.disabled.includes(modelId)) + return ( + <tr data-slot="model-row" data-enabled={isEnabled()}> + <td data-slot="model-name">{modelId}</td> + <td data-slot="model-status">{isEnabled() ? "Enabled" : "Disabled"}</td> + <td data-slot="model-toggle"> + <form action={updateModel} method="post"> + <input type="hidden" name="model" value={modelId} /> + <input type="hidden" name="workspaceID" value={params.id} /> + <input type="hidden" name="enabled" value={isEnabled().toString()} /> + <button data-color="ghost">{isEnabled() ? "Disable" : "Enable"}</button> + </form> + </td> + </tr> + ) + }} + </For> + </tbody> + </table> + </div> + </Show> + </div> + </section> + ) +} |
