diff --git a/cloud/app/src/routes/index.tsx b/cloud/app/src/routes/index.tsx index 3474a993..b9344bc5 100644 --- a/cloud/app/src/routes/index.tsx +++ b/cloud/app/src/routes/index.tsx @@ -8,7 +8,8 @@ import IMG_VSCODE from "../asset/lander/screenshot-vscode.png" import IMG_GITHUB from "../asset/lander/screenshot-github.png" import { IconCopy, IconCheck } from "../component/icon" import { createAsync, query, redirect } from "@solidjs/router" -import { getActor, withActor } from "~/context/auth" +import { getActor } from "~/context/auth" +import { withActor } from "~/context/auth.withActor" import { Account } from "@opencode/cloud-core/account.js" function CopyStatus() { diff --git a/cloud/app/src/routes/workspace/[id].tsx b/cloud/app/src/routes/workspace/[id].tsx index abada1c8..bfeb0688 100644 --- a/cloud/app/src/routes/workspace/[id].tsx +++ b/cloud/app/src/routes/workspace/[id].tsx @@ -49,7 +49,7 @@ const createPortalUrl = action(async (returnUrl: string) => { return withActor(() => Billing.generatePortalUrl({ returnUrl })) }, "portalUrl") -export default function() { +export default function () { const actor = createAsync(() => getActor()) onMount(() => { console.log("MOUNTED", actor()) @@ -154,126 +154,174 @@ export default function() { } return ( -
-

Actor

-
{JSON.stringify(actor())}
-

API Keys

- - setKeyName(e.currentTarget.value)} - onKeyPress={(e) => e.key === "Enter" && handleCreateKey()} - /> -
- - -
-
- } - > - - -
- + {/* Actor Section */} +
+
+

Actor

+

Current authenticated user information and session details.

+
+
{JSON.stringify(actor())}
+
+ + {/* API Keys Section */} +
+
+

API Keys

+

Manage your API keys for accessing opencode services.

+
+ -

Create an API key to access opencode gateway

+
+ setKeyName(e.currentTarget.value)} + onKeyPress={(e) => e.key === "Enter" && handleCreateKey()} + /> +
+ + +
} > - {(key) => ( -
-
-
{key.name}
-
{formatKey(key.key)}
-
- Created: {formatDate(key.timeCreated)} - {key.timeUsed && ` • Last used: ${formatDate(key.timeUsed)}`} + + +
+ +

Create an API key to access opencode gateway

+
+ } + > + {(key) => ( +
+
+
{key.name}
+
{formatKey(key.key)}
+
+ Created: {formatDate(key.timeCreated)} + {key.timeUsed && ` • Last used: ${formatDate(key.timeUsed)}`} +
+
+
+ +
-
- - + )} + +
+
+ + {/* Balance Section */} +
+
+

Balance

+

Manage your billing and add credits to your account.

+
+
+

+ {(() => { + const balanceStr = ((billingInfo()?.billing?.balance ?? 0) / 100000000).toFixed(2) + return `$${balanceStr === "-0.00" ? "0.00" : balanceStr}` + })()} +

+ +
+
+ + {/* Payments Section */} +
+
+

Payments History

+

Your recent payment transactions.

+
+
+ +

No payment history yet. Your payments will appear here after your first purchase.

-
- )} - - + } + > + {(payment) => ( +
+ {payment.id} + {" | "} + ${((payment.amount ?? 0) / 100000000).toFixed(2)} + {" | "} + {new Date(payment.timeCreated).toLocaleDateString()} +
+ )} + + + -

Balance

-

Manage your billing and add credits to your account.

-

- {(() => { - const balanceStr = ((billingInfo()?.billing?.balance ?? 0) / 100000000).toFixed(2) - return `$${balanceStr === "-0.00" ? "0.00" : balanceStr}` - })()} -

- - -

Payments History

-

Your recent payment transactions.

- No payments found.

}> - {(payment) => ( -
- {payment.id} - {" | "} - ${((payment.amount ?? 0) / 100000000).toFixed(2)} - {" | "} - {new Date(payment.timeCreated).toLocaleDateString()} -
- )} -
- -

Usage History

-

Your recent API usage and costs.

