Commit Graph
100 Commits
Author SHA1 Message Date
pewdiepie-archdaemon 2153d478a9 Email reader: shift From row up 4px on desktop, +2px To/Cc gap
- Desktop (>= 769px): From row gets margin-top -4px so the whole
  From + action cluster sits 4px higher in the header.
- Mobile @media block untouched.
- To/Cc gap bumped 4px → 6px for slight breathing room.
2026-06-11 20:22:12 +09:00
pewdiepie-archdaemon 50bfd85a6d Email reader: nudge meta chevron 1px right (-4px → -3px margin) 2026-06-11 20:21:15 +09:00
pewdiepie-archdaemon 907961af4a Email reader: align From/To/Cc labels to a fixed 36px column
Strong labels reserve min-width:36px so the chips after each label
start at the same x — From, To, Cc all line up. Killed the
docked/docpane grid-stack overrides that were splitting label and
chips onto separate rows, since chips already scroll horizontally
inside each row when there are too many.
2026-06-11 20:19:02 +09:00
pewdiepie-archdaemon 220dbf7256 Email reader: tighten spacing in docked view + meta details
- Docked: From row + action cluster nudged up 4px
- Chevron pulled 4px left so it sits tight to the From chip
- To/Cc detail block pulled up 8px to hug the From row
- 4px gap between To and Cc rows (was 2px)
2026-06-11 20:18:06 +09:00
pewdiepie-archdaemon c2e0e5f02a Email reader: all actions on the From row, wrap when narrow
Found the culprit — the docked-modal CSS forced .email-reader-meta-row
into a single-column grid, which collapsed the From row into a
vertical stack and pushed the action buttons below it.

Fix:
- Merged the primary + secondary action rows into one flat
  .email-reader-actions-inline cluster inside the From row
- Made the cluster flex-wrap so it stays inline when undocked and
  wraps below the chip when truly cramped (docked, narrow tab)
- Excluded .email-reader-meta-from from the docked-modal and
  narrow-docpane grid-stack rules — those overrides now only
  apply to the To/Cc detail rows
2026-06-11 20:07:35 +09:00
pewdiepie-archdaemon c8654f3166 Email library: magnifying glass inside the chip-bar search field
Absolutely-positioned 13px search SVG at the left edge of the chip bar
(same circle+line glyph used elsewhere). Bar padding-left bumped 8→26
to leave room. pointer-events:none on the icon so clicks still land
on the input, opacity 0.55 to match other muted prefix icons.
2026-06-11 20:06:22 +09:00
pewdiepie-archdaemon abb1667a82 Email: bookmark icon everywhere for favorites; subject matches in suggestions
Star → bookmark banner SVG also in the card title row (em.is_flagged
glyph) and the inbox toolbar's _starIcon / _starFilledIcon, so every
favorites affordance matches the chats sidebar bookmark.

Search dropdown gains a third suggestion kind:
- kind: 'email' rows surface emails from the snapshot whose subject or
  sender name match the typed term (top 4, scored by startsWith vs
  substring). Render row carries a small envelope glyph + bolded
  subject + 'from name' on the right.
- Picking one closes the dropdown and expands that exact card via
  _toggleCardPreview, scrolling it into view.
2026-06-11 19:46:45 +09:00
pewdiepie-archdaemon b11354f45e Email reader: primary action row literally inside the From row
Restructured the DOM so the Reply / Reply-all / Forward row lives
INSIDE the email-reader-meta-from div (after the chips span), and
the Summary / AI / More row sits directly below as a sibling of
From inside the meta. Killed the outer email-reader-actions
wrapper that kept letting the buttons drift out of position.

CSS now pushes the primary row right via margin-left:auto on the
From row and right-aligns the secondary row below it.
2026-06-11 19:46:06 +09:00
pewdiepie-archdaemon 5c880e322c Email reader: Reply group on From row, Summary/AI/More below
Reorganized the action cluster into two visible rows so each fits
the available width:
- Top row (on the From line): Reply / Reply-all / Forward
- Bottom row (under it):      Summary / AI reply / More

Action cluster goes back to flex-direction:column, the row
wrappers are flex rows again (no more display:contents flatten).
2026-06-11 19:39:13 +09:00
pewdiepie-archdaemon 2a93895b8a Email library: swap Favorites icon star→bookmark banner (matches chat .session-fav) 2026-06-11 19:39:12 +09:00
pewdiepie-archdaemon 8925b3bf77 Email library: filter pills render as icon-only chips
After picking a filter from the dropdown the pill was 'icon + Unread'.
Drop the text — the icon is the affordance — so the pill collapses to
just the glyph + ×. Hover surfaces the friendly label via the title
attribute. Contact + text pills still carry their text label.
2026-06-11 19:37:56 +09:00
pewdiepie-archdaemon 23365114b7 Email reader: top-align action cluster against From row
align-items: flex-start on the header keeps the action cluster
locked to the From line when the user expands the To/Cc details
— previously it drifted to vertical center as the meta grew taller.
2026-06-11 19:34:20 +09:00
pewdiepie-archdaemon dd977d03e2 Email library chip-bar: filter + tag suggestions with their icons
Typing a filter keyword now surfaces the matching filter row in the
autocomplete (each with its existing dropdown icon). Picking one pins
a filter pill and drives the global filter state.

Keyword catalog (_LIB_FILTER_OPTIONS):
- has-attachments  ← 'attachment', 'attachments', 'has attachment', 'attach'
- unread           ← 'unread', 'new', 'unseen'
- favorites        ← 'favorite', 'starred', 'star', 'flagged'
- undone           ← 'undone', 'pending', 'todo'
- reminders        ← 'reminder', 'reminders'
- unanswered      ← 'unanswered', 'unreplied', 'no reply'
- pending_30d      ← 'pending 30d', 'pending', 'recent pending'
- stale_30d        ← 'stale', 'old', 'stale 30d'
- tag:urgent       ← 'urgent', 'critical'
- tag:reply-soon   ← 'reply soon', 'reply', 'follow up'
- tag:spam         ← 'spam', 'junk'
- tag:newsletter   ← 'newsletter', 'newsletters', 'subscriptions'
- tag:marketing    ← 'marketing', 'promo', 'promotional'

Filter pill behaviour:
- Only one filter pill is active at a time — adding a new one replaces
  any existing filter pill.
