blob: 8079d2e350e4ec00cf86bc8c98f1245837ea6a85 [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";
Sean McCullough86b56862025-04-18 13:04:03 -07004
5@customElement("sketch-container-status")
6export class SketchContainerStatus extends LitElement {
7 // Header bar: Container status details
8
9 @property()
10 state: State;
11
12 // See https://lit.dev/docs/components/styles/ for how lit-element handles CSS.
13 // Note that these styles only apply to the scope of this web component's
14 // shadow DOM node, so they won't leak out or collide with CSS declared in
15 // other components or the containing web page (...unless you want it to do that).
16 static styles = css`
17 .info-card {
18 background: #f9f9f9;
19 border-radius: 8px;
20 padding: 15px;
21 margin-bottom: 20px;
22 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
23 display: none; /* Hidden in the combined layout */
24 }
25
26 .info-grid {
27 display: flex;
28 flex-wrap: wrap;
29 gap: 8px;
30 background: #f9f9f9;
31 border-radius: 4px;
32 padding: 4px 10px;
33 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
34 flex: 1;
35 }
36
37 .info-item {
38 display: flex;
39 align-items: center;
40 white-space: nowrap;
41 margin-right: 10px;
42 font-size: 13px;
43 }
44
45 .info-label {
46 font-size: 11px;
47 color: #555;
48 margin-right: 3px;
49 font-weight: 500;
50 }
51
52 .info-value {
53 font-size: 11px;
54 font-weight: 600;
55 }
56
Philip Zeyligerd1402952025-04-23 03:54:37 +000057 [title] {
58 cursor: help;
59 text-decoration: underline dotted;
60 }
61
Sean McCullough86b56862025-04-18 13:04:03 -070062 .cost {
63 color: #2e7d32;
64 }
65
66 .info-item a {
67 --tw-text-opacity: 1;
68 color: rgb(37 99 235 / var(--tw-text-opacity, 1));
69 text-decoration: inherit;
70 }
71 `;
72
73 constructor() {
74 super();
75 }
76
Philip Zeyligerd1402952025-04-23 03:54:37 +000077 formatHostname() {
Philip Zeyliger18532b22025-04-23 21:11:46 +000078 const outsideHostname = this.state?.outside_hostname;
79 const insideHostname = this.state?.inside_hostname;
Philip Zeyligerd1402952025-04-23 03:54:37 +000080
Philip Zeyliger18532b22025-04-23 21:11:46 +000081 if (!outsideHostname || !insideHostname) {
Philip Zeyligerd1402952025-04-23 03:54:37 +000082 return this.state?.hostname;
83 }
84
Philip Zeyliger18532b22025-04-23 21:11:46 +000085 if (outsideHostname === insideHostname) {
86 return outsideHostname;
Philip Zeyligerd1402952025-04-23 03:54:37 +000087 }
88
Philip Zeyliger18532b22025-04-23 21:11:46 +000089 return `${outsideHostname}:${insideHostname}`;
Philip Zeyligerd1402952025-04-23 03:54:37 +000090 }
91
92 formatWorkingDir() {
Philip Zeyliger18532b22025-04-23 21:11:46 +000093 const outsideWorkingDir = this.state?.outside_working_dir;
94 const insideWorkingDir = this.state?.inside_working_dir;
Philip Zeyligerd1402952025-04-23 03:54:37 +000095
Philip Zeyliger18532b22025-04-23 21:11:46 +000096 if (!outsideWorkingDir || !insideWorkingDir) {
Philip Zeyligerd1402952025-04-23 03:54:37 +000097 return this.state?.working_dir;
98 }
99
Philip Zeyliger18532b22025-04-23 21:11:46 +0000100 if (outsideWorkingDir === insideWorkingDir) {
101 return outsideWorkingDir;
Philip Zeyligerd1402952025-04-23 03:54:37 +0000102 }
103
Philip Zeyliger18532b22025-04-23 21:11:46 +0000104 return `${outsideWorkingDir}:${insideWorkingDir}`;
Philip Zeyligerd1402952025-04-23 03:54:37 +0000105 }
106
107 getHostnameTooltip() {
Philip Zeyliger18532b22025-04-23 21:11:46 +0000108 const outsideHostname = this.state?.outside_hostname;
109 const insideHostname = this.state?.inside_hostname;
Philip Zeyligerd1402952025-04-23 03:54:37 +0000110
111 if (
Philip Zeyliger18532b22025-04-23 21:11:46 +0000112 !outsideHostname ||
113 !insideHostname ||
114 outsideHostname === insideHostname
Philip Zeyligerd1402952025-04-23 03:54:37 +0000115 ) {
116 return "";
117 }
118
Philip Zeyliger18532b22025-04-23 21:11:46 +0000119 return `Outside: ${outsideHostname}, Inside: ${insideHostname}`;
120 }
121
122 getWorkingDirTooltip() {
123 const outsideWorkingDir = this.state?.outside_working_dir;
124 const insideWorkingDir = this.state?.inside_working_dir;
125
126 if (
127 !outsideWorkingDir ||
128 !insideWorkingDir ||
129 outsideWorkingDir === insideWorkingDir
130 ) {
131 return "";
132 }
133
134 return `Outside: ${outsideWorkingDir}, Inside: ${insideWorkingDir}`;
Philip Zeyligerd1402952025-04-23 03:54:37 +0000135 }
136
Sean McCullough86b56862025-04-18 13:04:03 -0700137 // See https://lit.dev/docs/components/lifecycle/
138 connectedCallback() {
139 super.connectedCallback();
140 // register event listeners
141 }
142
143 // See https://lit.dev/docs/components/lifecycle/
144 disconnectedCallback() {
145 super.disconnectedCallback();
146 // unregister event listeners
147 }
148
149 render() {
150 return html`
151 <div class="info-grid">
152 <div class="info-item">
153 <a href="logs">Logs</a>
154 </div>
155 <div class="info-item">
156 <a href="download">Download</a>
157 </div>
158 <div class="info-item">
Philip Zeyligerd1402952025-04-23 03:54:37 +0000159 <span
160 id="hostname"
161 class="info-value"
162 title="${this.getHostnameTooltip()}"
163 >
164 ${this.formatHostname()}
165 </span>
Sean McCullough86b56862025-04-18 13:04:03 -0700166 </div>
167 <div class="info-item">
Philip Zeyligerd1402952025-04-23 03:54:37 +0000168 <span
169 id="workingDir"
170 class="info-value"
171 title="${this.getWorkingDirTooltip()}"
Sean McCullough86b56862025-04-18 13:04:03 -0700172 >
Philip Zeyligerd1402952025-04-23 03:54:37 +0000173 ${this.formatWorkingDir()}
174 </span>
Sean McCullough86b56862025-04-18 13:04:03 -0700175 </div>
Philip Zeyligerd1402952025-04-23 03:54:37 +0000176 ${this.state?.git_origin
177 ? html`
178 <div class="info-item">
179 <span class="info-label">Origin:</span>
180 <span id="gitOrigin" class="info-value"
181 >${this.state?.git_origin}</span
182 >
183 </div>
184 `
185 : ""}
Sean McCullough86b56862025-04-18 13:04:03 -0700186 <div class="info-item">
187 <span class="info-label">Commit:</span>
188 <span id="initialCommit" class="info-value"
189 >${this.state?.initial_commit?.substring(0, 8)}</span
190 >
191 </div>
192 <div class="info-item">
193 <span class="info-label">Msgs:</span>
194 <span id="messageCount" class="info-value"
195 >${this.state?.message_count}</span
196 >
197 </div>
198 <div class="info-item">
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000199 <span class="info-label">Input tokens:</span>
Sean McCullough86b56862025-04-18 13:04:03 -0700200 <span id="inputTokens" class="info-value"
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000201 >${(this.state?.total_usage?.input_tokens || 0) +
202 (this.state?.total_usage?.cache_read_input_tokens || 0) +
203 (this.state?.total_usage?.cache_creation_input_tokens || 0)}</span
Sean McCullough86b56862025-04-18 13:04:03 -0700204 >
205 </div>
206 <div class="info-item">
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000207 <span class="info-label">Output tokens:</span>
Sean McCullough86b56862025-04-18 13:04:03 -0700208 <span id="outputTokens" class="info-value"
209 >${this.state?.total_usage?.output_tokens}</span
210 >
211 </div>
212 <div class="info-item">
213 <span class="info-label">Cost:</span>
Sean McCullough71941bd2025-04-18 13:31:48 -0700214 <span id="totalCost" class="info-value cost"
215 >$${(this.state?.total_usage?.total_cost_usd || 0).toFixed(2)}</span
216 >
Sean McCullough86b56862025-04-18 13:04:03 -0700217 </div>
218 </div>
219 `;
220 }
221}
222
223declare global {
224 interface HTMLElementTagNameMap {
225 "sketch-container-status": SketchContainerStatus;
226 }
227}