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]>
This commit is contained in:
2026-10-18 17:10:36 +01:00
co-authored by Claude Opus 5.5
parent 81e4a8aecc
commit 3286b80029
340 changed files with 3161 additions and 12060 deletions
+120
View File
@@ -0,0 +1,120 @@
/**
* Bot identity hues. A bot keeps its hue for life; a hue never means state.
* The coordinator has no hue.
*/
export const BotHues = [
{
title: "Ember",
value: "ember",
},
{
title: "Amber",
value: "amber",
},
{
title: "Moss",
value: "moss",
},
{
title: "Teal",
value: "teal",
},
{
title: "Cobalt",
value: "cobalt",
},
{
title: "Violet",
value: "violet",
},
{
title: "Rose",
value: "rose",
},
] as const;
export type BotHue = (typeof BotHues)[number]["value"];
export const BotKinds = [
{
title: "Worker",
value: "worker",
},
{
title: "Coordinator",
value: "coordinator",
},
] as const;
export type BotKind = (typeof BotKinds)[number]["value"];
/**
* A bot's face is its status. There are no badges or spinners.
*/
export const BotFaces = [
{
title: "Idle",
value: "idle",
},
{
title: "Working",
value: "working",
},
{
title: "Done",
value: "done",
},
{
title: "Asleep",
value: "asleep",
},
{
title: "Needs you",
value: "needs-you",
},
] as const;
export type BotFace = (typeof BotFaces)[number]["value"];
export const BotMessageAuthors = [
{
title: "Bot",
value: "bot",
},
{
title: "User",
value: "user",
},
] as const;
export type BotMessageAuthor = (typeof BotMessageAuthors)[number]["value"];
export const BotTaskStatuses = [
{
title: "Active",
value: "active",
},
{
title: "Done",
value: "done",
},
] as const;
export type BotTaskStatus = (typeof BotTaskStatuses)[number]["value"];
export const BotTaskStepStates = [
{
title: "Done",
value: "done",
},
{
title: "Active",
value: "active",
},
{
title: "Pending",
value: "pending",
},
] as const;
export type BotTaskStepState = (typeof BotTaskStepStates)[number]["value"];
+24
View File
@@ -0,0 +1,24 @@
import type { BotHue } from "./bot-dict";
/**
* Static Tailwind class names per hue, so the classes survive the build.
*/
export const BotHueFillClasses: Record<BotHue, string> = {
ember: "fill-hue-ember",
amber: "fill-hue-amber",
moss: "fill-hue-moss",
teal: "fill-hue-teal",
cobalt: "fill-hue-cobalt",
violet: "fill-hue-violet",
rose: "fill-hue-rose",
};
export const BotHueBgClasses: Record<BotHue, string> = {
ember: "bg-hue-ember",
amber: "bg-hue-amber",
moss: "bg-hue-moss",
teal: "bg-hue-teal",
cobalt: "bg-hue-cobalt",
violet: "bg-hue-violet",
rose: "bg-hue-rose",
};
-79
View File
@@ -1,79 +0,0 @@
export const EmailStatuses = [
{
title: "Draft",
value: "draft",
},
{
title: "Sent",
value: "sent",
},
] as const;
export type EmailStatus = (typeof EmailStatuses)[number]["value"];
export const EmailAttachmentTypes = [
{
title: "Letter",
value: "letter",
},
{
title: "Invoice",
value: "invoice",
},
{
title: "Quote",
value: "quote",
},
{
title: "File",
value: "file",
},
] as const;
export type EmailAttachmentType = (typeof EmailAttachmentTypes)[number]["value"];
export const InvoiceStatuses = [
{
title: "Draft",
value: "draft",
},
{
title: "Issued",
value: "issued",
},
{
title: "Paid",
value: "paid",
},
{
title: "Cancelled",
value: "cancelled",
},
] as const;
export type InvoiceStatus = (typeof InvoiceStatuses)[number]["value"];
export const QuoteStatuses = [
{
title: "Draft",
value: "draft",
},
{
title: "Sent",
value: "sent",
},
{
title: "Accepted",
value: "accepted",
},
{
title: "Declined",
value: "declined",
},
{
title: "Expired",
value: "expired",
},
] as const;
export type QuoteStatus = (typeof QuoteStatuses)[number]["value"];