Files
cobble/src/components/general/bot-stone.tsx
T
tbenandClaude Opus 5.5 3286b80029 Build Cobble project structure from the handoff
- Remove the preset document suite (letters, emails, invoices, quotes,
  PDF generation, line items) with its routes, API handlers and tables
- Rebrand to Cobble: site data, Lato, handoff design tokens (light and
  dark) in main.css, brand assets in public/brand, ember stone logo and
  regenerated favicons
- Add bots, bot_messages, bot_tasks and bot_task_steps tables, regenerate
  DB types (BUN_SQLITE_COBBLE_*) and add a sample crew seed (seed:bots)
- Add the chat shell at /admin/bots: rail with pinned coordinator and
  user popover, bottom-anchored conversation with composer, working-on
  panel, and a send-message API scoped to the session user
- Add BotStone and Wordmark components, crew status and relative time
  utils with tests, and update AGENTS.md

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 17:10:36 +01:00

64 lines
1.9 KiB
TypeScript

import { twMerge } from "tailwind-merge";
import type { BotFace, BotHue } from "@/src/dict/bot-dict";
import { BotHueFillClasses } from "@/src/dict/bot-hue-classes-dict";
import {
CoordinatorBodyPath,
StoneBodyPaths,
StoneViewBox,
} from "@/src/data/stone-geometry";
import BotStoneFace from "./bot-stone-face";
type Props = {
/**
* The bot's identity hue. Omit (or `null`) for the coordinator, which
* renders hue-less: ink body, paper eyes, inverted in dark mode.
*/
hue?: BotHue | null;
face?: BotFace;
size?: number;
/**
* Accessible name. Leave empty when a visible bot name sits beside the
* stone (the default), so the stone is decorative.
*/
label?: string;
className?: string;
};
/**
* # Bot Stone
* @description A Cobble bot avatar: a stone with exactly one flat cut and two
* eyes. Never put it in a circle, ring or badge, or add an outline or shadow.
*/
export default function BotStone({
hue,
face = "idle",
size = 32,
label,
className,
}: Props) {
const is_coordinator = !hue;
const body_path = hue ? StoneBodyPaths[hue] : CoordinatorBodyPath;
const body_class = hue ? BotHueFillClasses[hue] : "fill-ink";
const eye_fill = is_coordinator ? "fill-paper" : "fill-dark";
const eye_stroke = is_coordinator ? "stroke-paper" : "stroke-dark";
return (
<svg
width={size}
height={size}
viewBox={StoneViewBox}
role={label ? "img" : undefined}
aria-label={label || undefined}
aria-hidden={label ? undefined : true}
className={twMerge("shrink-0", className)}
>
<path d={body_path} className={body_class} />
<BotStoneFace
face={face}
fill_class={eye_fill}
stroke_class={eye_stroke}
/>
</svg>
);
}