Group the rail's open bot with its sessions
The active row and its session previews now share one highlighted card, with a hairline thread from the bot's icon down to the last session.
This commit is contained in:
@@ -27,7 +27,8 @@ type Props = {
|
|||||||
/**
|
/**
|
||||||
* 48px rail row. Status is the face and the subtitle (the bot's status
|
* 48px rail row. Status is the face and the subtitle (the bot's status
|
||||||
* line while it has one): no badges, spinners or state dots.
|
* line while it has one): no badges, spinners or state dots.
|
||||||
* Selected is ink at 5%, never a hue.
|
* The selected background (ink at 5%, never a hue) is painted by
|
||||||
|
* RailBotCard, which also holds the sessions.
|
||||||
*/
|
*/
|
||||||
export default function BotRow({ bot, href, selected, subtitle }: Props) {
|
export default function BotRow({ bot, href, selected, subtitle }: Props) {
|
||||||
const hue = (bot.hue || null) as BotHue | null;
|
const hue = (bot.hue || null) as BotHue | null;
|
||||||
@@ -48,7 +49,7 @@ export default function BotRow({ bot, href, selected, subtitle }: Props) {
|
|||||||
"flex shrink-0 h-12 items-center gap-3 rounded-default px-1 @rail:px-3 border-0 leading-normal",
|
"flex shrink-0 h-12 items-center gap-3 rounded-default px-1 @rail:px-3 border-0 leading-normal",
|
||||||
"hover:opacity-100 hover:bg-sunken transition-colors duration-[var(--m-fast)]",
|
"hover:opacity-100 hover:bg-sunken transition-colors duration-[var(--m-fast)]",
|
||||||
"focus-visible:ring-2 focus-visible:ring-ink focus-visible:ring-offset-2 focus-visible:ring-offset-paper",
|
"focus-visible:ring-2 focus-visible:ring-ink focus-visible:ring-offset-2 focus-visible:ring-offset-paper",
|
||||||
selected && "bg-ink/5 hover:bg-ink/5",
|
selected && "hover:bg-transparent hover:dark:bg-transparent",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<BotAvatar image_media_id={bot.image_media_id} hue={hue} face={(bot.face || "idle") as BotFace} />
|
<BotAvatar image_media_id={bot.image_media_id} hue={hue} face={(bot.face || "idle") as BotFace} />
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import Stack from "@moduletrace/twui/layout/Stack";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
/**
|
||||||
|
* Groups the open bot's row with its sessions, so the active background
|
||||||
|
* covers both and the sessions read as belonging to that bot.
|
||||||
|
*/
|
||||||
|
selected?: boolean;
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RailBotCard({ selected, children }: Props) {
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
className={twMerge(
|
||||||
|
"w-full shrink-0 flex-col gap-0! items-stretch rounded-default py-1",
|
||||||
|
selected && "bg-ink/5",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import type { BotWithLastMessage } from "@/src/types";
|
import type { BotWithLastMessage } from "@/src/types";
|
||||||
import BotRow from "./bot-row";
|
import BotRow from "./bot-row";
|
||||||
|
import RailBotCard from "./rail-bot-card";
|
||||||
import RailBotSessions from "./rail-bot-sessions";
|
import RailBotSessions from "./rail-bot-sessions";
|
||||||
|
import Divider from "@moduletrace/twui/layout/Divider";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
bot: BotWithLastMessage;
|
bot: BotWithLastMessage;
|
||||||
@@ -12,11 +14,24 @@ type Props = {
|
|||||||
/**
|
/**
|
||||||
* A worker in the rail, and its sessions while it is the open conversation.
|
* A worker in the rail, and its sessions while it is the open conversation.
|
||||||
*/
|
*/
|
||||||
export default function RailBotEntry({ bot, href, selected, workspace_id }: Props) {
|
export default function RailBotEntry({
|
||||||
|
bot,
|
||||||
|
href,
|
||||||
|
selected,
|
||||||
|
workspace_id,
|
||||||
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<RailBotCard selected={selected}>
|
||||||
<BotRow bot={bot} href={href} selected={selected} />
|
<BotRow bot={bot} href={href} selected={selected} />
|
||||||
{selected ? <RailBotSessions key={bot.id} bot={bot} workspace_id={workspace_id} /> : null}
|
{selected ? (
|
||||||
|
<>
|
||||||
|
<RailBotSessions
|
||||||
|
key={bot.id}
|
||||||
|
bot={bot}
|
||||||
|
workspace_id={workspace_id}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
|
) : null}
|
||||||
|
</RailBotCard>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export default function RailBotSessionRow({ session, loading, disabled, onOpen }
|
|||||||
buttonContentProps={{ className: "w-full min-w-0 flex-nowrap" }}
|
buttonContentProps={{ className: "w-full min-w-0 flex-nowrap" }}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"w-full h-8 shrink-0 justify-start! items-center! gap-2! rounded-default px-2! @rail:pl-14! text-left text-ink dark:text-ink hover:bg-ink/5 hover:dark:bg-ink/5 focus-visible:ring-2 focus-visible:ring-ink",
|
"w-full h-8 shrink-0 justify-start! items-center! gap-2! rounded-default px-2! @rail:pl-14! text-left text-ink dark:text-ink hover:bg-ink/5 hover:dark:bg-ink/5 focus-visible:ring-2 focus-visible:ring-ink",
|
||||||
active && "bg-sunken hover:bg-sunken hover:dark:bg-sunken opacity-100!",
|
active && "bg-transparent hover:bg-transparent hover:dark:bg-transparent opacity-100!",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Span
|
<Span
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ export default function RailBotSessions({ bot, workspace_id }: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className="hidden @rail:flex w-full shrink-0 flex-col gap-0! items-stretch pb-1">
|
<Stack className="hidden @rail:flex w-full shrink-0 flex-col gap-0! items-stretch pb-1">
|
||||||
|
<Stack className="relative w-full flex-col gap-0! items-stretch">
|
||||||
|
<Span
|
||||||
|
aria-hidden
|
||||||
|
className="pointer-events-none absolute left-7 -translate-x-1/2 -top-2 bottom-0 w-px bg-hairline"
|
||||||
|
/>
|
||||||
{shown.map((session) => (
|
{shown.map((session) => (
|
||||||
<RailBotSessionRow
|
<RailBotSessionRow
|
||||||
key={session.id}
|
key={session.id}
|
||||||
@@ -39,6 +44,7 @@ export default function RailBotSessions({ bot, workspace_id }: Props) {
|
|||||||
onOpen={() => sessions.reopenSession({ session_id: Number(session.id) })}
|
onOpen={() => sessions.reopenSession({ session_id: Number(session.id) })}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
</Stack>
|
||||||
{sessions.sessions.length > AppData.RailRecentSessions ? (
|
{sessions.sessions.length > AppData.RailRecentSessions ? (
|
||||||
<BotSessionsModal
|
<BotSessionsModal
|
||||||
bot={bot}
|
bot={bot}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { AppContext } from "@/src/pages/__root";
|
|||||||
import canManageCrew from "@/src/utils/rbac/can-manage-crew";
|
import canManageCrew from "@/src/utils/rbac/can-manage-crew";
|
||||||
import type { BotWithLastMessage, WorkspaceWithActivity } from "@/src/types";
|
import type { BotWithLastMessage, WorkspaceWithActivity } from "@/src/types";
|
||||||
import CoordinatorRow from "./coordinator-row";
|
import CoordinatorRow from "./coordinator-row";
|
||||||
|
import RailBotCard from "./rail-bot-card";
|
||||||
import RailBotSessions from "./rail-bot-sessions";
|
import RailBotSessions from "./rail-bot-sessions";
|
||||||
import RailBotsSection from "./rail-bots-section";
|
import RailBotsSection from "./rail-bots-section";
|
||||||
import RailSectionAddButton from "./rail-section-add-button";
|
import RailSectionAddButton from "./rail-section-add-button";
|
||||||
@@ -28,7 +29,7 @@ export default function RailCrewList({ bots, workspaces, selected_id }: Props) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{coordinator ? (
|
{coordinator ? (
|
||||||
<>
|
<RailBotCard selected={coordinator.id === selected_id}>
|
||||||
<CoordinatorRow
|
<CoordinatorRow
|
||||||
bot={coordinator}
|
bot={coordinator}
|
||||||
bots={bots}
|
bots={bots}
|
||||||
@@ -37,7 +38,7 @@ export default function RailCrewList({ bots, workspaces, selected_id }: Props) {
|
|||||||
{coordinator.id === selected_id ? (
|
{coordinator.id === selected_id ? (
|
||||||
<RailBotSessions key={coordinator.id} bot={coordinator} />
|
<RailBotSessions key={coordinator.id} bot={coordinator} />
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</RailBotCard>
|
||||||
) : null}
|
) : null}
|
||||||
<RailBotsSection
|
<RailBotsSection
|
||||||
action={<RailSectionAddButton title="Add a bot" onClick={() => openForm({ kind: "bot" })} />}
|
action={<RailSectionAddButton title="Add a bot" onClick={() => openForm({ kind: "bot" })} />}
|
||||||
|
|||||||
Reference in New Issue
Block a user