Transparent at rest, accent gradient animates in on hover with a 0.18s
ease transition. Drag affordance + col-resize cursor still work; the
stripe just stops bothering you when not touched.
Right-side handle mirrors the gradient direction (left-to-right
gradient flipped to right-to-left).
The drag handle painted a 35% accent gradient strip on the page edge
of any docked panel. The col-resize cursor on hover is enough to
surface the affordance; the stripe felt like a stray UI element.
Two months of iteration on the Settings panel, integration forms, and
small visual nudges across the app. Highlights:
Settings restructure
- Add Models: split into separate Local + API cards (no more in-card
tabs); each fuses Type/Provider with the URL input.
- Added Models: new dedicated sidebar tab, with Probe + Clear-offline
pulled into its header; Local/API sub-section icons accent-tinted.
- Search: Web Search and a new Deep Research card (Model + tuning),
with a cross-link to AI Defaults. Provider hints use real clickable
anchors; Web Search Test button shows a whirlpool spinner.
- AI Defaults: Image Generation card returns; Research Model card
carries only Endpoint+Model with a cross-link to Search; Vision /
Default / Utility fallbacks unified under one numbered-row design
matching Search's chain.
- API Permissions (was 'API Tokens'): per-row rename, inline
Permissions toggle that expands the scope-edit panel, in-field
copy icons (icon→check on success). Empty state accent-tinted.
- Integrations: + Add Integration drops a type-picker menu directly
under the button (drop-up on tight viewports); each integration
form (API, CalDAV, CardDAV, Email, Codex/Claude, Vault, MCP) uses
the same accent-outlined Save/Test/Cancel buttons right-aligned.
- Danger Zone: Wipe→Delete with trash icons; new 'Delete everything'
row at the bottom that loops every category.
AI Synthesis (Reminders)
- Persona dropdown sourced from PROMPT_TEMPLATES + custom preset.
- src/reminder_personas.py mirrors the five built-ins for the
server-side synthesis path.
- dispatch_reminder() reads reminder_llm_persona and uses the
persona's system prompt; empty/unknown falls back to warm-neutral.
Esc handling
- Kebab menus and the provider picker intercept Esc in capture phase
so dismissing a popup no longer closes the whole Settings modal.
Accent tinting
- Scoped CSS rule across data-settings-panel=ai/services/added-models/
search/integrations/reminders for card h2 icons + the Added Models
sub-section icons.
Codex/Claude integration form
- No more auto-creation on form open — explicit Create token button.
- New tokens start with every scope granted; existing tokens move out
of the integration form into the API Permissions card.
- Setup reveal: copy buttons inline inside the token + setup code
blocks; shorter subtitle wording.
Misc visual polish
- Save/Test/Cancel uniformly accent-outlined and right-aligned on
every integration form.
- Provider logos render inline next to the search fallback selects
and the Deep Research Search dropdown.
- Trash icons in fallback rows bumped to 20x20 so they fill the 32px
button.
- Image generation default flipped to off.
* Make edge-docked windows resizable
Add draggable resize seams for left and right docked windows.
Keep the main chat area from getting too narrow and remember each window's dock width.
* Show emoji shortcodes as icons by default
Keep text-only emoji mode opt-in so model output like 😊 goes through the normal emoji renderer.
* Fix dock resize seams and left dock layout
Hide the resize seam when another floating modal is open, and keep the left-docked window from covering the chat area.
* Keep narrow modal tabs usable
* Fix split layout with both edge docks
* Fix left snap after right dock
* Enable left edge snap for all windows
* Tighten dock resize handle observers
* Use edge docking for settings window
Docking a modal to a window edge pushes the chat aside (body padding via
right-dock-active + --right-dock-w). Three problems on close/reopen:
1. Chat stayed offset after closing a docked modal. The close-watcher only
reacted to the `.hidden` class or DOM removal, but the draggable modals
(calendar, plan, workspace, document, …) close via inline `display:none`.
Watch the `style` attribute too and treat `display:none` as closed.
2. Reopening a previously-docked singleton modal floated it off to the side,
overlapping the chat. The reused element kept its docked inline geometry.
Clear the content's inline position/size on close so it reopens at its CSS
default (centered).
3. Undock wasn't animated. The transition lived on `.right/left-dock-active`,
so removing the class dropped the transition with it and padding snapped to
0. Move the transition to the base `body` so the push animates both ways.
Files: static/js/modalSnap.js, static/style.css.
Checks: node --check static/js/modalSnap.js; verified in-browser (dock → close
→ chat animates back; reopen → centered, no overlap).