Commit Graph

275 Commits

Author SHA1 Message Date
Roberto
732235c93a feat(contextual): mount ContextualSidebar via ResizablePanelGroup in AppShell
Provider wraps the Outlet so contextual chat survives all route
transitions. The sidebar is hidden on the home route and when
chat.open is false. Sidebar size is provider-owned and persisted
to localStorage.
2026-05-14 21:55:53 +02:00
Roberto
539beaf225 feat(contextual): ContextualSidebar shell
Top-right elevated controls (new chat, close) and a ChatSurface in
contextual variant. No header, no scope chip. Not yet mounted into
AppShell (M4.5).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 21:52:31 +02:00
Roberto
f9eb4b41b6 feat(contextual): adiuva trigger button + compass icon
Elevated 48px button with continuous compass-settle animation.
Hover deepens shadow and adds a gold ambient glow. .sm variant
(32px) reused by sidebar controls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 21:51:59 +02:00
Roberto
4e42ac8b04 feat(contextual): useContextualScope hook
Pages call this in render with their current scope. Provider diffs
by JSON key and only fires the WS scope update on real change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 21:51:02 +02:00
Roberto
869e0d82ee feat(contextual): ContextualChatProvider
Holds open/size/sessionId/scope/messages/streaming state. Creates
or hydrates a contextual aiChatSessions row on mount, persists
messages, and fires scope updates through window.electronAI when
the renderer scope changes. Not yet mounted into AppShell (M4.5).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 21:50:30 +02:00
Roberto
49c0ae2413 fix(drizzle-executor): split comma-separated filter values into IN clause
Backend agent tools (e.g. list_tasks, count_tasks) pass multi-value
filters as a single comma-joined string like status="todo,in_progress".
The generic buildConditions matched that with eq(status, 'todo,in_progress'),
which matched zero rows — agent reported "no tasks found" for any
multi-status query. Now split on comma and emit inArray(col, parts)
when more than one part is present. Also accepts an array directly.

Repro: home chat, ask "what tasks are overdue?". Trace b09714e6e14825f5fa3a226cad053647.
2026-05-14 20:54:29 +02:00
Roberto
4b5f379126 feat(chat): persist home chat history to SQLite
Home chat now creates an aiChatSessions row on first use and
appends every user/assistant message. Session id persisted in
localStorage so reopening the app reattaches to the same row.
Hydration of past messages into the in-memory cache is deferred
to a follow-up — current visible behavior matches the previous
in-memory cache.
2026-05-14 20:11:44 +02:00
Roberto
aad8292f9e fix(chat): useChatStream appends entity-tag mutations like useAIChat
Critical fix: stream_end was dropping event.mutations, which the
baseline useAIChat parses into inline entity tags. Without this,
any consumer migrated from useAIChat to useChatStream would
silently lose entity card rendering on assistant messages.

Exports parseMutationsToEntityTags from useAIChat for reuse.
Also adds the missing 'stream_start' no-op switch case.
2026-05-14 19:11:10 +02:00
Roberto
44a21d662d refactor(chat): home AIChatPanel uses ChatSurface
Pure refactor, no behavior change.
2026-05-14 19:03:33 +02:00
Roberto
ae2cef4335 refactor(chat): extract ChatSurface presentational component
Shared between home and contextual channels. Variant prop selects
between home (full-width, fixed-bottom input) and contextual
(absolute-positioned translucent input with gradient fade) layouts.
2026-05-14 18:59:49 +02:00
Roberto
57462af4f4 refactor(chat): extract useChatStream hook
Shared streaming engine for home (and forthcoming contextual)
channels. useAIChat still owns cache-key + tRPC dispatch; that
wiring is migrated in the next commit.
2026-05-14 18:57:12 +02:00
Roberto
425025ad68 feat(router): add aiChat tRPC sub-router
CRUD for chat sessions and messages, used by both home and contextual
channels. No UI consumer yet — added ahead of refactor.
2026-05-14 18:53:03 +02:00
Roberto
b879760013 chore: gitignore local dev.db used by drizzle-kit push
drizzle-kit push connects to ./dev.db for local schema
verification. The file should not be tracked.
2026-05-14 18:48:55 +02:00
Roberto
21aa1db07e feat(db): add ai_chat_sessions and ai_chat_messages tables
Local chat history persistence. Same model used by both home and
contextual channels. Indexes on (session_id, created_at) and
(channel, updated_at) for ordering and listing.
2026-05-14 18:46:39 +02:00
Roberto
81fe6d29e2 perf(DateTimeField): keep typing local, memoize Calendar + SegmentSpan
Typing in a segment no longer calls onChange — local state only.
onChange now fires only on commit (Enter, calendar pick), so the
parent TaskFormDialog stops re-rendering on every keystroke (and
the heavy Calendar grid + every pill / popover / query stops
re-rendering with it).

