webui: auto-generate types.ts from go structs
diff --git a/loop/webui/src/web-components/demo/sketch-view-mode-select.demo.html b/loop/webui/src/web-components/demo/sketch-view-mode-select.demo.html
index 0d71067..7f795fc 100644
--- a/loop/webui/src/web-components/demo/sketch-view-mode-select.demo.html
+++ b/loop/webui/src/web-components/demo/sketch-view-mode-select.demo.html
@@ -21,6 +21,7 @@
console.log("view mode change event: ", evt);
const msgDiv = document.querySelector("#selected-mode");
msgDiv.innerText = `selected mode: ${evt.detail.mode}`;
+ viewModeSelect.activeMode = evt.detail.mode;
});
});
</script>
diff --git a/loop/webui/src/web-components/sketch-app-shell.ts b/loop/webui/src/web-components/sketch-app-shell.ts
index 62bcd03..6ef9232 100644
--- a/loop/webui/src/web-components/sketch-app-shell.ts
+++ b/loop/webui/src/web-components/sketch-app-shell.ts
@@ -1,7 +1,7 @@
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { DataManager, ConnectionStatus } from "../data";
-import { State, TimelineMessage, ToolCall } from "../types";
+import { State, AgentMessage } from "../types";
import "./sketch-container-status";
import "./sketch-view-mode-select";
import "./sketch-network-status";
@@ -11,7 +11,6 @@
import "./sketch-charts";
import "./sketch-terminal";
import { SketchDiffView } from "./sketch-diff-view";
-import { View } from "vega";
type ViewMode = "chat" | "diff" | "charts" | "terminal";
@@ -175,7 +174,7 @@
// Chat messages
@property()
- messages: TimelineMessage[] = [];
+ messages: AgentMessage[] = [];
@property()
chatMessageText: string = "";
@@ -186,7 +185,14 @@
private dataManager = new DataManager();
@property()
- containerState: State = { title: "", os: "", total_usage: {} };
+ containerState: State = {
+ title: "",
+ os: "",
+ message_count: 0,
+ hostname: "",
+ working_dir: "",
+ initial_commit: "",
+ };
// Track if this is the first load of messages
@state()
@@ -470,15 +476,12 @@
});
}
- mergeAndDedupe(
- arr1: TimelineMessage[],
- arr2: TimelineMessage[],
- ): TimelineMessage[] {
+ mergeAndDedupe(arr1: AgentMessage[], arr2: AgentMessage[]): AgentMessage[] {
const mergedArray = [...arr1, ...arr2];
const seenIds = new Set<number>();
- const toolCallResults = new Map<string, TimelineMessage>();
+ const toolCallResults = new Map<string, AgentMessage>();
- let ret: TimelineMessage[] = mergedArray
+ let ret: AgentMessage[] = mergedArray
.filter((msg) => {
if (msg.type == "tool") {
toolCallResults.set(msg.tool_call_id, msg);
@@ -491,7 +494,7 @@
seenIds.add(msg.idx);
return true;
})
- .sort((a: TimelineMessage, b: TimelineMessage) => a.idx - b.idx);
+ .sort((a: AgentMessage, b: AgentMessage) => a.idx - b.idx);
// Attach any tool_call result messages to the original message's tool_call object.
ret.forEach((msg) => {
@@ -506,7 +509,7 @@
private handleDataChanged(eventData: {
state: State;
- newMessages: TimelineMessage[];
+ newMessages: AgentMessage[];
isFirstFetch?: boolean;
}): void {
const { state, newMessages, isFirstFetch } = eventData;
diff --git a/loop/webui/src/web-components/sketch-charts.ts b/loop/webui/src/web-components/sketch-charts.ts
index a933c44..8cf2606 100644
--- a/loop/webui/src/web-components/sketch-charts.ts
+++ b/loop/webui/src/web-components/sketch-charts.ts
@@ -2,7 +2,7 @@
import { css, html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { TopLevelSpec } from "vega-lite";
-import type { TimelineMessage } from "../types";
+import type { AgentMessage } from "../types";
import "vega-embed";
import { VisualizationSpec } from "vega-embed";
@@ -13,7 +13,7 @@
@customElement("sketch-charts")
export class SketchCharts extends LitElement {
@property({ type: Array })
- messages: TimelineMessage[] = [];
+ messages: AgentMessage[] = [];
@state()
private chartData: { timestamp: Date; cost: number }[] = [];
@@ -77,7 +77,7 @@
}
private calculateCumulativeCostData(
- messages: TimelineMessage[],
+ messages: AgentMessage[],
): { timestamp: Date; cost: number }[] {
if (!messages || messages.length === 0) {
return [];
diff --git a/loop/webui/src/web-components/sketch-container-status.test.ts b/loop/webui/src/web-components/sketch-container-status.test.ts
index 4a0c397..db11a4e 100644
--- a/loop/webui/src/web-components/sketch-container-status.test.ts
+++ b/loop/webui/src/web-components/sketch-container-status.test.ts
@@ -16,6 +16,9 @@
cache_read_input_tokens: 300,
cache_creation_input_tokens: 400,
total_cost_usd: 0.25,
+ start_time: "",
+ messages: 0,
+ tool_uses: {},
},
};
@@ -78,6 +81,13 @@
title: "Partial Test",
total_usage: {
input_tokens: 500,
+ start_time: "",
+ messages: 0,
+ output_tokens: 0,
+ cache_read_input_tokens: 0,
+ cache_creation_input_tokens: 0,
+ total_cost_usd: 0,
+ tool_uses: {},
},
};
diff --git a/loop/webui/src/web-components/sketch-network-status.ts b/loop/webui/src/web-components/sketch-network-status.ts
index e4af00b..2a0e455 100644
--- a/loop/webui/src/web-components/sketch-network-status.ts
+++ b/loop/webui/src/web-components/sketch-network-status.ts
@@ -1,8 +1,5 @@
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
-import { DataManager, ConnectionStatus } from "../data";
-import { State, TimelineMessage } from "../types";
-import { SketchContainerStatus } from "./sketch-container-status";
@customElement("sketch-network-status")
export class SketchNetworkStatus extends LitElement {
diff --git a/loop/webui/src/web-components/sketch-terminal.ts b/loop/webui/src/web-components/sketch-terminal.ts
index 106f7e2..8e4805e 100644
--- a/loop/webui/src/web-components/sketch-terminal.ts
+++ b/loop/webui/src/web-components/sketch-terminal.ts
@@ -2,9 +2,7 @@
import { FitAddon } from "@xterm/addon-fit";
import { css, html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators.js";
-import { DataManager, ConnectionStatus } from "../data";
-import { State, TimelineMessage } from "../types";
+import { customElement } from "lit/decorators.js";
import "./sketch-container-status";
@customElement("sketch-terminal")
diff --git a/loop/webui/src/web-components/sketch-timeline-message.test.ts b/loop/webui/src/web-components/sketch-timeline-message.test.ts
index eb1b788..bc74202 100644
--- a/loop/webui/src/web-components/sketch-timeline-message.test.ts
+++ b/loop/webui/src/web-components/sketch-timeline-message.test.ts
@@ -1,11 +1,14 @@
import { test, expect } from "@sand4rt/experimental-ct-web";
import { SketchTimelineMessage } from "./sketch-timeline-message";
-import { TimelineMessage, ToolCall, GitCommit, Usage } from "../types";
+import {
+ AgentMessage,
+ CodingAgentMessageType,
+ GitCommit,
+ Usage,
+} from "../types";
// Helper function to create mock timeline messages
-function createMockMessage(
- props: Partial<TimelineMessage> = {},
-): TimelineMessage {
+function createMockMessage(props: Partial<AgentMessage> = {}): AgentMessage {
return {
idx: props.idx || 0,
type: props.type || "agent",
@@ -40,7 +43,15 @@
});
test.skip("renders with correct message type classes", async ({ mount }) => {
- const messageTypes = ["user", "agent", "tool", "error"];
+ const messageTypes: CodingAgentMessageType[] = [
+ "user",
+ "agent",
+ "error",
+ "budget",
+ "tool",
+ "commit",
+ "auto",
+ ];
for (const type of messageTypes) {
const message = createMockMessage({ type });
@@ -124,6 +135,7 @@
output_tokens: 300,
cost_usd: 0.025,
cache_read_input_tokens: 50,
+ cache_creation_input_tokens: 0,
};
const message = createMockMessage({
diff --git a/loop/webui/src/web-components/sketch-timeline-message.ts b/loop/webui/src/web-components/sketch-timeline-message.ts
index 88e9f14..e34d61f 100644
--- a/loop/webui/src/web-components/sketch-timeline-message.ts
+++ b/loop/webui/src/web-components/sketch-timeline-message.ts
@@ -1,16 +1,16 @@
import { css, html, LitElement } from "lit";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { customElement, property } from "lit/decorators.js";
-import { State, TimelineMessage } from "../types";
+import { AgentMessage } from "../types";
import { marked, MarkedOptions } from "marked";
import "./sketch-tool-calls";
@customElement("sketch-timeline-message")
export class SketchTimelineMessage extends LitElement {
@property()
- message: TimelineMessage;
+ message: AgentMessage;
@property()
- previousMessage: TimelineMessage;
+ previousMessage: AgentMessage;
// See https://lit.dev/docs/components/styles/ for how lit-element handles CSS.
// Note that these styles only apply to the scope of this web component's
diff --git a/loop/webui/src/web-components/sketch-timeline.ts b/loop/webui/src/web-components/sketch-timeline.ts
index 7deeb97..80efd0f 100644
--- a/loop/webui/src/web-components/sketch-timeline.ts
+++ b/loop/webui/src/web-components/sketch-timeline.ts
@@ -2,13 +2,13 @@
import { PropertyValues } from "lit";
import { repeat } from "lit/directives/repeat.js";
import { customElement, property, state } from "lit/decorators.js";
-import { State, TimelineMessage } from "../types";
+import { AgentMessage } from "../types";
import "./sketch-timeline-message";
@customElement("sketch-timeline")
export class SketchTimeline extends LitElement {
@property()
- messages: TimelineMessage[] = [];
+ messages: AgentMessage[] = [];
// Track if we should scroll to the bottom
@state()
@@ -177,9 +177,9 @@
this.scrollContainer?.removeEventListener("scroll", this._handleScroll);
}
- // messageKey uniquely identifes a TimelineMessage based on its ID and tool_calls, so
+ // messageKey uniquely identifes a AgentMessage based on its ID and tool_calls, so
// that we only re-render <sketch-message> elements that we need to re-render.
- messageKey(message: TimelineMessage): string {
+ messageKey(message: AgentMessage): string {
// If the message has tool calls, and any of the tool_calls get a response, we need to
// re-render that message.
const toolCallResponses = message.tool_calls
@@ -194,7 +194,7 @@
<div id="scroll-container">
<div class="timeline-container">
${repeat(this.messages, this.messageKey, (message, index) => {
- let previousMessage: TimelineMessage;
+ let previousMessage: AgentMessage;
if (index > 0) {
previousMessage = this.messages[index - 1];
}
diff --git a/loop/webui/src/web-components/sketch-tool-calls.ts b/loop/webui/src/web-components/sketch-tool-calls.ts
index 2b4c426..9461d6d 100644
--- a/loop/webui/src/web-components/sketch-tool-calls.ts
+++ b/loop/webui/src/web-components/sketch-tool-calls.ts
@@ -1,8 +1,6 @@
import { css, html, LitElement } from "lit";
-import { repeat } from "lit/directives/repeat.js";
import { customElement, property } from "lit/decorators.js";
-import { State, ToolCall } from "../types";
-import { marked, MarkedOptions } from "marked";
+import { ToolCall } from "../types";
import "./sketch-tool-card";
@customElement("sketch-tool-calls")
diff --git a/loop/webui/src/web-components/sketch-tool-card.ts b/loop/webui/src/web-components/sketch-tool-card.ts
index 800c665..0144ba0 100644
--- a/loop/webui/src/web-components/sketch-tool-card.ts
+++ b/loop/webui/src/web-components/sketch-tool-card.ts
@@ -1,8 +1,7 @@
import { css, html, LitElement } from "lit";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
-import { repeat } from "lit/directives/repeat.js";
import { customElement, property } from "lit/decorators.js";
-import { State, ToolCall } from "../types";
+import { ToolCall } from "../types";
import { marked, MarkedOptions } from "marked";
function renderMarkdown(markdownContent: string): string {
diff --git a/loop/webui/src/web-components/sketch-view-mode-select.ts b/loop/webui/src/web-components/sketch-view-mode-select.ts
index d67da0b..52f8a4e 100644
--- a/loop/webui/src/web-components/sketch-view-mode-select.ts
+++ b/loop/webui/src/web-components/sketch-view-mode-select.ts
@@ -1,7 +1,5 @@
import { css, html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators.js";
-import { DataManager, ConnectionStatus } from "../data";
-import { State, TimelineMessage } from "../types";
+import { customElement, property } from "lit/decorators.js";
import "./sketch-container-status";
@customElement("sketch-view-mode-select")
@@ -11,11 +9,6 @@
activeMode: "chat" | "diff" | "charts" | "terminal" = "chat";
// Header bar: view mode buttons
- // See https://lit.dev/docs/components/styles/ for how lit-element handles CSS.
- // Note that these styles only apply to the scope of this web component's
- // shadow DOM node, so they won't leak out or collide with CSS declared in
- // other components or the containing web page (...unless you want it to do that).
-
static styles = css`
/* View Mode Button Styles */
.view-mode-buttons {