summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJay V <[email protected]>2025-05-26 20:33:21 -0400
committerJay V <[email protected]>2025-05-26 20:33:21 -0400
commite1c897c1aed381aa07354f398417aeff6a720471 (patch)
tree06a30c750d80c9f36bbfab7e95379818e754f574
parent39f54e83e1497b95dd9f9d2201a5a7c3c47538e6 (diff)
downloadopencode-e1c897c1aed381aa07354f398417aeff6a720471.tar.gz
opencode-e1c897c1aed381aa07354f398417aeff6a720471.zip
styles
-rw-r--r--app/packages/web/src/components/Share.tsx108
-rw-r--r--app/packages/web/src/components/share.module.css79
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>&#9679;</span>
- <span>{connectionStatus()}</span>
- </p>
+ <div data-section="title">
+ <h1>Untitled conversation</h1>
+ <p>
+ <span data-status={connectionStatus()[0]}>&#9679;</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>&mdash;</span>
+ }
+ </li>
+ <li>
+ <span>Input Tokens</span>
+ {sessionInfo()?.tokens?.input ?
+ <span>{sessionInfo()?.tokens?.input}</span>
+ :
+ <span data-placeholder>&mdash;</span>
+ }
+ </li>
+ <li>
+ <span>Output Tokens</span>
+ {sessionInfo()?.tokens?.output ?
+ <span>{sessionInfo()?.tokens?.output}</span>
+ :
+ <span data-placeholder>&mdash;</span>
+ }
+ </li>
+ <li>
+ <span>Reasoning Tokens</span>
+ {sessionInfo()?.tokens?.reasoning ?
+ <span>{sessionInfo()?.tokens?.reasoning}</span>
+ :
+ <span data-placeholder>&mdash;</span>
+ }
+ </li>
+ </ul>
+ <div class={styles.context}>
+ <button>View Context &gt;</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);
+ }
+ }
+}