Show a bot group's time beside the last message's play button

The timestamp no longer takes its own line under a bot group: it sits
before the read-aloud button on the last message, after the needs-you
chip when there is one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-19 21:23:53 +01:00
co-authored by Claude Opus 5.5
parent 163d9a434c
commit 148d02dcfe
2 changed files with 15 additions and 7 deletions
@@ -5,7 +5,6 @@ import type { BotFace, BotHue } from "@/src/dict/bot-dict";
import type { BotWithLastMessage } from "@/src/types"; import type { BotWithLastMessage } from "@/src/types";
import type { MessageGroup } from "../(utils)/group-messages"; import type { MessageGroup } from "../(utils)/group-messages";
import BotMessageLine from "./bot-message-line"; import BotMessageLine from "./bot-message-line";
import GroupTimestamp from "./group-timestamp";
type Props = { type Props = {
bot: BotWithLastMessage; bot: BotWithLastMessage;
@@ -13,7 +12,8 @@ type Props = {
}; };
/** /**
* Plain text, no bubble. A 28px stone sits on the group's first line only. * Plain text, no bubble. A 28px stone sits on the group's first line only;
* the group's time sits beside the last message's play button.
*/ */
export default function BotMessageGroup({ bot, group }: Props) { export default function BotMessageGroup({ bot, group }: Props) {
return ( return (
@@ -26,15 +26,15 @@ export default function BotMessageGroup({ bot, group }: Props) {
className="-mt-1" className="-mt-1"
/> />
<Stack className="min-w-0 grow gap-1! items-start"> <Stack className="min-w-0 grow gap-1! items-start">
{group.messages.map((message) => ( {group.messages.map((message, index) => (
<BotMessageLine <BotMessageLine
key={message.id} key={message.id}
message_id={message.id || null} message_id={message.id || null}
text={message.text || ""} text={message.text || ""}
needs_you={Boolean(message.needs_you)} needs_you={Boolean(message.needs_you)}
at={index == group.messages.length - 1 ? group.at : undefined}
/> />
))} ))}
<GroupTimestamp at={group.at} />
</Stack> </Stack>
</Row> </Row>
); );
@@ -4,6 +4,7 @@ import Row from "@moduletrace/twui/layout/Row";
import Stack from "@moduletrace/twui/layout/Stack"; import Stack from "@moduletrace/twui/layout/Stack";
import linkComputerRefs from "@/src/utils/link-computer-refs"; import linkComputerRefs from "@/src/utils/link-computer-refs";
import { MessageMarkdownComponents } from "../(data)/message-markdown-components"; import { MessageMarkdownComponents } from "../(data)/message-markdown-components";
import GroupTimestamp from "./group-timestamp";
import NeedsYouChip from "./needs-you-chip"; import NeedsYouChip from "./needs-you-chip";
import SpeakMessageButton from "./speak-message-button"; import SpeakMessageButton from "./speak-message-button";
@@ -11,15 +12,21 @@ type Props = {
message_id?: number | null; message_id?: number | null;
text: string; text: string;
needs_you?: boolean; needs_you?: boolean;
/**
* The group's time, on the last message only: shown beside its play
* button.
*/
at?: number;
}; };
/** /**
* One bot message, rendered from markdown (GFM; raw HTML is never * One bot message, rendered from markdown (GFM; raw HTML is never
* rendered). Computers it names (`computerRef`) show as buttons that * rendered). Computers it names (`computerRef`) show as buttons that
* switch the bot to them. A play button under it reads it aloud, * switch the bot to them. A play button under it reads it aloud,
* disabled while the bot has no speech model. * disabled while the bot has no speech model, with the group's time
* before it on the last message.
*/ */
export default function BotMessageLine({ message_id, text, needs_you }: Props) { export default function BotMessageLine({ message_id, text, needs_you, at }: Props) {
const can_speak = Boolean(message_id && text.trim()); const can_speak = Boolean(message_id && text.trim());
return ( return (
@@ -32,9 +39,10 @@ export default function BotMessageLine({ message_id, text, needs_you }: Props) {
{linkComputerRefs({ text })} {linkComputerRefs({ text })}
</Markdown> </Markdown>
</Stack> </Stack>
{needs_you || can_speak ? ( {needs_you || can_speak || at ? (
<Row className="gap-2! items-center"> <Row className="gap-2! items-center">
{needs_you ? <NeedsYouChip /> : null} {needs_you ? <NeedsYouChip /> : null}
{at ? <GroupTimestamp at={at} /> : null}
{can_speak ? ( {can_speak ? (
<SpeakMessageButton message_id={message_id!} /> <SpeakMessageButton message_id={message_id!} />
) : null} ) : null}