Phase 10: CalDAV full lifecycle, update_note, dashboard inline streaming, keyboard shortcuts

Backend:
- caldav.py: Full event lifecycle — update_event, delete_event; VTODO suite —
  create_todo, list_todos, complete_todo, delete_todo; list_calendars; timezone
  support via ZoneInfo; reminders via VALARM; attendees; multi-calendar search
  (_get_all_calendars scans all calendars when no specific one is configured)
- tools.py: New update_note tool (find by title + replace/append modes),
  7 new CalDAV tool definitions, corresponding execute_tool cases
- llm.py: Update system prompt — add update_note guidance, full CalDAV action list
- intent.py: Confidence scoring (high/medium/low) + should_execute property;
  conversation history support for anaphora resolution; routing rules for
  update/delete events, todos, update_note vs create_note disambiguation,
  time-period → list_events (not search_events), reminder_minutes conversion
- generation_task.py: Parallel fetch of tools + intent_model setting; dedicated
  intent model (OLLAMA_INTENT_MODEL env var or per-user intent_model setting)
- config.py: Add OLLAMA_INTENT_MODEL env var

Frontend:
- HomeView.vue: Inline streaming response (no navigation); quick action chips;
  isConversational computed — prominent "Continue this conversation" CTA when
  no tool calls; auto-focus chat input on mount via chatInputRef
- DashboardChatInput.vue: defineExpose({ focus }) for external focus control
- ChatView.vue: Escape key handler — close picker → close sidebar → clear
  textarea → navigate home; onUnmounted cleanup
- App.vue: Global ? key shortcut toggles keyboard shortcuts overlay; shared
  state via useShortcuts composable; Transition animation
- AppHeader.vue: ? button for shortcuts overlay discoverability
- useShortcuts.ts (new): Shared showShortcuts ref + open/close/toggle helpers
- ToolCallCard.vue: note_updated, event_updated, event_deleted, calendars,
  todo, todos, todo_completed, todo_deleted label cases + render blocks
- SettingsView.vue: Intent model field + caldav_timezone setting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 22:04:41 -05:00
parent 75560dee4e
commit 70cba72a80
15 changed files with 1569 additions and 71 deletions
+29 -1
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { onMounted, ref, computed, watch, nextTick } from "vue";
import { onMounted, onUnmounted, ref, computed, watch, nextTick } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useChatStore } from "@/stores/chat";
import { useSettingsStore } from "@/stores/settings";
@@ -69,6 +69,7 @@ const inputPlaceholder = computed(() => {
});
onMounted(async () => {
document.addEventListener("keydown", onGlobalKeydown);
await store.fetchConversations();
if (convId.value) {
await store.fetchConversation(convId.value);
@@ -265,6 +266,33 @@ function promoteAutoNote(note: { id: number; title: string }) {
function excludeAutoNote(noteId: number) {
excludedNoteIds.value = new Set([...excludedNoteIds.value, noteId]);
}
// Keyboard shortcuts
function onGlobalKeydown(e: KeyboardEvent) {
if (e.key !== "Escape") return;
// Close note picker first
if (showNotePicker.value) {
showNotePicker.value = false;
return;
}
// Close mobile sidebar overlay
if (sidebarOpen.value) {
sidebarOpen.value = false;
return;
}
// If textarea is focused and has content, clear it (don't navigate away)
if (document.activeElement === inputEl.value && messageInput.value.trim()) {
messageInput.value = "";
resetTextareaHeight();
return;
}
// Navigate home
router.push("/");
}
onUnmounted(() => {
document.removeEventListener("keydown", onGlobalKeydown);
});
</script>
<template>