Commit Graph

16 Commits

Author SHA1 Message Date
bvandeusen 7a9a8b7819 feat(design): foundation pass — dusty violet palette, warm parchment light, Inter + JetBrains Mono
Applies the FabledSword + Scribe iteration palette to theme.css end to
end: indigo (#7c3aed) → dusty violet (#5B4A8A) accent, cool grey →
Obsidian/Iron/Pewter dark surfaces, warm parchment (#F5F1E8) light
mode, Inter body + JetBrains Mono code loaded alongside Fraunces, and
neutral hairline scrollbars (chrome is structural, not branded).

Adds the action token set (--color-action-primary Moss,
--color-action-secondary Bronze, --color-action-destructive Oxblood,
--color-action-ghost-border Pewter) but does not yet reclassify any
buttons — surface-phase work. Buttons remain dusty-violet gradients in
the meantime, by design.

Removes deprecated --color-accent-warm; replaces concrete usages with
--color-text-secondary (dates, flavor copy) or --color-warning (paused
status). Sweeps hardcoded indigo literals in component scoped CSS so
they don't bypass the token system.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 17:20:01 -04:00
bvandeusen c9f2134ad4 feat(journal): conversational LLM-generated daily prep (replaces card)
The structured prep card was data-rich but voiceless. Replaced with an
LLM-generated conversational opener — same shape the briefing's compilation
slot had — that renders as a normal assistant chat bubble at the top of
the day's conversation.

Backend (services/journal_prep.py):
- Renamed generate_daily_prep -> gather_daily_sections (still pure data
  fetching, no LLM); kept the old name as a backwards-compat alias.
- New _generate_prep_prose: hands the gathered sections to generate_completion
  with a warm-conversational system prompt; returns prose. Falls back to a
  plain greeting if the LLM call fails or no model is configured.
- ensure_daily_prep_message now persists the prep as role='assistant' with
  the prose as content. Structured sections stay on msg_metadata for
  provenance. Auto-upgrades legacy system-role preps in place on next call.

Frontend:
- Drop the <article class="daily-prep"> structured block from JournalView.
  The prep is now just the first chat bubble — picks up the existing
  Illuminated Transcript styling automatically.
- Drop dayMessages / prepMessage / prepSections / asArray helpers — no
  longer needed.
- ChatMessage hideMessage filter: comment refined to clarify it only
  catches LEGACY system-role prep rows. Current preps are assistant-role
  and render normally.

Net effect: open /journal -> first thing you see is a warm assistant bubble
that talks about your day -> input bar below to reply.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 15:42:30 -04:00
bvandeusen cacfcac86a fix(journal): restore weather + events panels; hide daily-prep system msg in chat
The journal UI was over-stripped earlier — weather panel, current-conditions
poll, and the upcoming-events sidebar were all dropped. Restored those (calls
the new /api/journal/weather, /api/journal/weather/current,
/api/journal/weather/refresh, /api/journal/weather/geocode endpoints).

Also: the daily-prep system message was rendering as flat text inside
ChatPanel because there's no .role-system bubble styling. Added a hideMessage
guard in ChatMessage so daily-prep system messages don't render in the chat
stream — they're already shown above as a structured prep card.

News / RSS reactions / article-discuss are intentionally NOT in the journal
(scoped out per user direction). The broader RSS infrastructure cleanup is
a separate, larger task.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 12:22:51 -04:00
bvandeusen 7602bf2293 feat(briefing): hard-cut tear-down
Backend:
- Delete briefing services (pipeline, scheduler, conversations, profile, tools)
- Delete routes/briefing.py + remove blueprint registration
- Move _get_temp_unit into services/weather.get_temp_unit (reads top-level temp_unit setting)
- Rename briefing_preferences.py → rss_filtering.py (functions are RSS-specific)
- Strip briefing scheduler hooks from app.py
- Strip briefing scheduler call from routes/settings.py
- Update test imports (test_rss_service, test_tz_helpers)

Frontend:
- Delete BriefingView, BriefingSetupWizard, BriefingToolStatusRow
- Strip /briefing route + nav links (AppHeader, KnowledgeView)
- Strip Settings → Briefing tab + state + functions + imports
- Strip briefing-intermediate handling from ChatMessage
- Hide /news route + nav links (NewsView depended on briefing endpoints; orphaned in tree)
- Drop unused useSettingsStore from AppHeader

The Android BriefingScreen lives in a separate repo and is not touched here.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:33:37 -04:00
bvandeusen 593d737e26 feat(briefing): UI polish for agentic briefing messages
Three visual improvements for the briefing conversation:

1. Intermediate tool-call messages (empty content, briefing_intermediate:
   true) now render as a compact dashed status row with per-tool pills
   showing result counts, instead of an empty assistant bubble followed
   by a stack of ToolCallCards. Click to expand the full cards.

2. Slot badge on non-intermediate briefing messages — "Full Briefing"
   for compilation, "Morning/Midday/Afternoon Update" for slot
   injections. Slot updates get a softer bubble treatment (transparent
   background, muted border, smaller text) so the compilation stays
   visually dominant.

3. Slot separator now triggers on briefing_slot metadata (not the
   compilation-only rss_item_ids), and uses a look-behind so it only
   fires when there's a prior slot message — no separator above the
   first briefing of the day.

New component: BriefingToolStatusRow.vue handles the intermediate
pill row and delegates to ToolCallCard when expanded.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 22:10:31 -04:00
Bryan Van Deusen 3c3dd7377a UI polish: Illuminated Transcript design language
- Fraunces serif for headings and assistant identity label
- Chat bubbles flipped: assistant responses elevated (card + left indigo
  accent + shadow), user prompts ghosted (transparent bg, muted text)
- AppLogo: book fill changed from text-color to indigo in light mode
- AppHeader: border-bottom removed; active nav uses underline accent
- NoteCard/TaskCard: border replaced with whisper indigo-tint shadow
- HomeView: dashboard-response uses left accent; progress bars indigo;
  empty-state decorative marker; project cards use shadow depth
- NotesListView: btn-new/btn-cta use indigo gradient + glow
- All primary CTA buttons: indigo gradient (135deg #6366f1 → #4f46e5)
  with box-shadow glow on hover
- Dark palette: navy (#1a1a2e family) → slate-indigo (#111113 family)
- Scrollbars: thin 4px indigo-tinted

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 15:18:22 -04:00
Bryan Van Deusen 5d9085d564 UI: flip bubble hierarchy — responses elevated, prompts recessed
Design direction: "Illuminated Transcript" — the AI response is the
primary artifact; user prompts are subordinate margin notes.

ChatMessage.vue:
- User bubble: ghosted background (rgba 3-4% opacity), muted text,
  thin near-invisible border. De-emphasized, recessed.
- Assistant bubble: left indigo accent border (2px), elevated shadow
  via --color-bubble-asst-shadow. Commands attention.
- Assistant role label: indigo (var(--color-primary)); user label muted.
- Removed old white-override content rules (no longer on dark bg).
- Thinking block: full border → left-only indigo accent bar (35% opacity),
  removed inner top border.

ChatView.vue:
- Streaming bubble: same left accent + shadow as assistant bubbles.
- Streaming role label: indigo to match ChatMessage.
- Thinking block in streaming: same treatment as ChatMessage.

theme.css:
- Added --color-bubble-user-bg/border/text for light + dark.
- Added --color-bubble-asst-shadow for light + dark.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 15:02:39 -04:00
Bryan Van Deusen 4a3899d103 UI: indigo/slate theme, border cleanup, rounder edges
theme.css:
- Dark mode palette shifted from navy to dark slate with indigo warmth
  (#111113 / #18181f / #1e1e27). Primary restored to true indigo
  (#818cf8) instead of the blue (#5b9cf6) it had drifted to.
- Borders reduced to near-invisible rgba(99,102,241,0.10) indigo tint.
- --radius-md 8px→12px, --radius-lg 12px→18px.
- Tag/wikilink/status-in-progress colors unified to indigo family.
- Thin indigo-tinted scrollbars (4px, rgba thumb).

ChatMessage.vue:
- Message bubbles 16px→18px radius.
- User bubble: flat primary → indigo gradient (#6366f1→#4f46e5).
- Assistant bubble: border removed, relies on bg-card vs bg contrast.

ChatView.vue:
- Sidebar border-right, chat-header border-bottom, context-sidebar
  border-left all removed.
- Streaming bubble: border removed, radius 16px→18px.
- Active conv item: solid primary fill → 15% opacity indigo tint with
  primary-colored text (softer, more refined selection state).
- Send button: flat primary → indigo gradient + glow on hover.
- New Chat button: glow on hover.
- Queued bubble: matches send button gradient.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 14:38:19 -04:00
bvandeusen 432e0bd2a0 Show Qwen3 thinking output in chat as collapsible Reasoning block
Ollama streams message.thinking tokens alongside message.content when
think=True — previously silently dropped. Now forwarded end-to-end.

Backend:
- llm.py: ChatChunk type gains "thinking" variant; stream_chat_with_tools
  yields ChatChunk(type="thinking") for msg.thinking chunks before content
- generation_task.py: thinking chunks emit "thinking_chunk" SSE events
  (not added to content_so_far — not persisted to DB)

Frontend:
- types/chat.ts: Message.thinking?: string (session-only, not from DB)
- stores/chat.ts: streamingThinking ref; thinking_chunk handler accumulates
  chunks; on done, thinking carried into committed Message object then cleared
- ChatMessage.vue: collapsible <details class="thinking-block"> shown for
  messages that have .thinking content (collapsed by default)
- ChatView.vue + ChatPanel.vue: live thinking block in streaming bubble —
  open while only thinking is flowing, auto-collapses when content arrives;
  typing indicator hidden while thinking is active

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 23:16:59 -05:00
bvandeusen 32e4ee12f2 Add persistent context sidebar, note title fix, and expanded tool suite
Context sidebar + note title:
- ChatView: replace ephemeral context pills with a persistent right-panel sidebar;
  auto-found notes accumulate across turns; attached note shows with pin icon;
  × button excludes a note from future auto-search; hidden on mobile
- routes/chat.py: batch-fetch note titles via get_notes_by_ids() and inject
  context_note_title into each message dict at conversation load time
- notes.py: add get_notes_by_ids() batch fetch helper
- types/chat.ts: add context_note_title field to Message interface
- stores/chat.ts: sendMessage accepts optional 5th arg contextNoteTitle,
  included in optimistic user message
- ChatMessage.vue: context badge shows note title instead of 'Note #N'

Expanded LLM tool suite (all with intent router rules + ToolCallCard display):
- delete_note / delete_task: permanent delete with user confirmation (write tool),
  type-safe (refuse to delete wrong type), clears note context cache on success
- get_note: fetch full note body by query (search_notes returns only 200-char preview)
- list_notes: browse notes by recency/keyword/tags with limit; notes only
- update_note: add tags + tag_mode (replace/add/remove) parameters
- search_notes: add optional type filter ("note" | "task")
- search_todos (CalDAV): keyword-filter todos, companion to list_todos
- caldav.py: add search_todos() built on top of list_todos()
- generation_task.py: register new tools in _WRITE_TOOLS, _TOOL_LABELS, _TOOL_ACTIONS
- llm.py: update available actions list and guidance in system prompt
- intent.py: routing rules for all new tools

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 14:40:34 -05:00
bvandeusen 7ff60eb8a6 Fix unused GenerationTiming import in ChatMessage.vue
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 18:54:21 -05:00
bvandeusen 765e99bb24 Fix duplicate message bug and add generation timing instrumentation
Bug fix:
- ChatView.vue onMounted now skips fetchConversation when the conversation
  is already loaded in the store (same guard that the convId watcher uses).
  This prevents duplicate assistant messages when navigating from the
  dashboard inline chat to /chat/:id after streaming completes.

Generation timing:
- logging.py: add log_generation() — persists per-generation timing
  breakdown to app_logs (category=usage, action=generation) including
  model, total_ms, intent_ms, ttft_ms, generation_ms, and per-tool timings.
  Queryable via existing admin log viewer.
- generation_task.py: collect wall-clock timestamps at every pipeline stage:
  intent classification, per-tool execution (both intent-routed and native),
  time-to-first-token (measured from generation start to first content chunk),
  LLM streaming round duration. Logs via log_generation() and includes timing
  in the SSE 'done' event payload.
- types/chat.ts: add GenerationTiming interface; add optional timing field
  to Message.
- chat.ts: capture timing from done event and attach to assistant message.
- ChatMessage.vue: show timing footer on assistant messages with breakdown:
  "⏱ 4.2s total · first token 0.8s · analyzed 0.3s · created event 0.4s
  · generated 3.5s". Visible this session; persisted to app_logs for
  cross-session benchmarking.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 18:18:46 -05:00
bvandeusen 8996b45e50 Add LLM tool calling for creating tasks, notes, and searching from chat
Ollama tool/function calling integration allows the LLM to create tasks,
create notes, and search existing notes on behalf of the user during chat.
Multi-round tool loop (max 5 rounds) lets the model execute tools then
produce a natural language response. Tool results are persisted in a new
JSONB column on messages and rendered as compact cards with linked titles.

- Migration 0013: add tool_calls JSONB column to messages
- New services/tools.py: tool definitions + execute_tool dispatcher
- llm.py: ChatChunk dataclass, stream_chat_with_tools(), date in system prompt
- generation_task.py: multi-round tool call loop with SSE tool_call events
- Frontend: ToolCallRecord type, streamingToolCalls in store, ToolCallCard
  component, rendering in ChatMessage and ChatView streaming bubble

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 23:34:36 -05:00
bvandeusen fb18d2c41d Add note context visibility in chat and standardize UI design tokens
Improve chat context: build_context() now returns metadata about auto-found
notes, emitted as an SSE event so the frontend can display context pills
showing which notes influenced the response. Users can promote notes for
deeper context (+) or exclude irrelevant ones (x). A note picker lets users
manually attach notes. Multi-word search uses per-term AND matching, and
auto-search iterates keywords individually for broader OR-style coverage.

Standardize styling: introduce CSS design tokens (--radius-sm/md/lg/pill,
--color-success/warning/overlay, --focus-ring) and migrate all components
to use them. Fix header alignment to full-width, add active nav link state,
replace hardcoded colors with CSS variables, and normalize button padding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 06:49:12 -05:00
bvandeusen 38b1ac933e Add settings page, model management, and chat UX improvements
- Settings infrastructure: key-value settings table, GET/PUT API, Pinia store
- Configurable assistant name (default "Fable") in settings and LLM system prompt
- Model catalog with 18 models in 3 categories (General Purpose, Coding,
  Uncensored / Creative Writing) with download/select/remove functionality
- Move Ollama status indicator from chat views to global nav bar
- Chat bubble layout: user messages right-aligned, assistant left-aligned
- Floating dark input bar with auto-focus and circular send button
- Fix HTML entity rendering (&#39; apostrophe issue in marked/DOMPurify pipeline)
- Fix new chat button navigation (fetchConversation before router.push)
- Recent chats section on home page with "New Chat" button
- Update summary.md with Phase 4.5 changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 21:32:02 -05:00
bvandeusen d2b8ab8fe8 Add LLM chat integration with streaming responses via Ollama
Phase 4: Full chat system with SSE streaming, note-aware context, and
conversation persistence.

Backend:
- Migration 0005: conversations + messages tables with FKs and indexes
- Conversation/Message SQLAlchemy models with relationships
- LLM service: ensure_model (auto-pull on startup), stream_chat (NDJSON),
  generate_completion, fetch_url_content (HTML stripping), build_context
  (keyword extraction, related note search, URL content injection)
- Chat service: conversation CRUD, save_response_as_note,
  summarize_conversation_as_note
- Chat routes blueprint: 9 endpoints including SSE streaming for messages,
  save/summarize as note, Ollama model listing
- Auto-pull llama3.1 model on app startup (non-blocking)

Frontend:
- apiStreamPost: SSE client using fetch + ReadableStream
- Chat Pinia store with streaming state management
- ChatView: dedicated /chat page with conversation sidebar + message thread
- ChatPanel: slide-out panel with contextNoteId from current route
- ChatMessage: markdown-rendered message bubble with "Save as Note" action
- Updated AppHeader with Chat nav link + panel toggle button
- Updated App.vue to mount ChatPanel with route-derived context
- Added /chat and /chat/:id routes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 18:45:22 -05:00