Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src-tauri/src/agent/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,9 +485,43 @@ fn hot_swap_llm(app_handle: &AppHandle, model_id: &str) -> Result<(), AppError>
}

fn emit_state(app_handle: &AppHandle, state: &str) -> Result<(), tauri::Error> {
// The widget must be visible while it is actively reflecting a recording,
// even when the user has hidden it. We force it on when recording begins
// and restore the configured visibility once we settle back to idle.
match state {
"recording" => show_widget(app_handle),
"idle" => restore_widget_visibility(app_handle),
_ => {}
}
app_handle.emit("agent-state-changed", serde_json::json!({ "state": state }))
}

/// Force the widget window visible (used when recording starts).
fn show_widget(app_handle: &AppHandle) {
if let Some(widget) = app_handle.get_webview_window("widget") {
let _ = widget.show();
}
}

/// Restore the widget to the user's configured visibility. Called when the
/// pipeline returns to idle, so a hidden widget that we surfaced for recording
/// gets hidden again.
fn restore_widget_visibility(app_handle: &AppHandle) {
let state = app_handle.state::<AppState>();
let visible = state
.db
.lock()
.ok()
.and_then(|db| db.get_config("widgetVisible").ok().flatten())
.map_or(true, |v| v == "true");

if !visible {
if let Some(widget) = app_handle.get_webview_window("widget") {
let _ = widget.hide();
}
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

fn parse_retention_bytes(value: &str) -> Option<i64> {
match value {
"100mb" => Some(104_857_600),
Expand Down
15 changes: 12 additions & 3 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ pub fn run() {

if onboarding_done {
if let Some(widget) = app.get_webview_window("widget") {
let (position, size) = {
let (position, size, visible) = {
let db = state.db.lock().unwrap();
let pos = db
.get_config("widgetPosition")
Expand All @@ -188,11 +188,20 @@ pub fn run() {
.ok()
.flatten()
.unwrap_or_else(|| "medium".to_string());
(pos, sz)
let vis = db
.get_config("widgetVisible")
.ok()
.flatten()
.map_or(true, |v| v == "true");
(pos, sz, vis)
};
let _ =
window::widget::apply_position_and_size(&widget, &position, &size);
let _ = widget.show();
// Respect the user's hide preference at startup. When hidden,
// the pipeline still surfaces the widget during recording.
if visible {
let _ = widget.show();
}
}

// Load STT model in background — but only if the user is on
Expand Down
2 changes: 1 addition & 1 deletion src/dashboard/stats/stats-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function StatsRow() {
<div className="grid grid-cols-4 gap-4 mb-8">
{items.map((stat) => (
<Card key={stat.label} className="px-5 py-4">
<div className="text-2xl font-bold text-text-primary">
<div className="text-2xl font-bold text-text-primary tabular-nums">
{stat.value}
</div>
<div className="text-xs text-text-secondary mt-1">{stat.label}</div>
Expand Down
45 changes: 45 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,57 @@
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
Comment thread
Lionvsx marked this conversation as resolved.
}

#root {
height: 100%;
}

/* Tighter, balanced headings — large text wants negative tracking. */
h1,
h2,
h3 {
text-wrap: balance;
letter-spacing: -0.011em;
}

/* Dynamic figures (stats, counters, durations) must not jitter as they
change width. Opt in with `tabular-nums`. */
.tabular-nums {
font-variant-numeric: tabular-nums;
}

/* Subtle entrance for popovers/menus — origin-aware, ease-out. */
@keyframes pop-in {
from {
opacity: 0;
transform: scale(0.96) translateY(-2px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}

.animate-pop-in {
animation: pop-in 0.14s cubic-bezier(0.16, 1, 0.3, 1);
transform-origin: top;
}

/* Respect the user's motion preference everywhere by default. */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}

#root[data-window="main"] {
background-color: var(--color-bg-primary);
border-radius: 12px;
Expand Down
11 changes: 7 additions & 4 deletions src/shared/components/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ export default function Select({
type="button"
onClick={() => setOpen(!open)}
className={cn(
"flex items-center gap-2 bg-bg-secondary border border-border rounded-lg px-3 py-1.5 text-sm text-text-primary transition-colors hover:border-text-tertiary",
"flex items-center gap-2 bg-bg-secondary border border-border rounded-lg px-3 py-1.5 text-sm text-text-primary",
"transition-colors duration-150 ease-out motion-reduce:transition-none hover:border-text-tertiary",
"focus-visible:outline-none focus-visible:border-accent focus-visible:ring-2 focus-visible:ring-accent/20",
fullWidth && "w-full justify-between h-9",
open && "border-accent ring-2 ring-accent/20",
)}
Expand All @@ -67,8 +69,8 @@ export default function Select({
{open && (
<div
className={cn(
"absolute top-full mt-1 z-50 bg-bg-card rounded-lg border border-border shadow-[0_4px_24px_rgba(0,0,0,0.08)] p-1",
fullWidth ? "left-0 right-0" : "right-0 min-w-[180px]",
"animate-pop-in absolute top-full mt-1.5 z-50 bg-bg-card rounded-lg border border-border shadow-[0_8px_28px_-6px_rgba(0,0,0,0.16),0_0_0_0.5px_rgba(0,0,0,0.04)] p-1",
fullWidth ? "left-0 right-0 origin-top" : "right-0 min-w-[180px] origin-top-right",
)}
>
{options.map((opt) => (
Expand All @@ -80,7 +82,8 @@ export default function Select({
setOpen(false);
}}
className={cn(
"w-full text-left px-3 py-2 rounded-md text-sm transition-colors flex items-start gap-2",
"w-full text-left px-3 py-2 rounded-md text-sm flex items-start gap-2",
"transition-colors duration-150 ease-out motion-reduce:transition-none",
opt.value === value
? "bg-accent-subtle text-accent"
: "text-text-primary hover:bg-bg-tertiary",
Expand Down
28 changes: 20 additions & 8 deletions src/shared/components/toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,40 @@ interface ToggleProps {

export default function Toggle({ checked, onChange, label, disabled }: ToggleProps) {
return (
<label className="inline-flex items-center gap-3 cursor-pointer select-none">
<label
className={cn(
"inline-flex items-center gap-3 select-none",
disabled ? "cursor-not-allowed" : "cursor-pointer",
)}
>
<button
type="button"
role="switch"
aria-checked={checked}
aria-label={label}
Comment thread
Lionvsx marked this conversation as resolved.
Outdated
disabled={disabled}
onClick={() => onChange(!checked)}
className={cn(
"relative inline-flex h-6 w-11 shrink-0 overflow-hidden rounded-full transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-accent/20 disabled:opacity-50 disabled:cursor-not-allowed",
checked ? "bg-accent" : "bg-bg-tertiary"
// The track. `items-center` + symmetric padding keeps the knob
// perfectly centered at any size — no magic margins to drift.
"relative inline-flex h-6 w-11 shrink-0 items-center rounded-full px-0.5",
"transition-colors duration-200 ease-out motion-reduce:transition-none",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/30",
"focus-visible:ring-offset-2 focus-visible:ring-offset-bg-primary",
"disabled:opacity-50 disabled:cursor-not-allowed",
checked ? "bg-accent" : "bg-bg-tertiary",
)}
>
<span
className={cn(
"pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow-sm transform transition-transform duration-200 mt-0.5",
checked ? "translate-x-[22px]" : "translate-x-0.5"
"pointer-events-none block h-5 w-5 rounded-full bg-white",
"shadow-[0_1px_2px_rgba(0,0,0,0.2),0_0_0_0.5px_rgba(0,0,0,0.04)]",
"transition-transform duration-200 ease-out motion-reduce:transition-none",
checked ? "translate-x-5" : "translate-x-0",
)}
/>
</button>
{label && (
<span className="text-sm text-text-primary">{label}</span>
)}
{label && <span className="text-sm text-text-primary">{label}</span>}
</label>
);
}
Loading