Inside DateTimeField:
- Calendar element memoized via useMemo keyed on the committed
  date's ms — only re-renders when a full valid date is reached
  or changes.
- SegmentSpan wrapped in React.memo.
- onSegKeyDown stabilized via useCallback + functional setSeg +
  refs for order / withTime / onChange / onCommit, so its
  identity never changes.
- Per-segment ref setters cached in a useRef map so they don't
  swap identity on each render.

TaskFormDialog:
- onChange / onCommit passed to DateTimeField wrapped in useCallback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 13:46:47 +02:00
Roberto
b2d7fa1723 fix(DateTimeField): drop value-sync useEffect that wiped partial typing
Each onChange propagated to the parent caused a re-render with a fresh
Date instance, which retriggered the value-sync effect and overwrote
the in-progress segment state. Result: after picking a day in the
calendar, typing '14' in the hour field only kept the last digit.

Initial value still seeds segment state via the useState lazy
initializer, and Radix Popover unmounts the content on close so each
open starts from the current value.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 13:09:21 +02:00
Roberto
4c641ab93a fix(DateTimeField): autocomplete missing segments on Enter, keep popover open on calendar pick
Enter inside any segment now commits even when the value is partial.
Missing segments default to today (day, month, year) or to 00
(hour, minute). Out-of-range values clamp to their segment max,
and an impossible day-in-month (e.g. Feb 31) clamps to the last
day of the resolved month.

Calendar click no longer fires onCommit, so the Due popover stays
open and the date segments update inline. The popover closes via
Enter inside a segment, Esc, or click outside.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 12:56:02 +02:00
Roberto
84720ff23c feat(TaskFormDialog): segment-based DateTimeField for Due, header padding, assignees kbd wrap
- New DateTimeField component: segment editor (DD/MM/YYYY HH:MM) honoring
  FormatPrefs.dateFormat. Each segment is keyboard-driven (digits to
  enter, arrows up/down to inc/dec, arrows left/right or separators to
  move, Enter commits and closes the Due popover). Calendar grid below
  stays in sync.
- TaskFormDialog: header gains px-5 pt-5 pb-2 so the title+description
  don't sit flush against the DialogContent edges.
- AssigneesList: ArrowDown on the last list item now focuses the
  "new name" Input; ArrowUp from the Input returns to the last list
  item; Esc on the Input closes the popover.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 12:38:27 +02:00
