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-timeline.ts b/webui/src/web-components/sketch-timeline.ts
index 686ea01..7fbe83a 100644
--- a/webui/src/web-components/sketch-timeline.ts
+++ b/webui/src/web-components/sketch-timeline.ts
@@ -11,6 +11,16 @@
@property({ attribute: false })
messages: AgentMessage[] = [];
+ // Active state properties to show thinking indicator
+ @property({ attribute: false })
+ agentState: string | null = null;
+
+ @property({ attribute: false })
+ llmCalls: number = 0;
+
+ @property({ attribute: false })
+ toolCalls: string[] = [];
+
// Track if we should scroll to the bottom
@state()
private scrollingState: "pinToLatest" | "floating" = "pinToLatest";
@@ -38,30 +48,20 @@
.timeline-container {
width: 100%;
position: relative;
+ max-width: 100%;
+ margin: 0 auto;
+ padding: 0 15px;
+ box-sizing: border-box;
}
- /* Timeline styles that should remain unchanged */
+ /* Chat-like timeline styles */
.timeline {
position: relative;
margin: 10px 0;
scroll-behavior: smooth;
}
- .timeline::before {
- content: "";
- position: absolute;
- top: 0;
- bottom: 0;
- left: 15px;
- width: 2px;
- background: #e0e0e0;
- border-radius: 1px;
- }
-
- /* Hide the timeline vertical line when there are no messages */
- .timeline.empty::before {
- display: none;
- }
+ /* Remove the vertical timeline line */
#scroll-container {
overflow: auto;
@@ -114,6 +114,64 @@
font-size: 1rem;
text-align: left;
}
+
+ /* Thinking indicator styles */
+ .thinking-indicator {
+ padding-left: 85px;
+ margin-top: 5px;
+ margin-bottom: 15px;
+ display: flex;
+ }
+
+ .thinking-bubble {
+ background-color: #f1f1f1;
+ border-radius: 15px;
+ padding: 10px 15px;
+ max-width: 80px;
+ color: black;
+ position: relative;
+ border-bottom-left-radius: 5px;
+ }
+
+ .thinking-dots {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 4px;
+ height: 14px;
+ }
+
+ .dot {
+ width: 6px;
+ height: 6px;
+ background-color: #888;
+ border-radius: 50%;
+ opacity: 0.6;
+ }
+
+ .dot:nth-child(1) {
+ animation: pulse 1.5s infinite ease-in-out;
+ }
+
+ .dot:nth-child(2) {
+ animation: pulse 1.5s infinite ease-in-out 0.3s;
+ }
+
+ .dot:nth-child(3) {
+ animation: pulse 1.5s infinite ease-in-out 0.6s;
+ }
+
+ @keyframes pulse {
+ 0%,
+ 100% {
+ opacity: 0.4;
+ transform: scale(1);
+ }
+ 50% {
+ opacity: 1;
+ transform: scale(1.2);
+ }
+ }
`;
constructor() {
@@ -257,6 +315,9 @@
}
// Otherwise render the regular timeline with messages
+ const isThinking =
+ this.llmCalls > 0 || (this.toolCalls && this.toolCalls.length > 0);
+
return html`
<div id="scroll-container">
<div class="timeline-container">
@@ -271,6 +332,19 @@
.open=${false}
></sketch-timeline-message>`;
})}
+ ${isThinking
+ ? html`
+ <div class="thinking-indicator">
+ <div class="thinking-bubble">
+ <div class="thinking-dots">
+ <div class="dot"></div>
+ <div class="dot"></div>
+ <div class="dot"></div>
+ </div>
+ </div>
+ </div>
+ `
+ : ""}
</div>
</div>
<div