blob: c6aa81104621ac77a6891c81f64dce2e92e9a43d [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() {
78 const hostHostname = this.state?.host_hostname;
79 const runtimeHostname = this.state?.runtime_hostname;
80
81 if (!hostHostname || !runtimeHostname) {
82 return this.state?.hostname;
83 }
84
85 if (hostHostname === runtimeHostname) {
86 return hostHostname;
87 }
88
89 return `${hostHostname}:${runtimeHostname}`;
90 }
91
92 formatWorkingDir() {
93 const hostWorkingDir = this.state?.host_working_dir;
94 const runtimeWorkingDir = this.state?.runtime_working_dir;
95
96 if (!hostWorkingDir || !runtimeWorkingDir) {
97 return this.state?.working_dir;
98 }
99
100 if (hostWorkingDir === runtimeWorkingDir) {
101 return hostWorkingDir;
102 }
103
104 return `${hostWorkingDir}:${runtimeWorkingDir}`;
105 }
106
107 getHostnameTooltip() {
108 const hostHostname = this.state?.host_hostname;
109 const runtimeHostname = this.state?.runtime_hostname;
110
111 if (!hostHostname || !runtimeHostname || hostHostname === runtimeHostname) {
112 return "";
113 }
114
115 return `Host: ${hostHostname}, Runtime: ${runtimeHostname}`;
116 }
117
118 getWorkingDirTooltip() {
119 const hostWorkingDir = this.state?.host_working_dir;
120 const runtimeWorkingDir = this.state?.runtime_working_dir;
121
122 if (
123 !hostWorkingDir ||
124 !runtimeWorkingDir ||
125 hostWorkingDir === runtimeWorkingDir
126 ) {
127 return "";
128 }
129
130 return `Host: ${hostWorkingDir}, Runtime: ${runtimeWorkingDir}`;
131 }
132
Sean McCullough86b56862025-04-18 13:04:03 -0700133 // See https://lit.dev/docs/components/lifecycle/
134 connectedCallback() {
135 super.connectedCallback();
136 // register event listeners
137 }
138
139 // See https://lit.dev/docs/components/lifecycle/
140 disconnectedCallback() {
141 super.disconnectedCallback();
142 // unregister event listeners
143 }
144
145 render() {
146 return html`
147 <div class="info-grid">
148 <div class="info-item">
149 <a href="logs">Logs</a>
150 </div>
151 <div class="info-item">
152 <a href="download">Download</a>
153 </div>
154 <div class="info-item">
Philip Zeyligerd1402952025-04-23 03:54:37 +0000155 <span
156 id="hostname"
157 class="info-value"
158 title="${this.getHostnameTooltip()}"
159 >
160 ${this.formatHostname()}
161 </span>
Sean McCullough86b56862025-04-18 13:04:03 -0700162 </div>
163 <div class="info-item">
Philip Zeyligerd1402952025-04-23 03:54:37 +0000164 <span
165 id="workingDir"
166 class="info-value"
167 title="${this.getWorkingDirTooltip()}"
Sean McCullough86b56862025-04-18 13:04:03 -0700168 >
Philip Zeyligerd1402952025-04-23 03:54:37 +0000169 ${this.formatWorkingDir()}
170 </span>
Sean McCullough86b56862025-04-18 13:04:03 -0700171 </div>
Philip Zeyligerd1402952025-04-23 03:54:37 +0000172 ${this.state?.git_origin
173 ? html`
174 <div class="info-item">
175 <span class="info-label">Origin:</span>
176 <span id="gitOrigin" class="info-value"
177 >${this.state?.git_origin}</span
178 >
179 </div>
180 `
181 : ""}
Sean McCullough86b56862025-04-18 13:04:03 -0700182 <div class="info-item">
183 <span class="info-label">Commit:</span>
184 <span id="initialCommit" class="info-value"
185 >${this.state?.initial_commit?.substring(0, 8)}</span
186 >
187 </div>
188 <div class="info-item">
189 <span class="info-label">Msgs:</span>
190 <span id="messageCount" class="info-value"
191 >${this.state?.message_count}</span
192 >
193 </div>
194 <div class="info-item">
195 <span class="info-label">In:</span>
196 <span id="inputTokens" class="info-value"
197 >${this.state?.total_usage?.input_tokens}</span
198 >
199 </div>
200 <div class="info-item">
201 <span class="info-label">Cache Read:</span>
202 <span id="cacheReadInputTokens" class="info-value"
203 >${this.state?.total_usage?.cache_read_input_tokens}</span
204 >
205 </div>
206 <div class="info-item">
207 <span class="info-label">Cache Create:</span>
208 <span id="cacheCreationInputTokens" class="info-value"
209 >${this.state?.total_usage?.cache_creation_input_tokens}</span
210 >
211 </div>
212 <div class="info-item">
213 <span class="info-label">Out:</span>
214 <span id="outputTokens" class="info-value"
215 >${this.state?.total_usage?.output_tokens}</span
216 >
217 </div>
218 <div class="info-item">
219 <span class="info-label">Cost:</span>
Sean McCullough71941bd2025-04-18 13:31:48 -0700220 <span id="totalCost" class="info-value cost"
221 >$${(this.state?.total_usage?.total_cost_usd || 0).toFixed(2)}</span
222 >
Sean McCullough86b56862025-04-18 13:04:03 -0700223 </div>
224 </div>
225 `;
226 }
227}
228
229declare global {
230 interface HTMLElementTagNameMap {
231 "sketch-container-status": SketchContainerStatus;
232 }
233}