Roberto
d7307e146a fix(TaskFormDialog): control Due popover open state for keyboard close
Add dueOpen state + onOpenChange so Esc and outside-click close the
popover consistently with the other pills; wire DateField.onCommit
to close the popover after Enter or calendar click.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 11:21:42 +02:00
Roberto
7d4059ca4b i18n: add tasks.newTaskDescription / editTaskDescription
Two new keys for the DialogDescription line under the TaskFormDialog
header. Added to all five supported languages (en, it, es, fr, de).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 11:16:56 +02:00
Roberto
9691842e79 feat(TaskFormDialog): new header, full keyboard nav, DateField-based due
Header: DialogTitle + DialogDescription, no separator border, matching
AddEventDialog.
Keyboard: pills row uses roving tabindex (←/→/↑/↓ + Home/End +
Enter-to-open). Each list popover (Project, Priority, Status,
Assignees) uses useListboxKeys for ↑/↓/Home/End/Enter/Space/Esc/Tab.
Due: replaced bespoke Calendar + hour/minute Selects with a
DateField (flat + withTime), which is keyboard-typeable and
format-prefs aware. derivePartsInTz / updateDueTime / HOURS /
MINUTES helpers removed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 11:15:18 +02:00
Roberto
094840e671 refactor(PropertyPill): render as button with forwardRef and focus ring
Switch the trigger element from a presentational <span> to a real
<button type="button"> with forwardRef so the pill can receive keyboard
focus, be used directly as a PopoverTrigger asChild, and show a
focus-visible ring. Public API stays compatible: icon, label, value,
empty plus any standard button HTML attributes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 11:07:55 +02:00
Roberto
e8592b25a8 feat(date-field): add withTime and flat props
withTime renders hour/minute selects under the Calendar and appends
HH:MM to the display text when the value has a non-midnight time.
flat renders Input + Calendar (+ Time row) inline without the
internal Popover, so a caller can embed DateField inside its own
popover without nesting. Existing callers continue to work
unchanged (both props default to false).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 11:05:31 +02:00
Roberto
27b385df53 feat(parseDate): accept optional ' HH:MM' suffix
Existing callers unaffected: a bare date still parses to midnight as
before. New behavior: an optional trailing ' HH:MM' is stripped from
the input, the date portion goes through the existing resolution
order, and setHours/setMinutes is applied to the result. Out-of-range
times (e.g. 25:00, 12:99) return null.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 11:01:28 +02:00
Roberto
e170844f17 feat: add useListboxKeys hook for popover list keyboard
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 10:58:49 +02:00
Roberto
27c1194384 feat: add useRovingFocus hook for roving-tabindex groups
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 10:57:03 +02:00
Roberto
26ea095f60 ui(AddEventDialog): add DialogDescription to match project dialog header 2026-05-14 08:58:57 +02:00
Roberto
751d16a9f4 fix(AddEventDialog): clear focusedRowId on row blur + skip X icon in Tab cycle 2026-05-14 08:52:39 +02:00
Roberto
285214a2d2 ui(AddEventDialog): swap ToggleGroup for Tabs to match Gantt zoom selector 2026-05-14 08:38:10 +02:00
Roberto
89645f2abd polish(timeline): end-date validation + project-lock hint
- Surface invalidMessage on end DateField when end < start
- Auto-clear endDate when start changes past it
- Add title tooltip on SelectTrigger when project is locked

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 19:06:40 +02:00
Roberto
7dadeb88fe fix(AddEventDialog): reset edit mode when removing the row being edited 2026-05-13 19:05:27 +02:00
Roberto
13531fec40 feat(timeline): keyboard nav + edit mode for staged rows
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 19:01:37 +02:00
Roberto
e254efd420 a11y(AddEventDialog): i18n staged-list label + add title aria-label 2026-05-13 18:59:44 +02:00
Roberto
6d79911414 feat(timeline): batch-stage flow in AddEventDialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:55:53 +02:00
Roberto
69a859e19f i18n(timeline): add keys for batch-add dialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:52:30 +02:00
Roberto
098ce86c76 fix(EditEventDialog): remove non-null assertion via inline expression 2026-05-13 18:50:17 +02:00
Roberto
9ef809ba02 refactor(timeline): migrate EditEventDialog to DateField
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:56:32 +02:00
Roberto
024d572ebb fix(DateField): wire aria-describedby + prevent calendar-icon focus steal
- Add useId() to generate stable fieldId/errorId; link <Input> to error
  <p> via aria-describedby so screen readers announce the message.
- Add onMouseDown={e => e.preventDefault()} to the calendar icon Button
  so clicking it does not trigger onBlur on the input mid-typing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:49:38 +02:00
Roberto
d24f09bbea feat(ui): add DateField with typed entry + calendar popover 2026-05-13 17:45:51 +02:00
Roberto
56fe6c0754 i18n(date): add date keyword arrays for parseDate 2026-05-13 17:25:41 +02:00
Roberto
c76de207d7 fix(date): re-validate leap-day roll-forward; tighten parseDateRange separator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 16:10:08 +02:00
Roberto
4e89a7a96c feat(date): add parseDate utility with locale-aware parsing 2026-05-13 16:04:44 +02:00
Roberto
0fc3aa421e fix(adiuvAI): WsStreamEndSchema accepts null mutations/error (backend emits null for zero-tool turns) 2026-05-13 09:20:45 +02:00
Roberto
c10fbe22d7 fix(adiuvAI): cap default DialogContent at sm:max-w-lg 2026-05-13 08:34:37 +02:00
Roberto
e3e0b06fb6 fix(adiuvAI): add 3 folder actions to ToolCallActionSchema enum (caused silent WS hang) 2026-05-12 17:57:52 +02:00
Roberto
b3d85b93f1 feat(adiuvAI): drizzle-executor read action returns kind+totalSize, supports offset/length 2026-05-12 17:34:19 +02:00
Roberto
659607a1e9 fix(adiuvAI): remove card border from FolderLinkCard, inline layout 2026-05-12 14:46:09 +02:00
Roberto
80a0d2c56f fix(adiuvAI): files section uses standard project section layout 2026-05-12 14:43:38 +02:00
Roberto
66448a25f4 fix(adiuvAI): collapse chip height in compact mode to preserve hero shrink 2026-05-12 14:32:51 +02:00
Roberto
93144b9de8 fix(adiuvAI): position FolderChip right of project title 2026-05-12 14:30:10 +02:00