Overhaul UI with chat-like interface
Major UI improvements:
- Revamp timeline messages with chat-like interface
- User messages now on right with white text on blue background
- Agent/tool messages on left with black text on grey background
- Chat bubbles extend up to 80% of screen width
- Maintain left-aligned text for code readability
- Move metadata to outer gutters
- Show turn duration for end-of-turn messages
- Integrate tool calls within agent message bubbles
- Add thinking indicator with animated dots when LLM is processing
- Replace buttons with intuitive icons (copy, info, etc.)
- Improve tool call presentation
- Simplify to single row design with all essential info
- Add clear status indicators for success/pending/error
- Fix horizontal scrolling for long commands and outputs
- Prevent tool name truncation
- Improve spacing and alignment throughout
- Enhance header and status displays
- Move Last Commit to dedicated third column in header grid
- Add proper labeling with two-row structure
- Provide consistent styling across all status elements
- Other UI refinements
- Add root URL redirection to demo page
- Fix spacing throughout the interface
- Optimize CSS for better performance
- Ensure consistent styling across components
- Improve command output display and wrapping
Co-Authored-By: sketch <hello@sketch.dev>
diff --git a/webui/src/web-components/sketch-container-status.ts b/webui/src/web-components/sketch-container-status.ts
index ba745cc..a0db867 100644
--- a/webui/src/web-components/sketch-container-status.ts
+++ b/webui/src/web-components/sketch-container-status.ts
@@ -1,4 +1,4 @@
-import { State } from "../types";
+import { State, AgentMessage } from "../types";
import { LitElement, css, html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { formatNumber } from "../utils";
@@ -13,11 +13,92 @@
@state()
showDetails: boolean = false;
+ @state()
+ lastCommit: { hash: string; pushedBranch?: string } | null = null;
+
+ @state()
+ lastCommitCopied: boolean = false;
+
// 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`
+ /* Last commit display styling */
+ .last-commit {
+ display: flex;
+ flex-direction: column;
+ padding: 3px 8px;
+ cursor: pointer;
+ position: relative;
+ margin: 4px 0;
+ transition: color 0.2s ease;
+ }
+
+ .last-commit:hover {
+ color: #0366d6;
+ }
+
+ .last-commit-title {
+ color: #666;
+ font-family: system-ui, sans-serif;
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 1.2;
+ }
+
+ .last-commit-hash {
+ font-family: monospace;
+ font-size: 12px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ /* Styles for the last commit in main grid */
+ .last-commit-column {
+ justify-content: flex-start;
+ }
+
+ .info-label {
+ color: #666;
+ font-family: system-ui, sans-serif;
+ font-size: 11px;
+ font-weight: 500;
+ }
+
+ .last-commit-main {
+ cursor: pointer;
+ position: relative;
+ padding-top: 0;
+ }
+
+ .last-commit-main:hover {
+ color: #0366d6;
+ }
+
+ .main-grid-commit {
+ font-family: monospace;
+ font-size: 12px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .commit-hash-indicator {
+ color: #666;
+ }
+
+ .commit-branch-indicator {
+ color: #28a745;
+ }
+
+ .no-commit-indicator {
+ color: #999;
+ font-style: italic;
+ font-size: 12px;
+ }
+
.info-container {
display: flex;
align-items: center;
@@ -71,6 +152,7 @@
.info-value {
font-size: 11px;
font-weight: 600;
+ word-break: break-all;
}
[title] {
@@ -115,8 +197,10 @@
}
.main-info-grid {
- display: flex;
- gap: 20px;
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ gap: 10px;
+ width: 100%;
}
.info-column {
@@ -232,6 +316,58 @@
this.requestUpdate();
}
+ /**
+ * Update the last commit information based on messages
+ */
+ public updateLastCommitInfo(newMessages: AgentMessage[]): void {
+ if (!newMessages || newMessages.length === 0) return;
+
+ // Process messages in chronological order (latest last)
+ for (const message of newMessages) {
+ if (
+ message.type === "commit" &&
+ message.commits &&
+ message.commits.length > 0
+ ) {
+ // Get the first commit from the list
+ const commit = message.commits[0];
+ if (commit) {
+ this.lastCommit = {
+ hash: commit.hash,
+ pushedBranch: commit.pushed_branch,
+ };
+ this.lastCommitCopied = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Copy commit info to clipboard when clicked
+ */
+ private copyCommitInfo(event: MouseEvent): void {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (!this.lastCommit) return;
+
+ const textToCopy =
+ this.lastCommit.pushedBranch || this.lastCommit.hash.substring(0, 8);
+
+ navigator.clipboard
+ .writeText(textToCopy)
+ .then(() => {
+ this.lastCommitCopied = true;
+ // Reset the copied state after 2 seconds
+ setTimeout(() => {
+ this.lastCommitCopied = false;
+ }, 2000);
+ })
+ .catch((err) => {
+ console.error("Failed to copy commit info:", err);
+ });
+ }
+
formatHostname() {
// Only display outside hostname
const outsideHostname = this.state?.outside_hostname;
@@ -472,6 +608,31 @@
>
</div>
</div>
+
+ <!-- Third column: Last Commit -->
+ <div class="info-column last-commit-column">
+ <div class="info-item">
+ <span class="info-label">Last Commit</span>
+ </div>
+ <div
+ class="info-item last-commit-main"
+ @click=${(e: MouseEvent) => this.copyCommitInfo(e)}
+ title="Click to copy"
+ >
+ ${this.lastCommitCopied
+ ? html`<span class="copied-indicator">Copied!</span>`
+ : ""}
+ ${this.lastCommit
+ ? this.lastCommit.pushedBranch
+ ? html`<span class="commit-branch-indicator main-grid-commit"
+ >${this.lastCommit.pushedBranch}</span
+ >`
+ : html`<span class="commit-hash-indicator main-grid-commit"
+ >${this.lastCommit.hash.substring(0, 8)}</span
+ >`
+ : html`<span class="no-commit-indicator">N/A</span>`}
+ </div>
+ </div>
</div>
<!-- Info toggle button -->
@@ -485,6 +646,8 @@
<!-- Expanded info panel -->
<div class="info-expanded ${this.showDetails ? "active" : ""}">
+ <!-- Last Commit section moved to main grid -->
+
<div class="detailed-info-grid">
<div class="info-item">
<span class="info-label">Commit:</span>
@@ -499,6 +662,12 @@
>
</div>
<div class="info-item">
+ <span class="info-label">Session ID:</span>
+ <span id="sessionId" class="info-value"
+ >${this.state?.session_id || "N/A"}</span
+ >
+ </div>
+ <div class="info-item">
<span class="info-label">Input tokens:</span>
<span id="inputTokens" class="info-value"
>${formatNumber(