>;
chatNewMessagesBelow: boolean;
diff --git a/ui/src/ui/app.ts b/ui/src/ui/app.ts
index 799ea9100c64..dbadf1912150 100644
--- a/ui/src/ui/app.ts
+++ b/ui/src/ui/app.ts
@@ -154,6 +154,7 @@ export class OpenClawApp extends LitElement {
@state() chatQueue: ChatQueueItem[] = [];
@state() chatAttachments: ChatAttachment[] = [];
@state() chatManualRefreshInFlight = false;
+ @state() chatGapIndex: number | null = null;
// Sidebar state for tool output viewing
@state() sidebarOpen = false;
@state() sidebarContent: string | null = null;
diff --git a/ui/src/ui/types/chat-types.ts b/ui/src/ui/types/chat-types.ts
index aba1b17301e5..848bb3265b61 100644
--- a/ui/src/ui/types/chat-types.ts
+++ b/ui/src/ui/types/chat-types.ts
@@ -6,6 +6,7 @@
export type ChatItem =
| { kind: "message"; key: string; message: unknown }
| { kind: "divider"; key: string; label: string; timestamp: number }
+ | { kind: "gap"; key: string; newCount: number; timestamp: number }
| { kind: "stream"; key: string; text: string; startedAt: number }
| { kind: "reading-indicator"; key: string };
diff --git a/ui/src/ui/views/chat.ts b/ui/src/ui/views/chat.ts
index e63f56c25fa7..28fa6195f347 100644
--- a/ui/src/ui/views/chat.ts
+++ b/ui/src/ui/views/chat.ts
@@ -65,6 +65,9 @@ export type ChatProps = {
// Image attachments
attachments?: ChatAttachment[];
onAttachmentsChange?: (attachments: ChatAttachment[]) => void;
+ // Gap marker (auto-refresh on event gap)
+ gapIndex?: number | null;
+ onDismissGap?: () => void;
// Scroll control
showNewMessages?: boolean;
onScrollToBottom?: () => void;
@@ -286,6 +289,37 @@ export function renderChat(props: ChatProps) {
`;
}
+ if (item.kind === "gap") {
+ const label =
+ item.newCount > 0
+ ? `${item.newCount} new message${item.newCount !== 1 ? "s" : ""} since you were away`
+ : "New activity since you were away";
+ return html`
+
+
+
+ ${label}
+ ${
+ props.onDismissGap
+ ? html``
+ : nothing
+ }
+
+
+
+ `;
+ }
+
if (item.kind === "reading-indicator") {
return renderReadingIndicatorGroup(assistantIdentity);
}
@@ -538,7 +572,19 @@ function buildChatItems(props: ChatProps): Array {
},
});
}
+ const gapIndex = props.gapIndex ?? null;
for (let i = historyStart; i < history.length; i++) {
+ // Insert gap divider at the boundary between old and new messages.
+ if (gapIndex !== null && i === gapIndex && i > historyStart) {
+ const newCount = history.length - gapIndex;
+ items.push({
+ kind: "gap",
+ key: "divider:gap",
+ newCount,
+ timestamp: Date.now(),
+ });
+ }
+
const msg = history[i];
const normalized = normalizeMessage(msg);
const raw = msg as Record;