blob: ac7424f499dda08e2282a862c45e74e4a0750e8c [file] [log] [blame]
Sean McCullough86b56862025-04-18 13:04:03 -07001import { State } from "../types";
Sean McCulloughb29f8912025-04-20 15:39:11 -07002import { LitElement, css, html } from "lit";
3import { customElement, property } from "lit/decorators.js";
Josh Bleecher Snydera0801ad2025-04-25 19:34:53 +00004import { formatNumber } from "../utils";
Sean McCullough86b56862025-04-18 13:04:03 -07005
6@customElement("sketch-container-status")
7export class SketchContainerStatus extends LitElement {
8 // Header bar: Container status details
9
10 @property()
11 state: State;
12
13 // See https://lit.dev/docs/components/styles/ for how lit-element handles CSS.
14 // Note that these styles only apply to the scope of this web component's
15 // shadow DOM node, so they won't leak out or collide with CSS declared in
16 // other components or the containing web page (...unless you want it to do that).
17 static styles = css`
18 .info-card {
19 background: #f9f9f9;
20 border-radius: 8px;
21 padding: 15px;
22 margin-bottom: 20px;
23 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
24 display: none; /* Hidden in the combined layout */
25 }
26
27 .info-grid {
28 display: flex;
29 flex-wrap: wrap;
30 gap: 8px;
31 background: #f9f9f9;
32 border-radius: 4px;
33 padding: 4px 10px;
34 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
35 flex: 1;
36 }
37
38 .info-item {
39 display: flex;
40 align-items: center;
41 white-space: nowrap;
42 margin-right: 10px;
43 font-size: 13px;
44 }
45
46 .info-label {
47 font-size: 11px;
48 color: #555;
49 margin-right: 3px;
50 font-weight: 500;
51 }
52
53 .info-value {
54 font-size: 11px;
55 font-weight: 600;
56 }
57
Philip Zeyligerd1402952025-04-23 03:54:37 +000058 [title] {
59 cursor: help;
60 text-decoration: underline dotted;
61 }
62
Sean McCullough86b56862025-04-18 13:04:03 -070063 .cost {
64 color: #2e7d32;
65 }
66
67 .info-item a {
68 --tw-text-opacity: 1;
69 color: rgb(37 99 235 / var(--tw-text-opacity, 1));
70 text-decoration: inherit;
71 }
72 `;
73
74 constructor() {
75 super();
76 }
77
Philip Zeyligerd1402952025-04-23 03:54:37 +000078 formatHostname() {
Philip Zeyliger18532b22025-04-23 21:11:46 +000079 const outsideHostname = this.state?.outside_hostname;
80 const insideHostname = this.state?.inside_hostname;
Philip Zeyligerd1402952025-04-23 03:54:37 +000081
Philip Zeyliger18532b22025-04-23 21:11:46 +000082 if (!outsideHostname || !insideHostname) {
Philip Zeyligerd1402952025-04-23 03:54:37 +000083 return this.state?.hostname;
84 }
85
Philip Zeyliger18532b22025-04-23 21:11:46 +000086 if (outsideHostname === insideHostname) {
87 return outsideHostname;
Philip Zeyligerd1402952025-04-23 03:54:37 +000088 }
89
Philip Zeyliger18532b22025-04-23 21:11:46 +000090 return `${outsideHostname}:${insideHostname}`;
Philip Zeyligerd1402952025-04-23 03:54:37 +000091 }
92
93 formatWorkingDir() {
Philip Zeyliger18532b22025-04-23 21:11:46 +000094 const outsideWorkingDir = this.state?.outside_working_dir;
95 const insideWorkingDir = this.state?.inside_working_dir;
Philip Zeyligerd1402952025-04-23 03:54:37 +000096
Philip Zeyliger18532b22025-04-23 21:11:46 +000097 if (!outsideWorkingDir || !insideWorkingDir) {
Philip Zeyligerd1402952025-04-23 03:54:37 +000098 return this.state?.working_dir;
99 }
100
Philip Zeyliger18532b22025-04-23 21:11:46 +0000101 if (outsideWorkingDir === insideWorkingDir) {
102 return outsideWorkingDir;
Philip Zeyligerd1402952025-04-23 03:54:37 +0000103 }
104
Philip Zeyliger18532b22025-04-23 21:11:46 +0000105 return `${outsideWorkingDir}:${insideWorkingDir}`;
Philip Zeyligerd1402952025-04-23 03:54:37 +0000106 }
107
108 getHostnameTooltip() {
Philip Zeyliger18532b22025-04-23 21:11:46 +0000109 const outsideHostname = this.state?.outside_hostname;
110 const insideHostname = this.state?.inside_hostname;
Philip Zeyligerd1402952025-04-23 03:54:37 +0000111
112 if (
Philip Zeyliger18532b22025-04-23 21:11:46 +0000113 !outsideHostname ||
114 !insideHostname ||
115 outsideHostname === insideHostname
Philip Zeyligerd1402952025-04-23 03:54:37 +0000116 ) {
117 return "";
118 }
119
Philip Zeyliger18532b22025-04-23 21:11:46 +0000120 return `Outside: ${outsideHostname}, Inside: ${insideHostname}`;
121 }
122
123 getWorkingDirTooltip() {
124 const outsideWorkingDir = this.state?.outside_working_dir;
125 const insideWorkingDir = this.state?.inside_working_dir;
126
127 if (
128 !outsideWorkingDir ||
129 !insideWorkingDir ||
130 outsideWorkingDir === insideWorkingDir
131 ) {
132 return "";
133 }
134
135 return `Outside: ${outsideWorkingDir}, Inside: ${insideWorkingDir}`;
Philip Zeyligerd1402952025-04-23 03:54:37 +0000136 }
137
Sean McCullough86b56862025-04-18 13:04:03 -0700138 // See https://lit.dev/docs/components/lifecycle/
139 connectedCallback() {
140 super.connectedCallback();
141 // register event listeners
142 }
143
144 // See https://lit.dev/docs/components/lifecycle/
145 disconnectedCallback() {
146 super.disconnectedCallback();
147 // unregister event listeners
148 }
149
150 render() {
151 return html`
152 <div class="info-grid">
153 <div class="info-item">
154 <a href="logs">Logs</a>
155 </div>
156 <div class="info-item">
157 <a href="download">Download</a>
158 </div>
159 <div class="info-item">
Philip Zeyligerd1402952025-04-23 03:54:37 +0000160 <span
161 id="hostname"
162 class="info-value"
163 title="${this.getHostnameTooltip()}"
164 >
165 ${this.formatHostname()}
166 </span>
Sean McCullough86b56862025-04-18 13:04:03 -0700167 </div>
168 <div class="info-item">
Philip Zeyligerd1402952025-04-23 03:54:37 +0000169 <span
170 id="workingDir"
171 class="info-value"
172 title="${this.getWorkingDirTooltip()}"
Sean McCullough86b56862025-04-18 13:04:03 -0700173 >
Philip Zeyligerd1402952025-04-23 03:54:37 +0000174 ${this.formatWorkingDir()}
175 </span>
Sean McCullough86b56862025-04-18 13:04:03 -0700176 </div>
Philip Zeyligerd1402952025-04-23 03:54:37 +0000177 ${this.state?.git_origin
178 ? html`
179 <div class="info-item">
180 <span class="info-label">Origin:</span>
181 <span id="gitOrigin" class="info-value"
182 >${this.state?.git_origin}</span
183 >
184 </div>
185 `
186 : ""}
Sean McCullough86b56862025-04-18 13:04:03 -0700187 <div class="info-item">
188 <span class="info-label">Commit:</span>
189 <span id="initialCommit" class="info-value"
190 >${this.state?.initial_commit?.substring(0, 8)}</span
191 >
192 </div>
193 <div class="info-item">
194 <span class="info-label">Msgs:</span>
195 <span id="messageCount" class="info-value"
196 >${this.state?.message_count}</span
197 >
198 </div>
199 <div class="info-item">
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000200 <span class="info-label">Input tokens:</span>
Sean McCullough86b56862025-04-18 13:04:03 -0700201 <span id="inputTokens" class="info-value"
Josh Bleecher Snydera0801ad2025-04-25 19:34:53 +0000202 >${formatNumber(
203 (this.state?.total_usage?.input_tokens || 0) +
Autoformatter5c70bfe2025-04-25 21:28:00 +0000204 (this.state?.total_usage?.cache_read_input_tokens || 0) +
205 (this.state?.total_usage?.cache_creation_input_tokens || 0),
Josh Bleecher Snydera0801ad2025-04-25 19:34:53 +0000206 )}</span
Sean McCullough86b56862025-04-18 13:04:03 -0700207 >
208 </div>
209 <div class="info-item">
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000210 <span class="info-label">Output tokens:</span>
Sean McCullough86b56862025-04-18 13:04:03 -0700211 <span id="outputTokens" class="info-value"
Josh Bleecher Snydera0801ad2025-04-25 19:34:53 +0000212 >${formatNumber(this.state?.total_usage?.output_tokens)}</span
Sean McCullough86b56862025-04-18 13:04:03 -0700213 >
214 </div>
215 <div class="info-item">
216 <span class="info-label">Cost:</span>
Sean McCullough71941bd2025-04-18 13:31:48 -0700217 <span id="totalCost" class="info-value cost"
218 >$${(this.state?.total_usage?.total_cost_usd || 0).toFixed(2)}</span
219 >
Sean McCullough86b56862025-04-18 13:04:03 -0700220 </div>
221 </div>
222 `;
223 }
224}
225
226declare global {
227 interface HTMLElementTagNameMap {
228 "sketch-container-status": SketchContainerStatus;
229 }
230}