Add computer switching, crew reporting, job steps and markdown chat
- Bots have a current computer (bots.computer_id), switched from the chat header beside the model button or by clicking a computer the bot names ([computer:ID]); tasks run on it by default - search_computers runs one read-only command on all of a bot's computers; chat replies know their computers (compose-computers-note) - The coordinator can use every computer and grants or revokes crew access with assign_computer - Delegated jobs record requested_by_bot_id; the worker's result or failure is reported back in the coordinator's conversation - bots_queue_updates records each model turn of a job, pushed live and shown in the working-on panel - Live relay: every process pushes new messages, job steps and bot changes from the database to its own sockets, so work done elsewhere shows live - Bot messages render as markdown (react-markdown + remark-gfm, no raw HTML) with twui components; rail previews are plain text - Fix header button overflow in the model and computer selects - Bump @moduletrace/twui to 0.1.2 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import { useContext, useState } from "react";
|
||||
import Dropdown from "@moduletrace/twui/elements/Dropdown";
|
||||
import Paper from "@moduletrace/twui/elements/Paper";
|
||||
import Button from "@moduletrace/twui/layout/Button";
|
||||
import Span from "@moduletrace/twui/layout/Span";
|
||||
import Stack from "@moduletrace/twui/layout/Stack";
|
||||
import { ChevronDown, Monitor } from "lucide-react";
|
||||
import type { BotWithLastMessage } from "@/src/types";
|
||||
import { BotComputersContext } from "../(data)/bot-computers-context";
|
||||
import BotComputerOption from "./bot-computer-option";
|
||||
|
||||
type Props = {
|
||||
bot: BotWithLastMessage;
|
||||
};
|
||||
|
||||
/**
|
||||
* The header's computer button, beside the model button: shows the
|
||||
* computer the bot is on and, with more than one, switches it.
|
||||
*/
|
||||
export default function BotComputerSelect({ bot }: Props) {
|
||||
const { computers, switching_id, error, switchComputer } = useContext(BotComputersContext);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const current = computers[0];
|
||||
|
||||
if (!current?.id) return null;
|
||||
|
||||
const name = bot.name || "This bot";
|
||||
const can_switch = computers.length > 1;
|
||||
|
||||
const target = (
|
||||
<Button
|
||||
title={
|
||||
can_switch
|
||||
? `${name} is on ${current.name}. Switch computers`
|
||||
: `${name} is on ${current.name}, its only computer`
|
||||
}
|
||||
type="button"
|
||||
variant="outlined"
|
||||
color="gray"
|
||||
size="small"
|
||||
disabled={!can_switch}
|
||||
loading={Boolean(switching_id)}
|
||||
buttonContentProps={{ className: "min-w-0" }}
|
||||
className="max-w-full min-w-0 gap-2! flex-nowrap text-[13px] text-ink-secondary hover:text-ink"
|
||||
>
|
||||
<Monitor size={16} aria-hidden className="shrink-0 -mt-0.5" />
|
||||
<Span className="truncate">{current.name || "Computer"}</Span>
|
||||
{can_switch ? <ChevronDown size={14} aria-hidden className="shrink-0" /> : null}
|
||||
</Button>
|
||||
);
|
||||
|
||||
if (!can_switch) {
|
||||
return <Stack className="max-w-[200px] min-w-0 shrink">{target}</Stack>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
target={target}
|
||||
position="bottom-right"
|
||||
externalOpen={open}
|
||||
externalSetOpen={setOpen}
|
||||
targetWrapperProps={{ className: "max-w-full min-w-0" }}
|
||||
className="max-w-[200px] min-w-0 shrink items-stretch"
|
||||
>
|
||||
<Paper
|
||||
role="menu"
|
||||
aria-label={`${name}'s computers`}
|
||||
className="w-[260px] p-0! py-1 gap-0! items-stretch rounded-default bg-surface dark:bg-surface border-hairline dark:border-hairline shadow-popover! dark:shadow-popover!"
|
||||
>
|
||||
{computers.map((computer, index) => (
|
||||
<BotComputerOption
|
||||
key={computer.id}
|
||||
computer={computer}
|
||||
current={index == 0}
|
||||
onPick={async () => {
|
||||
setOpen(false);
|
||||
await switchComputer({ computer_id: computer.id || 0 });
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{error ? (
|
||||
<Span role="alert" className="px-4 py-2 text-[13px] leading-[18px] text-ink-secondary">
|
||||
{error}
|
||||
</Span>
|
||||
) : null}
|
||||
</Paper>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user