- _applyFilterPillSideEffect drives the existing #email-lib-filter
  select (or the #email-attach-btn toggle for has-attachments). The
  server-side list refetch follows for free via the existing 'change'
  handler.
- Removing the filter pill clears the side effect.

Pill render gains the filter icon as a leading glyph; the suggestion
row renders icon + label in the accent colour so it visually reads as
a filter, not a contact.
2026-06-11 19:33:55 +09:00
pewdiepie-archdaemon 8129662a4a Email reader (mobile): top-align meta with the two-row action cluster
After the toolbar reshuffle the action block is now two stacked rows
(Summary/More above Reply/Forward/AI), making it taller than the meta
block. The mobile header rule was align-items:center, which then pulled
the From:/To: rows down into the vertical middle of the header — the
'From: is in the middle' symptom. Switch to flex-start so meta sticks
to the top edge where the user expects it.
2026-06-11 19:29:41 +09:00
pewdiepie-archdaemon 753d6d8bb9 Email reader: actions inline on the From row
With the meta collapsed to a single visible From row + chevron,
there is room to put the action cluster on that same row as a
right-aligned sibling. Dropped the absolute positioning and
gradient-fade overlap — actions now flex-end via margin-left:auto
so From sits on the left and Reply / Reply-all / Forward / AI /
Summary / More all sit on the right of the same row.

Also moved the chevron inside the recipient-chips span so it sits
adjacent to the sender chip instead of wrapping onto a second line.
2026-06-11 19:23:34 +09:00
pewdiepie-archdaemon 2a32e1aac4 Email library chip-bar: AND across pills, plain Enter commits text, pill × up 4px
1. Multiple pills now AND together — 'alice + bob' means both alice
   AND bob are somewhere on the email, not 'from alice OR from bob'.
   (some → every in the filter.)
2. Default autocomplete focus is now -1 (no row pre-selected) so plain
   Enter commits the input as a text pill — typing then Enter behaves
   like a normal search. ArrowDown / ArrowUp + Enter still picks a
   contact suggestion. Tab still autocompletes the most-relevant match
   regardless of arrow state.
3. Pill × button nudged up 4px so it sits on the visual centerline
   inside the 18px pill height.
2026-06-11 19:21:37 +09:00
pewdiepie-archdaemon 44000f5917 Email reader: collapse To/Cc behind Gmail-style chevron
Only the From row shows by default. When the email has To and/or
Cc recipients, a small chevron sits next to the From chip — click
it to inline-expand the To/Cc rows below (rotates 180deg open).

Trims the header to a single visible row in the common case,
leaving the action cluster plenty of vertical headroom to stay
on a single row.
2026-06-11 19:19:12 +09:00
pewdiepie-archdaemon c5bf596d2f Email reader: keep From/To/Cc on separate rows, label tight to chips
Reverted the single-row meta strip — misread the user's ask. Each
meta field gets its own row (From / To / Cc stacked), label sits
tight to the chips on the same line, recipient chips inside the
row still scroll horizontally so long lists slide under the
floating action cluster.
2026-06-11 19:15:55 +09:00
pewdiepie-archdaemon 13c9970d0f Email reader: collapse From/To/Cc into a single inline row
Three stacked meta rows wasted vertical space — From, To, Cc now
share one horizontal strip with each label tight to its chips. The
strip itself scrolls horizontally so the action cluster (still
floating top-right) can cover the right edge and the user can drag
to reveal recipients hidden underneath.

This also gives the actions a single shared row, since the meta
no longer dictates a multi-row header height.
2026-06-11 19:12:06 +09:00
pewdiepie-archdaemon bb26735cb2 Email library chip-bar: smaller pills, persist across refresh, Esc + sender click
Four fixes from the first round of usage:

1. Pill height was larger than the chip-bar's row — shrink to a fixed
   18px-tall pill (line-height + height pinned) so it sits inside
   the input row.

2. List refresh wiped pill state — when _loadEmails replaces
   state._libEmails (refresh, folder switch, etc.), refresh the
   snapshot to the new list and re-apply the pill filter so pills
   persist instead of resetting to 'show all emails'.

3. Click-to-add only worked inside the open email reader. Extend the
   capture-phase handler to ALSO catch clicks on .email-meta-sender
   inside the library grid — the list card's sender name is the most
   natural place to want to pivot from.

4. Esc inside the chip-input didn't close the modal. New behaviour:
   if the autocomplete dropdown is open, Esc closes only the dropdown
   (and swallows the event); otherwise Esc blurs the input and bubbles
   so the existing modal Esc handler can close the library.

Also wires data-email + data-name on .email-meta-sender so the click
handler has reliable targeting.
2026-06-11 19:11:07 +09:00
pewdiepie-archdaemon 100305e0f6 Email reader: flatten action rows with display:contents
The primary/secondary row wrappers were still creating nested flex
containers — even with parent flex-direction:row the two row divs
sized to content and could stack visually. Switching the wrappers
to display:contents collapses them entirely so all 6 buttons
become direct flex children of .email-reader-actions and lay out
on a single row guaranteed.
2026-06-11 19:08:22 +09:00
pewdiepie-archdaemon b2e48b99bf Email library: gallery-style chip-input search with contact autocomplete
Replace the single text-input + IMAP search round-trip with a deterministic
local chip-bar filter modelled on the gallery's tag pills.

What lives in the bar
- Each filter is a pill: { type: 'contact', name, email } or
  { type: 'text', text }.
- Click anywhere in the bar lands the cursor in the input field.
- Typing populates a dropdown of matching contacts + recently-seen senders
  (cached per modal open via _buildSuggestionSource).
- Tab / Enter on a highlighted suggestion → adds a contact pill.
- Enter on free text with no suggestion match → adds a text pill.
- Backspace on empty input → pops the last pill.
- × on a pill removes that one.
- Arrow keys navigate the suggestion list.

Filtering
- _applyPillFilter snapshots the loaded list once, then for every render
  shows emails where ANY pill matches:
    contact pill — from_address equals OR to/cc contains the pill's email
    text pill    — broad substring match across subject/from/snippet

Click-to-add
- Capture-phase click handler on .recipient-chip inside the email reader
  drops the person into the library as a contact pill (and reopens the
  library window if it was closed/minimized).

Removed the debounced /api/email/search IMAP call and its 'Loading emails'
side effect. The dropped server search was the source of the 'type
jonathan, get stuck on Loading' bug.
2026-06-11 19:02:05 +09:00
pewdiepie-archdaemon fa1233a811 Email reader: collapse action cluster to a single row
Reply / Reply all / Forward / AI / Summary / More now flow inline
on one row instead of being split into a primary (Summary+More) and
secondary (Reply group) stack. Mobile + docked overrides also
flipped from column to row.
2026-06-11 18:59:50 +09:00
pewdiepie-archdaemon 327cce2948 Email reader: actions float top-right over scroll-able recipient row
From/To/Cc back on the left, action cluster (Reply / Reply-all /
Forward / AI / Summary / More) absolute-positioned top-right with a
gradient fade so chips that overflow slide cleanly underneath. The
recipient-chips lists no longer wrap — they scroll horizontally,
matching the account-chip strip pattern, so users can drag/swipe
to reveal recipients hidden under the action cluster.

Mobile (@media max-width:768px) gets the same row+absolute layout
instead of the previous column with actions on top. The narrow
container query (docpane max-width:460px) still falls back to
in-flow column so it doesn't overlap on very narrow panes.
2026-06-11 18:55:31 +09:00
pewdiepie-archdaemon 3c13e28062 Email reader: move action toolbar to the TOP, meta below
Was: from/to/cc/date meta on the left, action cluster (Reply / Reply
all / Forward / AI reply / Summary / More) pinned to the right of
the header. Now: actions stretch across the top in their two existing
sub-rows, the from/to/cc meta sits below.

Pure CSS — no template restructure. The .email-reader-header flexbox
flips to flex-direction:column, .email-reader-actions gets order:-1
to render first, and the existing flex-end aligned action-row rules
swap to flex-start so buttons read left-to-right across the top
toolbar. Mobile media query overrides bend the same way so the
layout is consistent across breakpoints.
2026-06-11 18:48:34 +09:00
pewdiepie-archdaemon 4817745490 AI Reply note: hide 'Draft with note' button until the textarea has text 2026-06-11 18:46:16 +09:00
pewdiepie-archdaemon 77e623f14f AI Reply menu: '...' kebab opens a note input to steer the draft
The Fast/Full popover now has a kebab (three-dot) button alongside the
two preset choices. Clicking it expands a textarea below with a
'Draft with note' send button. The textarea is for the user to tell
the AI how to reply ('confirm Tuesday at 2', 'decline politely', 'say
we'll need an extra week') instead of accepting a generic draft.

Plumbing:
- emailLibrary.js: kebab button + note panel inside .email-ai-reply-choice
  menu. Submitting calls _runAiReplyFromButton with mode='ai-reply-full'
  and a noteHint string.
- _runAiReplyFromButton signature gains noteHint; passes it through
  state._onEmailClick as opts.noteHint.
- emailInbox.js consumer: forwards opts.noteHint into _openEmail's new
  5th arg, which puts it in the /api/email/ai-reply POST body as
  user_hint.
- routes/email_routes.py /ai-reply: reads user_hint, appends a
  'User's instructions for THIS reply' section to the user message
  (priority over default tone/length). Also skips the per-message
  AI-reply cache when a hint is set — the cached generic draft would
  silently override the instructions otherwise.
2026-06-11 18:41:11 +09:00
pewdiepie-archdaemon 4a3d3d54a3 Email reader: two-row action layout — Summary+More above, Reply/Forward/AI reply below
Restructure the action cluster so it stays as two visible rows inside
.email-reader-actions instead of flattening via display:contents:
- Top row: Summary, More
- Bottom row: Reply, Reply all (conditional), Forward, AI reply
Dropped the Search button — wasn't part of the requested layout.

CSS: .email-reader-actions becomes flex column with both rows
right-aligned; .email-reader-actions-row becomes a real flex row
(no more display:contents flattening) so each row stays on its own
line. Whole block continues to sit beside the From/To meta inside
.email-reader-header.
2026-06-11 18:40:16 +09:00
pewdiepie-archdaemon dd40190c57 AI Reply Fast/Full icons: paint with var(--accent, var(--red)) 2026-06-11 18:36:27 +09:00
pewdiepie-archdaemon c1ea65d7bc AI Reply menu: SVG icons for Fast (lightning) and Full (concentric circles) 2026-06-11 18:29:35 +09:00
pewdiepie-archdaemon a28bf87fa0 Email library: await _loadAccounts before loading emails
After dropping the 'Default' chip, _loadAccounts started setting
state._libAccountId asynchronously while _loadEmails fired in parallel
with the still-null id. The list request was going out with no
account_id (so the server defaulted) while subsequent per-email reads
used the explicit id set after _loadAccounts resolved — back to the
same desync the chip-removal was meant to fix.

Sequence them: await _loadAccounts first, then kick off the folders /
reminders / emails fetches. The list always carries the right
account_id from the very first call.
2026-06-11 17:15:49 +09:00
pewdiepie-archdaemon 35bbdbd3b2 Email reader: 'open in new tab' windows don't auto-dock left on Reply
Replying from an email opened in a new tab was dragging that window to
the left-sidebar dock — same treatment as the main email library, even
though the user had explicitly opted to pop it into its own floating
viewer. Annoying when the viewer is mid-screen and Reply yanks it.

Add an early bail in _snapEmailModalToLeftSidebar for modals whose id
starts with 'email-view-' (the 'open in new tab' reader). Compose still
opens; the floating viewer just stays where it is, on top of the
library. User can move/close it themselves.
2026-06-11 17:13:15 +09:00
pewdiepie-archdaemon 638ecee6cd Email library: drop the 'Default' chip — pick an explicit account always
Bug: clicking the dot to change the server-side default account while
viewing 'Default' left a desynced state — the email list still showed
the OLD default's cached UIDs, but the server's default now pointed
at a different account. Opening any email used the visible UID +
account_id='' on the read endpoint, which resolved against the NEW
default account → wrong email content (or older mail entirely).

Fix: remove the 'Default' chip. _loadAccounts now auto-selects the
is_default account (or the first one) into state._libAccountId so the
list view + every per-email request always carries an explicit
account_id and can't desync from set-default.

The dot button still lives on each account chip for changing which
account the server treats as the default — but it no longer affects
which account the list is currently displaying.
2026-06-11 17:11:55 +09:00
pewdiepie-archdaemon 594a26000d Email filter Unread: use the incognito eye SVG (eye with X) instead of the ringed dot 2026-06-11 17:08:46 +09:00
pewdiepie-archdaemon a2fa10b785 Email reader: pin More to far right + allow actions to wrap beside meta
- .email-reader-actions flex-wrap nowrap → wrap so when the cluster
  exceeds the room next to a tall multi-recipient meta block, the
  buttons wrap within the actions area instead of pushing the whole
  block onto its own row below From/To.
- New rule: .email-reader-more-wrap gets order:99 so the More kebab
  sits at the far right of the flattened flex row instead of in the
  middle (its source order put it ahead of the secondary row's AI
  Reply / Summary buttons after display:contents flattening).
2026-06-11 17:06:26 +09:00
pewdiepie-archdaemon b8781f7b39 Email filter picker: nudge up 2px on desktop (3px → 1px) 2026-06-11 17:05:47 +09:00
pewdiepie-archdaemon 0d20357062 Email library: nudge .email-filter-btn up 4px 2026-06-11 17:01:23 +09:00
pewdiepie-archdaemon 81c9fe2958 Email filter: custom dropdown with SVG icons for each option
The All/Unread/Favorites/etc selector was a native <select>, which
can't render SVG inside <option>. Replace it with a custom picker
that:

- Keeps the existing <select id="email-lib-filter"> as the value
  store (hidden via display:none). All existing 'change' listeners
  keep working — the picker just dispatches a change event after
  updating the select's value.
- Renders a styled button + drop-out menu built from the select's
  options (preserves optgroup labels like 'Tags').
- Each option carries an SVG icon: lines for All, ringed dot for
  Unread, star for Favorites, empty checkbox for Undone, bell for
  Reminders, reply arrow for Unanswered/Reply-soon, clock for
  Pending, calendar-x for Stale, exclamation-triangle for Urgent,
  ban for Spam, newsletter and megaphone for the marketing tags.
- Icons use var(--accent) so they pick up the user's theme color.
- Click outside / Esc closes the menu (Esc handler is capture-phase
  + stopPropagation so it doesn't bubble to the modal-close listener
  and shut the whole email window).

CSS scoped under .email-filter-picker.
2026-06-11 12:53:39 +09:00
pewdiepie-archdaemon 1b965ccd7f Email reader: regroup More menu + reshuffle toolbar rows
More menu reorganization:
- Group 1: Open in new tab, Remind to reply
- Group 2 (state): Mark as Unread/Read, Mark as Done/Not Done, Move to
  Archive, Save sender to contacts
- Group 3 (destructive, unchanged): Move to Spam, Move to Trash,
  Delete Permanently
- Renames: Done→'Mark as Done', Archive→'Move to Archive', Mark
  Read/Unread→'Mark as Read'/'Mark as Unread'.
- Mark Unread moves out of group 1 down into the state-change group
  alongside Done; Save sender to contacts moves down into the same
  state group.

Toolbar row reshuffle (applies to both the email-list card reader and
the email document view):
- Row 1 (primary): Reply, Reply all, Forward, Search, More — Forward
  no longer has to fight Search/More for space in the secondary row.
- Row 2 (secondary): AI reply, Summary — gets its own dedicated row.
2026-06-11 12:50:47 +09:00
pewdiepie-archdaemon 28847d72d6 Email accounts strip: bigger 18x18 hit target around the small default-dot
The 6px dot was easy to miss on touch / small-cursor setups. Replace
padding-only sizing with explicit width:18px;height:18px on the
button, dot centered inside via justify-content. Anchor moved from
right:9 → right:6 so the visible dot stays where it was; the extra
clickable area extends inward from the chip edge.
2026-06-11 12:44:02 +09:00
pewdiepie-archdaemon 81c824d401 Email accounts strip: nudge default-dot 1px up + 2px left 2026-06-11 12:18:02 +09:00
pewdiepie-archdaemon 328d9d36bb Email accounts strip: shrink default-dot to 6px (matches sidebar notif dot) 2026-06-11 11:57:46 +09:00
pewdiepie-archdaemon e6b4975e2c Agent prompt builder: stop re-adding ALWAYS_AVAILABLE on top of filtered tools
Found the reason yesterday's tool-retrieval drop wasn't taking effect:
in _build_agent_prompt, when relevant_tools was provided, it computed
  tool_names = set(ALWAYS_AVAILABLE) | set(relevant_tools)
which silently re-added every tool get_tools_for_query had just
deliberately discarded. So when a 'save this for <person>' query
dropped manage_memory from the retrieved set, the prompt builder put
it right back, and the model saw both tools again.

Trust the relevant_tools set. get_tools_for_query already starts from
ALWAYS_AVAILABLE — any discard there is intentional and should
propagate. Only force-include ask_user and update_plan here as belt-
and-suspenders since the agent loop relies on those for its own
control flow.

Other callers (task_scheduler) already union ALWAYS_AVAILABLE or
ASSISTANT_ALWAYS_AVAILABLE into relevant_tools before passing it in,
so they're unaffected.
2026-06-11 09:49:20 +09:00
pewdiepie-archdaemon 83cac31417 Email accounts strip: nudge default-dot 1px left + shrink 10→8px 2026-06-11 09:49:03 +09:00
pewdiepie-archdaemon db3d8c7809 Settings/Contacts (CardDAV): show '(unchanged)' placeholder when password is saved
GET /api/contacts/config masks the saved password as '***' (or ''
when none). Mirror that into the password input's placeholder so users
can see at a glance that a password is on file — matching the email
account form's '(unchanged)' pattern.
2026-06-11 09:47:28 +09:00
pewdiepie-archdaemon 47913742e9 Tool retrieval: HARD drop manage_memory when query is a contact-save pattern
Description-level steering wasn't enough — even with the explicit 'DO
NOT use for info about another person' in manage_memory's description,
models kept choosing memory over manage_contact. They can't if memory
isn't in the toolset.

New logic in ToolIndex.get_tools_for_query: detect three contact-save
patterns and discard manage_memory from the returned set (overriding
ALWAYS_AVAILABLE):

1. 'save [up to 3 words] for/to <name>' where <name> isn't a timing /
   pronoun stopword (later, tomorrow, me, you, future, etc.). Catches
   the canonical 'save this for X' and the wider 'save this address
   for X', 'save it for X'.
2. 'to/in/into (my) contacts' or 'address book'. Catches both 'add X
   to my contacts' and 'put this in my address book for X'.
3. Possessive: 'save (his/her/their) (address/phone/email/...)'.
   Stronger signal — also force-adds manage_contact to the set in
   case the keyword fallback missed it.

Verified: 8 positive contact patterns all drop memory, 10 false-
positive 'save X for later/tomorrow/me/the next thing' all keep it.
2026-06-11 09:46:34 +09:00
pewdiepie-archdaemon e76e21b019 Email accounts strip: nudge default-dot 2px left (right 4→6) 2026-06-11 09:44:03 +09:00
pewdiepie-archdaemon 6c402f99fd manage_memory descriptions: explicit deferral to manage_contact for person info
Even with manage_contact in the retrieved tool set, models were still
defaulting to manage_memory when the user pasted an address + 'save for
<person>'. Both tools were in front of the model and it picked memory.

Tighten both descriptions to steer at decision-time:
- agent_loop.py manage_memory description: clarify scope is facts
  about the USER, with an explicit 'DO NOT use for info about another
  person' + a 'use manage_contact instead' line.
- tool_index.py manage_memory description: same in shorter form, so the
  embedded retrieval signal is consistent with the prompt-time
  description.
2026-06-11 09:25:23 +09:00
pewdiepie-archdaemon 842f62962f Contacts UI: address + phone inputs, search filter, address-only adds
The contacts manager in Settings was stuck at name+email inline only —
no address field, no phone input on add, no search to find anything in
a list of 100+ contacts.

UI:
- Add form gets phone and address inputs alongside name/email. The
  email-required gate becomes name-OR-email so address/phone-only
  entries are creatable.
- Edit form gets an address input, threaded into the PUT body.
- Search input above the list filters client-side by name / emails /
  phones / address (debounced 80ms). Count badge shows N/M when a
  filter is active.

Backend:
- /api/contacts/{uid} PUT now accepts address and routes it through
  _update_contact (which already supports it after the previous
  commit). Validation loosened: name OR email OR address.
- /api/contacts/add POST now accepts phone + address. Phone goes
  through an immediate _update_contact since _create_contact's
  signature only takes name+email+address.
2026-06-11 09:23:14 +09:00
pewdiepie-archdaemon 5faaac9215 Email accounts strip: bigger default-dot (10px) + 4px more chip padding
8px ring read as a sliver next to the chip label. Bump to a 10x10 SVG
with stroke-width:3 for the hollow ring so it presents like the
sidebar notif dot at this size. Chip padding-right bumped 20→24 so
the larger glyph isn't crushed against the text.
2026-06-11 09:18:34 +09:00
pewdiepie-archdaemon 18d19921bc Tool retrieval: catch 'add X to (my) contacts' / 'address book' phrasings
The literal phrase 'add to contacts' missed when there was a name
between 'add' and 'to', e.g. 'add Pat to my contacts'. Anchor on the
tail with 'to my contacts', 'to contacts', 'to address book' so word
boundaries fire regardless of what sits in front.
2026-06-11 09:18:30 +09:00
pewdiepie-archdaemon 2a8821f65f Email accounts strip: swap default-star for a dot, nudge up 2px
Replace the star polygon with a small 8px circle dot — filled +
accent-tinted on the default account, hollow + muted on others.
Vertical position bumped up 2px via top: calc(50% - 2px) so it
visually centers against the chip's text baseline instead of
geometric center.
2026-06-11 09:17:04 +09:00
pewdiepie-archdaemon f85b9cd8d9 Contacts: postal-address support via vCard ADR, keep tool prompt minimal
Closes the gap that pushed the agent into manage_memory when the user
pasted an address and said 'save this for X'. manage_contact now
accepts an optional address arg end-to-end:

- routes/contacts_routes.py:
  - _normalize_contact carries an 'address' field
  - _build_vcard emits ADR:;;<address>;;;; (street component of the
    RFC-6350 7-part ADR), only when address is non-empty
  - _parse_vcards reads ADR, joins non-empty components with ', '
  - _create_contact and _update_contact thread address through;
    update preserves existing address when caller passes empty
- src/tool_implementations.py do_manage_contact:
  - add accepts address; require at least name+address or email
    (was: email required) so address-only contacts are addable
  - update accepts address; require name OR emails OR address
- src/tool_schemas.py: schema gets a single 'address' string field
- src/tool_index.py + src/agent_loop.py: descriptions get one
  'address' arg mention and a 'use this for save-X-for-person /
  address pastes / phone-with-name' steering line. Net: a few
  bytes added, not a paragraph.

Also: removed a stray name from the schema's manage_contact example
strings ('save Jonathan's email…') — no real names in the codebase.
2026-06-11 09:14:52 +09:00
pewdiepie-archdaemon baeef1b1af Email accounts strip: rename 'All (default)' → 'Default', add star toggle
- The 'All (default)' chip showed only the default account, so the
  label was misleading. Rename to just 'Default' to match behavior.
- Each user account chip gets a star button (filled if it IS the
  default, hollow otherwise). Clicking calls the existing
  POST /api/email/accounts/{id}/set-default and refreshes the strip.

Cross-account aggregation (a true 'All') is a separate bigger lift
that needs UID namespacing and merge/sort in _list_emails_sync;
flagged for follow-up rather than smuggled into this change.
2026-06-11 09:12:37 +09:00
pewdiepie-archdaemon a7c974ab5e Tool retrieval: surface manage_contact for 'save X for <person>' patterns
When the user dumps a postal address or phone number alongside a
person's name and says 'save this for X', the vector retriever was
missing manage_contact because its description only mentioned the
literal word 'contact'. The model defaulted to manage_memory (which is
in ALWAYS_AVAILABLE), so the saved fact ended up as un-named memory
that wouldn't surface on a later 'what's X's address?' search.

- Rewrite manage_contact's index description to anchor on the
  semantics: 'save info about another person', including postal/
  mailing address, ZIP, phone, etc. Now it embeds close to address-
  paste queries.
- Extend the keyword intent-map with 'save this for', 'save it for',
  'mailing address', 'postal code', 'their address', etc. — common
  ways users say 'this belongs to a contact' without the literal word
  'contact'.
2026-06-11 08:56:42 +09:00
pewdiepie-archdaemon 9367f9ae3d Agent email safety: stage drafts for user approval instead of auto-send
Closes the auto-send hole that let earlier models invent signatures
(e.g. signing 'David' for a user named Felix) and SMTP them to real
recipients before the user could review.

New setting: agent_email_confirm (default True).

When on, the MCP send_email and reply_to_email tools no longer SMTP
directly — they write the composed email to scheduled_emails with a new
status 'agent_draft' (far-future send_at so the scheduled-send poller
ignores them) and return a {pending: true, pending_id, to, subject,
body, message: ...} payload. The model surfaces that to the user.

Backend endpoints to approve / cancel:
- GET    /api/email/pending          → list staged drafts for the owner
- POST   /api/email/pending/{id}/approve → flip status to 'pending' +
                                           backdate send_at so the
                                           existing scheduled-send
                                           poller delivers immediately
- DELETE /api/email/pending/{id}     → status = 'cancelled'

UI:
- Settings / AI Defaults gets a new 'Email Safety' card with the
  toggle, default on.
- Tool descriptions for send_email and reply_to_email now include the
  pending behavior + an explicit 'DO NOT invent a signature, do not
  type a person's name' guardrail.

Pass 2 (next): inline chat card with Send / Discard buttons so the user
doesn't have to type a confirmation reply. Today's prompt + the listing
endpoint give the model a clean path to surface drafts.
2026-06-11 08:50:06 +09:00
pewdiepie-archdaemon 92dbb5bfe4 Email library: center the loading whirlpool over the full grid
Old rule fixed the loading wrap at min-height:180px so the spinner
landed near the top of the email-list section. Switch to
position:absolute inset:0 over the grid (with #email-lib-grid set to
position:relative) so the whirlpool + 'Loading emails' label center
within the entire visible email area regardless of section height.
2026-06-11 08:46:34 +09:00
pewdiepie-archdaemon 40d92ee06b Email bulk bar: nudge 'Marking…' label up 2px + 'All' checkbox up 2px
- 'Marking done' / 'Marking read' / 'Marking unread' label was 2px low
  vs. the whirlpool spinner inside the Actions button. The existing
  loading-label CSS only scoped to #email-lib-bulk-delete; extend it
  to also cover #email-lib-bulk-actions and bump top from 0 to -2px.
- 'All' checkbox label was inline-styled top:2px so the box + text sat
  lower than the surrounding bulk-action items. Reset to top:0 to
  match memory + skills select-all rows.
2026-06-11 08:41:59 +09:00
pewdiepie-archdaemon 39c47ae35c Email search: instant local-cache filter + stop blanking the grid
Two pain points:
- IMAP server search is genuinely slow.
- The grid blanked to a whirlpool on every keystroke, so even fast
  searches felt dead because you couldn't see your own results.

Fix:
- _localSearchFilter runs synchronously on every keystroke, filtering
  the pre-search snapshot by subject / from-name / from-address /
  snippet so the grid responds immediately. Snapshot is taken on the
  first non-empty keystroke and restored when the input is cleared.
- _doSearch no longer renders the loading-whirlpool spinner into the
  grid. The local filter already shows useful results; surface
  'Searching…' in the stats badge to indicate the server search is in
  flight.
- When server results land, they replace the grid; if the user has
  already typed past them, the seq guard skips the stale render.
2026-06-11 08:28:25 +09:00
pewdiepie-archdaemon 1636b44280 Email bulk actions: loading state for every action + 6-way parallel fetches
Before: only delete showed a spinner/disabled buttons. Picking Done on
92 selected emails fired off 184 sequential HTTP calls (mark-answered
+ mark-read) with zero UI feedback, so it looked like the click did
nothing for the ~20-30 seconds it took to grind through.

- All five bulk actions (delete / archive / done / read / unread) now
  swap the target button into a whirlpool+verb-ing state, dim siblings,
  and show 'N/M…' progress in the count label that ticks as each
  request resolves.
- Per-uid work runs in parallel with a hard cap of 6 in flight, so a
  90-email Done finishes in ~3 server round-trips of latency instead
  of 90, but we still don't open 90 simultaneous IMAP-backed connections.
2026-06-11 07:41:36 +09:00
pewdiepie-archdaemon 56e40bd5d6 Email reader More menu: reorder + separators into three groups
Group 1 — per-email view actions:
  Open in new tab → Mark Unread/Read → Remind to reply
Group 2 — non-destructive state changes:
  Save sender to contacts → Done/Not Done → Archive
Group 3 — destructive (own divider):
  Move to Spam → Move to Trash → Delete Permanently

Adds support for { separator: true } items in the actions array,
rendered as .dropdown-divider rows.
2026-06-11 07:40:11 +09:00
pewdiepie-archdaemon 299625b559 Email library bulk Done: animate-out + drop when filter='undone'
Repro: filter Undone → Select All → uncheck a few → Actions → Done →
nothing visible happens. Reason: the bulk-Done branch only flipped
em.is_answered on the in-memory entries; the cards stayed in
state._libEmails so they kept rendering, but now with the done check
ticked. From the user's POV — still 'undone' filter, cards still
there — it looked like the action was a no-op.

When the filter is 'undone' specifically, treat marking done as a
view-removal (same animate-then-prune step archive/delete uses).
2026-06-11 07:37:38 +09:00
pewdiepie-archdaemon 102679f8fc Email list: scroll an expanded card into view after click
When clicking an email higher up in the list, its top edge can be hiding
behind the modal header or off-screen. After applying the
.email-card-expanded class + the new minHeight, scrollIntoView(block:start)
on the next animation frame so the user sees the whole card.
2026-06-11 07:35:32 +09:00
pewdiepie-archdaemon c97759f4e0 Email card: drop redundant header kebab; keep bottom '...' menu in expanded state
The expanded email card painted a kebab menu in its title row because
the per-card .memory-item-actions menu at the bottom was hidden while
expanded. Both pointed at _showCardMenu(em). Remove the duplicate:

- Drop the email-card-header-menu button (and its rightCluster
  wrapper) — title row now just holds the nav arrows.
- Remove the CSS rule that hid .memory-item-actions on
  .email-card-expanded so the bottom kebab stays visible.
- Unread-dot insert point retargets to .email-card-nav-arrows now
  that the rightCluster is gone.
2026-06-11 07:33:04 +09:00
pewdiepie-archdaemon 08484ddab5 Email library New (compose): envelope icon takes the accent color 2026-06-11 07:30:26 +09:00
pewdiepie-archdaemon 05755d4729 Email Library: drop redundant 'All emails. Click to open as a document' subtitle 2026-06-11 07:28:21 +09:00
pewdiepie-archdaemon 6cf9d1c30f Email library: bulk 'Done' actually marks selected emails done
state._selectedUids holds whatever the server returns for em.uid (string
or number); the bulk action looped Array.from(...) and did strict ===
against state._libEmails entries. When the types disagreed, the find()
returned undefined, the in-memory is_answered flip never happened, and
the post-loop _renderGrid() painted the cards back into their original
not-done state — looking like 'mark done' did nothing even though the
server-side call had succeeded.

- Compare via String() on both sides so the in-memory state actually
  flips.
- Surface HTTP failure from mark-answered/mark-read so the existing
  failedReadSync toast can fire if the calls don't go through.
2026-06-11 07:27:25 +09:00
pewdiepie-archdaemon 01a46ce8dc Email library compose button: scope taller+lower variant to desktop only
Wrap the height:28px / top:0 rule in @media (min-width:769px) so it
can't leak into mobile, where a different touch-friendly variant
already sets min-height:36px + top:-2px.
2026-06-11 07:24:25 +09:00
pewdiepie-archdaemon 335f4e5105 Email library: drop compose button another 2px (top -2→0) 2026-06-11 07:23:49 +09:00
pewdiepie-archdaemon 5ebb6ee1b6 Email library: New (compose) button 4px taller + 2px lower
Base .memory-toolbar-btn is 24px tall at top:-4px. Bump the compose
button alone to 28px (4px taller) and top:-2px (moves down 2px) so
it reads as the primary action in the toolbar without affecting
Select/Refresh.
2026-06-11 07:22:38 +09:00
pewdiepie-archdaemon decd7ca86b Email/doc split: stop auto-tab-down when there's no room
Previously _prepareEmailWindowForDocument would:
  1. Check if there was horizontal room for both email + doc.
  2. If not, try collapsing the sidebar to recover space.
  3. If even that wasn't enough, _clearEmailDocumentSplit() — the
     email tab-down the user has been disliking.

Drop step 3. We still try collapsing the sidebar (free easy room),
but if the layout is still cramped, just dock anyway and let the
user manage their layout. _clearEmailDocumentSplit() is still
called on the legitimate close paths.
2026-06-11 07:17:26 +09:00
pewdiepie-archdaemon ba39b6b601 Tasks: optional persona for LLM + research tasks (biases output voice)
Wire the existing built-in PERSONAS catalog through to scheduled tasks
the same way I wired it to reminder synthesis. Repurposes the
dormant scheduled_tasks.character_id column.

UI (static/js/tasks.js)
- New 'Persona' select in the LLM / Research task form, with the five
  built-in characters (socrates/razor/nietzsche/spark/odysseus) plus a
  default 'no persona' option. Pre-populates from existing.character_id
  on edit. Non-llm/research types explicitly clear it on save.

API (routes/task_routes.py)
- TaskCreate + TaskUpdate gain character_id: Optional[str].
- _task_to_dict echoes character_id back so the form can hydrate on
  edit. Update endpoint stores '' as None to allow clearing.

Runner (src/task_scheduler.py)
- When task.character_id is set and matches a built-in persona, prepend
  the persona prompt to the task system prompt so the model speaks in
  that voice while still knowing it's running a scheduled task.
- crew_member.personality still wins as the base; character_id stacks
  on top.
2026-06-10 23:36:18 +09:00
pewdiepie-archdaemon 9f75b40817 Email row: fix crash from leftover menu-wrap wiring after button removal
I removed the .email-menu-wrap markup from email rows earlier but
left the JS that queries it and calls .addEventListener on the
result. Since the query returns null, every _createEmailItem call
threw and the row never made it into the list — most visibly:
clicking a sender name to filter by them didn't appear to work,
because the row wiring (including the sender click handler) was
ripped out mid-construction.

- Drop the unconditional menuWrap.addEventListener('click', ...)
  block — there's no menu to open.
- Drop the early-return guard on touchstart that referenced the
  removed wrap.
- The two remaining .email-menu-wrap queries are already guarded
  with 'if (menuWrap)' so they safely no-op.
2026-06-10 23:31:23 +09:00
pewdiepie-archdaemon 706bc5c3af Email reminder bell: re-evaluate visibility live on settings change
The bell is already gated on settings.reminder_channel === 'email', but
the check only ran at email-library init — so switching the reminder
channel in Settings didn't update the bell until you reopened Email.

- Settings/Reminders channel-change handler now dispatches
  odysseus-reminder-channel-changed { channel } after saving.
- emailLibrary listens for it and re-runs _syncEmailReminderBellVisibility
  with the new channel value.
2026-06-10 23:26:53 +09:00
pewdiepie-archdaemon 79888890f9 Email accounts strip: drop redundant 'Accounts' label during load — whirlpool alone
The strip already lives where account chips render, so the text label
beside the whirlpool was redundant. Strip the label + the fallback
'Accounts...' text — the spinner alone tells the user accounts are
loading.
2026-06-10 23:22:59 +09:00
pewdiepie-archdaemon 13d26f4cbe Email row: remove the three-dot actions menu button
Dropped the .email-menu-wrap / .email-menu-btn from each row. Other
handlers that check 'if (e.target.closest(.email-menu-wrap)) return;'
safely no-op when the element doesn't exist. Row click + swipe still
open the email and its in-reader actions.
2026-06-10 23:21:17 +09:00
pewdiepie-archdaemon ed914da582 Email attachments: nudge download spinner up 2px to sit on icon baseline 2026-06-10 23:19:19 +09:00
pewdiepie-archdaemon 3f09271230 Edge-dock resize handle: fade accent stripe in on hover
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).
2026-06-10 23:18:07 +09:00
pewdiepie-archdaemon 18d1306d6a Email reader: theme-aware override for Gmail drive/attachment chips
Gmail composer chips arrive with inline border:1px solid #ddd + an
assumed white background, so on dark themes they read as a barely-
visible white box with the filename invisible. Override .gmail_chip /
.gmail_drive_chip inside .email-reader-body:

- Strip inline width:386px / height:20px (use auto + max-width:100%),
- Re-flow as inline-flex with a 6px gap so icon + filename align.
- Background tinted with var(--fg) 4%, border = var(--border).
- Anchor uses var(--accent) and the filename span uses var(--fg) so
  text is always legible regardless of theme.
- Icon img clamped to 16x16.
2026-06-10 23:17:18 +09:00
pewdiepie-archdaemon 4cf6a404a1 Email attachments: swap paperclip for whirlpool spinner during download
Before: the attachment chip just dimmed (opacity 0.6) while the file
downloaded — easy to miss on a large attachment.

Now: replace the paperclip SVG with a 12px whirlpool spinner for the
duration of the fetch, restoring the original icon when the download
finishes (or errors out). Same loading vocabulary as Test / Scan /
Probe / Send buttons elsewhere in the UI.
2026-06-10 23:15:52 +09:00
pewdiepie-archdaemon d589c44cee Email inbox: visual flash when an email is auto-marked done after sending
When the email-answered event fires (user just sent a reply, so the
source email auto-marks as done), the row was getting the .active
class instantly with no visible cue beyond the checkbox tick. Add a
brief .email-auto-done-flash class on the row that runs two keyframe
animations:
- email-auto-done-row: tints the row background with the accent for
  ~1.2s then fades to transparent.
- email-auto-done-check: pops the done checkbox to 1.4× with an
  accent ring that expands outward over 0.6s.

Class self-removes after 1.2s so it doesn't replay on re-renders.
2026-06-10 23:06:42 +09:00
pewdiepie-archdaemon 108fd8cc4e Edge-dock resize handle: drop the visible accent stripe
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.
2026-06-10 23:05:39 +09:00
pewdiepie-archdaemon 3326efceea Email accounts strip: wheel + grab-drag horizontal scroll
The single-row chip strip relied on native horizontal scroll, which is
hard to reach without a horizontal wheel. Wire two scroll mechanisms
on the strip once it's rendered:

- Vertical wheel → horizontal scroll (intercept only when overflow
  exists and the wheel motion is primarily vertical, so normal page
  scroll still works elsewhere).
- Mouse grab-and-drag: cursor goes grab/grabbing, mousedown→move
  bumps scrollLeft by the cursor delta. A 5px drag threshold cancels
  the chip click so the user can drag-scroll without accidentally
  switching accounts.
2026-06-10 23:00:29 +09:00
pewdiepie-archdaemon eff273088e Email: revert single-row email-item; account chips single-row at all widths
- Revert the email row layout — sender/date stay above subject again,
  matching the original two-line item that the user actually wanted.
- The account filter chips (#email-lib-accounts) wrapped onto multiple
  rows on desktop. Promote the mobile-only horizontal-scroll rule to
  apply at every breakpoint so the chips always sit on one row with
  overflow scroll, regardless of screen size.
2026-06-10 22:55:10 +09:00
pewdiepie-archdaemon dc3b47a471 Sessions sort dropdown: nudge all items 2px more left
Group row's auto-sort-sessions-btn padding-left 6→4, and
.sort-dropdown-item left padding 8→6 so 'Last Active', 'Newest First',
'By Folder', '↑↓ Rearrange', '● Select' all shift in by the same
amount, matching the Group nudge.
2026-06-10 22:49:53 +09:00
pewdiepie-archdaemon a1268ba2c4 Email list: collapse to a single row — [sender] [subject] [date]
Subject was on its own line below sender/date. Move it inline so each
email occupies one row: sender capped at 35% width (ellipsis), subject
takes the remaining space (ellipsis), date pins to the right. Tighter
list density at the cost of dropping the spare line for snippet text
(none was being rendered anyway).
2026-06-10 22:45:19 +09:00
pewdiepie-archdaemon f3b7d84c2e Sidebar/Chats manage button: parent-hover reveal + clearer 'manage' text
Two related fixes for the Chats section header:
- The 'manage' label only slid out when the button itself was hovered.
  Add section-header-flex:hover to the reveal rule so hovering the sort
  icon (or anywhere in the section header) also opens the label.
- Parent-hover opacity bumped 0.45 → 0.85 so the 'manage' text reads
  much more clearly when revealed. Direct hover on the button still
  pushes to full opacity 1.
2026-06-10 22:44:50 +09:00
pewdiepie-archdaemon 2eaecc6393 Sidebar/Chats manage button: drop hover background tint
The shared .section-header-btn:hover rule paints a tinted background
across all section header buttons. On the Chats manage button this
showed as a box behind the sliding 'manage' label, which the user
didn't want. Override background to transparent for that one button.
2026-06-10 22:42:06 +09:00
pewdiepie-archdaemon 3d2224aa28 Sessions: Esc + outside-click also close the Move-to-folder submenu
The session-dropdown Esc handler only closed .session-dropdown-menu,
leaving the .session-folder-submenu (Move to folder → folder list)
orphaned on screen. Same gap on the click-away path. Extend both
selectors to cover the submenu so a single Esc / outside-click
dismisses the whole stack.
2026-06-10 22:39:23 +09:00
pewdiepie-archdaemon 6f0b37e6af Chats sidebar: 'manage' label sits in flex flow so its area is clickable
Email's 'new' label is absolutely positioned to the LEFT of the '+'
icon, which works there because the '+' is the visible/clickable
anchor. The chats manage button has no visible glyph at rest, so the
label was rendered outside the button's bounding box — hovering
'manage' lost the :hover state and clicking it missed.

Override list-item-plus-label inside chats-manage-btn:
  position: static (in flex flow) + max-width:0 / max-width:80px
expand-on-hover so the button's clickable rect grows alongside the
text. Hover stays sticky; click hits.
2026-06-10 22:39:05 +09:00
pewdiepie-archdaemon 7feed91c07 Chats sidebar: 'manage' slides in from the side like email's 'new'
The list-item-plus-label slide-in needs a visible anchor element so
the button takes up consistent width and the absolutely-positioned
label can fly in to the left of it. Email uses the '+' SVG as that
anchor; here we use an empty 13x13 spacer span instead — same
footprint, no glyph. Result: empty button at rest (still visible per
the chats-manage-btn fade rules), 'manage' slides in from the left
on direct hover.
2026-06-10 22:37:06 +09:00
pewdiepie-archdaemon 53613e9ea7 Sessions sort: nudge auto-sort icon + 'Group' text 4px left (10→6 left padding) 2026-06-10 22:35:21 +09:00
pewdiepie-archdaemon db92c89445 Chats sidebar: drop library SVG from manage button — text-only 'manage'
Removed the book/library SVG and list-item-plus-btn/-label classes.
The button is now a plain text button styled like email's 'new' label
(9.5px, 0.02em letter-spacing), reusing the existing chats-manage-btn
opacity hover-reveal rules so it still fades until you hover the
section.
2026-06-10 22:35:06 +09:00
pewdiepie-archdaemon 26fbde308b Settings overhaul + UI polish pass
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.
2026-06-10 15:15:13 +09:00
pewdiepie-archdaemon b6158f3797 Settings/Add Models: bump Local Type select width 57→62px 2026-06-09 15:12:57 +09:00
pewdiepie-archdaemon 0661be92ef Settings/Add Models: fuse Local Type select + URL input into one bordered group 2026-06-09 15:12:12 +09:00
pewdiepie-archdaemon cbbe688b79 Settings/Add Models: shrink Local Type select by 15px (72→57) 2026-06-09 15:11:07 +09:00
pewdiepie-archdaemon 907d8d68cb Settings/Add Models: drop 'Type:' label, keep the LLM/Image select 2026-06-09 15:10:48 +09:00
pewdiepie-archdaemon 5a902c79fb Settings/Add Models: Local card — Type and Add inline with URL field
Lift the LLM/Image Type select to the left of the URL input and the Add
button to its right, so the primary action (URL + Add) sits on one row.
Scan / Ollama / Key / Test stay on the action row below.
2026-06-09 15:09:28 +09:00
pewdiepie-archdaemon bc4307a4a3 Settings/Add Models: split Local and API into separate cards, always show API key
Drop the in-card Local/API tab strip — each is now its own admin card with
a normal h2 heading (Local on top, API below). The API key input is
always visible (no more click-to-reveal toggle), matching how cloud
providers actually work. Local keeps the optional key reveal since
local servers usually don't need one.

Dead code removed: wireModelsTabs IIFE and the adm-epApiKeyBtn toggle wire.
2026-06-09 14:57:42 +09:00