mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-09-02 09:39:37 +00:00
feat(ux): complete UX overhaul — design system, onboarding, web polish (#1277)
* feat(ux): complete UX overhaul — design system, boot screen, onboarding, web polish Shared design system: CSS custom properties for spacing, typography, transitions, and color tokens used across web UI and boot screen. Boot screen: compact feature-tags line showing enabled subsystems (db, tools, routines, heartbeat, skills, sandbox, embeddings) at a glance. Downgrade startup info logs (libSQL, webhook, workspace seed) to debug level since the boot screen now covers this. Onboarding wizard: model picker with live API fetch, provider-aware auth flow, improved error recovery and progress display. Web UI: ARIA attributes, welcome card, streaming debounce, connection status banner, skeleton loaders, send cooldown. CLI: doctor command enhancements, status command cleanup, REPL banner consolidation, shared fmt module. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat(ux): Apple-level design refinements — spring physics, glass morphism, chat polish Merge staging theme support (dark/light/system toggle) and layer UX polish on top: spring-physics motion, glass morphism depth, chat experience improvements, and responsive mobile refinements. Design system: - Restore and extend design token system (spacing, typography, timing, easing) with legacy aliases for theme compatibility - Add shadow tiers, accent glow, glass morphism, spring easing tokens - Tokens defined in both dark (:root) and light ([data-theme="light"]) Micro-interactions (Phase 2): - Spring-overshoot message entry animation (slideUp) - Spring-scale button press on all interactive buttons - Tab crossfade animation, tool card smooth accordion (max-height) - Modal scale(0.95) + blur(8px) entry, toast spring slide - Sidebar width crossfade, card hover lift Visual depth (Phase 3): - Tab bar glass morphism + surface highlight + sliding indicator - Active tab accent background pill - Assistant message accent left border, user message bubble tail - Floating input area (rounded + shadow + margin) Chat polish (Phase 4): - Smooth streaming cursor (cursorPulse), message hover timestamps - Time separators (Today/Yesterday/date) - Textarea smooth auto-expand, send button glow Settings & forms (Phase 5): - iOS-style toggle switches for boolean settings - Input focus glow, save feedback spring animation - Welcome card with gradient background + proper spacing - Sticky settings group headers with glass backdrop Accessibility & mobile (Phase 6): - Animated focus ring, prefers-reduced-motion global kill-switch - Touch target audit (44px min), mobile bottom-sheet modals - Mobile bottom tab bar, toast redesign (icon + border + countdown) - Thread hover translateX, badge in_progress pulse Bug fixes: - Gateway/TEE popover z-index (tab-bar z-index: 200, popovers 500) - Connection lost banner as fixed top bar instead of flex child - Sidebar collapse keeps toggle + new thread buttons visible - Downgrade noisy startup logs (db, webhook, vector) to debug - Remove green dot pulse animation on connected status - Deduplicate confirm-modal in HTML, add tab-indicator div Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat(web): mobile layout improvements — sidebar toggle, settings drill-down, tab bar polish - Fix mobile sidebar toggle: use expanded-mobile class instead of collapsed, add backdrop overlay, auto-close on thread select, outside-click dismiss - Settings: replace cramped horizontal tabs with drill-down navigation (category list → detail view → back button) - Bottom tab bar: add glass morphism, hide theme toggle, flip tab indicator to top edge - Keep thread toggle button visible in collapsed 36px sidebar strip Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat(repl): interactive approval selector and transient status lines - Replace ASCII-art approval box with clean horizontal rule card - Add inquire-based interactive selector for tool approvals (↑↓ + Enter) - Selector runs directly from send_status via spawn_blocking, with stdin_locked flag to prevent readline from competing for stdin - Transient thinking/tool-started lines: each replaces the previous, all erased before final output (no clutter left in scrollback) - Esc in selector sends denial so agent never gets stuck Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: widen TurnCost token fields to u64 and remove unused variable - Change input_tokens/output_tokens from u32 to u64 in StatusUpdate::TurnCost, SseEvent::TurnCost, and the thread_ops emit site to avoid truncation on large conversations - Remove unused _routine_engine_for_loop binding in agent_loop.rs Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: reduce startup log noise — demote info to debug Demote routine startup messages (builder, WASM tools, tunnel, WASM channels) from info to debug so the default log output stays clean. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(web): allow CDN scripts in CSP connect-src directive Add cdn.jsdelivr.net and cdnjs.cloudflare.com to connect-src so the browser can fetch marked.js and DOMPurify without CSP violations. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * style: fix cargo fmt in repl.rs Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(web): gate turn_cost SSE handler on current thread Prevents cost badge from attaching to the wrong message when switching threads or receiving events from background threads. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * ci: retrigger CI * fix: add missing extension_manager to webhook EngineContext The webhook trigger path added in #736 was missing the extension_manager field introduced by #1453. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: ignore RUSTSEC-2026-0049 rustls-webpki CRL advisory Low impact — requires compromised CA to exploit. Tracked for upstream rustls-webpki upgrade. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(routines): use fields.join for cron normalization Use split_whitespace fields instead of re-trimming the original string to avoid preserving extra internal whitespace in cron expressions. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat(repl): Apple-style approval card — clean vertical flow - Drop verbose tool description (the command IS the decision surface) - Unified vertical pipe layout: ◆ header → │ params → │ selector - Selector options show keyboard shortcuts inline: Approve (y) - Compact help message, answered state uses └ to close the flow - No horizontal rules, no blank-line padding — just breathing room Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * refactor(repl): replace inquire with crossterm for approval selector Drop the inquire dependency (which pulled in crossterm 0.25, duplicating the existing 0.28). The 3-option approval selector is now built directly with crossterm raw mode — same UX, zero new dependencies. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore(deps): upgrade crossterm 0.28 → 0.29, eliminate duplication termimad (via crokey) uses crossterm 0.29. Upgrading our direct dependency from 0.28 to 0.29 collapses to a single crossterm version in the dependency tree. Also migrated termimad::crossterm:: references to the direct crossterm import. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: address review comments — box_top off-by-one, smart_truncate overflow, mobile theme toggle - Fix box_top() fill calculation: was off-by-one, producing boxes 1 char too wide (fmt.rs) - Fix smart_truncate(): account for "..." in the budget so output never exceeds max_chars (repl.rs) - Move theme toggle to settings sidebar on mobile instead of display:none, so mobile users can still switch themes (style.css, index.html, app.js) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * style: cargo fmt repl.rs Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: address review — retry duplication, CSP connect-src, deny color - Remove failed message before retry to prevent duplicate user messages - Revert connect-src to 'self' — CDN hosts only need script-src - Use red for Deny confirmation in REPL approval selector Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
8638895879
commit
a09c023642
+26
-2
@@ -317,7 +317,7 @@ impl<'a> LoopDelegate for ChatDelegate<'a> {
|
||||
.channels
|
||||
.send_status(
|
||||
&self.message.channel,
|
||||
StatusUpdate::Thinking("Calling LLM...".into()),
|
||||
StatusUpdate::Thinking(format!("Thinking (step {iteration})...")),
|
||||
&self.message.metadata,
|
||||
)
|
||||
.await;
|
||||
@@ -435,7 +435,7 @@ impl<'a> LoopDelegate for ChatDelegate<'a> {
|
||||
.channels
|
||||
.send_status(
|
||||
&self.message.channel,
|
||||
StatusUpdate::Thinking(format!("Executing {} tool(s)...", tool_calls.len())),
|
||||
StatusUpdate::Thinking(contextual_tool_message(&tool_calls)),
|
||||
&self.message.metadata,
|
||||
)
|
||||
.await;
|
||||
@@ -969,6 +969,30 @@ pub(super) fn check_auth_required(
|
||||
Some((name, instructions))
|
||||
}
|
||||
|
||||
/// Build a contextual thinking message based on tool names.
|
||||
///
|
||||
/// Instead of a generic "Executing 2 tool(s)..." this returns messages like
|
||||
/// "Running command..." or "Fetching page..." for single-tool calls, falling
|
||||
/// back to "Executing N tool(s)..." for multi-tool calls.
|
||||
fn contextual_tool_message(tool_calls: &[crate::llm::ToolCall]) -> String {
|
||||
if tool_calls.len() == 1 {
|
||||
match tool_calls[0].name.as_str() {
|
||||
"shell" => "Running command...".into(),
|
||||
"web_fetch" => "Fetching page...".into(),
|
||||
"memory_search" => "Searching memory...".into(),
|
||||
"memory_write" => "Writing to memory...".into(),
|
||||
"memory_read" => "Reading memory...".into(),
|
||||
"http_request" => "Making HTTP request...".into(),
|
||||
"file_read" => "Reading file...".into(),
|
||||
"file_write" => "Writing file...".into(),
|
||||
"json_transform" => "Transforming data...".into(),
|
||||
name => format!("Running {name}..."),
|
||||
}
|
||||
} else {
|
||||
format!("Executing {} tool(s)...", tool_calls.len())
|
||||
}
|
||||
}
|
||||
|
||||
/// Compact messages for retry after a context-length-exceeded error.
|
||||
///
|
||||
/// Keeps all `System` messages (which carry the system prompt and instructions),
|
||||
|
||||
@@ -529,8 +529,8 @@ pub fn normalize_cron_expression(schedule: &str) -> String {
|
||||
let trimmed = schedule.trim();
|
||||
let fields: Vec<&str> = trimmed.split_whitespace().collect();
|
||||
match fields.len() {
|
||||
5 => format!("0 {} *", trimmed),
|
||||
6 => format!("{} *", trimmed),
|
||||
5 => format!("0 {} *", fields.join(" ")),
|
||||
6 => format!("{} *", fields.join(" ")),
|
||||
_ => trimmed.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,6 +556,33 @@ impl Agent {
|
||||
.await;
|
||||
}
|
||||
|
||||
// Emit per-turn cost summary
|
||||
{
|
||||
let usage = self.cost_guard().model_usage().await;
|
||||
let (total_in, total_out, total_cost) =
|
||||
usage
|
||||
.values()
|
||||
.fold((0u64, 0u64, rust_decimal::Decimal::ZERO), |acc, m| {
|
||||
(
|
||||
acc.0 + m.input_tokens,
|
||||
acc.1 + m.output_tokens,
|
||||
acc.2 + m.cost,
|
||||
)
|
||||
});
|
||||
let _ = self
|
||||
.channels
|
||||
.send_status(
|
||||
&message.channel,
|
||||
StatusUpdate::TurnCost {
|
||||
input_tokens: total_in,
|
||||
output_tokens: total_out,
|
||||
cost_usd: format!("${:.4}", total_cost),
|
||||
},
|
||||
&message.metadata,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(SubmissionResult::response(response))
|
||||
}
|
||||
Ok(AgenticLoopResult::NeedApproval { pending }) => {
|
||||
|
||||
Reference in New Issue
Block a user