Files
cobble/src/pages/admin/bots/[bot_id]/(partials)/older-messages-loader.tsx
T
tbenandClaude Opus 5.5 cc109bc361 Carry conversations into new sessions with a visible summary
- Bots get continue_in_new_session: the bot writes a title and summary,
  the session closes for good (read-only) and the new one opens with the
  summary as a "Carried over from the last session" message that links
  back to the old session.
- Continue in new session (conversation menu, sessions modal) and the
  automatic rollover at the session length do the same; New session now
  starts a fresh, unlinked session.
- Session length is an app setting in Settings > General.
- ?session_id= opens a session; a carried-over one shows a bar linking to
  where the conversation went on instead of the composer.
- The thread can open the previous session above the current one (shown
  only, never in the prompt), and replies get search_past_sessions and
  read_past_session.
- Sessions are named from their opening messages, not their latest, and
  a carried summary is labelled as the earlier session's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 05:35:52 +01:00

76 lines
2.0 KiB
TypeScript

import { useEffect, useRef, type RefObject } from "react";
import Loading from "@moduletrace/twui/elements/Loading";
import Button from "@moduletrace/twui/layout/Button";
import Stack from "@moduletrace/twui/layout/Stack";
type Props = {
loading: boolean;
error?: string;
onLoad: () => void;
/**
* Defaults to "Show earlier messages".
*/
label?: string;
/**
* Loads only on a click, never as it scrolls into view.
*/
manual?: boolean;
};
/**
* The top of a thread with older messages: loads them as it scrolls into
* view (unless `manual`), with a button for when it can't (or a load
* failed).
*/
export default function OlderMessagesLoader({
loading,
error,
onLoad,
label = "Show earlier messages",
manual,
}: Props) {
const ref = useRef<HTMLDivElement>(null);
const load = useRef(onLoad);
load.current = onLoad;
useEffect(() => {
const el = ref.current;
if (!el || error || manual || typeof IntersectionObserver == "undefined") return;
const observer = new IntersectionObserver(
(entries) => {
if (entries.some((entry) => entry.isIntersecting)) load.current();
},
{ rootMargin: "400px 0px 0px 0px" },
);
observer.observe(el);
return () => observer.disconnect();
}, [error, loading, manual]);
return (
<Stack
componentRef={ref as RefObject<HTMLDivElement>}
className="w-full items-center justify-center min-h-8"
>
{loading ? (
<Loading size="smaller" />
) : (
<Button
title={label}
type="button"
variant="ghost"
color="text"
onClick={onLoad}
className="text-[13px]! leading-[18px]! text-ink-secondary"
>
{error ? `${error} Try again` : label}
</Button>
)}
</Stack>
);
}