mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-23 10:44:21 +01:00
wip: zen
This commit is contained in:
@@ -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 />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
94
packages/console/app/src/routes/workspace/model-section.tsx
Normal file
94
packages/console/app/src/routes/workspace/model-section.tsx
Normal file
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user