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
+15 -29
View File
@@ -1,17 +1,23 @@
import Row from "@moduletrace/twui/layout/Row";
import Span from "@moduletrace/twui/layout/Span";
import type { ComponentProps, PropsWithChildren } from "react";
import type Span from "@moduletrace/twui/layout/Span";
import type { ComponentProps } from "react";
import { twMerge } from "tailwind-merge";
import Stack from "@moduletrace/twui/layout/Stack";
import LogoIcon from "./logo-icon";
import { SiteData } from "@/src/data/site-data";
import Wordmark from "./wordmark";
type Props = {
icon_size?: number;
text_wrapper_props?: ComponentProps<typeof Stack>;
/**
* Only `className` applies: it sets the wordmark colour.
*/
text_props?: ComponentProps<typeof Span>;
};
/**
* Horizontal lockup: the ember stone beside the `cobble` wordmark.
*/
export default function Logo({
icon_size = 36,
text_props,
@@ -19,38 +25,18 @@ export default function Logo({
}: Props) {
return (
<a href="/" className="logo-link">
<Row className="gap-3! flex-nowrap items-center">
<Row className="gap-2.5! flex-nowrap items-center">
<LogoIcon icon_size={icon_size} />
<Stack
{...text_wrapper_props}
className={twMerge("gap-1!", text_wrapper_props?.className)}
className={twMerge("gap-0!", text_wrapper_props?.className)}
>
<Stack className="gap-0!">
<HeroText text_props={text_props}>
{SiteData["SiteName"]}
</HeroText>
</Stack>
<Wordmark
height={Math.round(icon_size * 0.5)}
className={text_props?.className}
/>
</Stack>
</Row>
</a>
);
}
function HeroText({
text_props,
children,
}: PropsWithChildren & {
text_props?: ComponentProps<typeof Span>;
}) {
return (
<Span
{...text_props}
className={twMerge(
"text-[17px] font-semibold tracking-tight text-dark dark:text-white heading leading-[24px] whitespace-nowrap",
text_props?.className,
)}
>
{children}
</Span>
);
}