mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-23 10:44:21 +01:00
ignore: zen
This commit is contained in:
@@ -15,7 +15,7 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.15s ease;
|
transition: all 0.15s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover:not(:disabled) {
|
||||||
background-color: var(--color-surface-hover);
|
background-color: var(--color-surface-hover);
|
||||||
border-color: var(--color-accent);
|
border-color: var(--color-accent);
|
||||||
}
|
}
|
||||||
@@ -26,21 +26,15 @@
|
|||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
cursor: not-allowed;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--color-bg);
|
|
||||||
border-color: var(--color-border);
|
|
||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&[data-color="primary"] {
|
&[data-color="primary"] {
|
||||||
background-color: var(--color-primary);
|
background-color: var(--color-primary);
|
||||||
border-color: var(--color-primary);
|
border-color: var(--color-primary);
|
||||||
color: var(--color-primary-text);
|
color: var(--color-primary-text);
|
||||||
|
|
||||||
&:hover {
|
&:hover:not(:disabled) {
|
||||||
background-color: var(--color-primary-hover);
|
background-color: var(--color-primary-hover);
|
||||||
border-color: var(--color-primary-hover);
|
border-color: var(--color-primary-hover);
|
||||||
}
|
}
|
||||||
@@ -51,7 +45,7 @@
|
|||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
|
|
||||||
&:hover {
|
&:hover:not(:disabled) {
|
||||||
background-color: var(--color-surface-hover);
|
background-color: var(--color-surface-hover);
|
||||||
border-color: var(--color-border);
|
border-color: var(--color-border);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
|
|||||||
@@ -47,12 +47,6 @@
|
|||||||
font-size: var(--font-size-md);
|
font-size: var(--font-size-md);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
|
||||||
line-height: 1.4;
|
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
section:not(:last-child) {
|
section:not(:last-child) {
|
||||||
@@ -192,11 +186,35 @@
|
|||||||
&[data-slot="key-value"] {
|
&[data-slot="key-value"] {
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
|
|
||||||
div {
|
button {
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--space-2);
|
gap: var(--space-2);
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
font-weight: 400;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
text-transform: none;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background-color: var(--color-bg-surface);
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: default;
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +280,9 @@
|
|||||||
[data-slot="value"] {
|
[data-slot="value"] {
|
||||||
color: var(--color-danger);
|
color: var(--color-danger);
|
||||||
}
|
}
|
||||||
|
[data-slot="currency"] {
|
||||||
|
color: var(--color-danger);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="currency"] {
|
[data-slot="currency"] {
|
||||||
@@ -428,4 +449,186 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-slot="new-user-sections"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-16);
|
||||||
|
|
||||||
|
@media (max-width: 30rem) {
|
||||||
|
gap: var(--space-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="feature-grid"] {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: var(--space-6);
|
||||||
|
|
||||||
|
@media (max-width: 30rem) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="feature"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2);
|
||||||
|
padding: var(--space-4);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
background-color: var(--color-bg-surface);
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-text);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -0.025rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="api-key-highlight"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-6);
|
||||||
|
|
||||||
|
[data-slot="section-title"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-1);
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: var(--font-size-md);
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
letter-spacing: -0.03125rem;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
@media (max-width: 30rem) {
|
||||||
|
font-size: var(--font-size-md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="key-display"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-3);
|
||||||
|
|
||||||
|
[data-slot="key-container"] {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-3);
|
||||||
|
padding: var(--space-4);
|
||||||
|
border: 2px solid var(--color-accent);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
background-color: var(--color-bg-surface);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
@media (max-width: 40rem) {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-3);
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="key-value"] {
|
||||||
|
flex: 1;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
color: var(--color-text);
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
padding: var(--space-3);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.4;
|
||||||
|
|
||||||
|
@media (max-width: 40rem) {
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
padding: var(--space-2-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 130px;
|
||||||
|
|
||||||
|
@media (max-width: 40rem) {
|
||||||
|
justify-content: center;
|
||||||
|
padding: var(--space-2-5) var(--space-3);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
min-width: 96px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="next-steps"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-6);
|
||||||
|
|
||||||
|
[data-slot="section-title"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-1);
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: var(--font-size-md);
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
letter-spacing: -0.03125rem;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
@media (max-width: 30rem) {
|
||||||
|
font-size: var(--font-size-md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2);
|
||||||
|
list-style-position: inside;
|
||||||
|
|
||||||
|
li {
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
padding: var(--space-1) var(--space-2);
|
||||||
|
background-color: var(--color-bg-surface);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,7 +237,12 @@ function KeysSection() {
|
|||||||
<tr>
|
<tr>
|
||||||
<td data-slot="key-name">{key.name}</td>
|
<td data-slot="key-name">{key.name}</td>
|
||||||
<td data-slot="key-value">
|
<td data-slot="key-value">
|
||||||
<div onClick={() => copyKeyToClipboard(key.key, key.id)} title="Click to copy API key">
|
<button
|
||||||
|
data-color="ghost"
|
||||||
|
disabled={copiedId() === key.id}
|
||||||
|
onClick={() => copyKeyToClipboard(key.key, key.id)}
|
||||||
|
title="Copy API key"
|
||||||
|
>
|
||||||
<span>{formatKey(key.key)}</span>
|
<span>{formatKey(key.key)}</span>
|
||||||
<Show
|
<Show
|
||||||
when={copiedId() === key.id}
|
when={copiedId() === key.id}
|
||||||
@@ -245,7 +250,7 @@ function KeysSection() {
|
|||||||
>
|
>
|
||||||
<IconCheck style={{ width: "14px", height: "14px" }} />
|
<IconCheck style={{ width: "14px", height: "14px" }} />
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td data-slot="key-date" title={formatDateUTC(key.timeCreated)}>
|
<td data-slot="key-date" title={formatDateUTC(key.timeCreated)}>
|
||||||
{formatDateForTable(key.timeCreated)}
|
{formatDateForTable(key.timeCreated)}
|
||||||
@@ -464,7 +469,99 @@ function PaymentsSection() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function NewUserSection() {
|
||||||
|
const params = useParams()
|
||||||
|
const keys = createAsync(() => listKeys(params.id))
|
||||||
|
const [copiedKey, setCopiedKey] = createSignal(false)
|
||||||
|
|
||||||
|
async function copyKeyToClipboard(text: string) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text)
|
||||||
|
setCopiedKey(true)
|
||||||
|
setTimeout(() => setCopiedKey(false), 2000)
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to copy to clipboard:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-slot="new-user-sections">
|
||||||
|
<div data-component="feature-grid">
|
||||||
|
<div data-slot="feature">
|
||||||
|
<h3>Tested & Verified Models</h3>
|
||||||
|
<p>We've benchmarked and tested models specifically for coding agents to ensure the best performance.</p>
|
||||||
|
</div>
|
||||||
|
<div data-slot="feature">
|
||||||
|
<h3>Highest Quality</h3>
|
||||||
|
<p>Access models configured for optimal performance - no downgrades or routing to cheaper providers.</p>
|
||||||
|
</div>
|
||||||
|
<div data-slot="feature">
|
||||||
|
<h3>No Lock-in</h3>
|
||||||
|
<p>Use Zen with any coding agent, and continue using other providers with opencode whenever you want.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div data-component="api-key-highlight">
|
||||||
|
<div data-slot="section-title">
|
||||||
|
<h2>Your API Key</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Show when={keys()?.length}>
|
||||||
|
<div data-slot="key-display">
|
||||||
|
<div data-slot="key-container">
|
||||||
|
<code data-slot="key-value">{keys()![0].key}</code>
|
||||||
|
<button
|
||||||
|
data-color="primary"
|
||||||
|
disabled={copiedKey()}
|
||||||
|
onClick={() => copyKeyToClipboard(keys()![0].key)}
|
||||||
|
title="Copy API key"
|
||||||
|
>
|
||||||
|
<Show
|
||||||
|
when={copiedKey()}
|
||||||
|
fallback={
|
||||||
|
<>
|
||||||
|
<IconCopy style={{ width: "16px", height: "16px" }} /> Copy Key
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconCheck style={{ width: "16px", height: "16px" }} /> Copied!
|
||||||
|
</Show>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div data-component="next-steps">
|
||||||
|
<div data-slot="section-title">
|
||||||
|
<h2>Next Steps</h2>
|
||||||
|
</div>
|
||||||
|
<ol>
|
||||||
|
<li>Copy your API key above</li>
|
||||||
|
<li>
|
||||||
|
Run <code>opencode auth login</code> and select opencode
|
||||||
|
</li>
|
||||||
|
<li>Paste your API key when prompted</li>
|
||||||
|
<li>
|
||||||
|
Run <code>/models</code> to see available models
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function() {
|
export default function() {
|
||||||
|
const params = useParams()
|
||||||
|
const keys = createAsync(() => listKeys(params.id))
|
||||||
|
const usage = createAsync(() => getUsageInfo(params.id))
|
||||||
|
|
||||||
|
const isNewUser = createMemo(() => {
|
||||||
|
const keysList = keys()
|
||||||
|
const usageList = usage()
|
||||||
|
return keysList?.length === 1 && (!usageList || usageList.length === 0)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-page="workspace-[id]">
|
<div data-page="workspace-[id]">
|
||||||
<section data-component="title-section">
|
<section data-component="title-section">
|
||||||
@@ -478,12 +575,14 @@ export default function () {
|
|||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<Show when={!isNewUser()} fallback={<NewUserSection />}>
|
||||||
<div data-slot="sections">
|
<div data-slot="sections">
|
||||||
<KeysSection />
|
<KeysSection />
|
||||||
<BalanceSection />
|
<BalanceSection />
|
||||||
<UsageSection />
|
<UsageSection />
|
||||||
<PaymentsSection />
|
<PaymentsSection />
|
||||||
</div>
|
</div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ $ opencode auth login
|
|||||||
┌ Add credential
|
┌ Add credential
|
||||||
│
|
│
|
||||||
◆ Select provider
|
◆ Select provider
|
||||||
│ ● Anthropic (recommended)
|
│ ● Anthropic
|
||||||
│ ○ OpenAI
|
│ ○ OpenAI
|
||||||
│ ○ Google
|
│ ○ Google
|
||||||
│ ○ Amazon Bedrock
|
│ ○ Amazon Bedrock
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ If you are new, we recommend starting with opencode zen.
|
|||||||
:::
|
:::
|
||||||
|
|
||||||
1. You sign in to **<a href={console}>opencode zen</a>** and get your API key.
|
1. You sign in to **<a href={console}>opencode zen</a>** and get your API key.
|
||||||
2. You run `opencode auth login` and select opencode zen and add your API key.
|
2. You run `opencode auth login` and select opencode and add your API key.
|
||||||
3. Run `/models` in the TUI to see the list of models we recommend.
|
3. Run `/models` in the TUI to see the list of models we recommend.
|
||||||
|
|
||||||
It works like any other provider in opencode. And is completely optional to use
|
It works like any other provider in opencode. And is completely optional to use
|
||||||
@@ -131,9 +131,7 @@ $ opencode auth login
|
|||||||
┌ Add credential
|
┌ Add credential
|
||||||
│
|
│
|
||||||
◆ Select provider
|
◆ Select provider
|
||||||
│ ● Anthropic (recommended)
|
│ ● Anthropic
|
||||||
│ ○ OpenAI
|
|
||||||
│ ○ Google
|
|
||||||
│ ...
|
│ ...
|
||||||
└
|
└
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ opencode zen is an AI gateway that gives you access to these models.
|
|||||||
opencode zen works like any other provider in opencode.
|
opencode zen works like any other provider in opencode.
|
||||||
|
|
||||||
1. You sign in to **<a href={console}>opencode zen</a>** and get your API key.
|
1. You sign in to **<a href={console}>opencode zen</a>** and get your API key.
|
||||||
2. You run `opencode auth login` and select opencode zen and add your API key.
|
2. You run `opencode auth login` and select opencode and add your API key.
|
||||||
3. Run `/models` in the TUI to see the list of models we recommend.
|
3. Run `/models` in the TUI to see the list of models we recommend.
|
||||||
|
|
||||||
You are charged per request and you can add credits to your account.
|
You are charged per request and you can add credits to your account.
|
||||||
|
|||||||
Reference in New Issue
Block a user