From 547b6e6560cb2256722965d13784040a0ec0deb8 Mon Sep 17 00:00:00 2001 From: Federico Jaramillo Martinez Date: Mon, 18 May 2026 21:59:58 +0200 Subject: [PATCH] feat: auto-expand live event groups --- .changeset/live-event-groups.md | 5 +++++ src/client/src/components/ChatView.ts | 30 ++++++++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 .changeset/live-event-groups.md diff --git a/.changeset/live-event-groups.md b/.changeset/live-event-groups.md new file mode 100644 index 0000000..50ac639 --- /dev/null +++ b/.changeset/live-event-groups.md @@ -0,0 +1,5 @@ +--- +"@jmfederico/pi-web": patch +--- + +Expand the live trailing events group while a session is active, then collapse it again once readable conversation output appears. diff --git a/src/client/src/components/ChatView.ts b/src/client/src/components/ChatView.ts index 3e41ac3..bed1a80 100644 --- a/src/client/src/components/ChatView.ts +++ b/src/client/src/components/ChatView.ts @@ -111,17 +111,18 @@ export class ChatView extends LitElement { } override render() { + const groups = this.groupedMessages(); return html`
${this.renderHistoryIndicator()}
{ this.onScroll(); }} @wheel=${(event: WheelEvent) => { this.onWheel(event); }} @touchstart=${(event: TouchEvent) => { this.onTouchStart(event); }} @touchmove=${(event: TouchEvent) => { this.onTouchMove(event); }}> ${this.renderHistoryBoundary()} ${repeat( - this.groupedMessages(), + groups, (group) => group.kind === "message" ? this.messageAnchorKey(group.index) : this.groupAnchorKey(group.endIndex), - (group) => group.kind === "message" + (group, index) => group.kind === "message" ? this.renderMessage(group.message, group.index) - : this.renderMessageGroup(group.messages, group.startIndex, group.endIndex), + : this.renderMessageGroup(group.messages, group.startIndex, group.endIndex, this.isLiveTailGroup(groups, index)), )} ${this.renderQueuedMessages()} ${this.renderSessionActivity()} @@ -139,6 +140,17 @@ export class ChatView extends LitElement { return this.groupedMessagesCache; } + private isLiveTailGroup(groups: ChatGroup[], index: number): boolean { + return index === groups.length - 1 && this.isSessionLive(); + } + + private isSessionLive(): boolean { + return this.status?.isStreaming === true + || this.status?.isCompacting === true + || this.status?.isBashRunning === true + || this.activity?.phase === "active"; + } + private renderActivityDock() { const state = this.activityState(); if (state === undefined) return null; @@ -259,13 +271,14 @@ export class ChatView extends LitElement { `; } - private renderMessageGroup(messages: ChatLine[], startIndex: number, endIndex: number) { + private renderMessageGroup(messages: ChatLine[], startIndex: number, endIndex: number, autoOpen: boolean) { const key = this.groupKey(endIndex); + const open = autoOpen || this.openGroupKeys.has(key); return html` ${this.renderScrollMarker(this.groupScrollMarkerId(endIndex))} -
{ this.onGroupToggle(key, event); }}> +
{ this.onGroupToggle(key, event, autoOpen); }}> - events + ${autoOpen ? "live events" : "events"} ${summarizeChatGroup(messages)}
@@ -410,12 +423,15 @@ export class ChatView extends LitElement { return null; } - private onGroupToggle(key: string, event: Event) { + private onGroupToggle(key: string, event: Event, autoOpen: boolean) { const details = event.currentTarget; if (!(details instanceof HTMLDetailsElement)) return; + if (autoOpen && details.open) return; const openGroupKeys = new Set(this.openGroupKeys); + const wasOpen = openGroupKeys.has(key); if (details.open) openGroupKeys.add(key); else openGroupKeys.delete(key); + if (openGroupKeys.has(key) === wasOpen) return; this.openGroupKeys = openGroupKeys; this.saveOpenGroupKeys(); }