Compare commits

..
2 Commits
Author SHA1 Message Date
tbenandClaude Opus 5.5 8c4658f6e3 Release v0.7.1
Release image / image (release) Canceled after 0s
CI / check (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 04:55:23 +01:00
tbenandClaude Opus 5.5 d3ce787087 Show every bot of a chatroom in its rail icon
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 04:54:55 +01:00
3 changed files with 54 additions and 7 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "cobble", "name": "cobble",
"version": "0.7.0", "version": "0.7.1",
"module": "index.ts", "module": "index.ts",
"type": "module", "type": "module",
"private": true, "private": true,
@@ -0,0 +1,48 @@
import Row from "@moduletrace/twui/layout/Row";
import Stack from "@moduletrace/twui/layout/Stack";
import type { ChatroomBot } from "@/src/types";
import ChatroomStone from "./chatroom-stone";
type Props = {
bots: ChatroomBot[];
};
/**
* The square the cluster fills (`w-7 h-7`), the size of a rail avatar.
*/
const ClusterSize = 28;
/**
* A chatroom's bots packed into one avatar-sized square: the asking bot
* on top, the two below it in a triangle (four make a square, the rest
* are left out). Stones stay small and apart: never overlapping, in a
* badge or outlined. One bot fills the square alone.
*/
export default function ChatroomStoneCluster({ bots }: Props) {
const shown = bots.slice(0, 4);
const stone = Math.floor((ClusterSize - 2) / 2);
if (shown.length <= 1) {
return shown[0] ? <ChatroomStone bot={shown[0]} size={ClusterSize} /> : null;
}
const top = shown.length == 3 ? shown.slice(0, 1) : shown.slice(0, 2);
const bottom = shown.length == 2 ? [] : shown.slice(top.length);
return (
<Stack className="shrink-0 gap-0.5! items-center justify-center w-7 h-7">
<Row className="gap-0.5! flex-nowrap justify-center">
{top.map((bot) => (
<ChatroomStone key={bot.id} bot={bot} size={stone} />
))}
</Row>
{bottom.length ? (
<Row className="gap-0.5! flex-nowrap justify-center">
{bottom.map((bot) => (
<ChatroomStone key={bot.id} bot={bot} size={stone} />
))}
</Row>
) : null}
</Stack>
);
}
@@ -2,7 +2,7 @@ import Link from "@moduletrace/twui/layout/Link";
import Span from "@moduletrace/twui/layout/Span"; import Span from "@moduletrace/twui/layout/Span";
import Stack from "@moduletrace/twui/layout/Stack"; import Stack from "@moduletrace/twui/layout/Stack";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import ChatroomStone from "@/src/components/general/chatroom-stone"; import ChatroomStoneCluster from "@/src/components/general/chatroom-stone-cluster";
import type { ChatroomSummary } from "@/src/types"; import type { ChatroomSummary } from "@/src/types";
import chatroomStatusLine from "@/src/utils/chatroom-status-line"; import chatroomStatusLine from "@/src/utils/chatroom-status-line";
@@ -13,12 +13,11 @@ type Props = {
}; };
/** /**
* A chatroom in the rail, the same 48px row as a bot: the asking bot's * A chatroom in the rail, the same 48px row as a bot: its bots' stones
* stone, the request and where it stands. Unread is said in words. * in one avatar-sized cluster, the request and where it stands. Unread
* is said in words.
*/ */
export default function RailChatroomRow({ chatroom, href, selected }: Props) { export default function RailChatroomRow({ chatroom, href, selected }: Props) {
const requester = chatroom.bots[0];
return ( return (
<Link <Link
href={href} href={href}
@@ -32,7 +31,7 @@ export default function RailChatroomRow({ chatroom, href, selected }: Props) {
)} )}
> >
<Span className="mx-auto @rail:mx-0 inline-flex shrink-0"> <Span className="mx-auto @rail:mx-0 inline-flex shrink-0">
{requester ? <ChatroomStone bot={requester} size={28} /> : null} <ChatroomStoneCluster bots={chatroom.bots} />
</Span> </Span>
<Stack className="hidden @rail:flex grow min-w-0 gap-0!"> <Stack className="hidden @rail:flex grow min-w-0 gap-0!">
<Span className="text-sm font-bold leading-5 text-ink truncate w-full"> <Span className="text-sm font-bold leading-5 text-ink truncate w-full">