diff --git a/frontend/app.vue b/frontend/app.vue index 03039d6..2c1f0f4 100644 --- a/frontend/app.vue +++ b/frontend/app.vue @@ -358,6 +358,22 @@ const pilotWaveContainer = ref(null); const livePilotUserText = ref(""); const livePilotAssistantText = ref(""); const pilotLiveLogs = ref>([]); +const PILOT_LIVE_LOGS_PREVIEW_LIMIT = 5; +const pilotLiveLogsExpanded = ref(false); +const pilotLiveLogHiddenCount = computed(() => { + const hidden = pilotLiveLogs.value.length - PILOT_LIVE_LOGS_PREVIEW_LIMIT; + return hidden > 0 ? hidden : 0; +}); +const pilotVisibleLiveLogs = computed(() => { + if (pilotLiveLogsExpanded.value || pilotLiveLogHiddenCount.value === 0) return pilotLiveLogs.value; + return pilotLiveLogs.value.slice(-PILOT_LIVE_LOGS_PREVIEW_LIMIT); +}); +const pilotVisibleLogCount = computed(() => + Math.min(pilotLiveLogs.value.length, PILOT_LIVE_LOGS_PREVIEW_LIMIT), +); +function togglePilotLiveLogsExpanded() { + pilotLiveLogsExpanded.value = !pilotLiveLogsExpanded.value; +} let pilotMediaRecorder: MediaRecorder | null = null; let pilotRecorderStream: MediaStream | null = null; let pilotRecordingChunks: Blob[] = []; @@ -422,6 +438,15 @@ let pilotBackgroundPoll: ReturnType | null = null; const lifecycleNowMs = ref(Date.now()); let lifecycleClock: ReturnType | null = null; +watch( + () => pilotLiveLogs.value.length, + (len) => { + if (len === 0 || len <= PILOT_LIVE_LOGS_PREVIEW_LIMIT) { + pilotLiveLogsExpanded.value = false; + } + }, +); + watch( () => authMe.value?.conversation.id, (id) => { @@ -763,6 +788,7 @@ async function sendPilotText(rawText: string) { pilotInput.value = ""; livePilotUserText.value = text; livePilotAssistantText.value = ""; + pilotLiveLogsExpanded.value = false; pilotLiveLogs.value = []; try { await pilotChat.sendMessage({ text }); @@ -1883,6 +1909,38 @@ const calendarViewOptions: { value: CalendarView; label: string }[] = [ { value: "agenda", label: "Agenda" }, ]; +const calendarZoomOptions: Array<{ value: number; label: string }> = [ + { value: 1, label: "400%" }, + { value: 2, label: "250%" }, + { value: 3, label: "100%" }, + { value: 4, label: "50%" }, +]; + +const calendarZoomLevel = computed({ + get() { + if (calendarView.value === "day") return 1; + if (calendarView.value === "week") return 2; + if (calendarView.value === "month" || calendarView.value === "agenda") return 3; + return 4; + }, + set(next) { + const level = Math.max(1, Math.min(4, Number(next) || 3)); + if (level === 1) { + calendarView.value = "day"; + return; + } + if (level === 2) { + calendarView.value = "week"; + return; + } + if (level === 3) { + calendarView.value = "month"; + return; + } + calendarView.value = "year"; + }, +}); + const monthCells = computed(() => { const year = calendarCursor.value.getFullYear(); const month = calendarCursor.value.getMonth(); @@ -1903,6 +1961,20 @@ const monthCells = computed(() => { }); }); +const monthRows = computed(() => { + const rows: Array<{ key: string; startKey: string; cells: typeof monthCells.value }> = []; + for (let index = 0; index < monthCells.value.length; index += 7) { + const cells = monthCells.value.slice(index, index + 7); + if (!cells.length) continue; + rows.push({ + key: `${cells[0]?.key ?? index}-week-row`, + startKey: cells[0]?.key ?? selectedDateKey.value, + cells, + }); + } + return rows; +}); + function monthCellHasFocusedEvent(events: CalendarEvent[]) { const id = focusedCalendarEventId.value.trim(); if (!id) return false; @@ -2023,6 +2095,16 @@ function pickDate(key: string) { calendarCursor.value = new Date(d.getFullYear(), d.getMonth(), 1); } +function openDayView(key: string) { + pickDate(key); + calendarView.value = "day"; +} + +function openWeekView(key: string) { + pickDate(key); + calendarView.value = "week"; +} + function openYearMonth(monthIndex: number) { focusedCalendarEventId.value = ""; const year = calendarCursor.value.getFullYear(); @@ -3393,8 +3475,21 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
+
+

+ Showing last {{ pilotVisibleLogCount }} steps +

+ +

- -
{{ calendarPeriodLabel }}
-
-