diff options
| author | Jay V <[email protected]> | 2025-05-26 20:33:21 -0400 |
|---|---|---|
| committer | Jay V <[email protected]> | 2025-05-26 20:33:21 -0400 |
| commit | e1c897c1aed381aa07354f398417aeff6a720471 (patch) | |
| tree | 06a30c750d80c9f36bbfab7e95379818e754f574 | |
| parent | 39f54e83e1497b95dd9f9d2201a5a7c3c47538e6 (diff) | |
| download | opencode-e1c897c1aed381aa07354f398417aeff6a720471.tar.gz opencode-e1c897c1aed381aa07354f398417aeff6a720471.zip | |
styles
| -rw-r--r-- | app/packages/web/src/components/Share.tsx | 108 | ||||
| -rw-r--r-- | app/packages/web/src/components/share.module.css | 79 |
2 files changed, 141 insertions, 46 deletions
diff --git a/app/packages/web/src/components/Share.tsx b/app/packages/web/src/components/Share.tsx index f99771055..ae163e81d 100644 --- a/app/packages/web/src/components/Share.tsx +++ b/app/packages/web/src/components/Share.tsx @@ -2,6 +2,8 @@ import { createSignal, onCleanup, onMount, Show, For } from "solid-js" import styles from "./share.module.css" import { type UIMessage } from "ai" +type Status = "disconnected" | "connecting" | "connected" | "error" | "reconnecting" + type Message = { key: string content: string @@ -15,11 +17,22 @@ type SessionInfo = { } } +function getStatusText(status: [Status, string?]): string { + switch (status[0]) { + case "connected": return "Connected" + case "connecting": return "Connecting..." + case "disconnected": return "Disconnected" + case "reconnecting": return "Reconnecting..." + case "error": return status[1] || "Error" + default: return "Unknown" + } +} + export default function Share(props: { api: string }) { let params = new URLSearchParams(document.location.search) const sessionId = params.get("id") - const [connectionStatus, setConnectionStatus] = createSignal("Disconnected") + const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>(["disconnected", "Disconnected"]) const [sessionInfo, setSessionInfo] = createSignal<SessionInfo | null>(null) const [systemMessage, setSystemMessage] = createSignal<Message | null>(null) const [messages, setMessages] = createSignal<Message[]>([]) @@ -33,13 +46,13 @@ export default function Share(props: { api: string }) { if (!sessionId) { console.error("Session ID not found in environment variables") - setConnectionStatus("Error: Session ID not found") + setConnectionStatus(["error", "Session ID not found"]) return } if (!apiUrl) { console.error("API URL not found in environment variables") - setConnectionStatus("Error: API URL not found") + setConnectionStatus(["error", "API URL not found"]) return } @@ -53,7 +66,7 @@ export default function Share(props: { api: string }) { socket.close() } - setConnectionStatus("Connecting...") + setConnectionStatus(["connecting"]) // Always use secure WebSocket protocol (wss) const wsBaseUrl = apiUrl.replace(/^https?:\/\//, "wss://") @@ -65,7 +78,7 @@ export default function Share(props: { api: string }) { // Handle connection opening socket.onopen = () => { - setConnectionStatus("Connected") + setConnectionStatus(["connected"]) console.log("WebSocket connection established") } @@ -113,13 +126,13 @@ export default function Share(props: { api: string }) { // Handle errors socket.onerror = (error) => { console.error("WebSocket error:", error) - setConnectionStatus("Error: Connection failed") + setConnectionStatus(["error", "Connection failed"]) } // Handle connection close and reconnection socket.onclose = (event) => { console.log(`WebSocket closed: ${event.code} ${event.reason}`) - setConnectionStatus("Disconnected, reconnecting...") + setConnectionStatus(["reconnecting"]) // Try to reconnect after 2 seconds clearTimeout(reconnectTimer) @@ -144,43 +157,58 @@ export default function Share(props: { api: string }) { }) return ( - <main> + <main class={`${styles.root} not-content`}> <div class={styles.header}> - <h1>Untitled conversation</h1> - <p> - <span>●</span> - <span>{connectionStatus()}</span> - </p> + <div data-section="title"> + <h1>Untitled conversation</h1> + <p> + <span data-status={connectionStatus()[0]}>●</span> + <span>{getStatusText(connectionStatus())}</span> + </p> + </div> + <div data-section="row"> + <ul class={styles.stats}> + <li> + <span>Cost</span> + {sessionInfo()?.cost ? + <span>{sessionInfo()?.cost}</span> + : + <span data-placeholder>—</span> + } + </li> + <li> + <span>Input Tokens</span> + {sessionInfo()?.tokens?.input ? + <span>{sessionInfo()?.tokens?.input}</span> + : + <span data-placeholder>—</span> + } + </li> + <li> + <span>Output Tokens</span> + {sessionInfo()?.tokens?.output ? + <span>{sessionInfo()?.tokens?.output}</span> + : + <span data-placeholder>—</span> + } + </li> + <li> + <span>Reasoning Tokens</span> + {sessionInfo()?.tokens?.reasoning ? + <span>{sessionInfo()?.tokens?.reasoning}</span> + : + <span data-placeholder>—</span> + } + </li> + </ul> + <div class={styles.context}> + <button>View Context ></button> + </div> + </div> </div> <div style={{ margin: "2rem 0" }}> - <Show when={sessionInfo()}> - <div - style={{ - padding: "1rem", - marginBottom: "1rem", - border: "1px solid #dee2e6", - }} - > - <h4 style={{ margin: "0 0 0.75rem 0" }}>Session Information</h4> - <div style={{ display: "flex", gap: "1.5rem" }}> - <div> - <strong>Input Tokens:</strong>{" "} - {sessionInfo()?.tokens?.input || 0} - </div> - <div> - <strong>Output Tokens:</strong>{" "} - {sessionInfo()?.tokens?.output || 0} - </div> - <div> - <strong>Reasoning Tokens:</strong>{" "} - {sessionInfo()?.tokens?.reasoning || 0} - </div> - </div> - </div> - </Show> - {/* Display system message as context in the Session Information block */} <Show when={systemMessage()}> <div @@ -687,6 +715,6 @@ export default function Share(props: { api: string }) { </Show> </div> </div> - </main> + </main > ) } diff --git a/app/packages/web/src/components/share.module.css b/app/packages/web/src/components/share.module.css index 1e8f5c9b0..bf0e03bb2 100644 --- a/app/packages/web/src/components/share.module.css +++ b/app/packages/web/src/components/share.module.css @@ -1,22 +1,89 @@ +.root { + line-height: 1; +} + .header { display: flex; - align-items: center; - justify-content: space-between; + flex-direction: column; + gap: 0.75rem; + + [data-section="title"] { + display: flex; + align-items: center; + justify-content: space-between; + } + + [data-section="row"] { + display: flex; + flex-direction: column; + gap: 0.375rem; + } h1 { - font-size: 1.125rem; + font-size: 1.75rem; font-weight: 500; } p { display: flex; + gap: 0.375rem; + font-size: 0.75rem; + + span:first-child { + color: var(--sl-color-gray-5); + + &[data-status="connected"] { color: var(--sl-color-green); } + &[data-status="connecting"] { color: var(--sl-color-orange); } + &[data-status="disconnected"] { color: var(--sl-color-gray-5); } + &[data-status="reconnecting"] { color: var(--sl-color-orange); } + &[data-status="error"] { color: var(--sl-color-red); } + } + span:last-child { + color: var(--sl-color-text-dimmed); + text-transform: uppercase; + letter-spacing: 0.05em; + } + } +} + +.stats { + list-style-type: none; + padding: 0; + margin: 0; + display: flex; + gap: 1rem; + + li { + display: flex; + align-items: center; gap: 0.5rem; font-size: 0.875rem; span:first-child { - &.connected { color: var(--sl-color-green); } - &.connecting { color: var(--sl-color-orange); } - &.disconnected { color: var(--sl-color-hairline); } + color: var(--sl-color-text-dimmed); + text-transform: uppercase; + letter-spacing: 0.05em; + } + span:last-child { + &[data-placeholder] { + color: var(--sl-color-text-dimmed); + } } } } +.context { + button { + appearance: none; + background: none; + border: none; + padding: 0; + margin: 0; + font-size: 0.875rem; + color: var(--sl-color-text-dimmed); + cursor: pointer; + + &:hover { + color: var(--sl-color-primary); + } + } +} |