- No usage found.

}> - {(usage) => ( -
- {usage.model} - {" | "} - {usage.inputTokens + usage.outputTokens} tokens - {" | "} - ${((usage.cost ?? 0) / 100000000).toFixed(4)} - {" | "} - {new Date(usage.timeCreated).toLocaleDateString()} -
- )} -
+ {/* Usage Section */} +
+
+

Usage History

+

Your recent API usage and costs.

+
+
+ +

No API usage yet. Your usage history will appear here after your first API calls.

+
+ } + > + {(usage) => ( +
+ {usage.model} + {" | "} + {usage.inputTokens + usage.outputTokens} tokens + {" | "} + ${((usage.cost ?? 0) / 100000000).toFixed(4)} + {" | "} + {new Date(usage.timeCreated).toLocaleDateString()} +
+ )} + + +
) } diff --git a/cloud/app/src/routes/workspace/index.css b/cloud/app/src/routes/workspace/index.css index 8d1006fc..81bdff5b 100644 --- a/cloud/app/src/routes/workspace/index.css +++ b/cloud/app/src/routes/workspace/index.css @@ -1,251 +1,268 @@ -[data-page="workspace"] { - /* Main content container */ - & > div { - display: flex; - flex-direction: column; - gap: var(--space-6); +/* Root container */ +[data-slot="root"] { + max-width: 64rem; + margin: 0 auto; + width: 100%; + display: flex; + flex-direction: column; + gap: var(--space-6); +} + +/* Adjust header spacing */ +[data-component="workspace-header"] + div { + margin-top: var(--space-2); +} + +/* Section titles */ +[data-slot="section-title"] { + display: flex; + flex-direction: column; + gap: var(--space-0-5); +} + +[data-slot="section-title"] h1 { + font-size: var(--font-size-lg); + font-weight: 500; + 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-lg); + line-height: 1.25; } +} - /* Adjust header spacing */ - [data-component="workspace-header"] + div { - margin-top: var(--space-2); - } +[data-slot="section-title"] p { + font-size: var(--font-size-sm); + color: var(--color-text-muted); +} - /* Section headers */ - h1 { - font-size: var(--font-size-3xl); - font-weight: 500; - line-height: 1.2; - letter-spacing: -0.05em; - margin: 0; - color: var(--color-text); +/* Section descriptions */ +p { + margin: 0; + color: var(--color-text-secondary); + font-size: var(--font-size-md); + line-height: 1.5; +} - @media (max-width: 30rem) { - font-size: var(--font-size-2xl); - line-height: 1.25; - } - } +/* Section containers */ +section { + display: flex; + flex-direction: column; + gap: var(--space-6); +} - /* Section descriptions */ - p { - margin: 0; - color: var(--color-text-secondary); - font-size: var(--font-size-md); - line-height: 1.5; - } +/* API Keys Section */ +[data-slot="create-form"] { + display: flex; + flex-direction: column; + gap: var(--space-3); + padding: var(--space-4); + background-color: var(--color-bg-surface); + border: 1px solid var(--color-border); + border-radius: var(--border-radius-sm); + max-width: 32rem; - /* API Keys Section */ - [data-slot="create-form"] { - display: flex; - flex-direction: column; - gap: var(--space-3); - padding: var(--space-4); - background-color: var(--color-bg-surface); + input { + padding: var(--space-2) var(--space-3); border: 1px solid var(--color-border); - border-radius: var(--space-2); - max-width: 32rem; - - input { - padding: var(--space-2) var(--space-3); - border: 1px solid var(--color-border); - border-radius: var(--space-2); - background-color: var(--color-bg); - color: var(--color-text); - font-size: var(--font-size-sm); - font-family: var(--font-mono); - - &:focus { - outline: none; - border-color: var(--color-accent); - } - - &::placeholder { - color: var(--color-text-disabled); - } - } - - [data-slot="form-actions"] { - display: flex; - gap: var(--space-2); - justify-content: flex-end; - } - } - - [data-slot="key-list"] { - display: flex; - flex-direction: column; - gap: var(--space-2); - } - - [data-slot="key-item"] { - display: flex; - justify-content: space-between; - align-items: flex-start; - padding: var(--space-4); - background-color: var(--color-bg-surface); - border: 1px solid var(--color-border); - border-radius: var(--space-2); - gap: var(--space-4); - - @media (max-width: 30rem) { - flex-direction: column; - gap: var(--space-3); - } - } - - [data-slot="key-info"] { - display: flex; - flex-direction: column; - gap: var(--space-1); - flex: 1; - } - - [data-slot="key-name"] { - font-size: var(--font-size-md); - font-weight: 500; - color: var(--color-text); - } - - [data-slot="key-value"] { - font-size: var(--font-size-xs); - font-family: var(--font-mono); - color: var(--color-text-secondary); - background-color: var(--color-bg); - padding: var(--space-1) var(--space-2); - border-radius: var(--space-1); - border: 1px solid var(--color-border-muted); - } - - [data-slot="key-meta"] { - font-size: var(--font-size-xs); - color: var(--color-text-disabled); - } - - [data-slot="key-actions"] { - display: flex; - gap: var(--space-2); - } - - [data-slot="empty-state"] { - padding: var(--space-8); - text-align: center; - color: var(--color-text-muted); - background-color: var(--color-bg-surface); - border: 1px solid var(--color-border); - border-radius: var(--space-2); - - p { - margin: 0; - font-size: var(--font-size-sm); - } - } - - /* Balance Section */ - [data-slot="balance"] { - display: flex; - flex-direction: column; - gap: var(--space-3); - padding: var(--space-4); - background-color: var(--color-bg-surface); - border: 1px solid var(--color-border); - border-radius: var(--space-2); - max-width: 32rem; - - p { - font-size: var(--font-size-2xl); - font-weight: 500; - color: var(--color-text); - margin: 0; - } - } - - /* Payment and Usage Items */ - [data-slot="payment-item"], - [data-slot="usage-item"] { - display: flex; - align-items: center; - gap: var(--space-4); - padding: var(--space-3); - background-color: var(--color-bg-surface); - border: 1px solid var(--color-border); - border-radius: var(--space-2); - font-size: var(--font-size-sm); - font-family: var(--font-mono); - - @media (max-width: 30rem) { - flex-direction: column; - align-items: flex-start; - gap: var(--space-2); - } - } - - [data-slot="payment-id"], - [data-slot="payment-amount"], - [data-slot="payment-date"], - [data-slot="usage-model"], - [data-slot="usage-tokens"], - [data-slot="usage-cost"], - [data-slot="usage-date"] { - color: var(--color-text-muted); - } - - /* Buttons */ - button { - padding: var(--space-2) var(--space-4); - border: 1px solid var(--color-border); - border-radius: var(--space-2); + border-radius: var(--border-radius-sm); background-color: var(--color-bg); color: var(--color-text); font-size: var(--font-size-sm); - font-family: var(--font-sans); - cursor: pointer; - transition: all 0.15s ease; + font-family: var(--font-mono); - &:hover { - background-color: var(--color-surface-hover); + &:focus { + outline: none; border-color: var(--color-accent); } - &:active { - transform: translateY(1px); - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - - &:hover { - background-color: var(--color-bg); - border-color: var(--color-border); - transform: none; - } - } - - &[color="primary"] { - background-color: var(--color-primary); - border-color: var(--color-primary); - color: var(--color-primary-text); - - &:hover { - background-color: var(--color-primary-hover); - border-color: var(--color-primary-hover); - } - } - - &[color="ghost"] { - background-color: transparent; - border-color: transparent; - color: var(--color-text-muted); - - &:hover { - background-color: var(--color-surface-hover); - border-color: var(--color-border); - color: var(--color-text); - } + &::placeholder { + color: var(--color-text-disabled); } } - @media (prefers-color-scheme: dark) { - /* Dark mode specific adjustments if needed */ + [data-slot="form-actions"] { + display: flex; + gap: var(--space-2); + justify-content: flex-end; + } +} + +[data-slot="key-list"], +[data-slot="payments-list"], +[data-slot="usage-list"] { + display: flex; + flex-direction: column; + gap: var(--space-2); +} + +[data-slot="key-item"] { + display: flex; + justify-content: space-between; + align-items: flex-start; + padding: var(--space-4); + background-color: var(--color-bg-surface); + border: 1px solid var(--color-border); + border-radius: var(--border-radius-sm); + gap: var(--space-4); + + @media (max-width: 30rem) { + flex-direction: column; + gap: var(--space-3); + } +} + +[data-slot="key-info"] { + display: flex; + flex-direction: column; + gap: var(--space-1); + flex: 1; +} + +[data-slot="key-name"] { + font-size: var(--font-size-md); + font-weight: 500; + color: var(--color-text); +} + +[data-slot="key-value"] { + font-size: var(--font-size-xs); + font-family: var(--font-mono); + color: var(--color-text-secondary); + background-color: var(--color-bg); + padding: var(--space-1) var(--space-2); + border-radius: var(--border-radius-sm); + border: 1px solid var(--color-border-muted); +} + +[data-slot="key-meta"] { + font-size: var(--font-size-xs); + color: var(--color-text-disabled); +} + +[data-slot="key-actions"] { + display: flex; + gap: var(--space-2); +} + +[data-slot="empty-state"] { + padding: var(--space-8); + text-align: center; + border: 1px dashed var(--color-border); + border-radius: var(--border-radius-sm); + + p { + margin: 0; + font-size: var(--font-size-sm); + color: var(--color-text-muted); + } +} + +/* Balance Section */ +[data-slot="balance"] { + display: flex; + flex-direction: column; + gap: var(--space-3); + padding: var(--space-4); + background-color: var(--color-bg-surface); + border: 1px solid var(--color-border); + border-radius: var(--border-radius-sm); + max-width: 32rem; + + p { + font-size: var(--font-size-2xl); + font-weight: 500; + color: var(--color-text); + margin: 0; + } +} + +/* Payment and Usage Items */ +[data-slot="payment-item"], +[data-slot="usage-item"] { + display: flex; + align-items: center; + gap: var(--space-4); + padding: var(--space-3); + background-color: var(--color-bg-surface); + border: 1px solid var(--color-border); + border-radius: var(--border-radius-sm); + font-size: var(--font-size-sm); + font-family: var(--font-mono); + + @media (max-width: 30rem) { + flex-direction: column; + align-items: flex-start; + gap: var(--space-2); + } +} + +[data-slot="payment-id"], +[data-slot="payment-amount"], +[data-slot="payment-date"], +[data-slot="usage-model"], +[data-slot="usage-tokens"], +[data-slot="usage-cost"], +[data-slot="usage-date"] { + color: var(--color-text-muted); +} + +/* Buttons */ +button { + padding: var(--space-2) var(--space-4); + border: 1px solid var(--color-border); + border-radius: var(--border-radius-sm); + background-color: var(--color-bg); + color: var(--color-text); + font-size: var(--font-size-sm); + font-family: var(--font-sans); + cursor: pointer; + transition: all 0.15s ease; + + &:hover { + background-color: var(--color-surface-hover); + border-color: var(--color-accent); + } + + &:active { + transform: translateY(1px); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + + &:hover { + background-color: var(--color-bg); + border-color: var(--color-border); + transform: none; + } + } + + &[color="primary"] { + background-color: var(--color-primary); + border-color: var(--color-primary); + color: var(--color-primary-text); + + &:hover { + background-color: var(--color-primary-hover); + border-color: var(--color-primary-hover); + } + } + + &[color="ghost"] { + background-color: transparent; + border-color: transparent; + color: var(--color-text-muted); + + &:hover { + background-color: var(--color-surface-hover); + border-color: var(--color-border); + color: var(--color-text); + } } } diff --git a/cloud/app/src/routes/workspace/workspace.css b/cloud/app/src/routes/workspace/workspace.css index 7ea96aed..f4a8b2af 100644 --- a/cloud/app/src/routes/workspace/workspace.css +++ b/cloud/app/src/routes/workspace/workspace.css @@ -18,7 +18,7 @@ display: flex; justify-content: space-between; align-items: center; - padding: var(--space-4) var(--space-3); + padding: var(--space-4) var(--space-4); margin: calc(-1 * var(--space-6)); margin-bottom: var(--space-6); border-bottom: 1px solid var(--color-border); diff --git a/cloud/app/src/style/token/space.css b/cloud/app/src/style/token/space.css index dcd871c5..7e1a1b39 100644 --- a/cloud/app/src/style/token/space.css +++ b/cloud/app/src/style/token/space.css @@ -39,4 +39,8 @@ body { --space-72: 18rem; --space-80: 20rem; --space-96: 24rem; + + --border-radius-sm: 0.1875rem; + --border-radius-md: 0.3125rem; + --border-radius-lg: 0.5rem; }