golem-ui 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/abi/error-card.tsx","../src/abi/define-component.tsx","../src/abi/fields.ts","../src/adapters/records.ts","../src/adapters/files.ts","../src/adapters/brain.ts","../src/adapters/fake/emitter.ts","../src/adapters/fake/records.ts","../src/adapters/fake/files.ts","../src/adapters/fake/identity.ts","../src/adapters/fake/chat.ts","../src/adapters/fake/clock.ts","../src/adapters/fake/navigation.ts","../src/adapters/fake/brain.ts","../src/adapters/brain.docs.ts","../src/adapters/chat.docs.ts","../src/adapters/clock.docs.ts","../src/adapters/files.docs.ts","../src/adapters/identity.docs.ts","../src/adapters/navigation.docs.ts","../src/adapters/records.docs.ts","../src/components/Brain/Brain.docs.ts","../src/components/Auth/Auth.docs.ts","../src/components/Chat/Chat.docs.ts","../src/components/Editor/Editor.docs.ts","../src/components/RecordForm/RecordForm.docs.ts","../src/components/RecordList/RecordList.docs.ts","../src/components/Report/Report.docs.ts","../src/components/Shell/Shell.docs.ts","../src/components/Timeline/Timeline.docs.ts","../src/components/Upload/Upload.docs.ts","../src/docs.ts","../src/components/Auth/Auth.config.ts","../src/components/Auth/Auth.tsx","../src/lib/markdown.tsx","../src/lib/use-container-width.ts","../src/components/Brain/Brain.config.ts","../src/components/Brain/Brain.tsx","../src/components/Chat/Chat.config.ts","../src/components/Chat/Chat.tsx","../src/lib/diff3.ts","../src/lib/locale.ts","../src/components/Editor/Editor.config.ts","../src/components/Editor/source.tsx","../src/components/Editor/Editor.tsx","../src/lib/format.ts","../src/components/RecordForm/RecordForm.config.ts","../src/components/RecordForm/values.ts","../src/components/RecordForm/RecordForm.tsx","../src/components/RecordList/RecordList.config.ts","../src/components/RecordList/RecordList.tsx","../src/components/Report/Report.config.ts","../src/components/Report/Report.tsx","../src/components/Shell/Shell.config.ts","../src/components/Shell/Shell.tsx","../src/components/Timeline/Timeline.config.ts","../src/components/Timeline/Timeline.tsx","../src/components/Upload/Upload.config.ts","../src/components/Upload/Upload.tsx"],"sourcesContent":["import type { ZodError } from 'zod'\n\nexport interface ConfigIssue {\n /** Dotted path of the offending field, or `(root)` for the object itself. */\n field: string\n /** The rule that rejected it, in Zod's own vocabulary. */\n rule: string\n message: string\n}\n\nexport function issuesFromZodError(error: ZodError): ConfigIssue[] {\n return error.issues.map((issue) => ({\n field: issue.path.length > 0 ? issue.path.join('.') : '(root)',\n rule: issue.code,\n message: issue.message,\n }))\n}\n\n/**\n * Rendered in place of the component whenever its config fails validation — in dev and in prod\n * alike. A silent fallback would hide the mistake from the agent that made it.\n */\nexport function ConfigErrorCard({\n component,\n issues,\n}: {\n component: string\n issues: ConfigIssue[]\n}) {\n return (\n <div\n role=\"alert\"\n data-golem-error=\"config\"\n className=\"golem-error-card m-2 rounded-lg border-2 border-red-500 bg-red-50 p-4 font-mono text-sm text-red-900\"\n >\n <p className=\"mb-2 font-bold\">\n golem-ui: {component} received an invalid <code>config</code>\n </p>\n <ul className=\"space-y-1\">\n {issues.map((issue, i) => (\n <li key={i}>\n <span className=\"font-bold underline\">{issue.field}</span>\n {' — '}\n {issue.message} <span className=\"opacity-70\">[{issue.rule}]</span>\n </li>\n ))}\n </ul>\n <p className=\"mt-2 opacity-70\">\n Fix the config object. The schema is on the {component} docs page.\n </p>\n </div>\n )\n}\n","import { useMemo, type FunctionComponent } from 'react'\nimport type { z, ZodType } from 'zod'\nimport { ConfigErrorCard, issuesFromZodError } from './error-card'\n\n/**\n * The outer shape of every golem-ui component, no exceptions.\n *\n * `Slots` carries the ReactNode holes a component exposes, plus the per-use props that belong to\n * the call site rather than to the config — `Auth.Guard`'s `roles` is one. Anything the agent would\n * write once for the whole app belongs in `config`, and anything that reaches the world in\n * `adapters`.\n */\nexport type GolemProps<ConfigInput, Adapters, Slots = object> = {\n config: ConfigInput\n adapters: Adapters\n} & Slots\n\nexport interface ComponentSpec<Schema extends ZodType, Adapters, Slots = object> {\n /** Shown in the error card, so it must be the name the agent writes in JSX. */\n name: string\n schema: Schema\n render: FunctionComponent<GolemProps<z.output<Schema>, Adapters, Slots>>\n}\n\n/**\n * Wraps a renderer so the component validates its config before it renders anything, and shows a\n * loud error card naming the field and the rule when validation fails.\n */\nexport function defineComponent<Schema extends ZodType, Adapters, Slots = object>(\n spec: ComponentSpec<Schema, Adapters, Slots>,\n): FunctionComponent<GolemProps<z.input<Schema>, Adapters, Slots>> & { schema: Schema } {\n const Render = spec.render\n\n const Component = (props: GolemProps<z.input<Schema>, Adapters, Slots>) => {\n const { config, ...rest } = props\n const parsed = useMemo(() => spec.schema.safeParse(config), [config])\n\n if (!parsed.success) {\n return <ConfigErrorCard component={spec.name} issues={issuesFromZodError(parsed.error)} />\n }\n\n return (\n <Render\n {...(rest as unknown as GolemProps<z.output<Schema>, Adapters, Slots>)}\n config={parsed.data}\n />\n )\n }\n\n Component.displayName = spec.name\n Component.schema = spec.schema\n return Component\n}\n","import { z } from 'zod'\n\n/**\n * The vocabulary an agent picks a field's `type` from, shared by every component configured with a\n * list of fields. The type decides how a value is rendered, what `format` means, and — on a form —\n * which control the reader gets. It never changes what the adapter returns or what is written back.\n */\nexport const FIELD_TYPES = [\n 'text',\n 'number',\n 'date',\n 'datetime',\n 'enum',\n 'boolean',\n 'money',\n 'user',\n] as const\n\nexport type FieldType = (typeof FIELD_TYPES)[number]\n\n/** What every field carries, whatever component is reading it. */\nconst common = {\n key: z\n .string()\n .min(1)\n .describe('The property to read off each record. Must match what the adapter returns.'),\n label: z\n .string()\n .min(1)\n .describe('What names the field on screen: a column heading, or the label above a control.'),\n type: z\n .enum(FIELD_TYPES, {\n error: (issue) =>\n `Unknown field type ${JSON.stringify(issue.input)}. Pick one of: ${FIELD_TYPES.join(', ')}.`,\n })\n .describe(\n 'How the value is rendered and edited. `text` verbatim; `number` grouped; `date` and `datetime` as UTC calendar dates; `enum` as a coloured chip or a select; `boolean` as Yes/No or a checkbox; `money` as a currency amount; `user` as a name with initials.',\n ),\n format: z\n .string()\n .optional()\n .describe(\n 'A hint read only by some types: an ISO currency code for `money` (default USD), an `Intl` dateStyle — short, medium, long, full — for `date` and `datetime`, and a digit count for `number`. Ignored by every other type.',\n ),\n}\n\n/** A field as a list shows it: read-only, one column wide. */\nexport const listFieldSchema = z\n .object({\n ...common,\n primary: z\n .boolean()\n .default(false)\n .describe(\n 'Marks the field that titles a row: the first column of the table and the heading of a card. At most one field may set it; with none, the first field is the title.',\n ),\n })\n .strict()\n\nexport const fieldOptionSchema = z\n .object({\n id: z.string().min(1).describe('The value written to the record when this choice is picked.'),\n label: z.string().min(1).describe('What the reader sees in the control. Never written back.'),\n })\n .strict()\n\n/** A field as a form edits it: everything above, plus what makes a control out of it. */\nexport const formFieldSchema = z\n .object({\n ...common,\n required: z\n .boolean()\n .default(false)\n .describe('Blocks submit while the value is empty, and marks the label with an asterisk.'),\n readOnly: z\n .boolean()\n .default(false)\n .describe(\n 'Shows the value formatted, with no control and no place in the patch. Use it for what the server owns — an id, a created date, a computed total.',\n ),\n help: z\n .string()\n .optional()\n .describe('One line under the control, always visible. Say what a good value looks like.'),\n placeholder: z\n .string()\n .optional()\n .describe('Ghost text inside an empty control. A sample value, never a second label.'),\n options: z\n .array(fieldOptionSchema)\n .min(1)\n .optional()\n .describe(\n 'The choices for an `enum` field, as `{ id, label }`. The reader picks the label and the record gets the id. Required on `enum`, rejected on every other type.',\n ),\n min: z\n .union([z.number(), z.string()])\n .optional()\n .describe(\n 'The lowest accepted value: a number for `number` and `money`, an ISO date or datetime string for `date` and `datetime`. Ignored by every other type.',\n ),\n max: z\n .union([z.number(), z.string()])\n .optional()\n .describe('The highest accepted value, in the same shape as `min`.'),\n multiline: z\n .boolean()\n .default(false)\n .describe(\n 'Draws a `text` field as a textarea, spanning both columns of a two-column layout. Ignored by every other type.',\n ),\n users: z\n .enum(['members', 'free'])\n .default('members')\n .describe(\n 'Where a `user` field gets its choices: `members` is a select over `Identity.listMembers()`, `free` is a text box for a name typed by hand. Ignored by every other type.',\n ),\n })\n .strict()\n\nexport type ListField = z.output<typeof listFieldSchema>\nexport type FormField = z.output<typeof formFieldSchema>\nexport type FieldOption = z.output<typeof fieldOptionSchema>\n\n/** Whatever a records adapter hands back. `id` is the row key when there is one. */\nexport type RecordRow = Record<string, unknown>\n\n/** The part of a field that formatting reads. Both field shapes satisfy it. */\nexport interface RenderedField {\n type: FieldType\n format?: string\n}\n","import type { Unsubscribe } from './common'\n\n/**\n * One value a filter matches, or a list of them meaning \"any of these\". Arrays are how a scoped\n * list says `status is waiting or in progress` without a query language.\n */\nexport type FilterValue = string | number | boolean | null | Array<string | number | boolean | null>\n\nexport interface RecordQuery {\n /** Field-by-field equality. A row matches when every entry matches. */\n filter?: Record<string, FilterValue>\n sort?: { field: string; direction: 'asc' | 'desc' }\n /** Substring match, case-insensitive, against the listed fields only. */\n search?: { text: string; fields: string[] }\n /** `nextCursor` from the previous page, or absent for the first one. */\n cursor?: string | null\n limit?: number\n}\n\nexport interface RecordPage<T> {\n rows: T[]\n /** Pass back as `cursor` for the next page. `null` means this was the last one. */\n nextCursor: string | null\n}\n\n/** One field the write was refused over, and the sentence the reader sees under that control. */\nexport interface FieldRefusal {\n /** The field `key`, as the component's config spells it. */\n field: string\n /** Written for a person: \"A quote over $500 needs the owner's sign-off.\" */\n message: string\n}\n\n/**\n * A write the adapter refused for reasons only it knows — a uniqueness clash, a business rule, a\n * server-side validator. Naming the fields is what lets a form put the sentence under the right\n * control instead of at the top of the page.\n */\nexport class RecordRefusedError extends Error {\n readonly fields: FieldRefusal[]\n\n constructor(message: string, fields: FieldRefusal[] = []) {\n super(message)\n this.name = 'RecordRefusedError'\n this.fields = fields\n }\n}\n\n/**\n * Reads the refusal out of anything thrown, by shape rather than by class: an adapter that crosses\n * a bundle boundary, or one that never imported this module, still gets its fields honoured.\n * Returns `null` for a rejection that names no field, which a form shows as one line instead.\n */\nexport function refusedFields(error: unknown): FieldRefusal[] | null {\n const fields = (error as { fields?: unknown } | null)?.fields\n if (!Array.isArray(fields) || fields.length === 0) return null\n const named = fields.filter(\n (one): one is FieldRefusal =>\n typeof (one as FieldRefusal)?.field === 'string' &&\n typeof (one as FieldRefusal)?.message === 'string',\n )\n return named.length > 0 ? named : null\n}\n\n/**\n * What a writer knew about the row when it started writing. A store that is handed this refuses the\n * write rather than letting the last writer win, which is what makes two writers on one document —\n * a person and an agent — safe.\n */\nexport interface UpdateOptions {\n /** The value the writer read out of `versionField` before it began editing. */\n expectedVersion: number\n /** The field the version lives on. */\n versionField?: string\n}\n\n/** The default `versionField`, so a caller with a plainly named field passes only the number. */\nexport const VERSION_FIELD = 'version'\n\n/**\n * An `update` the store refused because the row moved on since the writer read it. It carries the\n * row as it stands now, so the caller can merge against it without a second round trip.\n */\nexport class VersionConflictError extends Error {\n readonly current: Record<string, unknown> | null\n\n constructor(message: string, current: Record<string, unknown> | null = null) {\n super(message)\n this.name = 'VersionConflictError'\n this.current = current\n }\n}\n\n/**\n * Reads the current row out of anything thrown, by shape rather than by class — the same reason\n * `refusedFields` does. Returns `null` for a rejection that is not a version conflict, which the\n * caller shows as an error instead of merging.\n */\nexport function conflictingRecord(error: unknown): Record<string, unknown> | null {\n const thrown = error as { name?: unknown; current?: unknown } | null\n if (thrown?.name !== 'VersionConflictError') return null\n return typeof thrown.current === 'object' && thrown.current !== null\n ? (thrown.current as Record<string, unknown>)\n : null\n}\n\n/**\n * Records is a collection of rows and the four writes that change one.\n *\n * Every method that can be refused rejects with an `Error` whose `message` is shown to the reader\n * as it is written. Throw a `RecordRefusedError` when the refusal belongs to particular fields, and\n * a `VersionConflictError` when an `update` carrying `expectedVersion` arrived too late.\n */\nexport interface RecordsAdapter {\n list<T>(collection: string, query?: RecordQuery): Promise<RecordPage<T>>\n /** One row by id, or `null` when the collection has no such row. */\n get<T>(collection: string, id: string): Promise<T | null>\n /** Returns the stored row, including whatever id the store minted for it. */\n create<T>(collection: string, data: Record<string, unknown>): Promise<T>\n /**\n * Merges the patch into the stored row and returns the whole row back. With `options`, the write\n * is refused with a `VersionConflictError` when the row has moved on since the writer read it.\n */\n update<T>(\n collection: string,\n id: string,\n patch: Record<string, unknown>,\n options?: UpdateOptions,\n ): Promise<T>\n remove(collection: string, id: string): Promise<void>\n /** Called with no argument whenever the collection changed; the caller re-lists. */\n subscribe(collection: string, listener: () => void): Unsubscribe\n}\n","import type { Unsubscribe } from './common'\n\n/**\n * A stored file, as everything downstream refers to it. Plain JSON: an agent can put one in a\n * record, a chat message or a config and it survives the round trip. It carries no bytes — the\n * bytes are behind `url`.\n */\nexport interface FileRef {\n id: string\n /** The name it was uploaded under, extension included. */\n name: string\n /** The MIME type it is stored as, e.g. `image/jpeg`. A gallery reads it to know it has a photo. */\n contentType: string\n /** Bytes. */\n size: number\n /** The folder it lives in: the same string `upload` and `list` are given. */\n folder: string\n /** The instant the store accepted it, as a full ISO datetime. */\n uploadedAt: string\n /** One line the reader wrote under it. Absent until something captions it. */\n caption?: string\n}\n\nexport interface UploadOptions {\n /** Where the file lands, and where `list` and `subscribe` will find it again. */\n folder: string\n /**\n * Called as the bytes go up, with the fraction done between 0 and 1. A store that cannot report\n * progress calls it once with 1 rather than never — the caller draws a bar either way.\n */\n onProgress?: (fraction: number) => void\n}\n\n/**\n * A stored file, or just its id. A caller that kept only the id — an attachment on a record, say —\n * can still open and delete the file without having the whole ref to hand.\n */\nexport type FileHandle = FileRef | string\n\nexport const fileId = (ref: FileHandle): string => (typeof ref === 'string' ? ref : ref.id)\n\n/**\n * Files is a folder of blobs and the four things you do to one: put it there, open it, caption it,\n * take it away. Every method that can be refused rejects with an `Error` whose `message` is shown\n * to the reader as it is written.\n */\nexport interface FilesAdapter {\n upload(file: File, options: UploadOptions): Promise<FileRef>\n /**\n * A URL to open. `thumbnail` asks for a small one; a store that keeps no thumbnail returns the\n * full URL, so a caller never has to ask whether it has one.\n */\n url(ref: FileHandle, options?: { thumbnail?: boolean }): Promise<string>\n remove(ref: FileHandle): Promise<void>\n /** Everything in the folder, newest first. */\n list(folder: string): Promise<FileRef[]>\n /** Writes the caption on a stored file and returns the whole ref back. */\n caption(ref: FileHandle, caption: string): Promise<FileRef>\n /** Called with no argument whenever the folder changed; the caller re-lists. */\n subscribe(folder: string, listener: () => void): Unsubscribe\n}\n","import type { Unsubscribe } from './common'\n\n/**\n * A brain is an Open Knowledge Format bundle (v0.2): a directory tree of markdown concept documents\n * with YAML front matter, an optional `index.md` in any directory, and an optional `log.md` at the\n * root. Paths are relative to the bundle root, `/`-separated, no leading slash.\n *\n * A **source location** is `path#L<start>-L<end>`, 1-based and inclusive: what the agent cites,\n * and what `Brain`'s `openLocation` opens.\n */\nexport interface BrainEntry {\n path: string\n kind: 'file' | 'dir'\n}\n\nexport interface BrainHit {\n path: string\n /** 1-based line the match is on. */\n line: number\n excerpt: string\n}\n\nexport interface BrainAdapter {\n /** That directory's `index.md`, synthesized from the front matter of its concepts when absent. */\n index(dir?: string): Promise<string>\n /** The direct children of a directory, files and directories alike. Root when `dir` is omitted. */\n list(dir?: string): Promise<BrainEntry[]>\n read(path: string): Promise<string>\n search(query: string): Promise<BrainHit[]>\n /** Fires on any change to any file in the bundle. */\n subscribe(listener: () => void): Unsubscribe\n /**\n * Called with the location the reader just opened on its own — a tree tap, a link, a search hit,\n * back. Optional: an app puts it in the URL so a reload lands on the same document.\n */\n open?(location: string): void\n /** Replaces a file's text. Optional: a brain the app keeps read-only omits it. */\n write?(path: string, text: string): Promise<void>\n}\n\n/** `path#L3-L5` → `{ path, start: 3, end: 5 }`. A location with no range opens the file at the top. */\nexport function parseLocation(location: string): { path: string; start?: number; end?: number } {\n const match = /^([^#]+)(?:#L(\\d+)(?:-L(\\d+))?)?$/.exec(location)\n if (!match) return { path: location }\n const start = match[2] ? Number(match[2]) : undefined\n return { path: match[1]!, start, end: match[3] ? Number(match[3]) : start }\n}\n","import type { Unsubscribe } from '../common'\n\n/** Shared listener bookkeeping for the fakes; not part of the public adapter surface. */\nexport function createEmitter<T>() {\n const listeners = new Set<(value: T) => void>()\n return {\n subscribe(listener: (value: T) => void): Unsubscribe {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n emit(value: T) {\n for (const listener of listeners) listener(value)\n },\n }\n}\n","import {\n RecordRefusedError,\n VersionConflictError,\n VERSION_FIELD,\n type FilterValue,\n type RecordQuery,\n type RecordsAdapter,\n type UpdateOptions,\n} from '../records'\nimport { createEmitter } from './emitter'\n\nexport type FakeRow = Record<string, unknown> & { id: string }\n\n/**\n * The fake's store, on top of the adapter interface. `insert` is not part of `RecordsAdapter`: it\n * is the synchronous way a story or a test puts a row in without going through `create`, so a\n * seeded collection can grow while a refusal is switched on.\n */\nexport interface FakeRecords extends RecordsAdapter {\n insert(collection: string, row: Record<string, unknown>): FakeRow\n}\n\nexport interface FakeRecordsOptions {\n /**\n * Makes every `create` and `update` reject, naming this field. It is how a story shows what an\n * adapter's refusal looks like landing under one control.\n */\n refuse?: { field: string; message: string }\n}\n\nfunction matches(value: unknown, wanted: FilterValue): boolean {\n return Array.isArray(wanted) ? wanted.some((one) => one === value) : wanted === value\n}\n\n/** Numbers compare as numbers, everything else as its string form. Dates are ISO, so that holds. */\nfunction compare(a: unknown, b: unknown): number {\n if (typeof a === 'number' && typeof b === 'number') return a - b\n const left = String(a ?? '')\n const right = String(b ?? '')\n return left < right ? -1 : left > right ? 1 : 0\n}\n\nfunction hit(row: FakeRow, text: string, fields: string[]): boolean {\n const needle = text.trim().toLowerCase()\n if (needle === '') return true\n return fields.some((field) =>\n String(row[field] ?? '')\n .toLowerCase()\n .includes(needle),\n )\n}\n\n/**\n * An in-memory `Records` adapter, seeded per collection. Filtering, sorting, searching and paging\n * all happen over the seeded rows, so a story runs the same code path a server would.\n *\n * The cursor is an offset into the matching rows. That is what makes `nextCursor` honest and it is\n * also its limit: a row inserted while the reader is on page three shifts the pages under them,\n * which is why a live update re-lists from the start rather than paging on.\n */\nexport function fakeRecords(\n seed: Record<string, Record<string, unknown>[]> = {},\n options: FakeRecordsOptions = {},\n): FakeRecords {\n const store = new Map<string, FakeRow[]>(\n Object.entries(seed).map(([name, rows]) => [name, rows.map((row) => ({ ...row }) as FakeRow)]),\n )\n const emitters = new Map<string, ReturnType<typeof createEmitter<void>>>()\n let nextId = 1\n\n const rows = (collection: string) => {\n if (!store.has(collection)) store.set(collection, [])\n return store.get(collection)!\n }\n const emitterFor = (collection: string) => {\n if (!emitters.has(collection)) emitters.set(collection, createEmitter<void>())\n return emitters.get(collection)!\n }\n const indexOf = (collection: string, id: string) =>\n rows(collection).findIndex((row) => row.id === id)\n\n const refuseIfAsked = () => {\n const { refuse } = options\n if (refuse) {\n throw new RecordRefusedError(`The store refused this ${refuse.field}.`, [\n { field: refuse.field, message: refuse.message },\n ])\n }\n }\n\n const insert = (collection: string, row: Record<string, unknown>) => {\n const next = { id: `fake-${nextId++}`, ...row } as FakeRow\n rows(collection).unshift(next)\n emitterFor(collection).emit()\n return next\n }\n\n return {\n async list<T>(collection: string, query: RecordQuery = {}) {\n let result = [...rows(collection)]\n\n if (query.filter) {\n const entries = Object.entries(query.filter)\n result = result.filter((row) => entries.every(([key, want]) => matches(row[key], want)))\n }\n if (query.search) {\n result = result.filter((row) => hit(row, query.search!.text, query.search!.fields))\n }\n if (query.sort) {\n const { field, direction } = query.sort\n const sign = direction === 'asc' ? 1 : -1\n result.sort((a, b) => sign * compare(a[field], b[field]))\n }\n\n const total = result.length\n const offset = Number(query.cursor ?? 0) || 0\n const limit = query.limit ?? total\n const end = offset + limit\n\n return {\n rows: result.slice(offset, end) as T[],\n nextCursor: end < total ? String(end) : null,\n }\n },\n\n async get<T>(collection: string, id: string) {\n const found = rows(collection).find((row) => row.id === id)\n return (found ? ({ ...found } as T) : null) as T | null\n },\n\n async create<T>(collection: string, data: Record<string, unknown>) {\n refuseIfAsked()\n return { ...insert(collection, data) } as T\n },\n\n async update<T>(\n collection: string,\n id: string,\n patch: Record<string, unknown>,\n options?: UpdateOptions,\n ) {\n refuseIfAsked()\n const at = indexOf(collection, id)\n if (at === -1) throw new Error(`There is no ${collection} record with the id ${id}.`)\n\n const current = rows(collection)[at]!\n if (options) {\n const field = options.versionField ?? VERSION_FIELD\n if (current[field] !== options.expectedVersion) {\n throw new VersionConflictError(\n `This ${collection} record is at ${field} ${String(current[field])}, not ${options.expectedVersion}. Somebody else wrote to it first.`,\n { ...current },\n )\n }\n }\n\n const next = { ...current, ...patch, id }\n rows(collection)[at] = next\n emitterFor(collection).emit()\n return { ...next } as T\n },\n\n async remove(collection: string, id: string) {\n const at = indexOf(collection, id)\n if (at === -1) throw new Error(`There is no ${collection} record with the id ${id}.`)\n rows(collection).splice(at, 1)\n emitterFor(collection).emit()\n },\n\n insert,\n\n subscribe(collection: string, listener: () => void) {\n return emitterFor(collection).subscribe(listener)\n },\n }\n}\n","import {\n fileId,\n type FileHandle,\n type FileRef,\n type FilesAdapter,\n type UploadOptions,\n} from '../files'\nimport { createEmitter } from './emitter'\n\n/**\n * A file already in the store when the fake is built. `content` is what `url` hands back — a data\n * URL for a seeded image — because the fake has no bytes of its own for a file nobody uploaded.\n */\nexport interface FakeFileSeed {\n id?: string\n folder: string\n name: string\n contentType: string\n /** Bytes, for the tile. Defaults to the length of `content`. */\n size?: number\n content?: string\n caption?: string\n /** A full ISO datetime. Defaults to the fake's `now`. */\n uploadedAt?: string\n}\n\nexport interface FakeFilesOptions {\n seed?: FakeFileSeed[]\n /**\n * `upload` rejects any file with exactly this name, so the error path has somewhere to be seen.\n * Everything else in the same batch still goes up.\n */\n failOn?: string\n /** How long a fake upload takes end to end, in milliseconds. */\n uploadMs?: number\n /** What stamps `uploadedAt`, so a test is not reading the wall clock. */\n now?: () => Date\n}\n\n/** How many progress events a fake upload reports on its way up. */\nconst STEPS = 10\n\n/**\n * A small stand-in picture, drawn rather than photographed: the fakes ship no image files, and a\n * gallery with nothing in it teaches nothing. It is an SVG data URL, so it costs a few hundred\n * bytes and needs no canvas, no network and no build step.\n */\nexport function placeholderImage(label: string, hue = 205): string {\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"480\" height=\"360\" viewBox=\"0 0 480 360\">\n<rect width=\"480\" height=\"360\" fill=\"hsl(${hue} 40% 88%)\"/>\n<circle cx=\"150\" cy=\"140\" r=\"66\" fill=\"hsl(${hue} 45% 74%)\"/>\n<circle cx=\"330\" cy=\"140\" r=\"66\" fill=\"hsl(${hue} 45% 74%)\"/>\n<path d=\"M150 140 L215 78 L330 140\" stroke=\"hsl(${hue} 50% 46%)\" stroke-width=\"10\" fill=\"none\"/>\n<text x=\"240\" y=\"290\" font-family=\"system-ui,sans-serif\" font-size=\"26\" text-anchor=\"middle\" fill=\"hsl(${hue} 45% 32%)\">${label}</text>\n</svg>`\n return `data:image/svg+xml,${encodeURIComponent(svg)}`\n}\n\n/**\n * An object URL for the blob, or a stand-in string when the platform has no `createObjectURL` —\n * jsdom does not, and a test that only checks the gallery listed a file should not fail for it.\n */\nfunction objectUrlFor(file: File, id: string): string {\n try {\n return URL.createObjectURL(file)\n } catch {\n return `fake-file:${id}`\n }\n}\n\n/**\n * Loads the image, or gives up. Anything that does not load inside the grace period — a platform\n * that fetches no resources, a blob the decoder refuses — resolves `null`, and the caller keeps\n * the full-size URL rather than waiting on a thumbnail that is never coming.\n */\nfunction loadImage(url: string, graceMs: number): Promise<HTMLImageElement | null> {\n return new Promise((resolve) => {\n const image = new Image()\n const timer = setTimeout(() => resolve(null), graceMs)\n const settle = (value: HTMLImageElement | null) => {\n clearTimeout(timer)\n resolve(value)\n }\n image.onload = () => settle(image)\n image.onerror = () => settle(null)\n image.src = url\n })\n}\n\n/** The longest edge of a generated thumbnail, in pixels. */\nconst THUMB_EDGE = 160\n\n/**\n * A thumbnail drawn from the uploaded image through a canvas, or `null` when this platform cannot\n * draw one. A real store would make it server-side; the fake makes it in the page so the grid is\n * loading small pictures rather than full ones.\n */\nasync function thumbnailOf(url: string, contentType: string): Promise<string | null> {\n if (!contentType.startsWith('image/')) return null\n const image = await loadImage(url, 250)\n if (!image) return null\n try {\n const scale = Math.min(1, THUMB_EDGE / Math.max(image.width, image.height))\n const canvas = document.createElement('canvas')\n canvas.width = Math.max(1, Math.round(image.width * scale))\n canvas.height = Math.max(1, Math.round(image.height * scale))\n const context = canvas.getContext('2d')\n if (!context) return null\n context.drawImage(image, 0, 0, canvas.width, canvas.height)\n return canvas.toDataURL('image/png')\n } catch {\n return null\n }\n}\n\ninterface Stored {\n ref: FileRef\n full: string\n thumbnail: string | null\n}\n\n/**\n * Files in memory: blobs behind object URLs, an upload that takes about 600 ms and says how far it\n * has got, a canvas thumbnail for anything that is a picture, and one name it always refuses so the\n * failure has a story of its own.\n */\nexport function fakeFiles(options: FakeFilesOptions = {}): FilesAdapter {\n const { failOn, uploadMs = 600, now = () => new Date() } = options\n const store = new Map<string, Stored>()\n const folders = new Map<string, ReturnType<typeof createEmitter<void>>>()\n let nextId = 1\n\n const emitterFor = (folder: string) => {\n let emitter = folders.get(folder)\n if (!emitter) {\n emitter = createEmitter<void>()\n folders.set(folder, emitter)\n }\n return emitter\n }\n\n for (const seed of options.seed ?? []) {\n const id = seed.id ?? `fake-file-${nextId++}`\n const content = seed.content ?? `fake-file:${id}`\n store.set(id, {\n ref: {\n id,\n name: seed.name,\n contentType: seed.contentType,\n size: seed.size ?? content.length,\n folder: seed.folder,\n uploadedAt: seed.uploadedAt ?? now().toISOString(),\n ...(seed.caption === undefined ? {} : { caption: seed.caption }),\n },\n full: content,\n // A seeded picture is already small, so it is its own thumbnail.\n thumbnail: seed.contentType.startsWith('image/') ? content : null,\n })\n }\n\n const found = (ref: FileHandle): Stored => {\n const stored = store.get(fileId(ref))\n if (!stored) throw new Error(`No file with id ${fileId(ref)} is in the store.`)\n return stored\n }\n\n return {\n async upload(file: File, { folder, onProgress }: UploadOptions) {\n const id = `fake-file-${nextId++}`\n const full = objectUrlFor(file, id)\n\n // The bar moves before the answer arrives, which is the whole point of reporting progress.\n for (let step = 1; step <= STEPS; step++) {\n await new Promise((resolve) => setTimeout(resolve, uploadMs / STEPS))\n onProgress?.(step / STEPS)\n }\n\n if (failOn !== undefined && file.name === failOn) {\n throw new Error(`The store rejected “${file.name}”. Nothing was saved.`)\n }\n\n const ref: FileRef = {\n id,\n name: file.name,\n contentType: file.type,\n size: file.size,\n folder,\n uploadedAt: now().toISOString(),\n }\n store.set(id, { ref, full, thumbnail: await thumbnailOf(full, file.type) })\n emitterFor(folder).emit()\n return ref\n },\n\n async url(ref: FileHandle, urlOptions?: { thumbnail?: boolean }) {\n const stored = found(ref)\n return urlOptions?.thumbnail ? (stored.thumbnail ?? stored.full) : stored.full\n },\n\n async remove(ref: FileHandle) {\n const stored = found(ref)\n store.delete(stored.ref.id)\n emitterFor(stored.ref.folder).emit()\n },\n\n async list(folder: string) {\n return [...store.values()]\n .map((stored) => stored.ref)\n .filter((ref) => ref.folder === folder)\n .sort((a, b) => b.uploadedAt.localeCompare(a.uploadedAt) || b.id.localeCompare(a.id))\n },\n\n async caption(ref: FileHandle, caption: string) {\n const stored = found(ref)\n stored.ref = { ...stored.ref, caption }\n emitterFor(stored.ref.folder).emit()\n return stored.ref\n },\n\n subscribe(folder: string, listener: () => void) {\n return emitterFor(folder).subscribe(listener)\n },\n }\n}\n","import type { IdentityAdapter, SignUpInput, User } from '../identity'\nimport { createEmitter } from './emitter'\n\nexport const fakeUser: User = {\n id: 'u-1',\n name: 'Robin Vale',\n email: 'robin@example.com',\n roles: ['member'],\n}\n\nexport interface FakeIdentityOptions {\n /** Who is signed in on mount. Omit and it is the first member; `null` starts the app signed out. */\n user?: User | null\n /** The member table the list, invite and remove all work on. Defaults to `[fakeUser]`. */\n members?: User[]\n /** The one password every member accepts, so a demo can say it out loud. */\n password?: string\n /** The one code `requestCode` always \"sends\". */\n code?: string\n /** Role given to someone who signs up without an invite. */\n defaultRole?: string\n /** What `invite()` prefixes the token with. The token is the last thing in the URL. */\n inviteBase?: string\n /** Invites that already exist, token to role, so an example can open on a link nobody minted. */\n invites?: Record<string, string>\n}\n\n/**\n * The in-memory Identity adapter: a member table, a signed-in user, and invite tokens that really\n * round-trip — `invite()` hands back a URL whose token `signUp()` accepts exactly once.\n */\nexport function fakeIdentity(options: FakeIdentityOptions = {}): IdentityAdapter {\n const password = options.password ?? 'golem'\n const code = options.code ?? '123456'\n const defaultRole = options.defaultRole ?? 'member'\n const inviteBase = options.inviteBase ?? 'https://app.example/#/join?invite='\n\n const members = (options.members ?? [fakeUser]).map((member) => ({ ...member }))\n let user = options.user === undefined ? (members[0] ?? null) : options.user\n const invites = new Map<string, string>(Object.entries(options.invites ?? {}))\n const emitter = createEmitter<User | null>()\n let nextId = 1\n\n const find = (email: string) =>\n members.find((member) => member.email?.toLowerCase() === email.trim().toLowerCase())\n\n const enter = (member: User) => {\n user = { ...member }\n emitter.emit(user)\n return user\n }\n\n return {\n async currentUser() {\n return user && { ...user }\n },\n subscribe: emitter.subscribe,\n\n async signIn(email, given) {\n const member = find(email)\n if (!member) throw new Error('We do not know that email address.')\n if (given !== password) throw new Error('That password is not right.')\n return enter(member)\n },\n\n async signUp(input: SignUpInput) {\n let role = defaultRole\n if (input.invite !== undefined) {\n const invited = invites.get(input.invite)\n if (!invited) throw new Error('That invite link has already been used, or never existed.')\n invites.delete(input.invite)\n role = invited\n }\n if (find(input.email)) throw new Error('There is already an account with that email address.')\n const member: User = {\n id: `u-fake-${nextId++}`,\n name: input.name.trim(),\n email: input.email.trim(),\n roles: [role],\n }\n members.push(member)\n return enter(member)\n },\n\n async requestCode(email) {\n if (!find(email)) throw new Error('We do not know that email address.')\n },\n\n async verifyCode(email, given) {\n const member = find(email)\n if (!member) throw new Error('We do not know that email address.')\n if (given.trim() !== code) throw new Error('That code is not right.')\n return enter(member)\n },\n\n async signOut() {\n user = null\n emitter.emit(user)\n },\n\n async listMembers() {\n return members.map((member) => ({ ...member }))\n },\n\n async invite(role) {\n const token = `inv-${nextId++}`\n invites.set(token, role)\n return `${inviteBase}${token}`\n },\n\n async removeMember(userId) {\n const index = members.findIndex((member) => member.id === userId)\n if (index !== -1) members.splice(index, 1)\n if (user?.id === userId) {\n user = null\n emitter.emit(user)\n }\n },\n\n async setRole(userId, role) {\n const member = members.find((entry) => entry.id === userId)\n if (!member) throw new Error(`fakeIdentity: no member ${userId}`)\n member.roles = [role]\n if (user?.id === userId) enter(member)\n },\n }\n}\n","import type { ChatAdapter, ChatAttachment, ChatCommand, ChatMessage } from '../chat'\nimport { createEmitter } from './emitter'\n\nexport interface FakeChatOptions {\n /** Answered in order, one per send, looping when they run out. Empty means the agent stays mute. */\n replies?: string[]\n /** Milliseconds between tokens. Stories want it slow enough to watch, tests want it near zero. */\n tokenDelayMs?: number\n /** Slash commands the composer completes. `/reset` clears the conversation; any other replies with the line it ran. */\n commands?: ChatCommand[]\n}\n\nconst DEFAULT_REPLIES = [\n 'Noted. Writing that down now.',\n 'Done — take a look at the canvas.',\n 'Understood. Anything else you want changed?',\n]\n\n/**\n * The in-memory Chat adapter. `send` echoes the message, then streams the next canned reply word by\n * word under one message id, so a component's streaming path is exercised without a backend.\n */\nexport function fakeChat(initial: ChatMessage[] = [], options: FakeChatOptions = {}): ChatAdapter {\n const replies = options.replies ?? DEFAULT_REPLIES\n const tokenDelayMs = options.tokenDelayMs ?? 60\n const messages = [...initial]\n const emitter = createEmitter<ChatMessage[]>()\n let nextId = initial.length + 1\n let sent = 0\n\n const emit = () => emitter.emit(messages.map((message) => ({ ...message })))\n\n const stream = (reply: string) => {\n const id = `fake-msg-${nextId++}`\n const at = new Date().toISOString()\n const words = reply.split(' ')\n // The thinking bubble: the message exists with no text yet, so the reader sees the agent take\n // the turn before the first word lands.\n messages.push({ id, role: 'agent', text: '', at, streaming: true })\n emit()\n\n let written = 0\n const tick = () => {\n written += 1\n const index = messages.findIndex((message) => message.id === id)\n if (index < 0) return\n messages[index] = {\n id,\n role: 'agent',\n at,\n text: words.slice(0, written).join(' '),\n streaming: written < words.length,\n }\n emit()\n if (written < words.length) setTimeout(tick, tokenDelayMs)\n }\n setTimeout(tick, tokenDelayMs)\n }\n\n return {\n async history() {\n return messages.map((message) => ({ ...message }))\n },\n async send(text: string, attachments?: ChatAttachment[]) {\n messages.push({\n id: `fake-msg-${nextId++}`,\n role: 'user',\n text,\n at: new Date().toISOString(),\n ...(attachments?.length ? { attachments } : {}),\n })\n emit()\n if (replies.length) stream(replies[sent++ % replies.length]!)\n },\n subscribe: emitter.subscribe,\n ...(options.commands\n ? {\n async commands() {\n return options.commands!\n },\n async runCommand(line: string) {\n const [name = '', ...rest] = line.trim().split(/\\s+/)\n const command = options.commands!.find((c) => c.name === name)\n if (!command) throw new Error(`Unknown command: ${name}`)\n const arg = rest.join(' ')\n if (command.args?.length && !command.args.some((a) => a.value === arg))\n throw new Error(\n `${name} needs one of: ${command.args.map((a) => a.value).join(', ')}`,\n )\n if (name === '/reset') {\n messages.length = 0\n emit()\n return 'Conversation cleared.'\n }\n return `Ran ${line.trim()}.`\n },\n }\n : {}),\n }\n}\n","import type { ClockAdapter } from '../clock'\n\n/** Frozen by default so time-based views render the same in every story and every test run. */\nexport function fakeClock(\n fixed: Date = new Date('2026-01-01T09:00:00Z'),\n timeZone = 'UTC',\n): ClockAdapter {\n return {\n now: () => new Date(fixed),\n timeZone: () => timeZone,\n }\n}\n","import type { NavigationAdapter, Route } from '../navigation'\nimport { createEmitter } from './emitter'\n\nexport function fakeNavigation(initial: Route = { path: '/', params: {} }): NavigationAdapter {\n let route = initial\n const emitter = createEmitter<Route>()\n\n return {\n current: () => route,\n go(path: string) {\n route = { path, params: {} }\n emitter.emit(route)\n },\n subscribe: emitter.subscribe,\n }\n}\n","import type { BrainAdapter, BrainEntry, BrainHit } from '../brain'\nimport { createEmitter } from './emitter'\n\n/** The `key: value` lines of a document's YAML front matter; enough for `type` and `description`. */\nfunction frontMatter(text: string): Record<string, string> {\n const match = /^---\\n([\\s\\S]*?)\\n---/.exec(text)\n const fields: Record<string, string> = {}\n for (const line of match?.[1]?.split('\\n') ?? []) {\n const pair = /^(\\w+):\\s*(.*)$/.exec(line)\n if (pair) fields[pair[1]!] = pair[2]!.replace(/^[\"']|[\"']$/g, '')\n }\n return fields\n}\n\nconst RESERVED = new Set(['index.md', 'log.md'])\nconst dirOf = (dir?: string) => (dir ? dir.replace(/\\/$/, '') + '/' : '')\n\n/**\n * An in-memory bundle: `files` maps a path to its markdown. Real enough for the reader and the\n * search; `write` replaces a file and fires `subscribe`.\n */\nexport function fakeBrain(files: Record<string, string>): BrainAdapter {\n const store = { ...files }\n const emitter = createEmitter<void>()\n\n const list = async (dir?: string): Promise<BrainEntry[]> => {\n const prefix = dirOf(dir)\n const seen = new Map<string, BrainEntry>()\n for (const path of Object.keys(store)) {\n if (!path.startsWith(prefix)) continue\n const rest = path.slice(prefix.length)\n const slash = rest.indexOf('/')\n const entry: BrainEntry =\n slash < 0 ? { path, kind: 'file' } : { path: prefix + rest.slice(0, slash), kind: 'dir' }\n seen.set(entry.path, entry)\n }\n return [...seen.values()].sort((a, b) =>\n a.kind === b.kind ? a.path.localeCompare(b.path) : a.kind === 'dir' ? -1 : 1,\n )\n }\n\n return {\n list,\n async index(dir) {\n const own = store[`${dirOf(dir)}index.md`]\n if (own !== undefined) return own\n // The spec's fallback: one bullet per concept, from its front matter.\n const entries = await list(dir)\n const lines = entries\n .filter((entry) => !(entry.kind === 'file' && RESERVED.has(entry.path.split('/').pop()!)))\n .map((entry) => {\n const name = entry.path.slice(dirOf(dir).length)\n const description =\n entry.kind === 'file' ? frontMatter(store[entry.path]!).description : ''\n return `* [${name}](${name}${entry.kind === 'dir' ? '/' : ''})${description ? ` - ${description}` : ''}`\n })\n return `# ${dir ?? 'Index'}\\n\\n${lines.join('\\n')}\\n`\n },\n async read(path) {\n const text = store[path]\n if (text === undefined) throw new Error(`No file at ${path}.`)\n return text\n },\n async search(query) {\n const needle = query.trim().toLowerCase()\n if (!needle) return []\n const hits: BrainHit[] = []\n for (const [path, text] of Object.entries(store)) {\n text.split('\\n').forEach((excerpt, index) => {\n if (excerpt.toLowerCase().includes(needle)) hits.push({ path, line: index + 1, excerpt })\n })\n }\n return hits\n },\n subscribe: emitter.subscribe,\n async write(path, text) {\n store[path] = text\n emitter.emit()\n },\n }\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const brainAdapterDocs: AdapterDocs = {\n name: 'Brain',\n slug: 'brain',\n storybookPath: 'adapters-brain',\n tagline:\n 'A folder of markdown the agent reads and cites: list it, read it, search it, hear when it changes.',\n\n purpose: `\\`Brain\\` is the app's unstructured knowledge as a folder of markdown, in the\n[Open Knowledge Format](https://github.com/GoogleCloudPlatform/open-knowledge-format/blob/main/SPEC.md)\n(v0.2): concept documents with YAML front matter (\\`type\\` required, \\`description\\` used in listings),\nan optional \\`index.md\\` in any directory, an optional \\`log.md\\` at the root. \\`index.md\\` and \\`log.md\\`\nare reserved names, never concepts. The root \\`index.md\\` is where an agent starts reading.\n\nThe adapter is the folder, reduced to what the reader needs: a listing, a file, a search, and a\nlistener. It knows nothing about markdown beyond front matter. Implement it over a directory on\ndisk, a git tree, or an object store.\n\n**A source location is \\`path#L<start>-L<end>\\`**, 1-based and inclusive, relative to the bundle root.\nIt is what the agent puts in a \\`ChatMessage\\`'s \\`sources\\` and what \\`Brain\\`'s \\`openLocation\\` opens.\n\\`parseLocation\\` splits one.`,\n\n methods: [\n {\n signature: 'index(dir?: string): Promise<string>',\n guarantees: `The markdown of that directory's \\`index.md\\`, the root's when \\`dir\\` is omitted. When the directory has\nnone, a synthesized one: a heading and one \\`* [name](path) - description\\` bullet per entry, the\ndescription read from each concept's front matter, as the spec allows.`,\n },\n {\n signature: \"list(dir?: string): Promise<Array<{ path: string; kind: 'file' | 'dir' }>>\",\n guarantees:\n 'The direct children of a directory, directories first, each `path` relative to the bundle root. The root when `dir` is omitted. An empty directory is an empty array.',\n },\n {\n signature: 'read(path: string): Promise<string>',\n guarantees:\n 'The whole file, front matter included, so line numbers in a location count from line 1 of the file.',\n throws: 'An `Error` whose `message` is shown in the reader when there is no file at `path`.',\n },\n {\n signature:\n 'search(query: string): Promise<Array<{ path: string; line: number; excerpt: string }>>',\n guarantees:\n 'Every line containing the query, case-insensitive, with its 1-based `line`. An empty query is an empty result.',\n },\n {\n signature: 'subscribe(listener: () => void): Unsubscribe',\n guarantees:\n 'Calls the listener after any file in the bundle changes, with no payload: the component re-reads what it has open. Returns the function that detaches it.',\n },\n {\n signature: 'open?(location: string): void',\n guarantees:\n 'Optional. `Brain` calls it with `path#L<start>-L<end>` (or a bare path) every time the reader opens a document on its own: a tree tap, a link, a search hit, back. Never for a location the app passed in through `openLocation`. Write it into the URL so a reload lands on the same document.',\n },\n {\n signature: 'write?(path: string, text: string): Promise<void>',\n guarantees:\n 'Optional. Replaces the file at `path` (creating it) and fires `subscribe`. Omit it for a brain the app keeps read-only; `Brain` calls it in no slice yet.',\n },\n ],\n\n notes: `**Paths are \\`/\\`-separated, relative to the bundle root, no leading slash.** \\`notes/tyres.md\\`, not\n\\`/notes/tyres.md\\`. A directory path carries no trailing slash.\n\n**The reader does not validate the bundle.** A file without front matter is read and shown as it is;\n\\`index\\` only needs front matter when it synthesizes.`,\n\n fake: {\n name: 'fakeBrain',\n what: `A map of path to markdown, kept in memory. \\`list\\`, \\`index\\` and \\`search\\` are derived from it on every\ncall; \\`write\\` replaces an entry and fires \\`subscribe\\`. Nothing is persisted.`,\n example: `import { Brain, fakeBrain } from 'golem-ui'\n\nconst brain = fakeBrain({\n 'index.md': '---\\\\nokf_version: 0.2\\\\n---\\\\n# Shop knowledge\\\\n\\\\n* [tyres.md](tyres.md) - what we stock',\n 'tyres.md': '---\\\\ntype: concept\\\\ndescription: what we stock\\\\n---\\\\n# Tyres\\\\n\\\\nWe stock 700c and 27.5.',\n})\n\n;<Brain config={{ openLocation: 'tyres.md#L6-L6' }} adapters={{ brain }} />`,\n },\n\n consumers: ['Brain'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const chatAdapterDocs: AdapterDocs = {\n name: 'Chat',\n slug: 'chat',\n storybookPath: 'adapters-chat',\n tagline:\n 'The conversation with the agent: the history, one way to send, and a stream that arrives token by token.',\n\n purpose: `\\`Chat\\` is the conversation itself — the transport between the reader and whatever agent is answering.\nIt is three methods, and the smallest of them carries the most: \\`subscribe\\` is called with **the\nwhole conversation** on every change, including each token of a reply being written.\n\nThat \"whole conversation every time\" is the design. The component holds no message state of its own,\nso there is no reconciliation to get wrong and nothing to drift. Implement it over SSE, a WebSocket,\na polling loop, or a canned script.`,\n\n methods: [\n {\n signature: 'history(): Promise<ChatMessage[]>',\n guarantees:\n 'The conversation so far, oldest first. Called once on mount; everything after that arrives through `subscribe`. An empty conversation is an empty array, not an error.',\n },\n {\n signature: 'send(text: string, attachments?: ChatAttachment[]): Promise<void>',\n guarantees: `Resolves nothing. **The sent message reaches the component through \\`subscribe\\`, not through this\npromise** — insert it immediately with a stable \\`id\\` and \\`delivery: 'pending'\\`, then emit the complete\nconversation. Reconcile that same id to an omitted \\`delivery\\` when acknowledged, or \\`'failed'\\` when it cannot be sent. \\`attachments\\` are refs the composer is already holding: a name and a size, never\nbytes. Getting the bytes somewhere is the \\`Files\\` adapter's job, done before \\`send\\` is called.`,\n throws:\n 'An `Error` whose `message` is shown in the composer as written. A rejected send leaves its failed message in the conversation.',\n },\n {\n signature: 'retry?(messageId: string): Promise<void>',\n guarantees:\n 'Optional. Retries the failed message with this stable id and emits the complete conversation as its delivery changes. Omit it when retries are not available.',\n },\n {\n signature: 'interrupt?(): Promise<void>',\n guarantees:\n 'Optional. Stops the agent mid-turn and emits the conversation as it settles: the streaming message drops its flag, or is removed. Chat shows a Stop pill on the thinking row only when this exists; the pill is disabled until the promise settles.',\n throws: 'An `Error` whose `message` is shown in the error strip as written.',\n },\n {\n signature: 'commands(): Promise<ChatCommand[]>',\n guarantees:\n 'Optional. The slash commands this adapter honours, each `{ name, description, args? }` with the name carrying its leading slash. Chat calls it each time a composer line starting with `/` opens the picker. `args` lists the values a command accepts as its single argument; omit it and the picker closes on the space after the name.',\n },\n {\n signature: 'runCommand(line: string): Promise<string>',\n guarantees:\n 'Optional, paired with `commands`. Runs one `/...` line exactly as typed and resolves the reply text. Everything after the name is one argument, never tokenized. Chat renders the line and the reply as system rows.',\n throws:\n 'An `Error` for an unknown name or a missing argument, before anything runs; its `message` is shown in the error strip as written.',\n },\n {\n signature: 'openSource?(location: string): void',\n guarantees:\n \"Optional. Called with a source location (`path#L3-L5`) when the reader clicks a source chip under an agent bubble. The app hands the location to `Brain`'s `openLocation`. Chips render whether or not this exists; without it a click does nothing.\",\n },\n {\n signature: 'subscribe(listener: (messages: ChatMessage[]) => void): Unsubscribe',\n guarantees: `Calls the listener with the **entire** conversation on every change: a message sent, a reply starting,\nand every token of that reply. Returns the function that detaches it.`,\n },\n ],\n\n notes: `**Streaming is one message id emitted again and again.** The adapter re-emits the same \\`id\\` with a\nlonger \\`text\\` and \\`streaming: true\\`; **the last emission of that id drops the flag, and that is\n\\`done\\`.** There is no separate completion event to miss.\n\n- An empty \\`text\\` with \\`streaming: true\\` is the agent thinking before its first token — the component\n draws the thinking bubble off exactly that.\n- A stream that stops without ever dropping the flag leaves the bubble writing forever. If the\n transport can fail mid-reply, emit the id one last time without the flag.\n\n**Delivery belongs to the adapter.** Omit \\`delivery\\` for a confirmed message; \\`'pending'\\` is still\nsending and \\`'failed'\\` remains visible until a retry reconciles it. The component does not keep a\nsecond optimistic list, so every emission must be the complete conversation, including pending and\nfailed messages.\n\n**A \\`ChatMessage\\` is \\`id\\`, \\`role\\` (\\`'user'\\`, \\`'agent'\\` or \\`'system'\\`), \\`text\\`, and \\`at\\` as an ISO datetime.**\n\\`text\\` is markdown, and the component renders it as such. A \\`'system'\\` message is a slash command or its\nreply; emit one from history when the conversation keeps them, and Chat draws it as the same dim row.\n\n**Slash commands are two optional methods.** \\`commands\\` lists what the picker completes; \\`runCommand\\`\nruns a \\`/\\` line and answers in text. \"New conversation\" is a \\`/reset\\` command, not a button.\n\n**\\`sources\\` on an agent message are source locations**, \\`path#L<start>-L<end>\\` inside a brain, one chip\neach under the bubble. The chip shows the file name and the line range.\n\n**A \\`ChatAttachment\\` is a name and an optional size, no bytes and no URL.** \\`size\\` shows on the chip\nas \\`12 KB\\`; leave it off and the chip carries the name alone. Pair this adapter with \\`Files\\` when the\nattachment has to be openable later.`,\n\n fake: {\n name: 'fakeChat',\n what: `An in-memory conversation that really streams. \\`send\\` echoes a confirmed reader message, then writes the\nnext canned reply **word by word under one message id**, thinking bubble first — so a component's\nstreaming path is exercised with no backend. \\`replies\\` are answered in order and loop when they run\nout; an empty list leaves the agent mute. \\`tokenDelayMs\\` is the whole difference between a story\nworth watching and a test that finishes: stories want ~45 ms, tests want 0. \\`commands\\` turns on the\nslash picker: \\`/reset\\` clears the conversation and any other listed command replies with the line it ran.`,\n example: `import { Chat, fakeChat } from 'golem-ui'\n\nconst chat = fakeChat(\n [\n { id: 'm-1', role: 'user', text: 'What is on the bench today?', at: '2026-01-01T08:40:00Z' },\n { id: 'm-2', role: 'agent', text: 'Two tickets waiting.', at: '2026-01-01T08:40:06Z' },\n ],\n {\n replies: ['Noted — writing that down now.'],\n tokenDelayMs: 45,\n commands: [{ name: '/reset', description: 'Start a new conversation' }],\n },\n)\n\n;<Chat config={chatConfig} adapters={{ chat }} />`,\n },\n\n consumers: ['Chat'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const clockAdapterDocs: AdapterDocs = {\n name: 'Clock',\n slug: 'clock',\n storybookPath: 'adapters-clock',\n tagline:\n 'Now, and the zone to read it in — two methods, so nothing in the kit reads the wall clock.',\n\n purpose: `\\`Clock\\` is the smallest adapter in the kit and it exists for one reason: a component that reads the\nwall clock cannot be tested, screenshotted or demonstrated twice with the same result. Everything\nthat needs the time asks for it here.\n\nHand it \\`fakeClock()\\` and \"today\" is a fixed day, so a dated document renders the same in every story\nand every test run. Hand it a real clock in the app and nothing else changes.`,\n\n methods: [\n {\n signature: 'now(): Date',\n guarantees: `A fresh \\`Date\\`, synchronously. Callers may keep it, so return a new object rather than one they could\nmutate. It is called on render, which means it must be cheap and must never do I/O.`,\n },\n {\n signature: 'timeZone(): string',\n guarantees: `An IANA zone name — \\`Europe/Lisbon\\`, \\`UTC\\` — used to format the times a reader sees. It is the\nreader's zone, not the server's; a component formats every timestamp through it, so two people in two\nplaces read the same record differently and both read it correctly.`,\n },\n ],\n\n notes: `**Nothing here is asynchronous and nothing here is subscribable.** A component re-reads \\`now()\\` when\nit re-renders; a clock that has to tick drives that from outside.\n\n**Records carry their own timestamps.** \\`Clock\\` is for the *reading* time — \"Saved 13:20\", which day\n\"today\" is, which day a timeline entry falls in. What a row was created at came from the store.`,\n\n fake: {\n name: 'fakeClock',\n what: `Frozen. \\`fakeClock()\\` is \\`2026-01-01T09:00:00Z\\` in \\`UTC\\` and returns that same instant every time, so\na time-based view renders identically in every story and every test run. Pass a \\`Date\\` and a zone to\nmove it. There is no tick and no advance method: a test that needs time to pass builds a second\nclock.`,\n example: `import { Report, fakeClock } from 'golem-ui'\n\nconst clock = fakeClock(new Date('2026-01-14T08:00:00Z'), 'Europe/Lisbon')\n\n;<Report config={reportConfig} adapters={{ records, clock }} />`,\n },\n\n consumers: ['Report', 'Editor', 'Timeline'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const filesAdapterDocs: AdapterDocs = {\n name: 'Files',\n slug: 'files',\n storybookPath: 'adapters-files',\n tagline:\n 'A folder of blobs and the five things you do to one: put it there, open it, caption it, list it, take it away.',\n\n purpose: `\\`Files\\` is where bytes live. A component hands it a browser \\`File\\` and a folder name and gets back a\n\\`FileRef\\` — plain JSON with an id, a name, a MIME type, a size and a timestamp, and no bytes at all.\nWhen something wants to see the file it asks \\`url\\` for one.\n\nThat split is the point: a ref can go on a record, into a chat message or into a config and survive\nthe round trip, while the bytes stay behind whatever storage the app actually has. Implement it over\nS3, a CDN, a form POST, or a \\`Map\\` of object URLs.`,\n\n methods: [\n {\n signature: 'upload(file: File, options: UploadOptions): Promise<FileRef>',\n guarantees: `The file lands in \\`options.folder\\` — the same string \\`list\\` and \\`subscribe\\` are given — and the\nresolved ref is what everything downstream holds. \\`onProgress\\` is called as the bytes go up with the\nfraction done between 0 and 1; **a store that cannot report progress calls it once with \\`1\\` rather\nthan never**, so the caller's bar completes instead of hanging. \\`uploadedAt\\` is the store's own\nstamp, and it is what orders every gallery.`,\n throws:\n 'An `Error` whose `message` is shown on that file’s row as written — too large, wrong type, refused. One file failing leaves the rest of the batch alone.',\n },\n {\n signature: 'url(ref: FileHandle, options?: { thumbnail?: boolean }): Promise<string>',\n guarantees: `A URL the browser can open. \\`thumbnail: true\\` asks for a small one; **a store that keeps no thumbnail\nreturns the full URL** rather than rejecting, so a caller never has to ask whether it has one. Takes\na whole ref or just an id.`,\n },\n {\n signature: 'remove(ref: FileHandle): Promise<void>',\n guarantees:\n 'The file is gone when the promise resolves, and `subscribe` has fired for its folder.',\n throws:\n 'An `Error` whose `message` the reader is shown as written — a permission refusal reads as one.',\n },\n {\n signature: 'list(folder: string): Promise<FileRef[]>',\n guarantees:\n 'Everything in that folder, **newest first**. Ordering is the store’s job, not the gallery’s. An unknown folder is an empty list, not an error.',\n },\n {\n signature: 'caption(ref: FileHandle, caption: string): Promise<FileRef>',\n guarantees:\n 'Writes the line and resolves the whole ref back, so the caller replaces its copy rather than patching it.',\n throws: 'An `Error` whose `message` is shown under the tile as written.',\n },\n {\n signature: 'subscribe(folder: string, listener: () => void): Unsubscribe',\n guarantees:\n 'Calls the listener with no argument whenever that folder changed, including changes this caller made. The listener re-lists; it is a nudge, not a payload. Returns the function that detaches it.',\n },\n ],\n\n notes: `**A \\`FileRef\\` is plain JSON and carries no bytes**: \\`id\\`, \\`name\\`, \\`contentType\\`, \\`size\\`, \\`folder\\`,\n\\`uploadedAt\\`, and \\`caption\\` once something writes one. \\`contentType\\` is how a gallery knows it has a\nphoto rather than an invoice.\n\n**Every method that takes a ref takes an id too.** \\`FileHandle\\` is \\`FileRef | string\\`, and \\`fileId()\\`\nnarrows it — so a caller that kept only the id, an attachment on a record say, can still open and\ndelete the file.\n\n**Every rejection's \\`message\\` reaches a person unedited.** Write them as sentences.\n\n**The folder is the only scope there is.** There are no nested paths, no per-file permissions and no\nmove: a file is in one folder from upload to removal.`,\n\n fake: {\n name: 'fakeFiles',\n what: `An in-memory folder map over object URLs. Uploads report ten progress steps over about 600 ms so a\nbar is worth looking at, and thumbnails are really drawn — through a canvas, in the page — so the\ngrid loads small pictures rather than full ones. \\`failOn\\` rejects one file by name and lets the rest\nof the batch through, which is how a story shows a failed row beside successful ones. \\`now\\` decides\nwhat stamps \\`uploadedAt\\`, so a demo dated inside its own fiction stays there.\n\nSeeded files carry a \\`content\\` string instead of bytes, because the kit ships no image files:\n\\`placeholderImage(label)\\` draws one as a few hundred bytes of SVG data URL.`,\n example: `import { Upload, fakeFiles, placeholderImage } from 'golem-ui'\n\nconst files = fakeFiles({\n seed: [\n {\n id: 'f-1',\n folder: 'shop',\n name: 'rear-derailleur.jpg',\n contentType: 'image/jpeg',\n content: placeholderImage('rear derailleur'),\n caption: 'Hanger bent inwards.',\n },\n ],\n failOn: 'too-big.mov',\n})\n\n;<Upload config={galleryConfig} adapters={{ files }} />`,\n },\n\n consumers: ['Timeline', 'Upload'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const identityAdapterDocs: AdapterDocs = {\n name: 'Identity',\n slug: 'identity',\n storybookPath: 'adapters-identity',\n tagline: 'Who is here, how they got here, and who else may come in.',\n\n purpose: `\\`Identity\\` answers one question everywhere in the kit — who is signed in — and carries the whole\nmembership story behind it: signing in by password or by a one-time code, signing up, the member\ntable, invites and roles. A \\`User\\` is an id, a name, an optional email and **every** role the person\nholds; a guard lets them through when one of those roles is on its list.\n\nAuthentication itself is the app's, not the kit's. Implement this over a session cookie, a JWT,\nan OAuth SDK, or a table in memory.`,\n\n methods: [\n {\n signature: 'currentUser(): Promise<User | null>',\n guarantees:\n '`null` means signed out, and signed out is never an error. Safe to call on mount before anything has subscribed.',\n },\n {\n signature: 'subscribe(listener: (user: User | null) => void): Unsubscribe',\n guarantees: `Calls the listener with the new user on **every** sign-in, sign-up and sign-out, including ones this\ncaller triggered, and with \\`null\\` for signed out. Unlike the collection subscriptions, this one\ncarries its payload: the listener is handed the user rather than re-reading. Returns the function\nthat detaches it.`,\n },\n {\n signature: 'signIn(email: string, password: string): Promise<User>',\n guarantees: 'Resolves the signed-in user and fires `subscribe` before it does.',\n throws:\n 'An `Error` whose `message` the reader is shown as written — *\"That password is not right.\"* Write it for a person, and keep it vague enough not to say whether the address exists if that matters to you.',\n },\n {\n signature: 'signUp(input: SignUpInput): Promise<User>',\n guarantees: `Creates the account and signs it in. \\`input.invite\\` is the token the reader arrived on, and **it\ndecides the new role**; without one the store picks its own default. \\`password\\` is present in\n\\`password\\` mode and absent in \\`code\\` mode.`,\n throws:\n 'An `Error` for a spent or invented invite, and for an address that already has an account. Both messages reach the reader as written.',\n },\n {\n signature: 'requestCode(email: string): Promise<void>',\n guarantees: 'Sends a one-time code to the address. `code` mode only; resolving means sent.',\n throws: 'An `Error` when the address is unknown, if you choose to say so.',\n },\n {\n signature: 'verifyCode(email: string, code: string): Promise<User>',\n guarantees: 'Resolves the signed-in user, the same as `signIn` does, and fires `subscribe`.',\n throws: 'An `Error` whose `message` the reader is shown — *\"That code is not right.\"*',\n },\n {\n signature: 'signOut(): Promise<void>',\n guarantees:\n 'Fires `subscribe` with `null`. Signing out while already signed out is not an error.',\n },\n {\n signature: 'listMembers(): Promise<User[]>',\n guarantees:\n 'Everyone with an account, each with their whole `roles` list. This is what a Team screen draws.',\n },\n {\n signature: 'invite(role: string): Promise<string>',\n guarantees:\n 'Mints an invite for that role and resolves **the URL to hand out**, not the token. The token is the last thing in the URL, and `signUp` accepts it exactly once.',\n },\n {\n signature: 'removeMember(userId: string): Promise<void>',\n guarantees:\n 'The member is gone from `listMembers`. Removing the signed-in user signs them out, and `subscribe` fires with `null`.',\n throws: 'An `Error` whose `message` the reader is shown — a last-owner rule reads as one.',\n },\n {\n signature: 'setRole(userId: string, role: string): Promise<void>',\n guarantees:\n '**Replaces** the member’s roles with this one; it does not add to them. Changing the signed-in user’s role fires `subscribe` so a guard re-decides immediately.',\n throws: 'An `Error` whose `message` the reader is shown.',\n },\n ],\n\n notes: `**Every rejection's \\`message\\` is shown to the reader exactly as written**, which is why this\ninterface names them all: *\"That password is not right.\"*, not \\`AUTH_002\\`.\n\n**\\`roles\\` is a list, always.** A person can hold several; a guard passes them when any one matches.\n\\`setRole\\` replacing the list rather than appending is deliberate — role editing in the kit is a\nsingle-select control.\n\n**An invite is a URL, and the role rides on it.** The app decides where that URL points; \\`Auth\\` reads\nthe token out of \\`Route.params\\`, which is why the app's navigation adapter has to put a query string\nthere.\n\n**Nothing here is a permission check.** The adapter says who someone is and what roles they hold; a\ncomponent's config says which roles get in.`,\n\n fake: {\n name: 'fakeIdentity',\n what: `An in-memory member table with one password for everyone and one code for everyone, both stated in\nits options so a demo can say them out loud. Invites really round-trip: \\`invite()\\` hands back a URL\nwhose token \\`signUp()\\` accepts exactly once. \\`user: null\\` starts the app signed out, which is how a\nsign-in screen gets to be the first thing a reader sees.\n\nIt holds its invites in memory, which is a real limit: an invite link is meant to be opened in\nanother tab, and that is a cold page load. The showcase wraps the fake to keep open tokens in\n\\`localStorage\\`, and that wrapper is worth copying.`,\n example: `import { Auth, fakeIdentity } from 'golem-ui'\n\nconst identity = fakeIdentity({\n user: null,\n members: [\n { id: 'u-1', name: 'Nadia Okonkwo', email: 'nadia@example.com', roles: ['owner'] },\n { id: 'u-2', name: 'Omar Haddad', email: 'omar@example.com', roles: ['mechanic'] },\n ],\n password: 'golem',\n defaultRole: 'mechanic',\n})\n\n;<Auth.Guard config={authConfig} adapters={{ identity, navigation }}>\n <App />\n</Auth.Guard>`,\n },\n\n consumers: ['Shell', 'Auth', 'RecordForm', 'Timeline'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const navigationAdapterDocs: AdapterDocs = {\n name: 'Navigation',\n slug: 'navigation',\n storybookPath: 'adapters-navigation',\n tagline:\n 'Where the reader is, how to send them somewhere else, and a listener for when that changes.',\n\n purpose: `\\`Navigation\\` is the router, reduced to what a component actually needs: read the current route, go to a\npath, hear about it when it changes. A \\`Route\\` is a \\`path\\` and a flat \\`params\\` map of strings.\n\nNo component in this kit imports a router, and that is what makes the kit work under React Router,\nunder a hash, under Next.js, or under a \\`useState\\`. It is also the one adapter an app most often\nwrites itself rather than taking the fake — the showcase's \\`hashNavigation\\` is about thirty lines.`,\n\n methods: [\n {\n signature: 'current(): Route',\n guarantees: `The route right now, synchronously. Called during render, so it reads existing state rather than\nparsing anything expensive.`,\n },\n {\n signature: 'go(path: string): void',\n guarantees: `Navigates, and fires \\`subscribe\\`. Returns nothing and is not awaited — a component calls it and stops\ncaring. Whether it pushes onto history or replaces is the implementation's to decide.`,\n },\n {\n signature: 'subscribe(listener: (route: Route) => void): Unsubscribe',\n guarantees: `Calls the listener with the new route on every change, **including a change the reader made with the\nback button**, not only ones \\`go\\` caused. This one carries its payload rather than nudging. Returns\nthe function that detaches it.`,\n },\n ],\n\n notes: `**\\`params\\` is where a token arrives.** \\`Auth\\` reads an invite token out of \\`Route.params\\`, so an\nimplementation over a hash has to parse the hash's query string into it — that is the one detail\n\\`fakeNavigation\\` does not do and a real one must.\n\n**There is no \\`back\\`, no \\`replace\\` and no route matching.** The app owns its route table; a component\nonly ever says \"go here\".\n\n**Two components take it and neither routes.** \\`Shell\\` calls \\`go('/')\\` from the title, \\`Auth\\` sends a\nreader on after signing in. Everything else on a screen is the app's own wiring.`,\n\n fake: {\n name: 'fakeNavigation',\n what: `Keeps the route in a variable. It is right for a story, where the route is a prop and the point is\nthat a click changed it, and it is wrong for an app: the route is lost on reload and a screen has no\nURL anyone can link to. It also never fills \\`params\\`, so \\`Auth\\`'s invite path needs a real one.\n\nAn app writes its own. The showcase's \\`hashNavigation\\` reads \\`window.location.hash\\`, splits the query\nstring into \\`params\\`, and listens on \\`hashchange\\` — which survives a reload and survives being served\nunder a GitHub Pages subpath.`,\n example: `import { Shell, fakeNavigation } from 'golem-ui'\n\nconst navigation = fakeNavigation({ path: '/today', params: {} })\n\n;<Shell config={shellConfig} adapters={{ identity, navigation }} chat={chat} canvas={canvas} />`,\n },\n\n consumers: ['Shell', 'Auth'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const recordsAdapterDocs: AdapterDocs = {\n name: 'Records',\n slug: 'records',\n storybookPath: 'adapters-records',\n tagline:\n 'A collection of rows, the four writes that change one, and the version check that keeps two writers apart.',\n\n purpose: `\\`Records\\` is the store, as far as a component is concerned: named collections of JSON rows, each with an\n\\`id\\`, and the reads and writes that move them. Filtering, sorting, searching and paging are the\nstore's work and not the component's — a component hands over a \\`RecordQuery\\` and draws whatever\ncomes back.\n\nIt is the most widely taken adapter in the kit and the one with the most to get right, because it is\nwhere two writers meet. Implement it over anything: a REST API, a database client, a document store,\nan array in memory.`,\n\n methods: [\n {\n signature: 'list<T>(collection: string, query?: RecordQuery): Promise<RecordPage<T>>',\n guarantees: `Honours the whole query, not the parts that are convenient: \\`filter\\` is field-by-field equality and an\narray value means \"any of these\"; \\`search\\` is a case-insensitive substring over the named fields\nonly; \\`sort\\` orders before paging, so page two of a sorted list follows page one. Returns\n\\`nextCursor\\` for the next page, or \\`null\\` when this was the last one — a caller pages until it sees\nthat \\`null\\` and never guesses from a short page.`,\n },\n {\n signature: 'get<T>(collection: string, id: string): Promise<T | null>',\n guarantees:\n 'Resolves the row, or `null` when the collection has no such row. A missing row is not an error: the component draws an empty state, not a failure.',\n },\n {\n signature: 'create<T>(collection: string, data: Record<string, unknown>): Promise<T>',\n guarantees:\n 'Resolves the stored row, including whatever `id` the store minted. Callers hold the returned row rather than the data they sent, so server-side defaults arrive.',\n throws:\n 'A `RecordRefusedError` when a business rule or a uniqueness check refused it, carrying the fields it names. Anything else rejects with a plain `Error` whose `message` the reader is shown as written.',\n },\n {\n signature: 'update<T>(collection, id, patch, options?: UpdateOptions): Promise<T>',\n guarantees: `Merges the patch into the stored row — a key absent from the patch keeps its stored value — and resolves\nthe whole row back. With \\`options\\`, the write is conditional: compare \\`expectedVersion\\` against the\nrow's \\`versionField\\` (\\`version\\` by default) and apply the write only if they match. A store that\nignores \\`options\\` turns every concurrent write into a silent overwrite, which is the one failure\nthis interface exists to prevent.`,\n throws:\n 'A `VersionConflictError` carrying the row **as it now stands** when `expectedVersion` is stale, so the caller merges against it without a second round trip. A `RecordRefusedError` for a refused field. A plain `Error` for a row that is not there.',\n },\n {\n signature: 'remove(collection: string, id: string): Promise<void>',\n guarantees: 'The row is gone when the promise resolves, and `subscribe` has fired.',\n throws: 'A plain `Error` when there is no such row.',\n },\n {\n signature: 'subscribe(collection: string, listener: () => void): Unsubscribe',\n guarantees: `Calls the listener with no argument whenever anything in that collection changed, including changes\nthis same caller made. The listener re-lists; it is a nudge, not a payload. Returns the function that\ndetaches it, and calling that function twice is safe.`,\n },\n ],\n\n notes: `**Two error types, and both are read by shape.** \\`refusedFields(error)\\` and\n\\`conflictingRecord(error)\\` look at \\`fields\\` and \\`name\\` on whatever was thrown rather than at its\nclass, so an adapter that crossed a bundle boundary — or never imported this module — still gets its\nrefusal honoured. That is what lets an app throw its own error object and have \\`RecordForm\\` put the\nsentence under the right control.\n\n- \\`RecordRefusedError(message, fields)\\` — a write the store refused for reasons only it knows. Each\n entry in \\`fields\\` is a \\`field\\` key as the component's config spells it and a \\`message\\` written for\n a person: *\"A quote over $500 needs the owner's sign-off.\"* A refusal that names no field shows as\n one line at the top of the form instead.\n- \\`VersionConflictError(message, current)\\` — an \\`update\\` that arrived too late. \\`current\\` is the row\n as it now stands. \\`Editor\\` merges its draft against that record; \\`RecordForm\\` keeps the reader's\n unsaved values and asks whether to keep them or take the stored ones.\n\n**Every rejection's \\`message\\` reaches a person unedited.** Write them as sentences, not as codes.\n\n**Paging is a cursor, not an offset the caller computes.** \\`nextCursor\\` is opaque; hand it straight\nback as \\`cursor\\`. A cursor over a collection that is being written to may shift rows under the\nreader, which is why a live update re-lists from the start rather than paging on.`,\n\n fake: {\n name: 'fakeRecords',\n what: `An in-memory store seeded per collection. It really filters, searches, sorts and pages, and it really\nenforces \\`expectedVersion\\` — so a story and a test run the same code path a server would, including\nthe conflict path. \\`refuse\\` makes every write reject with a \\`RecordRefusedError\\` naming one field,\nwhich is how a story shows a refusal landing under a control. \\`insert\\` is the fake's own extra: the\nsynchronous way to put a row in without going through \\`create\\`.\n\nIts cursor is an offset into the matching rows, which is honest about paging and honest about its\nlimit — a row inserted while the reader is on page three shifts the pages under them.`,\n example: `import { RecordList, fakeRecords } from 'golem-ui'\n\nconst records = fakeRecords({\n jobs: [\n { id: 'j-1', ticket: 'NG-118', bike: 'Trek FX 2', status: 'waiting', version: 1 },\n { id: 'j-2', ticket: 'NG-119', bike: 'Brompton C Line', status: 'in progress', version: 3 },\n ],\n})\n\n;<RecordList config={jobsConfig} adapters={{ records }} />\n\n// The refusal path, for the story that shows it:\nconst refusing = fakeRecords({ jobs: [] }, {\n refuse: { field: 'ticket', message: 'That ticket number is already on the board.' },\n})`,\n },\n\n consumers: ['RecordList', 'RecordForm', 'Report', 'Editor', 'Timeline'],\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const brainDocs: ComponentDocs = {\n name: 'Brain',\n slug: 'brain',\n storybookPath: 'components-brain',\n tagline:\n 'A folder of markdown with a reader that opens the passage the agent cited, highlighted and in context.',\n\n purpose: `\\`Brain\\` shows the app's knowledge folder. At desktop width it is master–detail: a list column with\nthe tree and a search box, and a reading column with the open file at a reading measure, its front\nmatter as a chip. Below 768px it is one screen at a time: the list, then the document over it with a\nback button; a link inside a document pushes the next one, and back returns to where the reader was,\nscroll and all. \\`log.md\\` is one entry in the tree, read like any other file.\n\nIts one trick is \\`openLocation\\`: hand it \\`path#L6-L8\\` and the reader opens that file, highlights\nthose lines, and scrolls them into view with the rest of the document around them, so the reader can\ncheck what the agent said against where it came from.\n\nUse it with \\`Chat\\`: an agent message carries \\`sources\\`, Chat draws a chip per source, and the app\npasses the clicked location into this config. For editing the file, that is \\`Editor\\`; this slice\nreads only.`,\n\n configNotes: [\n `\\`openLocation\\` is what the app tells the reader to show. The reader keeps its own stack of what it\nopened since: a tap in the tree, a link, a search hit, back. Each of those is reported through the\nadapter's \\`open\\`, so an app that writes it into the URL comes back to the same document after a\nreload, and the location it passes back in is not pushed twice. Omit it and the desktop shows the\nroot \\`index.md\\`; the phone shows the list.`,\n ],\n\n adapters: [\n {\n adapter: 'Brain',\n calls: '`list(dir?)`',\n why: 'The tree, one call per directory as it is expanded.',\n },\n {\n adapter: 'Brain',\n calls: '`index()`',\n why: 'The root `index.md`, real or synthesized, shown before anything is opened.',\n },\n {\n adapter: 'Brain',\n calls: '`read(path)`',\n why: 'The open file, front matter and all, so line numbers match the location.',\n },\n {\n adapter: 'Brain',\n calls: '`search(query)`',\n why: 'The hits shown in the list column while the search box has text; a hit opens its line highlighted.',\n },\n {\n adapter: 'Brain',\n calls: '`subscribe(listener)`',\n why: 'Re-reads the open file and re-lists the tree when the bundle changes.',\n },\n {\n adapter: 'Brain',\n calls: '`open(location)`',\n why: 'Optional. Told every location the reader opened on its own, so the app can keep it in the URL.',\n },\n ],\n\n adapterNotes: `Brain never calls \\`write\\`; it is on the adapter for the edit-together step to come.`,\n\n slots: [\n {\n slot: 'toolbar',\n what: 'The right-hand end of the document header, beside the open path. An Edit button goes here; empty leaves the path alone.',\n },\n ],\n\n example: `import { Brain, fakeBrain } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Brain\n config={{\n title: 'Shop knowledge',\n openLocation: 'workshop/turnaround.md#L6-L8',\n }}\n adapters={{ brain: fakeBrain(files) }}\n/>`,\n\n failureModes: `- **Invalid config.** Brain renders an error card instead of the reader, naming every field that failed.\n A location whose range is not \\`#L<start>-L<end>\\` fails; an unknown field fails.\n- **The file is missing.** \\`read\\` rejects and the reader shows the adapter's message in place of the\n document; the tree stays usable.\n- **The range runs past the file.** The highlight covers what exists and stops at the last line.\n- **The range is inside the front matter.** The highlight starts at the first line after it; front\n matter is shown as a \\`type\\` chip and the description, never as YAML and never highlighted.\n- **A block spans the range's edge.** The file is rendered in three runs, so a list or table cut by\n the range renders as two. Cite whole blocks and it does not happen.\n- **Markdown is the kit's subset.** Headings, fenced code, pipe tables, lists, bold, italic, inline\n code and links. A relative link opens that file in the reader; \\`http(s)\\` opens a new tab; wiki\n links and images render as the characters they are.\n- **A new adapter object on every render** re-subscribes and re-lists the tree. Build it once.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const authDocs: ComponentDocs = {\n name: 'Auth',\n slug: 'auth',\n storybookPath: 'components-auth',\n tagline:\n 'The door and the guest list: sign-in, sign-up, invites, the member list, and a role guard.',\n\n purpose: `\\`Auth\\` is everything a multi-user app needs around identity, so the app writes no login code. It is\nthree surfaces on one config:\n\n- **\\`<Auth />\\`** — the screen. Signed out it is sign-in, sign-up and the invite landing; signed in it\n is the member list, with roles and removals for whoever is allowed to manage them.\n- **\\`<Auth.Guard roles={[...]}>\\`** — wraps what only some roles may see. It renders the sign-in\n screen to a stranger, a not-allowed card to the wrong role, and the children to the right one.\n- **\\`<Auth.AccountMenu />\\`** — the name, the role and the way out. It goes in \\`Shell\\`'s \\`account\\`\n slot.\n\nWrap the whole app in one \\`Guard\\` with no \\`roles\\` to make signing in the front door, and put a\nnarrower \\`Guard\\` around each screen that needs one. For a profile page, a settings form, or an\norganisation switcher, reach for something else: Auth is the door and the guest list, not the\naccount.`,\n\n configNotes: [\n `\\`roles\\` carries the whole role model. Each entry is \\`{ id, label, manages }\\`: \\`id\\` is what a \\`Guard\\`\nlists and what \\`setRole\\` writes, \\`label\\` is what a person reads, and \\`manages\\` marks the roles that\nmay invite, re-role and remove. The **first entry is the default** — what someone who signs up\nwithout an invite becomes, and what the invite control offers first.\n\n\\`copy\\` overrides labels one at a time; everything you leave out keeps its default.`,\n ],\n\n adapters: [\n {\n adapter: 'Identity',\n calls: '`currentUser()`, `subscribe()`',\n why: 'Who is here on mount, and every later flip. All three surfaces read it.',\n },\n {\n adapter: 'Identity',\n calls: '`signIn()`, `signOut()`',\n why: 'The password mode, and the way out of both the account menu and the not-allowed card.',\n },\n {\n adapter: 'Identity',\n calls: '`requestCode()`, `verifyCode()`',\n why: 'The `code` mode: the address first, the one-time code second.',\n },\n {\n adapter: 'Identity',\n calls: '`signUp()`',\n why: 'Sign-up, carrying the invite token when the reader arrived on a link.',\n },\n {\n adapter: 'Identity',\n calls: '`listMembers()`, `invite()`, `setRole()`, `removeMember()`',\n why: 'The member list and what a managing role does to it.',\n },\n {\n adapter: 'Navigation',\n calls: '`current()`, `subscribe()`',\n why: \"Reads `params.invite` — the app's router is what knows how its own URLs are shaped.\",\n },\n ],\n\n adapterNotes: `Auth takes no other adapter. It never fetches, never stores, and never reads \\`window\\`.\n\n**Refusals are prose, not codes.** Any Identity method may reject with an \\`Error\\`, and Auth prints\nits \\`message\\` under the form exactly as written. Write those messages for the person reading them.\n\n**Invite links round-trip through the router.** \\`invite(role)\\` returns a URL whose token is the last\nthing in it. The reader opens that URL, the app's Navigation adapter parses the token into\n\\`params.invite\\`, and Auth opens sign-up with the workspace named and the role already decided.\nAn app whose router does not put query parameters into \\`Route.params\\` never sees the invite.`,\n\n example: `import { Auth, Shell, fakeIdentity, fakeNavigation } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\nconst config = {\n workspaceName: 'Northgate Cycles',\n mode: 'password',\n allowSignUp: false,\n inviteOnly: true,\n roles: [\n { id: 'mechanic', label: 'Mechanic' },\n { id: 'front desk', label: 'Front desk' },\n { id: 'owner', label: 'Owner', manages: true },\n ],\n copy: { hint: 'Any of the five shop accounts, password northgate.' },\n}\n\nconst adapters = { identity: fakeIdentity(), navigation: fakeNavigation() }\n\n;<Auth.Guard config={config} adapters={adapters}>\n <Shell\n config={{ title: 'Northgate Cycles' }}\n adapters={adapters}\n account={<Auth.AccountMenu config={config} adapters={adapters} />}\n canvas={\n <Auth.Guard config={config} adapters={adapters} roles={['owner']}>\n <DnaScreen />\n </Auth.Guard>\n }\n />\n</Auth.Guard>`,\n\n failureModes: `- **Invalid config.** Auth renders an error card instead of the screen, in dev and in prod, naming\n every field that failed. An unknown field is a failure too — \\`allowsignup\\` does not quietly become\n \\`allowSignUp\\`.\n- **The adapter refuses.** Whatever the rejection's \\`message\\` says is what appears under the form,\n and the form keeps what was typed so it can be corrected. A rejection with no message reads\n \"Something went wrong. Try again.\"\n- **\\`inviteOnly\\` with no invite in the route.** There is no way to sign up, and the screen says so.\n That is the setting working; if it is a surprise, the router is not putting \\`?invite=\\` into\n \\`Route.params\\`.\n- **A used invite.** Tokens are single-use. A second sign-up on the same link is refused by the\n adapter, and the refusal shows under the form like any other.\n- **\\`Guard\\` while the session is still loading.** It renders nothing rather than flashing the\n sign-in screen at someone who is already signed in. A guard around a whole app is therefore blank\n for one tick.\n- **A role that no longer exists.** A member holding a role that is not in \\`roles\\` reads as the raw\n id in the list, and a \\`Guard\\` that does not list it turns them away. Changing \\`roles\\` does not\n migrate anyone; \\`setRole\\` does.\n- **Removing yourself.** The button is not drawn on your own row, because the way out of a workspace\n is signing out, not deleting the last managing account.\n- **A new adapter object on every render.** Auth re-subscribes when \\`adapters\\` changes identity,\n which re-reads the session and the member list. Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const chatDocs: ComponentDocs = {\n name: 'Chat',\n slug: 'chat',\n storybookPath: 'components-chat',\n tagline:\n 'The conversation with the agent: a following message list, a growing composer, replies that stream in.',\n\n purpose: `\\`Chat\\` is the conversation between a person and the agent: a message list that follows the newest\nmessage, a composer that grows as you type, markdown in the agent's replies, and a reply that fills\nin token by token while you watch. It is the column that goes in \\`Shell\\`'s \\`chat\\` slot.\n\nUse it once per conversation. It is the conversation and nothing else: it holds no messages of its\nown (the \\`Chat\\` adapter owns them), it does not know which agent it is talking to, and it does not\nroute. For a list of past conversations to pick from, that is a different component.`,\n\n adapters: [\n {\n adapter: 'Chat',\n calls: '`history()`',\n why: 'The conversation as it stands on mount.',\n },\n {\n adapter: 'Chat',\n calls: '`subscribe(listener)`',\n why: 'Every later change: a new message, and each token of a streaming reply.',\n },\n {\n adapter: 'Chat',\n calls: '`send(text, attachments?)`',\n why: 'What the reader typed, with whatever the composer has staged.',\n },\n {\n adapter: 'Chat',\n calls: '`retry?(messageId)`',\n why: 'Retries a visible failed message when the adapter supports it.',\n },\n {\n adapter: 'Chat',\n calls: '`interrupt?()`',\n why: 'The Stop pill on the thinking row, shown only when the adapter supports it.',\n },\n {\n adapter: 'Chat',\n calls: '`commands?()`',\n why: 'The slash picker, opened by a `/` at the start of the composer. Without it, `/` is text.',\n },\n {\n adapter: 'Chat',\n calls: '`runCommand?(line)`',\n why: 'A sent `/` line. Its reply comes back as text, and both render as system rows.',\n },\n {\n adapter: 'Chat',\n calls: '`openSource?(location)`',\n why: 'A click on a source chip under an agent bubble, with the cited `path#L3-L5`.',\n },\n ],\n\n adapterNotes: `Chat takes no other adapter. It never fetches, never stores, and never reads a clock — a bubble's\ntime comes from the message's own \\`at\\`.\n\n**Streaming is a rule about ids, not a second method.** The adapter re-emits a message with the same\n\\`id\\` and a longer \\`text\\`, \\`streaming: true\\` throughout; the emission that drops the flag is \\`done\\`.\nChat renders that as one bubble that grows with a caret on the end. A \\`streaming\\` message with no\ntext yet is the agent thinking, and renders as the thinking row rather than an empty bubble. When the\nadapter provides \\`interrupt\\`, that row carries a Stop pill; without it, the row is just the dots.\n\n**Slash commands come from the adapter, not the config.** With \\`commands\\`, a composer line starting\nwith \\`/\\` opens a picker over the composer: arrows move, Tab or Enter pick, Escape closes, and a\ncommand with \\`args\\` keeps completing its one argument after the space. A sent \\`/\\` line goes to\n\\`runCommand\\`, never to \\`send\\`, and the line and its reply render as dim centred system rows.\n\"New conversation\" is the adapter's \\`/reset\\`, not a button.`,\n\n slots: [\n {\n slot: 'attach',\n what: 'Replaces the paperclip and the row of staged chips above the composer. It is called with `stage`, which is what the next message is sent with; `Upload.Picker` is what belongs here, so an attachment is a file that really went somewhere. Empty falls back to the built-in paperclip, which stages a name and a size and uploads nothing.',\n },\n ],\n\n example: `import { Chat, fakeChat } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Chat\n config={{\n placeholder: 'Message Golem…',\n emptyState: 'Ask about a ticket, a customer or the day’s report.',\n agentName: 'Golem',\n userName: 'Nadia',\n markdown: true,\n showTimestamps: false,\n maxComposerLines: 6,\n }}\n adapters={{ chat: fakeChat() }}\n/>`,\n\n failureModes: `- **Invalid config.** Chat renders an error card instead of the conversation, in dev and in prod,\n naming every field that failed. An unknown field is a failure too — \\`showtimestamps\\` does not\n quietly become \\`showTimestamps\\`.\n- **The last message is the reader's.** Chat reads that as a reply the agent owes and shows the\n thinking row until an agent message arrives. A history that *ends* on a user message therefore\n looks like it is still waiting, which is usually true and occasionally a surprise.\n- **A message is sending or failed.** The adapter keeps it in the complete conversation with\n \\`delivery: 'pending'\\` or \\`'failed'\\`. Chat dims a pending bubble and labels it Sending; a failed bubble\n says Not sent and offers Retry only when the adapter provides \\`retry\\`. Neither state shows the agent\n thinking row.\n- **A command rejects.** \\`runCommand\\` throws for an unknown name or a bad argument. The typed line\n stays as a system row and the error shows in the strip under the feed, as written.\n- **Sending rejects.** Chat keeps the cleared composer clear, so it never overwrites a newer draft, and\n shows the error from the adapter. The adapter still owns the failed message and its retry.\n- **History rejects.** Chat shows the adapter error and keeps any conversation delivered through\n \\`subscribe\\`; a live update never disappears behind a late history failure.\n- **The reader has scrolled up.** New messages stop moving the list; it follows the newest message\n only while it is already within 48px of the bottom. A stream cannot yank the page out from under\n someone reading.\n- **A new adapter object on every render.** Chat re-subscribes when the \\`adapters\\` prop changes\n identity, which replays \\`history()\\` and can double a stream. Build adapters once, outside render.\n- **Markdown is a subset.** Fenced code, bullet and numbered lists, bold, italic, inline code, and\n \\`http(s)\\` links. Anything else — tables, images, raw HTML — renders as the characters it is, which\n is also what a half-written token mid-stream does.\n- **The built-in paperclip uploads nothing.** It stages a name and a size, which is enough to show\n the chip and enough for an adapter that only wants the metadata. For an attachment that is a real\n file in a real folder, host \\`Upload.Picker\\` in the \\`attach\\` slot.\n- **Timestamps are UTC**, read straight off the ISO string, so a bubble reads the same everywhere.\n For local time, put local time in \\`at\\`.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const editorDocs: ComponentDocs = {\n name: 'Editor',\n slug: 'editor',\n storybookPath: 'components-editor',\n tagline:\n 'One markdown document, edited by a person and by the agent at once, merged line by line.',\n\n purpose: `\\`Editor\\` is the document both writers share. One record is one document — a markdown body and a\nversion number — and the component shows it as editable source with the markdown styled where it\nstands, a preview of the rendering, and an outline of its headings. While the person types, the\ncomponent is subscribed to the record: a version the agent writes arrives, is merged into the draft\nline by line, and the lines it wrote carry a coloured gutter for a few seconds so the person can see\nwhat just changed under them. Neither writer overwrites the other, because neither write is a\nwhole-document write.\n\nReach for something else when the document is only to be read (\\`Report\\`), when the record is a set of\nnamed fields rather than one body of prose (\\`RecordForm\\`), or when the writing is a message rather\nthan a document (\\`Chat\\`).`,\n\n configNotes: [\n `\\`collection\\` and \\`id\\` name one record. An editor edits a document, not a collection: two documents\nare two editors.\n\n\\`bodyField\\` and \\`versionField\\` are how the component reads a record it did not design. The version\nis the whole safety story — read on load, sent back on every save as \\`expectedVersion\\`, and bumped\nby one — so it has to be a number the store keeps honest.`,\n {\n table: {\n headings: ['`preview`', 'On a phone', 'At 768px and wider'],\n rows: [\n ['`toggle`', 'One pane, swapped by a button', 'One pane, swapped by a button'],\n ['`split`', 'Falls back to `toggle`', 'Source and rendering side by side'],\n ['`off`', 'Source only, no button', 'Source only, no button'],\n ],\n },\n },\n `\\`autosaveMs\\` is how long typing has to stop before the draft goes. Short is what a shared document\nwants: the longer a draft sits unsent, the more of it a merge has to reconcile later. \\`0\\` saves on\nevery keystroke.\n\n\\`highlightMs\\` is how long the agent's lines stay marked. \\`0\\` marks nothing, which is what a document\nwith no second writer wants.`,\n ],\n\n adapters: [\n {\n adapter: 'Records',\n calls: '`get(collection, id)`',\n why: 'The document, on mount and again whenever the collection changes.',\n },\n {\n adapter: 'Records',\n calls: '`update(collection, id, patch, { expectedVersion, versionField })`',\n why: 'The save. The version it read goes out with the write, so a write that arrived second is refused rather than winning.',\n },\n {\n adapter: 'Records',\n calls: '`subscribe(collection)`',\n why: 'The agent writing to the document. Its version arrives and is merged; nothing polls.',\n },\n {\n adapter: 'Clock',\n calls: '`now()`, `timeZone()`',\n why: 'The time in the status line — “Saved 13:20” — in the reader’s own zone.',\n },\n ],\n\n adapterNotes: `Editor takes no other adapter. It never fetches, never routes, and never uploads.\n\n**The store has to enforce the version.** \\`update\\` is called with \\`{ expectedVersion, versionField }\\`,\nand a store that ignores it turns every concurrent write into a silent overwrite. A store that\nhonours it rejects with a \\`VersionConflictError\\` carrying the record as it now stands, and the\ncomponent merges against that record instead of showing an error. \\`fakeRecords\\` enforces it, so a\nstory and a test run the same path a server would.\n\n**The merge is a three-way merge over lines**, in \\`src/lib/diff3.ts\\` and tested on its own. The base\nis the version the person and the agent last agreed on, *mine* is the local draft, *theirs* is what\narrived. Edits in different places are both kept. Edits that overlap are a conflict, and a conflict\nis the one thing the component will not decide: it shows both versions and waits.\n\n**Lines, not characters.** A merge that resolves inside a sentence produces text neither writer\nwrote. A line is the smallest unit a person can look at and say \"that one, not that one\".\n\n**A new adapter object on every render** makes Editor re-subscribe and re-fetch on every render.\nBuild adapters once, outside render.`,\n\n slots: [\n {\n slot: 'draft',\n what: 'Unsaved work from a previous session — a local draft store, say. The editor opens on it instead of the record’s body, dirty against the record’s version, so the next thing the agent writes is merged rather than dropped on top of it.',\n },\n {\n slot: 'focus',\n what: '`{ line, endLine?, key?, version?, text? }` — a passage to show, in 1-based source lines with `endLine` inclusive. The source pane scrolls it into the middle and tints it until the person clicks or edits. Out-of-range lines are clamped to the document; a `line` that is not a number is ignored. The same request again needs a new `key`. Caret, selection, draft and keyboard focus are left where they are. `version` is the record version the lines were counted in: the request waits until the editor has read that version or a later one. `text` is the passage itself, its exact lines joined by `\\\\n`: the editor finds it in the draft on screen instead of trusting `line`, and marks nothing when those lines are missing or appear more than once.',\n },\n ],\n\n example: `import { Editor, fakeClock, fakeRecords } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Editor\n config={{\n collection: 'dna',\n id: 'dna',\n bodyField: 'body',\n versionField: 'version',\n autosaveMs: 1000,\n preview: 'toggle',\n outline: true,\n readOnly: false,\n placeholder: 'Write the workspace DNA in markdown…',\n highlightMs: 4000,\n locale: 'en-GB',\n }}\n adapters={{ records: fakeRecords({ dna }), clock: fakeClock() }}\n/>`,\n\n failureModes: `- **Invalid config.** Editor renders an error card instead of the document, in dev and in prod,\n naming every field that failed. \\`autosaveMs\\` and \\`highlightMs\\` are numbers and are not coerced: a\n debounce written as \\`'1000'\\` is named rather than guessed at.\n- **No such record.** \\`get\\` resolving \\`null\\` is not an empty document — the status line says which\n collection and which id came back with nothing, so a typo in \\`id\\` is visible instead of silently\n editing thin air.\n- **The adapter rejects a save** for its own reasons. The rejection's \\`message\\` is shown in the status\n line, verbatim, and the draft is kept: the person's text is never thrown away to report an error.\n- **A version mismatch is not an error.** A \\`VersionConflictError\\` from \\`update\\` triggers the merge and\n the save is retried against the version that came back. A store that rejects a stale write with a\n plain \\`Error\\` instead gets the plain-error path, and the two writers drift.\n- **A conflict stops the merge, not the editor.** Both versions of the disagreeing lines are shown\n with *Keep mine* and *Take theirs*, and nothing is applied until every one of them has been chosen.\n A version arriving while a conflict is open is held and applied once it is settled.\n- **The mark is drawn on line numbers, not on the text.** The gutter is placed when the merge lands;\n typing above those lines while the mark is still up shifts the text under it. It fades within\n \\`highlightMs\\`, which is why the drift is measured in seconds.\n- **The source is styled, not rendered.** \\`##\\` stays \\`##\\`, and the styling is weight, colour and\n background only — a heading drawn larger would slide the text out from under the caret. The\n preview is where the document is rendered, through the same markdown \\`Report\\` and \\`Chat\\` use.\n- **Tab only indents a list line.** Everywhere else it moves focus on, because a textarea a keyboard\n reader cannot leave is a trap. Cmd/Ctrl+S saves now, Cmd/Ctrl+B and Cmd/Ctrl+I wrap the selection,\n and Cmd/Ctrl+Z steps the editor's own undo — the browser's native textarea history has no entry\n for a line the agent wrote.\n- **\\`readOnly\\` blocks the writing, not the reading.** The source and the preview stay, the toolbar\n loses its actions, the autosave never runs, and nothing typed into the source reaches the record.\n- **\\`focus\\` counts lines of the text on screen.** That is the record's body, or the person's draft\n when they have unsaved changes above the passage — the component cannot know which lines the\n caller counted in. A request waits while the record loads or a conflict is open, and belongs to\n the record and the \\`records\\` adapter open when it arrived: move to another \\`id\\` or store\n before it lands and it is dropped, not applied to the wrong document.\n- **\\`focus.text\\` is matched whole line by whole line, exactly.** No trimming, no case folding. On a\n CRLF record the offered lines may keep their CRs, as the server's lines do, or drop them: both\n match. The offered \\`line\\`..\\`endLine\\` range picks among several copies only when the editor is\n on exactly \\`version\\`, with no unsaved changes, and that range holds the text; anywhere else the\n passage must appear once. A passage that cannot be placed opens the record with nothing tinted and\n nothing scrolled — the last request's tint is taken down too — and the draft is never written.\n- **A \\`focus\\` waits for a fresh read.** A record opened, or a parked draft brought back,\n is matched only after its latest version has been read and merged in, so the passage is looked\n for in the text the person will see, not in the draft as it was left. A request sent in the same\n render as a move to another record belongs to the record moved to.\n- **\\`focus\\` shows the source, not the preview.** A toggle editor showing the preview switches to\n the source pane, and \\`split\\` leaves the preview where it was. Source lines have no reliable\n position in the rendering, so the component does not pretend to scroll it.\n- **The focus tint lasts until the text changes.** Any edit — the person's or a merged agent version\n — a click in the source, or a move to another record takes it down, so it never sits on lines that\n moved under it or on a second record that happens to hold the same text.\n- **Moving to another record parks this one.** Changing \\`collection\\`, \\`id\\`, \\`bodyField\\`,\n \\`versionField\\` or the \\`records\\` adapter opens that record fresh and keeps the one left behind —\n draft, the version it read, an open conflict — for as long as the editor stays mounted. Nothing is\n written on the way out. A save still in flight settles against the record that sent it. Coming\n back restores the draft, and the load that follows merges it against the latest version, so a\n write made meanwhile is a merge or a conflict, never a loss. Unmounting drops parked drafts.\n- **A CRLF record stays CRLF.** A body whose every line break is CRLF is edited with bare LFs, as a\n textarea holds it, and every save writes CRLF back, so an edit changes only the lines it touched.\n A body with mixed line endings is written back with LFs once the person edits it.\n- **The \\`draft\\` slot belongs to the first record opened, in the first store.** Any other record,\n or the same record through another \\`records\\` adapter, opens on its own body.\n- **A new adapter object on every render** makes Editor re-subscribe and re-fetch on every render.\n Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const recordFormDocs: ComponentDocs = {\n name: 'RecordForm',\n slug: 'record-form',\n storybookPath: 'components-recordform',\n tagline: 'One record being written: a create form or an edit form, from one field list.',\n\n purpose: `\\`RecordForm\\` is one record being written: a create form or an edit form of the same collection\n\\`RecordList\\` shows, out of the same field vocabulary. It owns validation, the dirty-state guard, the\nconfirm before a delete, and where an adapter's refusal lands; the fields and the layout are the\nagent's.\n\nReach for something else when the screen is many records rather than one (\\`RecordList\\`), when the\nreader signs in or is invited (\\`Auth\\`), or when nothing is written and the record is only being read\n— a page of \\`readOnly\\` fields is a form with the controls taken out, and a document is usually the\nbetter shape for that.`,\n\n configNotes: [\n `\\`fields\\` is the whole form, in the order it is drawn. Each entry starts as the field \\`RecordList\\`\nknows — \\`{ key, label, type, format? }\\` — and adds what makes a control out of it:`,\n {\n table: {\n headings: ['Key', 'On which types', 'What it does'],\n rows: [\n ['`required`', 'all', 'Blocks submit while empty, and marks the label'],\n [\n '`readOnly`',\n 'all',\n 'Shows the value formatted, with no control and no place in a patch',\n ],\n ['`help`', 'all', 'One always-visible line under the control'],\n ['`placeholder`', 'all but `boolean`', 'Ghost text in an empty control'],\n [\n '`options`',\n '`enum` (required)',\n '`[{ id, label }]` — the reader picks the label, the record gets the id',\n ],\n [\n '`min` / `max`',\n '`number` `money` `date` `datetime`',\n 'The accepted range: a number, or an ISO string for a date',\n ],\n ['`multiline`', '`text`', 'A textarea, spanning both columns of `two-column`'],\n [\n '`users`',\n '`user`',\n '`members` is a select over `Identity.listMembers()`; `free` is a text box',\n ],\n ],\n },\n },\n `The eight types are the same eight \\`RecordList\\` renders, from one schema in \\`src/abi/fields.ts\\`, so\na field list can be written once and read by both.`,\n ],\n\n adapters: [\n {\n adapter: 'Records',\n calls: '`get(collection, id)`',\n why: 'Edit mode only, to fill the controls from the record.',\n },\n {\n adapter: 'Records',\n calls: '`create(collection, data)`',\n why: \"Create mode's submit. Every editable field goes in.\",\n },\n {\n adapter: 'Records',\n calls: '`update(collection, id, patch, { expectedVersion, versionField }?)`',\n why: \"Edit mode's submit. Only what the reader changed goes in, with the version it loaded when the record has one.\",\n },\n {\n adapter: 'Records',\n calls: '`remove(collection, id)`',\n why: 'The Delete button, after the confirm.',\n },\n {\n adapter: 'Identity',\n calls: '`listMembers()`',\n why: \"Only for a `user` field with `users: 'members'`.\",\n },\n ],\n\n adapterNotes: `RecordForm takes no other adapter. It never fetches, never routes and never reads a clock — a date\nis whatever the reader put in the control, kept as an ISO string.\n\n**Which record is a prop, not config.** \\`mode: 'edit'\\` loads the id in the \\`recordId\\` prop, because\nthe route knows it and the config does not: one config serves every ticket on the board. \\`onDone\\` is\ncalled with the stored row after a save and with \\`null\\` after a delete, and \\`onCancel\\` after the\nreader has confirmed discarding anything unsaved — where those go next is the app's to decide.\n\n**Refusals name fields.** A rejection carrying \\`fields: [{ field, message }]\\` — a\n\\`RecordRefusedError\\`, or anything shaped like one — puts each sentence under its own control and\nfocuses the first. A rejection without them shows its \\`message\\` as one line above the buttons.\n\n**A versioned record is saved against the version it was read at.** When the loaded record holds a\nnumber in \\`versionField\\` (\\`version\\` by default), \\`update\\` gets that number as \\`expectedVersion\\`\nand the patch sets the field one higher — the same contract \\`Editor\\` keeps. A store that honours it\nrejects a save that arrived second with a \\`VersionConflictError\\` carrying the record as it now\nstands. A record with no number there is saved with a plain patch.`,\n\n example: `import { RecordForm, fakeIdentity, fakeRecords } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<RecordForm\n config={{\n collection: 'jobs',\n fields: [\n { key: 'customer', label: 'Customer', type: 'text', required: true },\n { key: 'bike', label: 'Bike', type: 'text', required: true },\n { key: 'service', label: 'Service', type: 'text', required: true },\n {\n key: 'status',\n label: 'Status',\n type: 'enum',\n required: true,\n options: [\n { id: 'waiting', label: 'Waiting for parts' },\n { id: 'in progress', label: 'On the bench' },\n { id: 'ready', label: 'Ready for pickup' },\n ],\n },\n { key: 'assignee', label: 'Mechanic', type: 'user', users: 'members' },\n { key: 'date', label: 'Booked in', type: 'date', required: true },\n { key: 'quote', label: 'Quote', type: 'money', format: 'USD', min: 0, max: 5000 },\n { key: 'approved', label: 'Price approved', type: 'boolean' },\n { key: 'notes', label: 'Notes', type: 'text', multiline: true },\n ],\n mode: 'create',\n layout: 'single',\n submitLabel: 'Book the bike in',\n cancel: 'back',\n successMessage: 'Ticket written to the board.',\n }}\n adapters={{ records: fakeRecords(), identity: fakeIdentity() }}\n onDone={(row) => navigation.go(\\`/jobs/\\${String(row.id)}\\`)}\n onCancel={() => navigation.go('/jobs')}\n/>`,\n\n failureModes: `- **Invalid config.** RecordForm renders an error card instead of the form, in dev and in prod,\n naming every field that failed by its position — \\`fields.1.options\\` for an \\`enum\\` with nothing to\n pick. \\`options\\` on a non-enum, a field both \\`required\\` and \\`readOnly\\`, a \\`min\\` above its \\`max\\`,\n and \\`deleteAllowed\\` in create mode all fail the same way, before a control is drawn.\n- **Edit mode with no \\`recordId\\`** says so in place of the form. So does an id the adapter has no\n record for: the form will not open blank and quietly create a second record.\n- **The adapter refuses.** A rejection naming fields lands under them and focuses the first; one\n that names none is a single line above the buttons. Both take back the optimistic success line.\n- **Someone else saved first.** A \\`VersionConflictError\\` carrying the current record keeps every\n unsaved value on screen and lists the fields of this form that the other save changed, beside the\n reader's value for each field they changed; the version field itself is left out of that list. Nothing is written until the\n reader picks. *Keep mine* checks the form again, since the controls stay editable, then writes\n only the fields the reader changed, against the newer version, so the other save's remaining\n changes survive; if a third save got in meanwhile, the choice comes back.\n *Take theirs* drops the unsaved values and shows the stored record. Cancel asks before discarding, as\n it always does. A conflict carrying no record shows its \\`message\\` like any other rejection.\n- **The save is optimistic.** The success line appears before the adapter has answered, so a slow\n store still feels immediate. A refusal takes it back — nothing is lost, but a reader who looks\n away at the wrong moment can see the line and then the error.\n- **\\`readOnly\\` fields never reach the store.** They are not in the create data and not in the update\n patch, so a value the server owns cannot be sent back to it stale.\n- **A \\`user\\` field with \\`users: 'members'\\` and no \\`Identity\\` adapter** draws an empty select saying\n there are no members to pick from. The select writes the member's **name**, which is what the\n kit's \\`user\\` type renders; store an id instead by making it an \\`enum\\` with your own options.\n- **Dates are ISO strings**, and \\`datetime\\` is a UTC instant reshaped for \\`datetime-local\\`. What the\n reader types in the control is what the record gets, with no timezone applied on the way.\n- **Below 768px the form is one column**, whatever \\`layout\\` says, and the submit bar sticks to the\n bottom. The breakpoint is measured on the form's own container, so a form in a narrow panel is one\n column on a wide screen.\n- **Cancel with unsaved changes asks first.** With \\`cancel: 'none'\\` there is no cancel button and no\n guard: a form with no way out cannot lose anything by accident.\n- **A new adapter object on every render** makes RecordForm re-load its record on every render.\n Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const recordListDocs: ComponentDocs = {\n name: 'RecordList',\n slug: 'record-list',\n storybookPath: 'components-recordlist',\n tagline: 'Many records of one type: a table at desktop width, cards on a phone, from one config.',\n\n purpose: `\\`RecordList\\` is many records of one type on a screen: a table at desktop width, cards on a phone,\nboth from the same config. Sorting, filtering, searching and paging are its own; the fields it shows\nand the order it opens in are the agent's. It is the workhorse screen of most apps — a job board, an\norder list, a roster — and it is what goes in \\`Shell\\`'s \\`canvas\\`.\n\nReach for something else when the screen is one record rather than many (a record form), when the\nrecords are a dated sequence you read downwards (a timeline), or when the point is a total rather\nthan the rows.`,\n\n configNotes: [\n `\\`fields\\` is the whole list, in the order it is shown. Each entry is\n\\`{ key, label, type, format?, primary? }\\`:`,\n {\n table: {\n headings: ['`type`', 'Renders as', '`format` means'],\n rows: [\n ['`text`', 'The value verbatim', '—'],\n ['`number`', 'Grouped digits, right-shaped', 'How many decimal places'],\n ['`date`', 'A UTC calendar date', '`short`/`medium`/`long`/`full`'],\n ['`datetime`', 'The same, plus the time', '`short`/`medium`/`long`/`full`'],\n ['`enum`', 'A coloured chip', '—'],\n ['`boolean`', '`Yes` or `No`', '—'],\n ['`money`', 'A currency amount', 'An ISO currency code'],\n ['`user`', 'The name with an initials badge', '—'],\n ],\n },\n },\n `Those eight are the vocabulary. Any other value is a config error, not a fallback.\n\n\\`scope\\` and \\`filters\\` are different tools. \\`scope\\` is a narrowing the reader cannot undo — it is how\n\\`/today\\` shows the open tickets and \\`/jobs\\` shows all of them from one component. \\`filters\\` is a row\nof chips the reader picks from, one chip per value that has arrived.`,\n ],\n\n adapters: [\n {\n adapter: 'Records',\n calls: '`list(collection, …)`',\n why: 'Every page. The filter, the sort, the search text and the cursor go with it.',\n },\n {\n adapter: 'Records',\n calls: '`subscribe(collection)`',\n why: 'A row written by someone else. The list re-lists; it never polls.',\n },\n ],\n\n adapterNotes: `RecordList takes no other adapter. It never fetches, never routes and never reads a clock — a date\nis formatted from the record's own value, in UTC.\n\n**Opening a row is the call site's job.** \\`rowAction: 'open'\\` makes each row clickable and focusable\nand calls the \\`onOpen\\` prop with the whole record; the route that record leads to is something only\nthe app knows. With no \\`onOpen\\` the row is focusable and does nothing.\n\n**Paging is a cursor, not a page number.** \\`list\\` returns \\`{ rows, nextCursor }\\`, and \\`nextCursor\\`\ncomes back as \\`cursor\\` on the next call. \\`null\\` means that was the last page and the load-more button\ngoes away. There is no infinite scroll: a list you can reach the bottom of is a list you can put a\nfooter under.`,\n\n example: `import { RecordList, fakeRecords } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<RecordList\n config={{\n collection: 'tickets',\n fields: [\n { key: 'ticket', label: 'Ticket', type: 'text', primary: true },\n { key: 'customer', label: 'Customer', type: 'text' },\n { key: 'status', label: 'Status', type: 'enum' },\n { key: 'assignee', label: 'Mechanic', type: 'user' },\n { key: 'date', label: 'Booked in', type: 'date', format: 'medium' },\n { key: 'quote', label: 'Quote', type: 'money', format: 'USD' },\n ],\n sort: { field: 'date', direction: 'desc' },\n filters: ['status'],\n search: ['ticket', 'customer', 'bike'],\n pageSize: 25,\n emptyState: 'No tickets on the board.',\n rowAction: 'open',\n density: 'comfortable',\n }}\n adapters={{ records: fakeRecords() }}\n onOpen={(row) => navigation.go(\\`/jobs/\\${row.id}\\`)}\n/>`,\n\n failureModes: `- **Invalid config.** RecordList renders an error card instead of the list, in dev and in prod,\n naming every field that failed. A \\`type\\` outside the eight names the offending entry by its\n position — \\`fields.2.type\\` — and lists the vocabulary. A \\`filters\\` or \\`sort.field\\` key that no\n field carries fails the same way, so a typo never becomes a control that does nothing.\n- **The adapter rejects.** The list is replaced by the rejection's \\`message\\`, verbatim. Write those\n messages for the person reading them.\n- **A field key no record carries** is not an error: the cell shows an em dash. The config is the\n agent's claim about the shape of a record, and the list will not pretend to know better.\n- **Filter chips only know what has arrived.** A value that lives on page four has no chip until\n page four is loaded. Narrow with \\`scope\\`, or a bigger \\`pageSize\\`, when the whole vocabulary has to\n be there from the start.\n- **The cursor is a position, not a bookmark.** Rows inserted while the reader is on page three shift\n the pages under them, which is why a live update re-lists what is on screen rather than paging on.\n- **Below 768px there are no column headings**, and the reader sorts from a select instead. The\n breakpoint is Shell's default, and it is measured on the list's own container — a list in a narrow\n panel draws cards on a wide screen.\n- **A wide table scrolls inside its own box**, up to 70% of the viewport height, so the sticky\n heading has something to stick to and the page never scrolls sideways. Fewer columns, or\n \\`density: 'compact'\\`, is usually the better answer.\n- **Dates are UTC**, read straight off the record, so a row reads the same everywhere. For local\n time, put local time in the record.\n- **A new adapter object on every render** makes RecordList re-subscribe and re-list on every render.\n Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const reportDocs: ComponentDocs = {\n name: 'Report',\n slug: 'report',\n storybookPath: 'components-report',\n tagline: 'One dated document, rendered from markdown, printable, with the past ones under it.',\n\n purpose: `\\`Report\\` is the document an agent writes for people to read: a daily stand-up, a weekly summary, a\nmonthly pack. One record is one report — a title, a date, a markdown body — and the component\nrenders the latest one for the period it is sitting in, steps to the period before or after it, and\nprints on its own sheet. It is what goes on a \\`/report\\` route, and the same component with\n\\`showIndex\\` and \\`showNavigation\\` off is the report card on a dashboard.\n\nReach for something else when the records are rows to compare rather than prose to read\n(\\`RecordList\\`), when the reader is writing rather than reading (\\`RecordForm\\`), or when the point is\na dated sequence of short entries rather than one document.`,\n\n configNotes: [\n `\\`period\\` does three jobs at once: how much one report covers, what the arrows step by, and how\nwide the query is. It is the only field that changes what arrives.`,\n {\n table: {\n headings: ['`period`', 'The query asks for', 'The heading reads'],\n rows: [\n ['`day`', 'One ISO day', 'Thursday, 10 September 2026'],\n ['`week`', 'The seven days from Monday', '7 – 13 September 2026'],\n ['`month`', 'Every day in the calendar month', 'September 2026'],\n ],\n },\n },\n `The field names — \\`dateField\\`, \\`titleField\\`, \\`bodyField\\`, \\`statusField\\`, \\`authorField\\` — are how\none component reads a collection it did not design. Only \\`statusField\\` is optional, and leaving it\nout is what says these reports have no draft state.\n\n\\`showIndex\\`, \\`showNavigation\\` and \\`print\\` are the three pieces of chrome, and they come off\nindependently. A report on its own route wants all three; a report card inside a wider screen wants\nnone of them, because the screen already carries the navigation and the reader prints from the\nroute.`,\n ],\n\n adapters: [\n {\n adapter: 'Records',\n calls: '`list(collection, …)`',\n why: 'The latest report in the period, and the index of past ones.',\n },\n {\n adapter: 'Records',\n calls: '`get(collection, id)`',\n why: 'One report by id — the `id` prop, or a line the reader picked out of the index.',\n },\n {\n adapter: 'Records',\n calls: '`subscribe(collection)`',\n why: 'The agent regenerating the report. The document replaces itself; it never polls.',\n },\n {\n adapter: 'Clock',\n calls: '`now()`',\n why: 'Which period is the current one: where the component opens, and how far forward the arrows go.',\n },\n ],\n\n adapterNotes: `Report takes no other adapter. It never fetches, never routes, and never writes — the agent writes\nthe report through \\`Records\\` and this component reads it.\n\n**A period is an \\`any of\\`, not a range.** \\`list\\` is asked for\n\\`filter: { date: ['2026-09-07', …, '2026-09-13'] }\\` — every ISO day the period covers, spelled out.\nThat is why \\`Records\\` needs no range query and why \\`dateField\\` must hold an ISO calendar day\n(\\`2026-09-10\\`), not a timestamp. A month asks for up to 31 values in one call.\n\n**The latest report wins.** The query is sorted on \\`dateField\\` descending with a limit of one, so a\nperiod holding several reports shows the newest and the index is how the reader reaches the others.\n\n**The index is its own list, not a \\`RecordList\\`.** It is one \\`list\\` call of the twelve newest\nreports, drawn as titles and dates. \\`RecordList\\` would bring filters, chips, paging and a table\nlayout to a control that is a table of contents, and it would need a second config for a list the\nreader never sorts.\n\n**Every date is UTC**, read off the record and formatted with \\`locale\\`, so a report covers the same\nday in every timezone. \\`Clock\\` supplies only *now*.\n\n**A new adapter object on every render** makes Report re-subscribe and re-fetch on every render.\nBuild adapters once, outside render.`,\n\n slots: [\n {\n slot: 'id',\n what: 'One report by id instead of the latest of the period. The component opens on that report’s own period, so the arrows and the index carry on from where it sits.',\n },\n ],\n\n example: `import { Report, fakeClock, fakeRecords } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Report\n config={{\n collection: 'reports',\n period: 'day',\n dateField: 'date',\n titleField: 'title',\n bodyField: 'body',\n statusField: 'status',\n authorField: 'author',\n showAuthor: true,\n showIndex: true,\n showNavigation: true,\n print: true,\n emptyState: 'No report for this day.',\n locale: 'en-GB',\n }}\n adapters={{ records: fakeRecords({ reports }), clock: fakeClock() }}\n/>`,\n\n failureModes: `- **Invalid config.** Report renders an error card instead of the document, in dev and in prod,\n naming every field that failed. A \\`locale\\` no platform can format — \\`en_GB\\`, \\`english\\` — is named\n at \\`locale\\` with the shape of a tag that works, rather than throwing inside \\`Intl\\` at render time.\n- **The adapter rejects.** The document is replaced by the rejection's \\`message\\`, verbatim. Write\n those messages for the person reading them. A rejection on the *index* is swallowed: an aside that\n failed must not take down a document that arrived.\n- **A period with no report** is not an error: the \\`emptyState\\` line stands in, and the arrows stay,\n because stepping back to a period that has one is the way out.\n- **A \\`dateField\\` that is not an ISO day** matches nothing. The filter is an equality list of\n \\`YYYY-MM-DD\\` strings, so a timestamp in that field silently returns no report — put the calendar\n day in the record and the timestamp somewhere else.\n- **The next arrow stops at the current period.** \\`Clock\\` says which one that is; there is no report\n from next week, so the arrow is disabled rather than showing an empty period.\n- **The body is the markdown this kit renders**: headings, pipe tables, bullet and numbered lists,\n fenced code, inline code, bold, italic and http(s) links. Anything else falls through as the\n characters it is, which is what keeps a half-written document readable while the agent writes it.\n- **\\`sections\\` is optional metadata**, an array of strings or of \\`{ title }\\` objects. When a report\n carries it, a Contents list opens the document and scrolls to the matching heading in the body. A\n title with no matching heading scrolls nowhere.\n- **Printing needs \\`print: true\\`**, which brings both the action and the stylesheet. The chrome\n comes off the sheet, the card border goes, and headings, list items, code blocks and tables are\n kept off page breaks. With \\`print: false\\` a browser print takes the whole screen as it stands.\n- **A new adapter object on every render** makes Report re-subscribe and re-fetch on every render.\n Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const shellDocs: ComponentDocs = {\n name: 'Shell',\n slug: 'shell',\n storybookPath: 'components-shell',\n tagline:\n 'The app frame: a thin top bar, the canvas, the app’s bottom menu bar, and the chat behind a toggle.',\n\n purpose: `\\`Shell\\` is the app frame, pinned to the viewport. A thin **top bar** carries the app title and, on the\nright, icon-only buttons: the **chat toggle**, the **settings** gear (a dropdown with the theme toggle\nand whatever rows the app adds), and the **account** avatar. Along the bottom, a **menu bar** lists the\napp's own screens from \\`config.menu\\`, phone-app style: an icon over a short label, at every width.\nBetween the two is the canvas.\n\nThe chat is what the toggle shows and hides. Above the breakpoint it is a side column on \\`chatSide\\`,\ndrag-resizable, above the menu bar, and its width and open state are kept in this browser. Below\nthe breakpoint it is a sheet over the app, menu bar included: the canvas stays mounted underneath, so closing it lands you\nexactly where you were, scroll included. A route change closes the sheet, because a route change is\na screen the reader asked for. Chat hidden means the canvas has the whole width.\n\nThe frame never scrolls as a whole: the top bar and the menu bar stay put, and only the chat and\nthe canvas scroll inside themselves. Shell also owns the kit's theme — the\n\\`data-golem-theme\\` attribute on the document — from the saved choice or the OS preference.\n\nUse it once, at the root of an app. It does not render the conversation (hand that in as \\`chat\\`),\nand it does not route (hand the current screen in as \\`canvas\\`, and the screens as \\`menu\\`).`,\n\n adapters: [\n {\n adapter: 'Identity',\n calls: '`currentUser()`, `subscribe()`',\n why: \"Shows the signed-in user's name in the top bar when no `account` slot is given, and keeps it current when they sign out.\",\n },\n {\n adapter: 'Navigation',\n calls: '`current()`, `go()`, `subscribe()`',\n why: 'The title is the way home; a menu item with `href` goes there; the current route marks the current item and closes the chat sheet.',\n },\n ],\n\n adapterNotes:\n 'Shell takes no other adapter. It never fetches, never reads a router, and stores only its own chrome — chat width, chat open, theme — in this browser.',\n\n slots: [\n {\n slot: 'chat',\n what: 'The conversation. `Chat` is what belongs here; empty renders a labelled placeholder, `null` removes the chat and its toggle altogether.',\n },\n {\n slot: 'canvas',\n what: 'The screen the agent built. Empty renders a labelled placeholder.',\n },\n {\n slot: 'account',\n what: 'The right-hand end of the top bar. `Auth.AccountMenu` is what belongs here; empty falls back to the signed-in name as plain text.',\n },\n {\n slot: 'settings',\n what: 'Extra rows in the gear dropdown, under the theme toggle. Each row is a `Shell.Setting` — an icon, a short label, and `on` when it is a toggle. `Shell.Icon` has the line icons the bar uses.',\n },\n {\n slot: 'onSelect',\n what: 'Called with the item id when a menu item without `href` is tapped: the app changes its own state and passes `activeId` back.',\n },\n ],\n\n example: `import { Auth, Shell, fakeIdentity, fakeNavigation } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Shell\n config={{\n title: 'Northgate Cycles',\n menu: [\n { id: 'today', label: 'Today', icon: '🏠', href: '/today' },\n { id: 'jobs', label: 'Jobs', icon: '🔧', href: '/jobs' },\n { id: 'admin', label: 'Admin', icon: '🛠️' },\n ],\n activeId: showingAdmin ? 'admin' : undefined,\n chatSide: 'left',\n chatOpen: true,\n breakpoint: 768,\n }}\n adapters={{ identity: fakeIdentity(), navigation: fakeNavigation() }}\n chat={<AgentChat />}\n canvas={<TodayScreen />}\n account={<Auth.AccountMenu config={authConfig} adapters={{ identity, navigation }} />}\n settings={\n <Shell.Setting icon={<Shell.Icon name=\"wrench\" />} label=\"Builder\" on={builder} onClick={toggleBuilder} />\n }\n onSelect={(id) => id === 'admin' && setShowingAdmin(true)}\n/>`,\n\n failureModes: `- **Invalid config.** Shell renders an error card instead of the frame, in dev and in prod, naming\n every field that failed and the rule it broke. An unknown field is a failure too — \\`chatside\\` does\n not quietly become \\`chatSide\\`, and the retired \\`initialTab\\` is rejected.\n- **No slot.** A missing \\`chat\\` or \\`canvas\\` renders a labelled placeholder, so a half-wired app looks\n half-wired rather than broken. \\`chat={null}\\` is different: the app has no chat in this mode, so there\n is no column, no sheet and no toggle, only the canvas.\n- **An empty menu.** No bottom bar is drawn; the canvas runs to the bottom edge.\n- **A menu item with neither \\`href\\` nor an \\`onSelect\\` handler.** Tapping it does nothing. Give it one\n or the other.\n- **Identity resolves to \\`null\\`.** The top bar renders without a name. Signing out is not an error.\n- **The breakpoint is measured on the frame, not the window.** A Shell inside a narrow column gets\n the phone layout, which is usually what you want and is occasionally a surprise.\n- **The frame is \\`100dvh\\` tall, capped at its parent.** Put it at the root, or in a box with a\n height; inside an auto-height parent it is the viewport's height and the page would scroll.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const timelineDocs: ComponentDocs = {\n name: 'Timeline',\n slug: 'timeline',\n storybookPath: 'components-timeline',\n tagline: 'Dated entries read downwards, newest first: the log of what happened.',\n\n purpose: `\\`Timeline\\` is the log of what happened: dated entries read downwards, newest first, cut into days\nunder sticky headers. An entry is a short piece of markdown with an instant on it, and optionally a\nkind, a person and a file or two. Times inside today are how long ago they were — \"14 min ago\" —\nand every older entry carries its clock time.\n\nReach for something else when the records are a set rather than a sequence (\\`RecordList\\`), when the\npoint is one document rather than many entries (\\`Report\\`), or when the reader is talking to\nsomething that answers back (\\`Chat\\`).`,\n\n configNotes: [\n `\\`kindField\\` and \\`kinds\\` go together: the field says which kind an entry is, and \\`kinds\\` is the\nvocabulary it is read against. A kind's \\`tone\\` — never a colour — picks its mark:`,\n {\n table: {\n headings: ['`tone`', 'Mark', 'For'],\n rows: [\n ['`neutral`', 'A grey dot', 'What was said or noticed'],\n ['`good`', 'A green tick', 'What finished'],\n ['`warn`', 'An amber triangle', 'What is waiting or at risk'],\n ['`bad`', 'A red cross', 'What went wrong'],\n ],\n },\n },\n `Four tones and no palette, so the same kind of thing is the same colour on every screen the agent\nbuilds.\n\n\\`filters\\` and \\`search\\` are different tools. \\`filters\\` is a row of chips: \\`kind\\` offers the whole\nconfigured vocabulary, \\`actor\\` offers every person whose entries have arrived. \\`search\\` is a box\nmatching a substring against the field keys it lists — usually just the body field.\n\n\\`composer\\` is the one config that writes. On, a one-line box sits above the log; what it writes is\nthe body the reader typed, the clock's current instant in \\`dateField\\`, the signed-in member's name\nin \\`actorField\\`, and — when \\`kindField\\` is set — the kind picked from a select beside the box.`,\n ],\n\n adapters: [\n {\n adapter: 'Records',\n calls: '`list(collection, …)`',\n why: 'Every page, sorted by `dateField` descending. The chosen filters, the search text and the cursor go with it.',\n },\n {\n adapter: 'Records',\n calls: '`subscribe(collection)`',\n why: 'An entry written by someone else. The timeline re-lists; it never polls.',\n },\n {\n adapter: 'Records',\n calls: '`create(collection, data)`',\n why: 'The composer, and only the composer. Absent `composer: true` the timeline never writes.',\n },\n {\n adapter: 'Clock',\n calls: '`now()`, `timeZone()`',\n why: 'What \"Today\" and \"14 min ago\" are measured against, and which zone the days are cut in.',\n },\n {\n adapter: 'Identity',\n calls: '`currentUser()`',\n why: 'Who a composed entry is by. Only called with `composer: true`.',\n },\n {\n adapter: 'Files',\n calls: '`url(id)`',\n why: 'Turns an attachment chip into something to open. Only called when `attachmentsField` is set.',\n },\n ],\n\n adapterNotes: `Timeline takes no other adapter. It never fetches, never routes, and never reads the machine's\nclock — the time on screen is the \\`Clock\\` adapter's, which is why a frozen clock makes every\nexample read the same on every run.\n\n**\\`identity\\` and \\`files\\` are required by the config, not by the interface.** Turn \\`composer\\` on\nwithout an \\`identity\\` and entries are written unsigned; set \\`attachmentsField\\` without a \\`files\\`\nadapter and a chip says so when it is clicked, rather than failing silently.\n\n**Paging is a cursor, not a page number.** \\`list\\` returns \\`{ rows, nextCursor }\\`, and \\`nextCursor\\`\ncomes back as \\`cursor\\` on the next call. Load-more fetches *older* entries and appends them under\nwhat is already read; new entries only ever arrive at the top, through \\`subscribe\\`.`,\n\n example: `import { Timeline, fakeClock, fakeIdentity, fakeRecords } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Timeline\n config={{\n collection: 'log',\n dateField: 'at',\n bodyField: 'body',\n kindField: 'kind',\n kinds: [\n { id: 'note', label: 'Note', tone: 'neutral' },\n { id: 'done', label: 'Done', tone: 'good' },\n { id: 'parts', label: 'Parts', tone: 'warn' },\n { id: 'problem', label: 'Problem', tone: 'bad' },\n ],\n actorField: 'actor',\n attachmentsField: 'files',\n filters: ['kind', 'actor'],\n search: ['body'],\n pageSize: 25,\n composer: true,\n emptyState: 'Nothing on the log yet.',\n locale: 'en-GB',\n }}\n adapters={{ records, clock, identity, files }}\n/>`,\n\n failureModes: `- **Invalid config.** Timeline renders an error card instead of the log, in dev and in prod, naming\n every field that failed. A \\`tone\\` outside the four names the kind by its position —\n \\`kinds.1.tone\\` — and lists them. \\`kindField\\` without \\`kinds\\`, \\`kinds\\` without \\`kindField\\`, two\n kinds sharing an \\`id\\`, and a \\`filters\\` entry whose field is not set all fail the same way, so a\n chip row is never empty by surprise.\n- **The adapter rejects.** The entries are replaced by the rejection's \\`message\\`, verbatim. A\n composer write that is refused puts the message under the box and keeps what was typed.\n- **A kind the config has no word for** is not an error: the entry wears the raw value as a grey\n chip and a neutral dot. The record said something; the log will not hide it.\n- **An unreadable date** joins the day above it and shows an em dash instead of a time. Write\n \\`dateField\\` as a full ISO instant; a bare calendar date has no time to show.\n- **Days are cut in the \\`Clock\\` adapter's zone**, so \"Today\" means today where the reader is. A\n zone the platform does not know falls back to UTC rather than throwing mid-render.\n- **The actor chips only offer actors held as a name.** The chip's value is what \\`list\\` is asked to\n match, and an actor held as a record is not an equality an adapter can answer — it still shows on\n the entry, it just gets no chip.\n- **The pill counts arrivals, it does not hold them back.** A new entry is always on screen; the\n pill is only how the reader learns about it when they have scrolled away from the top.\n- **The log scrolls inside its own box**, up to 70% of the viewport height, so the day headers have\n something to stick to and the page never scrolls sideways. A shorter \\`pageSize\\` is the answer for\n a timeline sitting inside a wider screen.\n- **A new adapter object on every render** makes Timeline re-subscribe and re-list on every render.\n Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const uploadDocs: ComponentDocs = {\n name: 'Upload',\n slug: 'upload',\n storybookPath: 'components-upload',\n tagline: 'Files and photos in, a gallery out — and a picker a composer can host.',\n\n purpose: `\\`Upload\\` is how files get into a workspace and how they are looked at afterwards. One config gives\ntwo surfaces: \\`<Upload />\\` is a drop zone, a camera button on a phone, a queue with progress and\nper-file errors, and the gallery the files land in — thumbnails for pictures, tiles for everything\nelse, a lightbox, an optional caption, and a delete that asks first. \\`<Upload.Picker />\\` is the same\npicking and the same limits with no gallery: a button a composer hosts, handing file refs back to\nwhatever is about to send them.\n\nReach for something else when the files are already somewhere and you only need to open one — a\n\\`Timeline\\` attachment chip does that with \\`Files.url\\` alone — or when what you are collecting is\nfields rather than files (\\`RecordForm\\`).`,\n\n configNotes: [\n `\\`folder\\` is the whole of the component's identity: it is what \\`upload\\`, \\`list\\` and \\`subscribe\\` are\ngiven, so two Uploads on the same folder are two views of the same files and a file dropped on one\nappears on the other.\n\nThe three limits are checked in the browser, in this order, before anything is uploaded:`,\n {\n table: {\n headings: ['Limit', 'What it refuses', 'What the row says'],\n rows: [\n [\n '`accept`',\n 'A file whose MIME type no entry matches',\n 'The type it is, and the types this upload takes',\n ],\n ['`maxSizeMb`', 'A file bigger than the limit', 'How big it is, and the limit'],\n [\n '`maxFiles`',\n 'A file past the last place in the folder',\n 'What number it would have been, and how many the folder holds',\n ],\n ],\n },\n },\n `Each refusal is a \\`FileRejectedError\\` carrying the \\`fileName\\` and a \\`reason\\` of \\`type\\`, \\`size\\` or\n\\`count\\`. The component catches its own: nothing is thrown at the caller, and the sentence goes on\nthe file's row, which is where the reader can do something about it. \\`upload\\` is never called for a\nrefused file, so an over-size photo does not leave the machine.\n\n\\`accept\\` is a list of MIME types — \\`image/jpeg\\`, \\`application/pdf\\`, or a whole group as\n\\`image/*\\`. It is not a list of extensions: \\`jpg\\` fails validation and produces the error card,\nbecause a file picker offering \\`jpg\\` matches nothing.\n\n\\`capture\\` is the phone camera. \\`environment\\` is the rear camera, which is what a photo of a bike on\na bench wants; \\`user\\` is the front one. A desktop browser ignores the attribute and offers its file\npicker, so turning it on costs a laptop nothing.\n\n\\`layout\\` decides how the gallery reads, not what it holds. \\`grid\\` draws pictures as thumbnails and\neverything else as an icon tile; \\`list\\` draws one row per file whatever it is, which is what a\nfolder of invoices wants.`,\n ],\n\n adapters: [\n {\n adapter: 'Files',\n calls: '`upload(file, { folder, onProgress })`',\n why: 'Every accepted file, one call each. `onProgress` moves the bar on that file’s row; the ref it resolves to is what the gallery and the picker then hold.',\n },\n {\n adapter: 'Files',\n calls: '`list(folder)`',\n why: 'The gallery, newest first. Called on mount and again on every change the folder reports.',\n },\n {\n adapter: 'Files',\n calls: '`subscribe(folder)`',\n why: 'A file put there by someone else, or by the other surface. Upload re-lists; it never polls.',\n },\n {\n adapter: 'Files',\n calls: '`url(ref, { thumbnail })`',\n why: 'Twice per file: the small one for the grid, the full one for the lightbox. A store with no thumbnail returns the full URL and the grid still draws.',\n },\n {\n adapter: 'Files',\n calls: '`remove(ref)`',\n why: 'Delete, and only after the reader has confirmed it. With `deleteAllowed: false` it is never called.',\n },\n {\n adapter: 'Files',\n calls: '`caption(ref, text)`',\n why: 'The line under a tile, written when the reader leaves the box. Only called with `captions: true`.',\n },\n ],\n\n adapterNotes: `Upload takes one adapter and no other. It never fetches, never routes, and never reads the clock —\n\\`uploadedAt\\` is whatever the store stamped, and it is the only thing the gallery's order depends on.\n\n**A \\`FileRef\\` is plain JSON**: an id, a name, a MIME type, a size, the folder and the instant. No\nbytes. Put one on a record, in a chat message or in a config and it survives the round trip; when\nyou want to see the file, ask \\`url\\` for a URL. Methods that take a ref take just an id too, so\nsomething holding only the id can still open and delete the file.\n\n**Progress is the adapter's to report.** A store that cannot report it calls \\`onProgress\\` once with\n\\`1\\`, and the row jumps from 0% to done rather than showing nothing. \\`fakeFiles\\` reports ten steps\nover about 600 ms so the bar is worth looking at.\n\n**The two surfaces share a config and share nothing else.** \\`Upload.Picker\\` never lists and never\ndeletes; it uploads into the same folder and hands the refs to \\`onPicked\\`, which is a per-use prop\nof the call site rather than config, because who holds the files is the composer's business.`,\n\n slots: [\n {\n slot: 'onPicked',\n what: '`Upload.Picker` only. Called with every ref the picker is holding, each time that changes — a file finishing, or a chip taken off. The parent composer keeps the list; the picker only shows it.',\n },\n ],\n\n example: `import { Upload, fakeFiles } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Upload\n config={{\n folder: 'shop',\n accept: ['image/jpeg', 'image/png', 'image/webp'],\n maxFiles: 12,\n maxSizeMb: 8,\n capture: 'environment',\n captions: false,\n layout: 'grid',\n deleteAllowed: true,\n emptyState: 'No photos on this ticket yet.',\n }}\n adapters={{ files }}\n/>\n\n// The same config, as the button a composer hosts:\n;<Upload.Picker config={config} adapters={{ files }} onPicked={setStaged} />`,\n\n failureModes: `- **Invalid config.** Upload renders an error card instead of the drop zone, in dev and in prod,\n naming every field that failed. An \\`accept\\` entry that is not a MIME type is named by its\n position — \\`accept.1\\` — and told how to write it, because a picker offering \\`jpg\\` matches\n nothing and would fail silently otherwise.\n- **A file over the limits** never reaches the adapter. Its row turns red and says which limit it\n broke, in the file's own numbers; every other file in the same batch still goes up.\n- **An upload the store refuses** keeps its row, red, with the store's \\`message\\` on it verbatim.\n The rest of the batch is unaffected, and the row can be dismissed.\n- **\\`list\\` rejects.** The gallery is replaced by the rejection's \\`message\\`. The drop zone stays,\n so the reader can still try to add something.\n- **\\`url\\` rejects** for one file and that file draws its icon rather than its picture. One\n unreadable thumbnail does not empty the grid.\n- **Delete always asks.** The button turns into *Delete permanently* and *Keep it*; nothing is\n removed until the first is pressed. With \\`deleteAllowed: false\\` there is no button at all.\n- **A picture is whatever \\`contentType\\` says.** A file uploaded with an empty type — some browsers\n send none for an unusual extension — tiles as a generic file rather than a thumbnail.\n- **\\`capture\\` is a request, not a guarantee.** A desktop browser, and some mobile ones, show the\n ordinary file picker instead. Never make the camera the only way in: the file button is always\n there beside it.\n- **A new adapter object on every render** makes Upload re-subscribe and re-list on every render.\n Build adapters once, outside render.`,\n}\n","import type { AdapterDocs, ComponentDocs } from './abi'\nimport { brainAdapterDocs } from './adapters/brain.docs'\nimport { chatAdapterDocs } from './adapters/chat.docs'\nimport { clockAdapterDocs } from './adapters/clock.docs'\nimport { filesAdapterDocs } from './adapters/files.docs'\nimport { identityAdapterDocs } from './adapters/identity.docs'\nimport { navigationAdapterDocs } from './adapters/navigation.docs'\nimport { recordsAdapterDocs } from './adapters/records.docs'\nimport { brainDocs } from './components/Brain/Brain.docs'\nimport { authDocs } from './components/Auth/Auth.docs'\nimport { chatDocs } from './components/Chat/Chat.docs'\nimport { editorDocs } from './components/Editor/Editor.docs'\nimport { recordFormDocs } from './components/RecordForm/RecordForm.docs'\nimport { recordListDocs } from './components/RecordList/RecordList.docs'\nimport { reportDocs } from './components/Report/Report.docs'\nimport { shellDocs } from './components/Shell/Shell.docs'\nimport { timelineDocs } from './components/Timeline/Timeline.docs'\nimport { uploadDocs } from './components/Upload/Upload.docs'\n\n/**\n * Every exported component's page prose, in the order a reader meets them: the frame first, then\n * what goes in its slots, then the screens. The Storybook introduction and the showcase's `#/api`\n * index are both this list.\n */\nexport const componentDocs: ComponentDocs[] = [\n shellDocs,\n chatDocs,\n authDocs,\n recordListDocs,\n recordFormDocs,\n reportDocs,\n editorDocs,\n timelineDocs,\n uploadDocs,\n brainDocs,\n]\n\n/**\n * Every adapter's page prose, in the order a reader meets them: the two that carry the app's data\n * first, then who is using it, then the conversation, then the two small ones. The Storybook\n * `Adapters/Overview` page and the showcase's `#/adapters` index are both this list.\n */\nexport const adapterDocs: AdapterDocs[] = [\n recordsAdapterDocs,\n filesAdapterDocs,\n identityAdapterDocs,\n chatAdapterDocs,\n clockAdapterDocs,\n navigationAdapterDocs,\n brainAdapterDocs,\n]\n\nexport function adapterDocsFor(slug: string): AdapterDocs | undefined {\n return adapterDocs.find((docs) => docs.slug === slug)\n}\n\n/** The documented adapter a component's `adapters` row names, for the link between the two pages. */\nexport function adapterDocsNamed(name: string): AdapterDocs | undefined {\n return adapterDocs.find((docs) => docs.name === name)\n}\n\n/**\n * Every component that takes this adapter, read off the components' own adapter lists rather than\n * written down a second time. `<name>.docs.ts` carries the same list so the file reads as a whole\n * document; `adapters.docs.test.ts` fails when the two disagree.\n */\nexport function componentsUsing(adapter: string): ComponentDocs[] {\n return componentDocs.filter((docs) => docs.adapters.some((row) => row.adapter === adapter))\n}\n","import { z } from 'zod'\n\nconst roleSchema = z\n .object({\n id: z.string().min(1),\n label: z.string().min(1),\n manages: z.boolean().default(false),\n })\n .strict()\n\nconst copySchema = z\n .object({\n signInTitle: z.string().min(1).default('Sign in'),\n signUpTitle: z.string().min(1).default('Create your account'),\n hint: z.string().default(''),\n submit: z.string().min(1).default('Continue'),\n membersTitle: z.string().min(1).default('Members'),\n denied: z.string().min(1).default('This screen is not open to your role.'),\n })\n .strict()\n\nexport const authConfigSchema = z\n .object({\n workspaceName: z\n .string()\n .min(1)\n .describe(\n 'The workspace people are signing in to. Names the sign-in screen and the invite a new member opens.',\n ),\n mode: z\n .enum(['password', 'code'])\n .default('password')\n .describe(\n 'How a returning member proves who they are. `password` asks for one; `code` sends a one-time code to the address and asks for that instead.',\n ),\n allowSignUp: z\n .boolean()\n .default(true)\n .describe(\n 'Whether the sign-in screen offers a link to create an account. An invite link always leads to sign-up whatever this says.',\n ),\n inviteOnly: z\n .boolean()\n .default(false)\n .describe(\n 'Whether an account can only be created from an invite link. On, the sign-up link is hidden and the form appears only for a reader who arrived on an invite.',\n ),\n roles: z\n .array(roleSchema)\n .min(1)\n .prefault([\n { id: 'member', label: 'Member' },\n { id: 'admin', label: 'Admin', manages: true },\n ])\n .describe(\n 'Every role this workspace has, each `{ id, label, manages }`. The first is what a new member gets. A role with `manages: true` may invite people, change their role and remove them; every other role sees the same list read-only.',\n ),\n copy: copySchema\n .prefault({})\n .describe(\n 'Overrides for the labels the screens are built from: `signInTitle`, `signUpTitle`, `hint` (a line under the sign-in title, where a demo says which accounts work), `submit`, `membersTitle`, `denied`.',\n ),\n })\n .strict()\n\nexport type AuthConfig = z.output<typeof authConfigSchema>\nexport type AuthConfigInput = z.input<typeof authConfigSchema>\nexport type AuthRole = z.output<typeof roleSchema>\n","import {\n useCallback,\n useEffect,\n useState,\n type FormEvent,\n type InputHTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { IdentityAdapter, NavigationAdapter, User } from '../../adapters'\nimport { authConfigSchema, type AuthConfig, type AuthRole } from './Auth.config'\n\nexport interface AuthAdapters {\n identity: IdentityAdapter\n navigation: NavigationAdapter\n}\n\nexport interface AuthGuardSlots {\n /** Roles allowed through. Empty — or left out — means any signed-in person. */\n roles?: string[]\n /** What the allowed reader sees. */\n children?: ReactNode\n}\n\n/** Who is here, and whether we have asked yet. `ready` keeps a blank frame off the screen. */\nfunction useSession(identity: IdentityAdapter) {\n const [user, setUser] = useState<User | null>(null)\n const [ready, setReady] = useState(false)\n\n useEffect(() => {\n let live = true\n void identity.currentUser().then((next) => {\n if (!live) return\n setUser(next)\n setReady(true)\n })\n const unsubscribe = identity.subscribe(setUser)\n return () => {\n live = false\n unsubscribe()\n }\n }, [identity])\n\n return { user, ready }\n}\n\n/**\n * The invite token, read off the route rather than off `window`: the app's Navigation adapter is\n * what knows how its URLs are shaped, so a hash router and a path router both land here.\n */\nfunction useInviteToken(navigation: NavigationAdapter): string | undefined {\n const [token, setToken] = useState(() => navigation.current().params.invite)\n useEffect(() => navigation.subscribe((route) => setToken(route.params.invite)), [navigation])\n return token\n}\n\nfunction roleFor(config: AuthConfig, user: User | null): AuthRole | undefined {\n return config.roles.find((role) => user?.roles.includes(role.id))\n}\n\nfunction labelForRole(config: AuthConfig, user: User): string {\n return roleFor(config, user)?.label ?? user.roles[0] ?? '—'\n}\n\nfunction canManage(config: AuthConfig, user: User | null): boolean {\n return config.roles.some((role) => role.manages && user?.roles.includes(role.id))\n}\n\n/** Adapter rejections are written for the reader, so the message is shown as it arrives. */\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : 'Something went wrong. Try again.'\n}\n\nconst fieldClass =\n 'mt-1 w-full rounded-lg border border-(--chat-line2) bg-(--chat-panel) px-3 py-2.5 text-base text-(--chat-text)'\nconst primaryClass =\n 'w-full rounded-lg bg-(--chat-text) px-4 py-2.5 text-sm font-medium text-(--chat-bg) disabled:opacity-50'\nconst quietClass =\n 'rounded-lg border border-(--chat-line2) bg-(--chat-panel) px-3 py-2 text-sm font-medium text-(--chat-text)'\n\nfunction Field({ label, ...input }: { label: string } & InputHTMLAttributes<HTMLInputElement>) {\n return (\n <label className=\"block\">\n <span className=\"text-sm font-medium text-(--chat-dim)\">{label}</span>\n <input {...input} className={fieldClass} />\n </label>\n )\n}\n\nfunction Card({ children }: { children: ReactNode }) {\n return (\n <div className=\"golem-auth flex h-full w-full items-center justify-center bg-(--chat-bg) p-4 text-(--chat-text)\">\n <div className=\"w-full max-w-sm rounded-xl border border-(--chat-line) bg-(--chat-panel) p-5 sm:p-6\">\n {children}\n </div>\n </div>\n )\n}\n\n/**\n * Sign in, sign up, and the one-time-code detour. One card with one error line: whatever the\n * adapter refused is what the reader reads.\n */\nfunction SignInScreen({ config, adapters }: { config: AuthConfig; adapters: AuthAdapters }) {\n const invite = useInviteToken(adapters.navigation)\n const signUpOffered = config.allowSignUp && !config.inviteOnly\n const signUpReachable = Boolean(invite) || signUpOffered\n\n // `null` means the reader has not chosen: an invite link opens on sign-up, anything else on sign-in.\n const [chose, setChose] = useState<boolean | null>(null)\n const [name, setName] = useState('')\n const [email, setEmail] = useState('')\n const [secret, setSecret] = useState('')\n const [codeSent, setCodeSent] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n\n const creating = chose ?? Boolean(invite)\n const creatingNow = creating && signUpReachable\n const codeStage = config.mode === 'code' && !creatingNow && codeSent\n\n const run = async (event: FormEvent, action: () => Promise<unknown>) => {\n event.preventDefault()\n setBusy(true)\n setError(null)\n try {\n await action()\n } catch (failure) {\n setError(messageOf(failure))\n } finally {\n setBusy(false)\n }\n }\n\n const submit = (event: FormEvent) =>\n run(event, async () => {\n if (creatingNow) {\n await adapters.identity.signUp({\n name,\n email,\n ...(config.mode === 'password' ? { password: secret } : {}),\n ...(invite ? { invite } : {}),\n })\n return\n }\n if (config.mode === 'password') {\n await adapters.identity.signIn(email, secret)\n return\n }\n if (!codeSent) {\n await adapters.identity.requestCode(email)\n setCodeSent(true)\n return\n }\n await adapters.identity.verifyCode(email, secret)\n })\n\n const title = creatingNow ? config.copy.signUpTitle : config.copy.signInTitle\n\n return (\n <Card>\n <h1\n data-golem-auth-screen={creatingNow ? 'sign-up' : 'sign-in'}\n className=\"text-lg font-semibold\"\n >\n {title}\n </h1>\n <p className=\"mt-1 text-sm text-(--chat-dim)\">\n {invite && creatingNow\n ? `You have been invited to ${config.workspaceName}.`\n : config.workspaceName}\n </p>\n {/* The hint is about getting back in, so it stays off the sign-up form. */}\n {config.copy.hint && !creatingNow && (\n <p className=\"mt-3 text-xs text-(--chat-dim)\">{config.copy.hint}</p>\n )}\n\n <form className=\"mt-4 space-y-3\" onSubmit={submit}>\n {creatingNow && (\n <Field\n label=\"Your name\"\n value={name}\n autoComplete=\"name\"\n onChange={(event) => setName(event.target.value)}\n />\n )}\n <Field\n label=\"Email\"\n type=\"email\"\n value={email}\n autoComplete=\"email\"\n readOnly={codeStage}\n onChange={(event) => setEmail(event.target.value)}\n />\n {(creatingNow ? config.mode === 'password' : config.mode === 'password' || codeStage) && (\n <Field\n label={config.mode === 'password' ? 'Password' : 'Six-digit code'}\n type={config.mode === 'password' ? 'password' : 'text'}\n value={secret}\n autoComplete={config.mode === 'password' ? 'current-password' : 'one-time-code'}\n inputMode={config.mode === 'code' ? 'numeric' : undefined}\n onChange={(event) => setSecret(event.target.value)}\n />\n )}\n\n {codeStage && <p className=\"text-xs text-(--chat-dim)\">We sent a code to {email}.</p>}\n {error && (\n <p role=\"alert\" className=\"rounded-lg border border-(--chat-danger) px-3 py-2 text-sm text-(--chat-danger)\">\n {error}\n </p>\n )}\n\n <button type=\"submit\" disabled={busy} className={primaryClass}>\n {config.mode === 'code' && !creatingNow && !codeSent\n ? 'Email me a code'\n : config.copy.submit}\n </button>\n </form>\n\n {signUpOffered && !invite && (\n <button\n type=\"button\"\n onClick={() => {\n setChose(!creating)\n setError(null)\n }}\n className=\"mt-3 w-full text-sm text-(--chat-dim) underline\"\n >\n {creating ? 'I already have an account' : 'Create an account'}\n </button>\n )}\n {config.inviteOnly && !invite && (\n <p className=\"mt-3 text-xs text-(--chat-dim)\">\n {config.workspaceName} is invite only. Ask someone inside for a link.\n </p>\n )}\n </Card>\n )\n}\n\nfunction useMembers(identity: IdentityAdapter, user: User | null) {\n const [members, setMembers] = useState<User[]>([])\n const reload = useCallback(() => {\n void identity.listMembers().then(setMembers)\n }, [identity])\n\n // Re-read whenever the signed-in person changes: a different role sees a different list.\n useEffect(reload, [reload, user])\n\n return { members, reload }\n}\n\nfunction MembersScreen({\n config,\n adapters,\n user,\n}: {\n config: AuthConfig\n adapters: AuthAdapters\n user: User\n}) {\n const { members, reload } = useMembers(adapters.identity, user)\n const manages = canManage(config, user)\n const [inviteRole, setInviteRole] = useState(config.roles[0]!.id)\n const [inviteUrl, setInviteUrl] = useState<string | null>(null)\n const [copied, setCopied] = useState(false)\n\n const mint = async () => {\n const url = await adapters.identity.invite(inviteRole)\n setInviteUrl(url)\n setCopied(false)\n // Clipboard access is a permission and a browser feature, so the URL is on screen either way.\n try {\n await navigator.clipboard?.writeText(url)\n setCopied(true)\n } catch {\n setCopied(false)\n }\n }\n\n return (\n <div\n data-golem-component=\"Auth\"\n className=\"golem-auth mx-auto w-full max-w-3xl px-4 py-5 text-(--chat-text) sm:px-6 sm:py-7\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <h1 className=\"text-xl font-semibold tracking-tight sm:text-2xl\">\n {config.copy.membersTitle}\n </h1>\n <p className=\"mt-1 text-sm text-(--chat-dim)\">\n Everyone who can open {config.workspaceName}.\n </p>\n </div>\n {manages && (\n <div className=\"flex shrink-0 flex-wrap items-center gap-2\">\n <select\n aria-label=\"Role for the invite\"\n value={inviteRole}\n onChange={(event) => setInviteRole(event.target.value)}\n className=\"rounded-lg border border-(--chat-line2) bg-(--chat-panel) px-2 py-2 text-sm text-(--chat-text)\"\n >\n {config.roles.map((role) => (\n <option key={role.id} value={role.id}>\n {role.label}\n </option>\n ))}\n </select>\n <button type=\"button\" onClick={() => void mint()} className={quietClass}>\n Invite by link\n </button>\n </div>\n )}\n </div>\n\n {inviteUrl && (\n <div className=\"mt-4 rounded-xl border border-(--chat-line) bg-(--chat-panel) p-4\">\n <p className=\"text-sm font-medium\">\n Send this link. {copied ? 'It is on your clipboard.' : 'Copy it from here.'}\n </p>\n <input\n readOnly\n aria-label=\"Invite link\"\n value={inviteUrl}\n onFocus={(event) => event.target.select()}\n className={`${fieldClass} font-mono text-xs`}\n />\n </div>\n )}\n\n <ul className=\"mt-6 space-y-3\">\n {members.map((member) => (\n <li\n key={member.id}\n data-golem-auth-member={member.id}\n className=\"flex flex-wrap items-center gap-3 rounded-xl border border-(--chat-line) bg-(--chat-panel) p-4\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium\">{member.name}</p>\n <p className=\"truncate text-xs text-(--chat-dim)\">{member.email}</p>\n </div>\n {manages ? (\n <select\n aria-label={`Role for ${member.name}`}\n value={roleFor(config, member)?.id ?? config.roles[0]!.id}\n onChange={(event) => {\n void adapters.identity.setRole(member.id, event.target.value).then(reload)\n }}\n className=\"rounded-lg border border-(--chat-line2) bg-(--chat-panel) px-2 py-1.5 text-sm text-(--chat-text)\"\n >\n {config.roles.map((role) => (\n <option key={role.id} value={role.id}>\n {role.label}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"shrink-0 rounded-full border border-(--chat-line2) bg-(--chat-panel2) px-2 py-0.5 text-xs font-medium text-(--chat-text)\">\n {labelForRole(config, member)}\n </span>\n )}\n {/* Removing yourself is how a person locks themselves out, so that button is not drawn. */}\n {manages && member.id !== user.id && (\n <button\n type=\"button\"\n aria-label={`Remove ${member.name}`}\n onClick={() => {\n void adapters.identity.removeMember(member.id).then(reload)\n }}\n className=\"shrink-0 rounded-lg border border-(--chat-line2) px-3 py-1.5 text-sm text-(--chat-danger)\"\n >\n Remove\n </button>\n )}\n </li>\n ))}\n </ul>\n </div>\n )\n}\n\nfunction AccountMenuPanel({ config, adapters }: GolemProps<AuthConfig, AuthAdapters>) {\n const { user } = useSession(adapters.identity)\n const [open, setOpen] = useState(false)\n\n if (!user) return null\n\n return (\n <div data-golem-component=\"Auth.AccountMenu\" className=\"golem-auth relative shrink-0 text-(--chat-text)\">\n {/* Avatar only: the bar carries icons, and the name is in the popover. */}\n <button\n type=\"button\"\n aria-expanded={open}\n aria-label={user.name}\n title={user.name}\n onClick={() => setOpen(!open)}\n className=\"flex size-8 items-center justify-center rounded-full bg-(--chat-text) text-[11px] font-semibold text-(--chat-bg)\"\n >\n {initialsOf(user.name)}\n </button>\n {open && (\n <div className=\"absolute right-0 z-10 mt-2 w-56 rounded-xl border border-(--chat-line2) bg-(--chat-panel) p-3 shadow-lg\">\n <p className=\"truncate text-sm font-medium\">{user.name}</p>\n <p className=\"truncate text-xs text-(--chat-dim)\">{user.email}</p>\n <p className=\"mt-1 text-xs text-(--chat-dim)\">{labelForRole(config, user)}</p>\n <button\n type=\"button\"\n onClick={() => {\n setOpen(false)\n void adapters.identity.signOut()\n }}\n className={`mt-3 w-full ${quietClass}`}\n >\n Sign out\n </button>\n </div>\n )}\n </div>\n )\n}\n\nfunction initialsOf(name: string): string {\n return name\n .split(/\\s+/)\n .slice(0, 2)\n .map((part) => part[0]?.toUpperCase() ?? '')\n .join('')\n}\n\nfunction DeniedCard({\n config,\n adapters,\n user,\n roles,\n}: {\n config: AuthConfig\n adapters: AuthAdapters\n user: User\n roles: string[]\n}) {\n const allowed = roles\n .map((id) => config.roles.find((role) => role.id === id)?.label ?? id)\n .join(', ')\n\n return (\n <Card>\n <div role=\"alert\" data-golem-auth-denied=\"true\">\n <h1 className=\"text-lg font-semibold\">{config.copy.denied}</h1>\n <p className=\"mt-2 text-sm text-(--chat-dim)\">\n You are signed in as {user.name} ({labelForRole(config, user)}). This screen is for:{' '}\n {allowed}.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void adapters.identity.signOut()}\n className={`mt-4 w-full ${quietClass}`}\n >\n Sign out\n </button>\n </Card>\n )\n}\n\nfunction AuthScreen({ config, adapters }: GolemProps<AuthConfig, AuthAdapters>) {\n const { user, ready } = useSession(adapters.identity)\n if (!ready) return null\n if (!user) return <SignInScreen config={config} adapters={adapters} />\n return <MembersScreen config={config} adapters={adapters} user={user} />\n}\n\nfunction AuthGuard({\n config,\n adapters,\n roles = [],\n children,\n}: GolemProps<AuthConfig, AuthAdapters, AuthGuardSlots>) {\n const { user, ready } = useSession(adapters.identity)\n if (!ready) return null\n if (!user) return <SignInScreen config={config} adapters={adapters} />\n if (roles.length > 0 && !roles.some((role) => user.roles.includes(role))) {\n return <DeniedCard config={config} adapters={adapters} user={user} roles={roles} />\n }\n return <>{children}</>\n}\n\nconst AuthComponent = defineComponent<typeof authConfigSchema, AuthAdapters>({\n name: 'Auth',\n schema: authConfigSchema,\n render: AuthScreen,\n})\n\nconst Guard = defineComponent<typeof authConfigSchema, AuthAdapters, AuthGuardSlots>({\n name: 'Auth.Guard',\n schema: authConfigSchema,\n render: AuthGuard,\n})\n\nconst AccountMenu = defineComponent<typeof authConfigSchema, AuthAdapters>({\n name: 'Auth.AccountMenu',\n schema: authConfigSchema,\n render: AccountMenuPanel,\n})\n\n/**\n * Everything an app needs around identity. `Auth` is the screen — sign in when nobody is signed in,\n * the member list when someone is; `Auth.Guard` wraps what only certain roles may see; and\n * `Auth.AccountMenu` is the name-and-sign-out control a top bar hosts.\n */\nexport const Auth = Object.assign(AuthComponent, { Guard, AccountMenu })\n","import { Fragment, type ReactNode } from 'react'\n\n/**\n * The subset of markdown this kit writes: headings, fenced code, pipe tables, bullet and numbered\n * lists, and inline code / bold / italic / links. Everything else falls through as text. Three\n * readers share it — a chat bubble an agent is streaming into, the prose of a component's docs\n * page, and the body of a Report.\n *\n * It builds React nodes rather than an HTML string, so a message that arrives mid-stream with a\n * half-written `**` or an unclosed fence renders as the plain characters it currently is, and no\n * message can inject markup.\n */\n\nconst INLINE = /(`[^`]+`|\\*\\*[^*]+\\*\\*|\\*[^*\\n]+\\*|\\[[^\\]\\n]+\\]\\([^)\\s]+\\))/g\n\n/** Turns a non-http href into one worth linking, or `undefined` to leave it as text. */\nexport type LinkResolver = (href: string) => string | undefined\n\nfunction inlineNodes(text: string, keyPrefix: string, resolve?: LinkResolver): ReactNode[] {\n return text.split(INLINE).map((piece, index) => {\n const key = `${keyPrefix}-${index}`\n if (piece.startsWith('`') && piece.endsWith('`') && piece.length > 1) {\n return (\n <code\n key={key}\n className=\"rounded bg-black/5 dark:bg-white/10 px-1 py-0.5 font-mono text-[0.9em]\"\n >\n {piece.slice(1, -1)}\n </code>\n )\n }\n // Bold and italic recurse, so `**a `b`**` is bold text with code inside rather than backticks\n // on the screen. The inner text can hold no further `*`, so the recursion is one level deep.\n if (piece.startsWith('**') && piece.endsWith('**') && piece.length > 3) {\n return <strong key={key}>{inlineNodes(piece.slice(2, -2), key, resolve)}</strong>\n }\n if (piece.startsWith('*') && piece.endsWith('*') && piece.length > 2) {\n return <em key={key}>{inlineNodes(piece.slice(1, -1), key, resolve)}</em>\n }\n const link = /^\\[([^\\]]+)\\]\\(([^)\\s]+)\\)$/.exec(piece)\n if (link) {\n // Agent-authored URLs are untrusted; only http(s) becomes a link, anything else stays text\n // unless the reader's resolver claims it (a brain resolving a relative path).\n const external = /^https?:\\/\\//i.test(link[2]!)\n const href = external ? link[2]! : resolve?.(link[2]!)\n if (!href) return <Fragment key={key}>{piece}</Fragment>\n return (\n <a\n key={key}\n href={href}\n {...(external ? { target: '_blank', rel: 'noreferrer noopener' } : {})}\n className=\"underline\"\n >\n {link[1]}\n </a>\n )\n }\n return <Fragment key={key}>{piece}</Fragment>\n })\n}\n\ntype Block =\n | { kind: 'code'; lines: string[] }\n | { kind: 'heading'; level: number; text: string }\n | { kind: 'table'; headings: string[]; rows: string[][] }\n | { kind: 'list'; ordered: boolean; items: string[] }\n | { kind: 'para'; lines: string[] }\n\n/**\n * A heading's slug, so something outside this file can find it — a report's contents list scrolls\n * to `[data-golem-heading=\"closed-today\"]`. It is a data attribute rather than an `id` because two\n * documents on one page would otherwise mint the same id twice.\n */\nexport function headingSlug(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n}\n\n/** `| a | b |` split into its cells, with the outer pipes dropped. */\nconst cells = (line: string) =>\n line\n .trim()\n .replace(/^\\||\\|$/g, '')\n .split('|')\n .map((cell) => cell.trim())\n\n/** The `|---|:--:|` line under a table's headings. It is what tells a table from a line of prose. */\nconst isTableRule = (line: string | undefined) =>\n line !== undefined && /^\\s*\\|?\\s*:?-{2,}:?\\s*(\\|\\s*:?-{2,}:?\\s*)*\\|?\\s*$/.test(line)\n\nfunction blocksOf(text: string): Block[] {\n const blocks: Block[] = []\n let fence: Block | null = null\n // A blank line ends whatever block was open, which is what tells a wrapped bullet from the\n // paragraph after the list.\n let blank = false\n\n const lines = text.split('\\n')\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index]!\n if (line.trimStart().startsWith('```')) {\n if (fence) fence = null\n else {\n fence = { kind: 'code', lines: [] }\n blocks.push(fence)\n }\n blank = false\n continue\n }\n if (fence) {\n fence.lines.push(line)\n continue\n }\n\n if (!line.trim()) {\n blank = true\n continue\n }\n\n const heading = /^(#{1,6})\\s+(.*)$/.exec(line)\n if (heading) {\n blocks.push({ kind: 'heading', level: heading[1]!.length, text: heading[2]!.trim() })\n blank = true\n continue\n }\n\n // A row of cells is only a table when the next line is the rule; without it, a sentence with a\n // pipe in it is still a sentence.\n if (line.includes('|') && isTableRule(lines[index + 1])) {\n const table: Block = { kind: 'table', headings: cells(line), rows: [] }\n index += 1\n while (index + 1 < lines.length && lines[index + 1]!.includes('|')) {\n index += 1\n table.rows.push(cells(lines[index]!))\n }\n blocks.push(table)\n blank = true\n continue\n }\n\n const bullet = /^\\s*[-*]\\s+(.*)$/.exec(line)\n const numbered = /^\\s*\\d+[.)]\\s+(.*)$/.exec(line)\n const last = blocks[blocks.length - 1]\n\n if (bullet ?? numbered) {\n const ordered = !bullet\n const item = (bullet ?? numbered)![1]!\n if (!blank && last?.kind === 'list' && last.ordered === ordered) last.items.push(item)\n else blocks.push({ kind: 'list', ordered, items: [item] })\n blank = false\n continue\n }\n // An indented line under a bullet is that bullet wrapping, not a paragraph of its own.\n if (!blank && last?.kind === 'list') {\n const open = last.items.length - 1\n last.items[open] = `${last.items[open]} ${line.trim()}`\n continue\n }\n if (!blank && last?.kind === 'para') last.lines.push(line)\n else blocks.push({ kind: 'para', lines: [line] })\n blank = false\n }\n\n return blocks\n}\n\n/** Sized in `em`, so one container font-size sets the whole scale — a bubble's or a report's. */\nconst HEADING_SIZES = [\n 'text-[1.5em]',\n 'text-[1.3em]',\n 'text-[1.15em]',\n 'text-[1.05em]',\n 'text-[1em]',\n 'text-[1em]',\n]\n\n/**\n * `hardWraps` is the difference between the readers. A chat bubble keeps a single newline the\n * person typed; a docs paragraph wrapped at the source's column width must not show those wraps.\n */\nexport function Markdown({\n text,\n hardWraps = true,\n resolveLink,\n}: {\n text: string\n hardWraps?: boolean\n resolveLink?: LinkResolver\n}) {\n const inline = (piece: string, key: string) => inlineNodes(piece, key, resolveLink)\n return (\n <>\n {blocksOf(text).map((block, index) => {\n if (block.kind === 'code') {\n return (\n <pre\n key={index}\n className=\"my-1 overflow-x-auto rounded-md bg-neutral-900 p-2 font-mono text-xs text-neutral-100\"\n >\n <code>{block.lines.join('\\n')}</code>\n </pre>\n )\n }\n if (block.kind === 'heading') {\n const Tag = `h${block.level}` as 'h1'\n return (\n <Tag\n key={index}\n data-golem-heading={headingSlug(block.text)}\n className={`mt-4 mb-1 font-semibold first:mt-0 ${HEADING_SIZES[block.level - 1]} break-after-avoid`}\n >\n {inline(block.text, String(index))}\n </Tag>\n )\n }\n if (block.kind === 'table') {\n return (\n // Its own strip, so a wide table never makes the page or the printed sheet scroll.\n <div key={index} className=\"my-2 overflow-x-auto\">\n <table className=\"w-full border-collapse text-left text-[0.95em]\">\n <thead>\n <tr className=\"border-b border-neutral-300 dark:border-neutral-700\">\n {block.headings.map((cell, cellIndex) => (\n <th key={cellIndex} className=\"py-1.5 pr-4 font-semibold\">\n {inline(cell, `${index}-h-${cellIndex}`)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, rowIndex) => (\n <tr\n key={rowIndex}\n className=\"border-b border-neutral-200 dark:border-neutral-800 align-top last:border-0\"\n >\n {row.map((cell, cellIndex) => (\n <td key={cellIndex} className=\"py-1.5 pr-4\">\n {inline(cell, `${index}-${rowIndex}-${cellIndex}`)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n }\n if (block.kind === 'list') {\n const List = block.ordered ? 'ol' : 'ul'\n return (\n <List\n key={index}\n className={`my-1 ml-4 space-y-0.5 ${block.ordered ? 'list-decimal' : 'list-disc'}`}\n >\n {block.items.map((item, i) => (\n <li key={i}>{inline(item, `${index}-${i}`)}</li>\n ))}\n </List>\n )\n }\n return (\n <p\n key={index}\n className={`my-1 first:mt-0 last:mb-0 ${hardWraps ? 'whitespace-pre-wrap' : ''}`}\n >\n {inline(block.lines.join(hardWraps ? '\\n' : ' '), String(index))}\n </p>\n )\n })}\n </>\n )\n}\n","import { useEffect, useState, type RefObject } from 'react'\n\nconst windowWidth = () =>\n typeof window === 'undefined' ? Number.POSITIVE_INFINITY : window.innerWidth\n\n/**\n * The breakpoint is a config value, not a stylesheet constant, so the layout cannot be picked by a\n * CSS media query — the frame measures itself instead. Measuring the element rather than the\n * viewport means an embedded Shell in a narrow column gets the mobile layout too.\n *\n * Falls back to the viewport where there is no ResizeObserver, or before the first measurement\n * (jsdom, server rendering, the first paint).\n */\nexport function useContainerWidth(ref: RefObject<HTMLElement | null>): number {\n const [measured, setMeasured] = useState(0)\n const [viewport, setViewport] = useState(windowWidth)\n\n useEffect(() => {\n const element = ref.current\n if (element && typeof ResizeObserver !== 'undefined') {\n const observer = new ResizeObserver((entries) => {\n setMeasured(entries[0]?.contentRect.width ?? 0)\n })\n observer.observe(element)\n return () => observer.disconnect()\n }\n return undefined\n }, [ref])\n\n useEffect(() => {\n const onResize = () => setViewport(windowWidth())\n onResize()\n window.addEventListener('resize', onResize)\n return () => window.removeEventListener('resize', onResize)\n }, [])\n\n return measured > 0 ? measured : viewport\n}\n","import { z } from 'zod'\n\nexport const brainConfigSchema = z\n .object({\n title: z\n .string()\n .min(1)\n .default('Knowledge')\n .describe('Heading over the folder tree. Name the brain as the reader knows it.'),\n openLocation: z\n .string()\n .regex(/^[^#\\s]+(#L\\d+(-L\\d+)?)?$/, 'a path, optionally followed by #L<start>-L<end>')\n .optional()\n .describe(\n 'A source location, `path#L<start>-L<end>`, to open and highlight. Change it and the reader opens the file and scrolls the range into view; omit it and the root index.md opens.',\n ),\n })\n .strict()\n\nexport type BrainConfig = z.output<typeof brainConfigSchema>\nexport type BrainConfigInput = z.input<typeof brainConfigSchema>\n","import { useEffect, useLayoutEffect, useRef, useState, type ReactNode } from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport { parseLocation, type BrainAdapter, type BrainEntry, type BrainHit } from '../../adapters'\nimport { Markdown } from '../../lib/markdown'\nimport { useContainerWidth } from '../../lib/use-container-width'\nimport { brainConfigSchema, type BrainConfig } from './Brain.config'\n\nexport interface BrainAdapters {\n brain: BrainAdapter\n}\n\nexport interface BrainSlots {\n /** Rendered at the right-hand end of the document header: an Edit button, a share button. */\n toolbar?: ReactNode\n}\n\n/** Below this container width the reader is one screen at a time: the list, then the document. */\nconst PHONE = 768\nconst BRAIN_LINK = '#brain:'\nconst ROOT = 'index.md'\n\n/** One open document on the stack. A range comes from a citation or a search hit. */\ninterface Frame {\n path: string\n start?: number\n end?: number\n}\n\nconst same = (a: Frame | undefined, b: Frame) =>\n a?.path === b.path && a.start === b.start && a.end === b.end\nconst locationOf = (f: Frame) =>\n f.start === undefined ? f.path : `${f.path}#L${f.start}-L${f.end ?? f.start}`\n\nconst name = (path: string) => path.split('/').pop()!.replace(/\\.md$/, '')\n\n/** `../suppliers.md` seen from `workshop/fork-service.md` → `suppliers.md`. */\nfunction resolvePath(from: string, relative: string): string {\n const parts = from.split('/').slice(0, -1)\n for (const part of relative.split('/')) {\n if (part === '..') parts.pop()\n else if (part !== '.' && part) parts.push(part)\n }\n return parts.join('/')\n}\n\n/** The `key: value` lines of the front matter, and the line the body starts on. */\nfunction frontMatter(lines: string[]): { meta: Record<string, string>; body: number } {\n if (lines[0] !== '---') return { meta: {}, body: 0 }\n const close = lines.indexOf('---', 1)\n if (close < 0) return { meta: {}, body: 0 }\n const meta: Record<string, string> = {}\n for (const line of lines.slice(1, close)) {\n const m = /^(\\w[\\w-]*):\\s*(.*)$/.exec(line)\n if (m) meta[m[1]!] = m[2]!.replace(/^[\"']|[\"']$/g, '')\n }\n return { meta, body: close + 1 }\n}\n\nfunction Skeleton({ rows }: { rows: number }) {\n return (\n <div aria-hidden=\"true\" className=\"space-y-2 py-1\">\n {Array.from({ length: rows }, (_, i) => (\n <div\n key={i}\n className=\"h-3 animate-pulse rounded bg-(--chat-panel2)\"\n style={{ width: `${[85, 60, 72, 45][i % 4]}%` }}\n />\n ))}\n </div>\n )\n}\n\nfunction Tree({\n dir,\n adapter,\n open,\n onOpen,\n version,\n}: {\n dir?: string\n adapter: BrainAdapter\n open?: string\n onOpen: (path: string) => void\n version: number\n}) {\n const [entries, setEntries] = useState<BrainEntry[] | null>(null)\n const [collapsed, setCollapsed] = useState<Record<string, boolean>>({})\n\n useEffect(() => {\n let live = true\n void adapter.list(dir).then((next) => live && setEntries(next))\n return () => {\n live = false\n }\n }, [adapter, dir, version])\n\n if (!entries) return dir ? null : <Skeleton rows={4} />\n if (!entries.length && !dir)\n return <p className=\"px-2 py-3 text-sm text-(--chat-faint)\">Nothing written here yet.</p>\n\n return (\n <ul className={dir ? 'ml-2.5 border-l border-(--chat-line) pl-1' : ''}>\n {entries.map((entry) =>\n entry.kind === 'dir' ? (\n <li key={entry.path}>\n <button\n type=\"button\"\n onClick={() => setCollapsed((c) => ({ ...c, [entry.path]: !c[entry.path] }))}\n className=\"flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-left text-sm text-(--chat-dim) hover:bg-(--chat-panel2)\"\n >\n <span aria-hidden=\"true\" className=\"w-3 text-center text-[10px] text-(--chat-faint)\">\n {collapsed[entry.path] ? '▸' : '▾'}\n </span>\n <span className=\"truncate\">{name(entry.path)}</span>\n </button>\n {!collapsed[entry.path] && (\n <Tree\n dir={entry.path}\n adapter={adapter}\n open={open}\n onOpen={onOpen}\n version={version}\n />\n )}\n </li>\n ) : (\n <li key={entry.path}>\n <button\n type=\"button\"\n data-golem-brain-file={entry.path}\n aria-current={entry.path === open ? 'page' : undefined}\n onClick={() => onOpen(entry.path)}\n className={`flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-left text-sm hover:bg-(--chat-panel2) ${\n entry.path === open\n ? 'bg-(--chat-panel2) font-medium text-(--chat-text)'\n : 'text-(--chat-dim)'\n }`}\n >\n <span aria-hidden=\"true\" className=\"w-3 text-center text-[10px] text-(--chat-faint)\">\n {entry.path === open ? '●' : '·'}\n </span>\n <span className=\"truncate\">{name(entry.path)}</span>\n </button>\n </li>\n ),\n )}\n </ul>\n )\n}\n\nfunction Hits({\n query,\n adapter,\n onOpen,\n}: {\n query: string\n adapter: BrainAdapter\n onOpen: (frame: Frame) => void\n}) {\n // Hits are kept with the query they answer, so a new query shows the skeleton, not stale hits.\n const [found, setFound] = useState<{ query: string; hits: BrainHit[] }>()\n const hits = found?.query === query ? found.hits : null\n useEffect(() => {\n let live = true\n const timer = setTimeout(\n () => void adapter.search(query).then((hits) => live && setFound({ query, hits })),\n 150,\n )\n return () => {\n live = false\n clearTimeout(timer)\n }\n }, [adapter, query])\n\n if (!hits) return <Skeleton rows={3} />\n if (!hits.length)\n return (\n <p className=\"px-2 py-3 text-sm text-(--chat-faint)\">No line mentions “{query}”.</p>\n )\n return (\n <ul role=\"list\" data-golem-brain-hits=\"\">\n {hits.map((hit) => (\n <li key={`${hit.path}:${hit.line}`}>\n <button\n type=\"button\"\n onClick={() => onOpen({ path: hit.path, start: hit.line, end: hit.line })}\n className=\"w-full rounded-md px-2 py-1.5 text-left hover:bg-(--chat-panel2)\"\n >\n <span className=\"block truncate text-sm text-(--chat-text)\">{hit.excerpt}</span>\n <span className=\"block truncate font-mono text-[11px] text-(--chat-faint)\">\n {hit.path}:{hit.line}\n </span>\n </button>\n </li>\n ))}\n </ul>\n )\n}\n\n/**\n * The file in three runs of markdown — before, the cited range, after — so the range wears a\n * highlight at exactly the lines cited. Line numbers count from line 1 of the file, front matter\n * included, which the reader shows as a chip and a subtitle rather than as YAML.\n */\nfunction Reader({ text, frame }: { text: string; frame: Frame }) {\n const marked = useRef<HTMLDivElement>(null)\n const { path, start, end } = frame\n // A relative link in the bundle opens that file in this reader; the click handler on the article\n // reads the path back off the href.\n const resolveLink = (href: string) =>\n /^[a-z]+:/i.test(href) ? undefined : BRAIN_LINK + resolvePath(path, href.split('#')[0]!)\n const lines = text.split('\\n')\n const { meta, body } = frontMatter(lines)\n\n // Scrolls the reader's own box, not the page: a docs page hosting this component stays put.\n useEffect(() => {\n const mark = marked.current\n const box = mark?.closest('article')\n if (!mark || !box) return\n const offset = mark.getBoundingClientRect().top - box.getBoundingClientRect().top\n box.scrollTop += offset - box.clientHeight / 2 + mark.clientHeight / 2\n }, [text, start, end])\n\n const run = (from: number, to: number, key: string) =>\n to > from ? (\n <div key={key}>\n <Markdown\n text={lines.slice(from, to).join('\\n')}\n hardWraps={false}\n resolveLink={resolveLink}\n />\n </div>\n ) : null\n\n const head = (meta.type || meta.description) && (\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">\n {meta.type && (\n <span\n data-golem-brain-type={meta.type}\n className=\"rounded-full border border-(--chat-line2) bg-(--chat-panel2) px-2 py-0.5 font-mono text-[11px] text-(--chat-dim)\"\n >\n {meta.type}\n </span>\n )}\n {meta.description && <span className=\"text-sm text-(--chat-dim)\">{meta.description}</span>}\n </div>\n )\n\n if (start === undefined || end === undefined)\n return (\n <>\n {head}\n {run(body, lines.length, 'all')}\n </>\n )\n const from = Math.max(body, start - 1)\n const to = Math.min(lines.length, end)\n return (\n <>\n {head}\n {run(body, from, 'before')}\n <div\n ref={marked}\n data-golem-brain-highlight={`L${start}-L${end}`}\n className=\"-mx-3 my-1 rounded-md border-l-4 border-(--chat-accent) bg-(--chat-accent-soft) px-3 py-1\"\n >\n <Markdown\n text={lines.slice(from, to).join('\\n')}\n hardWraps={false}\n resolveLink={resolveLink}\n />\n </div>\n {run(to, lines.length, 'after')}\n </>\n )\n}\n\nconst BackIcon = () => (\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M10 3L5 8l5 5\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" />\n </svg>\n)\n\nconst SearchIcon = () => (\n <svg\n aria-hidden=\"true\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n className=\"pointer-events-none absolute top-1/2 left-2.5 -translate-y-1/2 text-(--chat-faint)\"\n >\n <circle cx=\"7\" cy=\"7\" r=\"4.5\" stroke=\"currentColor\" strokeWidth=\"1.6\" />\n <path d=\"M10.5 10.5L14 14\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" />\n </svg>\n)\n\nfunction BrainPanel({\n config,\n adapters,\n toolbar,\n}: GolemProps<BrainConfig, BrainAdapters, BrainSlots>) {\n const root = useRef<HTMLDivElement>(null)\n const article = useRef<HTMLElement>(null)\n const phone = useContainerWidth(root) < PHONE\n\n // The documents opened so far, newest last. Empty on a phone is the list screen; on a desktop\n // it is the root index. Scroll offsets are kept beside it so back lands where the reader was.\n const [stack, setStack] = useState<Frame[]>(() =>\n config.openLocation ? [parseLocation(config.openLocation)] : [],\n )\n const scrolls = useRef<number[]>([])\n const [version, setVersion] = useState(0)\n // The loaded document is kept with what it answers, so a new path shows the skeleton first.\n const [doc, setDoc] = useState<{ key: string; text?: string; error?: string }>()\n const [query, setQuery] = useState('')\n\n // A new location wins over whatever the reader opened: state adjusted during render, so the\n // reader never paints the old file first.\n const [seenLocation, setSeenLocation] = useState(config.openLocation)\n if (seenLocation !== config.openLocation) {\n setSeenLocation(config.openLocation)\n // The app echoing back what `open` just reported is the frame already on top: no push.\n const next = config.openLocation && parseLocation(config.openLocation)\n if (next && !same(stack[stack.length - 1], next)) setStack((s) => [...s, next])\n }\n\n const frame: Frame | undefined = stack[stack.length - 1] ?? (phone ? undefined : { path: ROOT })\n const open = frame?.path\n\n const push = (next: Frame) => {\n scrolls.current[stack.length] = article.current?.scrollTop ?? 0\n setStack((s) => [...s, next])\n adapters.brain.open?.(locationOf(next))\n }\n const back = () => {\n setStack((s) => s.slice(0, -1))\n adapters.brain.open?.(locationOf(stack[stack.length - 2] ?? { path: ROOT }))\n }\n\n useEffect(() => adapters.brain.subscribe(() => setVersion((v) => v + 1)), [adapters.brain])\n\n const key = `${open}@${version}`\n const { text, error } = doc?.key === key ? doc : {}\n useEffect(() => {\n if (!open) return\n let live = true\n const load = open === ROOT ? adapters.brain.index() : adapters.brain.read(open)\n load\n .then((text) => live && setDoc({ key, text }))\n .catch((cause: unknown) => {\n if (live)\n setDoc({\n key,\n error: cause instanceof Error ? cause.message : `Could not open ${open}.`,\n })\n })\n return () => {\n live = false\n }\n }, [adapters.brain, open, key])\n\n // Back restores the offset saved on push; a range does its own scrolling in Reader.\n const depth = stack.length\n useLayoutEffect(() => {\n if (text !== undefined && article.current && frame?.start === undefined)\n article.current.scrollTop = scrolls.current[depth] ?? 0\n }, [text, depth, frame?.start])\n\n const onArticleClick = (event: React.MouseEvent) => {\n const href = (event.target as HTMLElement).closest('a')?.getAttribute('href') ?? ''\n if (!href.startsWith(BRAIN_LINK)) return\n event.preventDefault()\n push({ path: href.slice(BRAIN_LINK.length) })\n }\n\n const list = (\n <nav\n aria-label={config.title}\n data-golem-brain-list=\"\"\n className={`flex min-h-0 flex-col bg-(--chat-panel) ${\n phone ? 'h-full w-full' : 'w-64 shrink-0 border-r border-(--chat-line)'\n }`}\n >\n <header className=\"shrink-0 space-y-2 border-b border-(--chat-line) px-3 py-2.5\">\n <h2 className=\"truncate text-sm font-semibold text-(--chat-text)\">{config.title}</h2>\n <div className=\"relative\">\n <SearchIcon />\n <input\n type=\"search\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={(event) => event.key === 'Escape' && setQuery('')}\n placeholder=\"Search\"\n aria-label=\"Search\"\n className=\"w-full rounded-lg border border-(--chat-line2) bg-(--chat-bg) py-1.5 pr-2 pl-8 text-sm text-(--chat-text) placeholder:text-(--chat-faint) focus:border-(--chat-accent) focus:outline-none\"\n />\n </div>\n </header>\n <div className=\"min-h-0 flex-1 overflow-y-auto px-1.5 py-1.5\">\n {query.trim() ? (\n <Hits query={query.trim()} adapter={adapters.brain} onOpen={push} />\n ) : (\n <Tree\n adapter={adapters.brain}\n open={open}\n onOpen={(path) => push({ path })}\n version={version}\n />\n )}\n </div>\n </nav>\n )\n\n const document = frame && (\n <section\n data-golem-brain-document={frame.path}\n className={`flex min-h-0 min-w-0 flex-col bg-(--chat-bg) ${\n phone ? 'absolute inset-0 animate-[golem-chat-in_160ms_ease-out]' : 'flex-1'\n }`}\n >\n <header className=\"flex shrink-0 items-center gap-2 border-b border-(--chat-line) bg-(--chat-panel) px-2 py-2\">\n {phone ? (\n <button\n type=\"button\"\n onClick={back}\n aria-label=\"Back\"\n className=\"flex shrink-0 items-center gap-0.5 rounded-md py-1 pr-2 pl-1 text-sm text-(--chat-accent) hover:bg-(--chat-panel2)\"\n >\n <BackIcon />\n {depth > 1 ? name(stack[depth - 2]!.path) : config.title}\n </button>\n ) : null}\n <span className=\"min-w-0 flex-1 truncate px-1 font-mono text-xs text-(--chat-dim)\">\n {frame.path}\n </span>\n {toolbar}\n </header>\n <article\n ref={article}\n className=\"min-h-0 flex-1 overflow-y-auto px-5 py-4 text-[15px] leading-7 md:px-8 md:py-6\"\n onClick={onArticleClick}\n >\n <div className=\"mx-auto max-w-[70ch]\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-(--chat-danger)\">\n {error}\n </p>\n ) : text === undefined ? (\n <Skeleton rows={6} />\n ) : (\n <Reader key={`${depth}:${frame.path}`} text={text} frame={frame} />\n )}\n </div>\n </article>\n </section>\n )\n\n return (\n <div\n ref={root}\n data-golem-component=\"Brain\"\n data-golem-brain-layout={phone ? 'phone' : 'desktop'}\n className=\"golem-chat relative flex h-full min-h-0 w-full overflow-hidden bg-(--chat-bg) text-(--chat-text)\"\n >\n {list}\n {document}\n </div>\n )\n}\n\nexport const Brain = defineComponent<typeof brainConfigSchema, BrainAdapters, BrainSlots>({\n name: 'Brain',\n schema: brainConfigSchema,\n render: BrainPanel,\n})\n","import { z } from 'zod'\n\nexport const chatConfigSchema = z\n .object({\n placeholder: z\n .string()\n .min(1)\n .default('Message the agent…')\n .describe('Ghost text in the empty composer. Say what this agent is good for.'),\n emptyState: z\n .string()\n .min(1)\n .default('No messages yet.')\n .describe('One line shown in place of the message list before the first message exists.'),\n agentName: z\n .string()\n .min(1)\n .default('Agent')\n .describe('Label above each agent bubble, and the name in the thinking indicator.'),\n userName: z\n .string()\n .min(1)\n .default('You')\n .describe('Label above each of the reader’s own bubbles.'),\n markdown: z\n .boolean()\n .default(true)\n .describe(\n 'Whether agent messages render as markdown — bold, lists, links, code. Turn it off for an agent that answers in plain prose, and the text renders verbatim.',\n ),\n showTimestamps: z\n .boolean()\n .default(false)\n .describe('Whether each bubble carries the time it was sent, as HH:MM in UTC.'),\n maxComposerLines: z\n .number()\n .int()\n .min(1)\n .max(20)\n .default(6)\n .describe(\n 'How tall the composer may grow as the reader types. Past it the composer stops growing and scrolls.',\n ),\n })\n .strict()\n\nexport type ChatConfig = z.output<typeof chatConfigSchema>\nexport type ChatConfigInput = z.input<typeof chatConfigSchema>\n","import { useEffect, useLayoutEffect, useRef, useState, type ReactNode } from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { ChatAdapter, ChatAttachment, ChatCommand, ChatMessage } from '../../adapters'\nimport { chatConfigSchema, type ChatConfig } from './Chat.config'\nimport { Markdown } from '../../lib/markdown'\n\nexport interface ChatAdapters {\n chat: ChatAdapter\n}\n\nexport interface ChatSlots {\n /**\n * Replaces the built-in attach button and the staged-chip row above the composer. It is handed\n * `stage`, which is what the composer sends with the next message; `Upload.Picker` is what\n * belongs here, so an attachment comes from a real upload rather than a name and a size.\n */\n attach?: (stage: (attachments: ChatAttachment[]) => void) => ReactNode\n}\n\n/** How close to the bottom still counts as \"reading the newest message\". */\nconst PIN_SLACK = 48\n\n/** A composer line that is a slash command: one line, leading `/`. */\nconst SLASH_LINE = /^\\/[^\\n]*$/\n/** The command stage: one `/` token, no space yet. */\nconst CMD_RE = /^\\/\\S*$/\n/** The argument stage: a complete name, whitespace, then the argument. A newline ends the picker. */\nconst ARG_RE = /^(\\/\\S+)[^\\S\\n]+([^\\n]*)$/\n\ninterface SlashMatch {\n name: string\n description: string\n /** The whole composer value a pick produces. Ends in a space when the command still wants its argument. */\n insert: string\n}\n\n/**\n * What the picker shows for a composer value, after Bridge Commander's `slash.js`. Two stages: the\n * command name, then, for a command with `args`, its values. Everything after the name is one\n * argument matched whole, so a value with spaces stays reachable.\n */\nfunction slashMatches(value: string, commands: ChatCommand[]): SlashMatch[] {\n if (CMD_RE.test(value))\n return commands\n .filter((c) => c.name.startsWith(value))\n .map((c) => ({\n name: c.name,\n description: c.description,\n insert: c.name + (c.args?.length ? ' ' : ''),\n }))\n const m = ARG_RE.exec(value)\n const args = m && commands.find((c) => c.name === m[1])?.args\n if (!m || !args?.length) return []\n const q = m[2]!.toLowerCase()\n return args\n .filter((a) => a.value.toLowerCase().startsWith(q))\n .map((a) => ({ name: a.value, description: a.description, insert: `${m[1]} ${a.value}` }))\n}\n\nfunction useConversation(adapter: ChatAdapter): { messages: ChatMessage[]; loadError: string } {\n const [messages, setMessages] = useState<ChatMessage[]>([])\n const [loadError, setLoadError] = useState<{ adapter: ChatAdapter; message: string } | null>(null)\n\n useEffect(() => {\n let live = true\n let receivedUpdate = false\n const unsubscribe = adapter.subscribe((next) => {\n receivedUpdate = true\n setLoadError(null)\n setMessages(next)\n })\n void adapter\n .history()\n .then((next) => {\n if (live && !receivedUpdate) {\n setLoadError(null)\n setMessages(next)\n }\n })\n .catch((error: unknown) => {\n if (live)\n setLoadError({\n adapter,\n message: error instanceof Error ? error.message : 'Conversation could not be loaded.',\n })\n })\n return () => {\n live = false\n unsubscribe()\n }\n }, [adapter])\n\n return { messages, loadError: loadError?.adapter === adapter ? loadError.message : '' }\n}\n\n/**\n * The message list follows the newest message only while the reader is already at the bottom.\n * Scroll up to read something and the list stays where it was put, however fast the stream runs.\n */\nfunction useStickToBottom(dependency: unknown) {\n const feed = useRef<HTMLDivElement>(null)\n const pinned = useRef(true)\n\n const onScroll = () => {\n const element = feed.current\n if (!element) return\n pinned.current = element.scrollHeight - element.scrollTop - element.clientHeight < PIN_SLACK\n }\n\n useLayoutEffect(() => {\n const element = feed.current\n if (element && pinned.current) element.scrollTop = element.scrollHeight\n }, [dependency])\n\n return { feed, onScroll }\n}\n\n/** Timestamps are read straight off the ISO string, so a bubble reads the same in every timezone. */\nfunction hhmm(at: string): string {\n const match = /T(\\d{2}:\\d{2})/.exec(at)\n return match ? match[1]! : ''\n}\n\nfunction sizeLabel(bytes: number | undefined): string {\n if (bytes == null) return ''\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction AttachmentChips({\n attachments,\n onRemove,\n}: {\n attachments: ChatAttachment[]\n onRemove?: (id: string) => void\n}) {\n return (\n <div className=\"mt-1.5 flex flex-wrap gap-1.5\">\n {attachments.map((file) => (\n <span\n key={file.id}\n data-golem-chat-attachment={file.name}\n className=\"inline-flex max-w-full items-center gap-1 rounded-full border border-(--chat-line2) bg-(--chat-panel2) px-2 py-0.5 text-xs text-(--chat-dim)\"\n >\n <span aria-hidden=\"true\">📄</span>\n <span className=\"truncate\">{file.name}</span>\n {file.size != null && <span className=\"shrink-0 opacity-60\">{sizeLabel(file.size)}</span>}\n {onRemove && (\n <button\n type=\"button\"\n onClick={() => onRemove(file.id)}\n aria-label={`Remove ${file.name}`}\n className=\"shrink-0 opacity-60 hover:opacity-100\"\n >\n ×\n </button>\n )}\n </span>\n ))}\n </div>\n )\n}\n\n/** `notes/tyres.md#L3-L5` → `tyres.md L3-5`: the chip's label. */\nfunction sourceLabel(location: string): string {\n const [path = '', range = ''] = location.split('#')\n return `${path.split('/').pop()}${range ? ` ${range.replace('-L', '-')}` : ''}`\n}\n\nfunction Bubble({\n message,\n config,\n onRetry,\n onOpenSource,\n}: {\n message: ChatMessage\n config: ChatConfig\n onRetry?: (messageId: string) => void\n onOpenSource?: (location: string) => void\n}) {\n const mine = message.role === 'user'\n const [copied, setCopied] = useState(false)\n if (message.role === 'system')\n return (\n <div\n data-golem-chat-message=\"system\"\n className=\"rounded-lg border border-dashed border-(--chat-line2) px-3 py-1.5 text-center font-mono text-xs whitespace-pre-wrap text-(--chat-dim) break-words\"\n >\n {message.text}\n </div>\n )\n const pending = message.delivery === 'pending'\n const failed = message.delivery === 'failed'\n const copy = () => {\n void navigator.clipboard?.writeText(message.text).then(() => {\n setCopied(true)\n setTimeout(() => setCopied(false), 1200)\n })\n }\n return (\n <div className={`flex flex-col ${mine ? 'items-end' : 'items-start'}`}>\n <div\n data-golem-chat-message={message.role}\n data-streaming={message.streaming ? 'true' : undefined}\n data-delivery={message.delivery}\n className={`group relative max-w-[88%] min-w-0 rounded-xl border px-[13px] py-[9px] text-[15px] leading-normal break-words animate-[golem-chat-in_180ms_ease-out] ${\n mine\n ? 'rounded-br-[4px] border-(--chat-user-line) bg-(--chat-user)'\n : 'rounded-bl-[4px] border-(--chat-line) bg-(--chat-panel)'\n } ${pending ? 'opacity-55' : ''} ${failed ? 'border-(--chat-danger)' : ''}`}\n >\n {/* The reader's own text is never markdown: they typed characters, not a document. */}\n {config.markdown && !mine ? (\n <Markdown text={message.text} />\n ) : (\n <p className=\"whitespace-pre-wrap\">{message.text}</p>\n )}\n {message.streaming && message.text && (\n <span className=\"ml-0.5 inline-block h-3.5 w-1.5 translate-y-0.5 animate-pulse bg-current align-baseline\" />\n )}\n {message.attachments?.length ? <AttachmentChips attachments={message.attachments} /> : null}\n {message.sources?.length ? (\n <div className=\"mt-1.5 flex flex-wrap gap-1.5\">\n {message.sources.map((location) => (\n <button\n key={location}\n type=\"button\"\n data-golem-chat-source={location}\n title={location}\n onClick={() => onOpenSource?.(location)}\n className=\"inline-flex max-w-full items-center gap-1 rounded-full border border-(--chat-line2) bg-(--chat-panel2) px-2 py-0.5 font-mono text-[11px] text-(--chat-dim) hover:border-(--chat-accent) hover:text-(--chat-text)\"\n >\n <span aria-hidden=\"true\">§</span>\n <span className=\"truncate\">{sourceLabel(location)}</span>\n </button>\n ))}\n </div>\n ) : null}\n {config.showTimestamps && (\n <span className=\"mt-1 block font-mono text-[11px] text-(--chat-faint)\">\n {mine ? config.userName : config.agentName} · {hhmm(message.at)}\n </span>\n )}\n {/* The copy chip shows on hover (always, faintly, on touch), and confirms with a tick. */}\n {message.text && (\n <button\n type=\"button\"\n onClick={copy}\n title=\"Copy message text\"\n aria-label=\"Copy message text\"\n className={`absolute right-1 bottom-[3px] rounded-md border px-[5px] py-px text-xs leading-none transition-opacity focus-visible:opacity-85 group-hover:opacity-85 hover:opacity-100 [@media(hover:none)]:opacity-60 ${\n copied\n ? 'border-(--chat-ok) text-(--chat-ok) opacity-100'\n : 'border-(--chat-line) bg-(--chat-panel2) text-(--chat-dim) opacity-0'\n }`}\n >\n {copied ? '✓' : '⧉'}\n </button>\n )}\n </div>\n {mine && pending && (\n <span role=\"status\" className=\"mt-0.5 px-1 font-mono text-[11px] text-(--chat-faint)\">\n Sending\n </span>\n )}\n {mine && failed && (\n <span className=\"mt-0.5 flex items-center gap-2 px-1 text-xs text-(--chat-danger)\">\n <span role=\"status\">Not sent</span>\n {onRetry && (\n <button type=\"button\" className=\"underline\" onClick={() => onRetry(message.id)}>\n Retry\n </button>\n )}\n </span>\n )}\n </div>\n )\n}\n\nfunction Thinking({ name, onStop }: { name: string; onStop?: () => Promise<void> }) {\n const [stopping, setStopping] = useState(false)\n return (\n <div\n data-golem-chat-thinking=\"true\"\n role=\"status\"\n className=\"flex w-fit items-center gap-1 rounded-xl rounded-bl-[4px] border border-(--chat-line) bg-(--chat-panel) px-[13px] py-[9px] animate-[golem-chat-in_180ms_ease-out]\"\n >\n {[0, 200, 400].map((delay) => (\n <span\n key={delay}\n aria-hidden=\"true\"\n style={{ animationDelay: `${delay}ms` }}\n className=\"size-1.5 rounded-full bg-(--chat-dim) animate-[golem-chat-blink_1.2s_infinite]\"\n />\n ))}\n <span className=\"ml-1 text-[11px] text-(--chat-faint)\">{name} is thinking…</span>\n {onStop && (\n <button\n type=\"button\"\n disabled={stopping}\n aria-label=\"Stop the agent\"\n onClick={() => {\n setStopping(true)\n void onStop().finally(() => setStopping(false))\n }}\n className=\"ml-2 rounded-full border border-(--chat-line) bg-(--chat-panel2) px-2 py-0.5 text-[11px] font-medium text-(--chat-danger) transition-colors hover:border-(--chat-danger) disabled:opacity-50\"\n >\n Stop\n </button>\n )}\n </div>\n )\n}\n\nfunction ChatPanel({ config, adapters, attach }: GolemProps<ChatConfig, ChatAdapters, ChatSlots>) {\n const { messages, loadError } = useConversation(adapters.chat)\n const [draft, setDraft] = useState('')\n const [staged, setStaged] = useState<ChatAttachment[]>([])\n // Bumped on every send, so a hosted picker remounts with nothing on it: the files went with the\n // message, and its chips would otherwise say they are still waiting to be sent.\n const [sent, setSent] = useState(0)\n const [sendError, setSendError] = useState('')\n const composer = useRef<HTMLTextAreaElement>(null)\n const fileInput = useRef<HTMLInputElement>(null)\n // Slash commands and their replies live here, not in the adapter's conversation: `runCommand`\n // answers with text, and the rows follow whatever the adapter emits.\n const [commandRows, setCommandRows] = useState<ChatMessage[]>([])\n const [commands, setCommands] = useState<ChatCommand[]>([])\n const [selected, setSelected] = useState(0)\n // Escape, or a pick that completes the line, closes the picker until the draft changes again.\n const [pickerClosed, setPickerClosed] = useState(false)\n const pickerWanted = Boolean(adapters.chat.commands) && !pickerClosed && SLASH_LINE.test(draft)\n const matches = pickerWanted ? slashMatches(draft, commands) : []\n const pickerOpen = matches.length > 0\n\n useEffect(() => {\n if (!pickerWanted) return\n let live = true\n void adapters.chat.commands!().then(\n (next) => live && setCommands(next),\n () => {},\n )\n return () => {\n live = false\n }\n // Fetched each time a `/` line opens the picker, as the set changes when the agent changes.\n }, [pickerWanted, adapters.chat])\n\n const pick = (insert: string) => {\n setDraft(insert)\n setSelected(0)\n // A pick ending in a space is a command still waiting for its argument: the picker stays open.\n if (!insert.endsWith(' ')) setPickerClosed(true)\n composer.current?.focus()\n }\n\n // A message with no text yet is the agent taking its turn, and the thinking row says so better\n // than an empty bubble would.\n const visible = [\n ...messages.filter((message) => message.text || message.attachments?.length),\n ...commandRows,\n ]\n const last = messages[messages.length - 1]\n const thinking = Boolean(\n last && ((last.role === 'user' && !last.delivery) || (last.streaming && !last.text)),\n )\n\n const { feed, onScroll } = useStickToBottom(\n `${visible.length}:${last?.text.length ?? 0}:${thinking}`,\n )\n\n useLayoutEffect(() => {\n const element = composer.current\n if (!element) return\n element.style.height = 'auto'\n const lineHeight = Number.parseFloat(getComputedStyle(element).lineHeight) || 20\n const max = lineHeight * config.maxComposerLines\n element.style.height = `${Math.min(element.scrollHeight, max)}px`\n element.style.overflowY = element.scrollHeight > max ? 'auto' : 'hidden'\n }, [draft, config.maxComposerLines])\n\n const send = () => {\n const text = draft.trim()\n if (!text && staged.length === 0) return\n setDraft('')\n setStaged([])\n setSent((count) => count + 1)\n setSendError('')\n if (text.startsWith('/') && adapters.chat.runCommand) {\n const row = (id: string, body: string): ChatMessage => ({\n id: `cmd-${id}`,\n role: 'system',\n text: body,\n at: new Date().toISOString(),\n })\n const stamp = Date.now()\n setCommandRows((rows) => [...rows, row(`${stamp}-line`, text)])\n void adapters.chat.runCommand(text).then(\n (reply) => setCommandRows((rows) => [...rows, row(`${stamp}-reply`, reply)]),\n (error: unknown) =>\n setSendError(error instanceof Error ? error.message : 'Command failed.'),\n )\n return\n }\n void adapters.chat.send(text, staged.length ? staged : undefined).catch((error: unknown) => {\n setSendError(error instanceof Error ? error.message : 'Message could not be sent.')\n })\n }\n\n const retry = adapters.chat.retry\n ? (messageId: string) => {\n setSendError('')\n void adapters.chat.retry!(messageId).catch((error: unknown) => {\n setSendError(error instanceof Error ? error.message : 'Message could not be sent.')\n })\n }\n : undefined\n\n const interrupt = adapters.chat.interrupt\n ? () => {\n setSendError('')\n return adapters.chat.interrupt!().catch((error: unknown) => {\n setSendError(error instanceof Error ? error.message : 'The agent could not be stopped.')\n })\n }\n : undefined\n\n return (\n <div\n data-golem-component=\"Chat\"\n className=\"golem-chat flex h-full min-h-0 w-full flex-col bg-(--chat-bg) text-(--chat-text)\"\n >\n <div\n ref={feed}\n onScroll={onScroll}\n role=\"log\"\n aria-label=\"Conversation\"\n aria-live=\"polite\"\n className=\"min-h-0 flex-1 space-y-2 overflow-y-auto px-3 py-3.5 [scrollbar-color:var(--chat-line2)_transparent] [scrollbar-width:thin]\"\n >\n {visible.length === 0 && !thinking ? (\n <p className=\"flex h-full items-center justify-center p-6 text-center text-sm text-(--chat-faint)\">\n {config.emptyState}\n </p>\n ) : (\n visible.map((message) => (\n <Bubble\n key={message.id}\n message={message}\n config={config}\n onRetry={retry}\n onOpenSource={adapters.chat.openSource?.bind(adapters.chat)}\n />\n ))\n )}\n {thinking && <Thinking name={config.agentName} onStop={interrupt} />}\n </div>\n\n {(loadError || sendError) && (\n <p\n role=\"alert\"\n className=\"shrink-0 border-t border-(--chat-line) px-3 py-2 text-sm text-(--chat-danger)\"\n >\n {loadError || sendError}\n </p>\n )}\n\n {attach ? (\n <div key={sent} className=\"shrink-0 border-t border-(--chat-line) px-3 pt-2\">\n {attach(setStaged)}\n </div>\n ) : (\n staged.length > 0 && (\n <div className=\"shrink-0 border-t border-(--chat-line) px-3 pt-2\">\n <AttachmentChips\n attachments={staged}\n onRemove={(id) => setStaged((files) => files.filter((file) => file.id !== id))}\n />\n </div>\n )\n )}\n\n <form\n className=\"relative flex shrink-0 items-end gap-2 border-t border-(--chat-line) px-3 py-2.5\"\n onSubmit={(event) => {\n event.preventDefault()\n send()\n }}\n >\n {pickerOpen && (\n <div\n role=\"listbox\"\n aria-label=\"Slash commands\"\n // Picking must not blur the composer.\n onMouseDown={(event) => event.preventDefault()}\n className=\"absolute right-3 bottom-full left-3 z-10 mb-1 max-h-56 overflow-y-auto rounded-[10px] border border-(--chat-line2) bg-(--chat-panel) py-1 shadow-lg\"\n >\n {matches.map((match, index) => (\n <button\n key={match.insert}\n type=\"button\"\n role=\"option\"\n aria-selected={index === Math.min(selected, matches.length - 1)}\n onClick={() => pick(match.insert)}\n className={`flex w-full items-baseline gap-3 px-3 py-1.5 text-left text-sm ${\n index === Math.min(selected, matches.length - 1)\n ? 'bg-(--chat-panel2) text-(--chat-text)'\n : 'text-(--chat-dim)'\n }`}\n >\n <span className=\"shrink-0 font-mono text-(--chat-text)\">{match.name}</span>\n <span className=\"min-w-0 truncate text-xs\">{match.description}</span>\n </button>\n ))}\n </div>\n )}\n {!attach && (\n <>\n <input\n ref={fileInput}\n type=\"file\"\n multiple\n className=\"hidden\"\n aria-label=\"Attach files\"\n onChange={(event) => {\n const files = [...(event.target.files ?? [])].map((file, index) => ({\n id: `att-${Date.now()}-${index}`,\n name: file.name,\n size: file.size,\n }))\n setStaged((current) => [...current, ...files])\n event.target.value = ''\n }}\n />\n <button\n type=\"button\"\n onClick={() => fileInput.current?.click()}\n title=\"Attach a file\"\n aria-label=\"Attach a file\"\n className=\"flex size-[34px] shrink-0 items-center justify-center rounded-full border border-(--chat-line) bg-(--chat-panel2) text-[15px] text-(--chat-dim) transition-colors hover:border-(--chat-accent) hover:text-(--chat-text)\"\n >\n 📎\n </button>\n </>\n )}\n <textarea\n ref={composer}\n rows={1}\n value={draft}\n onChange={(event) => {\n setDraft(event.target.value)\n setPickerClosed(false)\n setSelected(0)\n }}\n onKeyDown={(event) => {\n // With the picker open, arrows move, Tab and Enter pick, Escape closes.\n if (pickerOpen) {\n const current = Math.min(selected, matches.length - 1)\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault()\n const step = event.key === 'ArrowDown' ? 1 : matches.length - 1\n setSelected((current + step) % matches.length)\n return\n }\n if (event.key === 'Tab' || event.key === 'Enter') {\n event.preventDefault()\n pick(matches[current]!.insert)\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n setPickerClosed(true)\n return\n }\n }\n // Enter sends because the composer is a chat line, not a document; Shift+Enter is the\n // deliberate line break. `isComposing` guards an IME candidate being accepted.\n if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault()\n send()\n }\n }}\n placeholder={config.placeholder}\n aria-label={config.placeholder}\n className=\"min-w-0 flex-1 resize-none rounded-[10px] border border-(--chat-line) bg-(--chat-panel) px-3 py-2 text-[15px] leading-6 text-(--chat-text) outline-none transition-[border-color,box-shadow] placeholder:text-(--chat-faint) focus:border-(--chat-accent) focus:shadow-[0_0_0_3px_var(--chat-accent-soft)]\"\n />\n <button\n type=\"submit\"\n className=\"flex size-[34px] shrink-0 items-center justify-center rounded-full bg-(--chat-accent) text-sm font-medium text-white transition-opacity hover:opacity-85\"\n >\n <span aria-hidden=\"true\">➤</span>\n <span className=\"sr-only\">Send</span>\n </button>\n </form>\n </div>\n )\n}\n\nexport const Chat = defineComponent<typeof chatConfigSchema, ChatAdapters, ChatSlots>({\n name: 'Chat',\n schema: chatConfigSchema,\n render: ChatPanel,\n})\n","/**\n * A three-way merge over lines, which is what lets a person and an agent write the same document at\n * the same time without either one flattening the other. `base` is the version both started from,\n * `mine` is the local draft, `theirs` is what arrived; the result is the chunks a merged document is\n * made of, each one saying where its lines came from.\n *\n * Lines, not characters: a merge that resolves inside a sentence produces text neither writer wrote.\n * A whole line is the smallest unit a person can look at and say \"that one, not that one\".\n */\n\nexport type Merge3Chunk =\n | {\n kind: 'clean'\n lines: string[]\n /** `both` is untouched, `mine` is the local edit, `theirs` is what the agent just wrote. */\n from: 'both' | 'mine' | 'theirs'\n }\n | { kind: 'conflict'; mine: string[]; theirs: string[] }\n\nexport interface Merge3 {\n chunks: Merge3Chunk[]\n /** True when at least one chunk is a conflict, so the reader has to choose. */\n conflicted: boolean\n}\n\n/** One side's edit against the base, as half-open ranges. */\ninterface Edit {\n side: 'mine' | 'theirs'\n baseStart: number\n baseEnd: number\n sideStart: number\n sideEnd: number\n}\n\n/**\n * Above this many lines the quadratic table below would cost more than the merge is worth, so the\n * whole document counts as one edit: the reader still gets a choice, just a coarse one.\n */\nconst MAX_LINES = 4000\n\nconst splitLines = (text: string): string[] => (text === '' ? [] : text.split('\\n'))\n\nconst same = (a: string[], b: string[]) =>\n a.length === b.length && a.every((one, i) => one === b[i])\n\n/**\n * Matched line pairs between two sequences, longest-common-subsequence order. The table is\n * `(a.length + 1) × (b.length + 1)` numbers, which is why `MAX_LINES` exists.\n */\nfunction matchedPairs(a: string[], b: string[]): [number, number][] {\n const rows = a.length\n const cols = b.length\n const table: number[][] = Array.from({ length: rows + 1 }, () =>\n new Array<number>(cols + 1).fill(0),\n )\n\n for (let i = rows - 1; i >= 0; i--) {\n for (let j = cols - 1; j >= 0; j--) {\n table[i]![j] =\n a[i] === b[j] ? table[i + 1]![j + 1]! + 1 : Math.max(table[i + 1]![j]!, table[i]![j + 1]!)\n }\n }\n\n const pairs: [number, number][] = []\n let i = 0\n let j = 0\n while (i < rows && j < cols) {\n if (a[i] === b[j]) {\n pairs.push([i, j])\n i++\n j++\n } else if (table[i + 1]![j]! >= table[i]![j + 1]!) i++\n else j++\n }\n return pairs\n}\n\n/** The runs where `side` differs from `base`, derived from the lines the two still share. */\nfunction editsOf(base: string[], side: string[], which: 'mine' | 'theirs'): Edit[] {\n const pairs = matchedPairs(base, side)\n const edits: Edit[] = []\n let baseAt = 0\n let sideAt = 0\n\n const close = (baseEnd: number, sideEnd: number) => {\n if (baseEnd > baseAt || sideEnd > sideAt) {\n edits.push({ side: which, baseStart: baseAt, baseEnd, sideStart: sideAt, sideEnd })\n }\n }\n\n for (const [baseIndex, sideIndex] of pairs) {\n close(baseIndex, sideIndex)\n baseAt = baseIndex + 1\n sideAt = sideIndex + 1\n }\n close(base.length, side.length)\n return edits\n}\n\nexport function merge3(baseText: string, mineText: string, theirsText: string): Merge3 {\n const base = splitLines(baseText)\n const mine = splitLines(mineText)\n const theirs = splitLines(theirsText)\n\n if (same(mine, theirs)) {\n return { chunks: [{ kind: 'clean', lines: mine, from: 'both' }], conflicted: false }\n }\n if (Math.max(base.length, mine.length, theirs.length) > MAX_LINES) {\n return { chunks: [{ kind: 'conflict', mine, theirs }], conflicted: true }\n }\n\n // Both sides' edits on one timeline, ordered by where they land in the base. Two edits that\n // overlap in the base are one decision, so they are grouped before either is applied.\n const edits = [...editsOf(base, mine, 'mine'), ...editsOf(base, theirs, 'theirs')].sort(\n (a, b) => a.baseStart - b.baseStart || a.baseEnd - b.baseEnd,\n )\n\n const chunks: Merge3Chunk[] = []\n let conflicted = false\n let baseAt = 0\n let mineAt = 0\n let theirsAt = 0\n let at = 0\n\n const keepUntouched = (until: number) => {\n if (until > baseAt)\n chunks.push({ kind: 'clean', lines: base.slice(baseAt, until), from: 'both' })\n mineAt += until - baseAt\n theirsAt += until - baseAt\n baseAt = until\n }\n\n while (at < edits.length) {\n const group = [edits[at]!]\n let baseEnd = group[0]!.baseEnd\n at++\n // Only a real overlap makes one decision out of two edits. Edits that merely touch — the agent\n // rewriting the line above the one being typed on — stay two, and both are kept.\n while (at < edits.length && edits[at]!.baseStart < baseEnd) {\n baseEnd = Math.max(baseEnd, edits[at]!.baseEnd)\n group.push(edits[at]!)\n at++\n }\n const baseStart = group[0]!.baseStart\n\n keepUntouched(baseStart)\n\n // Each side's span for this region. Every base line before the group's first edit is unchanged\n // on both sides, so the running cursor is already that side's start; only the end has to be\n // carried out from the last edit.\n const sides = {\n mine: group.filter((edit) => edit.side === 'mine'),\n theirs: group.filter((edit) => edit.side === 'theirs'),\n }\n const sliceOf = (which: 'mine' | 'theirs', lines: string[], cursor: number) => {\n const own = sides[which]\n const last = own[own.length - 1]\n const end =\n last === undefined\n ? cursor + (baseEnd - baseStart)\n : last.sideEnd + (baseEnd - last.baseEnd)\n return lines.slice(cursor, Math.max(cursor, end))\n }\n\n const mineLines = sliceOf('mine', mine, mineAt)\n const theirsLines = sliceOf('theirs', theirs, theirsAt)\n\n if (sides.theirs.length === 0) chunks.push({ kind: 'clean', lines: mineLines, from: 'mine' })\n else if (sides.mine.length === 0)\n chunks.push({ kind: 'clean', lines: theirsLines, from: 'theirs' })\n else if (same(mineLines, theirsLines))\n chunks.push({ kind: 'clean', lines: mineLines, from: 'both' })\n else {\n chunks.push({ kind: 'conflict', mine: mineLines, theirs: theirsLines })\n conflicted = true\n }\n\n mineAt += mineLines.length\n theirsAt += theirsLines.length\n baseAt = baseEnd\n }\n\n keepUntouched(base.length)\n\n return {\n chunks: chunks.filter((chunk) => chunk.kind === 'conflict' || chunk.lines.length > 0),\n conflicted,\n }\n}\n\n/**\n * The merged document as text, plus the line ranges the other side wrote — what the editor paints a\n * gutter down so the person can see what the agent just changed. `choose` settles any conflict; a\n * merge with none never calls it.\n */\nexport function mergedText(\n chunks: Merge3Chunk[],\n choose: (chunk: { mine: string[]; theirs: string[] }, index: number) => 'mine' | 'theirs' = () =>\n 'mine',\n): { text: string; theirLines: { start: number; end: number }[] } {\n const lines: string[] = []\n const theirLines: { start: number; end: number }[] = []\n\n chunks.forEach((chunk, index) => {\n const taken =\n chunk.kind === 'clean'\n ? chunk.lines\n : choose(chunk, index) === 'mine'\n ? chunk.mine\n : chunk.theirs\n const fromThem =\n chunk.kind === 'clean' ? chunk.from === 'theirs' : choose(chunk, index) === 'theirs'\n if (fromThem && taken.length > 0)\n theirLines.push({ start: lines.length, end: lines.length + taken.length })\n lines.push(...taken)\n })\n\n return { text: lines.join('\\n'), theirLines }\n}\n","/**\n * A locale the platform can actually format with. `en_GB` and `english` are the two an agent\n * writes by mistake, and both would otherwise throw inside `Intl` at render time rather than land\n * on the error card.\n */\nexport function isFormattableLocale(locale: string): boolean {\n try {\n new Intl.DateTimeFormat(locale)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * A time zone `Intl` accepts, or UTC. The zone comes from the `Clock` adapter rather than from the\n * config, so there is no field to name on an error card — a zone the platform does not know falls\n * back rather than throwing mid-render.\n */\nexport function formattableZone(timeZone: string): string {\n try {\n new Intl.DateTimeFormat('en-CA', { timeZone })\n return timeZone\n } catch {\n return 'UTC'\n }\n}\n","import { z } from 'zod'\nimport { isFormattableLocale } from '../../lib/locale'\n\nexport const editorConfigSchema = z\n .object({\n collection: z\n .string()\n .min(1)\n .describe(\n 'The record type the document lives in, passed to `get`, `update` and `subscribe`. One document is one record.',\n ),\n id: z\n .string()\n .min(1)\n .describe(\n 'The id of the one record this editor opens. An editor edits a document, not a collection: point two editors at two ids.',\n ),\n bodyField: z\n .string()\n .min(1)\n .default('body')\n .describe(\n 'The field holding the document itself, as markdown. It is both what the person types into and what the agent rewrites.',\n ),\n versionField: z\n .string()\n .min(1)\n .default('version')\n .describe(\n 'The field holding a number that goes up on every write. Saving sends the version it read as `expectedVersion`, so a write that arrived second is refused rather than overwriting the other writer.',\n ),\n autosaveMs: z\n .number()\n .int()\n .min(0)\n .max(60_000)\n .default(1200)\n .describe(\n 'How long typing has to stop before the draft is saved. `0` saves on every keystroke, which is what a document two writers share wants; a slow store wants two or three thousand.',\n ),\n preview: z\n .enum(['toggle', 'split', 'off'])\n .default('toggle')\n .describe(\n 'How the rendered document is reached. `toggle` swaps the pane, `split` puts the rendering beside the source at desktop width and falls back to `toggle` on a phone, `off` shows the source only.',\n ),\n outline: z\n .boolean()\n .default(true)\n .describe(\n 'Whether the headings are offered as a drawer that jumps to them. Off for a document short enough to scroll.',\n ),\n readOnly: z\n .boolean()\n .default(false)\n .describe(\n 'Whether the document is only readable. The source and the preview stay, the toolbar and the autosave go, and nothing the person types reaches the record.',\n ),\n placeholder: z\n .string()\n .default('Write the document in markdown…')\n .describe('The line shown in place of an empty document, to say what belongs in it.'),\n highlightMs: z\n .number()\n .int()\n .min(0)\n .max(60_000)\n .default(4000)\n .describe(\n 'How long a line the agent just wrote keeps its coloured gutter. `0` never marks anything; a few seconds is long enough to catch the eye and short enough to stop being noise.',\n ),\n locale: z\n .string()\n .min(2)\n .default('en-GB')\n .describe(\n 'A BCP-47 tag — `en-GB`, `en-US`, `pt-BR` — that the saved-at time in the status line is formatted with.',\n ),\n })\n .strict()\n .superRefine((config, ctx) => {\n if (!isFormattableLocale(config.locale)) {\n ctx.addIssue({\n code: 'custom',\n path: ['locale'],\n message: `\"${config.locale}\" is not a BCP-47 language tag, so the saved-at time cannot be formatted. Write it with a hyphen: en-GB, en-US, pt-BR.`,\n })\n }\n })\n\nexport type EditorConfig = z.output<typeof editorConfigSchema>\nexport type EditorConfigInput = z.input<typeof editorConfigSchema>\n","import { Fragment, type ReactNode } from 'react'\n\n/**\n * Markdown source styled where it stands: the characters the person typed, still all there, wearing\n * the weight and colour of what they mean. It is not a preview and it is not a WYSIWYG surface —\n * `##` stays `##`, and deleting one is how a heading stops being a heading.\n *\n * It is drawn under a transparent textarea, so **every span has to keep the monospace advance\n * width**: weight, colour and background only. A larger heading or an italic run would slide the\n * text out from under the caret.\n */\n\nexport type LineKind = 'heading' | 'code' | 'list' | 'quote' | 'plain'\n\nexport interface SourceLine {\n text: string\n kind: LineKind\n}\n\n/** A fence opens and closes across lines, so a line's kind is only knowable in document order. */\nexport function scanSource(lines: string[]): SourceLine[] {\n let fenced = false\n return lines.map((text) => {\n if (text.trimStart().startsWith('```')) {\n fenced = !fenced\n return { text, kind: 'code' as const }\n }\n if (fenced) return { text, kind: 'code' as const }\n if (/^#{1,6}\\s/.test(text)) return { text, kind: 'heading' as const }\n if (/^\\s*([-*+]|\\d+[.)])\\s/.test(text)) return { text, kind: 'list' as const }\n if (/^\\s*>/.test(text)) return { text, kind: 'quote' as const }\n return { text, kind: 'plain' as const }\n })\n}\n\n/** Every heading in the document, in order, for the outline. */\nexport function headingsOf(lines: string[]): { line: number; level: number; text: string }[] {\n return scanSource(lines).flatMap((line, index) => {\n if (line.kind !== 'heading') return []\n const match = /^(#{1,6})\\s+(.*)$/.exec(line.text)\n if (!match || match[2]!.trim() === '') return []\n return [{ line: index, level: match[1]!.length, text: match[2]!.trim() }]\n })\n}\n\nconst LINE_CLASS: Record<LineKind, string> = {\n heading: 'font-semibold text-neutral-900',\n code: 'bg-neutral-100 text-neutral-600',\n list: 'text-neutral-800',\n quote: 'text-neutral-500',\n plain: 'text-neutral-800',\n}\n\nconst INLINE = /(`[^`]+`|\\*\\*[^*]+\\*\\*|\\[[^\\]\\n]+\\]\\([^)\\s]+\\))/g\n\nfunction inline(text: string, key: string): ReactNode[] {\n return text.split(INLINE).map((piece, index) => {\n const id = `${key}-${index}`\n if (piece.startsWith('`') && piece.endsWith('`') && piece.length > 1) {\n return (\n <span key={id} className=\"rounded bg-neutral-200/70 text-rose-700\">\n {piece}\n </span>\n )\n }\n if (piece.startsWith('**') && piece.endsWith('**') && piece.length > 3) {\n return (\n <span key={id} className=\"font-semibold\">\n {piece}\n </span>\n )\n }\n if (piece.startsWith('[')) {\n return (\n <span key={id} className=\"text-sky-700\">\n {piece}\n </span>\n )\n }\n return <Fragment key={id}>{piece}</Fragment>\n })\n}\n\n/** The marker a line opens with — `##`, `-`, `1.`, `>` — coloured apart from what follows it. */\nconst MARKER = /^(\\s*)(#{1,6}|[-*+]|\\d+[.)]|>)(\\s+)(.*)$/\n\nexport function SourceRow({ line, index }: { line: SourceLine; index: number }) {\n if (line.kind === 'code') return <>{line.text}</>\n\n const marked = MARKER.exec(line.text)\n if (!marked || (line.kind !== 'heading' && line.kind !== 'list' && line.kind !== 'quote')) {\n return <>{inline(line.text, String(index))}</>\n }\n\n return (\n <>\n {marked[1]}\n <span className=\"text-sky-600\">{marked[2]}</span>\n {marked[3]}\n {inline(marked[4]!, String(index))}\n </>\n )\n}\n\nexport { LINE_CLASS }\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactNode,\n} from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport { conflictingRecord, type ClockAdapter, type RecordsAdapter } from '../../adapters'\nimport { merge3, mergedText, type Merge3Chunk } from '../../lib/diff3'\nimport { headingSlug, Markdown } from '../../lib/markdown'\nimport { useContainerWidth } from '../../lib/use-container-width'\nimport { editorConfigSchema, type EditorConfig } from './Editor.config'\nimport { headingsOf, scanSource, SourceRow, LINE_CLASS } from './source'\n\nexport interface EditorAdapters {\n records: RecordsAdapter\n clock: ClockAdapter\n}\n\nexport interface EditorSlots {\n /**\n * Work the person had not saved when they last left — from a local draft store, say. The editor\n * opens on it instead of the record's body, dirty against the record's version, so the first\n * thing that arrives from the agent is merged rather than dropped on top of it.\n */\n draft?: string\n /**\n * A passage to show: 1-based source lines, `endLine` inclusive, clamped to the document. The source\n * pane scrolls to it and marks it until the person clicks or edits; keyboard focus, caret and draft\n * are left alone. Send a new `key` to show the same range again.\n *\n * With `version`, the request waits until the editor holds that version or a later one. With\n * `text`, the passage is found by its lines in the draft on screen, and nothing is marked when\n * those lines are not there exactly once.\n */\n focus?: EditorFocus\n}\n\nexport interface EditorFocus {\n line: number\n endLine?: number\n key?: string\n /** The record version `line` counts in. The request waits until the editor has loaded it. */\n version?: number\n /** The passage's exact lines, joined by `\\n`. Found in the draft rather than trusted to `line`. */\n text?: string\n}\n\n/** Below this the source and the preview cannot both fit, so `split` falls back to `toggle`. */\nconst SPLIT_ABOVE = 768\n\ntype Status = 'loading' | 'saved' | 'dirty' | 'saving' | 'conflict' | 'error'\n\n/** A run of lines the agent wrote, in the draft's own line numbers, and when it landed. */\ninterface Mark {\n start: number\n end: number\n}\n\ninterface Doc {\n draft: string\n /** The text the person and the agent last agreed on: the base every merge is run from. */\n base: string\n version: number\n status: Status\n error: string | null\n savedAt: Date | null\n marks: Mark[]\n /** Set while the two disagree; the source pane is replaced by the choice until it is empty. */\n conflict: Merge3Chunk[] | null\n choices: ('mine' | 'theirs' | null)[]\n /** The record's line break. `draft` and `base` always hold `\\n`, as a textarea does. */\n eol: '\\n' | '\\r\\n'\n}\n\nconst EMPTY: Doc = {\n draft: '',\n base: '',\n version: 0,\n status: 'loading',\n error: null,\n savedAt: null,\n marks: [],\n conflict: null,\n choices: [],\n eol: '\\n',\n}\n\n/**\n * A body whose every line break is CRLF is held with bare LF — a textarea turns CRLF into LF on the\n * first keystroke anyway — and written back with CRLF. Any other body, mixed ones too, is held as is.\n */\nfunction decode(body: string): { text: string; eol: Doc['eol'] } {\n return body.includes('\\r\\n') && !/\\r(?!\\n)|(?<!\\r)\\n/.test(body)\n ? { text: body.replaceAll('\\r\\n', '\\n'), eol: '\\r\\n' }\n : { text: body, eol: '\\n' }\n}\n\n/**\n * The 0-based `[start, end)` rows a focus request marks in `lines`, or `null` for none. A request\n * without `text` counts lines, clamped. With `text`, the offered range is trusted only on the exact\n * version it was counted in, untouched; anywhere else the lines must appear exactly once.\n */\nfunction passageOf(\n lines: string[],\n focus: EditorFocus,\n atVersion: boolean,\n eol: Doc['eol'],\n): [number, number] | null {\n const first = Math.trunc(focus.line) - 1\n const last = Number.isFinite(focus.endLine) ? Math.trunc(focus.endLine!) : first + 1\n if (focus.text === undefined) {\n const clamp = (row: number) => Math.min(Math.max(row, 0), lines.length - 1)\n const start = clamp(first)\n return [start, Math.max(start, clamp(last - 1)) + 1]\n }\n // The offered lines of a CRLF record keep their CRs, the last one's too; the draft has none.\n const text = eol === '\\n' ? focus.text : focus.text.replaceAll('\\r\\n', '\\n').replace(/\\r$/, '')\n const want = text.split('\\n')\n const holds = (at: number) => want.every((line, index) => lines[at + index] === line)\n if (atVersion && last - first === want.length && holds(first)) return [first, last]\n let found: number | null = null\n for (let at = 0; at + want.length <= lines.length; at++) {\n if (!holds(at)) continue\n if (found !== null) return null\n found = at\n }\n return found === null ? null : [found, found + want.length]\n}\n\nconst message = (cause: unknown) => (cause instanceof Error ? cause.message : String(cause))\n\ntype DocRecord = Record<string, unknown> & { id: string }\n\n/**\n * Undo is local and it is the editor's own: a draft the agent rewrote is set programmatically, and\n * the browser's native textarea history has no entry for that. Keystrokes inside half a second\n * coalesce into one step, measured on the wall clock rather than on the `Clock` adapter — a frozen\n * clock is a document's clock, not a stopwatch.\n */\nconst COALESCE_MS = 500\n\nfunction useHistory() {\n const state = useRef({ stack: [] as string[], at: -1, last: 0 })\n\n const reset = useCallback((text: string) => {\n state.current = { stack: [text], at: 0, last: 0 }\n }, [])\n\n const remember = useCallback((text: string) => {\n const history = state.current\n const now = Date.now()\n if (history.at >= 0 && now - history.last < COALESCE_MS) {\n history.stack[history.at] = text\n history.last = now\n return\n }\n history.stack = [...history.stack.slice(0, history.at + 1), text]\n history.at = history.stack.length - 1\n history.last = now\n }, [])\n\n const step = useCallback((delta: number): string | null => {\n const history = state.current\n const next = history.at + delta\n if (next < 0 || next >= history.stack.length) return null\n history.at = next\n history.last = 0\n return history.stack[next]!\n }, [])\n\n return useMemo(() => ({ reset, remember, step }), [reset, remember, step])\n}\n\nfunction EditorBody({\n config,\n adapters,\n draft: openWith,\n focus,\n}: GolemProps<EditorConfig, EditorAdapters, EditorSlots>) {\n const { records, clock } = adapters\n const root = useRef<HTMLDivElement>(null)\n const textarea = useRef<HTMLTextAreaElement>(null)\n const overlay = useRef<HTMLDivElement>(null)\n const preview = useRef<HTMLDivElement>(null)\n const sourcePane = useRef<HTMLDivElement>(null)\n\n const [doc, setDocState] = useState<Doc>(EMPTY)\n const docRef = useRef(doc)\n const setDoc = useCallback((patch: Partial<Doc>) => {\n docRef.current = { ...docRef.current, ...patch }\n setDocState(docRef.current)\n }, [])\n\n const [mode, setMode] = useState<'source' | 'preview'>('source')\n const [outlineOpen, setOutlineOpen] = useState(false)\n const history = useHistory()\n const selection = useRef<[number, number] | null>(null)\n /** An incoming version that arrived while a conflict was open, applied once it is settled. */\n const pending = useRef<DocRecord | null>(null)\n /**\n * The record the doc belongs to, read through which fields and which store. Moving to another one\n * parks this doc — draft, versions, an open conflict — untouched, and nothing is written on the way\n * out. Coming back restores it, and the load that follows merges it against the latest version.\n */\n const record = `${config.collection}/${config.id}/${config.bodyField}/${config.versionField}`\n const opened = useRef({ record, records, first: { record, records } })\n const parked = useRef(\n new WeakMap<RecordsAdapter, Map<string, { doc: Doc; pending: DocRecord | null }>>(),\n )\n\n /** The record whose latest version has been read since it was opened, so a draft is up to date. */\n const loaded = useRef<{ record: string; records: RecordsAdapter } | null>(null)\n const [reads, setReads] = useState(0)\n\n const wide = useContainerWidth(root) >= SPLIT_ABOVE\n const split = config.preview === 'split' && wide\n const showPreview = split || (config.preview !== 'off' && mode === 'preview')\n const showSource = split || !showPreview\n\n /**\n * A version that arrived from somewhere else, folded into the draft. Three outcomes: it is already\n * what we have, it merges cleanly and the lines it wrote are marked, or the two disagree and the\n * person is asked which lines to keep.\n */\n const applyIncoming = useCallback(\n (record: DocRecord) => {\n const current = docRef.current\n const { text: theirs, eol } = decode(String(record[config.bodyField] ?? ''))\n const version = Number(record[config.versionField] ?? current.version)\n\n if (current.conflict !== null) {\n pending.current = record\n return\n }\n if (theirs === current.base && version === current.version) return\n\n const merged = merge3(current.base, current.draft, theirs)\n if (merged.conflicted) {\n setDoc({\n base: theirs,\n version,\n eol,\n status: 'conflict',\n error: null,\n conflict: merged.chunks,\n choices: merged.chunks.map(() => null),\n })\n return\n }\n\n const { text, theirLines } = mergedText(merged.chunks)\n history.remember(text)\n setDoc({\n draft: text,\n base: theirs,\n version,\n eol,\n status: text === theirs ? 'saved' : 'dirty',\n error: null,\n marks: config.highlightMs > 0 ? theirLines : [],\n })\n },\n [config.bodyField, config.versionField, config.highlightMs, history, setDoc],\n )\n\n /** The one write. The version it read goes out as `expectedVersion`; a refusal merges instead. */\n const save = useCallback(async () => {\n const current = docRef.current\n if (config.readOnly || current.status === 'conflict' || current.status === 'loading') return\n if (current.draft === current.base) return\n\n const text = current.draft\n const body = current.eol === '\\n' ? text : text.replaceAll('\\n', current.eol)\n const expected = current.version\n setDoc({ status: 'saving' })\n\n // The answer belongs to the record that asked, which may be parked by the time it comes back.\n const here = () => opened.current.record === record && opened.current.records === records\n const settle = (patch: (doc: Doc) => Partial<Doc>) => {\n if (here()) return setDoc(patch(docRef.current))\n const away = parked.current.get(records)?.get(record)\n if (away) away.doc = { ...away.doc, ...patch(away.doc) }\n }\n\n try {\n const saved = await records.update<DocRecord>(\n config.collection,\n config.id,\n { [config.bodyField]: body, [config.versionField]: expected + 1 },\n { expectedVersion: expected, versionField: config.versionField },\n )\n settle((doc) => ({\n base: text,\n version: Number(saved[config.versionField] ?? expected + 1),\n // The person kept typing while the write was in flight, so there is already more to send.\n status: doc.draft === text ? 'saved' : 'dirty',\n error: null,\n savedAt: clock.now(),\n }))\n } catch (cause) {\n const theirs = conflictingRecord(cause)\n if (theirs && here()) {\n setDoc({ status: 'dirty' })\n applyIncoming(theirs as DocRecord)\n return\n }\n // A parked refusal stays dirty; coming back loads the latest version and merges against it.\n settle(() => (theirs ? { status: 'dirty' } : { status: 'error', error: message(cause) }))\n }\n }, [applyIncoming, clock, config, record, records, setDoc])\n\n // The document, and the subscription that brings every later version of it. Nothing polls.\n useEffect(() => {\n let live = true\n if (opened.current.record !== record || opened.current.records !== records) {\n const left = opened.current\n const shelf = parked.current.get(left.records) ?? new Map()\n parked.current.set(\n left.records,\n shelf.set(left.record, { doc: docRef.current, pending: pending.current }),\n )\n const back = parked.current.get(records)?.get(record)\n parked.current.get(records)?.delete(record)\n opened.current = { ...left, record, records }\n pending.current = back?.pending ?? null\n history.reset(back?.doc.draft ?? '')\n setDoc(back?.doc ?? EMPTY)\n loaded.current = null\n }\n\n const read = () => {\n if (loaded.current?.record === record && loaded.current.records === records) return\n loaded.current = { record, records }\n setReads((count) => count + 1)\n }\n const load = () =>\n records.get<DocRecord>(config.collection, config.id).then(\n (record) => {\n if (!live) return\n if (record === null) {\n setDoc({\n status: 'error',\n error: `There is no ${config.collection} record with the id ${config.id}.`,\n })\n return\n }\n if (docRef.current.status === 'loading') {\n const { text: body, eol } = decode(String(record[config.bodyField] ?? ''))\n // The draft slot is the first record's unsaved work, never another record's.\n const opening =\n openWith !== undefined &&\n opened.current.record === opened.current.first.record &&\n opened.current.records === opened.current.first.records\n ? eol === '\\n'\n ? openWith\n : openWith.replaceAll('\\r\\n', '\\n')\n : body\n history.reset(opening)\n setDoc({\n draft: opening,\n base: body,\n eol,\n version: Number(record[config.versionField] ?? 0),\n status: opening === body ? 'saved' : 'dirty',\n error: null,\n })\n read()\n return\n }\n applyIncoming(record)\n read()\n },\n (cause: unknown) => {\n if (live) setDoc({ status: 'error', error: message(cause) })\n },\n )\n\n void load()\n const unsubscribe = records.subscribe(config.collection, () => void load())\n return () => {\n live = false\n unsubscribe()\n }\n // `openWith` is the draft the editor opens on, read once; a later change to it is not an edit.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n records,\n record,\n config.collection,\n config.id,\n config.bodyField,\n config.versionField,\n applyIncoming,\n history,\n setDoc,\n ])\n\n // Autosave: typing stops, the debounce runs out, the draft goes.\n useEffect(() => {\n if (config.readOnly || doc.status !== 'dirty') return undefined\n const timer = setTimeout(() => void save(), config.autosaveMs)\n return () => clearTimeout(timer)\n }, [doc.draft, doc.status, config.autosaveMs, config.readOnly, save])\n\n // The agent's lines keep their gutter for a few seconds, then the document is just a document.\n useEffect(() => {\n if (doc.marks.length === 0 || config.highlightMs === 0) return undefined\n const timer = setTimeout(() => setDoc({ marks: [] }), config.highlightMs)\n return () => clearTimeout(timer)\n }, [doc.marks, config.highlightMs, setDoc])\n\n // The textarea grows with its content, so the pane scrolls and the overlay never has to be\n // scrolled in step with it.\n useLayoutEffect(() => {\n const element = textarea.current\n if (!element) return\n element.style.height = 'auto'\n element.style.height = `${element.scrollHeight}px`\n }, [doc.draft, showSource])\n\n // A draft set in code — a wrap, an indent, an undo — puts the caret back where the person left it.\n useLayoutEffect(() => {\n const wanted = selection.current\n if (!wanted || !textarea.current) return\n selection.current = null\n textarea.current.focus()\n textarea.current.setSelectionRange(wanted[0], wanted[1])\n }, [doc.draft])\n\n /**\n * A focus request belongs to the record open when it arrived, and is applied once, when that record\n * has been read since it was opened, no conflict is open, and the version it names has arrived. A\n * parked draft brought back is matched only after the latest version has been merged into it. The mark it leaves is tied to the draft it was drawn on, so any\n * edit — the person's or the agent's — takes it down rather than leaving it on shifted lines.\n */\n const ask = focus\n ? JSON.stringify([focus.line, focus.endLine, focus.key, focus.version, focus.text])\n : ''\n const asked = useRef({ ask: '', record, records, done: true })\n const [spot, setSpot] = useState<{\n start: number\n end: number\n draft: string\n record: string\n records: RecordsAdapter\n } | null>(null)\n const scrollToSpot = useRef(false)\n\n useEffect(() => {\n if (ask !== asked.current.ask) asked.current = { ask, record, records, done: ask === '' }\n }, [ask, record, records])\n\n useEffect(() => {\n const request = asked.current\n if (request.done || !focus) return\n const current = docRef.current\n if (request.record !== record || request.records !== records || !Number.isFinite(focus.line)) {\n request.done = true\n return\n }\n if (loaded.current?.record !== record || loaded.current.records !== records) return\n if (current.status === 'loading' || current.conflict !== null) return\n if (Number.isFinite(focus.version) && current.version < focus.version!) return\n request.done = true\n const atVersion = current.version === focus.version && current.draft === current.base\n const passage = passageOf(current.draft.split('\\n'), focus, atVersion, current.eol)\n // A passage that cannot be placed leaves nothing marked, not even the last request's mark.\n if (!passage) return setSpot(null)\n scrollToSpot.current = true\n setMode('source')\n setSpot({ start: passage[0], end: passage[1], draft: current.draft, record, records })\n }, [ask, record, records, focus, reads, doc.status, doc.conflict, doc.version])\n\n // Centre the passage in the source pane, or put its top in view with two lines above it when it is\n // taller than the pane. Only the pane scrolls: the caret and the page stay where they are.\n useLayoutEffect(() => {\n const pane = sourcePane.current\n const rows = overlay.current?.children\n if (!scrollToSpot.current || !spot || !pane || !rows) return\n scrollToSpot.current = false\n const origin = pane.getBoundingClientRect().top - pane.scrollTop\n const top = rows[spot.start]!.getBoundingClientRect().top - origin\n const bottom = rows[spot.end - 1]!.getBoundingClientRect().bottom - origin\n pane.scrollTop =\n bottom - top > pane.clientHeight ? top - 48 : (top + bottom - pane.clientHeight) / 2\n }, [spot, showSource])\n\n const lines = useMemo(() => doc.draft.split('\\n'), [doc.draft])\n const scanned = useMemo(() => scanSource(lines), [lines])\n const headings = useMemo(() => headingsOf(lines), [lines])\n const marked = useMemo(() => {\n const rows = new Set<number>()\n for (const mark of doc.marks)\n for (let line = mark.start; line < mark.end; line++) rows.add(line)\n return rows\n }, [doc.marks])\n const shown =\n spot && spot.record === record && spot.records === records && spot.draft === doc.draft\n ? spot\n : null\n\n const write = (text: string, caret?: [number, number]) => {\n history.remember(text)\n if (caret) selection.current = caret\n setDoc({ draft: text, status: text === docRef.current.base ? 'saved' : 'dirty' })\n }\n\n /** Wraps the selection, or opens an empty pair with the caret inside it. */\n const wrap = (marker: string) => {\n const element = textarea.current\n if (!element) return\n const [from, to] = [element.selectionStart, element.selectionEnd]\n const text = docRef.current.draft\n const next = `${text.slice(0, from)}${marker}${text.slice(from, to)}${marker}${text.slice(to)}`\n write(next, [from + marker.length, to + marker.length])\n }\n\n /** Two spaces on or off the front of every line the selection touches. */\n const indent = (out: boolean) => {\n const element = textarea.current\n if (!element) return\n const text = docRef.current.draft\n const start = text.lastIndexOf('\\n', element.selectionStart - 1) + 1\n const end = text.indexOf('\\n', element.selectionEnd)\n const stop = end === -1 ? text.length : end\n const block = text.slice(start, stop)\n const shifted = block\n .split('\\n')\n .map((line) => (out ? line.replace(/^ {1,2}/, '') : ` ${line}`))\n .join('\\n')\n const moved = shifted.length - block.length\n write(`${text.slice(0, start)}${shifted}${text.slice(stop)}`, [\n Math.max(start, element.selectionStart + (out ? -2 : 2)),\n Math.max(start, element.selectionEnd + moved),\n ])\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n const meta = event.metaKey || event.ctrlKey\n const key = event.key.toLowerCase()\n\n if (meta && key === 's') {\n event.preventDefault()\n void save()\n return\n }\n if (meta && key === 'b') {\n event.preventDefault()\n wrap('**')\n return\n }\n if (meta && key === 'i') {\n event.preventDefault()\n wrap('*')\n return\n }\n if (meta && key === 'z') {\n event.preventDefault()\n const text = history.step(event.shiftKey ? 1 : -1)\n if (text !== null)\n setDoc({ draft: text, status: text === docRef.current.base ? 'saved' : 'dirty' })\n return\n }\n if (event.key === 'Tab') {\n // Only a list line takes Tab. Everywhere else it moves focus on, which is how a keyboard\n // reader gets out of a textarea.\n const at = event.currentTarget.selectionStart\n const line = docRef.current.draft.slice(\n docRef.current.draft.lastIndexOf('\\n', at - 1) + 1,\n at,\n )\n if (!/^\\s*([-*+]|\\d+[.)])\\s/.test(line)) return\n event.preventDefault()\n indent(event.shiftKey)\n }\n }\n\n const jumpTo = (heading: { line: number; text: string }) => {\n setOutlineOpen(false)\n if (showPreview && !showSource) {\n preview.current\n ?.querySelector(`[data-golem-heading=\"${headingSlug(heading.text)}\"]`)\n ?.scrollIntoView({ block: 'start' })\n return\n }\n overlay.current?.children[heading.line]?.scrollIntoView({ block: 'start' })\n const element = textarea.current\n if (!element) return\n const at = lines.slice(0, heading.line).reduce((sum, line) => sum + line.length + 1, 0)\n element.focus()\n element.setSelectionRange(at, at)\n }\n\n const choose = (index: number, side: 'mine' | 'theirs') => {\n const current = docRef.current\n if (current.conflict === null) return\n const choices = current.choices.map((one, at) => (at === index ? side : one))\n const undecided = current.conflict.some(\n (chunk, at) => chunk.kind === 'conflict' && choices[at] === null,\n )\n if (undecided) {\n setDoc({ choices })\n return\n }\n\n const { text, theirLines } = mergedText(current.conflict, (_chunk, at) => choices[at] ?? 'mine')\n history.remember(text)\n setDoc({\n draft: text,\n status: text === current.base ? 'saved' : 'dirty',\n conflict: null,\n choices: [],\n marks: config.highlightMs > 0 ? theirLines : [],\n })\n\n const later = pending.current\n pending.current = null\n if (later) applyIncoming(later)\n }\n\n return (\n <div\n ref={root}\n data-golem-component=\"Editor\"\n data-status={doc.status}\n className=\"golem-editor flex h-full min-h-0 w-full flex-col bg-white text-neutral-900\"\n >\n {wide && (\n <Toolbar\n doc={doc}\n config={config}\n headings={headings.length}\n outlineOpen={outlineOpen}\n onOutline={() => setOutlineOpen((open) => !open)}\n mode={mode}\n split={split}\n onMode={setMode}\n onWrap={wrap}\n onSave={() => void save()}\n clock={clock}\n />\n )}\n\n <div className=\"flex min-h-0 flex-1 flex-col sm:flex-row\">\n {config.outline && outlineOpen && (\n <Outline headings={headings} onJump={jumpTo} wide={wide} />\n )}\n\n {doc.conflict !== null ? (\n <Conflict chunks={doc.conflict} choices={doc.choices} onChoose={choose} />\n ) : (\n <>\n {showSource && (\n <div\n ref={sourcePane}\n data-golem-pane=\"source\"\n className={`min-h-0 flex-1 overflow-auto ${split ? 'sm:border-r sm:border-neutral-200' : ''}`}\n >\n <div className=\"relative min-h-full font-mono text-[13px] leading-6\">\n {/* The styled source, drawn under a textarea whose own text is transparent. Both\n wrap identically, so the caret always sits on the character it is on. */}\n <div\n ref={overlay}\n aria-hidden=\"true\"\n style={{ '--golem-mark-ms': `${config.highlightMs}ms` } as CSSProperties}\n className=\"pointer-events-none absolute inset-0 p-4 break-words whitespace-pre-wrap\"\n >\n {scanned.map((line, index) => {\n const focused = shown !== null && index >= shown.start && index < shown.end\n return (\n <div\n key={index}\n data-golem-marked={marked.has(index) ? 'true' : undefined}\n data-golem-focus={focused ? 'true' : undefined}\n className={`-mx-2 px-2 ${LINE_CLASS[line.kind]} ${\n marked.has(index) ? 'golem-editor-mark' : ''\n } ${focused ? 'golem-editor-focus' : ''}`}\n >\n {line.text === '' ? ' ' : <SourceRow line={line} index={index} />}\n </div>\n )\n })}\n </div>\n <textarea\n ref={textarea}\n value={doc.draft}\n readOnly={config.readOnly}\n aria-label=\"Document source\"\n placeholder={config.placeholder}\n spellCheck={false}\n onChange={(event) => write(event.target.value)}\n onKeyDown={onKeyDown}\n onPointerDown={() => setSpot(null)}\n className=\"relative block w-full resize-none overflow-hidden bg-transparent p-4 font-mono text-[13px] leading-6 break-words whitespace-pre-wrap text-transparent caret-neutral-900 outline-none placeholder:text-neutral-400\"\n />\n </div>\n </div>\n )}\n\n {showPreview && (\n <div\n ref={preview}\n data-golem-pane=\"preview\"\n className=\"min-h-0 flex-1 overflow-auto bg-neutral-50 p-4 text-[15px] leading-7 sm:p-6\"\n >\n <Markdown text={doc.draft} hardWraps={false} />\n </div>\n )}\n </>\n )}\n </div>\n\n {!wide && (\n <Toolbar\n doc={doc}\n config={config}\n headings={headings.length}\n outlineOpen={outlineOpen}\n onOutline={() => setOutlineOpen((open) => !open)}\n mode={mode}\n split={split}\n onMode={setMode}\n onWrap={wrap}\n onSave={() => void save()}\n clock={clock}\n bottom\n />\n )}\n </div>\n )\n}\n\nconst button =\n 'rounded-lg border border-neutral-300 bg-white px-2.5 py-2 text-sm font-medium disabled:opacity-40'\n\nfunction Toolbar({\n doc,\n config,\n headings,\n outlineOpen,\n onOutline,\n mode,\n split,\n onMode,\n onWrap,\n onSave,\n clock,\n bottom = false,\n}: {\n doc: Doc\n config: EditorConfig\n headings: number\n outlineOpen: boolean\n onOutline: () => void\n mode: 'source' | 'preview'\n split: boolean\n onMode: (mode: 'source' | 'preview') => void\n onWrap: (marker: string) => void\n onSave: () => void\n clock: ClockAdapter\n bottom?: boolean\n}) {\n return (\n <div\n data-golem-toolbar={bottom ? 'bottom' : 'top'}\n className={`flex shrink-0 flex-wrap items-center gap-2 bg-white px-3 py-2 ${\n bottom ? 'border-t border-neutral-200' : 'border-b border-neutral-200'\n }`}\n >\n {!config.readOnly && (\n <>\n <button type=\"button\" onClick={() => onWrap('**')} aria-label=\"Bold\" className={button}>\n <span className=\"font-bold\">B</span>\n </button>\n <button type=\"button\" onClick={() => onWrap('*')} aria-label=\"Italic\" className={button}>\n <span className=\"italic\">I</span>\n </button>\n </>\n )}\n\n {config.outline && headings > 0 && (\n <button type=\"button\" onClick={onOutline} aria-expanded={outlineOpen} className={button}>\n Outline\n </button>\n )}\n\n {config.preview !== 'off' && !split && (\n <button\n type=\"button\"\n onClick={() => onMode(mode === 'source' ? 'preview' : 'source')}\n className={button}\n >\n {mode === 'source' ? 'Preview' : 'Source'}\n </button>\n )}\n\n <Status doc={doc} config={config} clock={clock} />\n\n {!config.readOnly && (\n <button\n type=\"button\"\n onClick={onSave}\n disabled={doc.status !== 'dirty'}\n className=\"ml-auto rounded-lg bg-neutral-900 px-3 py-2 text-sm font-medium text-white disabled:opacity-40\"\n >\n Save\n </button>\n )}\n </div>\n )\n}\n\nfunction Status({ doc, config, clock }: { doc: Doc; config: EditorConfig; clock: ClockAdapter }) {\n const time = (at: Date) =>\n new Intl.DateTimeFormat(config.locale, {\n hour: '2-digit',\n minute: '2-digit',\n timeZone: clock.timeZone(),\n }).format(at)\n\n const line =\n doc.status === 'loading'\n ? 'Loading…'\n : doc.status === 'error'\n ? (doc.error ?? 'Something went wrong.')\n : doc.status === 'conflict'\n ? 'The agent changed lines you were editing.'\n : doc.status === 'saving'\n ? 'Saving…'\n : doc.status === 'dirty'\n ? 'Unsaved changes'\n : doc.savedAt\n ? `Saved ${time(doc.savedAt)}`\n : 'Saved'\n\n return (\n <p\n data-golem-status={doc.status}\n role={doc.status === 'error' ? 'alert' : undefined}\n className={`min-w-0 truncate text-xs ${\n doc.status === 'error' || doc.status === 'conflict' ? 'text-red-800' : 'text-neutral-500'\n }`}\n >\n {line}\n </p>\n )\n}\n\nfunction Outline({\n headings,\n onJump,\n wide,\n}: {\n headings: { line: number; level: number; text: string }[]\n onJump: (heading: { line: number; text: string }) => void\n wide: boolean\n}) {\n return (\n <nav\n data-golem-outline=\"\"\n aria-label=\"Outline\"\n className={`shrink-0 overflow-auto border-neutral-200 bg-neutral-50 p-2 ${\n wide ? 'w-56 border-r' : 'max-h-48 border-b'\n }`}\n >\n <ul className=\"space-y-0.5 text-sm\">\n {headings.map((heading) => (\n <li key={`${heading.line}-${heading.text}`}>\n <button\n type=\"button\"\n onClick={() => onJump(heading)}\n style={{ paddingLeft: `${(heading.level - 1) * 12 + 8}px` }}\n className=\"block w-full truncate rounded py-1 pr-2 text-left hover:bg-neutral-200\"\n >\n {heading.text}\n </button>\n </li>\n ))}\n </ul>\n </nav>\n )\n}\n\n/**\n * The lines the two writers disagree on, side by side, with the untouched document around them for\n * context. Nothing is applied until every one of them has been chosen: a half-resolved document is\n * one neither writer wrote.\n */\nfunction Conflict({\n chunks,\n choices,\n onChoose,\n}: {\n chunks: Merge3Chunk[]\n choices: ('mine' | 'theirs' | null)[]\n onChoose: (index: number, side: 'mine' | 'theirs') => void\n}) {\n return (\n <div data-golem-pane=\"conflict\" className=\"min-h-0 flex-1 overflow-auto p-3\">\n <p className=\"mb-3 rounded-lg bg-amber-50 px-3 py-2 text-sm text-amber-900\">\n The agent rewrote lines you were editing. Choose which version stays; the rest of the\n document is already merged.\n </p>\n <div className=\"space-y-2 font-mono text-[13px] leading-6\">\n {chunks.map((chunk, index) =>\n chunk.kind === 'clean' ? (\n <pre key={index} className=\"overflow-x-auto whitespace-pre-wrap text-neutral-500\">\n {chunk.lines.join('\\n')}\n </pre>\n ) : (\n <div\n key={index}\n data-golem-conflict=\"\"\n data-chosen={choices[index] ?? undefined}\n className=\"overflow-hidden rounded-lg border-2 border-amber-400\"\n >\n <Side\n label=\"Yours\"\n lines={chunk.mine}\n chosen={choices[index] === 'mine'}\n action=\"Keep mine\"\n onChoose={() => onChoose(index, 'mine')}\n />\n <Side\n label=\"The agent’s\"\n lines={chunk.theirs}\n chosen={choices[index] === 'theirs'}\n action=\"Take theirs\"\n onChoose={() => onChoose(index, 'theirs')}\n />\n </div>\n ),\n )}\n </div>\n </div>\n )\n}\n\nfunction Side({\n label,\n lines,\n chosen,\n action,\n onChoose,\n}: {\n label: string\n lines: string[]\n chosen: boolean\n action: string\n onChoose: () => void\n}): ReactNode {\n return (\n <div className={`border-b border-amber-200 p-2 last:border-0 ${chosen ? 'bg-emerald-50' : ''}`}>\n <div className=\"mb-1 flex items-center justify-between gap-2\">\n <span className=\"font-sans text-xs font-semibold text-neutral-500\">{label}</span>\n <button\n type=\"button\"\n onClick={onChoose}\n className=\"shrink-0 rounded-lg border border-neutral-300 bg-white px-2.5 py-1.5 font-sans text-xs font-medium\"\n >\n {action}\n </button>\n </div>\n <pre className=\"overflow-x-auto whitespace-pre-wrap\">{lines.join('\\n') || '(nothing)'}</pre>\n </div>\n )\n}\n\nexport const Editor = defineComponent<typeof editorConfigSchema, EditorAdapters, EditorSlots>({\n name: 'Editor',\n schema: editorConfigSchema,\n render: EditorBody,\n})\n","import type { RenderedField } from '../abi/fields'\n\nconst DATE_STYLES = ['short', 'medium', 'long', 'full'] as const\ntype DateStyle = (typeof DATE_STYLES)[number]\n\nfunction dateStyle(format: string | undefined, fallback: DateStyle): DateStyle {\n return DATE_STYLES.includes(format as DateStyle) ? (format as DateStyle) : fallback\n}\n\n/**\n * A `user` value is either the name as a string or a record with one on it, because a records\n * adapter may hand back either and the list should not care which.\n */\nexport function userName(value: unknown): string {\n if (typeof value === 'string') return value\n if (value && typeof value === 'object') {\n const named = value as { name?: unknown; email?: unknown }\n if (typeof named.name === 'string') return named.name\n if (typeof named.email === 'string') return named.email\n }\n return ''\n}\n\nexport function initials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean)\n if (parts.length === 0) return '·'\n return parts\n .slice(0, 2)\n .map((part) => part[0]!.toUpperCase())\n .join('')\n}\n\n/**\n * Every value is formatted in UTC, so a row reads the same in every timezone — for a local reading,\n * put local time in the record. An unparseable date falls back to the raw string rather than to\n * `Invalid Date`: the reader should see what the record actually holds.\n */\nexport function formatValue(value: unknown, field: RenderedField): string {\n if (value === null || value === undefined || value === '') return '—'\n\n switch (field.type) {\n case 'number': {\n const digits = Number(field.format)\n return new Intl.NumberFormat('en-US', {\n minimumFractionDigits: Number.isFinite(digits) ? digits : undefined,\n maximumFractionDigits: Number.isFinite(digits) ? digits : undefined,\n }).format(Number(value))\n }\n case 'money':\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: field.format || 'USD',\n }).format(Number(value))\n case 'date':\n case 'datetime': {\n const at = new Date(field.type === 'date' ? `${String(value)}T12:00:00Z` : String(value))\n if (Number.isNaN(at.getTime())) return String(value)\n return new Intl.DateTimeFormat('en-GB', {\n dateStyle: dateStyle(field.format, 'medium'),\n timeStyle: field.type === 'datetime' ? 'short' : undefined,\n timeZone: 'UTC',\n }).format(at)\n }\n case 'boolean':\n return value ? 'Yes' : 'No'\n case 'user':\n return userName(value) || String(value)\n default:\n return String(value)\n }\n}\n\nconst CHIP_TONES = [\n 'bg-emerald-100 text-emerald-800',\n 'bg-amber-100 text-amber-800',\n 'bg-sky-100 text-sky-800',\n 'bg-violet-100 text-violet-800',\n 'bg-rose-100 text-rose-800',\n 'bg-neutral-200 text-neutral-700',\n]\n\n/**\n * An enum chip's colour is derived from the value itself, so the same status is the same colour on\n * every screen and no config has to name a palette.\n */\nexport function chipTone(value: string): string {\n let hash = 0\n for (let i = 0; i < value.length; i++) hash = (hash * 31 + value.charCodeAt(i)) >>> 0\n return CHIP_TONES[hash % CHIP_TONES.length]!\n}\n","import { z } from 'zod'\nimport { formFieldSchema } from '../../abi/fields'\n\nexport const recordFormConfigSchema = z\n .object({\n collection: z\n .string()\n .min(1)\n .describe('The record type the adapter is asked for, passed to every read and every write.'),\n fields: z\n .array(formFieldSchema)\n .min(1)\n .describe(\n 'The controls, in the order they are shown. Each is `{ key, label, type, … }` from the same field vocabulary `RecordList` reads: text, number, date, datetime, enum, boolean, money, user.',\n ),\n mode: z\n .enum(['create', 'edit'])\n .describe(\n '`create` opens blank and calls `create`; `edit` loads the record named by the `recordId` prop and calls `update` with only what changed.',\n ),\n versionField: z\n .string()\n .min(1)\n .default('version')\n .describe(\n 'The field holding a number that goes up on every write. When the loaded record has a number there, saving sends it as `expectedVersion`, so a save that arrived second is refused and the reader chooses whose values win. A record with no number there saves as a plain update.',\n ),\n layout: z\n .enum(['single', 'two-column'])\n .default('single')\n .describe(\n 'How the controls are arranged above 768px: `single` is one column at a readable width, `two-column` pairs them. Below 768px both are one column, whatever this says.',\n ),\n submitLabel: z\n .string()\n .min(1)\n .default('Save')\n .describe('The button that writes. Name the outcome — \"Save ticket\", not \"Submit\".'),\n cancel: z\n .enum(['back', 'none'])\n .default('back')\n .describe(\n '`back` puts a Cancel button beside submit and calls the `onCancel` prop; `none` leaves the form with no way out, for a form that fills a page of its own.',\n ),\n successMessage: z\n .string()\n .min(1)\n .default('Saved.')\n .describe('The line shown once the adapter has taken the write.'),\n deleteAllowed: z\n .boolean()\n .default(false)\n .describe(\n 'Adds a Delete button that asks once before calling `remove`. Only in `edit` mode, and only for a reader allowed to delete — the call site decides that by what it puts here.',\n ),\n })\n .strict()\n .superRefine((config, ctx) => {\n for (const [index, field] of config.fields.entries()) {\n if (field.type === 'enum' && !field.options) {\n ctx.addIssue({\n code: 'custom',\n path: ['fields', index, 'options'],\n message: `The enum field \"${field.key}\" has no \\`options\\`, so there is nothing to pick. Give it \\`[{ id, label }]\\`.`,\n })\n }\n if (field.type !== 'enum' && field.options) {\n ctx.addIssue({\n code: 'custom',\n path: ['fields', index, 'options'],\n message: `\\`options\\` belongs to an enum field, and \"${field.key}\" is typed ${field.type}. Change the type or drop the options.`,\n })\n }\n if (field.required && field.readOnly) {\n ctx.addIssue({\n code: 'custom',\n path: ['fields', index],\n message: `The field \"${field.key}\" is both \\`required\\` and \\`readOnly\\`, so nobody can ever satisfy it. Pick one.`,\n })\n }\n if (field.min !== undefined && field.max !== undefined && field.min > field.max) {\n ctx.addIssue({\n code: 'custom',\n path: ['fields', index, 'min'],\n message: `The field \"${field.key}\" has a \\`min\\` above its \\`max\\`, so no value passes.`,\n })\n }\n }\n if (config.mode === 'create' && config.deleteAllowed) {\n ctx.addIssue({\n code: 'custom',\n path: ['deleteAllowed'],\n message: 'A record being created cannot be deleted. `deleteAllowed` belongs to edit mode.',\n })\n }\n })\n\nexport type RecordFormConfig = z.output<typeof recordFormConfigSchema>\nexport type RecordFormConfigInput = z.input<typeof recordFormConfigSchema>\n","import type { FormField, RecordRow } from '../../abi/fields'\nimport { formatValue } from '../../lib/format'\n\n/** What an untouched control holds in create mode. A checkbox is off; everything else is blank. */\nexport function blankValue(field: FormField): unknown {\n return field.type === 'boolean' ? false : ''\n}\n\nexport function isEmpty(value: unknown): boolean {\n return value === null || value === undefined || value === ''\n}\n\n/**\n * The record's value as the control shows it. `datetime` is the one that has to be reshaped: a\n * record holds a full ISO instant and `datetime-local` takes minutes in UTC.\n */\nexport function toInput(value: unknown, field: FormField): string {\n if (isEmpty(value)) return ''\n if (field.type === 'datetime') {\n const at = new Date(String(value))\n return Number.isNaN(at.getTime()) ? String(value) : at.toISOString().slice(0, 16)\n }\n return String(value)\n}\n\n/** The control's text as the record holds it. An empty control writes an empty string, never `0`. */\nexport function fromInput(text: string, field: FormField): unknown {\n if (text === '') return ''\n if (field.type === 'number' || field.type === 'money') {\n const parsed = Number(text)\n // A number the browser could not parse is kept as typed, so validation can name it.\n return Number.isNaN(parsed) ? text : parsed\n }\n if (field.type === 'datetime') {\n const at = new Date(`${text}Z`)\n return Number.isNaN(at.getTime()) ? text : at.toISOString()\n }\n return text\n}\n\n/** Numbers compare as numbers; dates are ISO, so they compare as strings. */\nfunction below(value: unknown, bound: unknown, field: FormField): boolean {\n if (field.type === 'number' || field.type === 'money') return Number(value) < Number(bound)\n return String(value) < String(bound)\n}\n\n/**\n * One field's complaint, or `null` when it is fine. The sentence is what the reader sees under the\n * control, so it names the field the way its label does.\n */\nexport function validateField(field: FormField, value: unknown): string | null {\n if (field.readOnly) return null\n\n if (isEmpty(value)) {\n return field.required ? `${field.label} is required.` : null\n }\n if ((field.type === 'number' || field.type === 'money') && typeof value !== 'number') {\n return `${field.label} must be a number.`\n }\n if (field.type === 'enum' && !field.options?.some((option) => option.id === value)) {\n return `${field.label} must be one of the offered choices.`\n }\n if (field.min !== undefined && below(value, field.min, field)) {\n return `${field.label} must be ${formatValue(field.min, field)} or more.`\n }\n if (field.max !== undefined && below(field.max, value, field)) {\n return `${field.label} must be ${formatValue(field.max, field)} or less.`\n }\n return null\n}\n\n/** Every editable field that fails, in the order the form shows them. */\nexport function validateAll(\n fields: FormField[],\n values: RecordRow,\n): { key: string; message: string }[] {\n const failures: { key: string; message: string }[] = []\n for (const field of fields) {\n const message = validateField(field, values[field.key])\n if (message !== null) failures.push({ key: field.key, message })\n }\n return failures\n}\n\n/** Every editable field's value, which is what `create` sends: a new record has nothing to diff. */\nexport function dataOf(fields: FormField[], values: RecordRow): RecordRow {\n const data: RecordRow = {}\n for (const field of fields) data[field.key] = values[field.key]\n return data\n}\n\n/** What the reader actually changed. `readOnly` fields are never in it: the store owns them. */\nexport function patchOf(fields: FormField[], values: RecordRow, initial: RecordRow): RecordRow {\n const patch: RecordRow = {}\n for (const field of fields) {\n if (field.readOnly) continue\n if (values[field.key] !== initial[field.key]) patch[field.key] = values[field.key]\n }\n return patch\n}\n","import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type FormEvent,\n type ReactNode,\n} from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { FormField, RecordRow } from '../../abi/fields'\nimport {\n conflictingRecord,\n refusedFields,\n type IdentityAdapter,\n type RecordsAdapter,\n type User,\n} from '../../adapters'\nimport { chipTone, formatValue, initials, userName } from '../../lib/format'\nimport { useContainerWidth } from '../../lib/use-container-width'\nimport { recordFormConfigSchema, type RecordFormConfig } from './RecordForm.config'\nimport {\n blankValue,\n dataOf,\n fromInput,\n patchOf,\n toInput,\n validateAll,\n validateField,\n} from './values'\n\nexport interface RecordFormAdapters {\n records: RecordsAdapter\n /** Read only for a `user` field whose choices come from `users: 'members'`. */\n identity?: IdentityAdapter\n}\n\nexport interface RecordFormSlots {\n /**\n * The record `edit` mode loads and writes. The route knows it and the config does not, which is\n * why it is a prop: one config serves every ticket on the board.\n */\n recordId?: string\n /** After a write has landed: the stored row, or `null` when the record was deleted. */\n onDone?: (row: RecordRow | null) => void\n /** Cancel, once the reader has confirmed discarding whatever was unsaved. */\n onCancel?: () => void\n}\n\n/** One column below this width, whatever `layout` says. It is Shell's default breakpoint. */\nconst ONE_COLUMN_BELOW = 768\n\nconst messageOf = (cause: unknown) =>\n cause instanceof Error ? cause.message : 'The write did not go through. Try again.'\n\nconst controlClass =\n 'mt-1 w-full rounded-lg border bg-white px-3 py-2.5 text-base text-neutral-900 sm:text-sm dark:bg-neutral-900 dark:text-neutral-100'\nconst okBorder = 'border-neutral-300 dark:border-neutral-700'\nconst badBorder = 'border-red-400'\n\n/**\n * Loads the record `edit` mode is about. Create mode never asks, and neither does edit mode with no\n * id — both are answered without a round trip. `null` means a load is still in flight.\n */\nfunction useLoadedRecord(\n records: RecordsAdapter,\n config: RecordFormConfig,\n recordId: string | undefined,\n) {\n const [state, setState] = useState<{ id: string; row: RecordRow | null; error: string | null }>()\n const wanted = config.mode === 'edit' ? recordId : undefined\n\n useEffect(() => {\n if (wanted === undefined) return undefined\n let live = true\n void records.get<RecordRow>(config.collection, wanted).then(\n (row) => {\n if (!live) return\n setState({\n id: wanted,\n row,\n error: row ? null : `There is no ${config.collection} record with the id ${wanted}.`,\n })\n },\n (cause: unknown) => {\n if (live) setState({ id: wanted, row: null, error: messageOf(cause) })\n },\n )\n return () => {\n live = false\n }\n }, [records, config.collection, wanted])\n\n if (config.mode !== 'edit') return { row: null, error: null }\n if (recordId === undefined) {\n return { row: null, error: 'This form is in edit mode with no `recordId` to load.' }\n }\n // A result for a record the form has moved on from is still in flight for this one.\n return state?.id === recordId ? state : null\n}\n\n/** The member list a `user` field picks from. Absent Identity leaves it empty, and the field says so. */\nfunction useMembers(config: RecordFormConfig, identity: IdentityAdapter | undefined) {\n const [members, setMembers] = useState<User[] | null>(null)\n const wanted = config.fields.some((field) => field.type === 'user' && field.users === 'members')\n\n useEffect(() => {\n if (!wanted || !identity) return undefined\n let live = true\n void identity.listMembers().then(\n (list) => {\n if (live) setMembers(list)\n },\n () => {\n if (live) setMembers([])\n },\n )\n return () => {\n live = false\n }\n }, [wanted, identity])\n\n return members\n}\n\nfunction ReadOnlyValue({ field, value }: { field: FormField; value: unknown }): ReactNode {\n if (field.type === 'enum') {\n const label = field.options?.find((option) => option.id === value)?.label ?? String(value ?? '')\n if (label === '') return <span className=\"text-neutral-400\">—</span>\n return (\n <span\n className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${chipTone(String(value))}`}\n >\n {label}\n </span>\n )\n }\n if (field.type === 'user') {\n const name = userName(value) || String(value ?? '')\n if (name === '') return <span className=\"text-neutral-400\">—</span>\n return (\n <span className=\"flex items-center gap-2\">\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-full bg-neutral-900 text-[10px] font-semibold text-white dark:bg-neutral-200 dark:text-neutral-900\">\n {initials(name)}\n </span>\n {name}\n </span>\n )\n }\n return <span>{formatValue(value, field)}</span>\n}\n\nfunction RecordFormBody({\n config,\n adapters,\n recordId,\n onDone,\n onCancel,\n}: GolemProps<RecordFormConfig, RecordFormAdapters, RecordFormSlots>) {\n const formId = useId()\n const loaded = useLoadedRecord(adapters.records, config, recordId)\n const members = useMembers(config, adapters.identity)\n\n const blank = useMemo(() => {\n const start: RecordRow = {}\n for (const field of config.fields) start[field.key] = blankValue(field)\n return start\n }, [config.fields])\n\n // Nothing is copied out of the loaded record into state: `base` is what the store says plus what\n // has already been written, and `edits` is what the reader has changed on top of it. That keeps\n // one source of truth for every value and makes \"dirty\" a comparison rather than a flag.\n const [edits, setEdits] = useState<RecordRow>({})\n const [committed, setCommitted] = useState<RecordRow>({})\n const [errors, setErrors] = useState<Record<string, string>>({})\n const [formError, setFormError] = useState<string | null>(null)\n const [saved, setSaved] = useState(false)\n const [busy, setBusy] = useState(false)\n const [asking, setAsking] = useState<'cancel' | 'delete' | null>(null)\n // The row a stale save was refused over, and the fields on this form it changed.\n const [conflict, setConflict] = useState<{ row: RecordRow; changed: FormField[] } | null>(null)\n\n const controls = useRef<Record<string, HTMLElement | null>>({})\n\n const baseOf = useCallback(\n (row: RecordRow) => {\n const start: RecordRow = { ...blank }\n for (const field of config.fields) {\n if (row[field.key] !== undefined) start[field.key] = row[field.key]\n }\n return start\n },\n [blank, config.fields],\n )\n\n // The row as the store was last known to hold it: loaded, then every write and conflict since.\n const stored = useMemo(() => ({ ...loaded?.row, ...committed }), [loaded, committed])\n const base = useMemo(() => baseOf(stored), [baseOf, stored])\n\n const values = useMemo(() => ({ ...base, ...edits }), [base, edits])\n\n const editable = useMemo(() => config.fields.filter((field) => !field.readOnly), [config.fields])\n const dirty = editable.some((field) => values[field.key] !== base[field.key])\n\n const root = useRef<HTMLDivElement>(null)\n const width = useContainerWidth(root)\n const columns = config.layout === 'two-column' && width >= ONE_COLUMN_BELOW ? 2 : 1\n\n const put = (field: FormField, value: unknown) => {\n setSaved(false)\n setEdits((prev) => ({ ...prev, [field.key]: value }))\n }\n\n const check = (field: FormField) =>\n setErrors((prev) => {\n const message = validateField(field, values[field.key])\n const next = { ...prev }\n if (message === null) delete next[field.key]\n else next[field.key] = message\n return next\n })\n\n const land = useCallback((failures: { key: string; message: string }[]) => {\n setErrors(Object.fromEntries(failures.map(({ key, message }) => [key, message])))\n controls.current[failures[0]!.key]?.focus()\n }, [])\n\n const versionOf = (row: RecordRow) => {\n const version = row[config.versionField]\n return typeof version === 'number' ? version : undefined\n }\n\n /**\n * The one write. `from` is what the store is taken to hold and `next` what the form shows. An edit\n * of a versioned record goes out with the version it was read at, so a save that arrived second\n * is refused with the row as it now stands, and the reader chooses instead of anyone overwriting.\n */\n const write = (from: RecordRow, next: RecordRow, expected: number | undefined) => {\n setFormError(null)\n setErrors({})\n setBusy(true)\n // Optimistic: the form says it is saved before the adapter has answered, and a refusal takes\n // it back. The reader sees the common case immediately and the rare one accurately.\n setSaved(true)\n\n const patch = patchOf(editable, next, from)\n const request =\n config.mode === 'create'\n ? adapters.records.create<RecordRow>(config.collection, dataOf(editable, next))\n : expected === undefined\n ? adapters.records.update<RecordRow>(config.collection, recordId!, patch)\n : adapters.records.update<RecordRow>(\n config.collection,\n recordId!,\n { ...patch, [config.versionField]: expected + 1 },\n { expectedVersion: expected, versionField: config.versionField },\n )\n\n void request.then(\n (row) => {\n setBusy(false)\n // What was just written stops counting as unsaved: create empties the form, edit folds the\n // changes into what the store is now taken to hold.\n if (config.mode === 'edit') setCommitted((prev) => ({ ...prev, ...next, ...row }))\n setEdits({})\n onDone?.(row)\n },\n (cause: unknown) => {\n setBusy(false)\n setSaved(false)\n const theirs = conflictingRecord(cause)\n if (theirs) {\n const now = baseOf(theirs)\n setConflict({\n row: theirs,\n // The version is bookkeeping, not something the reader chooses between.\n changed: config.fields.filter(\n (field) => field.key !== config.versionField && now[field.key] !== from[field.key],\n ),\n })\n return\n }\n const refused = refusedFields(cause)\n if (refused) land(refused.map((one) => ({ key: one.field, message: one.message })))\n else setFormError(messageOf(cause))\n },\n )\n }\n\n /** Drops the unsaved values and shows the record as the other writer left it. */\n const takeTheirs = () => {\n setCommitted((prev) => ({ ...prev, ...conflict!.row }))\n setEdits({})\n setConflict(null)\n setSaved(false)\n }\n\n /**\n * Writes the reader's changes over the newer version. Only fields the reader actually changed go\n * out, so whatever else the other writer changed survives.\n */\n const keepMine = () => {\n // The controls stay live while the choice is open, so what is on screen is checked again.\n const failures = validateAll(editable, values)\n if (failures.length > 0) {\n land(failures)\n return\n }\n const mine = Object.fromEntries(\n Object.entries(edits).filter(([key, value]) => value !== base[key]),\n )\n const row = { ...stored, ...conflict!.row }\n setCommitted((prev) => ({ ...prev, ...conflict!.row }))\n setEdits(mine)\n setConflict(null)\n const from = baseOf(row)\n write(from, { ...from, ...mine }, versionOf(row))\n }\n\n const submit = (event: FormEvent) => {\n event.preventDefault()\n // Enter in a control while the conflict is open must not pick a side for the reader.\n if (conflict !== null) return\n setFormError(null)\n\n const failures = validateAll(editable, values)\n if (failures.length > 0) {\n land(failures)\n return\n }\n\n write(base, values, versionOf(stored))\n }\n\n const cancel = () => {\n if (dirty) setAsking('cancel')\n else onCancel?.()\n }\n\n const remove = () => {\n setBusy(true)\n setFormError(null)\n void adapters.records.remove(config.collection, recordId!).then(\n () => {\n setBusy(false)\n setAsking(null)\n onDone?.(null)\n },\n (cause: unknown) => {\n setBusy(false)\n setAsking(null)\n setFormError(messageOf(cause))\n },\n )\n }\n\n if (loaded === null) {\n return (\n <p className=\"rounded-xl border border-neutral-200 bg-white px-4 py-8 dark:border-neutral-800 dark:bg-neutral-900 text-center text-sm text-neutral-500 dark:text-neutral-400\">\n Loading…\n </p>\n )\n }\n if (loaded.error !== null) {\n return (\n <p\n role=\"alert\"\n className=\"rounded-xl border border-red-300 bg-red-50 px-4 py-6 text-center text-sm text-red-900 dark:border-red-800 dark:bg-red-950 dark:text-red-200\"\n >\n {loaded.error}\n </p>\n )\n }\n\n const control = (field: FormField): ReactNode => {\n const id = `${formId}-${field.key}`\n const bad = errors[field.key] !== undefined\n const described = [field.help ? `${id}-help` : null, bad ? `${id}-error` : null]\n .filter(Boolean)\n .join(' ')\n const shared = {\n id,\n name: field.key,\n 'aria-invalid': bad || undefined,\n 'aria-describedby': described || undefined,\n 'aria-required': field.required || undefined,\n onBlur: () => check(field),\n className: `${controlClass} ${bad ? badBorder : okBorder}`,\n }\n const keep = (element: HTMLElement | null) => {\n controls.current[field.key] = element\n }\n\n if (field.type === 'boolean') {\n return (\n <input\n {...shared}\n ref={keep}\n type=\"checkbox\"\n checked={values[field.key] === true}\n onChange={(event) => put(field, event.target.checked)}\n className={`mt-1 size-5 rounded border ${bad ? badBorder : okBorder}`}\n />\n )\n }\n if (field.type === 'enum') {\n return (\n <select\n {...shared}\n ref={keep}\n value={String(values[field.key] ?? '')}\n onChange={(event) => put(field, event.target.value)}\n >\n <option value=\"\">{field.placeholder ?? 'Choose…'}</option>\n {field.options?.map((option) => (\n <option key={option.id} value={option.id}>\n {option.label}\n </option>\n ))}\n </select>\n )\n }\n if (field.type === 'user' && field.users === 'members') {\n return (\n <select\n {...shared}\n ref={keep}\n value={String(values[field.key] ?? '')}\n onChange={(event) => put(field, event.target.value)}\n >\n <option value=\"\">\n {members === null || members.length === 0 ? 'No members to pick from' : 'Choose…'}\n </option>\n {members?.map((member) => (\n <option key={member.id} value={member.name}>\n {member.name}\n </option>\n ))}\n </select>\n )\n }\n if (field.type === 'text' && field.multiline) {\n return (\n <textarea\n {...shared}\n ref={keep}\n rows={4}\n placeholder={field.placeholder}\n value={toInput(values[field.key], field)}\n onChange={(event) => put(field, event.target.value)}\n />\n )\n }\n\n const numeric = field.type === 'number' || field.type === 'money'\n return (\n <input\n {...shared}\n ref={keep}\n type={\n field.type === 'date'\n ? 'date'\n : field.type === 'datetime'\n ? 'datetime-local'\n : numeric\n ? 'number'\n : 'text'\n }\n step={field.type === 'money' ? '0.01' : undefined}\n min={numeric && field.min !== undefined ? Number(field.min) : undefined}\n max={numeric && field.max !== undefined ? Number(field.max) : undefined}\n placeholder={field.placeholder}\n value={toInput(values[field.key], field)}\n onChange={(event) => put(field, fromInput(event.target.value, field))}\n />\n )\n }\n\n const actions = (() => {\n if (asking === 'cancel') {\n return (\n <div role=\"alertdialog\" aria-label=\"Discard changes\" className=\"flex flex-wrap gap-2\">\n <p className=\"w-full text-sm text-neutral-700 dark:text-neutral-300\">\n You have changes that have not been saved.\n </p>\n <button\n type=\"button\"\n onClick={() => {\n setAsking(null)\n onCancel?.()\n }}\n className=\"rounded-lg bg-red-600 px-4 py-2.5 text-sm font-medium text-white\"\n >\n Discard them\n </button>\n <button\n type=\"button\"\n onClick={() => setAsking(null)}\n className=\"rounded-lg border border-neutral-300 dark:border-neutral-700 px-4 py-2.5 text-sm font-medium\"\n >\n Keep editing\n </button>\n </div>\n )\n }\n if (conflict !== null) {\n return (\n <div role=\"alertdialog\" aria-label=\"Saved by someone else\" className=\"flex flex-wrap gap-2\">\n <p className=\"w-full text-sm font-medium text-amber-800 dark:text-amber-300\">\n Someone else saved this record after you opened it. Your changes are not saved yet.\n </p>\n {conflict.changed.length === 0 ? (\n <p className=\"w-full text-sm text-neutral-700 dark:text-neutral-300\">\n None of the fields here changed.\n </p>\n ) : (\n <dl className=\"mb-1 grid w-full grid-cols-[auto_1fr_1fr] gap-x-3 gap-y-1 text-sm\">\n <dt className=\"sr-only\">Field</dt>\n <dd className=\"col-start-2 text-xs text-neutral-500 dark:text-neutral-400\">\n Saved now\n </dd>\n <dd className=\"text-xs text-neutral-500 dark:text-neutral-400\">Yours</dd>\n {conflict.changed.map((field) => (\n <div key={field.key} className=\"contents\">\n <dt className=\"font-medium text-neutral-700 dark:text-neutral-300\">\n {field.label}\n </dt>\n <dd className=\"min-w-0 break-words\">\n <ReadOnlyValue field={field} value={conflict.row[field.key]} />\n </dd>\n <dd className=\"min-w-0 break-words\">\n {edits[field.key] !== undefined && edits[field.key] !== base[field.key] ? (\n <ReadOnlyValue field={field} value={values[field.key]} />\n ) : (\n <span className=\"text-neutral-400\">not changed</span>\n )}\n </dd>\n </div>\n ))}\n </dl>\n )}\n <button\n type=\"button\"\n onClick={keepMine}\n disabled={busy}\n className=\"rounded-lg bg-neutral-900 px-4 py-2.5 text-sm font-medium text-white dark:bg-neutral-200 dark:text-neutral-900 disabled:opacity-60\"\n >\n Keep mine\n </button>\n <button\n type=\"button\"\n onClick={takeTheirs}\n disabled={busy}\n className=\"rounded-lg border border-neutral-300 dark:border-neutral-700 px-4 py-2.5 text-sm font-medium\"\n >\n Take theirs\n </button>\n {config.cancel === 'back' && (\n <button\n type=\"button\"\n onClick={cancel}\n className=\"rounded-lg border border-neutral-300 dark:border-neutral-700 px-4 py-2.5 text-sm font-medium\"\n >\n Cancel\n </button>\n )}\n </div>\n )\n }\n if (asking === 'delete') {\n return (\n <div role=\"alertdialog\" aria-label=\"Confirm delete\" className=\"flex flex-wrap gap-2\">\n <p className=\"w-full text-sm text-neutral-700 dark:text-neutral-300\">\n Deleting this record cannot be undone.\n </p>\n <button\n type=\"button\"\n onClick={remove}\n disabled={busy}\n className=\"rounded-lg bg-red-600 px-4 py-2.5 text-sm font-medium text-white disabled:opacity-60\"\n >\n Delete permanently\n </button>\n <button\n type=\"button\"\n onClick={() => setAsking(null)}\n className=\"rounded-lg border border-neutral-300 dark:border-neutral-700 px-4 py-2.5 text-sm font-medium\"\n >\n Keep it\n </button>\n </div>\n )\n }\n return (\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"submit\"\n disabled={busy}\n className=\"flex-1 rounded-lg bg-neutral-900 px-4 py-2.5 text-sm font-medium text-white dark:bg-neutral-200 dark:text-neutral-900 disabled:opacity-60 sm:flex-none\"\n >\n {config.submitLabel}\n </button>\n {config.cancel === 'back' && (\n <button\n type=\"button\"\n onClick={cancel}\n className=\"rounded-lg border border-neutral-300 dark:border-neutral-700 px-4 py-2.5 text-sm font-medium\"\n >\n Cancel\n </button>\n )}\n {config.deleteAllowed && (\n <button\n type=\"button\"\n onClick={() => setAsking('delete')}\n className=\"ml-auto rounded-lg border border-red-300 px-4 dark:border-red-800 py-2.5 text-sm font-medium text-red-700 dark:text-red-300\"\n >\n Delete\n </button>\n )}\n </div>\n )\n })()\n\n return (\n <div\n ref={root}\n data-golem-component=\"RecordForm\"\n data-layout={columns === 2 ? 'two-column' : 'single'}\n className=\"golem-record-form w-full text-neutral-900 dark:text-neutral-100 dark:[color-scheme:dark]\"\n >\n <form onSubmit={submit} noValidate>\n <div className={`grid gap-4 ${columns === 2 ? 'grid-cols-2' : 'grid-cols-1'}`}>\n {config.fields.map((field) => {\n const id = `${formId}-${field.key}`\n const wide = columns === 2 && field.multiline ? 'col-span-2' : ''\n return (\n <div key={field.key} className={wide}>\n <label\n htmlFor={id}\n className=\"block text-sm font-medium text-neutral-700 dark:text-neutral-300\"\n >\n {field.label}\n {field.required && (\n <span aria-hidden=\"true\" className=\"text-red-600 dark:text-red-400\">\n {' '}\n *\n </span>\n )}\n </label>\n {field.readOnly ? (\n <p\n id={id}\n className=\"mt-1 rounded-lg bg-neutral-100 px-3 dark:bg-neutral-800 py-2.5 text-sm text-neutral-700 dark:text-neutral-300\"\n >\n <ReadOnlyValue field={field} value={values[field.key]} />\n </p>\n ) : (\n control(field)\n )}\n {field.help && (\n <p\n id={`${id}-help`}\n className=\"mt-1 text-xs text-neutral-500 dark:text-neutral-400\"\n >\n {field.help}\n </p>\n )}\n {errors[field.key] && (\n <p\n id={`${id}-error`}\n role=\"alert\"\n className=\"mt-1 text-xs font-medium text-red-700 dark:text-red-300\"\n >\n {errors[field.key]}\n </p>\n )}\n </div>\n )\n })}\n </div>\n\n {/* Thumb-reachable on a phone, and it stays put while a long form scrolls under it. */}\n <div className=\"sticky bottom-0 mt-5 border-t border-neutral-200 bg-white py-3 dark:border-neutral-800 dark:bg-neutral-900\">\n {formError !== null && (\n <p role=\"alert\" className=\"mb-2 text-sm font-medium text-red-700 dark:text-red-300\">\n {formError}\n </p>\n )}\n {saved && (\n <p\n role=\"status\"\n className=\"mb-2 text-sm font-medium text-emerald-700 dark:text-emerald-300\"\n >\n {config.successMessage}\n </p>\n )}\n {config.mode === 'edit' && dirty && !saved && (\n <p className=\"mb-2 text-xs font-medium text-amber-700 dark:text-amber-300\">\n Unsaved changes\n </p>\n )}\n {actions}\n </div>\n </form>\n </div>\n )\n}\n\nexport const RecordForm = defineComponent<\n typeof recordFormConfigSchema,\n RecordFormAdapters,\n RecordFormSlots\n>({\n name: 'RecordForm',\n schema: recordFormConfigSchema,\n render: RecordFormBody,\n})\n","import { z } from 'zod'\nimport { listFieldSchema } from '../../abi/fields'\n\nexport const recordListConfigSchema = z\n .object({\n collection: z\n .string()\n .min(1)\n .describe('The record type the adapter is asked for, passed to `list` and `subscribe`.'),\n fields: z\n .array(listFieldSchema)\n .min(1)\n .describe(\n 'The columns, in the order they are shown. Each is `{ key, label, type, format?, primary? }`, where `type` is one of text, number, date, datetime, enum, boolean, money, user.',\n ),\n sort: z\n .object({\n field: z.string().min(1).describe('The `key` of the field rows are ordered by.'),\n direction: z.enum(['asc', 'desc']).describe('Ascending or descending, to start with.'),\n })\n .strict()\n .optional()\n .describe(\n 'The order the list opens in. The reader changes it from the column headings; leave it out and the adapter’s own order is what arrives.',\n ),\n scope: z\n .record(\n z.string(),\n z.union([\n z.string(),\n z.number(),\n z.boolean(),\n z.array(z.union([z.string(), z.number(), z.boolean()])),\n ]),\n )\n .default({})\n .describe(\n 'A fixed narrowing applied to every query and never shown as a control: `{ status: [\"waiting\", \"in progress\"] }` is a list of what is still open. An array means any of. This is how one collection feeds several screens.',\n ),\n filters: z\n .array(z.string().min(1))\n .default([])\n .describe(\n 'Field keys that get a row of filter chips above the list, one chip per value seen. Use it for `enum` and `boolean` fields; every key must also appear in `fields`.',\n ),\n search: z\n .array(z.string().min(1))\n .default([])\n .describe(\n 'Field keys the search box matches, as a case-insensitive substring. Empty hides the search box. Keys need not appear in `fields` — a list can be searchable by a field it does not show.',\n ),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(200)\n .default(25)\n .describe('How many rows arrive per page, and how many more the load-more button fetches.'),\n emptyState: z\n .string()\n .min(1)\n .default('Nothing here yet.')\n .describe('One line shown in place of the list when a query matches no rows.'),\n rowAction: z\n .enum(['open', 'none'])\n .default('open')\n .describe(\n '`open` makes each row clickable and focusable and calls the `onOpen` prop with the record; `none` makes the list read-only.',\n ),\n density: z\n .enum(['compact', 'comfortable'])\n .default('comfortable')\n .describe(\n 'Row height. `comfortable` for a screen of its own, `compact` for a list sitting inside a panel among other blocks.',\n ),\n })\n .strict()\n .superRefine((config, ctx) => {\n const keys = new Set(config.fields.map((field) => field.key))\n\n if (config.fields.filter((field) => field.primary).length > 1) {\n ctx.addIssue({\n code: 'custom',\n path: ['fields'],\n message: 'More than one field sets `primary`. Exactly one field titles a row.',\n })\n }\n for (const [index, key] of config.filters.entries()) {\n if (!keys.has(key)) {\n ctx.addIssue({\n code: 'custom',\n path: ['filters', index],\n message: `No field has the key \"${key}\", so there is nothing to filter. Add it to \\`fields\\` first.`,\n })\n }\n }\n if (config.sort && !keys.has(config.sort.field)) {\n ctx.addIssue({\n code: 'custom',\n path: ['sort', 'field'],\n message: `No field has the key \"${config.sort.field}\", so the list cannot be sorted by it.`,\n })\n }\n })\n\nexport type RecordListConfig = z.output<typeof recordListConfigSchema>\nexport type RecordListConfigInput = z.input<typeof recordListConfigSchema>\n","import {\n Fragment,\n useCallback,\n useId,\n useEffect,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n} from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { FilterValue, RecordsAdapter } from '../../adapters'\nimport { useContainerWidth } from '../../lib/use-container-width'\nimport type { ListField, RecordRow } from '../../abi/fields'\nimport { chipTone, formatValue, initials, userName } from '../../lib/format'\nimport { recordListConfigSchema, type RecordListConfig } from './RecordList.config'\n\nexport type { RecordRow }\n\nexport interface RecordListAdapters {\n records: RecordsAdapter\n}\n\nexport interface RecordListSlots {\n /**\n * Where a row goes when it is opened — a click, Enter or Space. Only reached with\n * `rowAction: 'open'`; the list has no router of its own, so the call site names the route.\n */\n onOpen?: (row: RecordRow) => void\n}\n\n/**\n * The width the table gives way to cards. It is Shell's default breakpoint, so a list in Shell's\n * canvas turns into cards on the same phone that turns Shell into tabs.\n */\nconst CARDS_BELOW = 768\n\ntype Sort = { field: string; direction: 'asc' | 'desc' }\ntype FacetValue = string | number | boolean\n\nfunction isFacet(value: unknown): value is FacetValue {\n return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'\n}\n\ninterface Result {\n /** The query these rows answer. A result whose key is not the current one is stale. */\n key: string\n rows: RecordRow[]\n nextCursor: string | null\n error: string | null\n}\n\nconst message = (cause: unknown) => (cause instanceof Error ? cause.message : String(cause))\n\n/**\n * Pages, live updates, and the filter chips' vocabulary — everything that arrives from the adapter.\n *\n * The chips offer the values that have actually arrived, and keep offering them once a filter\n * narrows the list; otherwise choosing a value would erase every other choice from the row.\n */\nfunction useRecordPages(\n records: RecordsAdapter,\n config: RecordListConfig,\n sort: Sort | null,\n chosen: Record<string, FacetValue>,\n search: string,\n) {\n const [result, setResult] = useState<Result | null>(null)\n const [loadingMore, setLoadingMore] = useState(false)\n const [facets, setFacets] = useState<Record<string, FacetValue[]>>({})\n\n // How deep the reader has paged, so a live update re-lists what they can see rather than\n // snapping them back to page one.\n const loaded = useRef(0)\n\n const query = useMemo(() => {\n const filter: Record<string, FilterValue> = { ...config.scope, ...chosen }\n const text = search.trim()\n return {\n filter: Object.keys(filter).length > 0 ? filter : undefined,\n sort: sort ?? undefined,\n search: text !== '' && config.search.length > 0 ? { text, fields: config.search } : undefined,\n }\n }, [config.scope, config.search, chosen, sort, search])\n\n const key = useMemo(\n () => JSON.stringify([config.collection, query, config.pageSize]),\n [config.collection, query, config.pageSize],\n )\n\n const absorb = useCallback(\n (rows: RecordRow[]) => {\n if (config.filters.length === 0) return\n setFacets((prev) => {\n let changed = false\n const next: Record<string, FacetValue[]> = { ...prev }\n for (const name of config.filters) {\n const seen = new Set<FacetValue>(prev[name] ?? [])\n for (const row of rows) {\n const value = row[name]\n if (isFacet(value) && !seen.has(value)) {\n seen.add(value)\n changed = true\n }\n }\n next[name] = [...seen].sort((a, b) => (String(a) < String(b) ? -1 : 1))\n }\n return changed ? next : prev\n })\n },\n [config.filters],\n )\n\n const fetchPage = useCallback(\n (cursor: string | null, limit: number) =>\n records.list<RecordRow>(config.collection, { ...query, cursor, limit }),\n [records, config.collection, query],\n )\n\n useEffect(() => {\n let live = true\n const refresh = (limit: number) => {\n void fetchPage(null, limit).then(\n (page) => {\n if (!live) return\n loaded.current = page.rows.length\n absorb(page.rows)\n setResult({ key, rows: page.rows, nextCursor: page.nextCursor, error: null })\n },\n (cause: unknown) => {\n if (!live) return\n setResult({ key, rows: [], nextCursor: null, error: message(cause) })\n },\n )\n }\n\n refresh(config.pageSize)\n const unsubscribe = records.subscribe(config.collection, () =>\n refresh(Math.max(config.pageSize, loaded.current)),\n )\n return () => {\n live = false\n unsubscribe()\n }\n }, [fetchPage, absorb, key, records, config.collection, config.pageSize])\n\n const nextCursor = result?.key === key ? result.nextCursor : null\n\n const loadMore = useCallback(() => {\n if (nextCursor === null) return\n setLoadingMore(true)\n void fetchPage(nextCursor, config.pageSize).then(\n (page) => {\n absorb(page.rows)\n setResult((prev) => {\n if (!prev || prev.key !== key) return prev\n const rows = [...prev.rows, ...page.rows]\n loaded.current = rows.length\n return { ...prev, rows, nextCursor: page.nextCursor }\n })\n setLoadingMore(false)\n },\n (cause: unknown) => {\n setResult((prev) => (prev ? { ...prev, error: message(cause) } : prev))\n setLoadingMore(false)\n },\n )\n }, [fetchPage, absorb, key, nextCursor, config.pageSize])\n\n return {\n // Rows from an older query stay on screen while the new ones are in flight, so a keystroke in\n // the search box does not blank the list.\n rows: result?.rows ?? [],\n nextCursor,\n loading: result === null,\n stale: result !== null && result.key !== key,\n loadingMore,\n error: result?.error ?? null,\n facets,\n loadMore,\n }\n}\n\nfunction Chip({ value }: { value: string }) {\n return (\n <span\n className={`inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-xs font-medium ${chipTone(value)}`}\n >\n {value}\n </span>\n )\n}\n\nfunction Cell({ row, field }: { row: RecordRow; field: ListField }): ReactNode {\n const value = row[field.key]\n if (value === null || value === undefined || value === '') {\n return <span className=\"text-neutral-400\">—</span>\n }\n if (field.type === 'enum') return <Chip value={String(value)} />\n if (field.type === 'user') {\n const name = userName(value) || String(value)\n return (\n <span className=\"flex items-center gap-2\">\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-full bg-neutral-900 text-[10px] font-semibold text-white\">\n {initials(name)}\n </span>\n <span className=\"truncate\">{name}</span>\n </span>\n )\n }\n const text = formatValue(value, field)\n const numeric = field.type === 'number' || field.type === 'money'\n return <span className={numeric ? 'tabular-nums' : undefined}>{text}</span>\n}\n\n/** Arrow keys walk the rows; the row itself is the tab stop, so one Tab enters the list. */\nfunction moveFocus(from: HTMLElement, delta: number) {\n const list = from.closest('[data-golem-rows]')\n const rows = Array.from(list?.querySelectorAll<HTMLElement>('[data-golem-row]') ?? [])\n const index = rows.indexOf(from)\n if (index === -1) return\n rows[Math.min(rows.length - 1, Math.max(0, index + delta))]?.focus()\n}\n\nfunction RecordListBody({\n config,\n adapters,\n onOpen,\n}: GolemProps<RecordListConfig, RecordListAdapters, RecordListSlots>) {\n const listId = useId()\n const [sort, setSort] = useState<Sort | null>(config.sort ?? null)\n const [chosen, setChosen] = useState<Record<string, FacetValue>>({})\n const [draft, setDraft] = useState('')\n const [search, setSearch] = useState('')\n\n // One list call per pause in the typing, not one per keystroke.\n useEffect(() => {\n const timer = setTimeout(() => setSearch(draft), 150)\n return () => clearTimeout(timer)\n }, [draft])\n\n const { rows, nextCursor, loading, stale, loadingMore, error, facets, loadMore } = useRecordPages(\n adapters.records,\n config,\n sort,\n chosen,\n search,\n )\n\n const root = useRef<HTMLDivElement>(null)\n const cards = useContainerWidth(root) < CARDS_BELOW\n\n const primary = config.fields.find((field) => field.primary) ?? config.fields[0]!\n const chipField = config.fields.find((field) => field.type === 'enum' && field !== primary)\n const secondary = config.fields.filter((field) => field !== primary && field !== chipField)\n const searchable = config.fields.filter((field) => config.search.includes(field.key))\n const activatable = config.rowAction === 'open'\n const pad = config.density === 'compact' ? 'py-1.5' : 'py-3'\n\n const toggleSort = (key: string) =>\n setSort((prev) =>\n prev && prev.field === key\n ? { field: key, direction: prev.direction === 'asc' ? 'desc' : 'asc' }\n : { field: key, direction: 'asc' },\n )\n\n const choose = (key: string, value: FacetValue | null) =>\n setChosen((prev) => {\n const next = { ...prev }\n if (value === null) delete next[key]\n else next[key] = value\n return next\n })\n\n const rowProps = (row: RecordRow) =>\n activatable\n ? {\n tabIndex: 0,\n onClick: () => onOpen?.(row),\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault()\n moveFocus(event.currentTarget, event.key === 'ArrowDown' ? 1 : -1)\n } else if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n onOpen?.(row)\n }\n },\n }\n : {}\n\n const keyOf = (row: RecordRow, index: number) =>\n typeof row.id === 'string' || typeof row.id === 'number' ? String(row.id) : `row-${index}`\n\n // At card width there are no column headings to click, so the sort moves into the control bar.\n const sortControl = cards && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <label className=\"text-xs font-medium text-neutral-500\" htmlFor={`${listId}-sort`}>\n Sort\n </label>\n <select\n id={`${listId}-sort`}\n value={sort?.field ?? ''}\n onChange={(event) =>\n setSort(\n event.target.value === ''\n ? null\n : { field: event.target.value, direction: sort?.direction ?? 'asc' },\n )\n }\n className=\"rounded-lg border border-neutral-300 bg-white px-2 py-1 text-sm\"\n >\n <option value=\"\">Adapter order</option>\n {config.fields.map((field) => (\n <option key={field.key} value={field.key}>\n {field.label}\n </option>\n ))}\n </select>\n {sort && (\n <button\n type=\"button\"\n onClick={() => toggleSort(sort.field)}\n aria-label={sort.direction === 'asc' ? 'Sort descending' : 'Sort ascending'}\n className=\"rounded-lg border border-neutral-300 bg-white px-2 py-1 text-sm\"\n >\n {sort.direction === 'asc' ? '↑' : '↓'}\n </button>\n )}\n </div>\n )\n\n const controls = (config.search.length > 0 || config.filters.length > 0 || cards) && (\n <div className=\"mb-3 space-y-2\">\n {config.search.length > 0 && (\n <input\n type=\"search\"\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n aria-label=\"Search\"\n placeholder={\n searchable.length > 0\n ? `Search ${searchable.map((field) => field.label.toLowerCase()).join(', ')}`\n : 'Search'\n }\n className=\"w-full rounded-lg border border-neutral-300 bg-white px-3 py-2 text-base sm:text-sm\"\n />\n )}\n {config.filters.map((key) => {\n const field = config.fields.find((one) => one.key === key)!\n return (\n <div key={key} className=\"flex flex-wrap items-center gap-1.5\">\n <span className=\"text-xs font-medium text-neutral-500\">{field.label}</span>\n {[null, ...(facets[key] ?? [])].map((value) => {\n const active = value === null ? chosen[key] === undefined : chosen[key] === value\n return (\n <button\n key={value === null ? '__all' : String(value)}\n type=\"button\"\n aria-pressed={active}\n onClick={() => choose(key, value)}\n className={`rounded-full px-2.5 py-1 text-xs font-medium ${\n active ? 'bg-neutral-900 text-white' : 'bg-neutral-100 text-neutral-600'\n }`}\n >\n {value === null ? 'All' : formatValue(value, field)}\n </button>\n )\n })}\n </div>\n )\n })}\n {sortControl}\n </div>\n )\n\n let body: ReactNode\n if (loading) {\n body = (\n <p className=\"rounded-xl border border-neutral-200 bg-white px-4 py-8 text-center text-sm text-neutral-500\">\n Loading…\n </p>\n )\n } else if (error !== null) {\n body = (\n <p\n role=\"alert\"\n className=\"rounded-xl border border-red-300 bg-red-50 px-4 py-6 text-center text-sm text-red-900\"\n >\n {error}\n </p>\n )\n } else if (rows.length === 0) {\n body = (\n <p className=\"rounded-xl border border-neutral-200 bg-white px-4 py-8 text-center text-sm text-neutral-500\">\n {config.emptyState}\n </p>\n )\n } else if (cards) {\n body = (\n <ul data-golem-rows=\"\" className=\"space-y-2\">\n {rows.map((row, index) => (\n <li key={keyOf(row, index)}>\n <article\n data-golem-row=\"\"\n {...rowProps(row)}\n className={`rounded-xl border border-neutral-200 bg-white ${\n config.density === 'compact' ? 'p-2.5' : 'p-3'\n } ${\n activatable\n ? 'cursor-pointer focus:outline-2 focus:outline-offset-2 focus:outline-neutral-900'\n : ''\n }`}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <h3 className=\"min-w-0 truncate text-sm font-semibold\">\n {formatValue(row[primary.key], primary)}\n </h3>\n {chipField && row[chipField.key] !== undefined && (\n <Chip value={String(row[chipField.key])} />\n )}\n </div>\n <dl className=\"mt-2 grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 text-sm\">\n {secondary.map((field) => (\n <Fragment key={field.key}>\n <dt className=\"text-neutral-500\">{field.label}</dt>\n <dd className=\"min-w-0 truncate\">\n <Cell row={row} field={field} />\n </dd>\n </Fragment>\n ))}\n </dl>\n </article>\n </li>\n ))}\n </ul>\n )\n } else {\n body = (\n // Its own scroll box, so the sticky heading has something to stick to and a wide table\n // scrolls sideways inside the list instead of pushing the page out.\n <div className=\"max-h-[70vh] overflow-auto rounded-xl border border-neutral-200 bg-white\">\n <table className=\"w-full min-w-max border-collapse text-left text-sm\">\n <thead>\n <tr>\n {config.fields.map((field) => {\n const active = sort?.field === field.key\n return (\n <th\n key={field.key}\n scope=\"col\"\n aria-sort={\n active ? (sort.direction === 'asc' ? 'ascending' : 'descending') : 'none'\n }\n className=\"sticky top-0 z-10 border-b border-neutral-200 bg-neutral-50 p-0\"\n >\n <button\n type=\"button\"\n onClick={() => toggleSort(field.key)}\n className=\"flex w-full items-center gap-1 px-3 py-2 text-xs font-semibold tracking-wide text-neutral-600 uppercase hover:text-neutral-900\"\n >\n {field.label}\n <span aria-hidden=\"true\" className={active ? '' : 'opacity-0'}>\n {active && sort.direction === 'desc' ? '↓' : '↑'}\n </span>\n </button>\n </th>\n )\n })}\n </tr>\n </thead>\n <tbody data-golem-rows=\"\">\n {rows.map((row, index) => (\n <tr\n key={keyOf(row, index)}\n data-golem-row=\"\"\n {...rowProps(row)}\n className={`border-b border-neutral-100 last:border-0 ${\n activatable\n ? 'cursor-pointer hover:bg-neutral-50 focus:bg-neutral-50 focus:outline-2 focus:-outline-offset-2 focus:outline-neutral-900'\n : ''\n }`}\n >\n {config.fields.map((field) => (\n <td\n key={field.key}\n className={`px-3 align-top ${pad} ${\n field === primary ? 'font-medium whitespace-nowrap' : ''\n }`}\n >\n <Cell row={row} field={field} />\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n }\n\n return (\n <div\n ref={root}\n data-golem-component=\"RecordList\"\n data-layout={cards ? 'cards' : 'table'}\n className={`golem-record-list w-full text-neutral-900 ${stale ? 'opacity-60' : ''}`}\n >\n {controls}\n {body}\n {nextCursor !== null && !loading && (\n <div className=\"mt-3 flex justify-center\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loadingMore}\n className=\"rounded-lg border border-neutral-300 bg-white px-4 py-2 text-sm font-medium disabled:opacity-60\"\n >\n {loadingMore ? 'Loading…' : `Load ${config.pageSize} more`}\n </button>\n </div>\n )}\n </div>\n )\n}\n\nexport const RecordList = defineComponent<\n typeof recordListConfigSchema,\n RecordListAdapters,\n RecordListSlots\n>({\n name: 'RecordList',\n schema: recordListConfigSchema,\n render: RecordListBody,\n})\n","import { z } from 'zod'\nimport { isFormattableLocale } from '../../lib/locale'\n\nexport const reportConfigSchema = z\n .object({\n collection: z\n .string()\n .min(1)\n .describe(\n 'The record type the reports live in, passed to `list`, `get` and `subscribe`. One report is one record.',\n ),\n period: z\n .enum(['day', 'week', 'month'])\n .default('day')\n .describe(\n 'How long one report covers, which is also what the previous/next arrows step by. A daily stand-up report is `day`; a Monday summary is `week`; a board pack is `month`.',\n ),\n dateField: z\n .string()\n .min(1)\n .default('date')\n .describe(\n 'The field holding the day the report covers, as an ISO calendar date (`2026-09-10`). It picks the period and orders the index.',\n ),\n titleField: z\n .string()\n .min(1)\n .default('title')\n .describe('The field shown as the document’s heading, and as each line of the index.'),\n bodyField: z\n .string()\n .min(1)\n .default('body')\n .describe(\n 'The field holding the document itself, as markdown: headings, lists, tables, code and links.',\n ),\n statusField: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'The field holding `draft` or `final`. Set it and a report still being written wears a Draft badge; leave it out and every report reads as finished.',\n ),\n authorField: z\n .string()\n .min(1)\n .default('author')\n .describe(\n 'The field naming who wrote the report — a name, or a record with a `name` on it. Only read when `showAuthor` is on.',\n ),\n showAuthor: z\n .boolean()\n .default(true)\n .describe('Whether the byline is shown under the date. Off for a report nobody signs.'),\n showIndex: z\n .boolean()\n .default(true)\n .describe(\n 'Whether past reports are listed under the document as a compact index. Off for a report embedded in a wider screen, where the index belongs to the screen.',\n ),\n showNavigation: z\n .boolean()\n .default(true)\n .describe(\n 'Whether the previous/next arrows are shown. Off pins the component to the latest report of the current period, which is what a dashboard card wants.',\n ),\n print: z\n .boolean()\n .default(true)\n .describe(\n 'Whether the Print / Save as PDF action is offered. On also brings the print stylesheet: the chrome disappears and the document prints on its own.',\n ),\n emptyState: z\n .string()\n .min(1)\n .default('No report for this period.')\n .describe('One line shown in place of the document when the period holds no report.'),\n locale: z\n .string()\n .min(2)\n .default('en-GB')\n .describe(\n 'A BCP-47 tag — `en-GB`, `en-US`, `pt-BR` — that every date on the page is formatted with. Dates are read in UTC, so a report reads the same everywhere.',\n ),\n })\n .strict()\n .superRefine((config, ctx) => {\n if (!isFormattableLocale(config.locale)) {\n ctx.addIssue({\n code: 'custom',\n path: ['locale'],\n message: `\"${config.locale}\" is not a BCP-47 language tag, so no date on the page can be formatted. Write it with a hyphen: en-GB, en-US, pt-BR.`,\n })\n }\n })\n\nexport type ReportConfig = z.output<typeof reportConfigSchema>\nexport type ReportConfigInput = z.input<typeof reportConfigSchema>\n","import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { ClockAdapter, RecordsAdapter } from '../../adapters'\nimport { headingSlug, Markdown } from '../../lib/markdown'\nimport { userName } from '../../lib/format'\nimport { reportConfigSchema, type ReportConfig } from './Report.config'\n\nexport interface ReportAdapters {\n records: RecordsAdapter\n clock: ClockAdapter\n}\n\nexport interface ReportSlots {\n /**\n * One report by id, instead of the latest of the current period. The component still opens on\n * that report's period, so the arrows and the index carry on from where it sits.\n */\n id?: string\n}\n\n/** One report record, as the component reads it: every field is named by the config. */\nexport type ReportRecord = Record<string, unknown> & { id: string }\n\n/** How many past reports the index offers. Long enough to reach last month, short enough to read. */\nconst INDEX_LENGTH = 12\n\ntype Period = ReportConfig['period']\n\n/* Every date is handled in UTC, so a report covers the same day in every timezone. */\n\nconst isoDay = (at: Date) => at.toISOString().slice(0, 10)\n\n/** The first day of the period `at` falls in. A week starts on Monday. */\nfunction startOf(period: Period, at: Date): Date {\n const start = new Date(Date.UTC(at.getUTCFullYear(), at.getUTCMonth(), at.getUTCDate()))\n if (period === 'week') start.setUTCDate(start.getUTCDate() - ((start.getUTCDay() + 6) % 7))\n if (period === 'month') start.setUTCDate(1)\n return start\n}\n\n/** Every ISO day the period covers. This is the filter: an `any of`, not a range query. */\nfunction daysOf(period: Period, start: Date): string[] {\n const days: string[] = []\n const cursor = new Date(start)\n const month = start.getUTCMonth()\n const length = period === 'day' ? 1 : period === 'week' ? 7 : Number.POSITIVE_INFINITY\n\n while (days.length < length && (period !== 'month' || cursor.getUTCMonth() === month)) {\n days.push(isoDay(cursor))\n cursor.setUTCDate(cursor.getUTCDate() + 1)\n }\n return days\n}\n\n/** The period `delta` steps away, forwards or back. */\nfunction step(period: Period, start: Date, delta: number): Date {\n const next = new Date(start)\n if (period === 'day') next.setUTCDate(next.getUTCDate() + delta)\n if (period === 'week') next.setUTCDate(next.getUTCDate() + delta * 7)\n if (period === 'month') next.setUTCMonth(next.getUTCMonth() + delta)\n return startOf(period, next)\n}\n\n/** What the period is called, in the reader's locale: a day, a Monday-to-Sunday span, a month. */\nfunction periodLabel(period: Period, start: Date, locale: string): string {\n const options: Intl.DateTimeFormatOptions = { timeZone: 'UTC' }\n if (period === 'month') {\n return new Intl.DateTimeFormat(locale, { ...options, month: 'long', year: 'numeric' }).format(\n start,\n )\n }\n if (period === 'week') {\n const format = new Intl.DateTimeFormat(locale, {\n ...options,\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n })\n const end = new Date(start)\n end.setUTCDate(end.getUTCDate() + 6)\n return format.formatRange(start, end)\n }\n return new Intl.DateTimeFormat(locale, {\n ...options,\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(start)\n}\n\n/** A record's own date, read as a calendar day at noon so no timezone can shift it. */\nfunction dayOf(value: unknown): Date | null {\n const at = new Date(`${String(value)}T12:00:00Z`)\n return Number.isNaN(at.getTime()) ? null : at\n}\n\nconst message = (cause: unknown) => (cause instanceof Error ? cause.message : String(cause))\n\n/**\n * A report's optional `sections`: the headings it was written from, as strings or as objects with a\n * `title`. Anything else on the field is ignored rather than guessed at.\n */\nfunction sectionTitles(record: ReportRecord | null): string[] {\n const value = record?.sections\n if (!Array.isArray(value)) return []\n return value\n .map((one) =>\n typeof one === 'string'\n ? one\n : typeof (one as { title?: unknown })?.title === 'string'\n ? (one as { title: string }).title\n : '',\n )\n .filter((title) => title !== '')\n}\n\ninterface Loaded {\n /** The query these rows answer. A result whose key is not the current one is stale. */\n key: string\n report: ReportRecord | null\n error: string | null\n}\n\n/**\n * The document on screen and the index under it, and the one `subscribe` that refreshes both when\n * the agent writes a new version. Nothing here polls.\n */\nfunction useReport(\n records: RecordsAdapter,\n config: ReportConfig,\n days: string[],\n openId: string | undefined,\n) {\n const [loaded, setLoaded] = useState<Loaded | null>(null)\n const [index, setIndex] = useState<ReportRecord[]>([])\n\n const key = useMemo(\n () => JSON.stringify([config.collection, config.dateField, days, openId ?? null]),\n [config.collection, config.dateField, days, openId],\n )\n\n const fetchReport = useCallback((): Promise<ReportRecord | null> => {\n if (openId !== undefined) return records.get<ReportRecord>(config.collection, openId)\n return records\n .list<ReportRecord>(config.collection, {\n filter: { [config.dateField]: days },\n sort: { field: config.dateField, direction: 'desc' },\n limit: 1,\n })\n .then((page) => page.rows[0] ?? null)\n }, [records, config.collection, config.dateField, days, openId])\n\n useEffect(() => {\n let live = true\n\n const refresh = () => {\n void fetchReport().then(\n (report) => {\n if (live) setLoaded({ key, report, error: null })\n },\n (cause: unknown) => {\n if (live) setLoaded({ key, report: null, error: message(cause) })\n },\n )\n if (!config.showIndex) return\n void records\n .list<ReportRecord>(config.collection, {\n sort: { field: config.dateField, direction: 'desc' },\n limit: INDEX_LENGTH,\n })\n .then(\n (page) => {\n if (live) setIndex(page.rows)\n },\n () => {\n // The index is an aside; a document that arrived should not be replaced by its error.\n },\n )\n }\n\n refresh()\n const unsubscribe = records.subscribe(config.collection, refresh)\n return () => {\n live = false\n unsubscribe()\n }\n }, [fetchReport, key, records, config.collection, config.dateField, config.showIndex])\n\n return {\n report: loaded?.report ?? null,\n error: loaded?.error ?? null,\n loading: loaded === null || loaded.key !== key,\n index,\n }\n}\n\nfunction Chrome({ children }: { children: ReactNode }) {\n return (\n <div data-golem-chrome=\"\" className=\"mb-3 flex flex-wrap items-center justify-between gap-2\">\n {children}\n </div>\n )\n}\n\nconst stepButton =\n 'flex size-9 shrink-0 items-center justify-center rounded-lg border border-neutral-300 bg-white text-base disabled:opacity-40'\n\nfunction ReportBody({\n config,\n adapters,\n id,\n}: GolemProps<ReportConfig, ReportAdapters, ReportSlots>) {\n const root = useRef<HTMLDivElement>(null)\n const [anchor, setAnchor] = useState(() => startOf(config.period, adapters.clock.now()))\n const [openId, setOpenId] = useState(id)\n\n const days = useMemo(() => daysOf(config.period, anchor), [config.period, anchor])\n const { report, error, loading, index } = useReport(adapters.records, config, days, openId)\n\n const reportDate = report ? dayOf(report[config.dateField]) : null\n\n // The period on screen. A report opened by id brings its own, so the arrows carry on from where\n // it sits rather than from wherever the reader was before they opened it.\n const shown = openId !== undefined && reportDate ? startOf(config.period, reportDate) : anchor\n\n // The reader cannot step past the period they are living in: there is no report from next week.\n const latest = startOf(config.period, adapters.clock.now())\n const move = (delta: number) => {\n setOpenId(undefined)\n setAnchor(step(config.period, shown, delta))\n }\n\n const title = String(report?.[config.titleField] ?? '')\n const body = String(report?.[config.bodyField] ?? '')\n const author = config.showAuthor ? userName(report?.[config.authorField]) : ''\n const draft = config.statusField !== undefined && report?.[config.statusField] === 'draft'\n const sections = sectionTitles(report)\n\n const dateLine = periodLabel(\n config.period,\n reportDate ? startOf(config.period, reportDate) : shown,\n config.locale,\n )\n\n const jumpTo = (heading: string) => {\n root.current\n ?.querySelector(`[data-golem-heading=\"${headingSlug(heading)}\"]`)\n ?.scrollIntoView({ block: 'start' })\n }\n\n let document: ReactNode\n if (loading) {\n document = <p className=\"px-1 py-10 text-center text-sm text-neutral-500\">Loading…</p>\n } else if (error !== null) {\n document = (\n <p role=\"alert\" className=\"px-1 py-8 text-center text-sm text-red-900\">\n {error}\n </p>\n )\n } else if (report === null) {\n document = (\n <p className=\"px-1 py-10 text-center text-sm text-neutral-500\">{config.emptyState}</p>\n )\n } else {\n document = (\n <>\n <header className=\"mb-5 border-b border-neutral-200 pb-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h1 className=\"text-xl font-semibold tracking-tight sm:text-2xl\">{title}</h1>\n {draft && (\n <span className=\"rounded-full bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-800 uppercase\">\n Draft\n </span>\n )}\n </div>\n <p className=\"mt-1 text-sm text-neutral-500\">\n {dateLine}\n {author !== '' && ` · ${author}`}\n </p>\n </header>\n\n {sections.length > 0 && (\n <nav\n data-golem-contents=\"\"\n aria-label=\"Contents\"\n className=\"mb-5 rounded-lg bg-neutral-50 px-4 py-3\"\n >\n <p className=\"text-xs font-semibold tracking-wide text-neutral-500 uppercase\">\n Contents\n </p>\n <ul className=\"mt-1.5 space-y-1 text-sm\">\n {sections.map((heading) => (\n <li key={heading}>\n <button\n type=\"button\"\n onClick={() => jumpTo(heading)}\n className=\"text-left underline-offset-2 hover:underline\"\n >\n {heading}\n </button>\n </li>\n ))}\n </ul>\n </nav>\n )}\n\n {/* The one place the font size is set; every heading in the body is sized in `em` off it. */}\n <div className=\"text-[15px] leading-7 text-neutral-800 sm:text-base sm:leading-8\">\n <Markdown text={body} hardWraps={false} />\n </div>\n </>\n )\n }\n\n return (\n <div\n ref={root}\n data-golem-component=\"Report\"\n data-period={config.period}\n data-print={config.print ? 'on' : 'off'}\n className=\"golem-report w-full text-neutral-900\"\n >\n {(config.showNavigation || config.print) && (\n <Chrome>\n {config.showNavigation ? (\n <div className=\"flex min-w-0 items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => move(-1)}\n aria-label={`Previous ${config.period}`}\n className={stepButton}\n >\n ‹\n </button>\n <button\n type=\"button\"\n onClick={() => move(1)}\n aria-label={`Next ${config.period}`}\n disabled={shown.getTime() >= latest.getTime()}\n className={stepButton}\n >\n ›\n </button>\n <p data-golem-period=\"\" className=\"min-w-0 truncate text-sm font-medium\">\n {periodLabel(config.period, shown, config.locale)}\n </p>\n </div>\n ) : (\n <span />\n )}\n {config.print && (\n <button\n type=\"button\"\n onClick={() => window.print()}\n className=\"rounded-lg border border-neutral-300 bg-white px-3 py-2 text-sm font-medium\"\n >\n Print / Save as PDF\n </button>\n )}\n </Chrome>\n )}\n\n <article\n data-golem-paper=\"\"\n className=\"rounded-xl border border-neutral-200 bg-white px-5 py-6 sm:px-8 sm:py-8\"\n >\n {document}\n </article>\n\n {config.showIndex && index.length > 0 && (\n <nav data-golem-chrome=\"\" aria-label=\"Past reports\" className=\"mt-4\">\n <p className=\"mb-2 text-xs font-semibold tracking-wide text-neutral-500 uppercase\">\n Past reports\n </p>\n <ul className=\"divide-y divide-neutral-200 overflow-hidden rounded-xl border border-neutral-200 bg-white\">\n {index.map((past) => {\n const at = dayOf(past[config.dateField])\n const current = report !== null && past.id === report.id\n return (\n <li key={past.id}>\n <button\n type=\"button\"\n aria-current={current ? 'true' : undefined}\n onClick={() => setOpenId(past.id)}\n className={`flex w-full items-baseline justify-between gap-3 px-4 py-2.5 text-left text-sm ${\n current ? 'bg-neutral-100 font-medium' : 'hover:bg-neutral-50'\n }`}\n >\n <span className=\"min-w-0 truncate\">\n {String(past[config.titleField] ?? '—')}\n </span>\n <span className=\"shrink-0 text-xs text-neutral-500\">\n {at\n ? new Intl.DateTimeFormat(config.locale, {\n day: 'numeric',\n month: 'short',\n timeZone: 'UTC',\n }).format(at)\n : '—'}\n </span>\n </button>\n </li>\n )\n })}\n </ul>\n </nav>\n )}\n </div>\n )\n}\n\nexport const Report = defineComponent<typeof reportConfigSchema, ReportAdapters, ReportSlots>({\n name: 'Report',\n schema: reportConfigSchema,\n render: ReportBody,\n})\n","import { z } from 'zod'\n\nconst menuItemSchema = z\n .object({\n id: z.string().min(1).describe('Names the item to `activeId` and to `onSelect`.'),\n label: z.string().min(1).describe('The short word under the icon.'),\n icon: z.string().optional().describe('The glyph over the label — an emoji does. Left out, the label\\'s first letter stands in.'),\n href: z\n .string()\n .optional()\n .describe(\n 'The route the item opens, through `navigation.go`. Left out, tapping it calls `onSelect(id)` instead.',\n ),\n })\n .strict()\n\nexport const shellConfigSchema = z\n .object({\n title: z.string().min(1).describe('Text shown in the top bar. Usually the app name.'),\n menu: z\n .array(menuItemSchema)\n .default([])\n .describe(\n \"The app's own screens, one item each, in the bottom menu bar. Empty means no bar.\",\n ),\n activeId: z\n .string()\n .optional()\n .describe(\n 'The menu item drawn as current. Left out, the item whose `href` starts the current route is current.',\n ),\n chatSide: z\n .enum(['left', 'right'])\n .default('left')\n .describe('Which side of the frame the agent chat column sits on, at desktop width.'),\n chatOpen: z\n .boolean()\n .default(true)\n .describe(\n 'Whether the chat column is open at the first paint at desktop width, when this browser has no remembered choice. Turning it on later opens the chat on either layout, which is how an app raises the chat when a mode switches on. Below the breakpoint the chat starts closed.',\n ),\n breakpoint: z\n .number()\n .int()\n .min(320)\n .max(1920)\n .default(768)\n .describe(\n 'Viewport width in pixels. Below it the chat stops being a side column and opens as a sheet over the app.',\n ),\n chatWidth: z\n .number()\n .int()\n .min(240)\n .max(960)\n .default(320)\n .describe(\n 'Starting width of the chat column in pixels, at desktop width. The reader can drag the edge between chat and canvas to change it, and the choice is kept in this browser.',\n ),\n showTopBar: z\n .boolean()\n .default(true)\n .describe('Whether the top bar is rendered at all. Turn it off when the host app has one.'),\n })\n .strict()\n\nexport type ShellConfig = z.output<typeof shellConfigSchema>\nexport type ShellConfigInput = z.input<typeof shellConfigSchema>\nexport type ShellMenuItem = z.output<typeof menuItemSchema>\n","import { useEffect, useRef, useState, type ReactNode } from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { IdentityAdapter, NavigationAdapter, User } from '../../adapters'\nimport { shellConfigSchema, type ShellConfig } from './Shell.config'\nimport { useContainerWidth } from '../../lib/use-container-width'\n\nexport interface ShellAdapters {\n identity: IdentityAdapter\n navigation: NavigationAdapter\n}\n\nexport interface ShellSlots {\n /** The agent conversation. Left empty it renders a placeholder, so the frame is never blank; `null` means no chat at all, and no chat toggle. */\n chat?: ReactNode | null\n /** The screen the agent built. */\n canvas?: ReactNode\n /**\n * The right-hand end of the top bar — `Auth.AccountMenu` is what usually goes here. Left empty\n * the bar shows the signed-in name as plain text.\n */\n account?: ReactNode\n /** Extra rows in the settings dropdown, under the theme toggle. `Shell.Setting` is the row. */\n settings?: ReactNode\n /** Called with the item's id when a menu item without `href` is tapped. */\n onSelect?: (id: string) => void\n}\n\nfunction Placeholder({ label }: { label: string }) {\n return (\n <div className=\"flex h-full items-center justify-center p-6 text-center text-sm text-(--chat-faint)\">\n {label}\n </div>\n )\n}\n\nfunction useCurrentUser(identity: IdentityAdapter): User | null {\n const [user, setUser] = useState<User | null>(null)\n\n useEffect(() => {\n let live = true\n void identity.currentUser().then((next) => {\n if (live) setUser(next)\n })\n const unsubscribe = identity.subscribe(setUser)\n return () => {\n live = false\n unsubscribe()\n }\n }, [identity])\n\n return user\n}\n\nconst CHAT_WIDTH_KEY = 'golem-shell-chat-width'\nconst CHAT_OPEN_KEY = 'golem-shell-chat-open'\nconst THEME_KEY = 'golem-shell-theme'\nconst CHAT_MIN = 240\n/** The canvas keeps at least this much, however far the chat edge is dragged. */\nconst CANVAS_MIN = 320\n\nfunction saved(key: string): string | null {\n try {\n return localStorage.getItem(key)\n } catch {\n return null\n }\n}\n\nfunction save(key: string, value: string | null) {\n try {\n if (value === null) localStorage.removeItem(key)\n else localStorage.setItem(key, value)\n } catch {\n /* private mode: the choice lasts the session */\n }\n}\n\ntype Theme = 'light' | 'dark'\n\n/**\n * The kit's theme is the `data-golem-theme` attribute on the document. Shell owns it: the saved\n * choice, else the OS preference, written to the document so every component's `dark:` follows.\n */\nfunction useTheme(): [Theme, () => void] {\n const [theme, setTheme] = useState<Theme>(() => {\n const kept = saved(THEME_KEY)\n if (kept === 'light' || kept === 'dark') return kept\n return typeof matchMedia !== 'undefined' && matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light'\n })\n useEffect(() => {\n document.documentElement.dataset.golemTheme = theme\n document.documentElement.style.colorScheme = theme\n }, [theme])\n return [\n theme,\n () => {\n const next = theme === 'dark' ? 'light' : 'dark'\n save(THEME_KEY, next)\n setTheme(next)\n },\n ]\n}\n\nconst paths: Record<string, string> = {\n chat: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z',\n settings:\n 'M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2zM12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z',\n sun: 'M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8z',\n moon: 'M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9z',\n close: 'M18 6 6 18M6 6l12 12',\n user: 'M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2M12 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8z',\n wrench:\n 'M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z',\n}\n\nexport type ShellIconName = keyof typeof paths\n\n/** The line icons the bar and the dropdown use; an app's own `Shell.Setting` rows can share them. */\nfunction Icon({ name }: { name: ShellIconName }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n width=\"16\"\n height=\"16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d={paths[name]} />\n </svg>\n )\n}\n\n/** An icon-only bar button: the label is the tooltip and the accessible name, never text in the bar. */\nfunction IconButton({\n label,\n on,\n onClick,\n expanded,\n children,\n}: {\n label: string\n on?: boolean\n expanded?: boolean\n onClick: () => void\n children: ReactNode\n}) {\n return (\n <button\n type=\"button\"\n title={label}\n aria-label={label}\n aria-pressed={on}\n aria-expanded={expanded}\n onClick={onClick}\n className={`flex size-8 shrink-0 items-center justify-center rounded-lg border ${\n on || expanded\n ? 'border-(--chat-accent) text-(--chat-accent)'\n : 'border-(--chat-line) text-(--chat-dim) hover:border-(--chat-accent) hover:text-(--chat-accent)'\n }`}\n >\n {children}\n </button>\n )\n}\n\n/** One row of the settings dropdown: an icon, a short label, and a dot when it is a toggle. */\nfunction Setting({\n icon,\n label,\n on,\n onClick,\n}: {\n icon: ReactNode\n label: string\n /** Set on a toggle; left out on a plain action. */\n on?: boolean\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n role=\"menuitem\"\n aria-pressed={on}\n onClick={onClick}\n className=\"flex w-full items-center gap-2.5 rounded-lg px-2.5 py-2 text-sm text-(--chat-text) hover:bg-(--chat-panel2)\"\n >\n <span aria-hidden=\"true\" className=\"text-(--chat-dim)\">\n {icon}\n </span>\n <span className=\"flex-1 text-left\">{label}</span>\n {on !== undefined && (\n <span\n aria-hidden=\"true\"\n className={`size-2 rounded-full ${on ? 'bg-(--chat-ok)' : 'bg-(--chat-line2)'}`}\n />\n )}\n </button>\n )\n}\n\nfunction ShellFrame({\n config,\n adapters,\n chat,\n canvas,\n account,\n settings,\n onSelect,\n}: GolemProps<ShellConfig, ShellAdapters, ShellSlots>) {\n const root = useRef<HTMLDivElement>(null)\n const width = useContainerWidth(root)\n const isMobile = width < config.breakpoint\n const user = useCurrentUser(adapters.identity)\n const [theme, toggleTheme] = useTheme()\n const [route, setRoute] = useState(() => adapters.navigation.current())\n const [gear, setGear] = useState(false)\n const gearRef = useRef<HTMLDivElement>(null)\n\n // Below the breakpoint the sheet is a modal, and modals start closed; the desktop column starts\n // where this browser left it, else where the config says.\n const [open, setOpen] = useState(() => {\n if (isMobile) return false\n const kept = saved(CHAT_OPEN_KEY)\n return kept === null ? config.chatOpen : kept === '1'\n })\n // Only the desktop column's state is remembered; opening and closing the sheet is not a choice.\n const setChatOpen = (next: boolean) => {\n setOpen(next)\n if (!isMobile) save(CHAT_OPEN_KEY, next ? '1' : '0')\n }\n // Crossing into the phone layout puts the sheet away: an open column is a desk arrangement, not a\n // wish to see nothing but the chat.\n const [wasMobile, setWasMobile] = useState(isMobile)\n if (wasMobile !== isMobile) {\n setWasMobile(isMobile)\n if (isMobile) setOpen(false)\n }\n // `chatOpen` flipping after the first paint is the app raising (or lowering) the chat.\n const wanted = useRef(config.chatOpen)\n useEffect(() => {\n if (wanted.current === config.chatOpen) return\n wanted.current = config.chatOpen\n setChatOpen(config.chatOpen)\n })\n\n // A route change is a screen the reader asked for (a chip in the chat, a link), so the sheet\n // over it closes to show it.\n useEffect(\n () =>\n adapters.navigation.subscribe((next) => {\n setRoute(next)\n if (isMobile) setOpen(false)\n }),\n [adapters.navigation, isMobile],\n )\n\n // Esc closes the dropdown first, then the sheet; a tap outside closes the dropdown.\n useEffect(() => {\n if (!gear && !(isMobile && open)) return\n const onKey = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return\n if (gear) setGear(false)\n else setChatOpen(false)\n }\n const onPointer = (event: PointerEvent) => {\n if (gear && !gearRef.current?.contains(event.target as Node)) setGear(false)\n }\n window.addEventListener('keydown', onKey)\n document.addEventListener('pointerdown', onPointer)\n return () => {\n window.removeEventListener('keydown', onKey)\n document.removeEventListener('pointerdown', onPointer)\n }\n })\n\n const [chatWidth, setChatWidth] = useState(\n () => Number.parseInt(saved(CHAT_WIDTH_KEY) ?? '', 10) || config.chatWidth,\n )\n const clampChat = (w: number) =>\n Math.round(Math.min(Math.max(w, CHAT_MIN), Math.max(CHAT_MIN, width - CANVAS_MIN)))\n\n // The handle sits on the chat column's inner edge; dragging it sets the column's width from the\n // pointer's distance to the frame's chat-side edge. Pointer capture keeps the drag alive when the\n // pointer outruns the 7px handle.\n const startResize = (event: React.PointerEvent<HTMLDivElement>) => {\n event.preventDefault()\n const handle = event.currentTarget\n const frame = root.current?.getBoundingClientRect()\n if (!frame) return\n const widthAt = (e: { clientX: number }) =>\n clampChat(config.chatSide === 'left' ? e.clientX - frame.left : frame.right - e.clientX)\n handle.setPointerCapture(event.pointerId)\n const move = (e: PointerEvent) => setChatWidth(widthAt(e))\n const up = (e: PointerEvent) => {\n handle.removeEventListener('pointermove', move)\n handle.removeEventListener('pointerup', up)\n save(CHAT_WIDTH_KEY, String(widthAt(e)))\n }\n handle.addEventListener('pointermove', move)\n handle.addEventListener('pointerup', up)\n }\n const resetResize = () => {\n setChatWidth(config.chatWidth)\n save(CHAT_WIDTH_KEY, null)\n }\n\n const activeId =\n config.activeId ??\n config.menu.find((item) => item.href !== undefined && route.path.startsWith(item.href))?.id\n\n const chatPane = (\n <div className=\"h-full min-w-0 overflow-hidden\">\n {chat ?? <Placeholder label=\"agent chat\" />}\n </div>\n )\n const canvasPane = (\n <div className=\"h-full min-w-0 flex-1 overflow-auto\">\n {canvas ?? <Placeholder label=\"canvas\" />}\n </div>\n )\n\n return (\n <div\n ref={root}\n data-golem-component=\"Shell\"\n data-layout={isMobile ? 'mobile' : 'desktop'}\n data-chat={chat === null ? 'none' : open ? 'open' : 'closed'}\n className={`golem-shell flex h-dvh max-h-full min-h-0 w-full flex-col overflow-hidden bg-(--chat-bg) text-(--chat-text) ${\n config.menu.length > 0 ? '' : 'pb-[env(safe-area-inset-bottom)]'\n }`}\n >\n {config.showTopBar && (\n <header className=\"flex min-h-11 shrink-0 items-center gap-2 border-b border-(--chat-line) bg-(--chat-panel) px-3 pt-[env(safe-area-inset-top)]\">\n <button\n type=\"button\"\n onClick={() => adapters.navigation.go('/')}\n className=\"min-w-0 truncate text-[15px] font-semibold tracking-wide hover:underline\"\n >\n {config.title}\n </button>\n <span className=\"ml-auto flex shrink-0 items-center gap-1.5\">\n {chat !== null && (\n <IconButton label=\"Chat\" on={open} onClick={() => setChatOpen(!open)}>\n <Icon name=\"chat\" />\n </IconButton>\n )}\n <div ref={gearRef} className=\"relative\">\n <IconButton label=\"Settings\" expanded={gear} onClick={() => setGear(!gear)}>\n <Icon name=\"settings\" />\n </IconButton>\n {gear && (\n <div\n role=\"menu\"\n className=\"absolute top-full right-0 z-30 mt-1.5 w-56 rounded-xl border border-(--chat-line2) bg-(--chat-panel) p-1.5 shadow-lg\"\n >\n <Setting\n icon={<Icon name={theme === 'dark' ? 'sun' : 'moon'} />}\n label={theme === 'dark' ? 'Light mode' : 'Dark mode'}\n onClick={toggleTheme}\n />\n {settings}\n </div>\n )}\n </div>\n {account ??\n (user && (\n <span className=\"max-w-32 truncate text-sm text-(--chat-dim)\">{user.name}</span>\n ))}\n </span>\n </header>\n )}\n\n <div className=\"relative flex min-h-0 flex-1 flex-col\">\n {isMobile || chat === null ? (\n <div className=\"flex min-h-0 flex-1\">{canvasPane}</div>\n ) : (\n <div\n className={`flex min-h-0 flex-1 ${\n config.chatSide === 'right' ? 'flex-row-reverse' : 'flex-row'\n }`}\n >\n <aside\n hidden={!open}\n data-golem-chat-width={clampChat(chatWidth)}\n style={{ width: clampChat(chatWidth) }}\n className={`relative shrink-0 border-(--chat-line) ${\n config.chatSide === 'left' ? 'border-r' : 'border-l'\n }`}\n >\n {chatPane}\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize chat\"\n title=\"Drag to resize, double-click to reset\"\n onPointerDown={startResize}\n onDoubleClick={resetResize}\n className={`absolute top-0 bottom-0 z-10 w-[7px] cursor-col-resize touch-none hover:bg-(--chat-accent-soft) active:bg-(--chat-accent-soft) ${\n config.chatSide === 'left' ? '-right-[3px]' : '-left-[3px]'\n }`}\n />\n </aside>\n {canvasPane}\n </div>\n )}\n\n {/* The bottom menu bar: phone-app style at every width, icon over a short label. */}\n {config.menu.length > 0 && (\n <nav\n aria-label=\"Menu\"\n className=\"flex shrink-0 justify-center border-t border-(--chat-line) bg-(--chat-panel) px-1 pt-1 pb-[max(0.25rem,env(safe-area-inset-bottom))]\"\n >\n {config.menu.map((item) => {\n const active = item.id === activeId\n return (\n <button\n key={item.id}\n type=\"button\"\n aria-current={active ? 'page' : undefined}\n onClick={() =>\n item.href !== undefined ? adapters.navigation.go(item.href) : onSelect?.(item.id)\n }\n className={`flex min-w-0 max-w-24 flex-1 flex-col items-center gap-0.5 rounded-lg px-1 py-1 text-[11px] font-medium ${\n active\n ? 'text-(--chat-accent)'\n : 'text-(--chat-dim) hover:text-(--chat-text)'\n }`}\n >\n <span aria-hidden=\"true\" className=\"text-lg leading-none\">\n {item.icon ?? item.label[0]}\n </span>\n <span className=\"max-w-full truncate\">{item.label}</span>\n </button>\n )\n })}\n </nav>\n )}\n\n {/* The sheet covers the canvas and the bottom menu, which stay mounted and keep their scroll. */}\n {isMobile && chat !== null && (\n <div\n role=\"dialog\"\n aria-label=\"Chat\"\n aria-modal=\"true\"\n hidden={!open}\n className=\"absolute inset-0 z-20 bg-(--chat-bg)\"\n >\n {chatPane}\n </div>\n )}\n </div>\n </div>\n )\n}\n\nconst ShellComponent = defineComponent<typeof shellConfigSchema, ShellAdapters, ShellSlots>({\n name: 'Shell',\n schema: shellConfigSchema,\n render: ShellFrame,\n})\n\n/** The frame, plus `Shell.Setting` (a dropdown row) and `Shell.Icon` (the bar's line icons) for the app's own rows. */\nexport const Shell = Object.assign(ShellComponent, { Setting, Icon })\n","import { z } from 'zod'\nimport { isFormattableLocale } from '../../lib/locale'\n\n/**\n * What a kind says about an entry, and the only thing that decides its mark and its colour. Four\n * tones rather than a palette: an agent picking a colour would pick a different one on every\n * screen, and a log is read by scanning down one column of marks.\n */\nexport const TONES = ['neutral', 'good', 'warn', 'bad'] as const\n\nexport type Tone = (typeof TONES)[number]\n\nexport const timelineKindSchema = z\n .object({\n id: z\n .string()\n .min(1)\n .describe('The value the record holds in `kindField` when the entry is of this kind.'),\n label: z\n .string()\n .min(1)\n .describe('What names the kind on the entry and on its filter chip. Never written back.'),\n tone: z\n .enum(TONES, {\n error: (issue) =>\n `Unknown tone ${JSON.stringify(issue.input)}. Pick one of: ${TONES.join(', ')}.`,\n })\n .describe(\n 'The mark and the colour: `neutral` a grey dot, `good` a green tick, `warn` an amber triangle, `bad` a red cross.',\n ),\n })\n .strict()\n\nexport const timelineConfigSchema = z\n .object({\n collection: z\n .string()\n .min(1)\n .describe(\n 'The record type the entries live in, passed to `list`, `subscribe` and `create`. One entry is one record.',\n ),\n dateField: z\n .string()\n .min(1)\n .default('at')\n .describe(\n 'The field holding the instant the entry happened, as a full ISO datetime (`2026-09-10T14:32:00Z`). It orders the timeline, newest first, and cuts it into days.',\n ),\n bodyField: z\n .string()\n .min(1)\n .default('body')\n .describe(\n 'The field holding what the entry says, as short markdown: a sentence or two, bold, links, a bullet list.',\n ),\n kindField: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'The field holding which of the `kinds` an entry is. Leave it out and every entry gets the same neutral dot.',\n ),\n kinds: z\n .array(timelineKindSchema)\n .default([])\n .describe(\n 'The vocabulary `kindField` is read against: `{ id, label, tone }` each, where `tone` is one of neutral, good, warn, bad. Set it with `kindField` or not at all.',\n ),\n actorField: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'The field naming who the entry is by — a name, or a record with a `name` on it. Leave it out and entries are unsigned.',\n ),\n attachmentsField: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'The field holding the entry’s file references, as an array of ids or of `{ id, name }`. Each becomes a chip that opens through `Files.url`, so setting this field makes the `files` adapter required.',\n ),\n filters: z\n .array(z.enum(['kind', 'actor']))\n .default([])\n .describe(\n 'Which rows of filter chips sit above the timeline. `kind` offers every configured kind; `actor` offers every person whose entries have arrived. Each needs its field set.',\n ),\n search: z\n .array(z.string().min(1))\n .default([])\n .describe(\n 'Field keys the search box matches, as a case-insensitive substring — usually just the body field. Empty hides the search box.',\n ),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(200)\n .default(25)\n .describe(\n 'How many entries arrive per page, and how many older ones the load-more button fetches.',\n ),\n composer: z\n .boolean()\n .default(false)\n .describe(\n 'Whether a one-line box for adding an entry sits above the timeline. On makes the `identity` adapter required: the new entry is stamped with the signed-in member and with the clock’s current instant.',\n ),\n emptyState: z\n .string()\n .min(1)\n .default('Nothing on the log yet.')\n .describe('One line shown in place of the entries when the query matches none.'),\n locale: z\n .string()\n .min(2)\n .default('en-GB')\n .describe(\n 'A BCP-47 tag — `en-GB`, `en-US`, `pt-BR` — that every day header and clock time is formatted with. Days are cut in the `Clock` adapter’s time zone, so \"Today\" means today where the reader is.',\n ),\n })\n .strict()\n .superRefine((config, ctx) => {\n if (!isFormattableLocale(config.locale)) {\n ctx.addIssue({\n code: 'custom',\n path: ['locale'],\n message: `\"${config.locale}\" is not a BCP-47 language tag, so no date on the page can be formatted. Write it with a hyphen: en-GB, en-US, pt-BR.`,\n })\n }\n if (config.kindField !== undefined && config.kinds.length === 0) {\n ctx.addIssue({\n code: 'custom',\n path: ['kinds'],\n message: `\\`kindField\\` reads \"${config.kindField}\" against a vocabulary that is empty, so no entry can be marked. List the kinds, or drop \\`kindField\\`.`,\n })\n }\n if (config.kindField === undefined && config.kinds.length > 0) {\n ctx.addIssue({\n code: 'custom',\n path: ['kindField'],\n message: 'There are `kinds` but no `kindField` to read them against. Name the field.',\n })\n }\n\n const seen = new Set<string>()\n for (const [index, kind] of config.kinds.entries()) {\n if (seen.has(kind.id)) {\n ctx.addIssue({\n code: 'custom',\n path: ['kinds', index, 'id'],\n message: `Two kinds share the id \"${kind.id}\", so an entry of that kind has two meanings.`,\n })\n }\n seen.add(kind.id)\n }\n\n for (const [index, filter] of config.filters.entries()) {\n const needed = filter === 'kind' ? config.kindField : config.actorField\n if (needed === undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['filters', index],\n message: `Filtering by ${filter} needs \\`${filter}Field\\` set, otherwise there is nothing to filter on.`,\n })\n }\n }\n })\n\nexport type TimelineConfig = z.output<typeof timelineConfigSchema>\nexport type TimelineConfigInput = z.input<typeof timelineConfigSchema>\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type FormEvent,\n type ReactNode,\n} from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type {\n ClockAdapter,\n FilesAdapter,\n FilterValue,\n IdentityAdapter,\n RecordsAdapter,\n} from '../../adapters'\nimport { initials, userName } from '../../lib/format'\nimport { formattableZone } from '../../lib/locale'\nimport { Markdown } from '../../lib/markdown'\nimport { timelineConfigSchema, type TimelineConfig, type Tone } from './Timeline.config'\n\nexport interface TimelineAdapters {\n records: RecordsAdapter\n clock: ClockAdapter\n /** Only read with `composer: true`, to stamp a new entry with whoever is signed in. */\n identity?: IdentityAdapter\n /** Only read when `attachmentsField` is set, to turn a file reference into a URL to open. */\n files?: FilesAdapter\n}\n\n/** One entry, as the component reads it: every field on it is named by the config. */\nexport type TimelineEntry = Record<string, unknown> & { id: string }\n\n/**\n * How close to the top still counts as \"reading the newest entry\". Chat pins to the bottom of its\n * feed for the same reason; a timeline reads downwards from the newest, so it pins to the top.\n */\nconst TOP_SLACK = 48\n\n/** A tone's mark and its colours. Four tones, so a column of marks is scannable. */\nconst MARKS: Record<Tone, { glyph: string; dot: string; chip: string }> = {\n neutral: { glyph: '●', dot: 'text-neutral-400', chip: 'bg-neutral-100 text-neutral-600' },\n good: { glyph: '✓', dot: 'text-emerald-600', chip: 'bg-emerald-100 text-emerald-800' },\n warn: { glyph: '▲', dot: 'text-amber-600', chip: 'bg-amber-100 text-amber-800' },\n bad: { glyph: '✕', dot: 'text-rose-600', chip: 'bg-rose-100 text-rose-800' },\n}\n\nconst message = (cause: unknown) => (cause instanceof Error ? cause.message : String(cause))\n\n/** The calendar day an instant falls on, in the reader's zone. This is what groups the entries. */\nfunction dayKeyOf(at: Date, timeZone: string): string {\n const parts = new Intl.DateTimeFormat('en-CA', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }).formatToParts(at)\n const part = (type: Intl.DateTimeFormatPartTypes) =>\n parts.find((one) => one.type === type)?.value ?? ''\n return `${part('year')}-${part('month')}-${part('day')}`\n}\n\n/**\n * What heads a day: the two days a person names rather than dates, then the date itself. The key\n * is already the reader's calendar day, so it is formatted as a plain date in UTC — reading it\n * back through a zone would shift it a day either way.\n */\nfunction dayLabel(key: string, today: string, yesterday: string, locale: string): string {\n if (key === today) return 'Today'\n if (key === yesterday) return 'Yesterday'\n return new Intl.DateTimeFormat(locale, {\n timeZone: 'UTC',\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n year: key.slice(0, 4) === today.slice(0, 4) ? undefined : 'numeric',\n }).format(new Date(`${key}T12:00:00Z`))\n}\n\n/**\n * How long ago, for an entry from today; the clock time for one from any other day. A log read\n * during the day is a log of minutes, and a log read later is a log of days.\n */\nfunction timeLabel(\n at: Date | null,\n now: Date,\n today: boolean,\n locale: string,\n timeZone: string,\n): string {\n if (at === null) return '—'\n if (today) {\n const minutes = Math.floor((now.getTime() - at.getTime()) / 60_000)\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes} min ago`\n const hours = Math.floor(minutes / 60)\n return hours === 1 ? '1 hour ago' : `${hours} hours ago`\n }\n return new Intl.DateTimeFormat(locale, { timeZone, hour: '2-digit', minute: '2-digit' }).format(\n at,\n )\n}\n\n/** A file reference as the entry holds it: an id on its own, or an id with a name beside it. */\ninterface EntryFile {\n id: string\n name: string\n}\n\nfunction filesOf(value: unknown): EntryFile[] {\n if (!Array.isArray(value)) return []\n const found: EntryFile[] = []\n for (const one of value) {\n if (typeof one === 'string') found.push({ id: one, name: one })\n else if (one && typeof one === 'object') {\n const ref = one as { id?: unknown; name?: unknown }\n if (typeof ref.id === 'string') {\n found.push({ id: ref.id, name: typeof ref.name === 'string' ? ref.name : ref.id })\n }\n }\n }\n return found\n}\n\ninterface Result {\n /** The query these entries answer. A result whose key is not the current one is stale. */\n key: string\n entries: TimelineEntry[]\n nextCursor: string | null\n error: string | null\n}\n\ntype Chosen = { kind?: string; actor?: string }\n\n/**\n * Pages of entries, newest first, and the live updates that put a new one on top. Everything that\n * arrives from the adapter is here; nothing polls.\n */\nfunction useEntries(\n records: RecordsAdapter,\n config: TimelineConfig,\n chosen: Chosen,\n search: string,\n) {\n const [result, setResult] = useState<Result | null>(null)\n const [loadingMore, setLoadingMore] = useState(false)\n const [actors, setActors] = useState<string[]>([])\n\n // How deep the reader has paged, so a live update re-lists what they can see rather than\n // snapping them back to the newest page.\n const loaded = useRef(0)\n\n const query = useMemo(() => {\n const filter: Record<string, FilterValue> = {}\n if (config.kindField && chosen.kind !== undefined) filter[config.kindField] = chosen.kind\n if (config.actorField && chosen.actor !== undefined) filter[config.actorField] = chosen.actor\n const text = search.trim()\n return {\n filter: Object.keys(filter).length > 0 ? filter : undefined,\n sort: { field: config.dateField, direction: 'desc' as const },\n search: text !== '' && config.search.length > 0 ? { text, fields: config.search } : undefined,\n }\n }, [config.kindField, config.actorField, config.dateField, config.search, chosen, search])\n\n const key = useMemo(\n () => JSON.stringify([config.collection, query, config.pageSize]),\n [config.collection, query, config.pageSize],\n )\n\n // The actor chips offer everyone whose entries have arrived, and keep offering them once a\n // filter narrows the list; otherwise picking a person would erase everyone else from the row.\n const wantsActors = config.filters.includes('actor')\n const absorb = useCallback(\n (entries: TimelineEntry[]) => {\n if (!wantsActors || !config.actorField) return\n const field = config.actorField\n setActors((prev) => {\n const seen = new Set(prev)\n let changed = false\n for (const entry of entries) {\n const value = entry[field]\n // Only an actor held as a name can be filtered on: the chip's value is what `list` is\n // asked to match, and a record is not an equality the adapter can answer.\n if (typeof value === 'string' && value !== '' && !seen.has(value)) {\n seen.add(value)\n changed = true\n }\n }\n return changed ? [...seen].sort() : prev\n })\n },\n [wantsActors, config.actorField],\n )\n\n const fetchPage = useCallback(\n (cursor: string | null, limit: number) =>\n records.list<TimelineEntry>(config.collection, { ...query, cursor, limit }),\n [records, config.collection, query],\n )\n\n useEffect(() => {\n let live = true\n const refresh = (limit: number) => {\n void fetchPage(null, limit).then(\n (page) => {\n if (!live) return\n loaded.current = page.rows.length\n absorb(page.rows)\n setResult({ key, entries: page.rows, nextCursor: page.nextCursor, error: null })\n },\n (cause: unknown) => {\n if (!live) return\n setResult({ key, entries: [], nextCursor: null, error: message(cause) })\n },\n )\n }\n\n refresh(config.pageSize)\n const unsubscribe = records.subscribe(config.collection, () =>\n refresh(Math.max(config.pageSize, loaded.current)),\n )\n return () => {\n live = false\n unsubscribe()\n }\n }, [fetchPage, absorb, key, records, config.collection, config.pageSize])\n\n const nextCursor = result?.key === key ? result.nextCursor : null\n\n const loadOlder = useCallback(() => {\n if (nextCursor === null) return\n setLoadingMore(true)\n void fetchPage(nextCursor, config.pageSize).then(\n (page) => {\n absorb(page.rows)\n setResult((prev) => {\n if (!prev || prev.key !== key) return prev\n const entries = [...prev.entries, ...page.rows]\n loaded.current = entries.length\n return { ...prev, entries, nextCursor: page.nextCursor }\n })\n setLoadingMore(false)\n },\n (cause: unknown) => {\n setResult((prev) => (prev ? { ...prev, error: message(cause) } : prev))\n setLoadingMore(false)\n },\n )\n }, [fetchPage, absorb, key, nextCursor, config.pageSize])\n\n return {\n // Entries from an older query stay on screen while the new ones are in flight, so a keystroke\n // in the search box does not blank the log.\n entries: result?.entries ?? [],\n nextCursor,\n loading: result === null,\n stale: result !== null && result.key !== key,\n loadingMore,\n error: result?.error ?? null,\n actors,\n loadOlder,\n }\n}\n\n/**\n * The pill's count: entries that have appeared *above* the one that used to be on top. Older\n * entries fetched by load-more land below it and are not news.\n */\nfunction useArrivals(entries: TimelineEntry[], atTop: { current: boolean }) {\n const [arrived, setArrived] = useState(0)\n const firstId = useRef<string | null>(null)\n const known = useRef<Set<string>>(new Set())\n const clear = useCallback(() => setArrived(0), [])\n\n useEffect(() => {\n const previousFirst = firstId.current\n firstId.current = entries[0]?.id ?? null\n\n if (previousFirst !== null) {\n const cut = entries.findIndex((entry) => entry.id === previousFirst)\n if (cut > 0 && !atTop.current) setArrived((count) => count + cut)\n }\n known.current = new Set(entries.map((entry) => entry.id))\n }, [entries, atTop])\n\n // An entry the reader has not seen before slides in; one already on screen must not re-animate,\n // which is why this is read during render rather than after it.\n const isNew = (entry: TimelineEntry) => known.current.size > 0 && !known.current.has(entry.id)\n\n return { arrived, clear, isNew }\n}\n\nfunction Attachments({\n files,\n adapter,\n}: {\n files: EntryFile[]\n adapter: FilesAdapter | undefined\n}) {\n const [failed, setFailed] = useState<string | null>(null)\n\n const open = (file: EntryFile) => {\n if (!adapter) {\n setFailed('No files adapter was given, so this attachment cannot be opened.')\n return\n }\n void adapter.url(file.id).then(\n (url) => window.open(url, '_blank', 'noopener,noreferrer'),\n (cause: unknown) => setFailed(message(cause)),\n )\n }\n\n return (\n <div className=\"mt-1.5 flex flex-wrap gap-1.5\">\n {files.map((file) => (\n <button\n key={file.id}\n type=\"button\"\n data-golem-attachment={file.name}\n onClick={() => open(file)}\n className=\"inline-flex max-w-full items-center gap-1 rounded-full border border-neutral-200 bg-neutral-50 px-2 py-0.5 text-xs hover:bg-neutral-100\"\n >\n <span aria-hidden=\"true\">📎</span>\n <span className=\"truncate\">{file.name}</span>\n </button>\n ))}\n {failed !== null && (\n <span role=\"alert\" className=\"text-xs text-red-800\">\n {failed}\n </span>\n )}\n </div>\n )\n}\n\n/** One row of chips: an All chip and one per value, the chosen one filled in. */\nfunction ChipRow({\n label,\n values,\n chosen,\n onChoose,\n}: {\n label: string\n values: { id: string; label: string }[]\n chosen: string | undefined\n onChoose: (value: string | null) => void\n}) {\n return (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <span className=\"text-xs font-medium text-neutral-500\">{label}</span>\n {[null, ...values].map((value) => {\n const active = value === null ? chosen === undefined : chosen === value.id\n return (\n <button\n key={value === null ? '__all' : value.id}\n type=\"button\"\n aria-pressed={active}\n onClick={() => onChoose(value === null ? null : value.id)}\n className={`rounded-full px-2.5 py-1 text-xs font-medium ${\n active ? 'bg-neutral-900 text-white' : 'bg-neutral-100 text-neutral-600'\n }`}\n >\n {value === null ? 'All' : value.label}\n </button>\n )\n })}\n </div>\n )\n}\n\nfunction TimelineBody({ config, adapters }: GolemProps<TimelineConfig, TimelineAdapters>) {\n const [chosen, setChosen] = useState<Chosen>({})\n const [draft, setDraft] = useState('')\n const [search, setSearch] = useState('')\n\n // One list call per pause in the typing, not one per keystroke.\n useEffect(() => {\n const timer = setTimeout(() => setSearch(draft), 150)\n return () => clearTimeout(timer)\n }, [draft])\n\n const { entries, nextCursor, loading, stale, loadingMore, error, actors, loadOlder } = useEntries(\n adapters.records,\n config,\n chosen,\n search,\n )\n\n const feed = useRef<HTMLDivElement>(null)\n const atTop = useRef(true)\n const { arrived, clear, isNew } = useArrivals(entries, atTop)\n\n // A narrowed list is a different list, so whatever had piled up above it is no longer news.\n useEffect(() => clear(), [chosen.kind, chosen.actor, search, clear])\n\n const onScroll = () => {\n const element = feed.current\n if (!element) return\n atTop.current = element.scrollTop < TOP_SLACK\n if (atTop.current && arrived > 0) clear()\n }\n\n const jumpToTop = () => {\n const element = feed.current\n if (element) element.scrollTop = 0\n atTop.current = true\n clear()\n }\n\n const now = adapters.clock.now()\n const timeZone = formattableZone(adapters.clock.timeZone())\n const today = dayKeyOf(now, timeZone)\n const yesterday = dayKeyOf(new Date(now.getTime() - 86_400_000), timeZone)\n\n const kindOf = (entry: TimelineEntry) => {\n if (!config.kindField) return undefined\n const value = entry[config.kindField]\n return config.kinds.find((kind) => kind.id === value)\n }\n\n /**\n * Entries cut into days, in the order they arrived — which is already newest first. One pass over\n * one page, so it is done on every render rather than memoised against a mutable `Date`.\n */\n const days: { key: string; entries: { entry: TimelineEntry; at: Date | null }[] }[] = []\n for (const entry of entries) {\n const parsed = new Date(String(entry[config.dateField]))\n // An entry whose date the platform cannot read still belongs on the log: it joins the day\n // above it and shows an em dash, rather than being dropped or headed `Invalid Date`.\n const at = Number.isNaN(parsed.getTime()) ? null : parsed\n const key = at ? dayKeyOf(at, timeZone) : (days[days.length - 1]?.key ?? today)\n const group = days[days.length - 1]\n if (group?.key === key) group.entries.push({ entry, at })\n else days.push({ key, entries: [{ entry, at }] })\n }\n\n const [composerBusy, setComposerBusy] = useState(false)\n const [composerText, setComposerText] = useState('')\n const [composerKind, setComposerKind] = useState(config.kinds[0]?.id ?? '')\n const [composerError, setComposerError] = useState<string | null>(null)\n\n const addEntry = (event: FormEvent) => {\n event.preventDefault()\n const text = composerText.trim()\n if (text === '' || composerBusy) return\n\n setComposerBusy(true)\n setComposerError(null)\n const write = async () => {\n const user = adapters.identity ? await adapters.identity.currentUser() : null\n const data: Record<string, unknown> = {\n [config.dateField]: adapters.clock.now().toISOString(),\n [config.bodyField]: text,\n }\n if (config.actorField && user) data[config.actorField] = user.name\n if (config.kindField && composerKind !== '') data[config.kindField] = composerKind\n await adapters.records.create(config.collection, data)\n }\n void write().then(\n () => {\n setComposerText('')\n setComposerBusy(false)\n jumpToTop()\n },\n (cause: unknown) => {\n setComposerError(message(cause))\n setComposerBusy(false)\n },\n )\n }\n\n const composer = config.composer && (\n <form onSubmit={addEntry} className=\"mb-3\">\n <div className=\"flex items-center gap-2\">\n <input\n value={composerText}\n onChange={(event) => setComposerText(event.target.value)}\n placeholder=\"Add an entry\"\n aria-label=\"Add an entry\"\n className=\"min-w-0 flex-1 rounded-lg border border-neutral-300 bg-white px-3 py-2 text-base sm:text-sm\"\n />\n {config.kindField && config.kinds.length > 0 && (\n <select\n value={composerKind}\n onChange={(event) => setComposerKind(event.target.value)}\n aria-label=\"Kind\"\n className=\"shrink-0 rounded-lg border border-neutral-300 bg-white px-2 py-2 text-sm\"\n >\n {config.kinds.map((kind) => (\n <option key={kind.id} value={kind.id}>\n {kind.label}\n </option>\n ))}\n </select>\n )}\n <button\n type=\"submit\"\n disabled={composerBusy}\n className=\"shrink-0 rounded-lg bg-neutral-900 px-3.5 py-2 text-sm font-medium text-white disabled:opacity-60\"\n >\n Add\n </button>\n </div>\n {composerError !== null && (\n <p role=\"alert\" className=\"mt-1.5 text-sm text-red-800\">\n {composerError}\n </p>\n )}\n </form>\n )\n\n const controls = (config.search.length > 0 || config.filters.length > 0) && (\n <div className=\"mb-3 space-y-2\">\n {config.search.length > 0 && (\n <input\n type=\"search\"\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n aria-label=\"Search\"\n placeholder=\"Search the log\"\n className=\"w-full rounded-lg border border-neutral-300 bg-white px-3 py-2 text-base sm:text-sm\"\n />\n )}\n {config.filters.includes('kind') && (\n <ChipRow\n label=\"Kind\"\n values={config.kinds}\n chosen={chosen.kind}\n onChoose={(value) =>\n setChosen((prev) => ({ ...prev, kind: value === null ? undefined : value }))\n }\n />\n )}\n {config.filters.includes('actor') && (\n <ChipRow\n label=\"Who\"\n values={actors.map((name) => ({ id: name, label: name }))}\n chosen={chosen.actor}\n onChoose={(value) =>\n setChosen((prev) => ({ ...prev, actor: value === null ? undefined : value }))\n }\n />\n )}\n </div>\n )\n\n let body: ReactNode\n if (loading) {\n body = <p className=\"px-4 py-8 text-center text-sm text-neutral-500\">Loading…</p>\n } else if (error !== null) {\n body = (\n <p role=\"alert\" className=\"px-4 py-6 text-center text-sm text-red-900\">\n {error}\n </p>\n )\n } else if (entries.length === 0) {\n body = <p className=\"px-4 py-8 text-center text-sm text-neutral-500\">{config.emptyState}</p>\n } else {\n body = days.map((group) => (\n <section key={group.key} data-golem-day={group.key}>\n {/* Sticky against the feed's own scroll box, so the day you are reading names itself. */}\n <h3 className=\"sticky top-0 z-10 border-b border-neutral-200 bg-neutral-50/95 px-4 py-1.5 text-xs font-semibold tracking-wide text-neutral-500 uppercase backdrop-blur\">\n {dayLabel(group.key, today, yesterday, config.locale)}\n </h3>\n <ol>\n {group.entries.map(({ entry, at }) => {\n const kind = kindOf(entry)\n const mark = MARKS[kind?.tone ?? 'neutral']\n const actor = config.actorField ? userName(entry[config.actorField]) : ''\n const files = config.attachmentsField ? filesOf(entry[config.attachmentsField]) : []\n return (\n <li\n key={entry.id}\n data-golem-entry={entry.id}\n className={`relative border-b border-neutral-100 py-3 pr-4 pl-9 last:border-0 ${\n isNew(entry) ? 'animate-[golem-slide-in_240ms_ease-out]' : ''\n }`}\n >\n <span\n aria-hidden=\"true\"\n className={`absolute top-3 left-3 text-xs leading-5 ${mark.dot}`}\n >\n {mark.glyph}\n </span>\n <div className=\"flex flex-wrap items-baseline gap-x-2 gap-y-1\">\n {kind && (\n <span\n className={`inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-xs font-medium ${mark.chip}`}\n >\n {kind.label}\n </span>\n )}\n {/* An unknown kind is shown as it is written, rather than hidden: the record\n says something the config has no word for, and that is worth seeing. */}\n {config.kindField && !kind && entry[config.kindField] != null && (\n <span className=\"inline-flex shrink-0 items-center rounded-full bg-neutral-100 px-2 py-0.5 text-xs font-medium text-neutral-500\">\n {String(entry[config.kindField])}\n </span>\n )}\n {actor !== '' && (\n <span className=\"flex items-center gap-1.5 text-xs text-neutral-600\">\n <span className=\"flex size-5 shrink-0 items-center justify-center rounded-full bg-neutral-900 text-[9px] font-semibold text-white\">\n {initials(actor)}\n </span>\n {actor}\n </span>\n )}\n <time\n dateTime={at?.toISOString()}\n className=\"ml-auto shrink-0 text-xs text-neutral-400\"\n >\n {timeLabel(at, now, group.key === today, config.locale, timeZone)}\n </time>\n </div>\n <div className=\"mt-1 text-sm leading-relaxed text-neutral-800\">\n <Markdown text={String(entry[config.bodyField] ?? '')} />\n </div>\n {files.length > 0 && <Attachments files={files} adapter={adapters.files} />}\n </li>\n )\n })}\n </ol>\n </section>\n ))\n }\n\n return (\n <div\n data-golem-component=\"Timeline\"\n className={`golem-timeline w-full text-neutral-900 ${stale ? 'opacity-60' : ''}`}\n >\n {composer}\n {controls}\n\n <div className=\"relative\">\n {arrived > 0 && (\n <button\n type=\"button\"\n data-golem-new-pill=\"\"\n onClick={jumpToTop}\n className=\"absolute top-2 left-1/2 z-20 -translate-x-1/2 rounded-full bg-neutral-900 px-3 py-1.5 text-xs font-medium text-white shadow-lg\"\n >\n {arrived === 1 ? '1 new entry' : `${arrived} new entries`} ↑\n </button>\n )}\n {/* Its own scroll box, so the day headers have something to stick to and the pill has a\n scroll position to watch. */}\n <div\n ref={feed}\n data-golem-feed=\"\"\n onScroll={onScroll}\n className=\"max-h-[70vh] overflow-y-auto rounded-xl border border-neutral-200 bg-white\"\n >\n {body}\n {nextCursor !== null && !loading && (\n <div className=\"flex justify-center border-t border-neutral-100 p-3\">\n <button\n type=\"button\"\n onClick={loadOlder}\n disabled={loadingMore}\n className=\"rounded-lg border border-neutral-300 bg-white px-4 py-2 text-sm font-medium disabled:opacity-60\"\n >\n {loadingMore ? 'Loading…' : `Load ${config.pageSize} older`}\n </button>\n </div>\n )}\n </div>\n </div>\n </div>\n )\n}\n\nexport const Timeline = defineComponent<typeof timelineConfigSchema, TimelineAdapters>({\n name: 'Timeline',\n schema: timelineConfigSchema,\n render: TimelineBody,\n})\n","import { z } from 'zod'\n\n/** `type/subtype`, where the subtype may be `*`. What `accept` is checked against. */\nconst MIME = /^[a-z]+\\/[a-z0-9!#$&^_.+-]+$|^[a-z]+\\/\\*$/\n\nexport const uploadConfigSchema = z\n .object({\n folder: z\n .string()\n .min(1)\n .describe(\n 'Where the files live, passed to `upload`, `list` and `subscribe`. One folder is one gallery; two Uploads on the same folder show the same files.',\n ),\n accept: z\n .array(z.string().min(1))\n .default([])\n .describe(\n 'The MIME types the picker offers and the component lets through, as `image/jpeg` or `image/*`. Empty accepts anything. A file of any other type is refused before it is uploaded.',\n ),\n maxFiles: z\n .number()\n .int()\n .min(1)\n .max(500)\n .default(20)\n .describe(\n 'How many files the folder may hold. Everything already in the gallery counts, so the last pick is the one refused.',\n ),\n maxSizeMb: z\n .number()\n .positive()\n .max(2048)\n .default(10)\n .describe(\n 'The largest file accepted, in megabytes. It is checked in the browser before `upload` is called, so an over-size file never leaves the machine.',\n ),\n capture: z\n .enum(['environment', 'user', 'off'])\n .default('off')\n .describe(\n 'Whether a phone gets a camera button beside the file button: `environment` opens the rear camera, `user` the front one, `off` shows no camera button. A desktop browser ignores it and offers the file picker.',\n ),\n captions: z\n .boolean()\n .default(false)\n .describe(\n 'Whether each file carries one line of the reader’s own words, written under the tile and saved through `Files.caption`.',\n ),\n layout: z\n .enum(['grid', 'list'])\n .default('grid')\n .describe(\n 'How the gallery reads: `grid` is thumbnails, for a folder of photos; `list` is one row per file with its icon, name and size, for a folder of documents.',\n ),\n deleteAllowed: z\n .boolean()\n .default(true)\n .describe(\n 'Whether a file can be deleted from the gallery. Off hides every delete button; the component never calls `Files.remove`.',\n ),\n emptyState: z\n .string()\n .min(1)\n .default('No files yet.')\n .describe('One line shown in place of the gallery while the folder is empty.'),\n })\n .strict()\n .superRefine((config, ctx) => {\n for (const [index, type] of config.accept.entries()) {\n if (!MIME.test(type)) {\n ctx.addIssue({\n code: 'custom',\n path: ['accept', index],\n message: `\"${type}\" is not a MIME type, so no file can ever match it. Write it as type/subtype: image/jpeg, application/pdf, image/*.`,\n })\n }\n }\n })\n\nexport type UploadConfig = z.output<typeof uploadConfigSchema>\nexport type UploadConfigInput = z.input<typeof uploadConfigSchema>\n\n/** Why a file was turned away before anything was uploaded. */\nexport type RejectionReason = 'size' | 'type' | 'count'\n\n/**\n * A file the component refused on its own, without asking the adapter. It is thrown nowhere: the\n * component catches its own limits and shows the `message` on the file's row, which is where the\n * reader can do something about it.\n */\nexport class FileRejectedError extends Error {\n readonly fileName: string\n readonly reason: RejectionReason\n\n constructor(fileName: string, reason: RejectionReason, message: string) {\n super(message)\n this.name = 'FileRejectedError'\n this.fileName = fileName\n this.reason = reason\n }\n}\n\nconst MB = 1024 * 1024\n\nexport function megabytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < MB) return `${Math.round(bytes / 1024)} KB`\n return `${(bytes / MB).toFixed(1)} MB`\n}\n\n/** Whether the type is one the `accept` list lets through. An empty list lets everything through. */\nexport function accepts(accept: string[], contentType: string): boolean {\n if (accept.length === 0) return true\n const [group, sub] = contentType.split('/')\n return accept.some((entry) => {\n const [wantGroup, wantSub] = entry.split('/')\n return wantGroup === group && (wantSub === '*' || wantSub === sub)\n })\n}\n\n/**\n * The three limits, checked in the order a reader would ask about them: is it the right kind of\n * thing, is it small enough, is there room. Returns the refusal, or `null` when the file may go up.\n * `position` is what number this file would be in the folder, counting from one.\n */\nexport function checkFile(\n file: { name: string; size: number; type: string },\n config: UploadConfig,\n position: number,\n): FileRejectedError | null {\n if (!accepts(config.accept, file.type)) {\n return new FileRejectedError(\n file.name,\n 'type',\n `“${file.name}” is ${file.type || 'of no known type'}. This upload takes ${config.accept.join(', ')}.`,\n )\n }\n if (file.size > config.maxSizeMb * MB) {\n return new FileRejectedError(\n file.name,\n 'size',\n `“${file.name}” is ${megabytes(file.size)}. The limit is ${config.maxSizeMb} MB.`,\n )\n }\n if (position > config.maxFiles) {\n return new FileRejectedError(\n file.name,\n 'count',\n `“${file.name}” would be file ${position}. This upload holds ${config.maxFiles}.`,\n )\n }\n return null\n}\n","import { useCallback, useEffect, useRef, useState, type DragEvent, type ReactNode } from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { FileRef, FilesAdapter } from '../../adapters'\nimport { checkFile, megabytes, uploadConfigSchema, type UploadConfig } from './Upload.config'\n\nexport interface UploadAdapters {\n files: FilesAdapter\n}\n\nexport interface PickerSlots {\n /**\n * Called with everything the picker is holding, every time that changes — a file finishing its\n * upload, or a chip being taken off. The parent composer keeps the list; the picker only shows it.\n */\n onPicked?: (refs: FileRef[]) => void\n}\n\n/** One file on its way up, or one that did not make it. */\ninterface QueueItem {\n key: string\n name: string\n size: number\n /** 0 to 1. Stays at 0 for a file refused before the upload started. */\n progress: number\n error: string | null\n}\n\nconst message = (cause: unknown) => (cause instanceof Error ? cause.message : String(cause))\n\n/** What tiles a file that is not a picture. Type first, then the extension. */\nfunction iconFor(contentType: string, name: string): string {\n if (contentType.startsWith('image/')) return '🖼'\n if (contentType.startsWith('audio/')) return '🎧'\n if (contentType.startsWith('video/')) return '🎬'\n if (contentType === 'application/pdf' || name.endsWith('.pdf')) return '📄'\n return '📎'\n}\n\nconst isImage = (ref: FileRef) => ref.contentType.startsWith('image/')\n\n/**\n * The upload queue: what the reader picked, what the limits said about it, and how far up each one\n * has got. Both surfaces run on this, so a file is refused for the same reason and with the same\n * sentence whether it was picked in a gallery or in a composer.\n */\nfunction useQueue(\n files: FilesAdapter,\n config: UploadConfig,\n onUploaded: (ref: FileRef) => void,\n held: () => number,\n) {\n const [queue, setQueue] = useState<QueueItem[]>([])\n const nextKey = useRef(0)\n\n const dismiss = useCallback((key: string) => {\n setQueue((rows) => rows.filter((row) => row.key !== key))\n }, [])\n\n const add = useCallback(\n (picked: File[]) => {\n // Where in the folder each of these would land, so the count limit names the right file.\n let position = held()\n\n for (const file of picked) {\n const key = `q-${nextKey.current++}`\n position += 1\n const refusal = checkFile(file, config, position)\n\n setQueue((rows) => [\n ...rows,\n {\n key,\n name: file.name,\n size: file.size,\n progress: 0,\n error: refusal ? refusal.message : null,\n },\n ])\n\n // A refused file never reaches the adapter: the limit is enforced here, on this machine.\n if (refusal) {\n position -= 1\n continue\n }\n\n const advance = (fraction: number) =>\n setQueue((rows) =>\n rows.map((row) => (row.key === key ? { ...row, progress: fraction } : row)),\n )\n\n void files.upload(file, { folder: config.folder, onProgress: advance }).then(\n (ref) => {\n // The file has left the queue for the gallery; keeping a finished row would say it\n // is still in flight.\n dismiss(key)\n onUploaded(ref)\n },\n (cause: unknown) => {\n setQueue((rows) =>\n rows.map((row) => (row.key === key ? { ...row, error: message(cause) } : row)),\n )\n },\n )\n }\n },\n [files, config, onUploaded, held, dismiss],\n )\n\n return { queue, add, dismiss }\n}\n\ninterface GalleryUrls {\n full: string\n thumbnail: string\n}\n\n/** What is in the folder, and the URLs to draw it with. Nothing polls; `subscribe` re-lists. */\nfunction useGallery(files: FilesAdapter, folder: string) {\n const [refs, setRefs] = useState<FileRef[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n // `null` is a file whose URL the store refused, told apart from one not asked for yet, so a\n // thumbnail that will never arrive tiles as an icon instead of an empty frame.\n const [urls, setUrls] = useState<Record<string, GalleryUrls | null>>({})\n\n useEffect(() => {\n let live = true\n const refresh = () => {\n void files.list(folder).then(\n (found) => {\n if (!live) return\n setRefs(found)\n setError(null)\n },\n (cause: unknown) => {\n if (!live) return\n setRefs([])\n setError(message(cause))\n },\n )\n }\n refresh()\n const unsubscribe = files.subscribe(folder, refresh)\n return () => {\n live = false\n unsubscribe()\n }\n }, [files, folder])\n\n // One pair of URLs per file, resolved once. A file that has gone keeps its entry until unmount,\n // which costs a map entry and saves re-resolving a file that comes back.\n useEffect(() => {\n let live = true\n for (const ref of refs ?? []) {\n if (ref.id in urls) continue\n void Promise.all([files.url(ref, { thumbnail: true }), files.url(ref)]).then(\n ([thumbnail, full]) => {\n if (live) setUrls((current) => ({ ...current, [ref.id]: { full, thumbnail } }))\n },\n () => {\n if (live) setUrls((current) => ({ ...current, [ref.id]: null }))\n },\n )\n }\n return () => {\n live = false\n }\n }, [refs, files, urls])\n\n return { refs, error, urls }\n}\n\nfunction ProgressBar({ fraction }: { fraction: number }) {\n const percent = Math.round(fraction * 100)\n return (\n <div\n role=\"progressbar\"\n aria-valuenow={percent}\n aria-valuemin={0}\n aria-valuemax={100}\n className=\"mt-1 h-1.5 w-full overflow-hidden rounded-full bg-neutral-200\"\n >\n <div\n style={{ width: `${percent}%` }}\n className=\"h-full rounded-full bg-neutral-900 transition-[width] duration-150\"\n />\n </div>\n )\n}\n\nfunction Queue({ rows, onDismiss }: { rows: QueueItem[]; onDismiss: (key: string) => void }) {\n if (rows.length === 0) return null\n return (\n <ul className=\"mt-3 space-y-2\">\n {rows.map((row) => (\n <li\n key={row.key}\n data-golem-queue-item={row.name}\n className={`rounded-lg border p-2.5 ${\n row.error === null ? 'border-neutral-200 bg-white' : 'border-red-300 bg-red-50'\n }`}\n >\n <div className=\"flex items-baseline gap-2\">\n <span className=\"min-w-0 flex-1 truncate text-sm\">{row.name}</span>\n <span className=\"shrink-0 text-xs text-neutral-500\">\n {row.error === null ? `${Math.round(row.progress * 100)}%` : megabytes(row.size)}\n </span>\n <button\n type=\"button\"\n onClick={() => onDismiss(row.key)}\n aria-label={`Dismiss ${row.name}`}\n className=\"shrink-0 text-neutral-400 hover:text-neutral-700\"\n >\n ×\n </button>\n </div>\n {row.error === null ? (\n <ProgressBar fraction={row.progress} />\n ) : (\n <p role=\"alert\" className=\"mt-1 text-xs text-red-800\">\n {row.error}\n </p>\n )}\n </li>\n ))}\n </ul>\n )\n}\n\n/**\n * The three ways a file gets in: dropped on the zone, chosen from the file picker, or taken with\n * the camera. All three land in the same `add`.\n */\nfunction DropZone({\n config,\n onFiles,\n}: {\n config: UploadConfig\n onFiles: (picked: File[]) => void\n}) {\n const [over, setOver] = useState(false)\n const chooser = useRef<HTMLInputElement>(null)\n const camera = useRef<HTMLInputElement>(null)\n\n const accept = config.accept.join(',')\n const take = (list: FileList | null) => onFiles([...(list ?? [])])\n\n const onDrop = (event: DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n setOver(false)\n take(event.dataTransfer.files)\n }\n\n return (\n <div\n data-golem-dropzone=\"\"\n onDragOver={(event) => {\n event.preventDefault()\n setOver(true)\n }}\n onDragLeave={() => setOver(false)}\n onDrop={onDrop}\n className={`rounded-xl border-2 border-dashed p-5 text-center ${\n over ? 'border-neutral-900 bg-neutral-100' : 'border-neutral-300 bg-white'\n }`}\n >\n <p className=\"text-sm text-neutral-600\">Drop files here, or pick them from this device.</p>\n <p className=\"mt-1 text-xs text-neutral-400\">\n Up to {config.maxFiles} files, {config.maxSizeMb} MB each\n {config.accept.length > 0 ? ` · ${config.accept.join(', ')}` : ''}\n </p>\n\n <div className=\"mt-3 flex flex-wrap justify-center gap-2\">\n <button\n type=\"button\"\n onClick={() => chooser.current?.click()}\n className=\"rounded-lg bg-neutral-900 px-4 py-2.5 text-sm font-medium text-white\"\n >\n Choose files\n </button>\n {config.capture !== 'off' && (\n <button\n type=\"button\"\n onClick={() => camera.current?.click()}\n className=\"rounded-lg border border-neutral-300 bg-white px-4 py-2.5 text-sm font-medium\"\n >\n 📷 Take a photo\n </button>\n )}\n </div>\n\n <input\n ref={chooser}\n type=\"file\"\n multiple\n accept={accept || undefined}\n aria-label=\"Choose files\"\n className=\"hidden\"\n onChange={(event) => {\n take(event.target.files)\n event.target.value = ''\n }}\n />\n {config.capture !== 'off' && (\n <input\n ref={camera}\n type=\"file\"\n accept=\"image/*\"\n capture={config.capture}\n aria-label=\"Take a photo\"\n className=\"hidden\"\n onChange={(event) => {\n take(event.target.files)\n event.target.value = ''\n }}\n />\n )}\n </div>\n )\n}\n\nfunction Caption({ file, files }: { file: FileRef; files: FilesAdapter }) {\n const [text, setText] = useState(file.caption ?? '')\n\n return (\n <input\n value={text}\n onChange={(event) => setText(event.target.value)}\n // Saved when the reader leaves the box, not on every keystroke: a caption is a sentence.\n onBlur={() => {\n if (text !== (file.caption ?? '')) void files.caption(file, text)\n }}\n placeholder=\"Add a caption\"\n aria-label={`Caption for ${file.name}`}\n className=\"mt-1.5 w-full rounded border border-neutral-200 px-2 py-1 text-xs\"\n />\n )\n}\n\nfunction DeleteButton({\n file,\n files,\n onDeleted,\n}: {\n file: FileRef\n files: FilesAdapter\n onDeleted: () => void\n}) {\n const [asking, setAsking] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n if (!asking) {\n return (\n <button\n type=\"button\"\n onClick={() => setAsking(true)}\n className=\"text-xs font-medium text-neutral-500 hover:text-red-700\"\n >\n Delete\n </button>\n )\n }\n\n return (\n <span role=\"alertdialog\" aria-label={`Delete ${file.name}?`} className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n void files.remove(file).then(onDeleted, (cause: unknown) => setError(message(cause)))\n }}\n className=\"rounded bg-red-600 px-2 py-1 text-xs font-medium text-white\"\n >\n Delete permanently\n </button>\n <button\n type=\"button\"\n onClick={() => setAsking(false)}\n className=\"rounded border border-neutral-300 px-2 py-1 text-xs font-medium\"\n >\n Keep it\n </button>\n {error !== null && (\n <span role=\"alert\" className=\"text-xs text-red-800\">\n {error}\n </span>\n )}\n </span>\n )\n}\n\nfunction Lightbox({\n file,\n url,\n onClose,\n}: {\n file: FileRef\n url: string | undefined\n onClose: () => void\n}) {\n useEffect(() => {\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onClose()\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [onClose])\n\n return (\n <div\n role=\"dialog\"\n aria-label={file.name}\n data-golem-lightbox={file.name}\n onClick={onClose}\n className=\"fixed inset-0 z-50 flex flex-col items-center justify-center gap-3 bg-black/80 p-4\"\n >\n {isImage(file) && url ? (\n <img\n src={url}\n alt={file.caption ?? file.name}\n onClick={(event) => event.stopPropagation()}\n className=\"max-h-[75vh] max-w-full rounded-lg bg-white object-contain\"\n />\n ) : (\n <div\n onClick={(event) => event.stopPropagation()}\n className=\"rounded-xl bg-white p-6 text-center\"\n >\n <p className=\"text-4xl\">{iconFor(file.contentType, file.name)}</p>\n <p className=\"mt-2 text-sm font-medium\">{file.name}</p>\n <p className=\"mt-1 text-xs text-neutral-500\">\n {file.contentType || 'unknown type'} · {megabytes(file.size)}\n </p>\n {url && (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-3 inline-block text-sm font-medium underline\"\n >\n Open it\n </a>\n )}\n </div>\n )}\n <p className=\"max-w-full truncate text-sm text-white\">{file.caption ?? file.name}</p>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg bg-white px-4 py-2 text-sm font-medium\"\n >\n Close\n </button>\n </div>\n )\n}\n\nfunction Gallery({\n config,\n files,\n refs,\n urls,\n}: {\n config: UploadConfig\n files: FilesAdapter\n refs: FileRef[]\n urls: Record<string, GalleryUrls | null>\n}) {\n const [open, setOpen] = useState<string | null>(null)\n const openFile = refs.find((ref) => ref.id === open)\n\n const grid = config.layout === 'grid'\n // A picture the store gave us no URL for is drawn as its icon: an empty frame would say the\n // file is blank rather than that the thumbnail did not arrive.\n const asPicture = (file: FileRef) => isImage(file) && urls[file.id] !== null\n\n const tile = (file: FileRef) => (\n <div className=\"min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={() => setOpen(file.id)}\n aria-label={`Open ${file.name}`}\n className=\"block w-full text-left\"\n >\n {grid ? (\n // Every tile is the same square, picture or not, so a row of the grid is one line high.\n <span className=\"block aspect-square overflow-hidden rounded-lg bg-neutral-100\">\n {asPicture(file) ? (\n urls[file.id] && (\n <img\n src={urls[file.id]!.thumbnail}\n alt={file.caption ?? file.name}\n className=\"size-full object-cover\"\n />\n )\n ) : (\n <span className=\"flex size-full flex-col items-center justify-center gap-1\">\n <span className=\"text-3xl leading-none\">\n {iconFor(file.contentType, file.name)}\n </span>\n <span className=\"text-xs text-neutral-500\">{megabytes(file.size)}</span>\n </span>\n )}\n </span>\n ) : (\n <span className=\"flex items-center gap-2\">\n <span className=\"text-2xl leading-none\">{iconFor(file.contentType, file.name)}</span>\n <span className=\"min-w-0\">\n <span className=\"block truncate text-sm font-medium\">{file.name}</span>\n <span className=\"block text-xs text-neutral-500\">{megabytes(file.size)}</span>\n </span>\n </span>\n )}\n </button>\n\n {grid && <p className=\"mt-1 truncate text-xs text-neutral-600\">{file.name}</p>}\n {config.captions && <Caption file={file} files={files} />}\n </div>\n )\n\n return (\n <>\n <ul\n className={\n config.layout === 'grid'\n ? 'mt-4 grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4'\n : 'mt-4 space-y-2'\n }\n >\n {refs.map((file) => (\n <li\n key={file.id}\n data-golem-file={file.id}\n className={`rounded-xl border border-neutral-200 bg-white p-2 ${\n config.layout === 'list' ? 'flex items-center gap-3' : ''\n }`}\n >\n {tile(file)}\n {config.deleteAllowed && (\n <div className={config.layout === 'list' ? 'shrink-0' : 'mt-1'}>\n <DeleteButton\n file={file}\n files={files}\n onDeleted={() => setOpen((id) => (id === file.id ? null : id))}\n />\n </div>\n )}\n </li>\n ))}\n </ul>\n\n {openFile && (\n <Lightbox file={openFile} url={urls[openFile.id]?.full} onClose={() => setOpen(null)} />\n )}\n </>\n )\n}\n\nfunction UploadPanel({ config, adapters }: GolemProps<UploadConfig, UploadAdapters>) {\n const { refs, error, urls } = useGallery(adapters.files, config.folder)\n\n // The count limit is about the folder, so what is already in the gallery counts towards it.\n const countHeld = useCallback(() => refs?.length ?? 0, [refs])\n const noop = useCallback(() => {}, [])\n\n const { queue, add, dismiss } = useQueue(adapters.files, config, noop, countHeld)\n\n let body: ReactNode\n if (refs === null) {\n body = <p className=\"mt-6 text-center text-sm text-neutral-500\">Loading…</p>\n } else if (error !== null) {\n body = (\n <p role=\"alert\" className=\"mt-6 text-center text-sm text-red-900\">\n {error}\n </p>\n )\n } else if (refs.length === 0) {\n body = <p className=\"mt-6 text-center text-sm text-neutral-500\">{config.emptyState}</p>\n } else {\n body = <Gallery config={config} files={adapters.files} refs={refs} urls={urls} />\n }\n\n return (\n <div data-golem-component=\"Upload\" className=\"golem-upload w-full text-neutral-900\">\n <DropZone config={config} onFiles={add} />\n <Queue rows={queue} onDismiss={dismiss} />\n {body}\n </div>\n )\n}\n\nfunction PickerButton({\n config,\n adapters,\n onPicked,\n}: GolemProps<UploadConfig, UploadAdapters, PickerSlots>) {\n const [picked, setPicked] = useState<FileRef[]>([])\n const input = useRef<HTMLInputElement>(null)\n\n // The parent hears about the list, never about the change: one place to report from, and the\n // callback is read out of a ref so a composer that writes its slot inline does not re-report on\n // every render of the composer.\n const report = useRef(onPicked)\n useEffect(() => {\n report.current = onPicked\n })\n useEffect(() => {\n report.current?.(picked)\n }, [picked])\n\n const onUploaded = useCallback((ref: FileRef) => setPicked((held) => [...held, ref]), [])\n const countHeld = useCallback(() => picked.length, [picked])\n\n const { queue, add, dismiss } = useQueue(adapters.files, config, onUploaded, countHeld)\n\n return (\n <div data-golem-component=\"Upload.Picker\" className=\"golem-upload-picker w-full\">\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <button\n type=\"button\"\n onClick={() => input.current?.click()}\n className=\"shrink-0 rounded-full border border-neutral-300 bg-white px-3 py-1.5 text-sm\"\n >\n 📎 Attach\n </button>\n {picked.map((file) => (\n <span\n key={file.id}\n data-golem-picker-chip={file.name}\n className=\"inline-flex max-w-full items-center gap-1 rounded-full border border-neutral-200 bg-neutral-50 px-2 py-0.5 text-xs\"\n >\n <span aria-hidden=\"true\">{iconFor(file.contentType, file.name)}</span>\n <span className=\"truncate\">{file.name}</span>\n <button\n type=\"button\"\n onClick={() => setPicked((held) => held.filter((one) => one.id !== file.id))}\n aria-label={`Remove ${file.name}`}\n className=\"shrink-0 opacity-60 hover:opacity-100\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n\n <Queue rows={queue} onDismiss={dismiss} />\n\n <input\n ref={input}\n type=\"file\"\n multiple\n accept={config.accept.join(',') || undefined}\n capture={config.capture === 'off' ? undefined : config.capture}\n aria-label=\"Attach files\"\n className=\"hidden\"\n onChange={(event) => {\n add([...(event.target.files ?? [])])\n event.target.value = ''\n }}\n />\n </div>\n )\n}\n\nconst UploadComponent = defineComponent<typeof uploadConfigSchema, UploadAdapters>({\n name: 'Upload',\n schema: uploadConfigSchema,\n render: UploadPanel,\n})\n\nconst Picker = defineComponent<typeof uploadConfigSchema, UploadAdapters, PickerSlots>({\n name: 'Upload.Picker',\n schema: uploadConfigSchema,\n render: PickerButton,\n})\n\n/**\n * Two surfaces on one config. `Upload` is the whole thing — a place to drop files, a queue while\n * they go up, and the gallery they land in. `Upload.Picker` is the same picking and the same\n * limits with no gallery: a button a composer hosts, handing the file refs back to whatever is\n * about to send them.\n */\nexport const Upload = Object.assign(UploadComponent, { Picker })\n"],"names":["issuesFromZodError","error","issue","ConfigErrorCard","component","issues","jsxs","jsx","i","defineComponent","spec","Render","Component","props","config","rest","parsed","useMemo","FIELD_TYPES","common","z","listFieldSchema","fieldOptionSchema","formFieldSchema","RecordRefusedError","message","fields","refusedFields","named","one","VERSION_FIELD","VersionConflictError","current","conflictingRecord","thrown","fileId","ref","parseLocation","location","match","start","createEmitter","listeners","listener","value","matches","wanted","compare","a","b","left","right","hit","row","text","needle","field","fakeRecords","seed","options","store","name","rows","emitters","nextId","collection","emitterFor","indexOf","id","refuseIfAsked","refuse","insert","next","query","result","entries","key","want","direction","sign","total","offset","limit","end","found","data","patch","at","STEPS","placeholderImage","label","hue","svg","objectUrlFor","file","loadImage","url","graceMs","resolve","image","timer","settle","THUMB_EDGE","thumbnailOf","contentType","scale","canvas","context","fakeFiles","failOn","uploadMs","now","folders","folder","emitter","content","stored","onProgress","full","step","urlOptions","caption","fakeUser","fakeIdentity","password","code","defaultRole","inviteBase","members","member","user","invites","find","email","enter","given","input","role","invited","token","userId","index","entry","DEFAULT_REPLIES","fakeChat","initial","replies","tokenDelayMs","messages","sent","emit","stream","reply","words","written","tick","attachments","line","command","c","arg","fakeClock","fixed","timeZone","fakeNavigation","route","path","frontMatter","pair","RESERVED","dirOf","dir","fakeBrain","files","list","prefix","seen","slash","own","lines","description","hits","excerpt","brainAdapterDocs","chatAdapterDocs","clockAdapterDocs","filesAdapterDocs","identityAdapterDocs","navigationAdapterDocs","recordsAdapterDocs","brainDocs","authDocs","chatDocs","editorDocs","recordFormDocs","recordListDocs","reportDocs","shellDocs","timelineDocs","uploadDocs","componentDocs","adapterDocs","adapterDocsFor","slug","docs","adapterDocsNamed","componentsUsing","adapter","roleSchema","copySchema","authConfigSchema","useSession","identity","setUser","useState","ready","setReady","useEffect","live","unsubscribe","useInviteToken","navigation","setToken","roleFor","labelForRole","canManage","messageOf","fieldClass","primaryClass","quietClass","Field","Card","children","SignInScreen","adapters","invite","signUpOffered","signUpReachable","chose","setChose","setName","setEmail","secret","setSecret","codeSent","setCodeSent","setError","busy","setBusy","creating","creatingNow","codeStage","run","event","action","failure","submit","title","useMembers","setMembers","reload","useCallback","MembersScreen","manages","inviteRole","setInviteRole","inviteUrl","setInviteUrl","copied","setCopied","mint","AccountMenuPanel","open","setOpen","initialsOf","part","DeniedCard","roles","allowed","AuthScreen","AuthGuard","AuthComponent","Guard","AccountMenu","Auth","INLINE","inlineNodes","keyPrefix","piece","link","external","href","Fragment","headingSlug","cells","cell","isTableRule","blocksOf","blocks","fence","blank","heading","table","bullet","numbered","last","ordered","item","HEADING_SIZES","Markdown","hardWraps","resolveLink","inline","block","Tag","cellIndex","rowIndex","List","windowWidth","useContainerWidth","measured","setMeasured","viewport","setViewport","element","observer","onResize","brainConfigSchema","PHONE","BRAIN_LINK","ROOT","same","locationOf","f","resolvePath","from","relative","parts","close","meta","m","Skeleton","_","Tree","onOpen","version","setEntries","collapsed","setCollapsed","Hits","setFound","Reader","frame","marked","useRef","body","mark","box","to","head","BackIcon","SearchIcon","BrainPanel","toolbar","root","article","phone","stack","setStack","scrolls","setVersion","doc","setDoc","setQuery","seenLocation","setSeenLocation","s","push","back","v","cause","depth","useLayoutEffect","onArticleClick","document","Brain","chatConfigSchema","PIN_SLACK","SLASH_LINE","CMD_RE","ARG_RE","slashMatches","commands","args","q","useConversation","setMessages","loadError","setLoadError","receivedUpdate","useStickToBottom","dependency","feed","pinned","onScroll","hhmm","sizeLabel","bytes","AttachmentChips","onRemove","sourceLabel","range","Bubble","onRetry","onOpenSource","mine","pending","failed","copy","Thinking","onStop","stopping","setStopping","delay","ChatPanel","attach","draft","setDraft","staged","setStaged","setSent","sendError","setSendError","composer","fileInput","commandRows","setCommandRows","setCommands","selected","setSelected","pickerClosed","setPickerClosed","pickerWanted","pickerOpen","pick","visible","thinking","max","send","count","stamp","retry","messageId","interrupt","Chat","MAX_LINES","splitLines","matchedPairs","cols","j","pairs","editsOf","base","side","which","edits","baseAt","sideAt","baseEnd","sideEnd","baseIndex","sideIndex","merge3","baseText","mineText","theirsText","theirs","chunks","conflicted","mineAt","theirsAt","keepUntouched","until","group","baseStart","sides","edit","sliceOf","cursor","mineLines","theirsLines","chunk","mergedText","choose","theirLines","taken","isFormattableLocale","locale","formattableZone","editorConfigSchema","ctx","scanSource","fenced","headingsOf","LINE_CLASS","MARKER","SourceRow","SPLIT_ABOVE","EMPTY","decode","passageOf","focus","atVersion","eol","first","clamp","holds","COALESCE_MS","useHistory","state","reset","remember","history","delta","EditorBody","openWith","records","clock","textarea","overlay","preview","sourcePane","setDocState","docRef","mode","setMode","outlineOpen","setOutlineOpen","selection","record","opened","parked","loaded","reads","setReads","wide","split","showPreview","showSource","applyIncoming","merged","save","expected","here","away","saved","shelf","read","load","opening","ask","asked","spot","setSpot","scrollToSpot","request","passage","pane","origin","top","bottom","scanned","headings","shown","write","caret","wrap","marker","indent","out","stop","shifted","moved","onKeyDown","jumpTo","sum","choices","_chunk","later","Toolbar","Outline","Conflict","focused","button","onOutline","onMode","onWrap","onSave","Status","time","onJump","onChoose","Side","chosen","Editor","DATE_STYLES","dateStyle","format","fallback","userName","initials","formatValue","digits","CHIP_TONES","chipTone","hash","recordFormConfigSchema","blankValue","isEmpty","toInput","fromInput","below","bound","validateField","option","validateAll","values","failures","dataOf","patchOf","ONE_COLUMN_BELOW","controlClass","okBorder","badBorder","useLoadedRecord","recordId","setState","ReadOnlyValue","RecordFormBody","onDone","onCancel","formId","useId","setEdits","committed","setCommitted","errors","setErrors","formError","setFormError","setSaved","asking","setAsking","conflict","setConflict","controls","baseOf","editable","dirty","width","columns","put","prev","check","land","versionOf","refused","takeTheirs","keepMine","cancel","remove","control","bad","described","shared","keep","numeric","actions","RecordForm","recordListConfigSchema","keys","CARDS_BELOW","isFacet","useRecordPages","sort","search","setResult","loadingMore","setLoadingMore","facets","setFacets","filter","absorb","changed","fetchPage","refresh","page","nextCursor","loadMore","Chip","Cell","moveFocus","RecordListBody","listId","setSort","setChosen","setSearch","loading","stale","cards","primary","chipField","secondary","searchable","activatable","pad","toggleSort","rowProps","keyOf","sortControl","active","RecordList","reportConfigSchema","INDEX_LENGTH","isoDay","startOf","period","daysOf","days","month","length","periodLabel","dayOf","sectionTitles","useReport","openId","setLoaded","setIndex","fetchReport","report","Chrome","stepButton","ReportBody","anchor","setAnchor","setOpenId","reportDate","latest","move","author","sections","dateLine","past","Report","menuItemSchema","shellConfigSchema","Placeholder","useCurrentUser","CHAT_WIDTH_KEY","CHAT_OPEN_KEY","THEME_KEY","CHAT_MIN","CANVAS_MIN","useTheme","theme","setTheme","kept","paths","Icon","IconButton","on","onClick","expanded","Setting","icon","ShellFrame","chat","account","settings","onSelect","isMobile","toggleTheme","setRoute","gear","setGear","gearRef","setChatOpen","wasMobile","setWasMobile","onKey","onPointer","chatWidth","setChatWidth","clampChat","w","startResize","handle","widthAt","e","up","resetResize","activeId","chatPane","canvasPane","ShellComponent","Shell","TONES","timelineKindSchema","timelineConfigSchema","kind","TOP_SLACK","MARKS","dayKeyOf","type","dayLabel","today","yesterday","timeLabel","minutes","hours","filesOf","useEntries","actors","setActors","wantsActors","loadOlder","useArrivals","atTop","arrived","setArrived","firstId","known","clear","previousFirst","cut","Attachments","setFailed","ChipRow","TimelineBody","isNew","jumpToTop","kindOf","composerBusy","setComposerBusy","composerText","setComposerText","composerKind","setComposerKind","composerError","setComposerError","addEntry","actor","Timeline","MIME","uploadConfigSchema","FileRejectedError","fileName","reason","MB","megabytes","accepts","accept","sub","wantGroup","wantSub","checkFile","position","iconFor","isImage","useQueue","onUploaded","held","queue","setQueue","nextKey","dismiss","add","picked","refusal","advance","fraction","useGallery","refs","setRefs","urls","setUrls","thumbnail","ProgressBar","percent","Queue","onDismiss","DropZone","onFiles","over","setOver","chooser","camera","take","Caption","setText","DeleteButton","onDeleted","Lightbox","onClose","Gallery","openFile","grid","asPicture","tile","UploadPanel","countHeld","noop","PickerButton","onPicked","setPicked","UploadComponent","Picker","Upload"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/abi/error-card.tsx","../src/abi/define-component.tsx","../src/abi/fields.ts","../src/adapters/records.ts","../src/adapters/files.ts","../src/adapters/brain.ts","../src/adapters/fake/emitter.ts","../src/adapters/fake/records.ts","../src/adapters/fake/files.ts","../src/adapters/fake/identity.ts","../src/adapters/fake/chat.ts","../src/adapters/fake/clock.ts","../src/adapters/fake/navigation.ts","../src/adapters/fake/brain.ts","../src/adapters/brain.docs.ts","../src/adapters/chat.docs.ts","../src/adapters/clock.docs.ts","../src/adapters/files.docs.ts","../src/adapters/identity.docs.ts","../src/adapters/navigation.docs.ts","../src/adapters/records.docs.ts","../src/components/Brain/Brain.docs.ts","../src/components/Auth/Auth.docs.ts","../src/components/Chat/Chat.docs.ts","../src/components/Editor/Editor.docs.ts","../src/components/RecordForm/RecordForm.docs.ts","../src/components/RecordList/RecordList.docs.ts","../src/components/Report/Report.docs.ts","../src/components/Shell/Shell.docs.ts","../src/components/Timeline/Timeline.docs.ts","../src/components/Upload/Upload.docs.ts","../src/docs.ts","../src/components/Auth/Auth.config.ts","../src/components/Auth/Auth.tsx","../src/lib/markdown.tsx","../src/lib/use-container-width.ts","../src/components/Brain/Brain.config.ts","../src/components/Brain/Brain.tsx","../src/components/Chat/Chat.config.ts","../src/components/Chat/Chat.tsx","../src/lib/diff3.ts","../src/lib/locale.ts","../src/components/Editor/Editor.config.ts","../src/components/Editor/source.tsx","../src/components/Editor/Editor.tsx","../src/lib/format.ts","../src/components/RecordForm/RecordForm.config.ts","../src/components/RecordForm/values.ts","../src/components/RecordForm/RecordForm.tsx","../src/components/RecordList/RecordList.config.ts","../src/components/RecordList/RecordList.tsx","../src/components/Report/Report.config.ts","../src/components/Report/Report.tsx","../src/components/Shell/Shell.config.ts","../src/components/Shell/Shell.tsx","../src/components/Timeline/Timeline.config.ts","../src/components/Timeline/Timeline.tsx","../src/components/Upload/Upload.config.ts","../src/components/Upload/Upload.tsx"],"sourcesContent":["import type { ZodError } from 'zod'\n\nexport interface ConfigIssue {\n /** Dotted path of the offending field, or `(root)` for the object itself. */\n field: string\n /** The rule that rejected it, in Zod's own vocabulary. */\n rule: string\n message: string\n}\n\nexport function issuesFromZodError(error: ZodError): ConfigIssue[] {\n return error.issues.map((issue) => ({\n field: issue.path.length > 0 ? issue.path.join('.') : '(root)',\n rule: issue.code,\n message: issue.message,\n }))\n}\n\n/**\n * Rendered in place of the component whenever its config fails validation — in dev and in prod\n * alike. A silent fallback would hide the mistake from the agent that made it.\n */\nexport function ConfigErrorCard({\n component,\n issues,\n}: {\n component: string\n issues: ConfigIssue[]\n}) {\n return (\n <div\n role=\"alert\"\n data-golem-error=\"config\"\n className=\"golem-error-card m-2 rounded-lg border-2 border-red-500 bg-red-50 p-4 font-mono text-sm text-red-900\"\n >\n <p className=\"mb-2 font-bold\">\n golem-ui: {component} received an invalid <code>config</code>\n </p>\n <ul className=\"space-y-1\">\n {issues.map((issue, i) => (\n <li key={i}>\n <span className=\"font-bold underline\">{issue.field}</span>\n {' — '}\n {issue.message} <span className=\"opacity-70\">[{issue.rule}]</span>\n </li>\n ))}\n </ul>\n <p className=\"mt-2 opacity-70\">\n Fix the config object. The schema is on the {component} docs page.\n </p>\n </div>\n )\n}\n","import { useMemo, type FunctionComponent } from 'react'\nimport type { z, ZodType } from 'zod'\nimport { ConfigErrorCard, issuesFromZodError } from './error-card'\n\n/**\n * The outer shape of every golem-ui component, no exceptions.\n *\n * `Slots` carries the ReactNode holes a component exposes, plus the per-use props that belong to\n * the call site rather than to the config — `Auth.Guard`'s `roles` is one. Anything the agent would\n * write once for the whole app belongs in `config`, and anything that reaches the world in\n * `adapters`.\n */\nexport type GolemProps<ConfigInput, Adapters, Slots = object> = {\n config: ConfigInput\n adapters: Adapters\n} & Slots\n\nexport interface ComponentSpec<Schema extends ZodType, Adapters, Slots = object> {\n /** Shown in the error card, so it must be the name the agent writes in JSX. */\n name: string\n schema: Schema\n render: FunctionComponent<GolemProps<z.output<Schema>, Adapters, Slots>>\n}\n\n/**\n * Wraps a renderer so the component validates its config before it renders anything, and shows a\n * loud error card naming the field and the rule when validation fails.\n */\nexport function defineComponent<Schema extends ZodType, Adapters, Slots = object>(\n spec: ComponentSpec<Schema, Adapters, Slots>,\n): FunctionComponent<GolemProps<z.input<Schema>, Adapters, Slots>> & { schema: Schema } {\n const Render = spec.render\n\n const Component = (props: GolemProps<z.input<Schema>, Adapters, Slots>) => {\n const { config, ...rest } = props\n const parsed = useMemo(() => spec.schema.safeParse(config), [config])\n\n if (!parsed.success) {\n return <ConfigErrorCard component={spec.name} issues={issuesFromZodError(parsed.error)} />\n }\n\n return (\n <Render\n {...(rest as unknown as GolemProps<z.output<Schema>, Adapters, Slots>)}\n config={parsed.data}\n />\n )\n }\n\n Component.displayName = spec.name\n Component.schema = spec.schema\n return Component\n}\n","import { z } from 'zod'\n\n/**\n * The vocabulary an agent picks a field's `type` from, shared by every component configured with a\n * list of fields. The type decides how a value is rendered, what `format` means, and — on a form —\n * which control the reader gets. It never changes what the adapter returns or what is written back.\n */\nexport const FIELD_TYPES = [\n 'text',\n 'number',\n 'date',\n 'datetime',\n 'enum',\n 'boolean',\n 'money',\n 'user',\n] as const\n\nexport type FieldType = (typeof FIELD_TYPES)[number]\n\n/** What every field carries, whatever component is reading it. */\nconst common = {\n key: z\n .string()\n .min(1)\n .describe('The property to read off each record. Must match what the adapter returns.'),\n label: z\n .string()\n .min(1)\n .describe('What names the field on screen: a column heading, or the label above a control.'),\n type: z\n .enum(FIELD_TYPES, {\n error: (issue) =>\n `Unknown field type ${JSON.stringify(issue.input)}. Pick one of: ${FIELD_TYPES.join(', ')}.`,\n })\n .describe(\n 'How the value is rendered and edited. `text` verbatim; `number` grouped; `date` and `datetime` as UTC calendar dates; `enum` as a coloured chip or a select; `boolean` as Yes/No or a checkbox; `money` as a currency amount; `user` as a name with initials.',\n ),\n format: z\n .string()\n .optional()\n .describe(\n 'A hint read only by some types: an ISO currency code for `money` (default USD), an `Intl` dateStyle — short, medium, long, full — for `date` and `datetime`, and a digit count for `number`. Ignored by every other type.',\n ),\n}\n\n/** A field as a list shows it: read-only, one column wide. */\nexport const listFieldSchema = z\n .object({\n ...common,\n primary: z\n .boolean()\n .default(false)\n .describe(\n 'Marks the field that titles a row: the first column of the table and the heading of a card. At most one field may set it; with none, the first field is the title.',\n ),\n })\n .strict()\n\nexport const fieldOptionSchema = z\n .object({\n id: z.string().min(1).describe('The value written to the record when this choice is picked.'),\n label: z.string().min(1).describe('What the reader sees in the control. Never written back.'),\n })\n .strict()\n\n/** A field as a form edits it: everything above, plus what makes a control out of it. */\nexport const formFieldSchema = z\n .object({\n ...common,\n required: z\n .boolean()\n .default(false)\n .describe('Blocks submit while the value is empty, and marks the label with an asterisk.'),\n readOnly: z\n .boolean()\n .default(false)\n .describe(\n 'Shows the value formatted, with no control and no place in the patch. Use it for what the server owns — an id, a created date, a computed total.',\n ),\n help: z\n .string()\n .optional()\n .describe('One line under the control, always visible. Say what a good value looks like.'),\n placeholder: z\n .string()\n .optional()\n .describe('Ghost text inside an empty control. A sample value, never a second label.'),\n options: z\n .array(fieldOptionSchema)\n .min(1)\n .optional()\n .describe(\n 'The choices for an `enum` field, as `{ id, label }`. The reader picks the label and the record gets the id. Required on `enum`, rejected on every other type.',\n ),\n min: z\n .union([z.number(), z.string()])\n .optional()\n .describe(\n 'The lowest accepted value: a number for `number` and `money`, an ISO date or datetime string for `date` and `datetime`. Ignored by every other type.',\n ),\n max: z\n .union([z.number(), z.string()])\n .optional()\n .describe('The highest accepted value, in the same shape as `min`.'),\n multiline: z\n .boolean()\n .default(false)\n .describe(\n 'Draws a `text` field as a textarea, spanning both columns of a two-column layout. Ignored by every other type.',\n ),\n users: z\n .enum(['members', 'free'])\n .default('members')\n .describe(\n 'Where a `user` field gets its choices: `members` is a select over `Identity.listMembers()`, `free` is a text box for a name typed by hand. Ignored by every other type.',\n ),\n })\n .strict()\n\nexport type ListField = z.output<typeof listFieldSchema>\nexport type FormField = z.output<typeof formFieldSchema>\nexport type FieldOption = z.output<typeof fieldOptionSchema>\n\n/** Whatever a records adapter hands back. `id` is the row key when there is one. */\nexport type RecordRow = Record<string, unknown>\n\n/** The part of a field that formatting reads. Both field shapes satisfy it. */\nexport interface RenderedField {\n type: FieldType\n format?: string\n}\n","import type { Unsubscribe } from './common'\n\n/**\n * One value a filter matches, or a list of them meaning \"any of these\". Arrays are how a scoped\n * list says `status is waiting or in progress` without a query language.\n */\nexport type FilterValue = string | number | boolean | null | Array<string | number | boolean | null>\n\nexport interface RecordQuery {\n /** Field-by-field equality. A row matches when every entry matches. */\n filter?: Record<string, FilterValue>\n sort?: { field: string; direction: 'asc' | 'desc' }\n /** Substring match, case-insensitive, against the listed fields only. */\n search?: { text: string; fields: string[] }\n /** `nextCursor` from the previous page, or absent for the first one. */\n cursor?: string | null\n limit?: number\n}\n\nexport interface RecordPage<T> {\n rows: T[]\n /** Pass back as `cursor` for the next page. `null` means this was the last one. */\n nextCursor: string | null\n}\n\n/** One field the write was refused over, and the sentence the reader sees under that control. */\nexport interface FieldRefusal {\n /** The field `key`, as the component's config spells it. */\n field: string\n /** Written for a person: \"A quote over $500 needs the owner's sign-off.\" */\n message: string\n}\n\n/**\n * A write the adapter refused for reasons only it knows — a uniqueness clash, a business rule, a\n * server-side validator. Naming the fields is what lets a form put the sentence under the right\n * control instead of at the top of the page.\n */\nexport class RecordRefusedError extends Error {\n readonly fields: FieldRefusal[]\n\n constructor(message: string, fields: FieldRefusal[] = []) {\n super(message)\n this.name = 'RecordRefusedError'\n this.fields = fields\n }\n}\n\n/**\n * Reads the refusal out of anything thrown, by shape rather than by class: an adapter that crosses\n * a bundle boundary, or one that never imported this module, still gets its fields honoured.\n * Returns `null` for a rejection that names no field, which a form shows as one line instead.\n */\nexport function refusedFields(error: unknown): FieldRefusal[] | null {\n const fields = (error as { fields?: unknown } | null)?.fields\n if (!Array.isArray(fields) || fields.length === 0) return null\n const named = fields.filter(\n (one): one is FieldRefusal =>\n typeof (one as FieldRefusal)?.field === 'string' &&\n typeof (one as FieldRefusal)?.message === 'string',\n )\n return named.length > 0 ? named : null\n}\n\n/**\n * What a writer knew about the row when it started writing. A store that is handed this refuses the\n * write rather than letting the last writer win, which is what makes two writers on one document —\n * a person and an agent — safe.\n */\nexport interface UpdateOptions {\n /** The value the writer read out of `versionField` before it began editing. */\n expectedVersion: number\n /** The field the version lives on. */\n versionField?: string\n}\n\n/** The default `versionField`, so a caller with a plainly named field passes only the number. */\nexport const VERSION_FIELD = 'version'\n\n/**\n * An `update` the store refused because the row moved on since the writer read it. It carries the\n * row as it stands now, so the caller can merge against it without a second round trip.\n */\nexport class VersionConflictError extends Error {\n readonly current: Record<string, unknown> | null\n\n constructor(message: string, current: Record<string, unknown> | null = null) {\n super(message)\n this.name = 'VersionConflictError'\n this.current = current\n }\n}\n\n/**\n * Reads the current row out of anything thrown, by shape rather than by class — the same reason\n * `refusedFields` does. Returns `null` for a rejection that is not a version conflict, which the\n * caller shows as an error instead of merging.\n */\nexport function conflictingRecord(error: unknown): Record<string, unknown> | null {\n const thrown = error as { name?: unknown; current?: unknown } | null\n if (thrown?.name !== 'VersionConflictError') return null\n return typeof thrown.current === 'object' && thrown.current !== null\n ? (thrown.current as Record<string, unknown>)\n : null\n}\n\n/**\n * Records is a collection of rows and the four writes that change one.\n *\n * Every method that can be refused rejects with an `Error` whose `message` is shown to the reader\n * as it is written. Throw a `RecordRefusedError` when the refusal belongs to particular fields, and\n * a `VersionConflictError` when an `update` carrying `expectedVersion` arrived too late.\n */\nexport interface RecordsAdapter {\n list<T>(collection: string, query?: RecordQuery): Promise<RecordPage<T>>\n /** One row by id, or `null` when the collection has no such row. */\n get<T>(collection: string, id: string): Promise<T | null>\n /** Returns the stored row, including whatever id the store minted for it. */\n create<T>(collection: string, data: Record<string, unknown>): Promise<T>\n /**\n * Merges the patch into the stored row and returns the whole row back. With `options`, the write\n * is refused with a `VersionConflictError` when the row has moved on since the writer read it.\n */\n update<T>(\n collection: string,\n id: string,\n patch: Record<string, unknown>,\n options?: UpdateOptions,\n ): Promise<T>\n remove(collection: string, id: string): Promise<void>\n /** Called with no argument whenever the collection changed; the caller re-lists. */\n subscribe(collection: string, listener: () => void): Unsubscribe\n}\n","import type { Unsubscribe } from './common'\n\n/**\n * A stored file, as everything downstream refers to it. Plain JSON: an agent can put one in a\n * record, a chat message or a config and it survives the round trip. It carries no bytes — the\n * bytes are behind `url`.\n */\nexport interface FileRef {\n id: string\n /** The name it was uploaded under, extension included. */\n name: string\n /** The MIME type it is stored as, e.g. `image/jpeg`. A gallery reads it to know it has a photo. */\n contentType: string\n /** Bytes. */\n size: number\n /** The folder it lives in: the same string `upload` and `list` are given. */\n folder: string\n /** The instant the store accepted it, as a full ISO datetime. */\n uploadedAt: string\n /** One line the reader wrote under it. Absent until something captions it. */\n caption?: string\n}\n\nexport interface UploadOptions {\n /** Where the file lands, and where `list` and `subscribe` will find it again. */\n folder: string\n /**\n * Called as the bytes go up, with the fraction done between 0 and 1. A store that cannot report\n * progress calls it once with 1 rather than never — the caller draws a bar either way.\n */\n onProgress?: (fraction: number) => void\n}\n\n/**\n * A stored file, or just its id. A caller that kept only the id — an attachment on a record, say —\n * can still open and delete the file without having the whole ref to hand.\n */\nexport type FileHandle = FileRef | string\n\nexport const fileId = (ref: FileHandle): string => (typeof ref === 'string' ? ref : ref.id)\n\n/**\n * Files is a folder of blobs and the four things you do to one: put it there, open it, caption it,\n * take it away. Every method that can be refused rejects with an `Error` whose `message` is shown\n * to the reader as it is written.\n */\nexport interface FilesAdapter {\n upload(file: File, options: UploadOptions): Promise<FileRef>\n /**\n * A URL to open. `thumbnail` asks for a small one; a store that keeps no thumbnail returns the\n * full URL, so a caller never has to ask whether it has one.\n */\n url(ref: FileHandle, options?: { thumbnail?: boolean }): Promise<string>\n remove(ref: FileHandle): Promise<void>\n /** Everything in the folder, newest first. */\n list(folder: string): Promise<FileRef[]>\n /** Writes the caption on a stored file and returns the whole ref back. */\n caption(ref: FileHandle, caption: string): Promise<FileRef>\n /** Called with no argument whenever the folder changed; the caller re-lists. */\n subscribe(folder: string, listener: () => void): Unsubscribe\n}\n","import type { Unsubscribe } from './common'\n\n/**\n * A brain is an Open Knowledge Format bundle (v0.2): a directory tree of markdown concept documents\n * with YAML front matter, an optional `index.md` in any directory, and an optional `log.md` at the\n * root. Paths are relative to the bundle root, `/`-separated, no leading slash.\n *\n * A **source location** is `path#L<start>-L<end>`, 1-based and inclusive: what the agent cites,\n * and what `Brain`'s `openLocation` opens.\n */\nexport interface BrainEntry {\n path: string\n kind: 'file' | 'dir'\n}\n\nexport interface BrainHit {\n path: string\n /** 1-based line the match is on. */\n line: number\n excerpt: string\n}\n\nexport interface BrainAdapter {\n /** That directory's `index.md`, synthesized from the front matter of its concepts when absent. */\n index(dir?: string): Promise<string>\n /** The direct children of a directory, files and directories alike. Root when `dir` is omitted. */\n list(dir?: string): Promise<BrainEntry[]>\n read(path: string): Promise<string>\n search(query: string): Promise<BrainHit[]>\n /** Fires on any change to any file in the bundle. */\n subscribe(listener: () => void): Unsubscribe\n /**\n * Called with the location the reader just opened on its own — a tree tap, a link, a search hit,\n * back. Optional: an app puts it in the URL so a reload lands on the same document.\n */\n open?(location: string): void\n /** Replaces a file's text. Optional: a brain the app keeps read-only omits it. */\n write?(path: string, text: string): Promise<void>\n}\n\n/** `path#L3-L5` → `{ path, start: 3, end: 5 }`. A location with no range opens the file at the top. */\nexport function parseLocation(location: string): { path: string; start?: number; end?: number } {\n const match = /^([^#]+)(?:#L(\\d+)(?:-L(\\d+))?)?$/.exec(location)\n if (!match) return { path: location }\n const start = match[2] ? Number(match[2]) : undefined\n return { path: match[1]!, start, end: match[3] ? Number(match[3]) : start }\n}\n","import type { Unsubscribe } from '../common'\n\n/** Shared listener bookkeeping for the fakes; not part of the public adapter surface. */\nexport function createEmitter<T>() {\n const listeners = new Set<(value: T) => void>()\n return {\n subscribe(listener: (value: T) => void): Unsubscribe {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n emit(value: T) {\n for (const listener of listeners) listener(value)\n },\n }\n}\n","import {\n RecordRefusedError,\n VersionConflictError,\n VERSION_FIELD,\n type FilterValue,\n type RecordQuery,\n type RecordsAdapter,\n type UpdateOptions,\n} from '../records'\nimport { createEmitter } from './emitter'\n\nexport type FakeRow = Record<string, unknown> & { id: string }\n\n/**\n * The fake's store, on top of the adapter interface. `insert` is not part of `RecordsAdapter`: it\n * is the synchronous way a story or a test puts a row in without going through `create`, so a\n * seeded collection can grow while a refusal is switched on.\n */\nexport interface FakeRecords extends RecordsAdapter {\n insert(collection: string, row: Record<string, unknown>): FakeRow\n}\n\nexport interface FakeRecordsOptions {\n /**\n * Makes every `create` and `update` reject, naming this field. It is how a story shows what an\n * adapter's refusal looks like landing under one control.\n */\n refuse?: { field: string; message: string }\n}\n\nfunction matches(value: unknown, wanted: FilterValue): boolean {\n return Array.isArray(wanted) ? wanted.some((one) => one === value) : wanted === value\n}\n\n/** Numbers compare as numbers, everything else as its string form. Dates are ISO, so that holds. */\nfunction compare(a: unknown, b: unknown): number {\n if (typeof a === 'number' && typeof b === 'number') return a - b\n const left = String(a ?? '')\n const right = String(b ?? '')\n return left < right ? -1 : left > right ? 1 : 0\n}\n\nfunction hit(row: FakeRow, text: string, fields: string[]): boolean {\n const needle = text.trim().toLowerCase()\n if (needle === '') return true\n return fields.some((field) =>\n String(row[field] ?? '')\n .toLowerCase()\n .includes(needle),\n )\n}\n\n/**\n * An in-memory `Records` adapter, seeded per collection. Filtering, sorting, searching and paging\n * all happen over the seeded rows, so a story runs the same code path a server would.\n *\n * The cursor is an offset into the matching rows. That is what makes `nextCursor` honest and it is\n * also its limit: a row inserted while the reader is on page three shifts the pages under them,\n * which is why a live update re-lists from the start rather than paging on.\n */\nexport function fakeRecords(\n seed: Record<string, Record<string, unknown>[]> = {},\n options: FakeRecordsOptions = {},\n): FakeRecords {\n const store = new Map<string, FakeRow[]>(\n Object.entries(seed).map(([name, rows]) => [name, rows.map((row) => ({ ...row }) as FakeRow)]),\n )\n const emitters = new Map<string, ReturnType<typeof createEmitter<void>>>()\n let nextId = 1\n\n const rows = (collection: string) => {\n if (!store.has(collection)) store.set(collection, [])\n return store.get(collection)!\n }\n const emitterFor = (collection: string) => {\n if (!emitters.has(collection)) emitters.set(collection, createEmitter<void>())\n return emitters.get(collection)!\n }\n const indexOf = (collection: string, id: string) =>\n rows(collection).findIndex((row) => row.id === id)\n\n const refuseIfAsked = () => {\n const { refuse } = options\n if (refuse) {\n throw new RecordRefusedError(`The store refused this ${refuse.field}.`, [\n { field: refuse.field, message: refuse.message },\n ])\n }\n }\n\n const insert = (collection: string, row: Record<string, unknown>) => {\n const next = { id: `fake-${nextId++}`, ...row } as FakeRow\n rows(collection).unshift(next)\n emitterFor(collection).emit()\n return next\n }\n\n return {\n async list<T>(collection: string, query: RecordQuery = {}) {\n let result = [...rows(collection)]\n\n if (query.filter) {\n const entries = Object.entries(query.filter)\n result = result.filter((row) => entries.every(([key, want]) => matches(row[key], want)))\n }\n if (query.search) {\n result = result.filter((row) => hit(row, query.search!.text, query.search!.fields))\n }\n if (query.sort) {\n const { field, direction } = query.sort\n const sign = direction === 'asc' ? 1 : -1\n result.sort((a, b) => sign * compare(a[field], b[field]))\n }\n\n const total = result.length\n const offset = Number(query.cursor ?? 0) || 0\n const limit = query.limit ?? total\n const end = offset + limit\n\n return {\n rows: result.slice(offset, end) as T[],\n nextCursor: end < total ? String(end) : null,\n }\n },\n\n async get<T>(collection: string, id: string) {\n const found = rows(collection).find((row) => row.id === id)\n return (found ? ({ ...found } as T) : null) as T | null\n },\n\n async create<T>(collection: string, data: Record<string, unknown>) {\n refuseIfAsked()\n return { ...insert(collection, data) } as T\n },\n\n async update<T>(\n collection: string,\n id: string,\n patch: Record<string, unknown>,\n options?: UpdateOptions,\n ) {\n refuseIfAsked()\n const at = indexOf(collection, id)\n if (at === -1) throw new Error(`There is no ${collection} record with the id ${id}.`)\n\n const current = rows(collection)[at]!\n if (options) {\n const field = options.versionField ?? VERSION_FIELD\n if (current[field] !== options.expectedVersion) {\n throw new VersionConflictError(\n `This ${collection} record is at ${field} ${String(current[field])}, not ${options.expectedVersion}. Somebody else wrote to it first.`,\n { ...current },\n )\n }\n }\n\n const next = { ...current, ...patch, id }\n rows(collection)[at] = next\n emitterFor(collection).emit()\n return { ...next } as T\n },\n\n async remove(collection: string, id: string) {\n const at = indexOf(collection, id)\n if (at === -1) throw new Error(`There is no ${collection} record with the id ${id}.`)\n rows(collection).splice(at, 1)\n emitterFor(collection).emit()\n },\n\n insert,\n\n subscribe(collection: string, listener: () => void) {\n return emitterFor(collection).subscribe(listener)\n },\n }\n}\n","import {\n fileId,\n type FileHandle,\n type FileRef,\n type FilesAdapter,\n type UploadOptions,\n} from '../files'\nimport { createEmitter } from './emitter'\n\n/**\n * A file already in the store when the fake is built. `content` is what `url` hands back — a data\n * URL for a seeded image — because the fake has no bytes of its own for a file nobody uploaded.\n */\nexport interface FakeFileSeed {\n id?: string\n folder: string\n name: string\n contentType: string\n /** Bytes, for the tile. Defaults to the length of `content`. */\n size?: number\n content?: string\n caption?: string\n /** A full ISO datetime. Defaults to the fake's `now`. */\n uploadedAt?: string\n}\n\nexport interface FakeFilesOptions {\n seed?: FakeFileSeed[]\n /**\n * `upload` rejects any file with exactly this name, so the error path has somewhere to be seen.\n * Everything else in the same batch still goes up.\n */\n failOn?: string\n /** How long a fake upload takes end to end, in milliseconds. */\n uploadMs?: number\n /** What stamps `uploadedAt`, so a test is not reading the wall clock. */\n now?: () => Date\n}\n\n/** How many progress events a fake upload reports on its way up. */\nconst STEPS = 10\n\n/**\n * A small stand-in picture, drawn rather than photographed: the fakes ship no image files, and a\n * gallery with nothing in it teaches nothing. It is an SVG data URL, so it costs a few hundred\n * bytes and needs no canvas, no network and no build step.\n */\nexport function placeholderImage(label: string, hue = 205): string {\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"480\" height=\"360\" viewBox=\"0 0 480 360\">\n<rect width=\"480\" height=\"360\" fill=\"hsl(${hue} 40% 88%)\"/>\n<circle cx=\"150\" cy=\"140\" r=\"66\" fill=\"hsl(${hue} 45% 74%)\"/>\n<circle cx=\"330\" cy=\"140\" r=\"66\" fill=\"hsl(${hue} 45% 74%)\"/>\n<path d=\"M150 140 L215 78 L330 140\" stroke=\"hsl(${hue} 50% 46%)\" stroke-width=\"10\" fill=\"none\"/>\n<text x=\"240\" y=\"290\" font-family=\"system-ui,sans-serif\" font-size=\"26\" text-anchor=\"middle\" fill=\"hsl(${hue} 45% 32%)\">${label}</text>\n</svg>`\n return `data:image/svg+xml,${encodeURIComponent(svg)}`\n}\n\n/**\n * An object URL for the blob, or a stand-in string when the platform has no `createObjectURL` —\n * jsdom does not, and a test that only checks the gallery listed a file should not fail for it.\n */\nfunction objectUrlFor(file: File, id: string): string {\n try {\n return URL.createObjectURL(file)\n } catch {\n return `fake-file:${id}`\n }\n}\n\n/**\n * Loads the image, or gives up. Anything that does not load inside the grace period — a platform\n * that fetches no resources, a blob the decoder refuses — resolves `null`, and the caller keeps\n * the full-size URL rather than waiting on a thumbnail that is never coming.\n */\nfunction loadImage(url: string, graceMs: number): Promise<HTMLImageElement | null> {\n return new Promise((resolve) => {\n const image = new Image()\n const timer = setTimeout(() => resolve(null), graceMs)\n const settle = (value: HTMLImageElement | null) => {\n clearTimeout(timer)\n resolve(value)\n }\n image.onload = () => settle(image)\n image.onerror = () => settle(null)\n image.src = url\n })\n}\n\n/** The longest edge of a generated thumbnail, in pixels. */\nconst THUMB_EDGE = 160\n\n/**\n * A thumbnail drawn from the uploaded image through a canvas, or `null` when this platform cannot\n * draw one. A real store would make it server-side; the fake makes it in the page so the grid is\n * loading small pictures rather than full ones.\n */\nasync function thumbnailOf(url: string, contentType: string): Promise<string | null> {\n if (!contentType.startsWith('image/')) return null\n const image = await loadImage(url, 250)\n if (!image) return null\n try {\n const scale = Math.min(1, THUMB_EDGE / Math.max(image.width, image.height))\n const canvas = document.createElement('canvas')\n canvas.width = Math.max(1, Math.round(image.width * scale))\n canvas.height = Math.max(1, Math.round(image.height * scale))\n const context = canvas.getContext('2d')\n if (!context) return null\n context.drawImage(image, 0, 0, canvas.width, canvas.height)\n return canvas.toDataURL('image/png')\n } catch {\n return null\n }\n}\n\ninterface Stored {\n ref: FileRef\n full: string\n thumbnail: string | null\n}\n\n/**\n * Files in memory: blobs behind object URLs, an upload that takes about 600 ms and says how far it\n * has got, a canvas thumbnail for anything that is a picture, and one name it always refuses so the\n * failure has a story of its own.\n */\nexport function fakeFiles(options: FakeFilesOptions = {}): FilesAdapter {\n const { failOn, uploadMs = 600, now = () => new Date() } = options\n const store = new Map<string, Stored>()\n const folders = new Map<string, ReturnType<typeof createEmitter<void>>>()\n let nextId = 1\n\n const emitterFor = (folder: string) => {\n let emitter = folders.get(folder)\n if (!emitter) {\n emitter = createEmitter<void>()\n folders.set(folder, emitter)\n }\n return emitter\n }\n\n for (const seed of options.seed ?? []) {\n const id = seed.id ?? `fake-file-${nextId++}`\n const content = seed.content ?? `fake-file:${id}`\n store.set(id, {\n ref: {\n id,\n name: seed.name,\n contentType: seed.contentType,\n size: seed.size ?? content.length,\n folder: seed.folder,\n uploadedAt: seed.uploadedAt ?? now().toISOString(),\n ...(seed.caption === undefined ? {} : { caption: seed.caption }),\n },\n full: content,\n // A seeded picture is already small, so it is its own thumbnail.\n thumbnail: seed.contentType.startsWith('image/') ? content : null,\n })\n }\n\n const found = (ref: FileHandle): Stored => {\n const stored = store.get(fileId(ref))\n if (!stored) throw new Error(`No file with id ${fileId(ref)} is in the store.`)\n return stored\n }\n\n return {\n async upload(file: File, { folder, onProgress }: UploadOptions) {\n const id = `fake-file-${nextId++}`\n const full = objectUrlFor(file, id)\n\n // The bar moves before the answer arrives, which is the whole point of reporting progress.\n for (let step = 1; step <= STEPS; step++) {\n await new Promise((resolve) => setTimeout(resolve, uploadMs / STEPS))\n onProgress?.(step / STEPS)\n }\n\n if (failOn !== undefined && file.name === failOn) {\n throw new Error(`The store rejected “${file.name}”. Nothing was saved.`)\n }\n\n const ref: FileRef = {\n id,\n name: file.name,\n contentType: file.type,\n size: file.size,\n folder,\n uploadedAt: now().toISOString(),\n }\n store.set(id, { ref, full, thumbnail: await thumbnailOf(full, file.type) })\n emitterFor(folder).emit()\n return ref\n },\n\n async url(ref: FileHandle, urlOptions?: { thumbnail?: boolean }) {\n const stored = found(ref)\n return urlOptions?.thumbnail ? (stored.thumbnail ?? stored.full) : stored.full\n },\n\n async remove(ref: FileHandle) {\n const stored = found(ref)\n store.delete(stored.ref.id)\n emitterFor(stored.ref.folder).emit()\n },\n\n async list(folder: string) {\n return [...store.values()]\n .map((stored) => stored.ref)\n .filter((ref) => ref.folder === folder)\n .sort((a, b) => b.uploadedAt.localeCompare(a.uploadedAt) || b.id.localeCompare(a.id))\n },\n\n async caption(ref: FileHandle, caption: string) {\n const stored = found(ref)\n stored.ref = { ...stored.ref, caption }\n emitterFor(stored.ref.folder).emit()\n return stored.ref\n },\n\n subscribe(folder: string, listener: () => void) {\n return emitterFor(folder).subscribe(listener)\n },\n }\n}\n","import type { IdentityAdapter, SignUpInput, User } from '../identity'\nimport { createEmitter } from './emitter'\n\nexport const fakeUser: User = {\n id: 'u-1',\n name: 'Robin Vale',\n email: 'robin@example.com',\n roles: ['member'],\n}\n\nexport interface FakeIdentityOptions {\n /** Who is signed in on mount. Omit and it is the first member; `null` starts the app signed out. */\n user?: User | null\n /** The member table the list, invite and remove all work on. Defaults to `[fakeUser]`. */\n members?: User[]\n /** The one password every member accepts, so a demo can say it out loud. */\n password?: string\n /** The one code `requestCode` always \"sends\". */\n code?: string\n /** Role given to someone who signs up without an invite. */\n defaultRole?: string\n /** What `invite()` prefixes the token with. The token is the last thing in the URL. */\n inviteBase?: string\n /** Invites that already exist, token to role, so an example can open on a link nobody minted. */\n invites?: Record<string, string>\n /** What `resetPassword()` prefixes the token with. The token is the last thing in the URL. */\n resetBase?: string\n /** Reset links that already exist, token to member id, so an example can open on one. */\n resets?: Record<string, string>\n}\n\n/**\n * The in-memory Identity adapter: a member table, a signed-in user, and invite tokens that really\n * round-trip — `invite()` hands back a URL whose token `signUp()` accepts exactly once.\n */\nexport function fakeIdentity(options: FakeIdentityOptions = {}): IdentityAdapter {\n const password = options.password ?? 'golem'\n const code = options.code ?? '123456'\n const defaultRole = options.defaultRole ?? 'member'\n const inviteBase = options.inviteBase ?? 'https://app.example/#/join?invite='\n const resetBase = options.resetBase ?? 'https://app.example/#/join?reset='\n\n const members = (options.members ?? [fakeUser]).map((member) => ({ ...member }))\n let user = options.user === undefined ? (members[0] ?? null) : options.user\n const invites = new Map<string, string>(Object.entries(options.invites ?? {}))\n const resets = new Map<string, string>(Object.entries(options.resets ?? {}))\n const emitter = createEmitter<User | null>()\n let nextId = 1\n\n const find = (email: string) =>\n members.find((member) => member.email?.toLowerCase() === email.trim().toLowerCase())\n\n const enter = (member: User) => {\n user = { ...member }\n emitter.emit(user)\n return user\n }\n\n return {\n async currentUser() {\n return user && { ...user }\n },\n subscribe: emitter.subscribe,\n\n async signIn(email, given) {\n const member = find(email)\n if (!member) throw new Error('We do not know that email address.')\n if (given !== password) throw new Error('That password is not right.')\n return enter(member)\n },\n\n async signUp(input: SignUpInput) {\n let role = defaultRole\n if (input.invite !== undefined) {\n const invited = invites.get(input.invite)\n if (!invited) throw new Error('That invite link has already been used, or never existed.')\n invites.delete(input.invite)\n role = invited\n }\n if (find(input.email)) throw new Error('There is already an account with that email address.')\n const member: User = {\n id: `u-fake-${nextId++}`,\n name: input.name.trim(),\n email: input.email.trim(),\n roles: [role],\n }\n members.push(member)\n return enter(member)\n },\n\n async requestCode(email) {\n if (!find(email)) throw new Error('We do not know that email address.')\n },\n\n async verifyCode(email, given) {\n const member = find(email)\n if (!member) throw new Error('We do not know that email address.')\n if (given.trim() !== code) throw new Error('That code is not right.')\n return enter(member)\n },\n\n async signOut() {\n user = null\n emitter.emit(user)\n },\n\n async listMembers() {\n return members.map((member) => ({ ...member }))\n },\n\n async invite(role) {\n const token = `inv-${nextId++}`\n invites.set(token, role)\n return `${inviteBase}${token}`\n },\n\n async removeMember(userId) {\n const index = members.findIndex((member) => member.id === userId)\n if (index !== -1) members.splice(index, 1)\n if (user?.id === userId) {\n user = null\n emitter.emit(user)\n }\n },\n\n async resetPassword(userId) {\n if (!members.some((member) => member.id === userId))\n throw new Error(`fakeIdentity: no member ${userId}`)\n const token = `res-${nextId++}`\n resets.set(token, userId)\n return `${resetBase}${token}`\n },\n\n async setPassword(token) {\n if (!resets.delete(token)) throw new Error('That reset link has expired or was already used.')\n // One password for everyone is the fake's whole story, so the new one is not remembered.\n user = null\n emitter.emit(user)\n },\n\n async setRole(userId, role) {\n const member = members.find((entry) => entry.id === userId)\n if (!member) throw new Error(`fakeIdentity: no member ${userId}`)\n member.roles = [role]\n if (user?.id === userId) enter(member)\n },\n }\n}\n","import type { ChatAdapter, ChatAttachment, ChatCommand, ChatMessage } from '../chat'\nimport { createEmitter } from './emitter'\n\nexport interface FakeChatOptions {\n /** Answered in order, one per send, looping when they run out. Empty means the agent stays mute. */\n replies?: string[]\n /** Milliseconds between tokens. Stories want it slow enough to watch, tests want it near zero. */\n tokenDelayMs?: number\n /** Slash commands the composer completes. `/reset` clears the conversation; any other replies with the line it ran. */\n commands?: ChatCommand[]\n}\n\nconst DEFAULT_REPLIES = [\n 'Noted. Writing that down now.',\n 'Done — take a look at the canvas.',\n 'Understood. Anything else you want changed?',\n]\n\n/**\n * The in-memory Chat adapter. `send` echoes the message, then streams the next canned reply word by\n * word under one message id, so a component's streaming path is exercised without a backend.\n */\nexport function fakeChat(initial: ChatMessage[] = [], options: FakeChatOptions = {}): ChatAdapter {\n const replies = options.replies ?? DEFAULT_REPLIES\n const tokenDelayMs = options.tokenDelayMs ?? 60\n const messages = [...initial]\n const emitter = createEmitter<ChatMessage[]>()\n let nextId = initial.length + 1\n let sent = 0\n\n const emit = () => emitter.emit(messages.map((message) => ({ ...message })))\n\n const stream = (reply: string) => {\n const id = `fake-msg-${nextId++}`\n const at = new Date().toISOString()\n const words = reply.split(' ')\n // The thinking bubble: the message exists with no text yet, so the reader sees the agent take\n // the turn before the first word lands.\n messages.push({ id, role: 'agent', text: '', at, streaming: true })\n emit()\n\n let written = 0\n const tick = () => {\n written += 1\n const index = messages.findIndex((message) => message.id === id)\n if (index < 0) return\n messages[index] = {\n id,\n role: 'agent',\n at,\n text: words.slice(0, written).join(' '),\n streaming: written < words.length,\n }\n emit()\n if (written < words.length) setTimeout(tick, tokenDelayMs)\n }\n setTimeout(tick, tokenDelayMs)\n }\n\n return {\n async history() {\n return messages.map((message) => ({ ...message }))\n },\n async send(text: string, attachments?: ChatAttachment[]) {\n messages.push({\n id: `fake-msg-${nextId++}`,\n role: 'user',\n text,\n at: new Date().toISOString(),\n ...(attachments?.length ? { attachments } : {}),\n })\n emit()\n if (replies.length) stream(replies[sent++ % replies.length]!)\n },\n subscribe: emitter.subscribe,\n ...(options.commands\n ? {\n async commands() {\n return options.commands!\n },\n async runCommand(line: string) {\n const [name = '', ...rest] = line.trim().split(/\\s+/)\n const command = options.commands!.find((c) => c.name === name)\n if (!command) throw new Error(`Unknown command: ${name}`)\n const arg = rest.join(' ')\n if (command.args?.length && !command.args.some((a) => a.value === arg))\n throw new Error(\n `${name} needs one of: ${command.args.map((a) => a.value).join(', ')}`,\n )\n if (name === '/reset') {\n messages.length = 0\n emit()\n return 'Conversation cleared.'\n }\n return `Ran ${line.trim()}.`\n },\n }\n : {}),\n }\n}\n","import type { ClockAdapter } from '../clock'\n\n/** Frozen by default so time-based views render the same in every story and every test run. */\nexport function fakeClock(\n fixed: Date = new Date('2026-01-01T09:00:00Z'),\n timeZone = 'UTC',\n): ClockAdapter {\n return {\n now: () => new Date(fixed),\n timeZone: () => timeZone,\n }\n}\n","import type { NavigationAdapter, Route } from '../navigation'\nimport { createEmitter } from './emitter'\n\nexport function fakeNavigation(initial: Route = { path: '/', params: {} }): NavigationAdapter {\n let route = initial\n const emitter = createEmitter<Route>()\n\n return {\n current: () => route,\n go(path: string) {\n route = { path, params: {} }\n emitter.emit(route)\n },\n subscribe: emitter.subscribe,\n }\n}\n","import type { BrainAdapter, BrainEntry, BrainHit } from '../brain'\nimport { createEmitter } from './emitter'\n\n/** The `key: value` lines of a document's YAML front matter; enough for `type` and `description`. */\nfunction frontMatter(text: string): Record<string, string> {\n const match = /^---\\n([\\s\\S]*?)\\n---/.exec(text)\n const fields: Record<string, string> = {}\n for (const line of match?.[1]?.split('\\n') ?? []) {\n const pair = /^(\\w+):\\s*(.*)$/.exec(line)\n if (pair) fields[pair[1]!] = pair[2]!.replace(/^[\"']|[\"']$/g, '')\n }\n return fields\n}\n\nconst RESERVED = new Set(['index.md', 'log.md'])\nconst dirOf = (dir?: string) => (dir ? dir.replace(/\\/$/, '') + '/' : '')\n\n/**\n * An in-memory bundle: `files` maps a path to its markdown. Real enough for the reader and the\n * search; `write` replaces a file and fires `subscribe`.\n */\nexport function fakeBrain(files: Record<string, string>): BrainAdapter {\n const store = { ...files }\n const emitter = createEmitter<void>()\n\n const list = async (dir?: string): Promise<BrainEntry[]> => {\n const prefix = dirOf(dir)\n const seen = new Map<string, BrainEntry>()\n for (const path of Object.keys(store)) {\n if (!path.startsWith(prefix)) continue\n const rest = path.slice(prefix.length)\n const slash = rest.indexOf('/')\n const entry: BrainEntry =\n slash < 0 ? { path, kind: 'file' } : { path: prefix + rest.slice(0, slash), kind: 'dir' }\n seen.set(entry.path, entry)\n }\n return [...seen.values()].sort((a, b) =>\n a.kind === b.kind ? a.path.localeCompare(b.path) : a.kind === 'dir' ? -1 : 1,\n )\n }\n\n return {\n list,\n async index(dir) {\n const own = store[`${dirOf(dir)}index.md`]\n if (own !== undefined) return own\n // The spec's fallback: one bullet per concept, from its front matter.\n const entries = await list(dir)\n const lines = entries\n .filter((entry) => !(entry.kind === 'file' && RESERVED.has(entry.path.split('/').pop()!)))\n .map((entry) => {\n const name = entry.path.slice(dirOf(dir).length)\n const description =\n entry.kind === 'file' ? frontMatter(store[entry.path]!).description : ''\n return `* [${name}](${name}${entry.kind === 'dir' ? '/' : ''})${description ? ` - ${description}` : ''}`\n })\n return `# ${dir ?? 'Index'}\\n\\n${lines.join('\\n')}\\n`\n },\n async read(path) {\n const text = store[path]\n if (text === undefined) throw new Error(`No file at ${path}.`)\n return text\n },\n async search(query) {\n const needle = query.trim().toLowerCase()\n if (!needle) return []\n const hits: BrainHit[] = []\n for (const [path, text] of Object.entries(store)) {\n text.split('\\n').forEach((excerpt, index) => {\n if (excerpt.toLowerCase().includes(needle)) hits.push({ path, line: index + 1, excerpt })\n })\n }\n return hits\n },\n subscribe: emitter.subscribe,\n async write(path, text) {\n store[path] = text\n emitter.emit()\n },\n }\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const brainAdapterDocs: AdapterDocs = {\n name: 'Brain',\n slug: 'brain',\n storybookPath: 'adapters-brain',\n tagline:\n 'A folder of markdown the agent reads and cites: list it, read it, search it, hear when it changes.',\n\n purpose: `\\`Brain\\` is the app's unstructured knowledge as a folder of markdown, in the\n[Open Knowledge Format](https://github.com/GoogleCloudPlatform/open-knowledge-format/blob/main/SPEC.md)\n(v0.2): concept documents with YAML front matter (\\`type\\` required, \\`description\\` used in listings),\nan optional \\`index.md\\` in any directory, an optional \\`log.md\\` at the root. \\`index.md\\` and \\`log.md\\`\nare reserved names, never concepts. The root \\`index.md\\` is where an agent starts reading.\n\nThe adapter is the folder, reduced to what the reader needs: a listing, a file, a search, and a\nlistener. It knows nothing about markdown beyond front matter. Implement it over a directory on\ndisk, a git tree, or an object store.\n\n**A source location is \\`path#L<start>-L<end>\\`**, 1-based and inclusive, relative to the bundle root.\nIt is what the agent puts in a \\`ChatMessage\\`'s \\`sources\\` and what \\`Brain\\`'s \\`openLocation\\` opens.\n\\`parseLocation\\` splits one.`,\n\n methods: [\n {\n signature: 'index(dir?: string): Promise<string>',\n guarantees: `The markdown of that directory's \\`index.md\\`, the root's when \\`dir\\` is omitted. When the directory has\nnone, a synthesized one: a heading and one \\`* [name](path) - description\\` bullet per entry, the\ndescription read from each concept's front matter, as the spec allows.`,\n },\n {\n signature: \"list(dir?: string): Promise<Array<{ path: string; kind: 'file' | 'dir' }>>\",\n guarantees:\n 'The direct children of a directory, directories first, each `path` relative to the bundle root. The root when `dir` is omitted. An empty directory is an empty array.',\n },\n {\n signature: 'read(path: string): Promise<string>',\n guarantees:\n 'The whole file, front matter included, so line numbers in a location count from line 1 of the file.',\n throws: 'An `Error` whose `message` is shown in the reader when there is no file at `path`.',\n },\n {\n signature:\n 'search(query: string): Promise<Array<{ path: string; line: number; excerpt: string }>>',\n guarantees:\n 'Every line containing the query, case-insensitive, with its 1-based `line`. An empty query is an empty result.',\n },\n {\n signature: 'subscribe(listener: () => void): Unsubscribe',\n guarantees:\n 'Calls the listener after any file in the bundle changes, with no payload: the component re-reads what it has open. Returns the function that detaches it.',\n },\n {\n signature: 'open?(location: string): void',\n guarantees:\n 'Optional. `Brain` calls it with `path#L<start>-L<end>` (or a bare path) every time the reader opens a document on its own: a tree tap, a link, a search hit, back. Never for a location the app passed in through `openLocation`. Write it into the URL so a reload lands on the same document.',\n },\n {\n signature: 'write?(path: string, text: string): Promise<void>',\n guarantees:\n 'Optional. Replaces the file at `path` (creating it) and fires `subscribe`. Omit it for a brain the app keeps read-only; `Brain` calls it in no slice yet.',\n },\n ],\n\n notes: `**Paths are \\`/\\`-separated, relative to the bundle root, no leading slash.** \\`notes/tyres.md\\`, not\n\\`/notes/tyres.md\\`. A directory path carries no trailing slash.\n\n**The reader does not validate the bundle.** A file without front matter is read and shown as it is;\n\\`index\\` only needs front matter when it synthesizes.`,\n\n fake: {\n name: 'fakeBrain',\n what: `A map of path to markdown, kept in memory. \\`list\\`, \\`index\\` and \\`search\\` are derived from it on every\ncall; \\`write\\` replaces an entry and fires \\`subscribe\\`. Nothing is persisted.`,\n example: `import { Brain, fakeBrain } from 'golem-ui'\n\nconst brain = fakeBrain({\n 'index.md': '---\\\\nokf_version: 0.2\\\\n---\\\\n# Shop knowledge\\\\n\\\\n* [tyres.md](tyres.md) - what we stock',\n 'tyres.md': '---\\\\ntype: concept\\\\ndescription: what we stock\\\\n---\\\\n# Tyres\\\\n\\\\nWe stock 700c and 27.5.',\n})\n\n;<Brain config={{ openLocation: 'tyres.md#L6-L6' }} adapters={{ brain }} />`,\n },\n\n consumers: ['Brain'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const chatAdapterDocs: AdapterDocs = {\n name: 'Chat',\n slug: 'chat',\n storybookPath: 'adapters-chat',\n tagline:\n 'The conversation with the agent: the history, one way to send, and a stream that arrives token by token.',\n\n purpose: `\\`Chat\\` is the conversation itself — the transport between the reader and whatever agent is answering.\nIt is three methods, and the smallest of them carries the most: \\`subscribe\\` is called with **the\nwhole conversation** on every change, including each token of a reply being written.\n\nThat \"whole conversation every time\" is the design. The component holds no message state of its own,\nso there is no reconciliation to get wrong and nothing to drift. Implement it over SSE, a WebSocket,\na polling loop, or a canned script.`,\n\n methods: [\n {\n signature: 'history(): Promise<ChatMessage[]>',\n guarantees:\n 'The conversation so far, oldest first. Called once on mount; everything after that arrives through `subscribe`. An empty conversation is an empty array, not an error.',\n },\n {\n signature: 'send(text: string, attachments?: ChatAttachment[]): Promise<void>',\n guarantees: `Resolves nothing. **The sent message reaches the component through \\`subscribe\\`, not through this\npromise** — insert it immediately with a stable \\`id\\` and \\`delivery: 'pending'\\`, then emit the complete\nconversation. Reconcile that same id to an omitted \\`delivery\\` when acknowledged, or \\`'failed'\\` when it cannot be sent. \\`attachments\\` are refs the composer is already holding: a name and a size, never\nbytes. Getting the bytes somewhere is the \\`Files\\` adapter's job, done before \\`send\\` is called.`,\n throws:\n 'An `Error` whose `message` is shown in the composer as written. A rejected send leaves its failed message in the conversation.',\n },\n {\n signature: 'retry?(messageId: string): Promise<void>',\n guarantees:\n 'Optional. Retries the failed message with this stable id and emits the complete conversation as its delivery changes. Omit it when retries are not available.',\n },\n {\n signature: 'interrupt?(): Promise<void>',\n guarantees:\n 'Optional. Stops the agent mid-turn and emits the conversation as it settles: the streaming message drops its flag, or is removed. Chat shows a Stop pill on the thinking row only when this exists; the pill is disabled until the promise settles.',\n throws: 'An `Error` whose `message` is shown in the error strip as written.',\n },\n {\n signature: 'commands(): Promise<ChatCommand[]>',\n guarantees:\n 'Optional. The slash commands this adapter honours, each `{ name, description, args? }` with the name carrying its leading slash. Chat calls it each time a composer line starting with `/` opens the picker. `args` lists the values a command accepts as its single argument; omit it and the picker closes on the space after the name.',\n },\n {\n signature: 'runCommand(line: string): Promise<string>',\n guarantees:\n 'Optional, paired with `commands`. Runs one `/...` line exactly as typed and resolves the reply text. Everything after the name is one argument, never tokenized. Chat renders the line and the reply as system rows.',\n throws:\n 'An `Error` for an unknown name or a missing argument, before anything runs; its `message` is shown in the error strip as written.',\n },\n {\n signature: 'openSource?(location: string): void',\n guarantees:\n \"Optional. Called with a source location (`path#L3-L5`) when the reader clicks a source chip under an agent bubble. The app hands the location to `Brain`'s `openLocation`. Chips render whether or not this exists; without it a click does nothing.\",\n },\n {\n signature: 'subscribe(listener: (messages: ChatMessage[]) => void): Unsubscribe',\n guarantees: `Calls the listener with the **entire** conversation on every change: a message sent, a reply starting,\nand every token of that reply. Returns the function that detaches it.`,\n },\n ],\n\n notes: `**Streaming is one message id emitted again and again.** The adapter re-emits the same \\`id\\` with a\nlonger \\`text\\` and \\`streaming: true\\`; **the last emission of that id drops the flag, and that is\n\\`done\\`.** There is no separate completion event to miss.\n\n- An empty \\`text\\` with \\`streaming: true\\` is the agent thinking before its first token — the component\n draws the thinking bubble off exactly that.\n- A stream that stops without ever dropping the flag leaves the bubble writing forever. If the\n transport can fail mid-reply, emit the id one last time without the flag.\n\n**Delivery belongs to the adapter.** Omit \\`delivery\\` for a confirmed message; \\`'pending'\\` is still\nsending and \\`'failed'\\` remains visible until a retry reconciles it. The component does not keep a\nsecond optimistic list, so every emission must be the complete conversation, including pending and\nfailed messages.\n\n**A \\`ChatMessage\\` is \\`id\\`, \\`role\\` (\\`'user'\\`, \\`'agent'\\` or \\`'system'\\`), \\`text\\`, and \\`at\\` as an ISO datetime.**\n\\`text\\` is markdown, and the component renders it as such. A \\`'system'\\` message is a slash command or its\nreply; emit one from history when the conversation keeps them, and Chat draws it as the same dim row.\n\n**Slash commands are two optional methods.** \\`commands\\` lists what the picker completes; \\`runCommand\\`\nruns a \\`/\\` line and answers in text. \"New conversation\" is a \\`/reset\\` command, not a button.\n\n**\\`sources\\` on an agent message are source locations**, \\`path#L<start>-L<end>\\` inside a brain, one chip\neach under the bubble. The chip shows the file name and the line range.\n\n**A \\`ChatAttachment\\` is a name and an optional size, no bytes and no URL.** \\`size\\` shows on the chip\nas \\`12 KB\\`; leave it off and the chip carries the name alone. Pair this adapter with \\`Files\\` when the\nattachment has to be openable later.`,\n\n fake: {\n name: 'fakeChat',\n what: `An in-memory conversation that really streams. \\`send\\` echoes a confirmed reader message, then writes the\nnext canned reply **word by word under one message id**, thinking bubble first — so a component's\nstreaming path is exercised with no backend. \\`replies\\` are answered in order and loop when they run\nout; an empty list leaves the agent mute. \\`tokenDelayMs\\` is the whole difference between a story\nworth watching and a test that finishes: stories want ~45 ms, tests want 0. \\`commands\\` turns on the\nslash picker: \\`/reset\\` clears the conversation and any other listed command replies with the line it ran.`,\n example: `import { Chat, fakeChat } from 'golem-ui'\n\nconst chat = fakeChat(\n [\n { id: 'm-1', role: 'user', text: 'What is on the bench today?', at: '2026-01-01T08:40:00Z' },\n { id: 'm-2', role: 'agent', text: 'Two tickets waiting.', at: '2026-01-01T08:40:06Z' },\n ],\n {\n replies: ['Noted — writing that down now.'],\n tokenDelayMs: 45,\n commands: [{ name: '/reset', description: 'Start a new conversation' }],\n },\n)\n\n;<Chat config={chatConfig} adapters={{ chat }} />`,\n },\n\n consumers: ['Chat'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const clockAdapterDocs: AdapterDocs = {\n name: 'Clock',\n slug: 'clock',\n storybookPath: 'adapters-clock',\n tagline:\n 'Now, and the zone to read it in — two methods, so nothing in the kit reads the wall clock.',\n\n purpose: `\\`Clock\\` is the smallest adapter in the kit and it exists for one reason: a component that reads the\nwall clock cannot be tested, screenshotted or demonstrated twice with the same result. Everything\nthat needs the time asks for it here.\n\nHand it \\`fakeClock()\\` and \"today\" is a fixed day, so a dated document renders the same in every story\nand every test run. Hand it a real clock in the app and nothing else changes.`,\n\n methods: [\n {\n signature: 'now(): Date',\n guarantees: `A fresh \\`Date\\`, synchronously. Callers may keep it, so return a new object rather than one they could\nmutate. It is called on render, which means it must be cheap and must never do I/O.`,\n },\n {\n signature: 'timeZone(): string',\n guarantees: `An IANA zone name — \\`Europe/Lisbon\\`, \\`UTC\\` — used to format the times a reader sees. It is the\nreader's zone, not the server's; a component formats every timestamp through it, so two people in two\nplaces read the same record differently and both read it correctly.`,\n },\n ],\n\n notes: `**Nothing here is asynchronous and nothing here is subscribable.** A component re-reads \\`now()\\` when\nit re-renders; a clock that has to tick drives that from outside.\n\n**Records carry their own timestamps.** \\`Clock\\` is for the *reading* time — \"Saved 13:20\", which day\n\"today\" is, which day a timeline entry falls in. What a row was created at came from the store.`,\n\n fake: {\n name: 'fakeClock',\n what: `Frozen. \\`fakeClock()\\` is \\`2026-01-01T09:00:00Z\\` in \\`UTC\\` and returns that same instant every time, so\na time-based view renders identically in every story and every test run. Pass a \\`Date\\` and a zone to\nmove it. There is no tick and no advance method: a test that needs time to pass builds a second\nclock.`,\n example: `import { Report, fakeClock } from 'golem-ui'\n\nconst clock = fakeClock(new Date('2026-01-14T08:00:00Z'), 'Europe/Lisbon')\n\n;<Report config={reportConfig} adapters={{ records, clock }} />`,\n },\n\n consumers: ['Report', 'Editor', 'Timeline'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const filesAdapterDocs: AdapterDocs = {\n name: 'Files',\n slug: 'files',\n storybookPath: 'adapters-files',\n tagline:\n 'A folder of blobs and the five things you do to one: put it there, open it, caption it, list it, take it away.',\n\n purpose: `\\`Files\\` is where bytes live. A component hands it a browser \\`File\\` and a folder name and gets back a\n\\`FileRef\\` — plain JSON with an id, a name, a MIME type, a size and a timestamp, and no bytes at all.\nWhen something wants to see the file it asks \\`url\\` for one.\n\nThat split is the point: a ref can go on a record, into a chat message or into a config and survive\nthe round trip, while the bytes stay behind whatever storage the app actually has. Implement it over\nS3, a CDN, a form POST, or a \\`Map\\` of object URLs.`,\n\n methods: [\n {\n signature: 'upload(file: File, options: UploadOptions): Promise<FileRef>',\n guarantees: `The file lands in \\`options.folder\\` — the same string \\`list\\` and \\`subscribe\\` are given — and the\nresolved ref is what everything downstream holds. \\`onProgress\\` is called as the bytes go up with the\nfraction done between 0 and 1; **a store that cannot report progress calls it once with \\`1\\` rather\nthan never**, so the caller's bar completes instead of hanging. \\`uploadedAt\\` is the store's own\nstamp, and it is what orders every gallery.`,\n throws:\n 'An `Error` whose `message` is shown on that file’s row as written — too large, wrong type, refused. One file failing leaves the rest of the batch alone.',\n },\n {\n signature: 'url(ref: FileHandle, options?: { thumbnail?: boolean }): Promise<string>',\n guarantees: `A URL the browser can open. \\`thumbnail: true\\` asks for a small one; **a store that keeps no thumbnail\nreturns the full URL** rather than rejecting, so a caller never has to ask whether it has one. Takes\na whole ref or just an id.`,\n },\n {\n signature: 'remove(ref: FileHandle): Promise<void>',\n guarantees:\n 'The file is gone when the promise resolves, and `subscribe` has fired for its folder.',\n throws:\n 'An `Error` whose `message` the reader is shown as written — a permission refusal reads as one.',\n },\n {\n signature: 'list(folder: string): Promise<FileRef[]>',\n guarantees:\n 'Everything in that folder, **newest first**. Ordering is the store’s job, not the gallery’s. An unknown folder is an empty list, not an error.',\n },\n {\n signature: 'caption(ref: FileHandle, caption: string): Promise<FileRef>',\n guarantees:\n 'Writes the line and resolves the whole ref back, so the caller replaces its copy rather than patching it.',\n throws: 'An `Error` whose `message` is shown under the tile as written.',\n },\n {\n signature: 'subscribe(folder: string, listener: () => void): Unsubscribe',\n guarantees:\n 'Calls the listener with no argument whenever that folder changed, including changes this caller made. The listener re-lists; it is a nudge, not a payload. Returns the function that detaches it.',\n },\n ],\n\n notes: `**A \\`FileRef\\` is plain JSON and carries no bytes**: \\`id\\`, \\`name\\`, \\`contentType\\`, \\`size\\`, \\`folder\\`,\n\\`uploadedAt\\`, and \\`caption\\` once something writes one. \\`contentType\\` is how a gallery knows it has a\nphoto rather than an invoice.\n\n**Every method that takes a ref takes an id too.** \\`FileHandle\\` is \\`FileRef | string\\`, and \\`fileId()\\`\nnarrows it — so a caller that kept only the id, an attachment on a record say, can still open and\ndelete the file.\n\n**Every rejection's \\`message\\` reaches a person unedited.** Write them as sentences.\n\n**The folder is the only scope there is.** There are no nested paths, no per-file permissions and no\nmove: a file is in one folder from upload to removal.`,\n\n fake: {\n name: 'fakeFiles',\n what: `An in-memory folder map over object URLs. Uploads report ten progress steps over about 600 ms so a\nbar is worth looking at, and thumbnails are really drawn — through a canvas, in the page — so the\ngrid loads small pictures rather than full ones. \\`failOn\\` rejects one file by name and lets the rest\nof the batch through, which is how a story shows a failed row beside successful ones. \\`now\\` decides\nwhat stamps \\`uploadedAt\\`, so a demo dated inside its own fiction stays there.\n\nSeeded files carry a \\`content\\` string instead of bytes, because the kit ships no image files:\n\\`placeholderImage(label)\\` draws one as a few hundred bytes of SVG data URL.`,\n example: `import { Upload, fakeFiles, placeholderImage } from 'golem-ui'\n\nconst files = fakeFiles({\n seed: [\n {\n id: 'f-1',\n folder: 'shop',\n name: 'rear-derailleur.jpg',\n contentType: 'image/jpeg',\n content: placeholderImage('rear derailleur'),\n caption: 'Hanger bent inwards.',\n },\n ],\n failOn: 'too-big.mov',\n})\n\n;<Upload config={galleryConfig} adapters={{ files }} />`,\n },\n\n consumers: ['Timeline', 'Upload'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const identityAdapterDocs: AdapterDocs = {\n name: 'Identity',\n slug: 'identity',\n storybookPath: 'adapters-identity',\n tagline: 'Who is here, how they got here, and who else may come in.',\n\n purpose: `\\`Identity\\` answers one question everywhere in the kit — who is signed in — and carries the whole\nmembership story behind it: signing in by password or by a one-time code, signing up, the member\ntable, invites and roles. A \\`User\\` is an id, a name, an optional email and **every** role the person\nholds; a guard lets them through when one of those roles is on its list.\n\nAuthentication itself is the app's, not the kit's. Implement this over a session cookie, a JWT,\nan OAuth SDK, or a table in memory.`,\n\n methods: [\n {\n signature: 'currentUser(): Promise<User | null>',\n guarantees:\n '`null` means signed out, and signed out is never an error. Safe to call on mount before anything has subscribed.',\n },\n {\n signature: 'subscribe(listener: (user: User | null) => void): Unsubscribe',\n guarantees: `Calls the listener with the new user on **every** sign-in, sign-up and sign-out, including ones this\ncaller triggered, and with \\`null\\` for signed out. Unlike the collection subscriptions, this one\ncarries its payload: the listener is handed the user rather than re-reading. Returns the function\nthat detaches it.`,\n },\n {\n signature: 'signIn(email: string, password: string): Promise<User>',\n guarantees: 'Resolves the signed-in user and fires `subscribe` before it does.',\n throws:\n 'An `Error` whose `message` the reader is shown as written — *\"That password is not right.\"* Write it for a person, and keep it vague enough not to say whether the address exists if that matters to you.',\n },\n {\n signature: 'signUp(input: SignUpInput): Promise<User>',\n guarantees: `Creates the account and signs it in. \\`input.invite\\` is the token the reader arrived on, and **it\ndecides the new role**; without one the store picks its own default. \\`password\\` is present in\n\\`password\\` mode and absent in \\`code\\` mode.`,\n throws:\n 'An `Error` for a spent or invented invite, and for an address that already has an account. Both messages reach the reader as written.',\n },\n {\n signature: 'requestCode(email: string): Promise<void>',\n guarantees: 'Sends a one-time code to the address. `code` mode only; resolving means sent.',\n throws: 'An `Error` when the address is unknown, if you choose to say so.',\n },\n {\n signature: 'verifyCode(email: string, code: string): Promise<User>',\n guarantees: 'Resolves the signed-in user, the same as `signIn` does, and fires `subscribe`.',\n throws: 'An `Error` whose `message` the reader is shown — *\"That code is not right.\"*',\n },\n {\n signature: 'signOut(): Promise<void>',\n guarantees:\n 'Fires `subscribe` with `null`. Signing out while already signed out is not an error.',\n },\n {\n signature: 'listMembers(): Promise<User[]>',\n guarantees:\n 'Everyone with an account, each with their whole `roles` list. This is what a Team screen draws.',\n },\n {\n signature: 'invite(role: string): Promise<string>',\n guarantees:\n 'Mints an invite for that role and resolves **the URL to hand out**, not the token. The token is the last thing in the URL, and `signUp` accepts it exactly once.',\n },\n {\n signature: 'removeMember(userId: string): Promise<void>',\n guarantees:\n 'The member is gone from `listMembers`. Removing the signed-in user signs them out, and `subscribe` fires with `null`.',\n throws: 'An `Error` whose `message` the reader is shown — a last-owner rule reads as one.',\n },\n {\n signature: 'setRole(userId: string, role: string): Promise<void>',\n guarantees:\n '**Replaces** the member’s roles with this one; it does not add to them. Changing the signed-in user’s role fires `subscribe` so a guard re-decides immediately.',\n throws: 'An `Error` whose `message` the reader is shown.',\n },\n {\n signature: 'resetPassword?(userId: string): Promise<string>',\n guarantees: `Optional. Mints a one-use link that lets that member choose a new password and resolves **the URL\nto hand out**. The member keeps their id, email and roles. Left out, the member list offers no reset.`,\n throws: 'An `Error` whose `message` the reader is shown.',\n },\n {\n signature: 'setPassword?(token: string, password: string): Promise<void>',\n guarantees: `Optional, and the other half of \\`resetPassword\\`: the token off the reset link's \\`Route.params.reset\\`\nand the password the reader chose. Resolving means the password is the new one and every session of\nthat account has ended, so the reader signs in again. Left out, a reset link opens the sign-in card.`,\n throws:\n 'An `Error` for a spent or expired link, shown to the reader as written — *\"That reset link has expired or was already used.\"*',\n },\n ],\n\n notes: `**Every rejection's \\`message\\` is shown to the reader exactly as written**, which is why this\ninterface names them all: *\"That password is not right.\"*, not \\`AUTH_002\\`.\n\n**\\`roles\\` is a list, always.** A person can hold several; a guard passes them when any one matches.\n\\`setRole\\` replacing the list rather than appending is deliberate — role editing in the kit is a\nsingle-select control.\n\n**An invite is a URL, and the role rides on it.** The app decides where that URL points; \\`Auth\\` reads\nthe token out of \\`Route.params\\`, which is why the app's navigation adapter has to put a query string\nthere.\n\n**Nothing here is a permission check.** The adapter says who someone is and what roles they hold; a\ncomponent's config says which roles get in.`,\n\n fake: {\n name: 'fakeIdentity',\n what: `An in-memory member table with one password for everyone and one code for everyone, both stated in\nits options so a demo can say them out loud. Invites really round-trip: \\`invite()\\` hands back a URL\nwhose token \\`signUp()\\` accepts exactly once. \\`user: null\\` starts the app signed out, which is how a\nsign-in screen gets to be the first thing a reader sees.\n\nIt holds its invites in memory, which is a real limit: an invite link is meant to be opened in\nanother tab, and that is a cold page load. The showcase wraps the fake to keep open tokens in\n\\`localStorage\\`, and that wrapper is worth copying.`,\n example: `import { Auth, fakeIdentity } from 'golem-ui'\n\nconst identity = fakeIdentity({\n user: null,\n members: [\n { id: 'u-1', name: 'Nadia Okonkwo', email: 'nadia@example.com', roles: ['owner'] },\n { id: 'u-2', name: 'Omar Haddad', email: 'omar@example.com', roles: ['mechanic'] },\n ],\n password: 'golem',\n defaultRole: 'mechanic',\n})\n\n;<Auth.Guard config={authConfig} adapters={{ identity, navigation }}>\n <App />\n</Auth.Guard>`,\n },\n\n consumers: ['Shell', 'Auth', 'RecordForm', 'Timeline'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const navigationAdapterDocs: AdapterDocs = {\n name: 'Navigation',\n slug: 'navigation',\n storybookPath: 'adapters-navigation',\n tagline:\n 'Where the reader is, how to send them somewhere else, and a listener for when that changes.',\n\n purpose: `\\`Navigation\\` is the router, reduced to what a component actually needs: read the current route, go to a\npath, hear about it when it changes. A \\`Route\\` is a \\`path\\` and a flat \\`params\\` map of strings.\n\nNo component in this kit imports a router, and that is what makes the kit work under React Router,\nunder a hash, under Next.js, or under a \\`useState\\`. It is also the one adapter an app most often\nwrites itself rather than taking the fake — the showcase's \\`hashNavigation\\` is about thirty lines.`,\n\n methods: [\n {\n signature: 'current(): Route',\n guarantees: `The route right now, synchronously. Called during render, so it reads existing state rather than\nparsing anything expensive.`,\n },\n {\n signature: 'go(path: string): void',\n guarantees: `Navigates, and fires \\`subscribe\\`. Returns nothing and is not awaited — a component calls it and stops\ncaring. Whether it pushes onto history or replaces is the implementation's to decide.`,\n },\n {\n signature: 'subscribe(listener: (route: Route) => void): Unsubscribe',\n guarantees: `Calls the listener with the new route on every change, **including a change the reader made with the\nback button**, not only ones \\`go\\` caused. This one carries its payload rather than nudging. Returns\nthe function that detaches it.`,\n },\n ],\n\n notes: `**\\`params\\` is where a token arrives.** \\`Auth\\` reads an invite token out of \\`Route.params\\`, so an\nimplementation over a hash has to parse the hash's query string into it — that is the one detail\n\\`fakeNavigation\\` does not do and a real one must.\n\n**There is no \\`back\\`, no \\`replace\\` and no route matching.** The app owns its route table; a component\nonly ever says \"go here\".\n\n**Two components take it and neither routes.** \\`Shell\\` calls \\`go('/')\\` from the title, \\`Auth\\` sends a\nreader on after signing in. Everything else on a screen is the app's own wiring.`,\n\n fake: {\n name: 'fakeNavigation',\n what: `Keeps the route in a variable. It is right for a story, where the route is a prop and the point is\nthat a click changed it, and it is wrong for an app: the route is lost on reload and a screen has no\nURL anyone can link to. It also never fills \\`params\\`, so \\`Auth\\`'s invite path needs a real one.\n\nAn app writes its own. The showcase's \\`hashNavigation\\` reads \\`window.location.hash\\`, splits the query\nstring into \\`params\\`, and listens on \\`hashchange\\` — which survives a reload and survives being served\nunder a GitHub Pages subpath.`,\n example: `import { Shell, fakeNavigation } from 'golem-ui'\n\nconst navigation = fakeNavigation({ path: '/today', params: {} })\n\n;<Shell config={shellConfig} adapters={{ identity, navigation }} chat={chat} canvas={canvas} />`,\n },\n\n consumers: ['Shell', 'Auth'],\n}\n","import type { AdapterDocs } from '../abi'\n\nexport const recordsAdapterDocs: AdapterDocs = {\n name: 'Records',\n slug: 'records',\n storybookPath: 'adapters-records',\n tagline:\n 'A collection of rows, the four writes that change one, and the version check that keeps two writers apart.',\n\n purpose: `\\`Records\\` is the store, as far as a component is concerned: named collections of JSON rows, each with an\n\\`id\\`, and the reads and writes that move them. Filtering, sorting, searching and paging are the\nstore's work and not the component's — a component hands over a \\`RecordQuery\\` and draws whatever\ncomes back.\n\nIt is the most widely taken adapter in the kit and the one with the most to get right, because it is\nwhere two writers meet. Implement it over anything: a REST API, a database client, a document store,\nan array in memory.`,\n\n methods: [\n {\n signature: 'list<T>(collection: string, query?: RecordQuery): Promise<RecordPage<T>>',\n guarantees: `Honours the whole query, not the parts that are convenient: \\`filter\\` is field-by-field equality and an\narray value means \"any of these\"; \\`search\\` is a case-insensitive substring over the named fields\nonly; \\`sort\\` orders before paging, so page two of a sorted list follows page one. Returns\n\\`nextCursor\\` for the next page, or \\`null\\` when this was the last one — a caller pages until it sees\nthat \\`null\\` and never guesses from a short page.`,\n },\n {\n signature: 'get<T>(collection: string, id: string): Promise<T | null>',\n guarantees:\n 'Resolves the row, or `null` when the collection has no such row. A missing row is not an error: the component draws an empty state, not a failure.',\n },\n {\n signature: 'create<T>(collection: string, data: Record<string, unknown>): Promise<T>',\n guarantees:\n 'Resolves the stored row, including whatever `id` the store minted. Callers hold the returned row rather than the data they sent, so server-side defaults arrive.',\n throws:\n 'A `RecordRefusedError` when a business rule or a uniqueness check refused it, carrying the fields it names. Anything else rejects with a plain `Error` whose `message` the reader is shown as written.',\n },\n {\n signature: 'update<T>(collection, id, patch, options?: UpdateOptions): Promise<T>',\n guarantees: `Merges the patch into the stored row — a key absent from the patch keeps its stored value — and resolves\nthe whole row back. With \\`options\\`, the write is conditional: compare \\`expectedVersion\\` against the\nrow's \\`versionField\\` (\\`version\\` by default) and apply the write only if they match. A store that\nignores \\`options\\` turns every concurrent write into a silent overwrite, which is the one failure\nthis interface exists to prevent.`,\n throws:\n 'A `VersionConflictError` carrying the row **as it now stands** when `expectedVersion` is stale, so the caller merges against it without a second round trip. A `RecordRefusedError` for a refused field. A plain `Error` for a row that is not there.',\n },\n {\n signature: 'remove(collection: string, id: string): Promise<void>',\n guarantees: 'The row is gone when the promise resolves, and `subscribe` has fired.',\n throws: 'A plain `Error` when there is no such row.',\n },\n {\n signature: 'subscribe(collection: string, listener: () => void): Unsubscribe',\n guarantees: `Calls the listener with no argument whenever anything in that collection changed, including changes\nthis same caller made. The listener re-lists; it is a nudge, not a payload. Returns the function that\ndetaches it, and calling that function twice is safe.`,\n },\n ],\n\n notes: `**Two error types, and both are read by shape.** \\`refusedFields(error)\\` and\n\\`conflictingRecord(error)\\` look at \\`fields\\` and \\`name\\` on whatever was thrown rather than at its\nclass, so an adapter that crossed a bundle boundary — or never imported this module — still gets its\nrefusal honoured. That is what lets an app throw its own error object and have \\`RecordForm\\` put the\nsentence under the right control.\n\n- \\`RecordRefusedError(message, fields)\\` — a write the store refused for reasons only it knows. Each\n entry in \\`fields\\` is a \\`field\\` key as the component's config spells it and a \\`message\\` written for\n a person: *\"A quote over $500 needs the owner's sign-off.\"* A refusal that names no field shows as\n one line at the top of the form instead.\n- \\`VersionConflictError(message, current)\\` — an \\`update\\` that arrived too late. \\`current\\` is the row\n as it now stands. \\`Editor\\` merges its draft against that record; \\`RecordForm\\` keeps the reader's\n unsaved values and asks whether to keep them or take the stored ones.\n\n**Every rejection's \\`message\\` reaches a person unedited.** Write them as sentences, not as codes.\n\n**Paging is a cursor, not an offset the caller computes.** \\`nextCursor\\` is opaque; hand it straight\nback as \\`cursor\\`. A cursor over a collection that is being written to may shift rows under the\nreader, which is why a live update re-lists from the start rather than paging on.`,\n\n fake: {\n name: 'fakeRecords',\n what: `An in-memory store seeded per collection. It really filters, searches, sorts and pages, and it really\nenforces \\`expectedVersion\\` — so a story and a test run the same code path a server would, including\nthe conflict path. \\`refuse\\` makes every write reject with a \\`RecordRefusedError\\` naming one field,\nwhich is how a story shows a refusal landing under a control. \\`insert\\` is the fake's own extra: the\nsynchronous way to put a row in without going through \\`create\\`.\n\nIts cursor is an offset into the matching rows, which is honest about paging and honest about its\nlimit — a row inserted while the reader is on page three shifts the pages under them.`,\n example: `import { RecordList, fakeRecords } from 'golem-ui'\n\nconst records = fakeRecords({\n jobs: [\n { id: 'j-1', ticket: 'NG-118', bike: 'Trek FX 2', status: 'waiting', version: 1 },\n { id: 'j-2', ticket: 'NG-119', bike: 'Brompton C Line', status: 'in progress', version: 3 },\n ],\n})\n\n;<RecordList config={jobsConfig} adapters={{ records }} />\n\n// The refusal path, for the story that shows it:\nconst refusing = fakeRecords({ jobs: [] }, {\n refuse: { field: 'ticket', message: 'That ticket number is already on the board.' },\n})`,\n },\n\n consumers: ['RecordList', 'RecordForm', 'Report', 'Editor', 'Timeline'],\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const brainDocs: ComponentDocs = {\n name: 'Brain',\n slug: 'brain',\n storybookPath: 'components-brain',\n tagline:\n 'A folder of markdown with a reader that opens the passage the agent cited, highlighted and in context.',\n\n purpose: `\\`Brain\\` shows the app's knowledge folder. At desktop width it is master–detail: a list column with\nthe tree and a search box, and a reading column with the open file at a reading measure, its front\nmatter as a chip. Below 768px it is one screen at a time: the list, then the document over it with a\nback button; a link inside a document pushes the next one, and back returns to where the reader was,\nscroll and all. \\`log.md\\` is one entry in the tree, read like any other file.\n\nIts one trick is \\`openLocation\\`: hand it \\`path#L6-L8\\` and the reader opens that file, highlights\nthose lines, and scrolls them into view with the rest of the document around them, so the reader can\ncheck what the agent said against where it came from.\n\nUse it with \\`Chat\\`: an agent message carries \\`sources\\`, Chat draws a chip per source, and the app\npasses the clicked location into this config. For editing the file, that is \\`Editor\\`; this slice\nreads only.`,\n\n configNotes: [\n `\\`openLocation\\` is what the app tells the reader to show. The reader keeps its own stack of what it\nopened since: a tap in the tree, a link, a search hit, back. Each of those is reported through the\nadapter's \\`open\\`, so an app that writes it into the URL comes back to the same document after a\nreload, and the location it passes back in is not pushed twice. Omit it and the desktop shows the\nroot \\`index.md\\`; the phone shows the list.`,\n ],\n\n adapters: [\n {\n adapter: 'Brain',\n calls: '`list(dir?)`',\n why: 'The tree, one call per directory as it is expanded.',\n },\n {\n adapter: 'Brain',\n calls: '`index()`',\n why: 'The root `index.md`, real or synthesized, shown before anything is opened.',\n },\n {\n adapter: 'Brain',\n calls: '`read(path)`',\n why: 'The open file, front matter and all, so line numbers match the location.',\n },\n {\n adapter: 'Brain',\n calls: '`search(query)`',\n why: 'The hits shown in the list column while the search box has text; a hit opens its line highlighted.',\n },\n {\n adapter: 'Brain',\n calls: '`subscribe(listener)`',\n why: 'Re-reads the open file and re-lists the tree when the bundle changes.',\n },\n {\n adapter: 'Brain',\n calls: '`open(location)`',\n why: 'Optional. Told every location the reader opened on its own, so the app can keep it in the URL.',\n },\n ],\n\n adapterNotes: `Brain never calls \\`write\\`; it is on the adapter for the edit-together step to come.`,\n\n slots: [\n {\n slot: 'toolbar',\n what: 'The right-hand end of the document header, beside the open path. An Edit button goes here; empty leaves the path alone.',\n },\n ],\n\n example: `import { Brain, fakeBrain } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Brain\n config={{\n title: 'Shop knowledge',\n openLocation: 'workshop/turnaround.md#L6-L8',\n }}\n adapters={{ brain: fakeBrain(files) }}\n/>`,\n\n failureModes: `- **Invalid config.** Brain renders an error card instead of the reader, naming every field that failed.\n A location whose range is not \\`#L<start>-L<end>\\` fails; an unknown field fails.\n- **The file is missing.** \\`read\\` rejects and the reader shows the adapter's message in place of the\n document; the tree stays usable.\n- **The range runs past the file.** The highlight covers what exists and stops at the last line.\n- **The range is inside the front matter.** The highlight starts at the first line after it; front\n matter is shown as a \\`type\\` chip and the description, never as YAML and never highlighted.\n- **A block spans the range's edge.** The file is rendered in three runs, so a list or table cut by\n the range renders as two. Cite whole blocks and it does not happen.\n- **Markdown is the kit's subset.** Headings, fenced code, pipe tables, lists, bold, italic, inline\n code and links. A relative link opens that file in the reader; \\`http(s)\\` opens a new tab; wiki\n links and images render as the characters they are.\n- **A new adapter object on every render** re-subscribes and re-lists the tree. Build it once.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const authDocs: ComponentDocs = {\n name: 'Auth',\n slug: 'auth',\n storybookPath: 'components-auth',\n tagline:\n 'The door and the guest list: sign-in, sign-up, invites, the member list, and a role guard.',\n\n purpose: `\\`Auth\\` is everything a multi-user app needs around identity, so the app writes no login code. It is\nthree surfaces on one config:\n\n- **\\`<Auth />\\`** — the screen. Signed out it is sign-in, sign-up and the invite landing; signed in it\n is the member list, with roles and removals for whoever is allowed to manage them.\n- **\\`<Auth.Guard roles={[...]}>\\`** — wraps what only some roles may see. It renders the sign-in\n screen to a stranger, a not-allowed card to the wrong role, and the children to the right one.\n- **\\`<Auth.AccountMenu />\\`** — the name, the role and the way out. It goes in \\`Shell\\`'s \\`account\\`\n slot.\n\nWrap the whole app in one \\`Guard\\` with no \\`roles\\` to make signing in the front door, and put a\nnarrower \\`Guard\\` around each screen that needs one. For a profile page, a settings form, or an\norganisation switcher, reach for something else: Auth is the door and the guest list, not the\naccount.`,\n\n configNotes: [\n `\\`roles\\` carries the whole role model. Each entry is \\`{ id, label, manages }\\`: \\`id\\` is what a \\`Guard\\`\nlists and what \\`setRole\\` writes, \\`label\\` is what a person reads, and \\`manages\\` marks the roles that\nmay invite, re-role and remove. The **first entry is the default** — what someone who signs up\nwithout an invite becomes, and what the invite control offers first.\n\n\\`copy\\` overrides labels one at a time; everything you leave out keeps its default.`,\n ],\n\n adapters: [\n {\n adapter: 'Identity',\n calls: '`currentUser()`, `subscribe()`',\n why: 'Who is here on mount, and every later flip. All three surfaces read it.',\n },\n {\n adapter: 'Identity',\n calls: '`signIn()`, `signOut()`',\n why: 'The password mode, and the way out of both the account menu and the not-allowed card.',\n },\n {\n adapter: 'Identity',\n calls: '`requestCode()`, `verifyCode()`',\n why: 'The `code` mode: the address first, the one-time code second.',\n },\n {\n adapter: 'Identity',\n calls: '`signUp()`',\n why: 'Sign-up, carrying the invite token when the reader arrived on a link.',\n },\n {\n adapter: 'Identity',\n calls: '`listMembers()`, `invite()`, `setRole()`, `removeMember()`',\n why: 'The member list and what a managing role does to it.',\n },\n {\n adapter: 'Navigation',\n calls: '`current()`, `subscribe()`',\n why: \"Reads `params.invite` — the app's router is what knows how its own URLs are shaped.\",\n },\n ],\n\n adapterNotes: `Auth takes no other adapter. It never fetches, never stores, and never reads \\`window\\`.\n\n**Refusals are prose, not codes.** Any Identity method may reject with an \\`Error\\`, and Auth prints\nits \\`message\\` under the form exactly as written. Write those messages for the person reading them.\n\n**Invite links round-trip through the router.** \\`invite(role)\\` returns a URL whose token is the last\nthing in it. The reader opens that URL, the app's Navigation adapter parses the token into\n\\`params.invite\\`, and Auth opens sign-up with the workspace named and the role already decided.\nAn app whose router does not put query parameters into \\`Route.params\\` never sees the invite.`,\n\n example: `import { Auth, Shell, fakeIdentity, fakeNavigation } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\nconst config = {\n workspaceName: 'Northgate Cycles',\n mode: 'password',\n allowSignUp: false,\n inviteOnly: true,\n roles: [\n { id: 'mechanic', label: 'Mechanic' },\n { id: 'front desk', label: 'Front desk' },\n { id: 'owner', label: 'Owner', manages: true },\n ],\n copy: { hint: 'Any of the five shop accounts, password northgate.' },\n}\n\nconst adapters = { identity: fakeIdentity(), navigation: fakeNavigation() }\n\n;<Auth.Guard config={config} adapters={adapters}>\n <Shell\n config={{ title: 'Northgate Cycles' }}\n adapters={adapters}\n account={<Auth.AccountMenu config={config} adapters={adapters} />}\n canvas={\n <Auth.Guard config={config} adapters={adapters} roles={['owner']}>\n <DnaScreen />\n </Auth.Guard>\n }\n />\n</Auth.Guard>`,\n\n failureModes: `- **Invalid config.** Auth renders an error card instead of the screen, in dev and in prod, naming\n every field that failed. An unknown field is a failure too — \\`allowsignup\\` does not quietly become\n \\`allowSignUp\\`.\n- **The adapter refuses.** Whatever the rejection's \\`message\\` says is what appears under the form,\n and the form keeps what was typed so it can be corrected. A rejection with no message reads\n \"Something went wrong. Try again.\"\n- **\\`inviteOnly\\` with no invite in the route.** There is no way to sign up, and the screen says so.\n That is the setting working; if it is a surprise, the router is not putting \\`?invite=\\` into\n \\`Route.params\\`.\n- **A used invite.** Tokens are single-use. A second sign-up on the same link is refused by the\n adapter, and the refusal shows under the form like any other.\n- **\\`Guard\\` while the session is still loading.** It renders nothing rather than flashing the\n sign-in screen at someone who is already signed in. A guard around a whole app is therefore blank\n for one tick.\n- **A role that no longer exists.** A member holding a role that is not in \\`roles\\` reads as the raw\n id in the list, and a \\`Guard\\` that does not list it turns them away. Changing \\`roles\\` does not\n migrate anyone; \\`setRole\\` does.\n- **Removing yourself.** The button is not drawn on your own row, because the way out of a workspace\n is signing out, not deleting the last managing account.\n- **A new adapter object on every render.** Auth re-subscribes when \\`adapters\\` changes identity,\n which re-reads the session and the member list. Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const chatDocs: ComponentDocs = {\n name: 'Chat',\n slug: 'chat',\n storybookPath: 'components-chat',\n tagline:\n 'The conversation with the agent: a following message list, a growing composer, replies that stream in.',\n\n purpose: `\\`Chat\\` is the conversation between a person and the agent: a message list that follows the newest\nmessage, a composer that grows as you type, markdown in the agent's replies, and a reply that fills\nin token by token while you watch. It is the column that goes in \\`Shell\\`'s \\`chat\\` slot.\n\nUse it once per conversation. It is the conversation and nothing else: it holds no messages of its\nown (the \\`Chat\\` adapter owns them), it does not know which agent it is talking to, and it does not\nroute. For a list of past conversations to pick from, that is a different component.`,\n\n adapters: [\n {\n adapter: 'Chat',\n calls: '`history()`',\n why: 'The conversation as it stands on mount.',\n },\n {\n adapter: 'Chat',\n calls: '`subscribe(listener)`',\n why: 'Every later change: a new message, and each token of a streaming reply.',\n },\n {\n adapter: 'Chat',\n calls: '`send(text, attachments?)`',\n why: 'What the reader typed, with whatever the composer has staged.',\n },\n {\n adapter: 'Chat',\n calls: '`retry?(messageId)`',\n why: 'Retries a visible failed message when the adapter supports it.',\n },\n {\n adapter: 'Chat',\n calls: '`interrupt?()`',\n why: 'The Stop pill on the thinking row, shown only when the adapter supports it.',\n },\n {\n adapter: 'Chat',\n calls: '`commands?()`',\n why: 'The slash picker, opened by a `/` at the start of the composer. Without it, `/` is text.',\n },\n {\n adapter: 'Chat',\n calls: '`runCommand?(line)`',\n why: 'A sent `/` line. Its reply comes back as text, and both render as system rows.',\n },\n {\n adapter: 'Chat',\n calls: '`openSource?(location)`',\n why: 'A click on a source chip under an agent bubble, with the cited `path#L3-L5`.',\n },\n ],\n\n adapterNotes: `Chat takes no other adapter. It never fetches, never stores, and never reads a clock — a bubble's\ntime comes from the message's own \\`at\\`.\n\n**Streaming is a rule about ids, not a second method.** The adapter re-emits a message with the same\n\\`id\\` and a longer \\`text\\`, \\`streaming: true\\` throughout; the emission that drops the flag is \\`done\\`.\nChat renders that as one bubble that grows with a caret on the end. A \\`streaming\\` message with no\ntext yet is the agent thinking, and renders as the thinking row rather than an empty bubble. When the\nadapter provides \\`interrupt\\`, that row carries a Stop pill; without it, the row is just the dots.\n\n**Slash commands come from the adapter, not the config.** With \\`commands\\`, a composer line starting\nwith \\`/\\` opens a picker over the composer: arrows move, Tab or Enter pick, Escape closes, and a\ncommand with \\`args\\` keeps completing its one argument after the space. A sent \\`/\\` line goes to\n\\`runCommand\\`, never to \\`send\\`, and the line and its reply render as dim centred system rows.\n\"New conversation\" is the adapter's \\`/reset\\`, not a button.`,\n\n slots: [\n {\n slot: 'attach',\n what: 'Replaces the paperclip and the row of staged chips above the composer. It is called with `stage`, which is what the next message is sent with; `Upload.Picker` is what belongs here, so an attachment is a file that really went somewhere. Empty falls back to the built-in paperclip, which stages a name and a size and uploads nothing.',\n },\n ],\n\n example: `import { Chat, fakeChat } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Chat\n config={{\n placeholder: 'Message Golem…',\n emptyState: 'Ask about a ticket, a customer or the day’s report.',\n agentName: 'Golem',\n userName: 'Nadia',\n markdown: true,\n showTimestamps: false,\n maxComposerLines: 6,\n }}\n adapters={{ chat: fakeChat() }}\n/>`,\n\n failureModes: `- **Invalid config.** Chat renders an error card instead of the conversation, in dev and in prod,\n naming every field that failed. An unknown field is a failure too — \\`showtimestamps\\` does not\n quietly become \\`showTimestamps\\`.\n- **The last message is the reader's.** Chat reads that as a reply the agent owes and shows the\n thinking row until an agent message arrives. A history that *ends* on a user message therefore\n looks like it is still waiting, which is usually true and occasionally a surprise.\n- **A message is sending or failed.** The adapter keeps it in the complete conversation with\n \\`delivery: 'pending'\\` or \\`'failed'\\`. Chat dims a pending bubble and labels it Sending; a failed bubble\n says Not sent and offers Retry only when the adapter provides \\`retry\\`. Neither state shows the agent\n thinking row.\n- **A command rejects.** \\`runCommand\\` throws for an unknown name or a bad argument. The typed line\n stays as a system row and the error shows in the strip under the feed, as written.\n- **Sending rejects.** Chat keeps the cleared composer clear, so it never overwrites a newer draft, and\n shows the error from the adapter. The adapter still owns the failed message and its retry.\n- **History rejects.** Chat shows the adapter error and keeps any conversation delivered through\n \\`subscribe\\`; a live update never disappears behind a late history failure.\n- **The reader has scrolled up.** New messages stop moving the list; it follows the newest message\n only while it is already within 48px of the bottom. A stream cannot yank the page out from under\n someone reading.\n- **A new adapter object on every render.** Chat re-subscribes when the \\`adapters\\` prop changes\n identity, which replays \\`history()\\` and can double a stream. Build adapters once, outside render.\n- **Markdown is a subset.** Fenced code, bullet and numbered lists, bold, italic, inline code, and\n \\`http(s)\\` links. Anything else — tables, images, raw HTML — renders as the characters it is, which\n is also what a half-written token mid-stream does.\n- **The built-in paperclip uploads nothing.** It stages a name and a size, which is enough to show\n the chip and enough for an adapter that only wants the metadata. For an attachment that is a real\n file in a real folder, host \\`Upload.Picker\\` in the \\`attach\\` slot.\n- **Timestamps are UTC**, read straight off the ISO string, so a bubble reads the same everywhere.\n For local time, put local time in \\`at\\`.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const editorDocs: ComponentDocs = {\n name: 'Editor',\n slug: 'editor',\n storybookPath: 'components-editor',\n tagline:\n 'One markdown document, edited by a person and by the agent at once, merged line by line.',\n\n purpose: `\\`Editor\\` is the document both writers share. One record is one document — a markdown body and a\nversion number — and the component shows it as editable source with the markdown styled where it\nstands, a preview of the rendering, and an outline of its headings. While the person types, the\ncomponent is subscribed to the record: a version the agent writes arrives, is merged into the draft\nline by line, and the lines it wrote carry a coloured gutter for a few seconds so the person can see\nwhat just changed under them. Neither writer overwrites the other, because neither write is a\nwhole-document write.\n\nReach for something else when the document is only to be read (\\`Report\\`), when the record is a set of\nnamed fields rather than one body of prose (\\`RecordForm\\`), or when the writing is a message rather\nthan a document (\\`Chat\\`).`,\n\n configNotes: [\n `\\`collection\\` and \\`id\\` name one record. An editor edits a document, not a collection: two documents\nare two editors.\n\n\\`bodyField\\` and \\`versionField\\` are how the component reads a record it did not design. The version\nis the whole safety story — read on load, sent back on every save as \\`expectedVersion\\`, and bumped\nby one — so it has to be a number the store keeps honest.`,\n {\n table: {\n headings: ['`preview`', 'On a phone', 'At 768px and wider'],\n rows: [\n ['`toggle`', 'One pane, swapped by a button', 'One pane, swapped by a button'],\n ['`split`', 'Falls back to `toggle`', 'Source and rendering side by side'],\n ['`off`', 'Source only, no button', 'Source only, no button'],\n ],\n },\n },\n `\\`autosaveMs\\` is how long typing has to stop before the draft goes. Short is what a shared document\nwants: the longer a draft sits unsent, the more of it a merge has to reconcile later. \\`0\\` saves on\nevery keystroke.\n\n\\`highlightMs\\` is how long the agent's lines stay marked. \\`0\\` marks nothing, which is what a document\nwith no second writer wants.`,\n ],\n\n adapters: [\n {\n adapter: 'Records',\n calls: '`get(collection, id)`',\n why: 'The document, on mount and again whenever the collection changes.',\n },\n {\n adapter: 'Records',\n calls: '`update(collection, id, patch, { expectedVersion, versionField })`',\n why: 'The save. The version it read goes out with the write, so a write that arrived second is refused rather than winning.',\n },\n {\n adapter: 'Records',\n calls: '`subscribe(collection)`',\n why: 'The agent writing to the document. Its version arrives and is merged; nothing polls.',\n },\n {\n adapter: 'Clock',\n calls: '`now()`, `timeZone()`',\n why: 'The time in the status line — “Saved 13:20” — in the reader’s own zone.',\n },\n ],\n\n adapterNotes: `Editor takes no other adapter. It never fetches, never routes, and never uploads.\n\n**The store has to enforce the version.** \\`update\\` is called with \\`{ expectedVersion, versionField }\\`,\nand a store that ignores it turns every concurrent write into a silent overwrite. A store that\nhonours it rejects with a \\`VersionConflictError\\` carrying the record as it now stands, and the\ncomponent merges against that record instead of showing an error. \\`fakeRecords\\` enforces it, so a\nstory and a test run the same path a server would.\n\n**The merge is a three-way merge over lines**, in \\`src/lib/diff3.ts\\` and tested on its own. The base\nis the version the person and the agent last agreed on, *mine* is the local draft, *theirs* is what\narrived. Edits in different places are both kept. Edits that overlap are a conflict, and a conflict\nis the one thing the component will not decide: it shows both versions and waits.\n\n**Lines, not characters.** A merge that resolves inside a sentence produces text neither writer\nwrote. A line is the smallest unit a person can look at and say \"that one, not that one\".\n\n**A new adapter object on every render** makes Editor re-subscribe and re-fetch on every render.\nBuild adapters once, outside render.`,\n\n slots: [\n {\n slot: 'draft',\n what: 'Unsaved work from a previous session — a local draft store, say. The editor opens on it instead of the record’s body, dirty against the record’s version, so the next thing the agent writes is merged rather than dropped on top of it.',\n },\n {\n slot: 'focus',\n what: '`{ line, endLine?, key?, version?, text? }` — a passage to show, in 1-based source lines with `endLine` inclusive. The source pane scrolls it into the middle and tints it until the person clicks or edits. Out-of-range lines are clamped to the document; a `line` that is not a number is ignored. The same request again needs a new `key`. Caret, selection, draft and keyboard focus are left where they are. `version` is the record version the lines were counted in: the request waits until the editor has read that version or a later one. `text` is the passage itself, its exact lines joined by `\\\\n`: the editor finds it in the draft on screen instead of trusting `line`, and marks nothing when those lines are missing or appear more than once.',\n },\n ],\n\n example: `import { Editor, fakeClock, fakeRecords } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Editor\n config={{\n collection: 'dna',\n id: 'dna',\n bodyField: 'body',\n versionField: 'version',\n autosaveMs: 1000,\n preview: 'toggle',\n outline: true,\n readOnly: false,\n placeholder: 'Write the workspace DNA in markdown…',\n highlightMs: 4000,\n locale: 'en-GB',\n }}\n adapters={{ records: fakeRecords({ dna }), clock: fakeClock() }}\n/>`,\n\n failureModes: `- **Invalid config.** Editor renders an error card instead of the document, in dev and in prod,\n naming every field that failed. \\`autosaveMs\\` and \\`highlightMs\\` are numbers and are not coerced: a\n debounce written as \\`'1000'\\` is named rather than guessed at.\n- **No such record.** \\`get\\` resolving \\`null\\` is not an empty document — the status line says which\n collection and which id came back with nothing, so a typo in \\`id\\` is visible instead of silently\n editing thin air.\n- **The adapter rejects a save** for its own reasons. The rejection's \\`message\\` is shown in the status\n line, verbatim, and the draft is kept: the person's text is never thrown away to report an error.\n- **A version mismatch is not an error.** A \\`VersionConflictError\\` from \\`update\\` triggers the merge and\n the save is retried against the version that came back. A store that rejects a stale write with a\n plain \\`Error\\` instead gets the plain-error path, and the two writers drift.\n- **A conflict stops the merge, not the editor.** Both versions of the disagreeing lines are shown\n with *Keep mine* and *Take theirs*, and nothing is applied until every one of them has been chosen.\n A version arriving while a conflict is open is held and applied once it is settled.\n- **The mark is drawn on line numbers, not on the text.** The gutter is placed when the merge lands;\n typing above those lines while the mark is still up shifts the text under it. It fades within\n \\`highlightMs\\`, which is why the drift is measured in seconds.\n- **The source is styled, not rendered.** \\`##\\` stays \\`##\\`, and the styling is weight, colour and\n background only — a heading drawn larger would slide the text out from under the caret. The\n preview is where the document is rendered, through the same markdown \\`Report\\` and \\`Chat\\` use.\n- **Tab only indents a list line.** Everywhere else it moves focus on, because a textarea a keyboard\n reader cannot leave is a trap. Cmd/Ctrl+S saves now, Cmd/Ctrl+B and Cmd/Ctrl+I wrap the selection,\n and Cmd/Ctrl+Z steps the editor's own undo — the browser's native textarea history has no entry\n for a line the agent wrote.\n- **\\`readOnly\\` blocks the writing, not the reading.** The source and the preview stay, the toolbar\n loses its actions, the autosave never runs, and nothing typed into the source reaches the record.\n- **\\`focus\\` counts lines of the text on screen.** That is the record's body, or the person's draft\n when they have unsaved changes above the passage — the component cannot know which lines the\n caller counted in. A request waits while the record loads or a conflict is open, and belongs to\n the record and the \\`records\\` adapter open when it arrived: move to another \\`id\\` or store\n before it lands and it is dropped, not applied to the wrong document.\n- **\\`focus.text\\` is matched whole line by whole line, exactly.** No trimming, no case folding. On a\n CRLF record the offered lines may keep their CRs, as the server's lines do, or drop them: both\n match. The offered \\`line\\`..\\`endLine\\` range picks among several copies only when the editor is\n on exactly \\`version\\`, with no unsaved changes, and that range holds the text; anywhere else the\n passage must appear once. A passage that cannot be placed opens the record with nothing tinted and\n nothing scrolled — the last request's tint is taken down too — and the draft is never written.\n- **A \\`focus\\` waits for a fresh read.** A record opened, or a parked draft brought back,\n is matched only after its latest version has been read and merged in, so the passage is looked\n for in the text the person will see, not in the draft as it was left. A request sent in the same\n render as a move to another record belongs to the record moved to.\n- **\\`focus\\` shows the source, not the preview.** A toggle editor showing the preview switches to\n the source pane, and \\`split\\` leaves the preview where it was. Source lines have no reliable\n position in the rendering, so the component does not pretend to scroll it.\n- **The focus tint lasts until the text changes.** Any edit — the person's or a merged agent version\n — a click in the source, or a move to another record takes it down, so it never sits on lines that\n moved under it or on a second record that happens to hold the same text.\n- **Moving to another record parks this one.** Changing \\`collection\\`, \\`id\\`, \\`bodyField\\`,\n \\`versionField\\` or the \\`records\\` adapter opens that record fresh and keeps the one left behind —\n draft, the version it read, an open conflict — for as long as the editor stays mounted. Nothing is\n written on the way out. A save still in flight settles against the record that sent it. Coming\n back restores the draft, and the load that follows merges it against the latest version, so a\n write made meanwhile is a merge or a conflict, never a loss. Unmounting drops parked drafts.\n- **A CRLF record stays CRLF.** A body whose every line break is CRLF is edited with bare LFs, as a\n textarea holds it, and every save writes CRLF back, so an edit changes only the lines it touched.\n A body with mixed line endings is written back with LFs once the person edits it.\n- **The \\`draft\\` slot belongs to the first record opened, in the first store.** Any other record,\n or the same record through another \\`records\\` adapter, opens on its own body.\n- **A new adapter object on every render** makes Editor re-subscribe and re-fetch on every render.\n Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const recordFormDocs: ComponentDocs = {\n name: 'RecordForm',\n slug: 'record-form',\n storybookPath: 'components-recordform',\n tagline: 'One record being written: a create form or an edit form, from one field list.',\n\n purpose: `\\`RecordForm\\` is one record being written: a create form or an edit form of the same collection\n\\`RecordList\\` shows, out of the same field vocabulary. It owns validation, the dirty-state guard, the\nconfirm before a delete, and where an adapter's refusal lands; the fields and the layout are the\nagent's.\n\nReach for something else when the screen is many records rather than one (\\`RecordList\\`), when the\nreader signs in or is invited (\\`Auth\\`), or when nothing is written and the record is only being read\n— a page of \\`readOnly\\` fields is a form with the controls taken out, and a document is usually the\nbetter shape for that.`,\n\n configNotes: [\n `\\`fields\\` is the whole form, in the order it is drawn. Each entry starts as the field \\`RecordList\\`\nknows — \\`{ key, label, type, format? }\\` — and adds what makes a control out of it:`,\n {\n table: {\n headings: ['Key', 'On which types', 'What it does'],\n rows: [\n ['`required`', 'all', 'Blocks submit while empty, and marks the label'],\n [\n '`readOnly`',\n 'all',\n 'Shows the value formatted, with no control and no place in a patch',\n ],\n ['`help`', 'all', 'One always-visible line under the control'],\n ['`placeholder`', 'all but `boolean`', 'Ghost text in an empty control'],\n [\n '`options`',\n '`enum` (required)',\n '`[{ id, label }]` — the reader picks the label, the record gets the id',\n ],\n [\n '`min` / `max`',\n '`number` `money` `date` `datetime`',\n 'The accepted range: a number, or an ISO string for a date',\n ],\n ['`multiline`', '`text`', 'A textarea, spanning both columns of `two-column`'],\n [\n '`users`',\n '`user`',\n '`members` is a select over `Identity.listMembers()`; `free` is a text box',\n ],\n ],\n },\n },\n `The eight types are the same eight \\`RecordList\\` renders, from one schema in \\`src/abi/fields.ts\\`, so\na field list can be written once and read by both.`,\n ],\n\n adapters: [\n {\n adapter: 'Records',\n calls: '`get(collection, id)`',\n why: 'Edit mode only, to fill the controls from the record.',\n },\n {\n adapter: 'Records',\n calls: '`create(collection, data)`',\n why: \"Create mode's submit. Every editable field goes in.\",\n },\n {\n adapter: 'Records',\n calls: '`update(collection, id, patch, { expectedVersion, versionField }?)`',\n why: \"Edit mode's submit. Only what the reader changed goes in, with the version it loaded when the record has one.\",\n },\n {\n adapter: 'Records',\n calls: '`remove(collection, id)`',\n why: 'The Delete button, after the confirm.',\n },\n {\n adapter: 'Identity',\n calls: '`listMembers()`',\n why: \"Only for a `user` field with `users: 'members'`.\",\n },\n ],\n\n adapterNotes: `RecordForm takes no other adapter. It never fetches, never routes and never reads a clock — a date\nis whatever the reader put in the control, kept as an ISO string.\n\n**Which record is a prop, not config.** \\`mode: 'edit'\\` loads the id in the \\`recordId\\` prop, because\nthe route knows it and the config does not: one config serves every ticket on the board. \\`onDone\\` is\ncalled with the stored row after a save and with \\`null\\` after a delete, and \\`onCancel\\` after the\nreader has confirmed discarding anything unsaved — where those go next is the app's to decide.\n\n**Refusals name fields.** A rejection carrying \\`fields: [{ field, message }]\\` — a\n\\`RecordRefusedError\\`, or anything shaped like one — puts each sentence under its own control and\nfocuses the first. A rejection without them shows its \\`message\\` as one line above the buttons.\n\n**A versioned record is saved against the version it was read at.** When the loaded record holds a\nnumber in \\`versionField\\` (\\`version\\` by default), \\`update\\` gets that number as \\`expectedVersion\\`\nand the patch sets the field one higher — the same contract \\`Editor\\` keeps. A store that honours it\nrejects a save that arrived second with a \\`VersionConflictError\\` carrying the record as it now\nstands. A record with no number there is saved with a plain patch.`,\n\n example: `import { RecordForm, fakeIdentity, fakeRecords } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<RecordForm\n config={{\n collection: 'jobs',\n fields: [\n { key: 'customer', label: 'Customer', type: 'text', required: true },\n { key: 'bike', label: 'Bike', type: 'text', required: true },\n { key: 'service', label: 'Service', type: 'text', required: true },\n {\n key: 'status',\n label: 'Status',\n type: 'enum',\n required: true,\n options: [\n { id: 'waiting', label: 'Waiting for parts' },\n { id: 'in progress', label: 'On the bench' },\n { id: 'ready', label: 'Ready for pickup' },\n ],\n },\n { key: 'assignee', label: 'Mechanic', type: 'user', users: 'members' },\n { key: 'date', label: 'Booked in', type: 'date', required: true },\n { key: 'quote', label: 'Quote', type: 'money', format: 'USD', min: 0, max: 5000 },\n { key: 'approved', label: 'Price approved', type: 'boolean' },\n { key: 'notes', label: 'Notes', type: 'text', multiline: true },\n ],\n mode: 'create',\n layout: 'single',\n submitLabel: 'Book the bike in',\n cancel: 'back',\n successMessage: 'Ticket written to the board.',\n }}\n adapters={{ records: fakeRecords(), identity: fakeIdentity() }}\n onDone={(row) => navigation.go(\\`/jobs/\\${String(row.id)}\\`)}\n onCancel={() => navigation.go('/jobs')}\n/>`,\n\n failureModes: `- **Invalid config.** RecordForm renders an error card instead of the form, in dev and in prod,\n naming every field that failed by its position — \\`fields.1.options\\` for an \\`enum\\` with nothing to\n pick. \\`options\\` on a non-enum, a field both \\`required\\` and \\`readOnly\\`, a \\`min\\` above its \\`max\\`,\n and \\`deleteAllowed\\` in create mode all fail the same way, before a control is drawn.\n- **Edit mode with no \\`recordId\\`** says so in place of the form. So does an id the adapter has no\n record for: the form will not open blank and quietly create a second record.\n- **The adapter refuses.** A rejection naming fields lands under them and focuses the first; one\n that names none is a single line above the buttons. Both take back the optimistic success line.\n- **Someone else saved first.** A \\`VersionConflictError\\` carrying the current record keeps every\n unsaved value on screen and lists the fields of this form that the other save changed, beside the\n reader's value for each field they changed; the version field itself is left out of that list. Nothing is written until the\n reader picks. *Keep mine* checks the form again, since the controls stay editable, then writes\n only the fields the reader changed, against the newer version, so the other save's remaining\n changes survive; if a third save got in meanwhile, the choice comes back.\n *Take theirs* drops the unsaved values and shows the stored record. Cancel asks before discarding, as\n it always does. A conflict carrying no record shows its \\`message\\` like any other rejection.\n- **The save is optimistic.** The success line appears before the adapter has answered, so a slow\n store still feels immediate. A refusal takes it back — nothing is lost, but a reader who looks\n away at the wrong moment can see the line and then the error.\n- **\\`readOnly\\` fields never reach the store.** They are not in the create data and not in the update\n patch, so a value the server owns cannot be sent back to it stale.\n- **A \\`user\\` field with \\`users: 'members'\\` and no \\`Identity\\` adapter** draws an empty select saying\n there are no members to pick from. The select writes the member's **name**, which is what the\n kit's \\`user\\` type renders; store an id instead by making it an \\`enum\\` with your own options.\n- **Dates are ISO strings**, and \\`datetime\\` is a UTC instant reshaped for \\`datetime-local\\`. What the\n reader types in the control is what the record gets, with no timezone applied on the way.\n- **Below 768px the form is one column**, whatever \\`layout\\` says, and the submit bar sticks to the\n bottom. The breakpoint is measured on the form's own container, so a form in a narrow panel is one\n column on a wide screen.\n- **Cancel with unsaved changes asks first.** With \\`cancel: 'none'\\` there is no cancel button and no\n guard: a form with no way out cannot lose anything by accident.\n- **A new adapter object on every render** makes RecordForm re-load its record on every render.\n Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const recordListDocs: ComponentDocs = {\n name: 'RecordList',\n slug: 'record-list',\n storybookPath: 'components-recordlist',\n tagline: 'Many records of one type: a table at desktop width, cards on a phone, from one config.',\n\n purpose: `\\`RecordList\\` is many records of one type on a screen: a table at desktop width, cards on a phone,\nboth from the same config. Sorting, filtering, searching and paging are its own; the fields it shows\nand the order it opens in are the agent's. It is the workhorse screen of most apps — a job board, an\norder list, a roster — and it is what goes in \\`Shell\\`'s \\`canvas\\`.\n\nReach for something else when the screen is one record rather than many (a record form), when the\nrecords are a dated sequence you read downwards (a timeline), or when the point is a total rather\nthan the rows.`,\n\n configNotes: [\n `\\`fields\\` is the whole list, in the order it is shown. Each entry is\n\\`{ key, label, type, format?, primary? }\\`:`,\n {\n table: {\n headings: ['`type`', 'Renders as', '`format` means'],\n rows: [\n ['`text`', 'The value verbatim', '—'],\n ['`number`', 'Grouped digits, right-shaped', 'How many decimal places'],\n ['`date`', 'A UTC calendar date', '`short`/`medium`/`long`/`full`'],\n ['`datetime`', 'The same, plus the time', '`short`/`medium`/`long`/`full`'],\n ['`enum`', 'A coloured chip', '—'],\n ['`boolean`', '`Yes` or `No`', '—'],\n ['`money`', 'A currency amount', 'An ISO currency code'],\n ['`user`', 'The name with an initials badge', '—'],\n ],\n },\n },\n `Those eight are the vocabulary. Any other value is a config error, not a fallback.\n\n\\`scope\\` and \\`filters\\` are different tools. \\`scope\\` is a narrowing the reader cannot undo — it is how\n\\`/today\\` shows the open tickets and \\`/jobs\\` shows all of them from one component. \\`filters\\` is a row\nof chips the reader picks from, one chip per value that has arrived.`,\n ],\n\n adapters: [\n {\n adapter: 'Records',\n calls: '`list(collection, …)`',\n why: 'Every page. The filter, the sort, the search text and the cursor go with it.',\n },\n {\n adapter: 'Records',\n calls: '`subscribe(collection)`',\n why: 'A row written by someone else. The list re-lists; it never polls.',\n },\n ],\n\n adapterNotes: `RecordList takes no other adapter. It never fetches, never routes and never reads a clock — a date\nis formatted from the record's own value, in UTC.\n\n**Opening a row is the call site's job.** \\`rowAction: 'open'\\` makes each row clickable and focusable\nand calls the \\`onOpen\\` prop with the whole record; the route that record leads to is something only\nthe app knows. With no \\`onOpen\\` the row is focusable and does nothing.\n\n**Paging is a cursor, not a page number.** \\`list\\` returns \\`{ rows, nextCursor }\\`, and \\`nextCursor\\`\ncomes back as \\`cursor\\` on the next call. \\`null\\` means that was the last page and the load-more button\ngoes away. There is no infinite scroll: a list you can reach the bottom of is a list you can put a\nfooter under.`,\n\n example: `import { RecordList, fakeRecords } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<RecordList\n config={{\n collection: 'tickets',\n fields: [\n { key: 'ticket', label: 'Ticket', type: 'text', primary: true },\n { key: 'customer', label: 'Customer', type: 'text' },\n { key: 'status', label: 'Status', type: 'enum' },\n { key: 'assignee', label: 'Mechanic', type: 'user' },\n { key: 'date', label: 'Booked in', type: 'date', format: 'medium' },\n { key: 'quote', label: 'Quote', type: 'money', format: 'USD' },\n ],\n sort: { field: 'date', direction: 'desc' },\n filters: ['status'],\n search: ['ticket', 'customer', 'bike'],\n pageSize: 25,\n emptyState: 'No tickets on the board.',\n rowAction: 'open',\n density: 'comfortable',\n }}\n adapters={{ records: fakeRecords() }}\n onOpen={(row) => navigation.go(\\`/jobs/\\${row.id}\\`)}\n/>`,\n\n failureModes: `- **Invalid config.** RecordList renders an error card instead of the list, in dev and in prod,\n naming every field that failed. A \\`type\\` outside the eight names the offending entry by its\n position — \\`fields.2.type\\` — and lists the vocabulary. A \\`filters\\` or \\`sort.field\\` key that no\n field carries fails the same way, so a typo never becomes a control that does nothing.\n- **The adapter rejects.** The list is replaced by the rejection's \\`message\\`, verbatim. Write those\n messages for the person reading them.\n- **A field key no record carries** is not an error: the cell shows an em dash. The config is the\n agent's claim about the shape of a record, and the list will not pretend to know better.\n- **Filter chips only know what has arrived.** A value that lives on page four has no chip until\n page four is loaded. Narrow with \\`scope\\`, or a bigger \\`pageSize\\`, when the whole vocabulary has to\n be there from the start.\n- **The cursor is a position, not a bookmark.** Rows inserted while the reader is on page three shift\n the pages under them, which is why a live update re-lists what is on screen rather than paging on.\n- **Below 768px there are no column headings**, and the reader sorts from a select instead. The\n breakpoint is Shell's default, and it is measured on the list's own container — a list in a narrow\n panel draws cards on a wide screen.\n- **A wide table scrolls inside its own box**, up to 70% of the viewport height, so the sticky\n heading has something to stick to and the page never scrolls sideways. Fewer columns, or\n \\`density: 'compact'\\`, is usually the better answer.\n- **Dates are UTC**, read straight off the record, so a row reads the same everywhere. For local\n time, put local time in the record.\n- **A new adapter object on every render** makes RecordList re-subscribe and re-list on every render.\n Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const reportDocs: ComponentDocs = {\n name: 'Report',\n slug: 'report',\n storybookPath: 'components-report',\n tagline: 'One dated document, rendered from markdown, printable, with the past ones under it.',\n\n purpose: `\\`Report\\` is the document an agent writes for people to read: a daily stand-up, a weekly summary, a\nmonthly pack. One record is one report — a title, a date, a markdown body — and the component\nrenders the latest one for the period it is sitting in, steps to the period before or after it, and\nprints on its own sheet. It is what goes on a \\`/report\\` route, and the same component with\n\\`showIndex\\` and \\`showNavigation\\` off is the report card on a dashboard.\n\nReach for something else when the records are rows to compare rather than prose to read\n(\\`RecordList\\`), when the reader is writing rather than reading (\\`RecordForm\\`), or when the point is\na dated sequence of short entries rather than one document.`,\n\n configNotes: [\n `\\`period\\` does three jobs at once: how much one report covers, what the arrows step by, and how\nwide the query is. It is the only field that changes what arrives.`,\n {\n table: {\n headings: ['`period`', 'The query asks for', 'The heading reads'],\n rows: [\n ['`day`', 'One ISO day', 'Thursday, 10 September 2026'],\n ['`week`', 'The seven days from Monday', '7 – 13 September 2026'],\n ['`month`', 'Every day in the calendar month', 'September 2026'],\n ],\n },\n },\n `The field names — \\`dateField\\`, \\`titleField\\`, \\`bodyField\\`, \\`statusField\\`, \\`authorField\\` — are how\none component reads a collection it did not design. Only \\`statusField\\` is optional, and leaving it\nout is what says these reports have no draft state.\n\n\\`showIndex\\`, \\`showNavigation\\` and \\`print\\` are the three pieces of chrome, and they come off\nindependently. A report on its own route wants all three; a report card inside a wider screen wants\nnone of them, because the screen already carries the navigation and the reader prints from the\nroute.`,\n ],\n\n adapters: [\n {\n adapter: 'Records',\n calls: '`list(collection, …)`',\n why: 'The latest report in the period, and the index of past ones.',\n },\n {\n adapter: 'Records',\n calls: '`get(collection, id)`',\n why: 'One report by id — the `id` prop, or a line the reader picked out of the index.',\n },\n {\n adapter: 'Records',\n calls: '`subscribe(collection)`',\n why: 'The agent regenerating the report. The document replaces itself; it never polls.',\n },\n {\n adapter: 'Clock',\n calls: '`now()`',\n why: 'Which period is the current one: where the component opens, and how far forward the arrows go.',\n },\n ],\n\n adapterNotes: `Report takes no other adapter. It never fetches, never routes, and never writes — the agent writes\nthe report through \\`Records\\` and this component reads it.\n\n**A period is an \\`any of\\`, not a range.** \\`list\\` is asked for\n\\`filter: { date: ['2026-09-07', …, '2026-09-13'] }\\` — every ISO day the period covers, spelled out.\nThat is why \\`Records\\` needs no range query and why \\`dateField\\` must hold an ISO calendar day\n(\\`2026-09-10\\`), not a timestamp. A month asks for up to 31 values in one call.\n\n**The latest report wins.** The query is sorted on \\`dateField\\` descending with a limit of one, so a\nperiod holding several reports shows the newest and the index is how the reader reaches the others.\n\n**The index is its own list, not a \\`RecordList\\`.** It is one \\`list\\` call of the twelve newest\nreports, drawn as titles and dates. \\`RecordList\\` would bring filters, chips, paging and a table\nlayout to a control that is a table of contents, and it would need a second config for a list the\nreader never sorts.\n\n**Every date is UTC**, read off the record and formatted with \\`locale\\`, so a report covers the same\nday in every timezone. \\`Clock\\` supplies only *now*.\n\n**A new adapter object on every render** makes Report re-subscribe and re-fetch on every render.\nBuild adapters once, outside render.`,\n\n slots: [\n {\n slot: 'id',\n what: 'One report by id instead of the latest of the period. The component opens on that report’s own period, so the arrows and the index carry on from where it sits.',\n },\n ],\n\n example: `import { Report, fakeClock, fakeRecords } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Report\n config={{\n collection: 'reports',\n period: 'day',\n dateField: 'date',\n titleField: 'title',\n bodyField: 'body',\n statusField: 'status',\n authorField: 'author',\n showAuthor: true,\n showIndex: true,\n showNavigation: true,\n print: true,\n emptyState: 'No report for this day.',\n locale: 'en-GB',\n }}\n adapters={{ records: fakeRecords({ reports }), clock: fakeClock() }}\n/>`,\n\n failureModes: `- **Invalid config.** Report renders an error card instead of the document, in dev and in prod,\n naming every field that failed. A \\`locale\\` no platform can format — \\`en_GB\\`, \\`english\\` — is named\n at \\`locale\\` with the shape of a tag that works, rather than throwing inside \\`Intl\\` at render time.\n- **The adapter rejects.** The document is replaced by the rejection's \\`message\\`, verbatim. Write\n those messages for the person reading them. A rejection on the *index* is swallowed: an aside that\n failed must not take down a document that arrived.\n- **A period with no report** is not an error: the \\`emptyState\\` line stands in, and the arrows stay,\n because stepping back to a period that has one is the way out.\n- **A \\`dateField\\` that is not an ISO day** matches nothing. The filter is an equality list of\n \\`YYYY-MM-DD\\` strings, so a timestamp in that field silently returns no report — put the calendar\n day in the record and the timestamp somewhere else.\n- **The next arrow stops at the current period.** \\`Clock\\` says which one that is; there is no report\n from next week, so the arrow is disabled rather than showing an empty period.\n- **The body is the markdown this kit renders**: headings, pipe tables, bullet and numbered lists,\n fenced code, inline code, bold, italic and http(s) links. Anything else falls through as the\n characters it is, which is what keeps a half-written document readable while the agent writes it.\n- **\\`sections\\` is optional metadata**, an array of strings or of \\`{ title }\\` objects. When a report\n carries it, a Contents list opens the document and scrolls to the matching heading in the body. A\n title with no matching heading scrolls nowhere.\n- **Printing needs \\`print: true\\`**, which brings both the action and the stylesheet. The chrome\n comes off the sheet, the card border goes, and headings, list items, code blocks and tables are\n kept off page breaks. With \\`print: false\\` a browser print takes the whole screen as it stands.\n- **A new adapter object on every render** makes Report re-subscribe and re-fetch on every render.\n Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const shellDocs: ComponentDocs = {\n name: 'Shell',\n slug: 'shell',\n storybookPath: 'components-shell',\n tagline:\n 'The app frame: a thin top bar, the canvas, the app’s bottom menu bar, and the chat behind a toggle.',\n\n purpose: `\\`Shell\\` is the app frame, pinned to the viewport. A thin **top bar** carries the app title and, on the\nright, icon-only buttons: the **chat toggle**, the **settings** gear (a dropdown with the theme toggle\nand whatever rows the app adds), and the **account** avatar. Along the bottom, a **menu bar** lists the\napp's own screens from \\`config.menu\\`, phone-app style: an icon over a short label, at every width.\nBetween the two is the canvas.\n\nThe chat is what the toggle shows and hides. Above the breakpoint it is a side column on \\`chatSide\\`,\ndrag-resizable, above the menu bar, and its width and open state are kept in this browser. Below\nthe breakpoint it is a sheet over the app, menu bar included: the canvas stays mounted underneath, so closing it lands you\nexactly where you were, scroll included. A route change closes the sheet, because a route change is\na screen the reader asked for. Chat hidden means the canvas has the whole width.\n\nThe frame never scrolls as a whole: the top bar and the menu bar stay put, and only the chat and\nthe canvas scroll inside themselves. Shell also owns the kit's theme — the\n\\`data-golem-theme\\` attribute on the document — from the saved choice or the OS preference.\n\nUse it once, at the root of an app. It does not render the conversation (hand that in as \\`chat\\`),\nand it does not route (hand the current screen in as \\`canvas\\`, and the screens as \\`menu\\`).`,\n\n adapters: [\n {\n adapter: 'Identity',\n calls: '`currentUser()`, `subscribe()`',\n why: \"Shows the signed-in user's name in the top bar when no `account` slot is given, and keeps it current when they sign out.\",\n },\n {\n adapter: 'Navigation',\n calls: '`current()`, `go()`, `subscribe()`',\n why: 'The title is the way home; a menu item with `href` goes there; the current route marks the current item and closes the chat sheet.',\n },\n ],\n\n adapterNotes:\n 'Shell takes no other adapter. It never fetches, never reads a router, and stores only its own chrome — chat width, chat open, theme — in this browser.',\n\n slots: [\n {\n slot: 'chat',\n what: 'The conversation. `Chat` is what belongs here; empty renders a labelled placeholder, `null` removes the chat and its toggle altogether.',\n },\n {\n slot: 'canvas',\n what: 'The screen the agent built. Empty renders a labelled placeholder.',\n },\n {\n slot: 'account',\n what: 'The right-hand end of the top bar. `Auth.AccountMenu` is what belongs here; empty falls back to the signed-in name as plain text.',\n },\n {\n slot: 'settings',\n what: 'Extra rows in the gear dropdown, under the theme toggle. Each row is a `Shell.Setting` — an icon, a short label, and `on` when it is a toggle. `Shell.Icon` has the line icons the bar uses.',\n },\n {\n slot: 'onSelect',\n what: 'Called with the item id when a menu item without `href` is tapped: the app changes its own state and passes `activeId` back.',\n },\n ],\n\n example: `import { Auth, Shell, fakeIdentity, fakeNavigation } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Shell\n config={{\n title: 'Northgate Cycles',\n menu: [\n { id: 'today', label: 'Today', icon: '🏠', href: '/today' },\n { id: 'jobs', label: 'Jobs', icon: '🔧', href: '/jobs' },\n { id: 'admin', label: 'Admin', icon: '🛠️' },\n ],\n activeId: showingAdmin ? 'admin' : undefined,\n chatSide: 'left',\n chatOpen: true,\n breakpoint: 768,\n }}\n adapters={{ identity: fakeIdentity(), navigation: fakeNavigation() }}\n chat={<AgentChat />}\n canvas={<TodayScreen />}\n account={<Auth.AccountMenu config={authConfig} adapters={{ identity, navigation }} />}\n settings={\n <Shell.Setting icon={<Shell.Icon name=\"wrench\" />} label=\"Builder\" on={builder} onClick={toggleBuilder} />\n }\n onSelect={(id) => id === 'admin' && setShowingAdmin(true)}\n/>`,\n\n failureModes: `- **Invalid config.** Shell renders an error card instead of the frame, in dev and in prod, naming\n every field that failed and the rule it broke. An unknown field is a failure too — \\`chatside\\` does\n not quietly become \\`chatSide\\`, and the retired \\`initialTab\\` is rejected.\n- **No slot.** A missing \\`chat\\` or \\`canvas\\` renders a labelled placeholder, so a half-wired app looks\n half-wired rather than broken. \\`chat={null}\\` is different: the app has no chat in this mode, so there\n is no column, no sheet and no toggle, only the canvas.\n- **An empty menu.** No bottom bar is drawn; the canvas runs to the bottom edge.\n- **A menu item with neither \\`href\\` nor an \\`onSelect\\` handler.** Tapping it does nothing. Give it one\n or the other.\n- **Identity resolves to \\`null\\`.** The top bar renders without a name. Signing out is not an error.\n- **The breakpoint is measured on the frame, not the window.** A Shell inside a narrow column gets\n the phone layout, which is usually what you want and is occasionally a surprise.\n- **The frame is \\`100dvh\\` tall, capped at its parent.** Put it at the root, or in a box with a\n height; inside an auto-height parent it is the viewport's height and the page would scroll.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const timelineDocs: ComponentDocs = {\n name: 'Timeline',\n slug: 'timeline',\n storybookPath: 'components-timeline',\n tagline: 'Dated entries read downwards, newest first: the log of what happened.',\n\n purpose: `\\`Timeline\\` is the log of what happened: dated entries read downwards, newest first, cut into days\nunder sticky headers. An entry is a short piece of markdown with an instant on it, and optionally a\nkind, a person and a file or two. Times inside today are how long ago they were — \"14 min ago\" —\nand every older entry carries its clock time.\n\nReach for something else when the records are a set rather than a sequence (\\`RecordList\\`), when the\npoint is one document rather than many entries (\\`Report\\`), or when the reader is talking to\nsomething that answers back (\\`Chat\\`).`,\n\n configNotes: [\n `\\`kindField\\` and \\`kinds\\` go together: the field says which kind an entry is, and \\`kinds\\` is the\nvocabulary it is read against. A kind's \\`tone\\` — never a colour — picks its mark:`,\n {\n table: {\n headings: ['`tone`', 'Mark', 'For'],\n rows: [\n ['`neutral`', 'A grey dot', 'What was said or noticed'],\n ['`good`', 'A green tick', 'What finished'],\n ['`warn`', 'An amber triangle', 'What is waiting or at risk'],\n ['`bad`', 'A red cross', 'What went wrong'],\n ],\n },\n },\n `Four tones and no palette, so the same kind of thing is the same colour on every screen the agent\nbuilds.\n\n\\`filters\\` and \\`search\\` are different tools. \\`filters\\` is a row of chips: \\`kind\\` offers the whole\nconfigured vocabulary, \\`actor\\` offers every person whose entries have arrived. \\`search\\` is a box\nmatching a substring against the field keys it lists — usually just the body field.\n\n\\`composer\\` is the one config that writes. On, a one-line box sits above the log; what it writes is\nthe body the reader typed, the clock's current instant in \\`dateField\\`, the signed-in member's name\nin \\`actorField\\`, and — when \\`kindField\\` is set — the kind picked from a select beside the box.`,\n ],\n\n adapters: [\n {\n adapter: 'Records',\n calls: '`list(collection, …)`',\n why: 'Every page, sorted by `dateField` descending. The chosen filters, the search text and the cursor go with it.',\n },\n {\n adapter: 'Records',\n calls: '`subscribe(collection)`',\n why: 'An entry written by someone else. The timeline re-lists; it never polls.',\n },\n {\n adapter: 'Records',\n calls: '`create(collection, data)`',\n why: 'The composer, and only the composer. Absent `composer: true` the timeline never writes.',\n },\n {\n adapter: 'Clock',\n calls: '`now()`, `timeZone()`',\n why: 'What \"Today\" and \"14 min ago\" are measured against, and which zone the days are cut in.',\n },\n {\n adapter: 'Identity',\n calls: '`currentUser()`',\n why: 'Who a composed entry is by. Only called with `composer: true`.',\n },\n {\n adapter: 'Files',\n calls: '`url(id)`',\n why: 'Turns an attachment chip into something to open. Only called when `attachmentsField` is set.',\n },\n ],\n\n adapterNotes: `Timeline takes no other adapter. It never fetches, never routes, and never reads the machine's\nclock — the time on screen is the \\`Clock\\` adapter's, which is why a frozen clock makes every\nexample read the same on every run.\n\n**\\`identity\\` and \\`files\\` are required by the config, not by the interface.** Turn \\`composer\\` on\nwithout an \\`identity\\` and entries are written unsigned; set \\`attachmentsField\\` without a \\`files\\`\nadapter and a chip says so when it is clicked, rather than failing silently.\n\n**Paging is a cursor, not a page number.** \\`list\\` returns \\`{ rows, nextCursor }\\`, and \\`nextCursor\\`\ncomes back as \\`cursor\\` on the next call. Load-more fetches *older* entries and appends them under\nwhat is already read; new entries only ever arrive at the top, through \\`subscribe\\`.`,\n\n example: `import { Timeline, fakeClock, fakeIdentity, fakeRecords } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Timeline\n config={{\n collection: 'log',\n dateField: 'at',\n bodyField: 'body',\n kindField: 'kind',\n kinds: [\n { id: 'note', label: 'Note', tone: 'neutral' },\n { id: 'done', label: 'Done', tone: 'good' },\n { id: 'parts', label: 'Parts', tone: 'warn' },\n { id: 'problem', label: 'Problem', tone: 'bad' },\n ],\n actorField: 'actor',\n attachmentsField: 'files',\n filters: ['kind', 'actor'],\n search: ['body'],\n pageSize: 25,\n composer: true,\n emptyState: 'Nothing on the log yet.',\n locale: 'en-GB',\n }}\n adapters={{ records, clock, identity, files }}\n/>`,\n\n failureModes: `- **Invalid config.** Timeline renders an error card instead of the log, in dev and in prod, naming\n every field that failed. A \\`tone\\` outside the four names the kind by its position —\n \\`kinds.1.tone\\` — and lists them. \\`kindField\\` without \\`kinds\\`, \\`kinds\\` without \\`kindField\\`, two\n kinds sharing an \\`id\\`, and a \\`filters\\` entry whose field is not set all fail the same way, so a\n chip row is never empty by surprise.\n- **The adapter rejects.** The entries are replaced by the rejection's \\`message\\`, verbatim. A\n composer write that is refused puts the message under the box and keeps what was typed.\n- **A kind the config has no word for** is not an error: the entry wears the raw value as a grey\n chip and a neutral dot. The record said something; the log will not hide it.\n- **An unreadable date** joins the day above it and shows an em dash instead of a time. Write\n \\`dateField\\` as a full ISO instant; a bare calendar date has no time to show.\n- **Days are cut in the \\`Clock\\` adapter's zone**, so \"Today\" means today where the reader is. A\n zone the platform does not know falls back to UTC rather than throwing mid-render.\n- **The actor chips only offer actors held as a name.** The chip's value is what \\`list\\` is asked to\n match, and an actor held as a record is not an equality an adapter can answer — it still shows on\n the entry, it just gets no chip.\n- **The pill counts arrivals, it does not hold them back.** A new entry is always on screen; the\n pill is only how the reader learns about it when they have scrolled away from the top.\n- **The log scrolls inside its own box**, up to 70% of the viewport height, so the day headers have\n something to stick to and the page never scrolls sideways. A shorter \\`pageSize\\` is the answer for\n a timeline sitting inside a wider screen.\n- **A new adapter object on every render** makes Timeline re-subscribe and re-list on every render.\n Build adapters once, outside render.`,\n}\n","import type { ComponentDocs } from '../../abi'\n\nexport const uploadDocs: ComponentDocs = {\n name: 'Upload',\n slug: 'upload',\n storybookPath: 'components-upload',\n tagline: 'Files and photos in, a gallery out — and a picker a composer can host.',\n\n purpose: `\\`Upload\\` is how files get into a workspace and how they are looked at afterwards. One config gives\ntwo surfaces: \\`<Upload />\\` is a drop zone, a camera button on a phone, a queue with progress and\nper-file errors, and the gallery the files land in — thumbnails for pictures, tiles for everything\nelse, a lightbox, an optional caption, and a delete that asks first. \\`<Upload.Picker />\\` is the same\npicking and the same limits with no gallery: a button a composer hosts, handing file refs back to\nwhatever is about to send them.\n\nReach for something else when the files are already somewhere and you only need to open one — a\n\\`Timeline\\` attachment chip does that with \\`Files.url\\` alone — or when what you are collecting is\nfields rather than files (\\`RecordForm\\`).`,\n\n configNotes: [\n `\\`folder\\` is the whole of the component's identity: it is what \\`upload\\`, \\`list\\` and \\`subscribe\\` are\ngiven, so two Uploads on the same folder are two views of the same files and a file dropped on one\nappears on the other.\n\nThe three limits are checked in the browser, in this order, before anything is uploaded:`,\n {\n table: {\n headings: ['Limit', 'What it refuses', 'What the row says'],\n rows: [\n [\n '`accept`',\n 'A file whose MIME type no entry matches',\n 'The type it is, and the types this upload takes',\n ],\n ['`maxSizeMb`', 'A file bigger than the limit', 'How big it is, and the limit'],\n [\n '`maxFiles`',\n 'A file past the last place in the folder',\n 'What number it would have been, and how many the folder holds',\n ],\n ],\n },\n },\n `Each refusal is a \\`FileRejectedError\\` carrying the \\`fileName\\` and a \\`reason\\` of \\`type\\`, \\`size\\` or\n\\`count\\`. The component catches its own: nothing is thrown at the caller, and the sentence goes on\nthe file's row, which is where the reader can do something about it. \\`upload\\` is never called for a\nrefused file, so an over-size photo does not leave the machine.\n\n\\`accept\\` is a list of MIME types — \\`image/jpeg\\`, \\`application/pdf\\`, or a whole group as\n\\`image/*\\`. It is not a list of extensions: \\`jpg\\` fails validation and produces the error card,\nbecause a file picker offering \\`jpg\\` matches nothing.\n\n\\`capture\\` is the phone camera. \\`environment\\` is the rear camera, which is what a photo of a bike on\na bench wants; \\`user\\` is the front one. A desktop browser ignores the attribute and offers its file\npicker, so turning it on costs a laptop nothing.\n\n\\`layout\\` decides how the gallery reads, not what it holds. \\`grid\\` draws pictures as thumbnails and\neverything else as an icon tile; \\`list\\` draws one row per file whatever it is, which is what a\nfolder of invoices wants.`,\n ],\n\n adapters: [\n {\n adapter: 'Files',\n calls: '`upload(file, { folder, onProgress })`',\n why: 'Every accepted file, one call each. `onProgress` moves the bar on that file’s row; the ref it resolves to is what the gallery and the picker then hold.',\n },\n {\n adapter: 'Files',\n calls: '`list(folder)`',\n why: 'The gallery, newest first. Called on mount and again on every change the folder reports.',\n },\n {\n adapter: 'Files',\n calls: '`subscribe(folder)`',\n why: 'A file put there by someone else, or by the other surface. Upload re-lists; it never polls.',\n },\n {\n adapter: 'Files',\n calls: '`url(ref, { thumbnail })`',\n why: 'Twice per file: the small one for the grid, the full one for the lightbox. A store with no thumbnail returns the full URL and the grid still draws.',\n },\n {\n adapter: 'Files',\n calls: '`remove(ref)`',\n why: 'Delete, and only after the reader has confirmed it. With `deleteAllowed: false` it is never called.',\n },\n {\n adapter: 'Files',\n calls: '`caption(ref, text)`',\n why: 'The line under a tile, written when the reader leaves the box. Only called with `captions: true`.',\n },\n ],\n\n adapterNotes: `Upload takes one adapter and no other. It never fetches, never routes, and never reads the clock —\n\\`uploadedAt\\` is whatever the store stamped, and it is the only thing the gallery's order depends on.\n\n**A \\`FileRef\\` is plain JSON**: an id, a name, a MIME type, a size, the folder and the instant. No\nbytes. Put one on a record, in a chat message or in a config and it survives the round trip; when\nyou want to see the file, ask \\`url\\` for a URL. Methods that take a ref take just an id too, so\nsomething holding only the id can still open and delete the file.\n\n**Progress is the adapter's to report.** A store that cannot report it calls \\`onProgress\\` once with\n\\`1\\`, and the row jumps from 0% to done rather than showing nothing. \\`fakeFiles\\` reports ten steps\nover about 600 ms so the bar is worth looking at.\n\n**The two surfaces share a config and share nothing else.** \\`Upload.Picker\\` never lists and never\ndeletes; it uploads into the same folder and hands the refs to \\`onPicked\\`, which is a per-use prop\nof the call site rather than config, because who holds the files is the composer's business.`,\n\n slots: [\n {\n slot: 'onPicked',\n what: '`Upload.Picker` only. Called with every ref the picker is holding, each time that changes — a file finishing, or a chip taken off. The parent composer keeps the list; the picker only shows it.',\n },\n ],\n\n example: `import { Upload, fakeFiles } from 'golem-ui'\nimport 'golem-ui/styles.css'\n\n;<Upload\n config={{\n folder: 'shop',\n accept: ['image/jpeg', 'image/png', 'image/webp'],\n maxFiles: 12,\n maxSizeMb: 8,\n capture: 'environment',\n captions: false,\n layout: 'grid',\n deleteAllowed: true,\n emptyState: 'No photos on this ticket yet.',\n }}\n adapters={{ files }}\n/>\n\n// The same config, as the button a composer hosts:\n;<Upload.Picker config={config} adapters={{ files }} onPicked={setStaged} />`,\n\n failureModes: `- **Invalid config.** Upload renders an error card instead of the drop zone, in dev and in prod,\n naming every field that failed. An \\`accept\\` entry that is not a MIME type is named by its\n position — \\`accept.1\\` — and told how to write it, because a picker offering \\`jpg\\` matches\n nothing and would fail silently otherwise.\n- **A file over the limits** never reaches the adapter. Its row turns red and says which limit it\n broke, in the file's own numbers; every other file in the same batch still goes up.\n- **An upload the store refuses** keeps its row, red, with the store's \\`message\\` on it verbatim.\n The rest of the batch is unaffected, and the row can be dismissed.\n- **\\`list\\` rejects.** The gallery is replaced by the rejection's \\`message\\`. The drop zone stays,\n so the reader can still try to add something.\n- **\\`url\\` rejects** for one file and that file draws its icon rather than its picture. One\n unreadable thumbnail does not empty the grid.\n- **Delete always asks.** The button turns into *Delete permanently* and *Keep it*; nothing is\n removed until the first is pressed. With \\`deleteAllowed: false\\` there is no button at all.\n- **A picture is whatever \\`contentType\\` says.** A file uploaded with an empty type — some browsers\n send none for an unusual extension — tiles as a generic file rather than a thumbnail.\n- **\\`capture\\` is a request, not a guarantee.** A desktop browser, and some mobile ones, show the\n ordinary file picker instead. Never make the camera the only way in: the file button is always\n there beside it.\n- **A new adapter object on every render** makes Upload re-subscribe and re-list on every render.\n Build adapters once, outside render.`,\n}\n","import type { AdapterDocs, ComponentDocs } from './abi'\nimport { brainAdapterDocs } from './adapters/brain.docs'\nimport { chatAdapterDocs } from './adapters/chat.docs'\nimport { clockAdapterDocs } from './adapters/clock.docs'\nimport { filesAdapterDocs } from './adapters/files.docs'\nimport { identityAdapterDocs } from './adapters/identity.docs'\nimport { navigationAdapterDocs } from './adapters/navigation.docs'\nimport { recordsAdapterDocs } from './adapters/records.docs'\nimport { brainDocs } from './components/Brain/Brain.docs'\nimport { authDocs } from './components/Auth/Auth.docs'\nimport { chatDocs } from './components/Chat/Chat.docs'\nimport { editorDocs } from './components/Editor/Editor.docs'\nimport { recordFormDocs } from './components/RecordForm/RecordForm.docs'\nimport { recordListDocs } from './components/RecordList/RecordList.docs'\nimport { reportDocs } from './components/Report/Report.docs'\nimport { shellDocs } from './components/Shell/Shell.docs'\nimport { timelineDocs } from './components/Timeline/Timeline.docs'\nimport { uploadDocs } from './components/Upload/Upload.docs'\n\n/**\n * Every exported component's page prose, in the order a reader meets them: the frame first, then\n * what goes in its slots, then the screens. The Storybook introduction and the showcase's `#/api`\n * index are both this list.\n */\nexport const componentDocs: ComponentDocs[] = [\n shellDocs,\n chatDocs,\n authDocs,\n recordListDocs,\n recordFormDocs,\n reportDocs,\n editorDocs,\n timelineDocs,\n uploadDocs,\n brainDocs,\n]\n\n/**\n * Every adapter's page prose, in the order a reader meets them: the two that carry the app's data\n * first, then who is using it, then the conversation, then the two small ones. The Storybook\n * `Adapters/Overview` page and the showcase's `#/adapters` index are both this list.\n */\nexport const adapterDocs: AdapterDocs[] = [\n recordsAdapterDocs,\n filesAdapterDocs,\n identityAdapterDocs,\n chatAdapterDocs,\n clockAdapterDocs,\n navigationAdapterDocs,\n brainAdapterDocs,\n]\n\nexport function adapterDocsFor(slug: string): AdapterDocs | undefined {\n return adapterDocs.find((docs) => docs.slug === slug)\n}\n\n/** The documented adapter a component's `adapters` row names, for the link between the two pages. */\nexport function adapterDocsNamed(name: string): AdapterDocs | undefined {\n return adapterDocs.find((docs) => docs.name === name)\n}\n\n/**\n * Every component that takes this adapter, read off the components' own adapter lists rather than\n * written down a second time. `<name>.docs.ts` carries the same list so the file reads as a whole\n * document; `adapters.docs.test.ts` fails when the two disagree.\n */\nexport function componentsUsing(adapter: string): ComponentDocs[] {\n return componentDocs.filter((docs) => docs.adapters.some((row) => row.adapter === adapter))\n}\n","import { z } from 'zod'\n\nconst roleSchema = z\n .object({\n id: z.string().min(1),\n label: z.string().min(1),\n manages: z.boolean().default(false),\n })\n .strict()\n\nconst copySchema = z\n .object({\n signInTitle: z.string().min(1).default('Sign in'),\n signUpTitle: z.string().min(1).default('Create your account'),\n hint: z.string().default(''),\n submit: z.string().min(1).default('Continue'),\n membersTitle: z.string().min(1).default('Members'),\n denied: z.string().min(1).default('This screen is not open to your role.'),\n })\n .strict()\n\nexport const authConfigSchema = z\n .object({\n workspaceName: z\n .string()\n .min(1)\n .describe(\n 'The workspace people are signing in to. Names the sign-in screen and the invite a new member opens.',\n ),\n mode: z\n .enum(['password', 'code'])\n .default('password')\n .describe(\n 'How a returning member proves who they are. `password` asks for one; `code` sends a one-time code to the address and asks for that instead.',\n ),\n allowSignUp: z\n .boolean()\n .default(true)\n .describe(\n 'Whether the sign-in screen offers a link to create an account. An invite link always leads to sign-up whatever this says.',\n ),\n inviteOnly: z\n .boolean()\n .default(false)\n .describe(\n 'Whether an account can only be created from an invite link. On, the sign-up link is hidden and the form appears only for a reader who arrived on an invite.',\n ),\n roles: z\n .array(roleSchema)\n .min(1)\n .prefault([\n { id: 'member', label: 'Member' },\n { id: 'admin', label: 'Admin', manages: true },\n ])\n .describe(\n 'Every role this workspace has, each `{ id, label, manages }`. The first is what a new member gets. A role with `manages: true` may invite people, change their role and remove them; every other role sees the same list read-only.',\n ),\n copy: copySchema\n .prefault({})\n .describe(\n 'Overrides for the labels the screens are built from: `signInTitle`, `signUpTitle`, `hint` (a line under the sign-in title, where a demo says which accounts work), `submit`, `membersTitle`, `denied`.',\n ),\n })\n .strict()\n\nexport type AuthConfig = z.output<typeof authConfigSchema>\nexport type AuthConfigInput = z.input<typeof authConfigSchema>\nexport type AuthRole = z.output<typeof roleSchema>\n","import {\n useCallback,\n useEffect,\n useState,\n type FormEvent,\n type InputHTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { IdentityAdapter, NavigationAdapter, User } from '../../adapters'\nimport { authConfigSchema, type AuthConfig, type AuthRole } from './Auth.config'\n\nexport interface AuthAdapters {\n identity: IdentityAdapter\n navigation: NavigationAdapter\n}\n\nexport interface AuthGuardSlots {\n /** Roles allowed through. Empty — or left out — means any signed-in person. */\n roles?: string[]\n /** What the allowed reader sees. */\n children?: ReactNode\n}\n\n/** Who is here, and whether we have asked yet. `ready` keeps a blank frame off the screen. */\nfunction useSession(identity: IdentityAdapter) {\n const [user, setUser] = useState<User | null>(null)\n const [ready, setReady] = useState(false)\n\n useEffect(() => {\n let live = true\n void identity.currentUser().then((next) => {\n if (!live) return\n setUser(next)\n setReady(true)\n })\n const unsubscribe = identity.subscribe(setUser)\n return () => {\n live = false\n unsubscribe()\n }\n }, [identity])\n\n return { user, ready }\n}\n\n/**\n * A token off the route — `invite` or `reset` — rather than off `window`: the app's Navigation\n * adapter is what knows how its URLs are shaped, so a hash router and a path router both land here.\n */\nfunction useRouteToken(navigation: NavigationAdapter, name: string): string | undefined {\n const [token, setToken] = useState(() => navigation.current().params[name])\n useEffect(() => navigation.subscribe((route) => setToken(route.params[name])), [navigation, name])\n return token\n}\n\nfunction roleFor(config: AuthConfig, user: User | null): AuthRole | undefined {\n return config.roles.find((role) => user?.roles.includes(role.id))\n}\n\nfunction labelForRole(config: AuthConfig, user: User): string {\n return roleFor(config, user)?.label ?? user.roles[0] ?? '—'\n}\n\nfunction canManage(config: AuthConfig, user: User | null): boolean {\n return config.roles.some((role) => role.manages && user?.roles.includes(role.id))\n}\n\n/** Adapter rejections are written for the reader, so the message is shown as it arrives. */\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : 'Something went wrong. Try again.'\n}\n\nconst fieldClass =\n 'mt-1 w-full rounded-lg border border-(--chat-line2) bg-(--chat-panel) px-3 py-2.5 text-base text-(--chat-text)'\nconst primaryClass =\n 'w-full rounded-lg bg-(--chat-text) px-4 py-2.5 text-sm font-medium text-(--chat-bg) disabled:opacity-50'\nconst quietClass =\n 'rounded-lg border border-(--chat-line2) bg-(--chat-panel) px-3 py-2 text-sm font-medium text-(--chat-text)'\n\nfunction Field({ label, ...input }: { label: string } & InputHTMLAttributes<HTMLInputElement>) {\n return (\n <label className=\"block\">\n <span className=\"text-sm font-medium text-(--chat-dim)\">{label}</span>\n <input {...input} className={fieldClass} />\n </label>\n )\n}\n\nfunction Card({ children }: { children: ReactNode }) {\n return (\n <div className=\"golem-auth flex h-full w-full items-center justify-center bg-(--chat-bg) p-4 text-(--chat-text)\">\n <div className=\"w-full max-w-sm rounded-xl border border-(--chat-line) bg-(--chat-panel) p-5 sm:p-6\">\n {children}\n </div>\n </div>\n )\n}\n\n/**\n * Sign in, sign up, and the one-time-code detour. One card with one error line: whatever the\n * adapter refused is what the reader reads.\n */\nfunction SignInScreen({ config, adapters }: { config: AuthConfig; adapters: AuthAdapters }) {\n const invite = useRouteToken(adapters.navigation, 'invite')\n const reset = useRouteToken(adapters.navigation, 'reset')\n const signUpOffered = config.allowSignUp && !config.inviteOnly\n const signUpReachable = Boolean(invite) || signUpOffered\n\n // `null` means the reader has not chosen: an invite link opens on sign-up, anything else on sign-in.\n const [chose, setChose] = useState<boolean | null>(null)\n const [name, setName] = useState('')\n const [email, setEmail] = useState('')\n const [secret, setSecret] = useState('')\n const [codeSent, setCodeSent] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [notice, setNotice] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n\n // A reset link only means something when the adapter can set a password from it.\n const resetting = Boolean(reset) && Boolean(adapters.identity.setPassword)\n const creating = chose ?? Boolean(invite)\n const creatingNow = !resetting && creating && signUpReachable\n const codeStage = !resetting && config.mode === 'code' && !creatingNow && codeSent\n\n const run = async (event: FormEvent, action: () => Promise<unknown>) => {\n event.preventDefault()\n setBusy(true)\n setError(null)\n try {\n await action()\n } catch (failure) {\n setError(messageOf(failure))\n } finally {\n setBusy(false)\n }\n }\n\n const submit = (event: FormEvent) =>\n run(event, async () => {\n if (resetting) {\n await adapters.identity.setPassword!(reset!, secret)\n setSecret('')\n setNotice('Your password is set. Sign in with it.')\n // The token is spent, so it leaves the route: a reload lands on the sign-in card.\n adapters.navigation.go(adapters.navigation.current().path)\n return\n }\n if (creatingNow) {\n await adapters.identity.signUp({\n name,\n email,\n ...(config.mode === 'password' ? { password: secret } : {}),\n ...(invite ? { invite } : {}),\n })\n return\n }\n if (config.mode === 'password') {\n await adapters.identity.signIn(email, secret)\n return\n }\n if (!codeSent) {\n await adapters.identity.requestCode(email)\n setCodeSent(true)\n return\n }\n await adapters.identity.verifyCode(email, secret)\n })\n\n const title = resetting\n ? 'Choose a new password'\n : creatingNow\n ? config.copy.signUpTitle\n : config.copy.signInTitle\n\n return (\n <Card>\n <h1\n data-golem-auth-screen={resetting ? 'reset-password' : creatingNow ? 'sign-up' : 'sign-in'}\n className=\"text-lg font-semibold\"\n >\n {title}\n </h1>\n <p className=\"mt-1 text-sm text-(--chat-dim)\">\n {invite && creatingNow\n ? `You have been invited to ${config.workspaceName}.`\n : config.workspaceName}\n </p>\n {/* The hint is about getting back in, so it stays off the sign-up form. */}\n {config.copy.hint && !creatingNow && !resetting && (\n <p className=\"mt-3 text-xs text-(--chat-dim)\">{config.copy.hint}</p>\n )}\n {notice && <p className=\"mt-3 text-sm text-(--chat-dim)\">{notice}</p>}\n\n <form className=\"mt-4 space-y-3\" onSubmit={submit}>\n {resetting ? (\n <Field\n label=\"New password\"\n type=\"password\"\n value={secret}\n autoComplete=\"new-password\"\n onChange={(event) => setSecret(event.target.value)}\n />\n ) : (\n <>\n {creatingNow && (\n <Field\n label=\"Your name\"\n value={name}\n autoComplete=\"name\"\n onChange={(event) => setName(event.target.value)}\n />\n )}\n <Field\n label=\"Email\"\n type=\"email\"\n value={email}\n autoComplete=\"email\"\n readOnly={codeStage}\n onChange={(event) => setEmail(event.target.value)}\n />\n {(creatingNow\n ? config.mode === 'password'\n : config.mode === 'password' || codeStage) && (\n <Field\n label={config.mode === 'password' ? 'Password' : 'Six-digit code'}\n type={config.mode === 'password' ? 'password' : 'text'}\n value={secret}\n autoComplete={config.mode === 'password' ? 'current-password' : 'one-time-code'}\n inputMode={config.mode === 'code' ? 'numeric' : undefined}\n onChange={(event) => setSecret(event.target.value)}\n />\n )}\n </>\n )}\n\n {codeStage && <p className=\"text-xs text-(--chat-dim)\">We sent a code to {email}.</p>}\n {error && (\n <p\n role=\"alert\"\n className=\"rounded-lg border border-(--chat-danger) px-3 py-2 text-sm text-(--chat-danger)\"\n >\n {error}\n </p>\n )}\n\n <button type=\"submit\" disabled={busy} className={primaryClass}>\n {resetting\n ? 'Set password'\n : config.mode === 'code' && !creatingNow && !codeSent\n ? 'Email me a code'\n : config.copy.submit}\n </button>\n </form>\n\n {signUpOffered && !invite && !resetting && (\n <button\n type=\"button\"\n onClick={() => {\n setChose(!creating)\n setError(null)\n }}\n className=\"mt-3 w-full text-sm text-(--chat-dim) underline\"\n >\n {creating ? 'I already have an account' : 'Create an account'}\n </button>\n )}\n {config.inviteOnly && !invite && !resetting && (\n <p className=\"mt-3 text-xs text-(--chat-dim)\">\n {config.workspaceName} is invite only. Ask someone inside for a link.\n </p>\n )}\n </Card>\n )\n}\n\nfunction useMembers(identity: IdentityAdapter, user: User | null) {\n const [members, setMembers] = useState<User[]>([])\n const reload = useCallback(() => {\n void identity.listMembers().then(setMembers)\n }, [identity])\n\n // Re-read whenever the signed-in person changes: a different role sees a different list.\n useEffect(reload, [reload, user])\n\n return { members, reload }\n}\n\nfunction MembersScreen({\n config,\n adapters,\n user,\n}: {\n config: AuthConfig\n adapters: AuthAdapters\n user: User\n}) {\n const { members, reload } = useMembers(adapters.identity, user)\n const manages = canManage(config, user)\n const [inviteRole, setInviteRole] = useState(config.roles[0]!.id)\n const [link, setLink] = useState<string | null>(null)\n const [copied, setCopied] = useState(false)\n\n const mint = async (minting: Promise<string>) => {\n const url = await minting\n setLink(url)\n // Clipboard access is a permission and a browser feature, so the URL is on screen either way.\n try {\n await navigator.clipboard?.writeText(url)\n setCopied(true)\n } catch {\n setCopied(false)\n }\n }\n\n return (\n <div\n data-golem-component=\"Auth\"\n className=\"golem-auth mx-auto w-full max-w-3xl px-4 py-5 text-(--chat-text) sm:px-6 sm:py-7\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <h1 className=\"text-xl font-semibold tracking-tight sm:text-2xl\">\n {config.copy.membersTitle}\n </h1>\n <p className=\"mt-1 text-sm text-(--chat-dim)\">\n Everyone who can open {config.workspaceName}.\n </p>\n </div>\n {manages && (\n <div className=\"flex shrink-0 flex-wrap items-center gap-2\">\n <select\n aria-label=\"Role for the invite\"\n value={inviteRole}\n onChange={(event) => setInviteRole(event.target.value)}\n className=\"rounded-lg border border-(--chat-line2) bg-(--chat-panel) px-2 py-2 text-sm text-(--chat-text)\"\n >\n {config.roles.map((role) => (\n <option key={role.id} value={role.id}>\n {role.label}\n </option>\n ))}\n </select>\n <button\n type=\"button\"\n onClick={() => void mint(adapters.identity.invite(inviteRole))}\n className={quietClass}\n >\n Invite by link\n </button>\n </div>\n )}\n </div>\n\n {link && (\n <div className=\"mt-4 rounded-xl border border-(--chat-line) bg-(--chat-panel) p-4\">\n <p className=\"text-sm font-medium\">\n Send this link. {copied ? 'It is on your clipboard.' : 'Copy it from here.'}\n </p>\n <input\n readOnly\n aria-label=\"Link to send\"\n value={link}\n onFocus={(event) => event.target.select()}\n className={`${fieldClass} font-mono text-xs`}\n />\n </div>\n )}\n\n <ul className=\"mt-6 space-y-3\">\n {members.map((member) => (\n <li\n key={member.id}\n data-golem-auth-member={member.id}\n className=\"flex flex-wrap items-center gap-3 rounded-xl border border-(--chat-line) bg-(--chat-panel) p-4\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium\">{member.name}</p>\n <p className=\"truncate text-xs text-(--chat-dim)\">{member.email}</p>\n </div>\n {manages ? (\n <select\n aria-label={`Role for ${member.name}`}\n value={roleFor(config, member)?.id ?? config.roles[0]!.id}\n onChange={(event) => {\n void adapters.identity.setRole(member.id, event.target.value).then(reload)\n }}\n className=\"rounded-lg border border-(--chat-line2) bg-(--chat-panel) px-2 py-1.5 text-sm text-(--chat-text)\"\n >\n {config.roles.map((role) => (\n <option key={role.id} value={role.id}>\n {role.label}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"shrink-0 rounded-full border border-(--chat-line2) bg-(--chat-panel2) px-2 py-0.5 text-xs font-medium text-(--chat-text)\">\n {labelForRole(config, member)}\n </span>\n )}\n {manages && adapters.identity.resetPassword && (\n <button\n type=\"button\"\n aria-label={`Reset password for ${member.name}`}\n onClick={() => {\n void mint(adapters.identity.resetPassword!(member.id))\n }}\n className=\"shrink-0 rounded-lg border border-(--chat-line2) px-3 py-1.5 text-sm\"\n >\n Reset password\n </button>\n )}\n {/* Removing yourself is how a person locks themselves out, so that button is not drawn. */}\n {manages && member.id !== user.id && (\n <button\n type=\"button\"\n aria-label={`Remove ${member.name}`}\n onClick={() => {\n void adapters.identity.removeMember(member.id).then(reload)\n }}\n className=\"shrink-0 rounded-lg border border-(--chat-line2) px-3 py-1.5 text-sm text-(--chat-danger)\"\n >\n Remove\n </button>\n )}\n </li>\n ))}\n </ul>\n </div>\n )\n}\n\nfunction AccountMenuPanel({ config, adapters }: GolemProps<AuthConfig, AuthAdapters>) {\n const { user } = useSession(adapters.identity)\n const [open, setOpen] = useState(false)\n\n if (!user) return null\n\n return (\n <div\n data-golem-component=\"Auth.AccountMenu\"\n className=\"golem-auth relative shrink-0 text-(--chat-text)\"\n >\n {/* Avatar only: the bar carries icons, and the name is in the popover. */}\n <button\n type=\"button\"\n aria-expanded={open}\n aria-label={user.name}\n title={user.name}\n onClick={() => setOpen(!open)}\n className=\"flex size-8 items-center justify-center rounded-full bg-(--chat-text) text-[11px] font-semibold text-(--chat-bg)\"\n >\n {initialsOf(user.name)}\n </button>\n {open && (\n <div className=\"absolute right-0 z-10 mt-2 w-56 rounded-xl border border-(--chat-line2) bg-(--chat-panel) p-3 shadow-lg\">\n <p className=\"truncate text-sm font-medium\">{user.name}</p>\n <p className=\"truncate text-xs text-(--chat-dim)\">{user.email}</p>\n <p className=\"mt-1 text-xs text-(--chat-dim)\">{labelForRole(config, user)}</p>\n <button\n type=\"button\"\n onClick={() => {\n setOpen(false)\n void adapters.identity.signOut()\n }}\n className={`mt-3 w-full ${quietClass}`}\n >\n Sign out\n </button>\n </div>\n )}\n </div>\n )\n}\n\nfunction initialsOf(name: string): string {\n return name\n .split(/\\s+/)\n .slice(0, 2)\n .map((part) => part[0]?.toUpperCase() ?? '')\n .join('')\n}\n\nfunction DeniedCard({\n config,\n adapters,\n user,\n roles,\n}: {\n config: AuthConfig\n adapters: AuthAdapters\n user: User\n roles: string[]\n}) {\n const allowed = roles\n .map((id) => config.roles.find((role) => role.id === id)?.label ?? id)\n .join(', ')\n\n return (\n <Card>\n <div role=\"alert\" data-golem-auth-denied=\"true\">\n <h1 className=\"text-lg font-semibold\">{config.copy.denied}</h1>\n <p className=\"mt-2 text-sm text-(--chat-dim)\">\n You are signed in as {user.name} ({labelForRole(config, user)}). This screen is for:{' '}\n {allowed}.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void adapters.identity.signOut()}\n className={`mt-4 w-full ${quietClass}`}\n >\n Sign out\n </button>\n </Card>\n )\n}\n\nfunction AuthScreen({ config, adapters }: GolemProps<AuthConfig, AuthAdapters>) {\n const { user, ready } = useSession(adapters.identity)\n if (!ready) return null\n if (!user) return <SignInScreen config={config} adapters={adapters} />\n return <MembersScreen config={config} adapters={adapters} user={user} />\n}\n\nfunction AuthGuard({\n config,\n adapters,\n roles = [],\n children,\n}: GolemProps<AuthConfig, AuthAdapters, AuthGuardSlots>) {\n const { user, ready } = useSession(adapters.identity)\n if (!ready) return null\n if (!user) return <SignInScreen config={config} adapters={adapters} />\n if (roles.length > 0 && !roles.some((role) => user.roles.includes(role))) {\n return <DeniedCard config={config} adapters={adapters} user={user} roles={roles} />\n }\n return <>{children}</>\n}\n\nconst AuthComponent = defineComponent<typeof authConfigSchema, AuthAdapters>({\n name: 'Auth',\n schema: authConfigSchema,\n render: AuthScreen,\n})\n\nconst Guard = defineComponent<typeof authConfigSchema, AuthAdapters, AuthGuardSlots>({\n name: 'Auth.Guard',\n schema: authConfigSchema,\n render: AuthGuard,\n})\n\nconst AccountMenu = defineComponent<typeof authConfigSchema, AuthAdapters>({\n name: 'Auth.AccountMenu',\n schema: authConfigSchema,\n render: AccountMenuPanel,\n})\n\n/**\n * Everything an app needs around identity. `Auth` is the screen — sign in when nobody is signed in,\n * the member list when someone is; `Auth.Guard` wraps what only certain roles may see; and\n * `Auth.AccountMenu` is the name-and-sign-out control a top bar hosts.\n */\nexport const Auth = Object.assign(AuthComponent, { Guard, AccountMenu })\n","import { Fragment, type ReactNode } from 'react'\n\n/**\n * The subset of markdown this kit writes: headings, fenced code, pipe tables, bullet and numbered\n * lists, and inline code / bold / italic / links. Everything else falls through as text. Three\n * readers share it — a chat bubble an agent is streaming into, the prose of a component's docs\n * page, and the body of a Report.\n *\n * It builds React nodes rather than an HTML string, so a message that arrives mid-stream with a\n * half-written `**` or an unclosed fence renders as the plain characters it currently is, and no\n * message can inject markup.\n */\n\nconst INLINE = /(`[^`]+`|\\*\\*[^*]+\\*\\*|\\*[^*\\n]+\\*|\\[[^\\]\\n]+\\]\\([^)\\s]+\\))/g\n\n/** Turns a non-http href into one worth linking, or `undefined` to leave it as text. */\nexport type LinkResolver = (href: string) => string | undefined\n\nfunction inlineNodes(text: string, keyPrefix: string, resolve?: LinkResolver): ReactNode[] {\n return text.split(INLINE).map((piece, index) => {\n const key = `${keyPrefix}-${index}`\n if (piece.startsWith('`') && piece.endsWith('`') && piece.length > 1) {\n return (\n <code\n key={key}\n className=\"rounded bg-black/5 dark:bg-white/10 px-1 py-0.5 font-mono text-[0.9em]\"\n >\n {piece.slice(1, -1)}\n </code>\n )\n }\n // Bold and italic recurse, so `**a `b`**` is bold text with code inside rather than backticks\n // on the screen. The inner text can hold no further `*`, so the recursion is one level deep.\n if (piece.startsWith('**') && piece.endsWith('**') && piece.length > 3) {\n return <strong key={key}>{inlineNodes(piece.slice(2, -2), key, resolve)}</strong>\n }\n if (piece.startsWith('*') && piece.endsWith('*') && piece.length > 2) {\n return <em key={key}>{inlineNodes(piece.slice(1, -1), key, resolve)}</em>\n }\n const link = /^\\[([^\\]]+)\\]\\(([^)\\s]+)\\)$/.exec(piece)\n if (link) {\n // Agent-authored URLs are untrusted; only http(s) becomes a link, anything else stays text\n // unless the reader's resolver claims it (a brain resolving a relative path).\n const external = /^https?:\\/\\//i.test(link[2]!)\n const href = external ? link[2]! : resolve?.(link[2]!)\n if (!href) return <Fragment key={key}>{piece}</Fragment>\n return (\n <a\n key={key}\n href={href}\n {...(external ? { target: '_blank', rel: 'noreferrer noopener' } : {})}\n className=\"underline\"\n >\n {link[1]}\n </a>\n )\n }\n return <Fragment key={key}>{piece}</Fragment>\n })\n}\n\ntype Block =\n | { kind: 'code'; lines: string[] }\n | { kind: 'heading'; level: number; text: string }\n | { kind: 'table'; headings: string[]; rows: string[][] }\n | { kind: 'list'; ordered: boolean; items: string[] }\n | { kind: 'para'; lines: string[] }\n\n/**\n * A heading's slug, so something outside this file can find it — a report's contents list scrolls\n * to `[data-golem-heading=\"closed-today\"]`. It is a data attribute rather than an `id` because two\n * documents on one page would otherwise mint the same id twice.\n */\nexport function headingSlug(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n}\n\n/** `| a | b |` split into its cells, with the outer pipes dropped. */\nconst cells = (line: string) =>\n line\n .trim()\n .replace(/^\\||\\|$/g, '')\n .split('|')\n .map((cell) => cell.trim())\n\n/** The `|---|:--:|` line under a table's headings. It is what tells a table from a line of prose. */\nconst isTableRule = (line: string | undefined) =>\n line !== undefined && /^\\s*\\|?\\s*:?-{2,}:?\\s*(\\|\\s*:?-{2,}:?\\s*)*\\|?\\s*$/.test(line)\n\nfunction blocksOf(text: string): Block[] {\n const blocks: Block[] = []\n let fence: Block | null = null\n // A blank line ends whatever block was open, which is what tells a wrapped bullet from the\n // paragraph after the list.\n let blank = false\n\n const lines = text.split('\\n')\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index]!\n if (line.trimStart().startsWith('```')) {\n if (fence) fence = null\n else {\n fence = { kind: 'code', lines: [] }\n blocks.push(fence)\n }\n blank = false\n continue\n }\n if (fence) {\n fence.lines.push(line)\n continue\n }\n\n if (!line.trim()) {\n blank = true\n continue\n }\n\n const heading = /^(#{1,6})\\s+(.*)$/.exec(line)\n if (heading) {\n blocks.push({ kind: 'heading', level: heading[1]!.length, text: heading[2]!.trim() })\n blank = true\n continue\n }\n\n // A row of cells is only a table when the next line is the rule; without it, a sentence with a\n // pipe in it is still a sentence.\n if (line.includes('|') && isTableRule(lines[index + 1])) {\n const table: Block = { kind: 'table', headings: cells(line), rows: [] }\n index += 1\n while (index + 1 < lines.length && lines[index + 1]!.includes('|')) {\n index += 1\n table.rows.push(cells(lines[index]!))\n }\n blocks.push(table)\n blank = true\n continue\n }\n\n const bullet = /^\\s*[-*]\\s+(.*)$/.exec(line)\n const numbered = /^\\s*\\d+[.)]\\s+(.*)$/.exec(line)\n const last = blocks[blocks.length - 1]\n\n if (bullet ?? numbered) {\n const ordered = !bullet\n const item = (bullet ?? numbered)![1]!\n if (!blank && last?.kind === 'list' && last.ordered === ordered) last.items.push(item)\n else blocks.push({ kind: 'list', ordered, items: [item] })\n blank = false\n continue\n }\n // An indented line under a bullet is that bullet wrapping, not a paragraph of its own.\n if (!blank && last?.kind === 'list') {\n const open = last.items.length - 1\n last.items[open] = `${last.items[open]} ${line.trim()}`\n continue\n }\n if (!blank && last?.kind === 'para') last.lines.push(line)\n else blocks.push({ kind: 'para', lines: [line] })\n blank = false\n }\n\n return blocks\n}\n\n/** Sized in `em`, so one container font-size sets the whole scale — a bubble's or a report's. */\nconst HEADING_SIZES = [\n 'text-[1.5em]',\n 'text-[1.3em]',\n 'text-[1.15em]',\n 'text-[1.05em]',\n 'text-[1em]',\n 'text-[1em]',\n]\n\n/**\n * `hardWraps` is the difference between the readers. A chat bubble keeps a single newline the\n * person typed; a docs paragraph wrapped at the source's column width must not show those wraps.\n */\nexport function Markdown({\n text,\n hardWraps = true,\n resolveLink,\n}: {\n text: string\n hardWraps?: boolean\n resolveLink?: LinkResolver\n}) {\n const inline = (piece: string, key: string) => inlineNodes(piece, key, resolveLink)\n return (\n <>\n {blocksOf(text).map((block, index) => {\n if (block.kind === 'code') {\n return (\n <pre\n key={index}\n className=\"my-1 overflow-x-auto rounded-md bg-neutral-900 p-2 font-mono text-xs text-neutral-100\"\n >\n <code>{block.lines.join('\\n')}</code>\n </pre>\n )\n }\n if (block.kind === 'heading') {\n const Tag = `h${block.level}` as 'h1'\n return (\n <Tag\n key={index}\n data-golem-heading={headingSlug(block.text)}\n className={`mt-4 mb-1 font-semibold first:mt-0 ${HEADING_SIZES[block.level - 1]} break-after-avoid`}\n >\n {inline(block.text, String(index))}\n </Tag>\n )\n }\n if (block.kind === 'table') {\n return (\n // Its own strip, so a wide table never makes the page or the printed sheet scroll.\n <div key={index} className=\"my-2 overflow-x-auto\">\n <table className=\"w-full border-collapse text-left text-[0.95em]\">\n <thead>\n <tr className=\"border-b border-neutral-300 dark:border-neutral-700\">\n {block.headings.map((cell, cellIndex) => (\n <th key={cellIndex} className=\"py-1.5 pr-4 font-semibold\">\n {inline(cell, `${index}-h-${cellIndex}`)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, rowIndex) => (\n <tr\n key={rowIndex}\n className=\"border-b border-neutral-200 dark:border-neutral-800 align-top last:border-0\"\n >\n {row.map((cell, cellIndex) => (\n <td key={cellIndex} className=\"py-1.5 pr-4\">\n {inline(cell, `${index}-${rowIndex}-${cellIndex}`)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n }\n if (block.kind === 'list') {\n const List = block.ordered ? 'ol' : 'ul'\n return (\n <List\n key={index}\n className={`my-1 ml-4 space-y-0.5 ${block.ordered ? 'list-decimal' : 'list-disc'}`}\n >\n {block.items.map((item, i) => (\n <li key={i}>{inline(item, `${index}-${i}`)}</li>\n ))}\n </List>\n )\n }\n return (\n <p\n key={index}\n className={`my-1 first:mt-0 last:mb-0 ${hardWraps ? 'whitespace-pre-wrap' : ''}`}\n >\n {inline(block.lines.join(hardWraps ? '\\n' : ' '), String(index))}\n </p>\n )\n })}\n </>\n )\n}\n","import { useEffect, useState, type RefObject } from 'react'\n\nconst windowWidth = () =>\n typeof window === 'undefined' ? Number.POSITIVE_INFINITY : window.innerWidth\n\n/**\n * The breakpoint is a config value, not a stylesheet constant, so the layout cannot be picked by a\n * CSS media query — the frame measures itself instead. Measuring the element rather than the\n * viewport means an embedded Shell in a narrow column gets the mobile layout too.\n *\n * Falls back to the viewport where there is no ResizeObserver, or before the first measurement\n * (jsdom, server rendering, the first paint).\n */\nexport function useContainerWidth(ref: RefObject<HTMLElement | null>): number {\n const [measured, setMeasured] = useState(0)\n const [viewport, setViewport] = useState(windowWidth)\n\n useEffect(() => {\n const element = ref.current\n if (element && typeof ResizeObserver !== 'undefined') {\n const observer = new ResizeObserver((entries) => {\n setMeasured(entries[0]?.contentRect.width ?? 0)\n })\n observer.observe(element)\n return () => observer.disconnect()\n }\n return undefined\n }, [ref])\n\n useEffect(() => {\n const onResize = () => setViewport(windowWidth())\n onResize()\n window.addEventListener('resize', onResize)\n return () => window.removeEventListener('resize', onResize)\n }, [])\n\n return measured > 0 ? measured : viewport\n}\n","import { z } from 'zod'\n\nexport const brainConfigSchema = z\n .object({\n title: z\n .string()\n .min(1)\n .default('Knowledge')\n .describe('Heading over the folder tree. Name the brain as the reader knows it.'),\n openLocation: z\n .string()\n .regex(/^[^#\\s]+(#L\\d+(-L\\d+)?)?$/, 'a path, optionally followed by #L<start>-L<end>')\n .optional()\n .describe(\n 'A source location, `path#L<start>-L<end>`, to open and highlight. Change it and the reader opens the file and scrolls the range into view; omit it and the root index.md opens.',\n ),\n })\n .strict()\n\nexport type BrainConfig = z.output<typeof brainConfigSchema>\nexport type BrainConfigInput = z.input<typeof brainConfigSchema>\n","import { useEffect, useLayoutEffect, useRef, useState, type ReactNode } from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport { parseLocation, type BrainAdapter, type BrainEntry, type BrainHit } from '../../adapters'\nimport { Markdown } from '../../lib/markdown'\nimport { useContainerWidth } from '../../lib/use-container-width'\nimport { brainConfigSchema, type BrainConfig } from './Brain.config'\n\nexport interface BrainAdapters {\n brain: BrainAdapter\n}\n\nexport interface BrainSlots {\n /** Rendered at the right-hand end of the document header: an Edit button, a share button. */\n toolbar?: ReactNode\n}\n\n/** Below this container width the reader is one screen at a time: the list, then the document. */\nconst PHONE = 768\nconst BRAIN_LINK = '#brain:'\nconst ROOT = 'index.md'\n\n/** One open document on the stack. A range comes from a citation or a search hit. */\ninterface Frame {\n path: string\n start?: number\n end?: number\n}\n\nconst same = (a: Frame | undefined, b: Frame) =>\n a?.path === b.path && a.start === b.start && a.end === b.end\nconst locationOf = (f: Frame) =>\n f.start === undefined ? f.path : `${f.path}#L${f.start}-L${f.end ?? f.start}`\n\nconst name = (path: string) => path.split('/').pop()!.replace(/\\.md$/, '')\n\n/** `../suppliers.md` seen from `workshop/fork-service.md` → `suppliers.md`. */\nfunction resolvePath(from: string, relative: string): string {\n const parts = from.split('/').slice(0, -1)\n for (const part of relative.split('/')) {\n if (part === '..') parts.pop()\n else if (part !== '.' && part) parts.push(part)\n }\n return parts.join('/')\n}\n\n/** The `key: value` lines of the front matter, and the line the body starts on. */\nfunction frontMatter(lines: string[]): { meta: Record<string, string>; body: number } {\n if (lines[0] !== '---') return { meta: {}, body: 0 }\n const close = lines.indexOf('---', 1)\n if (close < 0) return { meta: {}, body: 0 }\n const meta: Record<string, string> = {}\n for (const line of lines.slice(1, close)) {\n const m = /^(\\w[\\w-]*):\\s*(.*)$/.exec(line)\n if (m) meta[m[1]!] = m[2]!.replace(/^[\"']|[\"']$/g, '')\n }\n return { meta, body: close + 1 }\n}\n\nfunction Skeleton({ rows }: { rows: number }) {\n return (\n <div aria-hidden=\"true\" className=\"space-y-2 py-1\">\n {Array.from({ length: rows }, (_, i) => (\n <div\n key={i}\n className=\"h-3 animate-pulse rounded bg-(--chat-panel2)\"\n style={{ width: `${[85, 60, 72, 45][i % 4]}%` }}\n />\n ))}\n </div>\n )\n}\n\nfunction Tree({\n dir,\n adapter,\n open,\n onOpen,\n version,\n}: {\n dir?: string\n adapter: BrainAdapter\n open?: string\n onOpen: (path: string) => void\n version: number\n}) {\n const [entries, setEntries] = useState<BrainEntry[] | null>(null)\n const [collapsed, setCollapsed] = useState<Record<string, boolean>>({})\n\n useEffect(() => {\n let live = true\n void adapter.list(dir).then((next) => live && setEntries(next))\n return () => {\n live = false\n }\n }, [adapter, dir, version])\n\n if (!entries) return dir ? null : <Skeleton rows={4} />\n if (!entries.length && !dir)\n return <p className=\"px-2 py-3 text-sm text-(--chat-faint)\">Nothing written here yet.</p>\n\n return (\n <ul className={dir ? 'ml-2.5 border-l border-(--chat-line) pl-1' : ''}>\n {entries.map((entry) =>\n entry.kind === 'dir' ? (\n <li key={entry.path}>\n <button\n type=\"button\"\n onClick={() => setCollapsed((c) => ({ ...c, [entry.path]: !c[entry.path] }))}\n className=\"flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-left text-sm text-(--chat-dim) hover:bg-(--chat-panel2)\"\n >\n <span aria-hidden=\"true\" className=\"w-3 text-center text-[10px] text-(--chat-faint)\">\n {collapsed[entry.path] ? '▸' : '▾'}\n </span>\n <span className=\"truncate\">{name(entry.path)}</span>\n </button>\n {!collapsed[entry.path] && (\n <Tree\n dir={entry.path}\n adapter={adapter}\n open={open}\n onOpen={onOpen}\n version={version}\n />\n )}\n </li>\n ) : (\n <li key={entry.path}>\n <button\n type=\"button\"\n data-golem-brain-file={entry.path}\n aria-current={entry.path === open ? 'page' : undefined}\n onClick={() => onOpen(entry.path)}\n className={`flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-left text-sm hover:bg-(--chat-panel2) ${\n entry.path === open\n ? 'bg-(--chat-panel2) font-medium text-(--chat-text)'\n : 'text-(--chat-dim)'\n }`}\n >\n <span aria-hidden=\"true\" className=\"w-3 text-center text-[10px] text-(--chat-faint)\">\n {entry.path === open ? '●' : '·'}\n </span>\n <span className=\"truncate\">{name(entry.path)}</span>\n </button>\n </li>\n ),\n )}\n </ul>\n )\n}\n\nfunction Hits({\n query,\n adapter,\n onOpen,\n}: {\n query: string\n adapter: BrainAdapter\n onOpen: (frame: Frame) => void\n}) {\n // Hits are kept with the query they answer, so a new query shows the skeleton, not stale hits.\n const [found, setFound] = useState<{ query: string; hits: BrainHit[] }>()\n const hits = found?.query === query ? found.hits : null\n useEffect(() => {\n let live = true\n const timer = setTimeout(\n () => void adapter.search(query).then((hits) => live && setFound({ query, hits })),\n 150,\n )\n return () => {\n live = false\n clearTimeout(timer)\n }\n }, [adapter, query])\n\n if (!hits) return <Skeleton rows={3} />\n if (!hits.length)\n return (\n <p className=\"px-2 py-3 text-sm text-(--chat-faint)\">No line mentions “{query}”.</p>\n )\n return (\n <ul role=\"list\" data-golem-brain-hits=\"\">\n {hits.map((hit) => (\n <li key={`${hit.path}:${hit.line}`}>\n <button\n type=\"button\"\n onClick={() => onOpen({ path: hit.path, start: hit.line, end: hit.line })}\n className=\"w-full rounded-md px-2 py-1.5 text-left hover:bg-(--chat-panel2)\"\n >\n <span className=\"block truncate text-sm text-(--chat-text)\">{hit.excerpt}</span>\n <span className=\"block truncate font-mono text-[11px] text-(--chat-faint)\">\n {hit.path}:{hit.line}\n </span>\n </button>\n </li>\n ))}\n </ul>\n )\n}\n\n/**\n * The file in three runs of markdown — before, the cited range, after — so the range wears a\n * highlight at exactly the lines cited. Line numbers count from line 1 of the file, front matter\n * included, which the reader shows as a chip and a subtitle rather than as YAML.\n */\nfunction Reader({ text, frame }: { text: string; frame: Frame }) {\n const marked = useRef<HTMLDivElement>(null)\n const { path, start, end } = frame\n // A relative link in the bundle opens that file in this reader; the click handler on the article\n // reads the path back off the href.\n const resolveLink = (href: string) =>\n /^[a-z]+:/i.test(href) ? undefined : BRAIN_LINK + resolvePath(path, href.split('#')[0]!)\n const lines = text.split('\\n')\n const { meta, body } = frontMatter(lines)\n\n // Scrolls the reader's own box, not the page: a docs page hosting this component stays put.\n useEffect(() => {\n const mark = marked.current\n const box = mark?.closest('article')\n if (!mark || !box) return\n const offset = mark.getBoundingClientRect().top - box.getBoundingClientRect().top\n box.scrollTop += offset - box.clientHeight / 2 + mark.clientHeight / 2\n }, [text, start, end])\n\n const run = (from: number, to: number, key: string) =>\n to > from ? (\n <div key={key}>\n <Markdown\n text={lines.slice(from, to).join('\\n')}\n hardWraps={false}\n resolveLink={resolveLink}\n />\n </div>\n ) : null\n\n const head = (meta.type || meta.description) && (\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">\n {meta.type && (\n <span\n data-golem-brain-type={meta.type}\n className=\"rounded-full border border-(--chat-line2) bg-(--chat-panel2) px-2 py-0.5 font-mono text-[11px] text-(--chat-dim)\"\n >\n {meta.type}\n </span>\n )}\n {meta.description && <span className=\"text-sm text-(--chat-dim)\">{meta.description}</span>}\n </div>\n )\n\n if (start === undefined || end === undefined)\n return (\n <>\n {head}\n {run(body, lines.length, 'all')}\n </>\n )\n const from = Math.max(body, start - 1)\n const to = Math.min(lines.length, end)\n return (\n <>\n {head}\n {run(body, from, 'before')}\n <div\n ref={marked}\n data-golem-brain-highlight={`L${start}-L${end}`}\n className=\"-mx-3 my-1 rounded-md border-l-4 border-(--chat-accent) bg-(--chat-accent-soft) px-3 py-1\"\n >\n <Markdown\n text={lines.slice(from, to).join('\\n')}\n hardWraps={false}\n resolveLink={resolveLink}\n />\n </div>\n {run(to, lines.length, 'after')}\n </>\n )\n}\n\nconst BackIcon = () => (\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M10 3L5 8l5 5\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" />\n </svg>\n)\n\nconst SearchIcon = () => (\n <svg\n aria-hidden=\"true\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n className=\"pointer-events-none absolute top-1/2 left-2.5 -translate-y-1/2 text-(--chat-faint)\"\n >\n <circle cx=\"7\" cy=\"7\" r=\"4.5\" stroke=\"currentColor\" strokeWidth=\"1.6\" />\n <path d=\"M10.5 10.5L14 14\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" />\n </svg>\n)\n\nfunction BrainPanel({\n config,\n adapters,\n toolbar,\n}: GolemProps<BrainConfig, BrainAdapters, BrainSlots>) {\n const root = useRef<HTMLDivElement>(null)\n const article = useRef<HTMLElement>(null)\n const phone = useContainerWidth(root) < PHONE\n\n // The documents opened so far, newest last. Empty on a phone is the list screen; on a desktop\n // it is the root index. Scroll offsets are kept beside it so back lands where the reader was.\n const [stack, setStack] = useState<Frame[]>(() =>\n config.openLocation ? [parseLocation(config.openLocation)] : [],\n )\n const scrolls = useRef<number[]>([])\n const [version, setVersion] = useState(0)\n // The loaded document is kept with what it answers, so a new path shows the skeleton first.\n const [doc, setDoc] = useState<{ key: string; text?: string; error?: string }>()\n const [query, setQuery] = useState('')\n\n // A new location wins over whatever the reader opened: state adjusted during render, so the\n // reader never paints the old file first.\n const [seenLocation, setSeenLocation] = useState(config.openLocation)\n if (seenLocation !== config.openLocation) {\n setSeenLocation(config.openLocation)\n // The app echoing back what `open` just reported is the frame already on top: no push.\n const next = config.openLocation && parseLocation(config.openLocation)\n if (next && !same(stack[stack.length - 1], next)) setStack((s) => [...s, next])\n }\n\n const frame: Frame | undefined = stack[stack.length - 1] ?? (phone ? undefined : { path: ROOT })\n const open = frame?.path\n\n const push = (next: Frame) => {\n scrolls.current[stack.length] = article.current?.scrollTop ?? 0\n setStack((s) => [...s, next])\n adapters.brain.open?.(locationOf(next))\n }\n const back = () => {\n setStack((s) => s.slice(0, -1))\n adapters.brain.open?.(locationOf(stack[stack.length - 2] ?? { path: ROOT }))\n }\n\n useEffect(() => adapters.brain.subscribe(() => setVersion((v) => v + 1)), [adapters.brain])\n\n const key = `${open}@${version}`\n const { text, error } = doc?.key === key ? doc : {}\n useEffect(() => {\n if (!open) return\n let live = true\n const load = open === ROOT ? adapters.brain.index() : adapters.brain.read(open)\n load\n .then((text) => live && setDoc({ key, text }))\n .catch((cause: unknown) => {\n if (live)\n setDoc({\n key,\n error: cause instanceof Error ? cause.message : `Could not open ${open}.`,\n })\n })\n return () => {\n live = false\n }\n }, [adapters.brain, open, key])\n\n // Back restores the offset saved on push; a range does its own scrolling in Reader.\n const depth = stack.length\n useLayoutEffect(() => {\n if (text !== undefined && article.current && frame?.start === undefined)\n article.current.scrollTop = scrolls.current[depth] ?? 0\n }, [text, depth, frame?.start])\n\n const onArticleClick = (event: React.MouseEvent) => {\n const href = (event.target as HTMLElement).closest('a')?.getAttribute('href') ?? ''\n if (!href.startsWith(BRAIN_LINK)) return\n event.preventDefault()\n push({ path: href.slice(BRAIN_LINK.length) })\n }\n\n const list = (\n <nav\n aria-label={config.title}\n data-golem-brain-list=\"\"\n className={`flex min-h-0 flex-col bg-(--chat-panel) ${\n phone ? 'h-full w-full' : 'w-64 shrink-0 border-r border-(--chat-line)'\n }`}\n >\n <header className=\"shrink-0 space-y-2 border-b border-(--chat-line) px-3 py-2.5\">\n <h2 className=\"truncate text-sm font-semibold text-(--chat-text)\">{config.title}</h2>\n <div className=\"relative\">\n <SearchIcon />\n <input\n type=\"search\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={(event) => event.key === 'Escape' && setQuery('')}\n placeholder=\"Search\"\n aria-label=\"Search\"\n className=\"w-full rounded-lg border border-(--chat-line2) bg-(--chat-bg) py-1.5 pr-2 pl-8 text-sm text-(--chat-text) placeholder:text-(--chat-faint) focus:border-(--chat-accent) focus:outline-none\"\n />\n </div>\n </header>\n <div className=\"min-h-0 flex-1 overflow-y-auto px-1.5 py-1.5\">\n {query.trim() ? (\n <Hits query={query.trim()} adapter={adapters.brain} onOpen={push} />\n ) : (\n <Tree\n adapter={adapters.brain}\n open={open}\n onOpen={(path) => push({ path })}\n version={version}\n />\n )}\n </div>\n </nav>\n )\n\n const document = frame && (\n <section\n data-golem-brain-document={frame.path}\n className={`flex min-h-0 min-w-0 flex-col bg-(--chat-bg) ${\n phone ? 'absolute inset-0 animate-[golem-chat-in_160ms_ease-out]' : 'flex-1'\n }`}\n >\n <header className=\"flex shrink-0 items-center gap-2 border-b border-(--chat-line) bg-(--chat-panel) px-2 py-2\">\n {phone ? (\n <button\n type=\"button\"\n onClick={back}\n aria-label=\"Back\"\n className=\"flex shrink-0 items-center gap-0.5 rounded-md py-1 pr-2 pl-1 text-sm text-(--chat-accent) hover:bg-(--chat-panel2)\"\n >\n <BackIcon />\n {depth > 1 ? name(stack[depth - 2]!.path) : config.title}\n </button>\n ) : null}\n <span className=\"min-w-0 flex-1 truncate px-1 font-mono text-xs text-(--chat-dim)\">\n {frame.path}\n </span>\n {toolbar}\n </header>\n <article\n ref={article}\n className=\"min-h-0 flex-1 overflow-y-auto px-5 py-4 text-[15px] leading-7 md:px-8 md:py-6\"\n onClick={onArticleClick}\n >\n <div className=\"mx-auto max-w-[70ch]\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-(--chat-danger)\">\n {error}\n </p>\n ) : text === undefined ? (\n <Skeleton rows={6} />\n ) : (\n <Reader key={`${depth}:${frame.path}`} text={text} frame={frame} />\n )}\n </div>\n </article>\n </section>\n )\n\n return (\n <div\n ref={root}\n data-golem-component=\"Brain\"\n data-golem-brain-layout={phone ? 'phone' : 'desktop'}\n className=\"golem-chat relative flex h-full min-h-0 w-full overflow-hidden bg-(--chat-bg) text-(--chat-text)\"\n >\n {list}\n {document}\n </div>\n )\n}\n\nexport const Brain = defineComponent<typeof brainConfigSchema, BrainAdapters, BrainSlots>({\n name: 'Brain',\n schema: brainConfigSchema,\n render: BrainPanel,\n})\n","import { z } from 'zod'\n\nexport const chatConfigSchema = z\n .object({\n placeholder: z\n .string()\n .min(1)\n .default('Message the agent…')\n .describe('Ghost text in the empty composer. Say what this agent is good for.'),\n emptyState: z\n .string()\n .min(1)\n .default('No messages yet.')\n .describe('One line shown in place of the message list before the first message exists.'),\n agentName: z\n .string()\n .min(1)\n .default('Agent')\n .describe('Label above each agent bubble, and the name in the thinking indicator.'),\n userName: z\n .string()\n .min(1)\n .default('You')\n .describe('Label above each of the reader’s own bubbles.'),\n markdown: z\n .boolean()\n .default(true)\n .describe(\n 'Whether agent messages render as markdown — bold, lists, links, code. Turn it off for an agent that answers in plain prose, and the text renders verbatim.',\n ),\n showTimestamps: z\n .boolean()\n .default(false)\n .describe('Whether each bubble carries the time it was sent, as HH:MM in UTC.'),\n maxComposerLines: z\n .number()\n .int()\n .min(1)\n .max(20)\n .default(6)\n .describe(\n 'How tall the composer may grow as the reader types. Past it the composer stops growing and scrolls.',\n ),\n })\n .strict()\n\nexport type ChatConfig = z.output<typeof chatConfigSchema>\nexport type ChatConfigInput = z.input<typeof chatConfigSchema>\n","import { useEffect, useLayoutEffect, useRef, useState, type ReactNode } from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { ChatAdapter, ChatAttachment, ChatCommand, ChatMessage } from '../../adapters'\nimport { chatConfigSchema, type ChatConfig } from './Chat.config'\nimport { Markdown } from '../../lib/markdown'\n\nexport interface ChatAdapters {\n chat: ChatAdapter\n}\n\nexport interface ChatSlots {\n /**\n * Replaces the built-in attach button and the staged-chip row above the composer. It is handed\n * `stage`, which is what the composer sends with the next message; `Upload.Picker` is what\n * belongs here, so an attachment comes from a real upload rather than a name and a size.\n */\n attach?: (stage: (attachments: ChatAttachment[]) => void) => ReactNode\n}\n\n/** How close to the bottom still counts as \"reading the newest message\". */\nconst PIN_SLACK = 48\n\n/** A composer line that is a slash command: one line, leading `/`. */\nconst SLASH_LINE = /^\\/[^\\n]*$/\n/** The command stage: one `/` token, no space yet. */\nconst CMD_RE = /^\\/\\S*$/\n/** The argument stage: a complete name, whitespace, then the argument. A newline ends the picker. */\nconst ARG_RE = /^(\\/\\S+)[^\\S\\n]+([^\\n]*)$/\n\ninterface SlashMatch {\n name: string\n description: string\n /** The whole composer value a pick produces. Ends in a space when the command still wants its argument. */\n insert: string\n}\n\n/**\n * What the picker shows for a composer value, after Bridge Commander's `slash.js`. Two stages: the\n * command name, then, for a command with `args`, its values. Everything after the name is one\n * argument matched whole, so a value with spaces stays reachable.\n */\nfunction slashMatches(value: string, commands: ChatCommand[]): SlashMatch[] {\n if (CMD_RE.test(value))\n return commands\n .filter((c) => c.name.startsWith(value))\n .map((c) => ({\n name: c.name,\n description: c.description,\n insert: c.name + (c.args?.length ? ' ' : ''),\n }))\n const m = ARG_RE.exec(value)\n const args = m && commands.find((c) => c.name === m[1])?.args\n if (!m || !args?.length) return []\n const q = m[2]!.toLowerCase()\n return args\n .filter((a) => a.value.toLowerCase().startsWith(q))\n .map((a) => ({ name: a.value, description: a.description, insert: `${m[1]} ${a.value}` }))\n}\n\nfunction useConversation(adapter: ChatAdapter): { messages: ChatMessage[]; loadError: string } {\n const [messages, setMessages] = useState<ChatMessage[]>([])\n const [loadError, setLoadError] = useState<{ adapter: ChatAdapter; message: string } | null>(null)\n\n useEffect(() => {\n let live = true\n let receivedUpdate = false\n const unsubscribe = adapter.subscribe((next) => {\n receivedUpdate = true\n setLoadError(null)\n setMessages(next)\n })\n void adapter\n .history()\n .then((next) => {\n if (live && !receivedUpdate) {\n setLoadError(null)\n setMessages(next)\n }\n })\n .catch((error: unknown) => {\n if (live)\n setLoadError({\n adapter,\n message: error instanceof Error ? error.message : 'Conversation could not be loaded.',\n })\n })\n return () => {\n live = false\n unsubscribe()\n }\n }, [adapter])\n\n return { messages, loadError: loadError?.adapter === adapter ? loadError.message : '' }\n}\n\n/**\n * The message list follows the newest message only while the reader is already at the bottom.\n * Scroll up to read something and the list stays where it was put, however fast the stream runs.\n */\nfunction useStickToBottom(dependency: unknown) {\n const feed = useRef<HTMLDivElement>(null)\n const pinned = useRef(true)\n\n const onScroll = () => {\n const element = feed.current\n if (!element) return\n pinned.current = element.scrollHeight - element.scrollTop - element.clientHeight < PIN_SLACK\n }\n\n useLayoutEffect(() => {\n const element = feed.current\n if (element && pinned.current) element.scrollTop = element.scrollHeight\n }, [dependency])\n\n return { feed, onScroll }\n}\n\n/** Timestamps are read straight off the ISO string, so a bubble reads the same in every timezone. */\nfunction hhmm(at: string): string {\n const match = /T(\\d{2}:\\d{2})/.exec(at)\n return match ? match[1]! : ''\n}\n\nfunction sizeLabel(bytes: number | undefined): string {\n if (bytes == null) return ''\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction AttachmentChips({\n attachments,\n onRemove,\n}: {\n attachments: ChatAttachment[]\n onRemove?: (id: string) => void\n}) {\n return (\n <div className=\"mt-1.5 flex flex-wrap gap-1.5\">\n {attachments.map((file) => (\n <span\n key={file.id}\n data-golem-chat-attachment={file.name}\n className=\"inline-flex max-w-full items-center gap-1 rounded-full border border-(--chat-line2) bg-(--chat-panel2) px-2 py-0.5 text-xs text-(--chat-dim)\"\n >\n <span aria-hidden=\"true\">📄</span>\n <span className=\"truncate\">{file.name}</span>\n {file.size != null && <span className=\"shrink-0 opacity-60\">{sizeLabel(file.size)}</span>}\n {onRemove && (\n <button\n type=\"button\"\n onClick={() => onRemove(file.id)}\n aria-label={`Remove ${file.name}`}\n className=\"shrink-0 opacity-60 hover:opacity-100\"\n >\n ×\n </button>\n )}\n </span>\n ))}\n </div>\n )\n}\n\n/** `notes/tyres.md#L3-L5` → `tyres.md L3-5`: the chip's label. */\nfunction sourceLabel(location: string): string {\n const [path = '', range = ''] = location.split('#')\n return `${path.split('/').pop()}${range ? ` ${range.replace('-L', '-')}` : ''}`\n}\n\nfunction Bubble({\n message,\n config,\n onRetry,\n onOpenSource,\n}: {\n message: ChatMessage\n config: ChatConfig\n onRetry?: (messageId: string) => void\n onOpenSource?: (location: string) => void\n}) {\n const mine = message.role === 'user'\n const [copied, setCopied] = useState(false)\n if (message.role === 'system')\n return (\n <div\n data-golem-chat-message=\"system\"\n className=\"rounded-lg border border-dashed border-(--chat-line2) px-3 py-1.5 text-center font-mono text-xs whitespace-pre-wrap text-(--chat-dim) break-words\"\n >\n {message.text}\n </div>\n )\n const pending = message.delivery === 'pending'\n const failed = message.delivery === 'failed'\n const copy = () => {\n void navigator.clipboard?.writeText(message.text).then(() => {\n setCopied(true)\n setTimeout(() => setCopied(false), 1200)\n })\n }\n return (\n <div className={`flex flex-col ${mine ? 'items-end' : 'items-start'}`}>\n <div\n data-golem-chat-message={message.role}\n data-streaming={message.streaming ? 'true' : undefined}\n data-delivery={message.delivery}\n className={`group relative max-w-[88%] min-w-0 rounded-xl border px-[13px] py-[9px] text-[15px] leading-normal break-words animate-[golem-chat-in_180ms_ease-out] ${\n mine\n ? 'rounded-br-[4px] border-(--chat-user-line) bg-(--chat-user)'\n : 'rounded-bl-[4px] border-(--chat-line) bg-(--chat-panel)'\n } ${pending ? 'opacity-55' : ''} ${failed ? 'border-(--chat-danger)' : ''}`}\n >\n {/* The reader's own text is never markdown: they typed characters, not a document. */}\n {config.markdown && !mine ? (\n <Markdown text={message.text} />\n ) : (\n <p className=\"whitespace-pre-wrap\">{message.text}</p>\n )}\n {message.streaming && message.text && (\n <span className=\"ml-0.5 inline-block h-3.5 w-1.5 translate-y-0.5 animate-pulse bg-current align-baseline\" />\n )}\n {message.attachments?.length ? <AttachmentChips attachments={message.attachments} /> : null}\n {message.sources?.length ? (\n <div className=\"mt-1.5 flex flex-wrap gap-1.5\">\n {message.sources.map((location) => (\n <button\n key={location}\n type=\"button\"\n data-golem-chat-source={location}\n title={location}\n onClick={() => onOpenSource?.(location)}\n className=\"inline-flex max-w-full items-center gap-1 rounded-full border border-(--chat-line2) bg-(--chat-panel2) px-2 py-0.5 font-mono text-[11px] text-(--chat-dim) hover:border-(--chat-accent) hover:text-(--chat-text)\"\n >\n <span aria-hidden=\"true\">§</span>\n <span className=\"truncate\">{sourceLabel(location)}</span>\n </button>\n ))}\n </div>\n ) : null}\n {config.showTimestamps && (\n <span className=\"mt-1 block font-mono text-[11px] text-(--chat-faint)\">\n {mine ? config.userName : config.agentName} · {hhmm(message.at)}\n </span>\n )}\n {/* The copy chip shows on hover (always, faintly, on touch), and confirms with a tick. */}\n {message.text && (\n <button\n type=\"button\"\n onClick={copy}\n title=\"Copy message text\"\n aria-label=\"Copy message text\"\n className={`absolute right-1 bottom-[3px] rounded-md border px-[5px] py-px text-xs leading-none transition-opacity focus-visible:opacity-85 group-hover:opacity-85 hover:opacity-100 [@media(hover:none)]:opacity-60 ${\n copied\n ? 'border-(--chat-ok) text-(--chat-ok) opacity-100'\n : 'border-(--chat-line) bg-(--chat-panel2) text-(--chat-dim) opacity-0'\n }`}\n >\n {copied ? '✓' : '⧉'}\n </button>\n )}\n </div>\n {mine && pending && (\n <span role=\"status\" className=\"mt-0.5 px-1 font-mono text-[11px] text-(--chat-faint)\">\n Sending\n </span>\n )}\n {mine && failed && (\n <span className=\"mt-0.5 flex items-center gap-2 px-1 text-xs text-(--chat-danger)\">\n <span role=\"status\">Not sent</span>\n {onRetry && (\n <button type=\"button\" className=\"underline\" onClick={() => onRetry(message.id)}>\n Retry\n </button>\n )}\n </span>\n )}\n </div>\n )\n}\n\nfunction Thinking({ name, onStop }: { name: string; onStop?: () => Promise<void> }) {\n const [stopping, setStopping] = useState(false)\n return (\n <div\n data-golem-chat-thinking=\"true\"\n role=\"status\"\n className=\"flex w-fit items-center gap-1 rounded-xl rounded-bl-[4px] border border-(--chat-line) bg-(--chat-panel) px-[13px] py-[9px] animate-[golem-chat-in_180ms_ease-out]\"\n >\n {[0, 200, 400].map((delay) => (\n <span\n key={delay}\n aria-hidden=\"true\"\n style={{ animationDelay: `${delay}ms` }}\n className=\"size-1.5 rounded-full bg-(--chat-dim) animate-[golem-chat-blink_1.2s_infinite]\"\n />\n ))}\n <span className=\"ml-1 text-[11px] text-(--chat-faint)\">{name} is thinking…</span>\n {onStop && (\n <button\n type=\"button\"\n disabled={stopping}\n aria-label=\"Stop the agent\"\n onClick={() => {\n setStopping(true)\n void onStop().finally(() => setStopping(false))\n }}\n className=\"ml-2 rounded-full border border-(--chat-line) bg-(--chat-panel2) px-2 py-0.5 text-[11px] font-medium text-(--chat-danger) transition-colors hover:border-(--chat-danger) disabled:opacity-50\"\n >\n Stop\n </button>\n )}\n </div>\n )\n}\n\nfunction ChatPanel({ config, adapters, attach }: GolemProps<ChatConfig, ChatAdapters, ChatSlots>) {\n const { messages, loadError } = useConversation(adapters.chat)\n const [draft, setDraft] = useState('')\n const [staged, setStaged] = useState<ChatAttachment[]>([])\n // Bumped on every send, so a hosted picker remounts with nothing on it: the files went with the\n // message, and its chips would otherwise say they are still waiting to be sent.\n const [sent, setSent] = useState(0)\n const [sendError, setSendError] = useState('')\n const composer = useRef<HTMLTextAreaElement>(null)\n const fileInput = useRef<HTMLInputElement>(null)\n // Slash commands and their replies live here, not in the adapter's conversation: `runCommand`\n // answers with text, and the rows follow whatever the adapter emits.\n const [commandRows, setCommandRows] = useState<ChatMessage[]>([])\n const [commands, setCommands] = useState<ChatCommand[]>([])\n const [selected, setSelected] = useState(0)\n // Escape, or a pick that completes the line, closes the picker until the draft changes again.\n const [pickerClosed, setPickerClosed] = useState(false)\n const pickerWanted = Boolean(adapters.chat.commands) && !pickerClosed && SLASH_LINE.test(draft)\n const matches = pickerWanted ? slashMatches(draft, commands) : []\n const pickerOpen = matches.length > 0\n\n useEffect(() => {\n if (!pickerWanted) return\n let live = true\n void adapters.chat.commands!().then(\n (next) => live && setCommands(next),\n () => {},\n )\n return () => {\n live = false\n }\n // Fetched each time a `/` line opens the picker, as the set changes when the agent changes.\n }, [pickerWanted, adapters.chat])\n\n const pick = (insert: string) => {\n setDraft(insert)\n setSelected(0)\n // A pick ending in a space is a command still waiting for its argument: the picker stays open.\n if (!insert.endsWith(' ')) setPickerClosed(true)\n composer.current?.focus()\n }\n\n // A message with no text yet is the agent taking its turn, and the thinking row says so better\n // than an empty bubble would.\n const visible = [\n ...messages.filter((message) => message.text || message.attachments?.length),\n ...commandRows,\n ]\n const last = messages[messages.length - 1]\n const thinking = Boolean(\n last && ((last.role === 'user' && !last.delivery) || (last.streaming && !last.text)),\n )\n\n const { feed, onScroll } = useStickToBottom(\n `${visible.length}:${last?.text.length ?? 0}:${thinking}`,\n )\n\n useLayoutEffect(() => {\n const element = composer.current\n if (!element) return\n element.style.height = 'auto'\n const lineHeight = Number.parseFloat(getComputedStyle(element).lineHeight) || 20\n const max = lineHeight * config.maxComposerLines\n element.style.height = `${Math.min(element.scrollHeight, max)}px`\n element.style.overflowY = element.scrollHeight > max ? 'auto' : 'hidden'\n }, [draft, config.maxComposerLines])\n\n const send = () => {\n const text = draft.trim()\n if (!text && staged.length === 0) return\n setDraft('')\n setStaged([])\n setSent((count) => count + 1)\n setSendError('')\n if (text.startsWith('/') && adapters.chat.runCommand) {\n const row = (id: string, body: string): ChatMessage => ({\n id: `cmd-${id}`,\n role: 'system',\n text: body,\n at: new Date().toISOString(),\n })\n const stamp = Date.now()\n setCommandRows((rows) => [...rows, row(`${stamp}-line`, text)])\n void adapters.chat.runCommand(text).then(\n (reply) => setCommandRows((rows) => [...rows, row(`${stamp}-reply`, reply)]),\n (error: unknown) =>\n setSendError(error instanceof Error ? error.message : 'Command failed.'),\n )\n return\n }\n void adapters.chat.send(text, staged.length ? staged : undefined).catch((error: unknown) => {\n setSendError(error instanceof Error ? error.message : 'Message could not be sent.')\n })\n }\n\n const retry = adapters.chat.retry\n ? (messageId: string) => {\n setSendError('')\n void adapters.chat.retry!(messageId).catch((error: unknown) => {\n setSendError(error instanceof Error ? error.message : 'Message could not be sent.')\n })\n }\n : undefined\n\n const interrupt = adapters.chat.interrupt\n ? () => {\n setSendError('')\n return adapters.chat.interrupt!().catch((error: unknown) => {\n setSendError(error instanceof Error ? error.message : 'The agent could not be stopped.')\n })\n }\n : undefined\n\n return (\n <div\n data-golem-component=\"Chat\"\n className=\"golem-chat flex h-full min-h-0 w-full flex-col bg-(--chat-bg) text-(--chat-text)\"\n >\n <div\n ref={feed}\n onScroll={onScroll}\n role=\"log\"\n aria-label=\"Conversation\"\n aria-live=\"polite\"\n className=\"min-h-0 flex-1 space-y-2 overflow-y-auto px-3 py-3.5 [scrollbar-color:var(--chat-line2)_transparent] [scrollbar-width:thin]\"\n >\n {visible.length === 0 && !thinking ? (\n <p className=\"flex h-full items-center justify-center p-6 text-center text-sm text-(--chat-faint)\">\n {config.emptyState}\n </p>\n ) : (\n visible.map((message) => (\n <Bubble\n key={message.id}\n message={message}\n config={config}\n onRetry={retry}\n onOpenSource={adapters.chat.openSource?.bind(adapters.chat)}\n />\n ))\n )}\n {thinking && <Thinking name={config.agentName} onStop={interrupt} />}\n </div>\n\n {(loadError || sendError) && (\n <p\n role=\"alert\"\n className=\"shrink-0 border-t border-(--chat-line) px-3 py-2 text-sm text-(--chat-danger)\"\n >\n {loadError || sendError}\n </p>\n )}\n\n {attach ? (\n <div key={sent} className=\"shrink-0 border-t border-(--chat-line) px-3 pt-2\">\n {attach(setStaged)}\n </div>\n ) : (\n staged.length > 0 && (\n <div className=\"shrink-0 border-t border-(--chat-line) px-3 pt-2\">\n <AttachmentChips\n attachments={staged}\n onRemove={(id) => setStaged((files) => files.filter((file) => file.id !== id))}\n />\n </div>\n )\n )}\n\n <form\n className=\"relative flex shrink-0 items-end gap-2 border-t border-(--chat-line) px-3 py-2.5\"\n onSubmit={(event) => {\n event.preventDefault()\n send()\n }}\n >\n {pickerOpen && (\n <div\n role=\"listbox\"\n aria-label=\"Slash commands\"\n // Picking must not blur the composer.\n onMouseDown={(event) => event.preventDefault()}\n className=\"absolute right-3 bottom-full left-3 z-10 mb-1 max-h-56 overflow-y-auto rounded-[10px] border border-(--chat-line2) bg-(--chat-panel) py-1 shadow-lg\"\n >\n {matches.map((match, index) => (\n <button\n key={match.insert}\n type=\"button\"\n role=\"option\"\n aria-selected={index === Math.min(selected, matches.length - 1)}\n onClick={() => pick(match.insert)}\n className={`flex w-full items-baseline gap-3 px-3 py-1.5 text-left text-sm ${\n index === Math.min(selected, matches.length - 1)\n ? 'bg-(--chat-panel2) text-(--chat-text)'\n : 'text-(--chat-dim)'\n }`}\n >\n <span className=\"shrink-0 font-mono text-(--chat-text)\">{match.name}</span>\n <span className=\"min-w-0 truncate text-xs\">{match.description}</span>\n </button>\n ))}\n </div>\n )}\n {!attach && (\n <>\n <input\n ref={fileInput}\n type=\"file\"\n multiple\n className=\"hidden\"\n aria-label=\"Attach files\"\n onChange={(event) => {\n const files = [...(event.target.files ?? [])].map((file, index) => ({\n id: `att-${Date.now()}-${index}`,\n name: file.name,\n size: file.size,\n }))\n setStaged((current) => [...current, ...files])\n event.target.value = ''\n }}\n />\n <button\n type=\"button\"\n onClick={() => fileInput.current?.click()}\n title=\"Attach a file\"\n aria-label=\"Attach a file\"\n className=\"flex size-[34px] shrink-0 items-center justify-center rounded-full border border-(--chat-line) bg-(--chat-panel2) text-[15px] text-(--chat-dim) transition-colors hover:border-(--chat-accent) hover:text-(--chat-text)\"\n >\n 📎\n </button>\n </>\n )}\n <textarea\n ref={composer}\n rows={1}\n value={draft}\n onChange={(event) => {\n setDraft(event.target.value)\n setPickerClosed(false)\n setSelected(0)\n }}\n onKeyDown={(event) => {\n // With the picker open, arrows move, Tab and Enter pick, Escape closes.\n if (pickerOpen) {\n const current = Math.min(selected, matches.length - 1)\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault()\n const step = event.key === 'ArrowDown' ? 1 : matches.length - 1\n setSelected((current + step) % matches.length)\n return\n }\n if (event.key === 'Tab' || event.key === 'Enter') {\n event.preventDefault()\n pick(matches[current]!.insert)\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n setPickerClosed(true)\n return\n }\n }\n // Enter sends because the composer is a chat line, not a document; Shift+Enter is the\n // deliberate line break. `isComposing` guards an IME candidate being accepted.\n if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault()\n send()\n }\n }}\n placeholder={config.placeholder}\n aria-label={config.placeholder}\n className=\"min-w-0 flex-1 resize-none rounded-[10px] border border-(--chat-line) bg-(--chat-panel) px-3 py-2 text-[15px] leading-6 text-(--chat-text) outline-none transition-[border-color,box-shadow] placeholder:text-(--chat-faint) focus:border-(--chat-accent) focus:shadow-[0_0_0_3px_var(--chat-accent-soft)]\"\n />\n <button\n type=\"submit\"\n className=\"flex size-[34px] shrink-0 items-center justify-center rounded-full bg-(--chat-accent) text-sm font-medium text-white transition-opacity hover:opacity-85\"\n >\n <span aria-hidden=\"true\">➤</span>\n <span className=\"sr-only\">Send</span>\n </button>\n </form>\n </div>\n )\n}\n\nexport const Chat = defineComponent<typeof chatConfigSchema, ChatAdapters, ChatSlots>({\n name: 'Chat',\n schema: chatConfigSchema,\n render: ChatPanel,\n})\n","/**\n * A three-way merge over lines, which is what lets a person and an agent write the same document at\n * the same time without either one flattening the other. `base` is the version both started from,\n * `mine` is the local draft, `theirs` is what arrived; the result is the chunks a merged document is\n * made of, each one saying where its lines came from.\n *\n * Lines, not characters: a merge that resolves inside a sentence produces text neither writer wrote.\n * A whole line is the smallest unit a person can look at and say \"that one, not that one\".\n */\n\nexport type Merge3Chunk =\n | {\n kind: 'clean'\n lines: string[]\n /** `both` is untouched, `mine` is the local edit, `theirs` is what the agent just wrote. */\n from: 'both' | 'mine' | 'theirs'\n }\n | { kind: 'conflict'; mine: string[]; theirs: string[] }\n\nexport interface Merge3 {\n chunks: Merge3Chunk[]\n /** True when at least one chunk is a conflict, so the reader has to choose. */\n conflicted: boolean\n}\n\n/** One side's edit against the base, as half-open ranges. */\ninterface Edit {\n side: 'mine' | 'theirs'\n baseStart: number\n baseEnd: number\n sideStart: number\n sideEnd: number\n}\n\n/**\n * Above this many lines the quadratic table below would cost more than the merge is worth, so the\n * whole document counts as one edit: the reader still gets a choice, just a coarse one.\n */\nconst MAX_LINES = 4000\n\nconst splitLines = (text: string): string[] => (text === '' ? [] : text.split('\\n'))\n\nconst same = (a: string[], b: string[]) =>\n a.length === b.length && a.every((one, i) => one === b[i])\n\n/**\n * Matched line pairs between two sequences, longest-common-subsequence order. The table is\n * `(a.length + 1) × (b.length + 1)` numbers, which is why `MAX_LINES` exists.\n */\nfunction matchedPairs(a: string[], b: string[]): [number, number][] {\n const rows = a.length\n const cols = b.length\n const table: number[][] = Array.from({ length: rows + 1 }, () =>\n new Array<number>(cols + 1).fill(0),\n )\n\n for (let i = rows - 1; i >= 0; i--) {\n for (let j = cols - 1; j >= 0; j--) {\n table[i]![j] =\n a[i] === b[j] ? table[i + 1]![j + 1]! + 1 : Math.max(table[i + 1]![j]!, table[i]![j + 1]!)\n }\n }\n\n const pairs: [number, number][] = []\n let i = 0\n let j = 0\n while (i < rows && j < cols) {\n if (a[i] === b[j]) {\n pairs.push([i, j])\n i++\n j++\n } else if (table[i + 1]![j]! >= table[i]![j + 1]!) i++\n else j++\n }\n return pairs\n}\n\n/** The runs where `side` differs from `base`, derived from the lines the two still share. */\nfunction editsOf(base: string[], side: string[], which: 'mine' | 'theirs'): Edit[] {\n const pairs = matchedPairs(base, side)\n const edits: Edit[] = []\n let baseAt = 0\n let sideAt = 0\n\n const close = (baseEnd: number, sideEnd: number) => {\n if (baseEnd > baseAt || sideEnd > sideAt) {\n edits.push({ side: which, baseStart: baseAt, baseEnd, sideStart: sideAt, sideEnd })\n }\n }\n\n for (const [baseIndex, sideIndex] of pairs) {\n close(baseIndex, sideIndex)\n baseAt = baseIndex + 1\n sideAt = sideIndex + 1\n }\n close(base.length, side.length)\n return edits\n}\n\nexport function merge3(baseText: string, mineText: string, theirsText: string): Merge3 {\n const base = splitLines(baseText)\n const mine = splitLines(mineText)\n const theirs = splitLines(theirsText)\n\n if (same(mine, theirs)) {\n return { chunks: [{ kind: 'clean', lines: mine, from: 'both' }], conflicted: false }\n }\n if (Math.max(base.length, mine.length, theirs.length) > MAX_LINES) {\n return { chunks: [{ kind: 'conflict', mine, theirs }], conflicted: true }\n }\n\n // Both sides' edits on one timeline, ordered by where they land in the base. Two edits that\n // overlap in the base are one decision, so they are grouped before either is applied.\n const edits = [...editsOf(base, mine, 'mine'), ...editsOf(base, theirs, 'theirs')].sort(\n (a, b) => a.baseStart - b.baseStart || a.baseEnd - b.baseEnd,\n )\n\n const chunks: Merge3Chunk[] = []\n let conflicted = false\n let baseAt = 0\n let mineAt = 0\n let theirsAt = 0\n let at = 0\n\n const keepUntouched = (until: number) => {\n if (until > baseAt)\n chunks.push({ kind: 'clean', lines: base.slice(baseAt, until), from: 'both' })\n mineAt += until - baseAt\n theirsAt += until - baseAt\n baseAt = until\n }\n\n while (at < edits.length) {\n const group = [edits[at]!]\n let baseEnd = group[0]!.baseEnd\n at++\n // Only a real overlap makes one decision out of two edits. Edits that merely touch — the agent\n // rewriting the line above the one being typed on — stay two, and both are kept.\n while (at < edits.length && edits[at]!.baseStart < baseEnd) {\n baseEnd = Math.max(baseEnd, edits[at]!.baseEnd)\n group.push(edits[at]!)\n at++\n }\n const baseStart = group[0]!.baseStart\n\n keepUntouched(baseStart)\n\n // Each side's span for this region. Every base line before the group's first edit is unchanged\n // on both sides, so the running cursor is already that side's start; only the end has to be\n // carried out from the last edit.\n const sides = {\n mine: group.filter((edit) => edit.side === 'mine'),\n theirs: group.filter((edit) => edit.side === 'theirs'),\n }\n const sliceOf = (which: 'mine' | 'theirs', lines: string[], cursor: number) => {\n const own = sides[which]\n const last = own[own.length - 1]\n const end =\n last === undefined\n ? cursor + (baseEnd - baseStart)\n : last.sideEnd + (baseEnd - last.baseEnd)\n return lines.slice(cursor, Math.max(cursor, end))\n }\n\n const mineLines = sliceOf('mine', mine, mineAt)\n const theirsLines = sliceOf('theirs', theirs, theirsAt)\n\n if (sides.theirs.length === 0) chunks.push({ kind: 'clean', lines: mineLines, from: 'mine' })\n else if (sides.mine.length === 0)\n chunks.push({ kind: 'clean', lines: theirsLines, from: 'theirs' })\n else if (same(mineLines, theirsLines))\n chunks.push({ kind: 'clean', lines: mineLines, from: 'both' })\n else {\n chunks.push({ kind: 'conflict', mine: mineLines, theirs: theirsLines })\n conflicted = true\n }\n\n mineAt += mineLines.length\n theirsAt += theirsLines.length\n baseAt = baseEnd\n }\n\n keepUntouched(base.length)\n\n return {\n chunks: chunks.filter((chunk) => chunk.kind === 'conflict' || chunk.lines.length > 0),\n conflicted,\n }\n}\n\n/**\n * The merged document as text, plus the line ranges the other side wrote — what the editor paints a\n * gutter down so the person can see what the agent just changed. `choose` settles any conflict; a\n * merge with none never calls it.\n */\nexport function mergedText(\n chunks: Merge3Chunk[],\n choose: (chunk: { mine: string[]; theirs: string[] }, index: number) => 'mine' | 'theirs' = () =>\n 'mine',\n): { text: string; theirLines: { start: number; end: number }[] } {\n const lines: string[] = []\n const theirLines: { start: number; end: number }[] = []\n\n chunks.forEach((chunk, index) => {\n const taken =\n chunk.kind === 'clean'\n ? chunk.lines\n : choose(chunk, index) === 'mine'\n ? chunk.mine\n : chunk.theirs\n const fromThem =\n chunk.kind === 'clean' ? chunk.from === 'theirs' : choose(chunk, index) === 'theirs'\n if (fromThem && taken.length > 0)\n theirLines.push({ start: lines.length, end: lines.length + taken.length })\n lines.push(...taken)\n })\n\n return { text: lines.join('\\n'), theirLines }\n}\n","/**\n * A locale the platform can actually format with. `en_GB` and `english` are the two an agent\n * writes by mistake, and both would otherwise throw inside `Intl` at render time rather than land\n * on the error card.\n */\nexport function isFormattableLocale(locale: string): boolean {\n try {\n new Intl.DateTimeFormat(locale)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * A time zone `Intl` accepts, or UTC. The zone comes from the `Clock` adapter rather than from the\n * config, so there is no field to name on an error card — a zone the platform does not know falls\n * back rather than throwing mid-render.\n */\nexport function formattableZone(timeZone: string): string {\n try {\n new Intl.DateTimeFormat('en-CA', { timeZone })\n return timeZone\n } catch {\n return 'UTC'\n }\n}\n","import { z } from 'zod'\nimport { isFormattableLocale } from '../../lib/locale'\n\nexport const editorConfigSchema = z\n .object({\n collection: z\n .string()\n .min(1)\n .describe(\n 'The record type the document lives in, passed to `get`, `update` and `subscribe`. One document is one record.',\n ),\n id: z\n .string()\n .min(1)\n .describe(\n 'The id of the one record this editor opens. An editor edits a document, not a collection: point two editors at two ids.',\n ),\n bodyField: z\n .string()\n .min(1)\n .default('body')\n .describe(\n 'The field holding the document itself, as markdown. It is both what the person types into and what the agent rewrites.',\n ),\n versionField: z\n .string()\n .min(1)\n .default('version')\n .describe(\n 'The field holding a number that goes up on every write. Saving sends the version it read as `expectedVersion`, so a write that arrived second is refused rather than overwriting the other writer.',\n ),\n autosaveMs: z\n .number()\n .int()\n .min(0)\n .max(60_000)\n .default(1200)\n .describe(\n 'How long typing has to stop before the draft is saved. `0` saves on every keystroke, which is what a document two writers share wants; a slow store wants two or three thousand.',\n ),\n preview: z\n .enum(['toggle', 'split', 'off'])\n .default('toggle')\n .describe(\n 'How the rendered document is reached. `toggle` swaps the pane, `split` puts the rendering beside the source at desktop width and falls back to `toggle` on a phone, `off` shows the source only.',\n ),\n outline: z\n .boolean()\n .default(true)\n .describe(\n 'Whether the headings are offered as a drawer that jumps to them. Off for a document short enough to scroll.',\n ),\n readOnly: z\n .boolean()\n .default(false)\n .describe(\n 'Whether the document is only readable. The source and the preview stay, the toolbar and the autosave go, and nothing the person types reaches the record.',\n ),\n placeholder: z\n .string()\n .default('Write the document in markdown…')\n .describe('The line shown in place of an empty document, to say what belongs in it.'),\n highlightMs: z\n .number()\n .int()\n .min(0)\n .max(60_000)\n .default(4000)\n .describe(\n 'How long a line the agent just wrote keeps its coloured gutter. `0` never marks anything; a few seconds is long enough to catch the eye and short enough to stop being noise.',\n ),\n locale: z\n .string()\n .min(2)\n .default('en-GB')\n .describe(\n 'A BCP-47 tag — `en-GB`, `en-US`, `pt-BR` — that the saved-at time in the status line is formatted with.',\n ),\n })\n .strict()\n .superRefine((config, ctx) => {\n if (!isFormattableLocale(config.locale)) {\n ctx.addIssue({\n code: 'custom',\n path: ['locale'],\n message: `\"${config.locale}\" is not a BCP-47 language tag, so the saved-at time cannot be formatted. Write it with a hyphen: en-GB, en-US, pt-BR.`,\n })\n }\n })\n\nexport type EditorConfig = z.output<typeof editorConfigSchema>\nexport type EditorConfigInput = z.input<typeof editorConfigSchema>\n","import { Fragment, type ReactNode } from 'react'\n\n/**\n * Markdown source styled where it stands: the characters the person typed, still all there, wearing\n * the weight and colour of what they mean. It is not a preview and it is not a WYSIWYG surface —\n * `##` stays `##`, and deleting one is how a heading stops being a heading.\n *\n * It is drawn under a transparent textarea, so **every span has to keep the monospace advance\n * width**: weight, colour and background only. A larger heading or an italic run would slide the\n * text out from under the caret.\n */\n\nexport type LineKind = 'heading' | 'code' | 'list' | 'quote' | 'plain'\n\nexport interface SourceLine {\n text: string\n kind: LineKind\n}\n\n/** A fence opens and closes across lines, so a line's kind is only knowable in document order. */\nexport function scanSource(lines: string[]): SourceLine[] {\n let fenced = false\n return lines.map((text) => {\n if (text.trimStart().startsWith('```')) {\n fenced = !fenced\n return { text, kind: 'code' as const }\n }\n if (fenced) return { text, kind: 'code' as const }\n if (/^#{1,6}\\s/.test(text)) return { text, kind: 'heading' as const }\n if (/^\\s*([-*+]|\\d+[.)])\\s/.test(text)) return { text, kind: 'list' as const }\n if (/^\\s*>/.test(text)) return { text, kind: 'quote' as const }\n return { text, kind: 'plain' as const }\n })\n}\n\n/** Every heading in the document, in order, for the outline. */\nexport function headingsOf(lines: string[]): { line: number; level: number; text: string }[] {\n return scanSource(lines).flatMap((line, index) => {\n if (line.kind !== 'heading') return []\n const match = /^(#{1,6})\\s+(.*)$/.exec(line.text)\n if (!match || match[2]!.trim() === '') return []\n return [{ line: index, level: match[1]!.length, text: match[2]!.trim() }]\n })\n}\n\nconst LINE_CLASS: Record<LineKind, string> = {\n heading: 'font-semibold text-neutral-900',\n code: 'bg-neutral-100 text-neutral-600',\n list: 'text-neutral-800',\n quote: 'text-neutral-500',\n plain: 'text-neutral-800',\n}\n\nconst INLINE = /(`[^`]+`|\\*\\*[^*]+\\*\\*|\\[[^\\]\\n]+\\]\\([^)\\s]+\\))/g\n\nfunction inline(text: string, key: string): ReactNode[] {\n return text.split(INLINE).map((piece, index) => {\n const id = `${key}-${index}`\n if (piece.startsWith('`') && piece.endsWith('`') && piece.length > 1) {\n return (\n <span key={id} className=\"rounded bg-neutral-200/70 text-rose-700\">\n {piece}\n </span>\n )\n }\n if (piece.startsWith('**') && piece.endsWith('**') && piece.length > 3) {\n return (\n <span key={id} className=\"font-semibold\">\n {piece}\n </span>\n )\n }\n if (piece.startsWith('[')) {\n return (\n <span key={id} className=\"text-sky-700\">\n {piece}\n </span>\n )\n }\n return <Fragment key={id}>{piece}</Fragment>\n })\n}\n\n/** The marker a line opens with — `##`, `-`, `1.`, `>` — coloured apart from what follows it. */\nconst MARKER = /^(\\s*)(#{1,6}|[-*+]|\\d+[.)]|>)(\\s+)(.*)$/\n\nexport function SourceRow({ line, index }: { line: SourceLine; index: number }) {\n if (line.kind === 'code') return <>{line.text}</>\n\n const marked = MARKER.exec(line.text)\n if (!marked || (line.kind !== 'heading' && line.kind !== 'list' && line.kind !== 'quote')) {\n return <>{inline(line.text, String(index))}</>\n }\n\n return (\n <>\n {marked[1]}\n <span className=\"text-sky-600\">{marked[2]}</span>\n {marked[3]}\n {inline(marked[4]!, String(index))}\n </>\n )\n}\n\nexport { LINE_CLASS }\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactNode,\n} from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport { conflictingRecord, type ClockAdapter, type RecordsAdapter } from '../../adapters'\nimport { merge3, mergedText, type Merge3Chunk } from '../../lib/diff3'\nimport { headingSlug, Markdown } from '../../lib/markdown'\nimport { useContainerWidth } from '../../lib/use-container-width'\nimport { editorConfigSchema, type EditorConfig } from './Editor.config'\nimport { headingsOf, scanSource, SourceRow, LINE_CLASS } from './source'\n\nexport interface EditorAdapters {\n records: RecordsAdapter\n clock: ClockAdapter\n}\n\nexport interface EditorSlots {\n /**\n * Work the person had not saved when they last left — from a local draft store, say. The editor\n * opens on it instead of the record's body, dirty against the record's version, so the first\n * thing that arrives from the agent is merged rather than dropped on top of it.\n */\n draft?: string\n /**\n * A passage to show: 1-based source lines, `endLine` inclusive, clamped to the document. The source\n * pane scrolls to it and marks it until the person clicks or edits; keyboard focus, caret and draft\n * are left alone. Send a new `key` to show the same range again.\n *\n * With `version`, the request waits until the editor holds that version or a later one. With\n * `text`, the passage is found by its lines in the draft on screen, and nothing is marked when\n * those lines are not there exactly once.\n */\n focus?: EditorFocus\n}\n\nexport interface EditorFocus {\n line: number\n endLine?: number\n key?: string\n /** The record version `line` counts in. The request waits until the editor has loaded it. */\n version?: number\n /** The passage's exact lines, joined by `\\n`. Found in the draft rather than trusted to `line`. */\n text?: string\n}\n\n/** Below this the source and the preview cannot both fit, so `split` falls back to `toggle`. */\nconst SPLIT_ABOVE = 768\n\ntype Status = 'loading' | 'saved' | 'dirty' | 'saving' | 'conflict' | 'error'\n\n/** A run of lines the agent wrote, in the draft's own line numbers, and when it landed. */\ninterface Mark {\n start: number\n end: number\n}\n\ninterface Doc {\n draft: string\n /** The text the person and the agent last agreed on: the base every merge is run from. */\n base: string\n version: number\n status: Status\n error: string | null\n savedAt: Date | null\n marks: Mark[]\n /** Set while the two disagree; the source pane is replaced by the choice until it is empty. */\n conflict: Merge3Chunk[] | null\n choices: ('mine' | 'theirs' | null)[]\n /** The record's line break. `draft` and `base` always hold `\\n`, as a textarea does. */\n eol: '\\n' | '\\r\\n'\n}\n\nconst EMPTY: Doc = {\n draft: '',\n base: '',\n version: 0,\n status: 'loading',\n error: null,\n savedAt: null,\n marks: [],\n conflict: null,\n choices: [],\n eol: '\\n',\n}\n\n/**\n * A body whose every line break is CRLF is held with bare LF — a textarea turns CRLF into LF on the\n * first keystroke anyway — and written back with CRLF. Any other body, mixed ones too, is held as is.\n */\nfunction decode(body: string): { text: string; eol: Doc['eol'] } {\n return body.includes('\\r\\n') && !/\\r(?!\\n)|(?<!\\r)\\n/.test(body)\n ? { text: body.replaceAll('\\r\\n', '\\n'), eol: '\\r\\n' }\n : { text: body, eol: '\\n' }\n}\n\n/**\n * The 0-based `[start, end)` rows a focus request marks in `lines`, or `null` for none. A request\n * without `text` counts lines, clamped. With `text`, the offered range is trusted only on the exact\n * version it was counted in, untouched; anywhere else the lines must appear exactly once.\n */\nfunction passageOf(\n lines: string[],\n focus: EditorFocus,\n atVersion: boolean,\n eol: Doc['eol'],\n): [number, number] | null {\n const first = Math.trunc(focus.line) - 1\n const last = Number.isFinite(focus.endLine) ? Math.trunc(focus.endLine!) : first + 1\n if (focus.text === undefined) {\n const clamp = (row: number) => Math.min(Math.max(row, 0), lines.length - 1)\n const start = clamp(first)\n return [start, Math.max(start, clamp(last - 1)) + 1]\n }\n // The offered lines of a CRLF record keep their CRs, the last one's too; the draft has none.\n const text = eol === '\\n' ? focus.text : focus.text.replaceAll('\\r\\n', '\\n').replace(/\\r$/, '')\n const want = text.split('\\n')\n const holds = (at: number) => want.every((line, index) => lines[at + index] === line)\n if (atVersion && last - first === want.length && holds(first)) return [first, last]\n let found: number | null = null\n for (let at = 0; at + want.length <= lines.length; at++) {\n if (!holds(at)) continue\n if (found !== null) return null\n found = at\n }\n return found === null ? null : [found, found + want.length]\n}\n\nconst message = (cause: unknown) => (cause instanceof Error ? cause.message : String(cause))\n\ntype DocRecord = Record<string, unknown> & { id: string }\n\n/**\n * Undo is local and it is the editor's own: a draft the agent rewrote is set programmatically, and\n * the browser's native textarea history has no entry for that. Keystrokes inside half a second\n * coalesce into one step, measured on the wall clock rather than on the `Clock` adapter — a frozen\n * clock is a document's clock, not a stopwatch.\n */\nconst COALESCE_MS = 500\n\nfunction useHistory() {\n const state = useRef({ stack: [] as string[], at: -1, last: 0 })\n\n const reset = useCallback((text: string) => {\n state.current = { stack: [text], at: 0, last: 0 }\n }, [])\n\n const remember = useCallback((text: string) => {\n const history = state.current\n const now = Date.now()\n if (history.at >= 0 && now - history.last < COALESCE_MS) {\n history.stack[history.at] = text\n history.last = now\n return\n }\n history.stack = [...history.stack.slice(0, history.at + 1), text]\n history.at = history.stack.length - 1\n history.last = now\n }, [])\n\n const step = useCallback((delta: number): string | null => {\n const history = state.current\n const next = history.at + delta\n if (next < 0 || next >= history.stack.length) return null\n history.at = next\n history.last = 0\n return history.stack[next]!\n }, [])\n\n return useMemo(() => ({ reset, remember, step }), [reset, remember, step])\n}\n\nfunction EditorBody({\n config,\n adapters,\n draft: openWith,\n focus,\n}: GolemProps<EditorConfig, EditorAdapters, EditorSlots>) {\n const { records, clock } = adapters\n const root = useRef<HTMLDivElement>(null)\n const textarea = useRef<HTMLTextAreaElement>(null)\n const overlay = useRef<HTMLDivElement>(null)\n const preview = useRef<HTMLDivElement>(null)\n const sourcePane = useRef<HTMLDivElement>(null)\n\n const [doc, setDocState] = useState<Doc>(EMPTY)\n const docRef = useRef(doc)\n const setDoc = useCallback((patch: Partial<Doc>) => {\n docRef.current = { ...docRef.current, ...patch }\n setDocState(docRef.current)\n }, [])\n\n const [mode, setMode] = useState<'source' | 'preview'>('source')\n const [outlineOpen, setOutlineOpen] = useState(false)\n const history = useHistory()\n const selection = useRef<[number, number] | null>(null)\n /** An incoming version that arrived while a conflict was open, applied once it is settled. */\n const pending = useRef<DocRecord | null>(null)\n /**\n * The record the doc belongs to, read through which fields and which store. Moving to another one\n * parks this doc — draft, versions, an open conflict — untouched, and nothing is written on the way\n * out. Coming back restores it, and the load that follows merges it against the latest version.\n */\n const record = `${config.collection}/${config.id}/${config.bodyField}/${config.versionField}`\n const opened = useRef({ record, records, first: { record, records } })\n const parked = useRef(\n new WeakMap<RecordsAdapter, Map<string, { doc: Doc; pending: DocRecord | null }>>(),\n )\n\n /** The record whose latest version has been read since it was opened, so a draft is up to date. */\n const loaded = useRef<{ record: string; records: RecordsAdapter } | null>(null)\n const [reads, setReads] = useState(0)\n\n const wide = useContainerWidth(root) >= SPLIT_ABOVE\n const split = config.preview === 'split' && wide\n const showPreview = split || (config.preview !== 'off' && mode === 'preview')\n const showSource = split || !showPreview\n\n /**\n * A version that arrived from somewhere else, folded into the draft. Three outcomes: it is already\n * what we have, it merges cleanly and the lines it wrote are marked, or the two disagree and the\n * person is asked which lines to keep.\n */\n const applyIncoming = useCallback(\n (record: DocRecord) => {\n const current = docRef.current\n const { text: theirs, eol } = decode(String(record[config.bodyField] ?? ''))\n const version = Number(record[config.versionField] ?? current.version)\n\n if (current.conflict !== null) {\n pending.current = record\n return\n }\n if (theirs === current.base && version === current.version) return\n\n const merged = merge3(current.base, current.draft, theirs)\n if (merged.conflicted) {\n setDoc({\n base: theirs,\n version,\n eol,\n status: 'conflict',\n error: null,\n conflict: merged.chunks,\n choices: merged.chunks.map(() => null),\n })\n return\n }\n\n const { text, theirLines } = mergedText(merged.chunks)\n history.remember(text)\n setDoc({\n draft: text,\n base: theirs,\n version,\n eol,\n status: text === theirs ? 'saved' : 'dirty',\n error: null,\n marks: config.highlightMs > 0 ? theirLines : [],\n })\n },\n [config.bodyField, config.versionField, config.highlightMs, history, setDoc],\n )\n\n /** The one write. The version it read goes out as `expectedVersion`; a refusal merges instead. */\n const save = useCallback(async () => {\n const current = docRef.current\n if (config.readOnly || current.status === 'conflict' || current.status === 'loading') return\n if (current.draft === current.base) return\n\n const text = current.draft\n const body = current.eol === '\\n' ? text : text.replaceAll('\\n', current.eol)\n const expected = current.version\n setDoc({ status: 'saving' })\n\n // The answer belongs to the record that asked, which may be parked by the time it comes back.\n const here = () => opened.current.record === record && opened.current.records === records\n const settle = (patch: (doc: Doc) => Partial<Doc>) => {\n if (here()) return setDoc(patch(docRef.current))\n const away = parked.current.get(records)?.get(record)\n if (away) away.doc = { ...away.doc, ...patch(away.doc) }\n }\n\n try {\n const saved = await records.update<DocRecord>(\n config.collection,\n config.id,\n { [config.bodyField]: body, [config.versionField]: expected + 1 },\n { expectedVersion: expected, versionField: config.versionField },\n )\n settle((doc) => ({\n base: text,\n version: Number(saved[config.versionField] ?? expected + 1),\n // The person kept typing while the write was in flight, so there is already more to send.\n status: doc.draft === text ? 'saved' : 'dirty',\n error: null,\n savedAt: clock.now(),\n }))\n } catch (cause) {\n const theirs = conflictingRecord(cause)\n if (theirs && here()) {\n setDoc({ status: 'dirty' })\n applyIncoming(theirs as DocRecord)\n return\n }\n // A parked refusal stays dirty; coming back loads the latest version and merges against it.\n settle(() => (theirs ? { status: 'dirty' } : { status: 'error', error: message(cause) }))\n }\n }, [applyIncoming, clock, config, record, records, setDoc])\n\n // The document, and the subscription that brings every later version of it. Nothing polls.\n useEffect(() => {\n let live = true\n if (opened.current.record !== record || opened.current.records !== records) {\n const left = opened.current\n const shelf = parked.current.get(left.records) ?? new Map()\n parked.current.set(\n left.records,\n shelf.set(left.record, { doc: docRef.current, pending: pending.current }),\n )\n const back = parked.current.get(records)?.get(record)\n parked.current.get(records)?.delete(record)\n opened.current = { ...left, record, records }\n pending.current = back?.pending ?? null\n history.reset(back?.doc.draft ?? '')\n setDoc(back?.doc ?? EMPTY)\n loaded.current = null\n }\n\n const read = () => {\n if (loaded.current?.record === record && loaded.current.records === records) return\n loaded.current = { record, records }\n setReads((count) => count + 1)\n }\n const load = () =>\n records.get<DocRecord>(config.collection, config.id).then(\n (record) => {\n if (!live) return\n if (record === null) {\n setDoc({\n status: 'error',\n error: `There is no ${config.collection} record with the id ${config.id}.`,\n })\n return\n }\n if (docRef.current.status === 'loading') {\n const { text: body, eol } = decode(String(record[config.bodyField] ?? ''))\n // The draft slot is the first record's unsaved work, never another record's.\n const opening =\n openWith !== undefined &&\n opened.current.record === opened.current.first.record &&\n opened.current.records === opened.current.first.records\n ? eol === '\\n'\n ? openWith\n : openWith.replaceAll('\\r\\n', '\\n')\n : body\n history.reset(opening)\n setDoc({\n draft: opening,\n base: body,\n eol,\n version: Number(record[config.versionField] ?? 0),\n status: opening === body ? 'saved' : 'dirty',\n error: null,\n })\n read()\n return\n }\n applyIncoming(record)\n read()\n },\n (cause: unknown) => {\n if (live) setDoc({ status: 'error', error: message(cause) })\n },\n )\n\n void load()\n const unsubscribe = records.subscribe(config.collection, () => void load())\n return () => {\n live = false\n unsubscribe()\n }\n // `openWith` is the draft the editor opens on, read once; a later change to it is not an edit.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n records,\n record,\n config.collection,\n config.id,\n config.bodyField,\n config.versionField,\n applyIncoming,\n history,\n setDoc,\n ])\n\n // Autosave: typing stops, the debounce runs out, the draft goes.\n useEffect(() => {\n if (config.readOnly || doc.status !== 'dirty') return undefined\n const timer = setTimeout(() => void save(), config.autosaveMs)\n return () => clearTimeout(timer)\n }, [doc.draft, doc.status, config.autosaveMs, config.readOnly, save])\n\n // The agent's lines keep their gutter for a few seconds, then the document is just a document.\n useEffect(() => {\n if (doc.marks.length === 0 || config.highlightMs === 0) return undefined\n const timer = setTimeout(() => setDoc({ marks: [] }), config.highlightMs)\n return () => clearTimeout(timer)\n }, [doc.marks, config.highlightMs, setDoc])\n\n // The textarea grows with its content, so the pane scrolls and the overlay never has to be\n // scrolled in step with it.\n useLayoutEffect(() => {\n const element = textarea.current\n if (!element) return\n element.style.height = 'auto'\n element.style.height = `${element.scrollHeight}px`\n }, [doc.draft, showSource])\n\n // A draft set in code — a wrap, an indent, an undo — puts the caret back where the person left it.\n useLayoutEffect(() => {\n const wanted = selection.current\n if (!wanted || !textarea.current) return\n selection.current = null\n textarea.current.focus()\n textarea.current.setSelectionRange(wanted[0], wanted[1])\n }, [doc.draft])\n\n /**\n * A focus request belongs to the record open when it arrived, and is applied once, when that record\n * has been read since it was opened, no conflict is open, and the version it names has arrived. A\n * parked draft brought back is matched only after the latest version has been merged into it. The mark it leaves is tied to the draft it was drawn on, so any\n * edit — the person's or the agent's — takes it down rather than leaving it on shifted lines.\n */\n const ask = focus\n ? JSON.stringify([focus.line, focus.endLine, focus.key, focus.version, focus.text])\n : ''\n const asked = useRef({ ask: '', record, records, done: true })\n const [spot, setSpot] = useState<{\n start: number\n end: number\n draft: string\n record: string\n records: RecordsAdapter\n } | null>(null)\n const scrollToSpot = useRef(false)\n\n useEffect(() => {\n if (ask !== asked.current.ask) asked.current = { ask, record, records, done: ask === '' }\n }, [ask, record, records])\n\n useEffect(() => {\n const request = asked.current\n if (request.done || !focus) return\n const current = docRef.current\n if (request.record !== record || request.records !== records || !Number.isFinite(focus.line)) {\n request.done = true\n return\n }\n if (loaded.current?.record !== record || loaded.current.records !== records) return\n if (current.status === 'loading' || current.conflict !== null) return\n if (Number.isFinite(focus.version) && current.version < focus.version!) return\n request.done = true\n const atVersion = current.version === focus.version && current.draft === current.base\n const passage = passageOf(current.draft.split('\\n'), focus, atVersion, current.eol)\n // A passage that cannot be placed leaves nothing marked, not even the last request's mark.\n if (!passage) return setSpot(null)\n scrollToSpot.current = true\n setMode('source')\n setSpot({ start: passage[0], end: passage[1], draft: current.draft, record, records })\n }, [ask, record, records, focus, reads, doc.status, doc.conflict, doc.version])\n\n // Centre the passage in the source pane, or put its top in view with two lines above it when it is\n // taller than the pane. Only the pane scrolls: the caret and the page stay where they are.\n useLayoutEffect(() => {\n const pane = sourcePane.current\n const rows = overlay.current?.children\n if (!scrollToSpot.current || !spot || !pane || !rows) return\n scrollToSpot.current = false\n const origin = pane.getBoundingClientRect().top - pane.scrollTop\n const top = rows[spot.start]!.getBoundingClientRect().top - origin\n const bottom = rows[spot.end - 1]!.getBoundingClientRect().bottom - origin\n pane.scrollTop =\n bottom - top > pane.clientHeight ? top - 48 : (top + bottom - pane.clientHeight) / 2\n }, [spot, showSource])\n\n const lines = useMemo(() => doc.draft.split('\\n'), [doc.draft])\n const scanned = useMemo(() => scanSource(lines), [lines])\n const headings = useMemo(() => headingsOf(lines), [lines])\n const marked = useMemo(() => {\n const rows = new Set<number>()\n for (const mark of doc.marks)\n for (let line = mark.start; line < mark.end; line++) rows.add(line)\n return rows\n }, [doc.marks])\n const shown =\n spot && spot.record === record && spot.records === records && spot.draft === doc.draft\n ? spot\n : null\n\n const write = (text: string, caret?: [number, number]) => {\n history.remember(text)\n if (caret) selection.current = caret\n setDoc({ draft: text, status: text === docRef.current.base ? 'saved' : 'dirty' })\n }\n\n /** Wraps the selection, or opens an empty pair with the caret inside it. */\n const wrap = (marker: string) => {\n const element = textarea.current\n if (!element) return\n const [from, to] = [element.selectionStart, element.selectionEnd]\n const text = docRef.current.draft\n const next = `${text.slice(0, from)}${marker}${text.slice(from, to)}${marker}${text.slice(to)}`\n write(next, [from + marker.length, to + marker.length])\n }\n\n /** Two spaces on or off the front of every line the selection touches. */\n const indent = (out: boolean) => {\n const element = textarea.current\n if (!element) return\n const text = docRef.current.draft\n const start = text.lastIndexOf('\\n', element.selectionStart - 1) + 1\n const end = text.indexOf('\\n', element.selectionEnd)\n const stop = end === -1 ? text.length : end\n const block = text.slice(start, stop)\n const shifted = block\n .split('\\n')\n .map((line) => (out ? line.replace(/^ {1,2}/, '') : ` ${line}`))\n .join('\\n')\n const moved = shifted.length - block.length\n write(`${text.slice(0, start)}${shifted}${text.slice(stop)}`, [\n Math.max(start, element.selectionStart + (out ? -2 : 2)),\n Math.max(start, element.selectionEnd + moved),\n ])\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n const meta = event.metaKey || event.ctrlKey\n const key = event.key.toLowerCase()\n\n if (meta && key === 's') {\n event.preventDefault()\n void save()\n return\n }\n if (meta && key === 'b') {\n event.preventDefault()\n wrap('**')\n return\n }\n if (meta && key === 'i') {\n event.preventDefault()\n wrap('*')\n return\n }\n if (meta && key === 'z') {\n event.preventDefault()\n const text = history.step(event.shiftKey ? 1 : -1)\n if (text !== null)\n setDoc({ draft: text, status: text === docRef.current.base ? 'saved' : 'dirty' })\n return\n }\n if (event.key === 'Tab') {\n // Only a list line takes Tab. Everywhere else it moves focus on, which is how a keyboard\n // reader gets out of a textarea.\n const at = event.currentTarget.selectionStart\n const line = docRef.current.draft.slice(\n docRef.current.draft.lastIndexOf('\\n', at - 1) + 1,\n at,\n )\n if (!/^\\s*([-*+]|\\d+[.)])\\s/.test(line)) return\n event.preventDefault()\n indent(event.shiftKey)\n }\n }\n\n const jumpTo = (heading: { line: number; text: string }) => {\n setOutlineOpen(false)\n if (showPreview && !showSource) {\n preview.current\n ?.querySelector(`[data-golem-heading=\"${headingSlug(heading.text)}\"]`)\n ?.scrollIntoView({ block: 'start' })\n return\n }\n overlay.current?.children[heading.line]?.scrollIntoView({ block: 'start' })\n const element = textarea.current\n if (!element) return\n const at = lines.slice(0, heading.line).reduce((sum, line) => sum + line.length + 1, 0)\n element.focus()\n element.setSelectionRange(at, at)\n }\n\n const choose = (index: number, side: 'mine' | 'theirs') => {\n const current = docRef.current\n if (current.conflict === null) return\n const choices = current.choices.map((one, at) => (at === index ? side : one))\n const undecided = current.conflict.some(\n (chunk, at) => chunk.kind === 'conflict' && choices[at] === null,\n )\n if (undecided) {\n setDoc({ choices })\n return\n }\n\n const { text, theirLines } = mergedText(current.conflict, (_chunk, at) => choices[at] ?? 'mine')\n history.remember(text)\n setDoc({\n draft: text,\n status: text === current.base ? 'saved' : 'dirty',\n conflict: null,\n choices: [],\n marks: config.highlightMs > 0 ? theirLines : [],\n })\n\n const later = pending.current\n pending.current = null\n if (later) applyIncoming(later)\n }\n\n return (\n <div\n ref={root}\n data-golem-component=\"Editor\"\n data-status={doc.status}\n className=\"golem-editor flex h-full min-h-0 w-full flex-col bg-white text-neutral-900\"\n >\n {wide && (\n <Toolbar\n doc={doc}\n config={config}\n headings={headings.length}\n outlineOpen={outlineOpen}\n onOutline={() => setOutlineOpen((open) => !open)}\n mode={mode}\n split={split}\n onMode={setMode}\n onWrap={wrap}\n onSave={() => void save()}\n clock={clock}\n />\n )}\n\n <div className=\"flex min-h-0 flex-1 flex-col sm:flex-row\">\n {config.outline && outlineOpen && (\n <Outline headings={headings} onJump={jumpTo} wide={wide} />\n )}\n\n {doc.conflict !== null ? (\n <Conflict chunks={doc.conflict} choices={doc.choices} onChoose={choose} />\n ) : (\n <>\n {showSource && (\n <div\n ref={sourcePane}\n data-golem-pane=\"source\"\n className={`min-h-0 flex-1 overflow-auto ${split ? 'sm:border-r sm:border-neutral-200' : ''}`}\n >\n <div className=\"relative min-h-full font-mono text-[13px] leading-6\">\n {/* The styled source, drawn under a textarea whose own text is transparent. Both\n wrap identically, so the caret always sits on the character it is on. */}\n <div\n ref={overlay}\n aria-hidden=\"true\"\n style={{ '--golem-mark-ms': `${config.highlightMs}ms` } as CSSProperties}\n className=\"pointer-events-none absolute inset-0 p-4 break-words whitespace-pre-wrap\"\n >\n {scanned.map((line, index) => {\n const focused = shown !== null && index >= shown.start && index < shown.end\n return (\n <div\n key={index}\n data-golem-marked={marked.has(index) ? 'true' : undefined}\n data-golem-focus={focused ? 'true' : undefined}\n className={`-mx-2 px-2 ${LINE_CLASS[line.kind]} ${\n marked.has(index) ? 'golem-editor-mark' : ''\n } ${focused ? 'golem-editor-focus' : ''}`}\n >\n {line.text === '' ? ' ' : <SourceRow line={line} index={index} />}\n </div>\n )\n })}\n </div>\n <textarea\n ref={textarea}\n value={doc.draft}\n readOnly={config.readOnly}\n aria-label=\"Document source\"\n placeholder={config.placeholder}\n spellCheck={false}\n onChange={(event) => write(event.target.value)}\n onKeyDown={onKeyDown}\n onPointerDown={() => setSpot(null)}\n className=\"relative block w-full resize-none overflow-hidden bg-transparent p-4 font-mono text-[13px] leading-6 break-words whitespace-pre-wrap text-transparent caret-neutral-900 outline-none placeholder:text-neutral-400\"\n />\n </div>\n </div>\n )}\n\n {showPreview && (\n <div\n ref={preview}\n data-golem-pane=\"preview\"\n className=\"min-h-0 flex-1 overflow-auto bg-neutral-50 p-4 text-[15px] leading-7 sm:p-6\"\n >\n <Markdown text={doc.draft} hardWraps={false} />\n </div>\n )}\n </>\n )}\n </div>\n\n {!wide && (\n <Toolbar\n doc={doc}\n config={config}\n headings={headings.length}\n outlineOpen={outlineOpen}\n onOutline={() => setOutlineOpen((open) => !open)}\n mode={mode}\n split={split}\n onMode={setMode}\n onWrap={wrap}\n onSave={() => void save()}\n clock={clock}\n bottom\n />\n )}\n </div>\n )\n}\n\nconst button =\n 'rounded-lg border border-neutral-300 bg-white px-2.5 py-2 text-sm font-medium disabled:opacity-40'\n\nfunction Toolbar({\n doc,\n config,\n headings,\n outlineOpen,\n onOutline,\n mode,\n split,\n onMode,\n onWrap,\n onSave,\n clock,\n bottom = false,\n}: {\n doc: Doc\n config: EditorConfig\n headings: number\n outlineOpen: boolean\n onOutline: () => void\n mode: 'source' | 'preview'\n split: boolean\n onMode: (mode: 'source' | 'preview') => void\n onWrap: (marker: string) => void\n onSave: () => void\n clock: ClockAdapter\n bottom?: boolean\n}) {\n return (\n <div\n data-golem-toolbar={bottom ? 'bottom' : 'top'}\n className={`flex shrink-0 flex-wrap items-center gap-2 bg-white px-3 py-2 ${\n bottom ? 'border-t border-neutral-200' : 'border-b border-neutral-200'\n }`}\n >\n {!config.readOnly && (\n <>\n <button type=\"button\" onClick={() => onWrap('**')} aria-label=\"Bold\" className={button}>\n <span className=\"font-bold\">B</span>\n </button>\n <button type=\"button\" onClick={() => onWrap('*')} aria-label=\"Italic\" className={button}>\n <span className=\"italic\">I</span>\n </button>\n </>\n )}\n\n {config.outline && headings > 0 && (\n <button type=\"button\" onClick={onOutline} aria-expanded={outlineOpen} className={button}>\n Outline\n </button>\n )}\n\n {config.preview !== 'off' && !split && (\n <button\n type=\"button\"\n onClick={() => onMode(mode === 'source' ? 'preview' : 'source')}\n className={button}\n >\n {mode === 'source' ? 'Preview' : 'Source'}\n </button>\n )}\n\n <Status doc={doc} config={config} clock={clock} />\n\n {!config.readOnly && (\n <button\n type=\"button\"\n onClick={onSave}\n disabled={doc.status !== 'dirty'}\n className=\"ml-auto rounded-lg bg-neutral-900 px-3 py-2 text-sm font-medium text-white disabled:opacity-40\"\n >\n Save\n </button>\n )}\n </div>\n )\n}\n\nfunction Status({ doc, config, clock }: { doc: Doc; config: EditorConfig; clock: ClockAdapter }) {\n const time = (at: Date) =>\n new Intl.DateTimeFormat(config.locale, {\n hour: '2-digit',\n minute: '2-digit',\n timeZone: clock.timeZone(),\n }).format(at)\n\n const line =\n doc.status === 'loading'\n ? 'Loading…'\n : doc.status === 'error'\n ? (doc.error ?? 'Something went wrong.')\n : doc.status === 'conflict'\n ? 'The agent changed lines you were editing.'\n : doc.status === 'saving'\n ? 'Saving…'\n : doc.status === 'dirty'\n ? 'Unsaved changes'\n : doc.savedAt\n ? `Saved ${time(doc.savedAt)}`\n : 'Saved'\n\n return (\n <p\n data-golem-status={doc.status}\n role={doc.status === 'error' ? 'alert' : undefined}\n className={`min-w-0 truncate text-xs ${\n doc.status === 'error' || doc.status === 'conflict' ? 'text-red-800' : 'text-neutral-500'\n }`}\n >\n {line}\n </p>\n )\n}\n\nfunction Outline({\n headings,\n onJump,\n wide,\n}: {\n headings: { line: number; level: number; text: string }[]\n onJump: (heading: { line: number; text: string }) => void\n wide: boolean\n}) {\n return (\n <nav\n data-golem-outline=\"\"\n aria-label=\"Outline\"\n className={`shrink-0 overflow-auto border-neutral-200 bg-neutral-50 p-2 ${\n wide ? 'w-56 border-r' : 'max-h-48 border-b'\n }`}\n >\n <ul className=\"space-y-0.5 text-sm\">\n {headings.map((heading) => (\n <li key={`${heading.line}-${heading.text}`}>\n <button\n type=\"button\"\n onClick={() => onJump(heading)}\n style={{ paddingLeft: `${(heading.level - 1) * 12 + 8}px` }}\n className=\"block w-full truncate rounded py-1 pr-2 text-left hover:bg-neutral-200\"\n >\n {heading.text}\n </button>\n </li>\n ))}\n </ul>\n </nav>\n )\n}\n\n/**\n * The lines the two writers disagree on, side by side, with the untouched document around them for\n * context. Nothing is applied until every one of them has been chosen: a half-resolved document is\n * one neither writer wrote.\n */\nfunction Conflict({\n chunks,\n choices,\n onChoose,\n}: {\n chunks: Merge3Chunk[]\n choices: ('mine' | 'theirs' | null)[]\n onChoose: (index: number, side: 'mine' | 'theirs') => void\n}) {\n return (\n <div data-golem-pane=\"conflict\" className=\"min-h-0 flex-1 overflow-auto p-3\">\n <p className=\"mb-3 rounded-lg bg-amber-50 px-3 py-2 text-sm text-amber-900\">\n The agent rewrote lines you were editing. Choose which version stays; the rest of the\n document is already merged.\n </p>\n <div className=\"space-y-2 font-mono text-[13px] leading-6\">\n {chunks.map((chunk, index) =>\n chunk.kind === 'clean' ? (\n <pre key={index} className=\"overflow-x-auto whitespace-pre-wrap text-neutral-500\">\n {chunk.lines.join('\\n')}\n </pre>\n ) : (\n <div\n key={index}\n data-golem-conflict=\"\"\n data-chosen={choices[index] ?? undefined}\n className=\"overflow-hidden rounded-lg border-2 border-amber-400\"\n >\n <Side\n label=\"Yours\"\n lines={chunk.mine}\n chosen={choices[index] === 'mine'}\n action=\"Keep mine\"\n onChoose={() => onChoose(index, 'mine')}\n />\n <Side\n label=\"The agent’s\"\n lines={chunk.theirs}\n chosen={choices[index] === 'theirs'}\n action=\"Take theirs\"\n onChoose={() => onChoose(index, 'theirs')}\n />\n </div>\n ),\n )}\n </div>\n </div>\n )\n}\n\nfunction Side({\n label,\n lines,\n chosen,\n action,\n onChoose,\n}: {\n label: string\n lines: string[]\n chosen: boolean\n action: string\n onChoose: () => void\n}): ReactNode {\n return (\n <div className={`border-b border-amber-200 p-2 last:border-0 ${chosen ? 'bg-emerald-50' : ''}`}>\n <div className=\"mb-1 flex items-center justify-between gap-2\">\n <span className=\"font-sans text-xs font-semibold text-neutral-500\">{label}</span>\n <button\n type=\"button\"\n onClick={onChoose}\n className=\"shrink-0 rounded-lg border border-neutral-300 bg-white px-2.5 py-1.5 font-sans text-xs font-medium\"\n >\n {action}\n </button>\n </div>\n <pre className=\"overflow-x-auto whitespace-pre-wrap\">{lines.join('\\n') || '(nothing)'}</pre>\n </div>\n )\n}\n\nexport const Editor = defineComponent<typeof editorConfigSchema, EditorAdapters, EditorSlots>({\n name: 'Editor',\n schema: editorConfigSchema,\n render: EditorBody,\n})\n","import type { RenderedField } from '../abi/fields'\n\nconst DATE_STYLES = ['short', 'medium', 'long', 'full'] as const\ntype DateStyle = (typeof DATE_STYLES)[number]\n\nfunction dateStyle(format: string | undefined, fallback: DateStyle): DateStyle {\n return DATE_STYLES.includes(format as DateStyle) ? (format as DateStyle) : fallback\n}\n\n/**\n * A `user` value is either the name as a string or a record with one on it, because a records\n * adapter may hand back either and the list should not care which.\n */\nexport function userName(value: unknown): string {\n if (typeof value === 'string') return value\n if (value && typeof value === 'object') {\n const named = value as { name?: unknown; email?: unknown }\n if (typeof named.name === 'string') return named.name\n if (typeof named.email === 'string') return named.email\n }\n return ''\n}\n\nexport function initials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean)\n if (parts.length === 0) return '·'\n return parts\n .slice(0, 2)\n .map((part) => part[0]!.toUpperCase())\n .join('')\n}\n\n/**\n * Every value is formatted in UTC, so a row reads the same in every timezone — for a local reading,\n * put local time in the record. An unparseable date falls back to the raw string rather than to\n * `Invalid Date`: the reader should see what the record actually holds.\n */\nexport function formatValue(value: unknown, field: RenderedField): string {\n if (value === null || value === undefined || value === '') return '—'\n\n switch (field.type) {\n case 'number': {\n const digits = Number(field.format)\n return new Intl.NumberFormat('en-US', {\n minimumFractionDigits: Number.isFinite(digits) ? digits : undefined,\n maximumFractionDigits: Number.isFinite(digits) ? digits : undefined,\n }).format(Number(value))\n }\n case 'money':\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: field.format || 'USD',\n }).format(Number(value))\n case 'date':\n case 'datetime': {\n const at = new Date(field.type === 'date' ? `${String(value)}T12:00:00Z` : String(value))\n if (Number.isNaN(at.getTime())) return String(value)\n return new Intl.DateTimeFormat('en-GB', {\n dateStyle: dateStyle(field.format, 'medium'),\n timeStyle: field.type === 'datetime' ? 'short' : undefined,\n timeZone: 'UTC',\n }).format(at)\n }\n case 'boolean':\n return value ? 'Yes' : 'No'\n case 'user':\n return userName(value) || String(value)\n default:\n return String(value)\n }\n}\n\nconst CHIP_TONES = [\n 'bg-emerald-100 text-emerald-800',\n 'bg-amber-100 text-amber-800',\n 'bg-sky-100 text-sky-800',\n 'bg-violet-100 text-violet-800',\n 'bg-rose-100 text-rose-800',\n 'bg-neutral-200 text-neutral-700',\n]\n\n/**\n * An enum chip's colour is derived from the value itself, so the same status is the same colour on\n * every screen and no config has to name a palette.\n */\nexport function chipTone(value: string): string {\n let hash = 0\n for (let i = 0; i < value.length; i++) hash = (hash * 31 + value.charCodeAt(i)) >>> 0\n return CHIP_TONES[hash % CHIP_TONES.length]!\n}\n","import { z } from 'zod'\nimport { formFieldSchema } from '../../abi/fields'\n\nexport const recordFormConfigSchema = z\n .object({\n collection: z\n .string()\n .min(1)\n .describe('The record type the adapter is asked for, passed to every read and every write.'),\n fields: z\n .array(formFieldSchema)\n .min(1)\n .describe(\n 'The controls, in the order they are shown. Each is `{ key, label, type, … }` from the same field vocabulary `RecordList` reads: text, number, date, datetime, enum, boolean, money, user.',\n ),\n mode: z\n .enum(['create', 'edit'])\n .describe(\n '`create` opens blank and calls `create`; `edit` loads the record named by the `recordId` prop and calls `update` with only what changed.',\n ),\n versionField: z\n .string()\n .min(1)\n .default('version')\n .describe(\n 'The field holding a number that goes up on every write. When the loaded record has a number there, saving sends it as `expectedVersion`, so a save that arrived second is refused and the reader chooses whose values win. A record with no number there saves as a plain update.',\n ),\n layout: z\n .enum(['single', 'two-column'])\n .default('single')\n .describe(\n 'How the controls are arranged above 768px: `single` is one column at a readable width, `two-column` pairs them. Below 768px both are one column, whatever this says.',\n ),\n submitLabel: z\n .string()\n .min(1)\n .default('Save')\n .describe('The button that writes. Name the outcome — \"Save ticket\", not \"Submit\".'),\n cancel: z\n .enum(['back', 'none'])\n .default('back')\n .describe(\n '`back` puts a Cancel button beside submit and calls the `onCancel` prop; `none` leaves the form with no way out, for a form that fills a page of its own.',\n ),\n successMessage: z\n .string()\n .min(1)\n .default('Saved.')\n .describe('The line shown once the adapter has taken the write.'),\n deleteAllowed: z\n .boolean()\n .default(false)\n .describe(\n 'Adds a Delete button that asks once before calling `remove`. Only in `edit` mode, and only for a reader allowed to delete — the call site decides that by what it puts here.',\n ),\n })\n .strict()\n .superRefine((config, ctx) => {\n for (const [index, field] of config.fields.entries()) {\n if (field.type === 'enum' && !field.options) {\n ctx.addIssue({\n code: 'custom',\n path: ['fields', index, 'options'],\n message: `The enum field \"${field.key}\" has no \\`options\\`, so there is nothing to pick. Give it \\`[{ id, label }]\\`.`,\n })\n }\n if (field.type !== 'enum' && field.options) {\n ctx.addIssue({\n code: 'custom',\n path: ['fields', index, 'options'],\n message: `\\`options\\` belongs to an enum field, and \"${field.key}\" is typed ${field.type}. Change the type or drop the options.`,\n })\n }\n if (field.required && field.readOnly) {\n ctx.addIssue({\n code: 'custom',\n path: ['fields', index],\n message: `The field \"${field.key}\" is both \\`required\\` and \\`readOnly\\`, so nobody can ever satisfy it. Pick one.`,\n })\n }\n if (field.min !== undefined && field.max !== undefined && field.min > field.max) {\n ctx.addIssue({\n code: 'custom',\n path: ['fields', index, 'min'],\n message: `The field \"${field.key}\" has a \\`min\\` above its \\`max\\`, so no value passes.`,\n })\n }\n }\n if (config.mode === 'create' && config.deleteAllowed) {\n ctx.addIssue({\n code: 'custom',\n path: ['deleteAllowed'],\n message: 'A record being created cannot be deleted. `deleteAllowed` belongs to edit mode.',\n })\n }\n })\n\nexport type RecordFormConfig = z.output<typeof recordFormConfigSchema>\nexport type RecordFormConfigInput = z.input<typeof recordFormConfigSchema>\n","import type { FormField, RecordRow } from '../../abi/fields'\nimport { formatValue } from '../../lib/format'\n\n/** What an untouched control holds in create mode. A checkbox is off; everything else is blank. */\nexport function blankValue(field: FormField): unknown {\n return field.type === 'boolean' ? false : ''\n}\n\nexport function isEmpty(value: unknown): boolean {\n return value === null || value === undefined || value === ''\n}\n\n/**\n * The record's value as the control shows it. `datetime` is the one that has to be reshaped: a\n * record holds a full ISO instant and `datetime-local` takes minutes in UTC.\n */\nexport function toInput(value: unknown, field: FormField): string {\n if (isEmpty(value)) return ''\n if (field.type === 'datetime') {\n const at = new Date(String(value))\n return Number.isNaN(at.getTime()) ? String(value) : at.toISOString().slice(0, 16)\n }\n return String(value)\n}\n\n/** The control's text as the record holds it. An empty control writes an empty string, never `0`. */\nexport function fromInput(text: string, field: FormField): unknown {\n if (text === '') return ''\n if (field.type === 'number' || field.type === 'money') {\n const parsed = Number(text)\n // A number the browser could not parse is kept as typed, so validation can name it.\n return Number.isNaN(parsed) ? text : parsed\n }\n if (field.type === 'datetime') {\n const at = new Date(`${text}Z`)\n return Number.isNaN(at.getTime()) ? text : at.toISOString()\n }\n return text\n}\n\n/** Numbers compare as numbers; dates are ISO, so they compare as strings. */\nfunction below(value: unknown, bound: unknown, field: FormField): boolean {\n if (field.type === 'number' || field.type === 'money') return Number(value) < Number(bound)\n return String(value) < String(bound)\n}\n\n/**\n * One field's complaint, or `null` when it is fine. The sentence is what the reader sees under the\n * control, so it names the field the way its label does.\n */\nexport function validateField(field: FormField, value: unknown): string | null {\n if (field.readOnly) return null\n\n if (isEmpty(value)) {\n return field.required ? `${field.label} is required.` : null\n }\n if ((field.type === 'number' || field.type === 'money') && typeof value !== 'number') {\n return `${field.label} must be a number.`\n }\n if (field.type === 'enum' && !field.options?.some((option) => option.id === value)) {\n return `${field.label} must be one of the offered choices.`\n }\n if (field.min !== undefined && below(value, field.min, field)) {\n return `${field.label} must be ${formatValue(field.min, field)} or more.`\n }\n if (field.max !== undefined && below(field.max, value, field)) {\n return `${field.label} must be ${formatValue(field.max, field)} or less.`\n }\n return null\n}\n\n/** Every editable field that fails, in the order the form shows them. */\nexport function validateAll(\n fields: FormField[],\n values: RecordRow,\n): { key: string; message: string }[] {\n const failures: { key: string; message: string }[] = []\n for (const field of fields) {\n const message = validateField(field, values[field.key])\n if (message !== null) failures.push({ key: field.key, message })\n }\n return failures\n}\n\n/** Every editable field's value, which is what `create` sends: a new record has nothing to diff. */\nexport function dataOf(fields: FormField[], values: RecordRow): RecordRow {\n const data: RecordRow = {}\n for (const field of fields) data[field.key] = values[field.key]\n return data\n}\n\n/** What the reader actually changed. `readOnly` fields are never in it: the store owns them. */\nexport function patchOf(fields: FormField[], values: RecordRow, initial: RecordRow): RecordRow {\n const patch: RecordRow = {}\n for (const field of fields) {\n if (field.readOnly) continue\n if (values[field.key] !== initial[field.key]) patch[field.key] = values[field.key]\n }\n return patch\n}\n","import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type FormEvent,\n type ReactNode,\n} from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { FormField, RecordRow } from '../../abi/fields'\nimport {\n conflictingRecord,\n refusedFields,\n type IdentityAdapter,\n type RecordsAdapter,\n type User,\n} from '../../adapters'\nimport { chipTone, formatValue, initials, userName } from '../../lib/format'\nimport { useContainerWidth } from '../../lib/use-container-width'\nimport { recordFormConfigSchema, type RecordFormConfig } from './RecordForm.config'\nimport {\n blankValue,\n dataOf,\n fromInput,\n patchOf,\n toInput,\n validateAll,\n validateField,\n} from './values'\n\nexport interface RecordFormAdapters {\n records: RecordsAdapter\n /** Read only for a `user` field whose choices come from `users: 'members'`. */\n identity?: IdentityAdapter\n}\n\nexport interface RecordFormSlots {\n /**\n * The record `edit` mode loads and writes. The route knows it and the config does not, which is\n * why it is a prop: one config serves every ticket on the board.\n */\n recordId?: string\n /** After a write has landed: the stored row, or `null` when the record was deleted. */\n onDone?: (row: RecordRow | null) => void\n /** Cancel, once the reader has confirmed discarding whatever was unsaved. */\n onCancel?: () => void\n}\n\n/** One column below this width, whatever `layout` says. It is Shell's default breakpoint. */\nconst ONE_COLUMN_BELOW = 768\n\nconst messageOf = (cause: unknown) =>\n cause instanceof Error ? cause.message : 'The write did not go through. Try again.'\n\nconst controlClass =\n 'mt-1 w-full rounded-lg border bg-white px-3 py-2.5 text-base text-neutral-900 sm:text-sm dark:bg-neutral-900 dark:text-neutral-100'\nconst okBorder = 'border-neutral-300 dark:border-neutral-700'\nconst badBorder = 'border-red-400'\n\n/**\n * Loads the record `edit` mode is about. Create mode never asks, and neither does edit mode with no\n * id — both are answered without a round trip. `null` means a load is still in flight.\n */\nfunction useLoadedRecord(\n records: RecordsAdapter,\n config: RecordFormConfig,\n recordId: string | undefined,\n) {\n const [state, setState] = useState<{ id: string; row: RecordRow | null; error: string | null }>()\n const wanted = config.mode === 'edit' ? recordId : undefined\n\n useEffect(() => {\n if (wanted === undefined) return undefined\n let live = true\n void records.get<RecordRow>(config.collection, wanted).then(\n (row) => {\n if (!live) return\n setState({\n id: wanted,\n row,\n error: row ? null : `There is no ${config.collection} record with the id ${wanted}.`,\n })\n },\n (cause: unknown) => {\n if (live) setState({ id: wanted, row: null, error: messageOf(cause) })\n },\n )\n return () => {\n live = false\n }\n }, [records, config.collection, wanted])\n\n if (config.mode !== 'edit') return { row: null, error: null }\n if (recordId === undefined) {\n return { row: null, error: 'This form is in edit mode with no `recordId` to load.' }\n }\n // A result for a record the form has moved on from is still in flight for this one.\n return state?.id === recordId ? state : null\n}\n\n/** The member list a `user` field picks from. Absent Identity leaves it empty, and the field says so. */\nfunction useMembers(config: RecordFormConfig, identity: IdentityAdapter | undefined) {\n const [members, setMembers] = useState<User[] | null>(null)\n const wanted = config.fields.some((field) => field.type === 'user' && field.users === 'members')\n\n useEffect(() => {\n if (!wanted || !identity) return undefined\n let live = true\n void identity.listMembers().then(\n (list) => {\n if (live) setMembers(list)\n },\n () => {\n if (live) setMembers([])\n },\n )\n return () => {\n live = false\n }\n }, [wanted, identity])\n\n return members\n}\n\nfunction ReadOnlyValue({ field, value }: { field: FormField; value: unknown }): ReactNode {\n if (field.type === 'enum') {\n const label = field.options?.find((option) => option.id === value)?.label ?? String(value ?? '')\n if (label === '') return <span className=\"text-neutral-400\">—</span>\n return (\n <span\n className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${chipTone(String(value))}`}\n >\n {label}\n </span>\n )\n }\n if (field.type === 'user') {\n const name = userName(value) || String(value ?? '')\n if (name === '') return <span className=\"text-neutral-400\">—</span>\n return (\n <span className=\"flex items-center gap-2\">\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-full bg-neutral-900 text-[10px] font-semibold text-white dark:bg-neutral-200 dark:text-neutral-900\">\n {initials(name)}\n </span>\n {name}\n </span>\n )\n }\n return <span>{formatValue(value, field)}</span>\n}\n\nfunction RecordFormBody({\n config,\n adapters,\n recordId,\n onDone,\n onCancel,\n}: GolemProps<RecordFormConfig, RecordFormAdapters, RecordFormSlots>) {\n const formId = useId()\n const loaded = useLoadedRecord(adapters.records, config, recordId)\n const members = useMembers(config, adapters.identity)\n\n const blank = useMemo(() => {\n const start: RecordRow = {}\n for (const field of config.fields) start[field.key] = blankValue(field)\n return start\n }, [config.fields])\n\n // Nothing is copied out of the loaded record into state: `base` is what the store says plus what\n // has already been written, and `edits` is what the reader has changed on top of it. That keeps\n // one source of truth for every value and makes \"dirty\" a comparison rather than a flag.\n const [edits, setEdits] = useState<RecordRow>({})\n const [committed, setCommitted] = useState<RecordRow>({})\n const [errors, setErrors] = useState<Record<string, string>>({})\n const [formError, setFormError] = useState<string | null>(null)\n const [saved, setSaved] = useState(false)\n const [busy, setBusy] = useState(false)\n const [asking, setAsking] = useState<'cancel' | 'delete' | null>(null)\n // The row a stale save was refused over, and the fields on this form it changed.\n const [conflict, setConflict] = useState<{ row: RecordRow; changed: FormField[] } | null>(null)\n\n const controls = useRef<Record<string, HTMLElement | null>>({})\n\n const baseOf = useCallback(\n (row: RecordRow) => {\n const start: RecordRow = { ...blank }\n for (const field of config.fields) {\n if (row[field.key] !== undefined) start[field.key] = row[field.key]\n }\n return start\n },\n [blank, config.fields],\n )\n\n // The row as the store was last known to hold it: loaded, then every write and conflict since.\n const stored = useMemo(() => ({ ...loaded?.row, ...committed }), [loaded, committed])\n const base = useMemo(() => baseOf(stored), [baseOf, stored])\n\n const values = useMemo(() => ({ ...base, ...edits }), [base, edits])\n\n const editable = useMemo(() => config.fields.filter((field) => !field.readOnly), [config.fields])\n const dirty = editable.some((field) => values[field.key] !== base[field.key])\n\n const root = useRef<HTMLDivElement>(null)\n const width = useContainerWidth(root)\n const columns = config.layout === 'two-column' && width >= ONE_COLUMN_BELOW ? 2 : 1\n\n const put = (field: FormField, value: unknown) => {\n setSaved(false)\n setEdits((prev) => ({ ...prev, [field.key]: value }))\n }\n\n const check = (field: FormField) =>\n setErrors((prev) => {\n const message = validateField(field, values[field.key])\n const next = { ...prev }\n if (message === null) delete next[field.key]\n else next[field.key] = message\n return next\n })\n\n const land = useCallback((failures: { key: string; message: string }[]) => {\n setErrors(Object.fromEntries(failures.map(({ key, message }) => [key, message])))\n controls.current[failures[0]!.key]?.focus()\n }, [])\n\n const versionOf = (row: RecordRow) => {\n const version = row[config.versionField]\n return typeof version === 'number' ? version : undefined\n }\n\n /**\n * The one write. `from` is what the store is taken to hold and `next` what the form shows. An edit\n * of a versioned record goes out with the version it was read at, so a save that arrived second\n * is refused with the row as it now stands, and the reader chooses instead of anyone overwriting.\n */\n const write = (from: RecordRow, next: RecordRow, expected: number | undefined) => {\n setFormError(null)\n setErrors({})\n setBusy(true)\n // Optimistic: the form says it is saved before the adapter has answered, and a refusal takes\n // it back. The reader sees the common case immediately and the rare one accurately.\n setSaved(true)\n\n const patch = patchOf(editable, next, from)\n const request =\n config.mode === 'create'\n ? adapters.records.create<RecordRow>(config.collection, dataOf(editable, next))\n : expected === undefined\n ? adapters.records.update<RecordRow>(config.collection, recordId!, patch)\n : adapters.records.update<RecordRow>(\n config.collection,\n recordId!,\n { ...patch, [config.versionField]: expected + 1 },\n { expectedVersion: expected, versionField: config.versionField },\n )\n\n void request.then(\n (row) => {\n setBusy(false)\n // What was just written stops counting as unsaved: create empties the form, edit folds the\n // changes into what the store is now taken to hold.\n if (config.mode === 'edit') setCommitted((prev) => ({ ...prev, ...next, ...row }))\n setEdits({})\n onDone?.(row)\n },\n (cause: unknown) => {\n setBusy(false)\n setSaved(false)\n const theirs = conflictingRecord(cause)\n if (theirs) {\n const now = baseOf(theirs)\n setConflict({\n row: theirs,\n // The version is bookkeeping, not something the reader chooses between.\n changed: config.fields.filter(\n (field) => field.key !== config.versionField && now[field.key] !== from[field.key],\n ),\n })\n return\n }\n const refused = refusedFields(cause)\n if (refused) land(refused.map((one) => ({ key: one.field, message: one.message })))\n else setFormError(messageOf(cause))\n },\n )\n }\n\n /** Drops the unsaved values and shows the record as the other writer left it. */\n const takeTheirs = () => {\n setCommitted((prev) => ({ ...prev, ...conflict!.row }))\n setEdits({})\n setConflict(null)\n setSaved(false)\n }\n\n /**\n * Writes the reader's changes over the newer version. Only fields the reader actually changed go\n * out, so whatever else the other writer changed survives.\n */\n const keepMine = () => {\n // The controls stay live while the choice is open, so what is on screen is checked again.\n const failures = validateAll(editable, values)\n if (failures.length > 0) {\n land(failures)\n return\n }\n const mine = Object.fromEntries(\n Object.entries(edits).filter(([key, value]) => value !== base[key]),\n )\n const row = { ...stored, ...conflict!.row }\n setCommitted((prev) => ({ ...prev, ...conflict!.row }))\n setEdits(mine)\n setConflict(null)\n const from = baseOf(row)\n write(from, { ...from, ...mine }, versionOf(row))\n }\n\n const submit = (event: FormEvent) => {\n event.preventDefault()\n // Enter in a control while the conflict is open must not pick a side for the reader.\n if (conflict !== null) return\n setFormError(null)\n\n const failures = validateAll(editable, values)\n if (failures.length > 0) {\n land(failures)\n return\n }\n\n write(base, values, versionOf(stored))\n }\n\n const cancel = () => {\n if (dirty) setAsking('cancel')\n else onCancel?.()\n }\n\n const remove = () => {\n setBusy(true)\n setFormError(null)\n void adapters.records.remove(config.collection, recordId!).then(\n () => {\n setBusy(false)\n setAsking(null)\n onDone?.(null)\n },\n (cause: unknown) => {\n setBusy(false)\n setAsking(null)\n setFormError(messageOf(cause))\n },\n )\n }\n\n if (loaded === null) {\n return (\n <p className=\"rounded-xl border border-neutral-200 bg-white px-4 py-8 dark:border-neutral-800 dark:bg-neutral-900 text-center text-sm text-neutral-500 dark:text-neutral-400\">\n Loading…\n </p>\n )\n }\n if (loaded.error !== null) {\n return (\n <p\n role=\"alert\"\n className=\"rounded-xl border border-red-300 bg-red-50 px-4 py-6 text-center text-sm text-red-900 dark:border-red-800 dark:bg-red-950 dark:text-red-200\"\n >\n {loaded.error}\n </p>\n )\n }\n\n const control = (field: FormField): ReactNode => {\n const id = `${formId}-${field.key}`\n const bad = errors[field.key] !== undefined\n const described = [field.help ? `${id}-help` : null, bad ? `${id}-error` : null]\n .filter(Boolean)\n .join(' ')\n const shared = {\n id,\n name: field.key,\n 'aria-invalid': bad || undefined,\n 'aria-describedby': described || undefined,\n 'aria-required': field.required || undefined,\n onBlur: () => check(field),\n className: `${controlClass} ${bad ? badBorder : okBorder}`,\n }\n const keep = (element: HTMLElement | null) => {\n controls.current[field.key] = element\n }\n\n if (field.type === 'boolean') {\n return (\n <input\n {...shared}\n ref={keep}\n type=\"checkbox\"\n checked={values[field.key] === true}\n onChange={(event) => put(field, event.target.checked)}\n className={`mt-1 size-5 rounded border ${bad ? badBorder : okBorder}`}\n />\n )\n }\n if (field.type === 'enum') {\n return (\n <select\n {...shared}\n ref={keep}\n value={String(values[field.key] ?? '')}\n onChange={(event) => put(field, event.target.value)}\n >\n <option value=\"\">{field.placeholder ?? 'Choose…'}</option>\n {field.options?.map((option) => (\n <option key={option.id} value={option.id}>\n {option.label}\n </option>\n ))}\n </select>\n )\n }\n if (field.type === 'user' && field.users === 'members') {\n return (\n <select\n {...shared}\n ref={keep}\n value={String(values[field.key] ?? '')}\n onChange={(event) => put(field, event.target.value)}\n >\n <option value=\"\">\n {members === null || members.length === 0 ? 'No members to pick from' : 'Choose…'}\n </option>\n {members?.map((member) => (\n <option key={member.id} value={member.name}>\n {member.name}\n </option>\n ))}\n </select>\n )\n }\n if (field.type === 'text' && field.multiline) {\n return (\n <textarea\n {...shared}\n ref={keep}\n rows={4}\n placeholder={field.placeholder}\n value={toInput(values[field.key], field)}\n onChange={(event) => put(field, event.target.value)}\n />\n )\n }\n\n const numeric = field.type === 'number' || field.type === 'money'\n return (\n <input\n {...shared}\n ref={keep}\n type={\n field.type === 'date'\n ? 'date'\n : field.type === 'datetime'\n ? 'datetime-local'\n : numeric\n ? 'number'\n : 'text'\n }\n step={field.type === 'money' ? '0.01' : undefined}\n min={numeric && field.min !== undefined ? Number(field.min) : undefined}\n max={numeric && field.max !== undefined ? Number(field.max) : undefined}\n placeholder={field.placeholder}\n value={toInput(values[field.key], field)}\n onChange={(event) => put(field, fromInput(event.target.value, field))}\n />\n )\n }\n\n const actions = (() => {\n if (asking === 'cancel') {\n return (\n <div role=\"alertdialog\" aria-label=\"Discard changes\" className=\"flex flex-wrap gap-2\">\n <p className=\"w-full text-sm text-neutral-700 dark:text-neutral-300\">\n You have changes that have not been saved.\n </p>\n <button\n type=\"button\"\n onClick={() => {\n setAsking(null)\n onCancel?.()\n }}\n className=\"rounded-lg bg-red-600 px-4 py-2.5 text-sm font-medium text-white\"\n >\n Discard them\n </button>\n <button\n type=\"button\"\n onClick={() => setAsking(null)}\n className=\"rounded-lg border border-neutral-300 dark:border-neutral-700 px-4 py-2.5 text-sm font-medium\"\n >\n Keep editing\n </button>\n </div>\n )\n }\n if (conflict !== null) {\n return (\n <div role=\"alertdialog\" aria-label=\"Saved by someone else\" className=\"flex flex-wrap gap-2\">\n <p className=\"w-full text-sm font-medium text-amber-800 dark:text-amber-300\">\n Someone else saved this record after you opened it. Your changes are not saved yet.\n </p>\n {conflict.changed.length === 0 ? (\n <p className=\"w-full text-sm text-neutral-700 dark:text-neutral-300\">\n None of the fields here changed.\n </p>\n ) : (\n <dl className=\"mb-1 grid w-full grid-cols-[auto_1fr_1fr] gap-x-3 gap-y-1 text-sm\">\n <dt className=\"sr-only\">Field</dt>\n <dd className=\"col-start-2 text-xs text-neutral-500 dark:text-neutral-400\">\n Saved now\n </dd>\n <dd className=\"text-xs text-neutral-500 dark:text-neutral-400\">Yours</dd>\n {conflict.changed.map((field) => (\n <div key={field.key} className=\"contents\">\n <dt className=\"font-medium text-neutral-700 dark:text-neutral-300\">\n {field.label}\n </dt>\n <dd className=\"min-w-0 break-words\">\n <ReadOnlyValue field={field} value={conflict.row[field.key]} />\n </dd>\n <dd className=\"min-w-0 break-words\">\n {edits[field.key] !== undefined && edits[field.key] !== base[field.key] ? (\n <ReadOnlyValue field={field} value={values[field.key]} />\n ) : (\n <span className=\"text-neutral-400\">not changed</span>\n )}\n </dd>\n </div>\n ))}\n </dl>\n )}\n <button\n type=\"button\"\n onClick={keepMine}\n disabled={busy}\n className=\"rounded-lg bg-neutral-900 px-4 py-2.5 text-sm font-medium text-white dark:bg-neutral-200 dark:text-neutral-900 disabled:opacity-60\"\n >\n Keep mine\n </button>\n <button\n type=\"button\"\n onClick={takeTheirs}\n disabled={busy}\n className=\"rounded-lg border border-neutral-300 dark:border-neutral-700 px-4 py-2.5 text-sm font-medium\"\n >\n Take theirs\n </button>\n {config.cancel === 'back' && (\n <button\n type=\"button\"\n onClick={cancel}\n className=\"rounded-lg border border-neutral-300 dark:border-neutral-700 px-4 py-2.5 text-sm font-medium\"\n >\n Cancel\n </button>\n )}\n </div>\n )\n }\n if (asking === 'delete') {\n return (\n <div role=\"alertdialog\" aria-label=\"Confirm delete\" className=\"flex flex-wrap gap-2\">\n <p className=\"w-full text-sm text-neutral-700 dark:text-neutral-300\">\n Deleting this record cannot be undone.\n </p>\n <button\n type=\"button\"\n onClick={remove}\n disabled={busy}\n className=\"rounded-lg bg-red-600 px-4 py-2.5 text-sm font-medium text-white disabled:opacity-60\"\n >\n Delete permanently\n </button>\n <button\n type=\"button\"\n onClick={() => setAsking(null)}\n className=\"rounded-lg border border-neutral-300 dark:border-neutral-700 px-4 py-2.5 text-sm font-medium\"\n >\n Keep it\n </button>\n </div>\n )\n }\n return (\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"submit\"\n disabled={busy}\n className=\"flex-1 rounded-lg bg-neutral-900 px-4 py-2.5 text-sm font-medium text-white dark:bg-neutral-200 dark:text-neutral-900 disabled:opacity-60 sm:flex-none\"\n >\n {config.submitLabel}\n </button>\n {config.cancel === 'back' && (\n <button\n type=\"button\"\n onClick={cancel}\n className=\"rounded-lg border border-neutral-300 dark:border-neutral-700 px-4 py-2.5 text-sm font-medium\"\n >\n Cancel\n </button>\n )}\n {config.deleteAllowed && (\n <button\n type=\"button\"\n onClick={() => setAsking('delete')}\n className=\"ml-auto rounded-lg border border-red-300 px-4 dark:border-red-800 py-2.5 text-sm font-medium text-red-700 dark:text-red-300\"\n >\n Delete\n </button>\n )}\n </div>\n )\n })()\n\n return (\n <div\n ref={root}\n data-golem-component=\"RecordForm\"\n data-layout={columns === 2 ? 'two-column' : 'single'}\n className=\"golem-record-form w-full text-neutral-900 dark:text-neutral-100 dark:[color-scheme:dark]\"\n >\n <form onSubmit={submit} noValidate>\n <div className={`grid gap-4 ${columns === 2 ? 'grid-cols-2' : 'grid-cols-1'}`}>\n {config.fields.map((field) => {\n const id = `${formId}-${field.key}`\n const wide = columns === 2 && field.multiline ? 'col-span-2' : ''\n return (\n <div key={field.key} className={wide}>\n <label\n htmlFor={id}\n className=\"block text-sm font-medium text-neutral-700 dark:text-neutral-300\"\n >\n {field.label}\n {field.required && (\n <span aria-hidden=\"true\" className=\"text-red-600 dark:text-red-400\">\n {' '}\n *\n </span>\n )}\n </label>\n {field.readOnly ? (\n <p\n id={id}\n className=\"mt-1 rounded-lg bg-neutral-100 px-3 dark:bg-neutral-800 py-2.5 text-sm text-neutral-700 dark:text-neutral-300\"\n >\n <ReadOnlyValue field={field} value={values[field.key]} />\n </p>\n ) : (\n control(field)\n )}\n {field.help && (\n <p\n id={`${id}-help`}\n className=\"mt-1 text-xs text-neutral-500 dark:text-neutral-400\"\n >\n {field.help}\n </p>\n )}\n {errors[field.key] && (\n <p\n id={`${id}-error`}\n role=\"alert\"\n className=\"mt-1 text-xs font-medium text-red-700 dark:text-red-300\"\n >\n {errors[field.key]}\n </p>\n )}\n </div>\n )\n })}\n </div>\n\n {/* Thumb-reachable on a phone, and it stays put while a long form scrolls under it. */}\n <div className=\"sticky bottom-0 mt-5 border-t border-neutral-200 bg-white py-3 dark:border-neutral-800 dark:bg-neutral-900\">\n {formError !== null && (\n <p role=\"alert\" className=\"mb-2 text-sm font-medium text-red-700 dark:text-red-300\">\n {formError}\n </p>\n )}\n {saved && (\n <p\n role=\"status\"\n className=\"mb-2 text-sm font-medium text-emerald-700 dark:text-emerald-300\"\n >\n {config.successMessage}\n </p>\n )}\n {config.mode === 'edit' && dirty && !saved && (\n <p className=\"mb-2 text-xs font-medium text-amber-700 dark:text-amber-300\">\n Unsaved changes\n </p>\n )}\n {actions}\n </div>\n </form>\n </div>\n )\n}\n\nexport const RecordForm = defineComponent<\n typeof recordFormConfigSchema,\n RecordFormAdapters,\n RecordFormSlots\n>({\n name: 'RecordForm',\n schema: recordFormConfigSchema,\n render: RecordFormBody,\n})\n","import { z } from 'zod'\nimport { listFieldSchema } from '../../abi/fields'\n\nexport const recordListConfigSchema = z\n .object({\n collection: z\n .string()\n .min(1)\n .describe('The record type the adapter is asked for, passed to `list` and `subscribe`.'),\n fields: z\n .array(listFieldSchema)\n .min(1)\n .describe(\n 'The columns, in the order they are shown. Each is `{ key, label, type, format?, primary? }`, where `type` is one of text, number, date, datetime, enum, boolean, money, user.',\n ),\n sort: z\n .object({\n field: z.string().min(1).describe('The `key` of the field rows are ordered by.'),\n direction: z.enum(['asc', 'desc']).describe('Ascending or descending, to start with.'),\n })\n .strict()\n .optional()\n .describe(\n 'The order the list opens in. The reader changes it from the column headings; leave it out and the adapter’s own order is what arrives.',\n ),\n scope: z\n .record(\n z.string(),\n z.union([\n z.string(),\n z.number(),\n z.boolean(),\n z.array(z.union([z.string(), z.number(), z.boolean()])),\n ]),\n )\n .default({})\n .describe(\n 'A fixed narrowing applied to every query and never shown as a control: `{ status: [\"waiting\", \"in progress\"] }` is a list of what is still open. An array means any of. This is how one collection feeds several screens.',\n ),\n filters: z\n .array(z.string().min(1))\n .default([])\n .describe(\n 'Field keys that get a row of filter chips above the list, one chip per value seen. Use it for `enum` and `boolean` fields; every key must also appear in `fields`.',\n ),\n search: z\n .array(z.string().min(1))\n .default([])\n .describe(\n 'Field keys the search box matches, as a case-insensitive substring. Empty hides the search box. Keys need not appear in `fields` — a list can be searchable by a field it does not show.',\n ),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(200)\n .default(25)\n .describe('How many rows arrive per page, and how many more the load-more button fetches.'),\n emptyState: z\n .string()\n .min(1)\n .default('Nothing here yet.')\n .describe('One line shown in place of the list when a query matches no rows.'),\n rowAction: z\n .enum(['open', 'none'])\n .default('open')\n .describe(\n '`open` makes each row clickable and focusable and calls the `onOpen` prop with the record; `none` makes the list read-only.',\n ),\n density: z\n .enum(['compact', 'comfortable'])\n .default('comfortable')\n .describe(\n 'Row height. `comfortable` for a screen of its own, `compact` for a list sitting inside a panel among other blocks.',\n ),\n })\n .strict()\n .superRefine((config, ctx) => {\n const keys = new Set(config.fields.map((field) => field.key))\n\n if (config.fields.filter((field) => field.primary).length > 1) {\n ctx.addIssue({\n code: 'custom',\n path: ['fields'],\n message: 'More than one field sets `primary`. Exactly one field titles a row.',\n })\n }\n for (const [index, key] of config.filters.entries()) {\n if (!keys.has(key)) {\n ctx.addIssue({\n code: 'custom',\n path: ['filters', index],\n message: `No field has the key \"${key}\", so there is nothing to filter. Add it to \\`fields\\` first.`,\n })\n }\n }\n if (config.sort && !keys.has(config.sort.field)) {\n ctx.addIssue({\n code: 'custom',\n path: ['sort', 'field'],\n message: `No field has the key \"${config.sort.field}\", so the list cannot be sorted by it.`,\n })\n }\n })\n\nexport type RecordListConfig = z.output<typeof recordListConfigSchema>\nexport type RecordListConfigInput = z.input<typeof recordListConfigSchema>\n","import {\n Fragment,\n useCallback,\n useId,\n useEffect,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n} from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { FilterValue, RecordsAdapter } from '../../adapters'\nimport { useContainerWidth } from '../../lib/use-container-width'\nimport type { ListField, RecordRow } from '../../abi/fields'\nimport { chipTone, formatValue, initials, userName } from '../../lib/format'\nimport { recordListConfigSchema, type RecordListConfig } from './RecordList.config'\n\nexport type { RecordRow }\n\nexport interface RecordListAdapters {\n records: RecordsAdapter\n}\n\nexport interface RecordListSlots {\n /**\n * Where a row goes when it is opened — a click, Enter or Space. Only reached with\n * `rowAction: 'open'`; the list has no router of its own, so the call site names the route.\n */\n onOpen?: (row: RecordRow) => void\n}\n\n/**\n * The width the table gives way to cards. It is Shell's default breakpoint, so a list in Shell's\n * canvas turns into cards on the same phone that turns Shell into tabs.\n */\nconst CARDS_BELOW = 768\n\ntype Sort = { field: string; direction: 'asc' | 'desc' }\ntype FacetValue = string | number | boolean\n\nfunction isFacet(value: unknown): value is FacetValue {\n return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'\n}\n\ninterface Result {\n /** The query these rows answer. A result whose key is not the current one is stale. */\n key: string\n rows: RecordRow[]\n nextCursor: string | null\n error: string | null\n}\n\nconst message = (cause: unknown) => (cause instanceof Error ? cause.message : String(cause))\n\n/**\n * Pages, live updates, and the filter chips' vocabulary — everything that arrives from the adapter.\n *\n * The chips offer the values that have actually arrived, and keep offering them once a filter\n * narrows the list; otherwise choosing a value would erase every other choice from the row.\n */\nfunction useRecordPages(\n records: RecordsAdapter,\n config: RecordListConfig,\n sort: Sort | null,\n chosen: Record<string, FacetValue>,\n search: string,\n) {\n const [result, setResult] = useState<Result | null>(null)\n const [loadingMore, setLoadingMore] = useState(false)\n const [facets, setFacets] = useState<Record<string, FacetValue[]>>({})\n\n // How deep the reader has paged, so a live update re-lists what they can see rather than\n // snapping them back to page one.\n const loaded = useRef(0)\n\n const query = useMemo(() => {\n const filter: Record<string, FilterValue> = { ...config.scope, ...chosen }\n const text = search.trim()\n return {\n filter: Object.keys(filter).length > 0 ? filter : undefined,\n sort: sort ?? undefined,\n search: text !== '' && config.search.length > 0 ? { text, fields: config.search } : undefined,\n }\n }, [config.scope, config.search, chosen, sort, search])\n\n const key = useMemo(\n () => JSON.stringify([config.collection, query, config.pageSize]),\n [config.collection, query, config.pageSize],\n )\n\n const absorb = useCallback(\n (rows: RecordRow[]) => {\n if (config.filters.length === 0) return\n setFacets((prev) => {\n let changed = false\n const next: Record<string, FacetValue[]> = { ...prev }\n for (const name of config.filters) {\n const seen = new Set<FacetValue>(prev[name] ?? [])\n for (const row of rows) {\n const value = row[name]\n if (isFacet(value) && !seen.has(value)) {\n seen.add(value)\n changed = true\n }\n }\n next[name] = [...seen].sort((a, b) => (String(a) < String(b) ? -1 : 1))\n }\n return changed ? next : prev\n })\n },\n [config.filters],\n )\n\n const fetchPage = useCallback(\n (cursor: string | null, limit: number) =>\n records.list<RecordRow>(config.collection, { ...query, cursor, limit }),\n [records, config.collection, query],\n )\n\n useEffect(() => {\n let live = true\n const refresh = (limit: number) => {\n void fetchPage(null, limit).then(\n (page) => {\n if (!live) return\n loaded.current = page.rows.length\n absorb(page.rows)\n setResult({ key, rows: page.rows, nextCursor: page.nextCursor, error: null })\n },\n (cause: unknown) => {\n if (!live) return\n setResult({ key, rows: [], nextCursor: null, error: message(cause) })\n },\n )\n }\n\n refresh(config.pageSize)\n const unsubscribe = records.subscribe(config.collection, () =>\n refresh(Math.max(config.pageSize, loaded.current)),\n )\n return () => {\n live = false\n unsubscribe()\n }\n }, [fetchPage, absorb, key, records, config.collection, config.pageSize])\n\n const nextCursor = result?.key === key ? result.nextCursor : null\n\n const loadMore = useCallback(() => {\n if (nextCursor === null) return\n setLoadingMore(true)\n void fetchPage(nextCursor, config.pageSize).then(\n (page) => {\n absorb(page.rows)\n setResult((prev) => {\n if (!prev || prev.key !== key) return prev\n const rows = [...prev.rows, ...page.rows]\n loaded.current = rows.length\n return { ...prev, rows, nextCursor: page.nextCursor }\n })\n setLoadingMore(false)\n },\n (cause: unknown) => {\n setResult((prev) => (prev ? { ...prev, error: message(cause) } : prev))\n setLoadingMore(false)\n },\n )\n }, [fetchPage, absorb, key, nextCursor, config.pageSize])\n\n return {\n // Rows from an older query stay on screen while the new ones are in flight, so a keystroke in\n // the search box does not blank the list.\n rows: result?.rows ?? [],\n nextCursor,\n loading: result === null,\n stale: result !== null && result.key !== key,\n loadingMore,\n error: result?.error ?? null,\n facets,\n loadMore,\n }\n}\n\nfunction Chip({ value }: { value: string }) {\n return (\n <span\n className={`inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-xs font-medium ${chipTone(value)}`}\n >\n {value}\n </span>\n )\n}\n\nfunction Cell({ row, field }: { row: RecordRow; field: ListField }): ReactNode {\n const value = row[field.key]\n if (value === null || value === undefined || value === '') {\n return <span className=\"text-neutral-400\">—</span>\n }\n if (field.type === 'enum') return <Chip value={String(value)} />\n if (field.type === 'user') {\n const name = userName(value) || String(value)\n return (\n <span className=\"flex items-center gap-2\">\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-full bg-neutral-900 text-[10px] font-semibold text-white\">\n {initials(name)}\n </span>\n <span className=\"truncate\">{name}</span>\n </span>\n )\n }\n const text = formatValue(value, field)\n const numeric = field.type === 'number' || field.type === 'money'\n return <span className={numeric ? 'tabular-nums' : undefined}>{text}</span>\n}\n\n/** Arrow keys walk the rows; the row itself is the tab stop, so one Tab enters the list. */\nfunction moveFocus(from: HTMLElement, delta: number) {\n const list = from.closest('[data-golem-rows]')\n const rows = Array.from(list?.querySelectorAll<HTMLElement>('[data-golem-row]') ?? [])\n const index = rows.indexOf(from)\n if (index === -1) return\n rows[Math.min(rows.length - 1, Math.max(0, index + delta))]?.focus()\n}\n\nfunction RecordListBody({\n config,\n adapters,\n onOpen,\n}: GolemProps<RecordListConfig, RecordListAdapters, RecordListSlots>) {\n const listId = useId()\n const [sort, setSort] = useState<Sort | null>(config.sort ?? null)\n const [chosen, setChosen] = useState<Record<string, FacetValue>>({})\n const [draft, setDraft] = useState('')\n const [search, setSearch] = useState('')\n\n // One list call per pause in the typing, not one per keystroke.\n useEffect(() => {\n const timer = setTimeout(() => setSearch(draft), 150)\n return () => clearTimeout(timer)\n }, [draft])\n\n const { rows, nextCursor, loading, stale, loadingMore, error, facets, loadMore } = useRecordPages(\n adapters.records,\n config,\n sort,\n chosen,\n search,\n )\n\n const root = useRef<HTMLDivElement>(null)\n const cards = useContainerWidth(root) < CARDS_BELOW\n\n const primary = config.fields.find((field) => field.primary) ?? config.fields[0]!\n const chipField = config.fields.find((field) => field.type === 'enum' && field !== primary)\n const secondary = config.fields.filter((field) => field !== primary && field !== chipField)\n const searchable = config.fields.filter((field) => config.search.includes(field.key))\n const activatable = config.rowAction === 'open'\n const pad = config.density === 'compact' ? 'py-1.5' : 'py-3'\n\n const toggleSort = (key: string) =>\n setSort((prev) =>\n prev && prev.field === key\n ? { field: key, direction: prev.direction === 'asc' ? 'desc' : 'asc' }\n : { field: key, direction: 'asc' },\n )\n\n const choose = (key: string, value: FacetValue | null) =>\n setChosen((prev) => {\n const next = { ...prev }\n if (value === null) delete next[key]\n else next[key] = value\n return next\n })\n\n const rowProps = (row: RecordRow) =>\n activatable\n ? {\n tabIndex: 0,\n onClick: () => onOpen?.(row),\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault()\n moveFocus(event.currentTarget, event.key === 'ArrowDown' ? 1 : -1)\n } else if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n onOpen?.(row)\n }\n },\n }\n : {}\n\n const keyOf = (row: RecordRow, index: number) =>\n typeof row.id === 'string' || typeof row.id === 'number' ? String(row.id) : `row-${index}`\n\n // At card width there are no column headings to click, so the sort moves into the control bar.\n const sortControl = cards && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <label className=\"text-xs font-medium text-neutral-500\" htmlFor={`${listId}-sort`}>\n Sort\n </label>\n <select\n id={`${listId}-sort`}\n value={sort?.field ?? ''}\n onChange={(event) =>\n setSort(\n event.target.value === ''\n ? null\n : { field: event.target.value, direction: sort?.direction ?? 'asc' },\n )\n }\n className=\"rounded-lg border border-neutral-300 bg-white px-2 py-1 text-sm\"\n >\n <option value=\"\">Adapter order</option>\n {config.fields.map((field) => (\n <option key={field.key} value={field.key}>\n {field.label}\n </option>\n ))}\n </select>\n {sort && (\n <button\n type=\"button\"\n onClick={() => toggleSort(sort.field)}\n aria-label={sort.direction === 'asc' ? 'Sort descending' : 'Sort ascending'}\n className=\"rounded-lg border border-neutral-300 bg-white px-2 py-1 text-sm\"\n >\n {sort.direction === 'asc' ? '↑' : '↓'}\n </button>\n )}\n </div>\n )\n\n const controls = (config.search.length > 0 || config.filters.length > 0 || cards) && (\n <div className=\"mb-3 space-y-2\">\n {config.search.length > 0 && (\n <input\n type=\"search\"\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n aria-label=\"Search\"\n placeholder={\n searchable.length > 0\n ? `Search ${searchable.map((field) => field.label.toLowerCase()).join(', ')}`\n : 'Search'\n }\n className=\"w-full rounded-lg border border-neutral-300 bg-white px-3 py-2 text-base sm:text-sm\"\n />\n )}\n {config.filters.map((key) => {\n const field = config.fields.find((one) => one.key === key)!\n return (\n <div key={key} className=\"flex flex-wrap items-center gap-1.5\">\n <span className=\"text-xs font-medium text-neutral-500\">{field.label}</span>\n {[null, ...(facets[key] ?? [])].map((value) => {\n const active = value === null ? chosen[key] === undefined : chosen[key] === value\n return (\n <button\n key={value === null ? '__all' : String(value)}\n type=\"button\"\n aria-pressed={active}\n onClick={() => choose(key, value)}\n className={`rounded-full px-2.5 py-1 text-xs font-medium ${\n active ? 'bg-neutral-900 text-white' : 'bg-neutral-100 text-neutral-600'\n }`}\n >\n {value === null ? 'All' : formatValue(value, field)}\n </button>\n )\n })}\n </div>\n )\n })}\n {sortControl}\n </div>\n )\n\n let body: ReactNode\n if (loading) {\n body = (\n <p className=\"rounded-xl border border-neutral-200 bg-white px-4 py-8 text-center text-sm text-neutral-500\">\n Loading…\n </p>\n )\n } else if (error !== null) {\n body = (\n <p\n role=\"alert\"\n className=\"rounded-xl border border-red-300 bg-red-50 px-4 py-6 text-center text-sm text-red-900\"\n >\n {error}\n </p>\n )\n } else if (rows.length === 0) {\n body = (\n <p className=\"rounded-xl border border-neutral-200 bg-white px-4 py-8 text-center text-sm text-neutral-500\">\n {config.emptyState}\n </p>\n )\n } else if (cards) {\n body = (\n <ul data-golem-rows=\"\" className=\"space-y-2\">\n {rows.map((row, index) => (\n <li key={keyOf(row, index)}>\n <article\n data-golem-row=\"\"\n {...rowProps(row)}\n className={`rounded-xl border border-neutral-200 bg-white ${\n config.density === 'compact' ? 'p-2.5' : 'p-3'\n } ${\n activatable\n ? 'cursor-pointer focus:outline-2 focus:outline-offset-2 focus:outline-neutral-900'\n : ''\n }`}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <h3 className=\"min-w-0 truncate text-sm font-semibold\">\n {formatValue(row[primary.key], primary)}\n </h3>\n {chipField && row[chipField.key] !== undefined && (\n <Chip value={String(row[chipField.key])} />\n )}\n </div>\n <dl className=\"mt-2 grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 text-sm\">\n {secondary.map((field) => (\n <Fragment key={field.key}>\n <dt className=\"text-neutral-500\">{field.label}</dt>\n <dd className=\"min-w-0 truncate\">\n <Cell row={row} field={field} />\n </dd>\n </Fragment>\n ))}\n </dl>\n </article>\n </li>\n ))}\n </ul>\n )\n } else {\n body = (\n // Its own scroll box, so the sticky heading has something to stick to and a wide table\n // scrolls sideways inside the list instead of pushing the page out.\n <div className=\"max-h-[70vh] overflow-auto rounded-xl border border-neutral-200 bg-white\">\n <table className=\"w-full min-w-max border-collapse text-left text-sm\">\n <thead>\n <tr>\n {config.fields.map((field) => {\n const active = sort?.field === field.key\n return (\n <th\n key={field.key}\n scope=\"col\"\n aria-sort={\n active ? (sort.direction === 'asc' ? 'ascending' : 'descending') : 'none'\n }\n className=\"sticky top-0 z-10 border-b border-neutral-200 bg-neutral-50 p-0\"\n >\n <button\n type=\"button\"\n onClick={() => toggleSort(field.key)}\n className=\"flex w-full items-center gap-1 px-3 py-2 text-xs font-semibold tracking-wide text-neutral-600 uppercase hover:text-neutral-900\"\n >\n {field.label}\n <span aria-hidden=\"true\" className={active ? '' : 'opacity-0'}>\n {active && sort.direction === 'desc' ? '↓' : '↑'}\n </span>\n </button>\n </th>\n )\n })}\n </tr>\n </thead>\n <tbody data-golem-rows=\"\">\n {rows.map((row, index) => (\n <tr\n key={keyOf(row, index)}\n data-golem-row=\"\"\n {...rowProps(row)}\n className={`border-b border-neutral-100 last:border-0 ${\n activatable\n ? 'cursor-pointer hover:bg-neutral-50 focus:bg-neutral-50 focus:outline-2 focus:-outline-offset-2 focus:outline-neutral-900'\n : ''\n }`}\n >\n {config.fields.map((field) => (\n <td\n key={field.key}\n className={`px-3 align-top ${pad} ${\n field === primary ? 'font-medium whitespace-nowrap' : ''\n }`}\n >\n <Cell row={row} field={field} />\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n }\n\n return (\n <div\n ref={root}\n data-golem-component=\"RecordList\"\n data-layout={cards ? 'cards' : 'table'}\n className={`golem-record-list w-full text-neutral-900 ${stale ? 'opacity-60' : ''}`}\n >\n {controls}\n {body}\n {nextCursor !== null && !loading && (\n <div className=\"mt-3 flex justify-center\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loadingMore}\n className=\"rounded-lg border border-neutral-300 bg-white px-4 py-2 text-sm font-medium disabled:opacity-60\"\n >\n {loadingMore ? 'Loading…' : `Load ${config.pageSize} more`}\n </button>\n </div>\n )}\n </div>\n )\n}\n\nexport const RecordList = defineComponent<\n typeof recordListConfigSchema,\n RecordListAdapters,\n RecordListSlots\n>({\n name: 'RecordList',\n schema: recordListConfigSchema,\n render: RecordListBody,\n})\n","import { z } from 'zod'\nimport { isFormattableLocale } from '../../lib/locale'\n\nexport const reportConfigSchema = z\n .object({\n collection: z\n .string()\n .min(1)\n .describe(\n 'The record type the reports live in, passed to `list`, `get` and `subscribe`. One report is one record.',\n ),\n period: z\n .enum(['day', 'week', 'month'])\n .default('day')\n .describe(\n 'How long one report covers, which is also what the previous/next arrows step by. A daily stand-up report is `day`; a Monday summary is `week`; a board pack is `month`.',\n ),\n dateField: z\n .string()\n .min(1)\n .default('date')\n .describe(\n 'The field holding the day the report covers, as an ISO calendar date (`2026-09-10`). It picks the period and orders the index.',\n ),\n titleField: z\n .string()\n .min(1)\n .default('title')\n .describe('The field shown as the document’s heading, and as each line of the index.'),\n bodyField: z\n .string()\n .min(1)\n .default('body')\n .describe(\n 'The field holding the document itself, as markdown: headings, lists, tables, code and links.',\n ),\n statusField: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'The field holding `draft` or `final`. Set it and a report still being written wears a Draft badge; leave it out and every report reads as finished.',\n ),\n authorField: z\n .string()\n .min(1)\n .default('author')\n .describe(\n 'The field naming who wrote the report — a name, or a record with a `name` on it. Only read when `showAuthor` is on.',\n ),\n showAuthor: z\n .boolean()\n .default(true)\n .describe('Whether the byline is shown under the date. Off for a report nobody signs.'),\n showIndex: z\n .boolean()\n .default(true)\n .describe(\n 'Whether past reports are listed under the document as a compact index. Off for a report embedded in a wider screen, where the index belongs to the screen.',\n ),\n showNavigation: z\n .boolean()\n .default(true)\n .describe(\n 'Whether the previous/next arrows are shown. Off pins the component to the latest report of the current period, which is what a dashboard card wants.',\n ),\n print: z\n .boolean()\n .default(true)\n .describe(\n 'Whether the Print / Save as PDF action is offered. On also brings the print stylesheet: the chrome disappears and the document prints on its own.',\n ),\n emptyState: z\n .string()\n .min(1)\n .default('No report for this period.')\n .describe('One line shown in place of the document when the period holds no report.'),\n locale: z\n .string()\n .min(2)\n .default('en-GB')\n .describe(\n 'A BCP-47 tag — `en-GB`, `en-US`, `pt-BR` — that every date on the page is formatted with. Dates are read in UTC, so a report reads the same everywhere.',\n ),\n })\n .strict()\n .superRefine((config, ctx) => {\n if (!isFormattableLocale(config.locale)) {\n ctx.addIssue({\n code: 'custom',\n path: ['locale'],\n message: `\"${config.locale}\" is not a BCP-47 language tag, so no date on the page can be formatted. Write it with a hyphen: en-GB, en-US, pt-BR.`,\n })\n }\n })\n\nexport type ReportConfig = z.output<typeof reportConfigSchema>\nexport type ReportConfigInput = z.input<typeof reportConfigSchema>\n","import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { ClockAdapter, RecordsAdapter } from '../../adapters'\nimport { headingSlug, Markdown } from '../../lib/markdown'\nimport { userName } from '../../lib/format'\nimport { reportConfigSchema, type ReportConfig } from './Report.config'\n\nexport interface ReportAdapters {\n records: RecordsAdapter\n clock: ClockAdapter\n}\n\nexport interface ReportSlots {\n /**\n * One report by id, instead of the latest of the current period. The component still opens on\n * that report's period, so the arrows and the index carry on from where it sits.\n */\n id?: string\n}\n\n/** One report record, as the component reads it: every field is named by the config. */\nexport type ReportRecord = Record<string, unknown> & { id: string }\n\n/** How many past reports the index offers. Long enough to reach last month, short enough to read. */\nconst INDEX_LENGTH = 12\n\ntype Period = ReportConfig['period']\n\n/* Every date is handled in UTC, so a report covers the same day in every timezone. */\n\nconst isoDay = (at: Date) => at.toISOString().slice(0, 10)\n\n/** The first day of the period `at` falls in. A week starts on Monday. */\nfunction startOf(period: Period, at: Date): Date {\n const start = new Date(Date.UTC(at.getUTCFullYear(), at.getUTCMonth(), at.getUTCDate()))\n if (period === 'week') start.setUTCDate(start.getUTCDate() - ((start.getUTCDay() + 6) % 7))\n if (period === 'month') start.setUTCDate(1)\n return start\n}\n\n/** Every ISO day the period covers. This is the filter: an `any of`, not a range query. */\nfunction daysOf(period: Period, start: Date): string[] {\n const days: string[] = []\n const cursor = new Date(start)\n const month = start.getUTCMonth()\n const length = period === 'day' ? 1 : period === 'week' ? 7 : Number.POSITIVE_INFINITY\n\n while (days.length < length && (period !== 'month' || cursor.getUTCMonth() === month)) {\n days.push(isoDay(cursor))\n cursor.setUTCDate(cursor.getUTCDate() + 1)\n }\n return days\n}\n\n/** The period `delta` steps away, forwards or back. */\nfunction step(period: Period, start: Date, delta: number): Date {\n const next = new Date(start)\n if (period === 'day') next.setUTCDate(next.getUTCDate() + delta)\n if (period === 'week') next.setUTCDate(next.getUTCDate() + delta * 7)\n if (period === 'month') next.setUTCMonth(next.getUTCMonth() + delta)\n return startOf(period, next)\n}\n\n/** What the period is called, in the reader's locale: a day, a Monday-to-Sunday span, a month. */\nfunction periodLabel(period: Period, start: Date, locale: string): string {\n const options: Intl.DateTimeFormatOptions = { timeZone: 'UTC' }\n if (period === 'month') {\n return new Intl.DateTimeFormat(locale, { ...options, month: 'long', year: 'numeric' }).format(\n start,\n )\n }\n if (period === 'week') {\n const format = new Intl.DateTimeFormat(locale, {\n ...options,\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n })\n const end = new Date(start)\n end.setUTCDate(end.getUTCDate() + 6)\n return format.formatRange(start, end)\n }\n return new Intl.DateTimeFormat(locale, {\n ...options,\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(start)\n}\n\n/** A record's own date, read as a calendar day at noon so no timezone can shift it. */\nfunction dayOf(value: unknown): Date | null {\n const at = new Date(`${String(value)}T12:00:00Z`)\n return Number.isNaN(at.getTime()) ? null : at\n}\n\nconst message = (cause: unknown) => (cause instanceof Error ? cause.message : String(cause))\n\n/**\n * A report's optional `sections`: the headings it was written from, as strings or as objects with a\n * `title`. Anything else on the field is ignored rather than guessed at.\n */\nfunction sectionTitles(record: ReportRecord | null): string[] {\n const value = record?.sections\n if (!Array.isArray(value)) return []\n return value\n .map((one) =>\n typeof one === 'string'\n ? one\n : typeof (one as { title?: unknown })?.title === 'string'\n ? (one as { title: string }).title\n : '',\n )\n .filter((title) => title !== '')\n}\n\ninterface Loaded {\n /** The query these rows answer. A result whose key is not the current one is stale. */\n key: string\n report: ReportRecord | null\n error: string | null\n}\n\n/**\n * The document on screen and the index under it, and the one `subscribe` that refreshes both when\n * the agent writes a new version. Nothing here polls.\n */\nfunction useReport(\n records: RecordsAdapter,\n config: ReportConfig,\n days: string[],\n openId: string | undefined,\n) {\n const [loaded, setLoaded] = useState<Loaded | null>(null)\n const [index, setIndex] = useState<ReportRecord[]>([])\n\n const key = useMemo(\n () => JSON.stringify([config.collection, config.dateField, days, openId ?? null]),\n [config.collection, config.dateField, days, openId],\n )\n\n const fetchReport = useCallback((): Promise<ReportRecord | null> => {\n if (openId !== undefined) return records.get<ReportRecord>(config.collection, openId)\n return records\n .list<ReportRecord>(config.collection, {\n filter: { [config.dateField]: days },\n sort: { field: config.dateField, direction: 'desc' },\n limit: 1,\n })\n .then((page) => page.rows[0] ?? null)\n }, [records, config.collection, config.dateField, days, openId])\n\n useEffect(() => {\n let live = true\n\n const refresh = () => {\n void fetchReport().then(\n (report) => {\n if (live) setLoaded({ key, report, error: null })\n },\n (cause: unknown) => {\n if (live) setLoaded({ key, report: null, error: message(cause) })\n },\n )\n if (!config.showIndex) return\n void records\n .list<ReportRecord>(config.collection, {\n sort: { field: config.dateField, direction: 'desc' },\n limit: INDEX_LENGTH,\n })\n .then(\n (page) => {\n if (live) setIndex(page.rows)\n },\n () => {\n // The index is an aside; a document that arrived should not be replaced by its error.\n },\n )\n }\n\n refresh()\n const unsubscribe = records.subscribe(config.collection, refresh)\n return () => {\n live = false\n unsubscribe()\n }\n }, [fetchReport, key, records, config.collection, config.dateField, config.showIndex])\n\n return {\n report: loaded?.report ?? null,\n error: loaded?.error ?? null,\n loading: loaded === null || loaded.key !== key,\n index,\n }\n}\n\nfunction Chrome({ children }: { children: ReactNode }) {\n return (\n <div data-golem-chrome=\"\" className=\"mb-3 flex flex-wrap items-center justify-between gap-2\">\n {children}\n </div>\n )\n}\n\nconst stepButton =\n 'flex size-9 shrink-0 items-center justify-center rounded-lg border border-neutral-300 bg-white text-base disabled:opacity-40'\n\nfunction ReportBody({\n config,\n adapters,\n id,\n}: GolemProps<ReportConfig, ReportAdapters, ReportSlots>) {\n const root = useRef<HTMLDivElement>(null)\n const [anchor, setAnchor] = useState(() => startOf(config.period, adapters.clock.now()))\n const [openId, setOpenId] = useState(id)\n\n const days = useMemo(() => daysOf(config.period, anchor), [config.period, anchor])\n const { report, error, loading, index } = useReport(adapters.records, config, days, openId)\n\n const reportDate = report ? dayOf(report[config.dateField]) : null\n\n // The period on screen. A report opened by id brings its own, so the arrows carry on from where\n // it sits rather than from wherever the reader was before they opened it.\n const shown = openId !== undefined && reportDate ? startOf(config.period, reportDate) : anchor\n\n // The reader cannot step past the period they are living in: there is no report from next week.\n const latest = startOf(config.period, adapters.clock.now())\n const move = (delta: number) => {\n setOpenId(undefined)\n setAnchor(step(config.period, shown, delta))\n }\n\n const title = String(report?.[config.titleField] ?? '')\n const body = String(report?.[config.bodyField] ?? '')\n const author = config.showAuthor ? userName(report?.[config.authorField]) : ''\n const draft = config.statusField !== undefined && report?.[config.statusField] === 'draft'\n const sections = sectionTitles(report)\n\n const dateLine = periodLabel(\n config.period,\n reportDate ? startOf(config.period, reportDate) : shown,\n config.locale,\n )\n\n const jumpTo = (heading: string) => {\n root.current\n ?.querySelector(`[data-golem-heading=\"${headingSlug(heading)}\"]`)\n ?.scrollIntoView({ block: 'start' })\n }\n\n let document: ReactNode\n if (loading) {\n document = <p className=\"px-1 py-10 text-center text-sm text-neutral-500\">Loading…</p>\n } else if (error !== null) {\n document = (\n <p role=\"alert\" className=\"px-1 py-8 text-center text-sm text-red-900\">\n {error}\n </p>\n )\n } else if (report === null) {\n document = (\n <p className=\"px-1 py-10 text-center text-sm text-neutral-500\">{config.emptyState}</p>\n )\n } else {\n document = (\n <>\n <header className=\"mb-5 border-b border-neutral-200 pb-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h1 className=\"text-xl font-semibold tracking-tight sm:text-2xl\">{title}</h1>\n {draft && (\n <span className=\"rounded-full bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-800 uppercase\">\n Draft\n </span>\n )}\n </div>\n <p className=\"mt-1 text-sm text-neutral-500\">\n {dateLine}\n {author !== '' && ` · ${author}`}\n </p>\n </header>\n\n {sections.length > 0 && (\n <nav\n data-golem-contents=\"\"\n aria-label=\"Contents\"\n className=\"mb-5 rounded-lg bg-neutral-50 px-4 py-3\"\n >\n <p className=\"text-xs font-semibold tracking-wide text-neutral-500 uppercase\">\n Contents\n </p>\n <ul className=\"mt-1.5 space-y-1 text-sm\">\n {sections.map((heading) => (\n <li key={heading}>\n <button\n type=\"button\"\n onClick={() => jumpTo(heading)}\n className=\"text-left underline-offset-2 hover:underline\"\n >\n {heading}\n </button>\n </li>\n ))}\n </ul>\n </nav>\n )}\n\n {/* The one place the font size is set; every heading in the body is sized in `em` off it. */}\n <div className=\"text-[15px] leading-7 text-neutral-800 sm:text-base sm:leading-8\">\n <Markdown text={body} hardWraps={false} />\n </div>\n </>\n )\n }\n\n return (\n <div\n ref={root}\n data-golem-component=\"Report\"\n data-period={config.period}\n data-print={config.print ? 'on' : 'off'}\n className=\"golem-report w-full text-neutral-900\"\n >\n {(config.showNavigation || config.print) && (\n <Chrome>\n {config.showNavigation ? (\n <div className=\"flex min-w-0 items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => move(-1)}\n aria-label={`Previous ${config.period}`}\n className={stepButton}\n >\n ‹\n </button>\n <button\n type=\"button\"\n onClick={() => move(1)}\n aria-label={`Next ${config.period}`}\n disabled={shown.getTime() >= latest.getTime()}\n className={stepButton}\n >\n ›\n </button>\n <p data-golem-period=\"\" className=\"min-w-0 truncate text-sm font-medium\">\n {periodLabel(config.period, shown, config.locale)}\n </p>\n </div>\n ) : (\n <span />\n )}\n {config.print && (\n <button\n type=\"button\"\n onClick={() => window.print()}\n className=\"rounded-lg border border-neutral-300 bg-white px-3 py-2 text-sm font-medium\"\n >\n Print / Save as PDF\n </button>\n )}\n </Chrome>\n )}\n\n <article\n data-golem-paper=\"\"\n className=\"rounded-xl border border-neutral-200 bg-white px-5 py-6 sm:px-8 sm:py-8\"\n >\n {document}\n </article>\n\n {config.showIndex && index.length > 0 && (\n <nav data-golem-chrome=\"\" aria-label=\"Past reports\" className=\"mt-4\">\n <p className=\"mb-2 text-xs font-semibold tracking-wide text-neutral-500 uppercase\">\n Past reports\n </p>\n <ul className=\"divide-y divide-neutral-200 overflow-hidden rounded-xl border border-neutral-200 bg-white\">\n {index.map((past) => {\n const at = dayOf(past[config.dateField])\n const current = report !== null && past.id === report.id\n return (\n <li key={past.id}>\n <button\n type=\"button\"\n aria-current={current ? 'true' : undefined}\n onClick={() => setOpenId(past.id)}\n className={`flex w-full items-baseline justify-between gap-3 px-4 py-2.5 text-left text-sm ${\n current ? 'bg-neutral-100 font-medium' : 'hover:bg-neutral-50'\n }`}\n >\n <span className=\"min-w-0 truncate\">\n {String(past[config.titleField] ?? '—')}\n </span>\n <span className=\"shrink-0 text-xs text-neutral-500\">\n {at\n ? new Intl.DateTimeFormat(config.locale, {\n day: 'numeric',\n month: 'short',\n timeZone: 'UTC',\n }).format(at)\n : '—'}\n </span>\n </button>\n </li>\n )\n })}\n </ul>\n </nav>\n )}\n </div>\n )\n}\n\nexport const Report = defineComponent<typeof reportConfigSchema, ReportAdapters, ReportSlots>({\n name: 'Report',\n schema: reportConfigSchema,\n render: ReportBody,\n})\n","import { z } from 'zod'\n\nconst menuItemSchema = z\n .object({\n id: z.string().min(1).describe('Names the item to `activeId` and to `onSelect`.'),\n label: z.string().min(1).describe('The short word under the icon.'),\n icon: z.string().optional().describe('The glyph over the label — an emoji does. Left out, the label\\'s first letter stands in.'),\n href: z\n .string()\n .optional()\n .describe(\n 'The route the item opens, through `navigation.go`. Left out, tapping it calls `onSelect(id)` instead.',\n ),\n })\n .strict()\n\nexport const shellConfigSchema = z\n .object({\n title: z.string().min(1).describe('Text shown in the top bar. Usually the app name.'),\n menu: z\n .array(menuItemSchema)\n .default([])\n .describe(\n \"The app's own screens, one item each, in the bottom menu bar. Empty means no bar.\",\n ),\n activeId: z\n .string()\n .optional()\n .describe(\n 'The menu item drawn as current. Left out, the item whose `href` starts the current route is current.',\n ),\n chatSide: z\n .enum(['left', 'right'])\n .default('left')\n .describe('Which side of the frame the agent chat column sits on, at desktop width.'),\n chatOpen: z\n .boolean()\n .default(true)\n .describe(\n 'Whether the chat column is open at the first paint at desktop width, when this browser has no remembered choice. Turning it on later opens the chat on either layout, which is how an app raises the chat when a mode switches on. Below the breakpoint the chat starts closed.',\n ),\n breakpoint: z\n .number()\n .int()\n .min(320)\n .max(1920)\n .default(768)\n .describe(\n 'Viewport width in pixels. Below it the chat stops being a side column and opens as a sheet over the app.',\n ),\n chatWidth: z\n .number()\n .int()\n .min(240)\n .max(960)\n .default(320)\n .describe(\n 'Starting width of the chat column in pixels, at desktop width. The reader can drag the edge between chat and canvas to change it, and the choice is kept in this browser.',\n ),\n showTopBar: z\n .boolean()\n .default(true)\n .describe('Whether the top bar is rendered at all. Turn it off when the host app has one.'),\n })\n .strict()\n\nexport type ShellConfig = z.output<typeof shellConfigSchema>\nexport type ShellConfigInput = z.input<typeof shellConfigSchema>\nexport type ShellMenuItem = z.output<typeof menuItemSchema>\n","import { useEffect, useRef, useState, type ReactNode } from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { IdentityAdapter, NavigationAdapter, User } from '../../adapters'\nimport { shellConfigSchema, type ShellConfig } from './Shell.config'\nimport { useContainerWidth } from '../../lib/use-container-width'\n\nexport interface ShellAdapters {\n identity: IdentityAdapter\n navigation: NavigationAdapter\n}\n\nexport interface ShellSlots {\n /** The agent conversation. Left empty it renders a placeholder, so the frame is never blank; `null` means no chat at all, and no chat toggle. */\n chat?: ReactNode | null\n /** The screen the agent built. */\n canvas?: ReactNode\n /**\n * The right-hand end of the top bar — `Auth.AccountMenu` is what usually goes here. Left empty\n * the bar shows the signed-in name as plain text.\n */\n account?: ReactNode\n /** Extra rows in the settings dropdown, under the theme toggle. `Shell.Setting` is the row. */\n settings?: ReactNode\n /** Called with the item's id when a menu item without `href` is tapped. */\n onSelect?: (id: string) => void\n}\n\nfunction Placeholder({ label }: { label: string }) {\n return (\n <div className=\"flex h-full items-center justify-center p-6 text-center text-sm text-(--chat-faint)\">\n {label}\n </div>\n )\n}\n\nfunction useCurrentUser(identity: IdentityAdapter): User | null {\n const [user, setUser] = useState<User | null>(null)\n\n useEffect(() => {\n let live = true\n void identity.currentUser().then((next) => {\n if (live) setUser(next)\n })\n const unsubscribe = identity.subscribe(setUser)\n return () => {\n live = false\n unsubscribe()\n }\n }, [identity])\n\n return user\n}\n\nconst CHAT_WIDTH_KEY = 'golem-shell-chat-width'\nconst CHAT_OPEN_KEY = 'golem-shell-chat-open'\nconst THEME_KEY = 'golem-shell-theme'\nconst CHAT_MIN = 240\n/** The canvas keeps at least this much, however far the chat edge is dragged. */\nconst CANVAS_MIN = 320\n\nfunction saved(key: string): string | null {\n try {\n return localStorage.getItem(key)\n } catch {\n return null\n }\n}\n\nfunction save(key: string, value: string | null) {\n try {\n if (value === null) localStorage.removeItem(key)\n else localStorage.setItem(key, value)\n } catch {\n /* private mode: the choice lasts the session */\n }\n}\n\ntype Theme = 'light' | 'dark'\n\n/**\n * The kit's theme is the `data-golem-theme` attribute on the document. Shell owns it: the saved\n * choice, else the OS preference, written to the document so every component's `dark:` follows.\n */\nfunction useTheme(): [Theme, () => void] {\n const [theme, setTheme] = useState<Theme>(() => {\n const kept = saved(THEME_KEY)\n if (kept === 'light' || kept === 'dark') return kept\n return typeof matchMedia !== 'undefined' && matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light'\n })\n useEffect(() => {\n document.documentElement.dataset.golemTheme = theme\n document.documentElement.style.colorScheme = theme\n }, [theme])\n return [\n theme,\n () => {\n const next = theme === 'dark' ? 'light' : 'dark'\n save(THEME_KEY, next)\n setTheme(next)\n },\n ]\n}\n\nconst paths: Record<string, string> = {\n chat: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z',\n settings:\n 'M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2zM12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z',\n sun: 'M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8z',\n moon: 'M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9z',\n close: 'M18 6 6 18M6 6l12 12',\n user: 'M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2M12 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8z',\n wrench:\n 'M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z',\n}\n\nexport type ShellIconName = keyof typeof paths\n\n/** The line icons the bar and the dropdown use; an app's own `Shell.Setting` rows can share them. */\nfunction Icon({ name }: { name: ShellIconName }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n width=\"16\"\n height=\"16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d={paths[name]} />\n </svg>\n )\n}\n\n/** An icon-only bar button: the label is the tooltip and the accessible name, never text in the bar. */\nfunction IconButton({\n label,\n on,\n onClick,\n expanded,\n children,\n}: {\n label: string\n on?: boolean\n expanded?: boolean\n onClick: () => void\n children: ReactNode\n}) {\n return (\n <button\n type=\"button\"\n title={label}\n aria-label={label}\n aria-pressed={on}\n aria-expanded={expanded}\n onClick={onClick}\n className={`flex size-8 shrink-0 items-center justify-center rounded-lg border ${\n on || expanded\n ? 'border-(--chat-accent) text-(--chat-accent)'\n : 'border-(--chat-line) text-(--chat-dim) hover:border-(--chat-accent) hover:text-(--chat-accent)'\n }`}\n >\n {children}\n </button>\n )\n}\n\n/** One row of the settings dropdown: an icon, a short label, and a dot when it is a toggle. */\nfunction Setting({\n icon,\n label,\n on,\n onClick,\n}: {\n icon: ReactNode\n label: string\n /** Set on a toggle; left out on a plain action. */\n on?: boolean\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n role=\"menuitem\"\n aria-pressed={on}\n onClick={onClick}\n className=\"flex w-full items-center gap-2.5 rounded-lg px-2.5 py-2 text-sm text-(--chat-text) hover:bg-(--chat-panel2)\"\n >\n <span aria-hidden=\"true\" className=\"text-(--chat-dim)\">\n {icon}\n </span>\n <span className=\"flex-1 text-left\">{label}</span>\n {on !== undefined && (\n <span\n aria-hidden=\"true\"\n className={`size-2 rounded-full ${on ? 'bg-(--chat-ok)' : 'bg-(--chat-line2)'}`}\n />\n )}\n </button>\n )\n}\n\nfunction ShellFrame({\n config,\n adapters,\n chat,\n canvas,\n account,\n settings,\n onSelect,\n}: GolemProps<ShellConfig, ShellAdapters, ShellSlots>) {\n const root = useRef<HTMLDivElement>(null)\n const width = useContainerWidth(root)\n const isMobile = width < config.breakpoint\n const user = useCurrentUser(adapters.identity)\n const [theme, toggleTheme] = useTheme()\n const [route, setRoute] = useState(() => adapters.navigation.current())\n const [gear, setGear] = useState(false)\n const gearRef = useRef<HTMLDivElement>(null)\n\n // Below the breakpoint the sheet is a modal, and modals start closed; the desktop column starts\n // where this browser left it, else where the config says.\n const [open, setOpen] = useState(() => {\n if (isMobile) return false\n const kept = saved(CHAT_OPEN_KEY)\n return kept === null ? config.chatOpen : kept === '1'\n })\n // Only the desktop column's state is remembered; opening and closing the sheet is not a choice.\n const setChatOpen = (next: boolean) => {\n setOpen(next)\n if (!isMobile) save(CHAT_OPEN_KEY, next ? '1' : '0')\n }\n // Crossing into the phone layout puts the sheet away: an open column is a desk arrangement, not a\n // wish to see nothing but the chat.\n const [wasMobile, setWasMobile] = useState(isMobile)\n if (wasMobile !== isMobile) {\n setWasMobile(isMobile)\n if (isMobile) setOpen(false)\n }\n // `chatOpen` flipping after the first paint is the app raising (or lowering) the chat.\n const wanted = useRef(config.chatOpen)\n useEffect(() => {\n if (wanted.current === config.chatOpen) return\n wanted.current = config.chatOpen\n setChatOpen(config.chatOpen)\n })\n\n // A route change is a screen the reader asked for (a chip in the chat, a link), so the sheet\n // over it closes to show it.\n useEffect(\n () =>\n adapters.navigation.subscribe((next) => {\n setRoute(next)\n if (isMobile) setOpen(false)\n }),\n [adapters.navigation, isMobile],\n )\n\n // Esc closes the dropdown first, then the sheet; a tap outside closes the dropdown.\n useEffect(() => {\n if (!gear && !(isMobile && open)) return\n const onKey = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return\n if (gear) setGear(false)\n else setChatOpen(false)\n }\n const onPointer = (event: PointerEvent) => {\n if (gear && !gearRef.current?.contains(event.target as Node)) setGear(false)\n }\n window.addEventListener('keydown', onKey)\n document.addEventListener('pointerdown', onPointer)\n return () => {\n window.removeEventListener('keydown', onKey)\n document.removeEventListener('pointerdown', onPointer)\n }\n })\n\n const [chatWidth, setChatWidth] = useState(\n () => Number.parseInt(saved(CHAT_WIDTH_KEY) ?? '', 10) || config.chatWidth,\n )\n const clampChat = (w: number) =>\n Math.round(Math.min(Math.max(w, CHAT_MIN), Math.max(CHAT_MIN, width - CANVAS_MIN)))\n\n // The handle sits on the chat column's inner edge; dragging it sets the column's width from the\n // pointer's distance to the frame's chat-side edge. Pointer capture keeps the drag alive when the\n // pointer outruns the 7px handle.\n const startResize = (event: React.PointerEvent<HTMLDivElement>) => {\n event.preventDefault()\n const handle = event.currentTarget\n const frame = root.current?.getBoundingClientRect()\n if (!frame) return\n const widthAt = (e: { clientX: number }) =>\n clampChat(config.chatSide === 'left' ? e.clientX - frame.left : frame.right - e.clientX)\n handle.setPointerCapture(event.pointerId)\n const move = (e: PointerEvent) => setChatWidth(widthAt(e))\n const up = (e: PointerEvent) => {\n handle.removeEventListener('pointermove', move)\n handle.removeEventListener('pointerup', up)\n save(CHAT_WIDTH_KEY, String(widthAt(e)))\n }\n handle.addEventListener('pointermove', move)\n handle.addEventListener('pointerup', up)\n }\n const resetResize = () => {\n setChatWidth(config.chatWidth)\n save(CHAT_WIDTH_KEY, null)\n }\n\n const activeId =\n config.activeId ??\n config.menu.find((item) => item.href !== undefined && route.path.startsWith(item.href))?.id\n\n const chatPane = (\n <div className=\"h-full min-w-0 overflow-hidden\">\n {chat ?? <Placeholder label=\"agent chat\" />}\n </div>\n )\n const canvasPane = (\n <div className=\"h-full min-w-0 flex-1 overflow-auto\">\n {canvas ?? <Placeholder label=\"canvas\" />}\n </div>\n )\n\n return (\n <div\n ref={root}\n data-golem-component=\"Shell\"\n data-layout={isMobile ? 'mobile' : 'desktop'}\n data-chat={chat === null ? 'none' : open ? 'open' : 'closed'}\n className={`golem-shell flex h-dvh max-h-full min-h-0 w-full flex-col overflow-hidden bg-(--chat-bg) text-(--chat-text) ${\n config.menu.length > 0 ? '' : 'pb-[env(safe-area-inset-bottom)]'\n }`}\n >\n {config.showTopBar && (\n <header className=\"flex min-h-11 shrink-0 items-center gap-2 border-b border-(--chat-line) bg-(--chat-panel) px-3 pt-[env(safe-area-inset-top)]\">\n <button\n type=\"button\"\n onClick={() => adapters.navigation.go('/')}\n className=\"min-w-0 truncate text-[15px] font-semibold tracking-wide hover:underline\"\n >\n {config.title}\n </button>\n <span className=\"ml-auto flex shrink-0 items-center gap-1.5\">\n {chat !== null && (\n <IconButton label=\"Chat\" on={open} onClick={() => setChatOpen(!open)}>\n <Icon name=\"chat\" />\n </IconButton>\n )}\n <div ref={gearRef} className=\"relative\">\n <IconButton label=\"Settings\" expanded={gear} onClick={() => setGear(!gear)}>\n <Icon name=\"settings\" />\n </IconButton>\n {gear && (\n <div\n role=\"menu\"\n className=\"absolute top-full right-0 z-30 mt-1.5 w-56 rounded-xl border border-(--chat-line2) bg-(--chat-panel) p-1.5 shadow-lg\"\n >\n <Setting\n icon={<Icon name={theme === 'dark' ? 'sun' : 'moon'} />}\n label={theme === 'dark' ? 'Light mode' : 'Dark mode'}\n onClick={toggleTheme}\n />\n {settings}\n </div>\n )}\n </div>\n {account ??\n (user && (\n <span className=\"max-w-32 truncate text-sm text-(--chat-dim)\">{user.name}</span>\n ))}\n </span>\n </header>\n )}\n\n <div className=\"relative flex min-h-0 flex-1 flex-col\">\n {isMobile || chat === null ? (\n <div className=\"flex min-h-0 flex-1\">{canvasPane}</div>\n ) : (\n <div\n className={`flex min-h-0 flex-1 ${\n config.chatSide === 'right' ? 'flex-row-reverse' : 'flex-row'\n }`}\n >\n <aside\n hidden={!open}\n data-golem-chat-width={clampChat(chatWidth)}\n style={{ width: clampChat(chatWidth) }}\n className={`relative shrink-0 border-(--chat-line) ${\n config.chatSide === 'left' ? 'border-r' : 'border-l'\n }`}\n >\n {chatPane}\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize chat\"\n title=\"Drag to resize, double-click to reset\"\n onPointerDown={startResize}\n onDoubleClick={resetResize}\n className={`absolute top-0 bottom-0 z-10 w-[7px] cursor-col-resize touch-none hover:bg-(--chat-accent-soft) active:bg-(--chat-accent-soft) ${\n config.chatSide === 'left' ? '-right-[3px]' : '-left-[3px]'\n }`}\n />\n </aside>\n {canvasPane}\n </div>\n )}\n\n {/* The bottom menu bar: phone-app style at every width, icon over a short label. */}\n {config.menu.length > 0 && (\n <nav\n aria-label=\"Menu\"\n className=\"flex shrink-0 justify-center border-t border-(--chat-line) bg-(--chat-panel) px-1 pt-1 pb-[max(0.25rem,env(safe-area-inset-bottom))]\"\n >\n {config.menu.map((item) => {\n const active = item.id === activeId\n return (\n <button\n key={item.id}\n type=\"button\"\n aria-current={active ? 'page' : undefined}\n onClick={() =>\n item.href !== undefined ? adapters.navigation.go(item.href) : onSelect?.(item.id)\n }\n className={`flex min-w-0 max-w-24 flex-1 flex-col items-center gap-0.5 rounded-lg px-1 py-1 text-[11px] font-medium ${\n active\n ? 'text-(--chat-accent)'\n : 'text-(--chat-dim) hover:text-(--chat-text)'\n }`}\n >\n <span aria-hidden=\"true\" className=\"text-lg leading-none\">\n {item.icon ?? item.label[0]}\n </span>\n <span className=\"max-w-full truncate\">{item.label}</span>\n </button>\n )\n })}\n </nav>\n )}\n\n {/* The sheet covers the canvas and the bottom menu, which stay mounted and keep their scroll. */}\n {isMobile && chat !== null && (\n <div\n role=\"dialog\"\n aria-label=\"Chat\"\n aria-modal=\"true\"\n hidden={!open}\n className=\"absolute inset-0 z-20 bg-(--chat-bg)\"\n >\n {chatPane}\n </div>\n )}\n </div>\n </div>\n )\n}\n\nconst ShellComponent = defineComponent<typeof shellConfigSchema, ShellAdapters, ShellSlots>({\n name: 'Shell',\n schema: shellConfigSchema,\n render: ShellFrame,\n})\n\n/** The frame, plus `Shell.Setting` (a dropdown row) and `Shell.Icon` (the bar's line icons) for the app's own rows. */\nexport const Shell = Object.assign(ShellComponent, { Setting, Icon })\n","import { z } from 'zod'\nimport { isFormattableLocale } from '../../lib/locale'\n\n/**\n * What a kind says about an entry, and the only thing that decides its mark and its colour. Four\n * tones rather than a palette: an agent picking a colour would pick a different one on every\n * screen, and a log is read by scanning down one column of marks.\n */\nexport const TONES = ['neutral', 'good', 'warn', 'bad'] as const\n\nexport type Tone = (typeof TONES)[number]\n\nexport const timelineKindSchema = z\n .object({\n id: z\n .string()\n .min(1)\n .describe('The value the record holds in `kindField` when the entry is of this kind.'),\n label: z\n .string()\n .min(1)\n .describe('What names the kind on the entry and on its filter chip. Never written back.'),\n tone: z\n .enum(TONES, {\n error: (issue) =>\n `Unknown tone ${JSON.stringify(issue.input)}. Pick one of: ${TONES.join(', ')}.`,\n })\n .describe(\n 'The mark and the colour: `neutral` a grey dot, `good` a green tick, `warn` an amber triangle, `bad` a red cross.',\n ),\n })\n .strict()\n\nexport const timelineConfigSchema = z\n .object({\n collection: z\n .string()\n .min(1)\n .describe(\n 'The record type the entries live in, passed to `list`, `subscribe` and `create`. One entry is one record.',\n ),\n dateField: z\n .string()\n .min(1)\n .default('at')\n .describe(\n 'The field holding the instant the entry happened, as a full ISO datetime (`2026-09-10T14:32:00Z`). It orders the timeline, newest first, and cuts it into days.',\n ),\n bodyField: z\n .string()\n .min(1)\n .default('body')\n .describe(\n 'The field holding what the entry says, as short markdown: a sentence or two, bold, links, a bullet list.',\n ),\n kindField: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'The field holding which of the `kinds` an entry is. Leave it out and every entry gets the same neutral dot.',\n ),\n kinds: z\n .array(timelineKindSchema)\n .default([])\n .describe(\n 'The vocabulary `kindField` is read against: `{ id, label, tone }` each, where `tone` is one of neutral, good, warn, bad. Set it with `kindField` or not at all.',\n ),\n actorField: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'The field naming who the entry is by — a name, or a record with a `name` on it. Leave it out and entries are unsigned.',\n ),\n attachmentsField: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'The field holding the entry’s file references, as an array of ids or of `{ id, name }`. Each becomes a chip that opens through `Files.url`, so setting this field makes the `files` adapter required.',\n ),\n filters: z\n .array(z.enum(['kind', 'actor']))\n .default([])\n .describe(\n 'Which rows of filter chips sit above the timeline. `kind` offers every configured kind; `actor` offers every person whose entries have arrived. Each needs its field set.',\n ),\n search: z\n .array(z.string().min(1))\n .default([])\n .describe(\n 'Field keys the search box matches, as a case-insensitive substring — usually just the body field. Empty hides the search box.',\n ),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(200)\n .default(25)\n .describe(\n 'How many entries arrive per page, and how many older ones the load-more button fetches.',\n ),\n composer: z\n .boolean()\n .default(false)\n .describe(\n 'Whether a one-line box for adding an entry sits above the timeline. On makes the `identity` adapter required: the new entry is stamped with the signed-in member and with the clock’s current instant.',\n ),\n emptyState: z\n .string()\n .min(1)\n .default('Nothing on the log yet.')\n .describe('One line shown in place of the entries when the query matches none.'),\n locale: z\n .string()\n .min(2)\n .default('en-GB')\n .describe(\n 'A BCP-47 tag — `en-GB`, `en-US`, `pt-BR` — that every day header and clock time is formatted with. Days are cut in the `Clock` adapter’s time zone, so \"Today\" means today where the reader is.',\n ),\n })\n .strict()\n .superRefine((config, ctx) => {\n if (!isFormattableLocale(config.locale)) {\n ctx.addIssue({\n code: 'custom',\n path: ['locale'],\n message: `\"${config.locale}\" is not a BCP-47 language tag, so no date on the page can be formatted. Write it with a hyphen: en-GB, en-US, pt-BR.`,\n })\n }\n if (config.kindField !== undefined && config.kinds.length === 0) {\n ctx.addIssue({\n code: 'custom',\n path: ['kinds'],\n message: `\\`kindField\\` reads \"${config.kindField}\" against a vocabulary that is empty, so no entry can be marked. List the kinds, or drop \\`kindField\\`.`,\n })\n }\n if (config.kindField === undefined && config.kinds.length > 0) {\n ctx.addIssue({\n code: 'custom',\n path: ['kindField'],\n message: 'There are `kinds` but no `kindField` to read them against. Name the field.',\n })\n }\n\n const seen = new Set<string>()\n for (const [index, kind] of config.kinds.entries()) {\n if (seen.has(kind.id)) {\n ctx.addIssue({\n code: 'custom',\n path: ['kinds', index, 'id'],\n message: `Two kinds share the id \"${kind.id}\", so an entry of that kind has two meanings.`,\n })\n }\n seen.add(kind.id)\n }\n\n for (const [index, filter] of config.filters.entries()) {\n const needed = filter === 'kind' ? config.kindField : config.actorField\n if (needed === undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['filters', index],\n message: `Filtering by ${filter} needs \\`${filter}Field\\` set, otherwise there is nothing to filter on.`,\n })\n }\n }\n })\n\nexport type TimelineConfig = z.output<typeof timelineConfigSchema>\nexport type TimelineConfigInput = z.input<typeof timelineConfigSchema>\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type FormEvent,\n type ReactNode,\n} from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type {\n ClockAdapter,\n FilesAdapter,\n FilterValue,\n IdentityAdapter,\n RecordsAdapter,\n} from '../../adapters'\nimport { initials, userName } from '../../lib/format'\nimport { formattableZone } from '../../lib/locale'\nimport { Markdown } from '../../lib/markdown'\nimport { timelineConfigSchema, type TimelineConfig, type Tone } from './Timeline.config'\n\nexport interface TimelineAdapters {\n records: RecordsAdapter\n clock: ClockAdapter\n /** Only read with `composer: true`, to stamp a new entry with whoever is signed in. */\n identity?: IdentityAdapter\n /** Only read when `attachmentsField` is set, to turn a file reference into a URL to open. */\n files?: FilesAdapter\n}\n\n/** One entry, as the component reads it: every field on it is named by the config. */\nexport type TimelineEntry = Record<string, unknown> & { id: string }\n\n/**\n * How close to the top still counts as \"reading the newest entry\". Chat pins to the bottom of its\n * feed for the same reason; a timeline reads downwards from the newest, so it pins to the top.\n */\nconst TOP_SLACK = 48\n\n/** A tone's mark and its colours. Four tones, so a column of marks is scannable. */\nconst MARKS: Record<Tone, { glyph: string; dot: string; chip: string }> = {\n neutral: { glyph: '●', dot: 'text-neutral-400', chip: 'bg-neutral-100 text-neutral-600' },\n good: { glyph: '✓', dot: 'text-emerald-600', chip: 'bg-emerald-100 text-emerald-800' },\n warn: { glyph: '▲', dot: 'text-amber-600', chip: 'bg-amber-100 text-amber-800' },\n bad: { glyph: '✕', dot: 'text-rose-600', chip: 'bg-rose-100 text-rose-800' },\n}\n\nconst message = (cause: unknown) => (cause instanceof Error ? cause.message : String(cause))\n\n/** The calendar day an instant falls on, in the reader's zone. This is what groups the entries. */\nfunction dayKeyOf(at: Date, timeZone: string): string {\n const parts = new Intl.DateTimeFormat('en-CA', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }).formatToParts(at)\n const part = (type: Intl.DateTimeFormatPartTypes) =>\n parts.find((one) => one.type === type)?.value ?? ''\n return `${part('year')}-${part('month')}-${part('day')}`\n}\n\n/**\n * What heads a day: the two days a person names rather than dates, then the date itself. The key\n * is already the reader's calendar day, so it is formatted as a plain date in UTC — reading it\n * back through a zone would shift it a day either way.\n */\nfunction dayLabel(key: string, today: string, yesterday: string, locale: string): string {\n if (key === today) return 'Today'\n if (key === yesterday) return 'Yesterday'\n return new Intl.DateTimeFormat(locale, {\n timeZone: 'UTC',\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n year: key.slice(0, 4) === today.slice(0, 4) ? undefined : 'numeric',\n }).format(new Date(`${key}T12:00:00Z`))\n}\n\n/**\n * How long ago, for an entry from today; the clock time for one from any other day. A log read\n * during the day is a log of minutes, and a log read later is a log of days.\n */\nfunction timeLabel(\n at: Date | null,\n now: Date,\n today: boolean,\n locale: string,\n timeZone: string,\n): string {\n if (at === null) return '—'\n if (today) {\n const minutes = Math.floor((now.getTime() - at.getTime()) / 60_000)\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes} min ago`\n const hours = Math.floor(minutes / 60)\n return hours === 1 ? '1 hour ago' : `${hours} hours ago`\n }\n return new Intl.DateTimeFormat(locale, { timeZone, hour: '2-digit', minute: '2-digit' }).format(\n at,\n )\n}\n\n/** A file reference as the entry holds it: an id on its own, or an id with a name beside it. */\ninterface EntryFile {\n id: string\n name: string\n}\n\nfunction filesOf(value: unknown): EntryFile[] {\n if (!Array.isArray(value)) return []\n const found: EntryFile[] = []\n for (const one of value) {\n if (typeof one === 'string') found.push({ id: one, name: one })\n else if (one && typeof one === 'object') {\n const ref = one as { id?: unknown; name?: unknown }\n if (typeof ref.id === 'string') {\n found.push({ id: ref.id, name: typeof ref.name === 'string' ? ref.name : ref.id })\n }\n }\n }\n return found\n}\n\ninterface Result {\n /** The query these entries answer. A result whose key is not the current one is stale. */\n key: string\n entries: TimelineEntry[]\n nextCursor: string | null\n error: string | null\n}\n\ntype Chosen = { kind?: string; actor?: string }\n\n/**\n * Pages of entries, newest first, and the live updates that put a new one on top. Everything that\n * arrives from the adapter is here; nothing polls.\n */\nfunction useEntries(\n records: RecordsAdapter,\n config: TimelineConfig,\n chosen: Chosen,\n search: string,\n) {\n const [result, setResult] = useState<Result | null>(null)\n const [loadingMore, setLoadingMore] = useState(false)\n const [actors, setActors] = useState<string[]>([])\n\n // How deep the reader has paged, so a live update re-lists what they can see rather than\n // snapping them back to the newest page.\n const loaded = useRef(0)\n\n const query = useMemo(() => {\n const filter: Record<string, FilterValue> = {}\n if (config.kindField && chosen.kind !== undefined) filter[config.kindField] = chosen.kind\n if (config.actorField && chosen.actor !== undefined) filter[config.actorField] = chosen.actor\n const text = search.trim()\n return {\n filter: Object.keys(filter).length > 0 ? filter : undefined,\n sort: { field: config.dateField, direction: 'desc' as const },\n search: text !== '' && config.search.length > 0 ? { text, fields: config.search } : undefined,\n }\n }, [config.kindField, config.actorField, config.dateField, config.search, chosen, search])\n\n const key = useMemo(\n () => JSON.stringify([config.collection, query, config.pageSize]),\n [config.collection, query, config.pageSize],\n )\n\n // The actor chips offer everyone whose entries have arrived, and keep offering them once a\n // filter narrows the list; otherwise picking a person would erase everyone else from the row.\n const wantsActors = config.filters.includes('actor')\n const absorb = useCallback(\n (entries: TimelineEntry[]) => {\n if (!wantsActors || !config.actorField) return\n const field = config.actorField\n setActors((prev) => {\n const seen = new Set(prev)\n let changed = false\n for (const entry of entries) {\n const value = entry[field]\n // Only an actor held as a name can be filtered on: the chip's value is what `list` is\n // asked to match, and a record is not an equality the adapter can answer.\n if (typeof value === 'string' && value !== '' && !seen.has(value)) {\n seen.add(value)\n changed = true\n }\n }\n return changed ? [...seen].sort() : prev\n })\n },\n [wantsActors, config.actorField],\n )\n\n const fetchPage = useCallback(\n (cursor: string | null, limit: number) =>\n records.list<TimelineEntry>(config.collection, { ...query, cursor, limit }),\n [records, config.collection, query],\n )\n\n useEffect(() => {\n let live = true\n const refresh = (limit: number) => {\n void fetchPage(null, limit).then(\n (page) => {\n if (!live) return\n loaded.current = page.rows.length\n absorb(page.rows)\n setResult({ key, entries: page.rows, nextCursor: page.nextCursor, error: null })\n },\n (cause: unknown) => {\n if (!live) return\n setResult({ key, entries: [], nextCursor: null, error: message(cause) })\n },\n )\n }\n\n refresh(config.pageSize)\n const unsubscribe = records.subscribe(config.collection, () =>\n refresh(Math.max(config.pageSize, loaded.current)),\n )\n return () => {\n live = false\n unsubscribe()\n }\n }, [fetchPage, absorb, key, records, config.collection, config.pageSize])\n\n const nextCursor = result?.key === key ? result.nextCursor : null\n\n const loadOlder = useCallback(() => {\n if (nextCursor === null) return\n setLoadingMore(true)\n void fetchPage(nextCursor, config.pageSize).then(\n (page) => {\n absorb(page.rows)\n setResult((prev) => {\n if (!prev || prev.key !== key) return prev\n const entries = [...prev.entries, ...page.rows]\n loaded.current = entries.length\n return { ...prev, entries, nextCursor: page.nextCursor }\n })\n setLoadingMore(false)\n },\n (cause: unknown) => {\n setResult((prev) => (prev ? { ...prev, error: message(cause) } : prev))\n setLoadingMore(false)\n },\n )\n }, [fetchPage, absorb, key, nextCursor, config.pageSize])\n\n return {\n // Entries from an older query stay on screen while the new ones are in flight, so a keystroke\n // in the search box does not blank the log.\n entries: result?.entries ?? [],\n nextCursor,\n loading: result === null,\n stale: result !== null && result.key !== key,\n loadingMore,\n error: result?.error ?? null,\n actors,\n loadOlder,\n }\n}\n\n/**\n * The pill's count: entries that have appeared *above* the one that used to be on top. Older\n * entries fetched by load-more land below it and are not news.\n */\nfunction useArrivals(entries: TimelineEntry[], atTop: { current: boolean }) {\n const [arrived, setArrived] = useState(0)\n const firstId = useRef<string | null>(null)\n const known = useRef<Set<string>>(new Set())\n const clear = useCallback(() => setArrived(0), [])\n\n useEffect(() => {\n const previousFirst = firstId.current\n firstId.current = entries[0]?.id ?? null\n\n if (previousFirst !== null) {\n const cut = entries.findIndex((entry) => entry.id === previousFirst)\n if (cut > 0 && !atTop.current) setArrived((count) => count + cut)\n }\n known.current = new Set(entries.map((entry) => entry.id))\n }, [entries, atTop])\n\n // An entry the reader has not seen before slides in; one already on screen must not re-animate,\n // which is why this is read during render rather than after it.\n const isNew = (entry: TimelineEntry) => known.current.size > 0 && !known.current.has(entry.id)\n\n return { arrived, clear, isNew }\n}\n\nfunction Attachments({\n files,\n adapter,\n}: {\n files: EntryFile[]\n adapter: FilesAdapter | undefined\n}) {\n const [failed, setFailed] = useState<string | null>(null)\n\n const open = (file: EntryFile) => {\n if (!adapter) {\n setFailed('No files adapter was given, so this attachment cannot be opened.')\n return\n }\n void adapter.url(file.id).then(\n (url) => window.open(url, '_blank', 'noopener,noreferrer'),\n (cause: unknown) => setFailed(message(cause)),\n )\n }\n\n return (\n <div className=\"mt-1.5 flex flex-wrap gap-1.5\">\n {files.map((file) => (\n <button\n key={file.id}\n type=\"button\"\n data-golem-attachment={file.name}\n onClick={() => open(file)}\n className=\"inline-flex max-w-full items-center gap-1 rounded-full border border-neutral-200 bg-neutral-50 px-2 py-0.5 text-xs hover:bg-neutral-100\"\n >\n <span aria-hidden=\"true\">📎</span>\n <span className=\"truncate\">{file.name}</span>\n </button>\n ))}\n {failed !== null && (\n <span role=\"alert\" className=\"text-xs text-red-800\">\n {failed}\n </span>\n )}\n </div>\n )\n}\n\n/** One row of chips: an All chip and one per value, the chosen one filled in. */\nfunction ChipRow({\n label,\n values,\n chosen,\n onChoose,\n}: {\n label: string\n values: { id: string; label: string }[]\n chosen: string | undefined\n onChoose: (value: string | null) => void\n}) {\n return (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <span className=\"text-xs font-medium text-neutral-500\">{label}</span>\n {[null, ...values].map((value) => {\n const active = value === null ? chosen === undefined : chosen === value.id\n return (\n <button\n key={value === null ? '__all' : value.id}\n type=\"button\"\n aria-pressed={active}\n onClick={() => onChoose(value === null ? null : value.id)}\n className={`rounded-full px-2.5 py-1 text-xs font-medium ${\n active ? 'bg-neutral-900 text-white' : 'bg-neutral-100 text-neutral-600'\n }`}\n >\n {value === null ? 'All' : value.label}\n </button>\n )\n })}\n </div>\n )\n}\n\nfunction TimelineBody({ config, adapters }: GolemProps<TimelineConfig, TimelineAdapters>) {\n const [chosen, setChosen] = useState<Chosen>({})\n const [draft, setDraft] = useState('')\n const [search, setSearch] = useState('')\n\n // One list call per pause in the typing, not one per keystroke.\n useEffect(() => {\n const timer = setTimeout(() => setSearch(draft), 150)\n return () => clearTimeout(timer)\n }, [draft])\n\n const { entries, nextCursor, loading, stale, loadingMore, error, actors, loadOlder } = useEntries(\n adapters.records,\n config,\n chosen,\n search,\n )\n\n const feed = useRef<HTMLDivElement>(null)\n const atTop = useRef(true)\n const { arrived, clear, isNew } = useArrivals(entries, atTop)\n\n // A narrowed list is a different list, so whatever had piled up above it is no longer news.\n useEffect(() => clear(), [chosen.kind, chosen.actor, search, clear])\n\n const onScroll = () => {\n const element = feed.current\n if (!element) return\n atTop.current = element.scrollTop < TOP_SLACK\n if (atTop.current && arrived > 0) clear()\n }\n\n const jumpToTop = () => {\n const element = feed.current\n if (element) element.scrollTop = 0\n atTop.current = true\n clear()\n }\n\n const now = adapters.clock.now()\n const timeZone = formattableZone(adapters.clock.timeZone())\n const today = dayKeyOf(now, timeZone)\n const yesterday = dayKeyOf(new Date(now.getTime() - 86_400_000), timeZone)\n\n const kindOf = (entry: TimelineEntry) => {\n if (!config.kindField) return undefined\n const value = entry[config.kindField]\n return config.kinds.find((kind) => kind.id === value)\n }\n\n /**\n * Entries cut into days, in the order they arrived — which is already newest first. One pass over\n * one page, so it is done on every render rather than memoised against a mutable `Date`.\n */\n const days: { key: string; entries: { entry: TimelineEntry; at: Date | null }[] }[] = []\n for (const entry of entries) {\n const parsed = new Date(String(entry[config.dateField]))\n // An entry whose date the platform cannot read still belongs on the log: it joins the day\n // above it and shows an em dash, rather than being dropped or headed `Invalid Date`.\n const at = Number.isNaN(parsed.getTime()) ? null : parsed\n const key = at ? dayKeyOf(at, timeZone) : (days[days.length - 1]?.key ?? today)\n const group = days[days.length - 1]\n if (group?.key === key) group.entries.push({ entry, at })\n else days.push({ key, entries: [{ entry, at }] })\n }\n\n const [composerBusy, setComposerBusy] = useState(false)\n const [composerText, setComposerText] = useState('')\n const [composerKind, setComposerKind] = useState(config.kinds[0]?.id ?? '')\n const [composerError, setComposerError] = useState<string | null>(null)\n\n const addEntry = (event: FormEvent) => {\n event.preventDefault()\n const text = composerText.trim()\n if (text === '' || composerBusy) return\n\n setComposerBusy(true)\n setComposerError(null)\n const write = async () => {\n const user = adapters.identity ? await adapters.identity.currentUser() : null\n const data: Record<string, unknown> = {\n [config.dateField]: adapters.clock.now().toISOString(),\n [config.bodyField]: text,\n }\n if (config.actorField && user) data[config.actorField] = user.name\n if (config.kindField && composerKind !== '') data[config.kindField] = composerKind\n await adapters.records.create(config.collection, data)\n }\n void write().then(\n () => {\n setComposerText('')\n setComposerBusy(false)\n jumpToTop()\n },\n (cause: unknown) => {\n setComposerError(message(cause))\n setComposerBusy(false)\n },\n )\n }\n\n const composer = config.composer && (\n <form onSubmit={addEntry} className=\"mb-3\">\n <div className=\"flex items-center gap-2\">\n <input\n value={composerText}\n onChange={(event) => setComposerText(event.target.value)}\n placeholder=\"Add an entry\"\n aria-label=\"Add an entry\"\n className=\"min-w-0 flex-1 rounded-lg border border-neutral-300 bg-white px-3 py-2 text-base sm:text-sm\"\n />\n {config.kindField && config.kinds.length > 0 && (\n <select\n value={composerKind}\n onChange={(event) => setComposerKind(event.target.value)}\n aria-label=\"Kind\"\n className=\"shrink-0 rounded-lg border border-neutral-300 bg-white px-2 py-2 text-sm\"\n >\n {config.kinds.map((kind) => (\n <option key={kind.id} value={kind.id}>\n {kind.label}\n </option>\n ))}\n </select>\n )}\n <button\n type=\"submit\"\n disabled={composerBusy}\n className=\"shrink-0 rounded-lg bg-neutral-900 px-3.5 py-2 text-sm font-medium text-white disabled:opacity-60\"\n >\n Add\n </button>\n </div>\n {composerError !== null && (\n <p role=\"alert\" className=\"mt-1.5 text-sm text-red-800\">\n {composerError}\n </p>\n )}\n </form>\n )\n\n const controls = (config.search.length > 0 || config.filters.length > 0) && (\n <div className=\"mb-3 space-y-2\">\n {config.search.length > 0 && (\n <input\n type=\"search\"\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n aria-label=\"Search\"\n placeholder=\"Search the log\"\n className=\"w-full rounded-lg border border-neutral-300 bg-white px-3 py-2 text-base sm:text-sm\"\n />\n )}\n {config.filters.includes('kind') && (\n <ChipRow\n label=\"Kind\"\n values={config.kinds}\n chosen={chosen.kind}\n onChoose={(value) =>\n setChosen((prev) => ({ ...prev, kind: value === null ? undefined : value }))\n }\n />\n )}\n {config.filters.includes('actor') && (\n <ChipRow\n label=\"Who\"\n values={actors.map((name) => ({ id: name, label: name }))}\n chosen={chosen.actor}\n onChoose={(value) =>\n setChosen((prev) => ({ ...prev, actor: value === null ? undefined : value }))\n }\n />\n )}\n </div>\n )\n\n let body: ReactNode\n if (loading) {\n body = <p className=\"px-4 py-8 text-center text-sm text-neutral-500\">Loading…</p>\n } else if (error !== null) {\n body = (\n <p role=\"alert\" className=\"px-4 py-6 text-center text-sm text-red-900\">\n {error}\n </p>\n )\n } else if (entries.length === 0) {\n body = <p className=\"px-4 py-8 text-center text-sm text-neutral-500\">{config.emptyState}</p>\n } else {\n body = days.map((group) => (\n <section key={group.key} data-golem-day={group.key}>\n {/* Sticky against the feed's own scroll box, so the day you are reading names itself. */}\n <h3 className=\"sticky top-0 z-10 border-b border-neutral-200 bg-neutral-50/95 px-4 py-1.5 text-xs font-semibold tracking-wide text-neutral-500 uppercase backdrop-blur\">\n {dayLabel(group.key, today, yesterday, config.locale)}\n </h3>\n <ol>\n {group.entries.map(({ entry, at }) => {\n const kind = kindOf(entry)\n const mark = MARKS[kind?.tone ?? 'neutral']\n const actor = config.actorField ? userName(entry[config.actorField]) : ''\n const files = config.attachmentsField ? filesOf(entry[config.attachmentsField]) : []\n return (\n <li\n key={entry.id}\n data-golem-entry={entry.id}\n className={`relative border-b border-neutral-100 py-3 pr-4 pl-9 last:border-0 ${\n isNew(entry) ? 'animate-[golem-slide-in_240ms_ease-out]' : ''\n }`}\n >\n <span\n aria-hidden=\"true\"\n className={`absolute top-3 left-3 text-xs leading-5 ${mark.dot}`}\n >\n {mark.glyph}\n </span>\n <div className=\"flex flex-wrap items-baseline gap-x-2 gap-y-1\">\n {kind && (\n <span\n className={`inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-xs font-medium ${mark.chip}`}\n >\n {kind.label}\n </span>\n )}\n {/* An unknown kind is shown as it is written, rather than hidden: the record\n says something the config has no word for, and that is worth seeing. */}\n {config.kindField && !kind && entry[config.kindField] != null && (\n <span className=\"inline-flex shrink-0 items-center rounded-full bg-neutral-100 px-2 py-0.5 text-xs font-medium text-neutral-500\">\n {String(entry[config.kindField])}\n </span>\n )}\n {actor !== '' && (\n <span className=\"flex items-center gap-1.5 text-xs text-neutral-600\">\n <span className=\"flex size-5 shrink-0 items-center justify-center rounded-full bg-neutral-900 text-[9px] font-semibold text-white\">\n {initials(actor)}\n </span>\n {actor}\n </span>\n )}\n <time\n dateTime={at?.toISOString()}\n className=\"ml-auto shrink-0 text-xs text-neutral-400\"\n >\n {timeLabel(at, now, group.key === today, config.locale, timeZone)}\n </time>\n </div>\n <div className=\"mt-1 text-sm leading-relaxed text-neutral-800\">\n <Markdown text={String(entry[config.bodyField] ?? '')} />\n </div>\n {files.length > 0 && <Attachments files={files} adapter={adapters.files} />}\n </li>\n )\n })}\n </ol>\n </section>\n ))\n }\n\n return (\n <div\n data-golem-component=\"Timeline\"\n className={`golem-timeline w-full text-neutral-900 ${stale ? 'opacity-60' : ''}`}\n >\n {composer}\n {controls}\n\n <div className=\"relative\">\n {arrived > 0 && (\n <button\n type=\"button\"\n data-golem-new-pill=\"\"\n onClick={jumpToTop}\n className=\"absolute top-2 left-1/2 z-20 -translate-x-1/2 rounded-full bg-neutral-900 px-3 py-1.5 text-xs font-medium text-white shadow-lg\"\n >\n {arrived === 1 ? '1 new entry' : `${arrived} new entries`} ↑\n </button>\n )}\n {/* Its own scroll box, so the day headers have something to stick to and the pill has a\n scroll position to watch. */}\n <div\n ref={feed}\n data-golem-feed=\"\"\n onScroll={onScroll}\n className=\"max-h-[70vh] overflow-y-auto rounded-xl border border-neutral-200 bg-white\"\n >\n {body}\n {nextCursor !== null && !loading && (\n <div className=\"flex justify-center border-t border-neutral-100 p-3\">\n <button\n type=\"button\"\n onClick={loadOlder}\n disabled={loadingMore}\n className=\"rounded-lg border border-neutral-300 bg-white px-4 py-2 text-sm font-medium disabled:opacity-60\"\n >\n {loadingMore ? 'Loading…' : `Load ${config.pageSize} older`}\n </button>\n </div>\n )}\n </div>\n </div>\n </div>\n )\n}\n\nexport const Timeline = defineComponent<typeof timelineConfigSchema, TimelineAdapters>({\n name: 'Timeline',\n schema: timelineConfigSchema,\n render: TimelineBody,\n})\n","import { z } from 'zod'\n\n/** `type/subtype`, where the subtype may be `*`. What `accept` is checked against. */\nconst MIME = /^[a-z]+\\/[a-z0-9!#$&^_.+-]+$|^[a-z]+\\/\\*$/\n\nexport const uploadConfigSchema = z\n .object({\n folder: z\n .string()\n .min(1)\n .describe(\n 'Where the files live, passed to `upload`, `list` and `subscribe`. One folder is one gallery; two Uploads on the same folder show the same files.',\n ),\n accept: z\n .array(z.string().min(1))\n .default([])\n .describe(\n 'The MIME types the picker offers and the component lets through, as `image/jpeg` or `image/*`. Empty accepts anything. A file of any other type is refused before it is uploaded.',\n ),\n maxFiles: z\n .number()\n .int()\n .min(1)\n .max(500)\n .default(20)\n .describe(\n 'How many files the folder may hold. Everything already in the gallery counts, so the last pick is the one refused.',\n ),\n maxSizeMb: z\n .number()\n .positive()\n .max(2048)\n .default(10)\n .describe(\n 'The largest file accepted, in megabytes. It is checked in the browser before `upload` is called, so an over-size file never leaves the machine.',\n ),\n capture: z\n .enum(['environment', 'user', 'off'])\n .default('off')\n .describe(\n 'Whether a phone gets a camera button beside the file button: `environment` opens the rear camera, `user` the front one, `off` shows no camera button. A desktop browser ignores it and offers the file picker.',\n ),\n captions: z\n .boolean()\n .default(false)\n .describe(\n 'Whether each file carries one line of the reader’s own words, written under the tile and saved through `Files.caption`.',\n ),\n layout: z\n .enum(['grid', 'list'])\n .default('grid')\n .describe(\n 'How the gallery reads: `grid` is thumbnails, for a folder of photos; `list` is one row per file with its icon, name and size, for a folder of documents.',\n ),\n deleteAllowed: z\n .boolean()\n .default(true)\n .describe(\n 'Whether a file can be deleted from the gallery. Off hides every delete button; the component never calls `Files.remove`.',\n ),\n emptyState: z\n .string()\n .min(1)\n .default('No files yet.')\n .describe('One line shown in place of the gallery while the folder is empty.'),\n })\n .strict()\n .superRefine((config, ctx) => {\n for (const [index, type] of config.accept.entries()) {\n if (!MIME.test(type)) {\n ctx.addIssue({\n code: 'custom',\n path: ['accept', index],\n message: `\"${type}\" is not a MIME type, so no file can ever match it. Write it as type/subtype: image/jpeg, application/pdf, image/*.`,\n })\n }\n }\n })\n\nexport type UploadConfig = z.output<typeof uploadConfigSchema>\nexport type UploadConfigInput = z.input<typeof uploadConfigSchema>\n\n/** Why a file was turned away before anything was uploaded. */\nexport type RejectionReason = 'size' | 'type' | 'count'\n\n/**\n * A file the component refused on its own, without asking the adapter. It is thrown nowhere: the\n * component catches its own limits and shows the `message` on the file's row, which is where the\n * reader can do something about it.\n */\nexport class FileRejectedError extends Error {\n readonly fileName: string\n readonly reason: RejectionReason\n\n constructor(fileName: string, reason: RejectionReason, message: string) {\n super(message)\n this.name = 'FileRejectedError'\n this.fileName = fileName\n this.reason = reason\n }\n}\n\nconst MB = 1024 * 1024\n\nexport function megabytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < MB) return `${Math.round(bytes / 1024)} KB`\n return `${(bytes / MB).toFixed(1)} MB`\n}\n\n/** Whether the type is one the `accept` list lets through. An empty list lets everything through. */\nexport function accepts(accept: string[], contentType: string): boolean {\n if (accept.length === 0) return true\n const [group, sub] = contentType.split('/')\n return accept.some((entry) => {\n const [wantGroup, wantSub] = entry.split('/')\n return wantGroup === group && (wantSub === '*' || wantSub === sub)\n })\n}\n\n/**\n * The three limits, checked in the order a reader would ask about them: is it the right kind of\n * thing, is it small enough, is there room. Returns the refusal, or `null` when the file may go up.\n * `position` is what number this file would be in the folder, counting from one.\n */\nexport function checkFile(\n file: { name: string; size: number; type: string },\n config: UploadConfig,\n position: number,\n): FileRejectedError | null {\n if (!accepts(config.accept, file.type)) {\n return new FileRejectedError(\n file.name,\n 'type',\n `“${file.name}” is ${file.type || 'of no known type'}. This upload takes ${config.accept.join(', ')}.`,\n )\n }\n if (file.size > config.maxSizeMb * MB) {\n return new FileRejectedError(\n file.name,\n 'size',\n `“${file.name}” is ${megabytes(file.size)}. The limit is ${config.maxSizeMb} MB.`,\n )\n }\n if (position > config.maxFiles) {\n return new FileRejectedError(\n file.name,\n 'count',\n `“${file.name}” would be file ${position}. This upload holds ${config.maxFiles}.`,\n )\n }\n return null\n}\n","import { useCallback, useEffect, useRef, useState, type DragEvent, type ReactNode } from 'react'\nimport { defineComponent, type GolemProps } from '../../abi'\nimport type { FileRef, FilesAdapter } from '../../adapters'\nimport { checkFile, megabytes, uploadConfigSchema, type UploadConfig } from './Upload.config'\n\nexport interface UploadAdapters {\n files: FilesAdapter\n}\n\nexport interface PickerSlots {\n /**\n * Called with everything the picker is holding, every time that changes — a file finishing its\n * upload, or a chip being taken off. The parent composer keeps the list; the picker only shows it.\n */\n onPicked?: (refs: FileRef[]) => void\n}\n\n/** One file on its way up, or one that did not make it. */\ninterface QueueItem {\n key: string\n name: string\n size: number\n /** 0 to 1. Stays at 0 for a file refused before the upload started. */\n progress: number\n error: string | null\n}\n\nconst message = (cause: unknown) => (cause instanceof Error ? cause.message : String(cause))\n\n/** What tiles a file that is not a picture. Type first, then the extension. */\nfunction iconFor(contentType: string, name: string): string {\n if (contentType.startsWith('image/')) return '🖼'\n if (contentType.startsWith('audio/')) return '🎧'\n if (contentType.startsWith('video/')) return '🎬'\n if (contentType === 'application/pdf' || name.endsWith('.pdf')) return '📄'\n return '📎'\n}\n\nconst isImage = (ref: FileRef) => ref.contentType.startsWith('image/')\n\n/**\n * The upload queue: what the reader picked, what the limits said about it, and how far up each one\n * has got. Both surfaces run on this, so a file is refused for the same reason and with the same\n * sentence whether it was picked in a gallery or in a composer.\n */\nfunction useQueue(\n files: FilesAdapter,\n config: UploadConfig,\n onUploaded: (ref: FileRef) => void,\n held: () => number,\n) {\n const [queue, setQueue] = useState<QueueItem[]>([])\n const nextKey = useRef(0)\n\n const dismiss = useCallback((key: string) => {\n setQueue((rows) => rows.filter((row) => row.key !== key))\n }, [])\n\n const add = useCallback(\n (picked: File[]) => {\n // Where in the folder each of these would land, so the count limit names the right file.\n let position = held()\n\n for (const file of picked) {\n const key = `q-${nextKey.current++}`\n position += 1\n const refusal = checkFile(file, config, position)\n\n setQueue((rows) => [\n ...rows,\n {\n key,\n name: file.name,\n size: file.size,\n progress: 0,\n error: refusal ? refusal.message : null,\n },\n ])\n\n // A refused file never reaches the adapter: the limit is enforced here, on this machine.\n if (refusal) {\n position -= 1\n continue\n }\n\n const advance = (fraction: number) =>\n setQueue((rows) =>\n rows.map((row) => (row.key === key ? { ...row, progress: fraction } : row)),\n )\n\n void files.upload(file, { folder: config.folder, onProgress: advance }).then(\n (ref) => {\n // The file has left the queue for the gallery; keeping a finished row would say it\n // is still in flight.\n dismiss(key)\n onUploaded(ref)\n },\n (cause: unknown) => {\n setQueue((rows) =>\n rows.map((row) => (row.key === key ? { ...row, error: message(cause) } : row)),\n )\n },\n )\n }\n },\n [files, config, onUploaded, held, dismiss],\n )\n\n return { queue, add, dismiss }\n}\n\ninterface GalleryUrls {\n full: string\n thumbnail: string\n}\n\n/** What is in the folder, and the URLs to draw it with. Nothing polls; `subscribe` re-lists. */\nfunction useGallery(files: FilesAdapter, folder: string) {\n const [refs, setRefs] = useState<FileRef[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n // `null` is a file whose URL the store refused, told apart from one not asked for yet, so a\n // thumbnail that will never arrive tiles as an icon instead of an empty frame.\n const [urls, setUrls] = useState<Record<string, GalleryUrls | null>>({})\n\n useEffect(() => {\n let live = true\n const refresh = () => {\n void files.list(folder).then(\n (found) => {\n if (!live) return\n setRefs(found)\n setError(null)\n },\n (cause: unknown) => {\n if (!live) return\n setRefs([])\n setError(message(cause))\n },\n )\n }\n refresh()\n const unsubscribe = files.subscribe(folder, refresh)\n return () => {\n live = false\n unsubscribe()\n }\n }, [files, folder])\n\n // One pair of URLs per file, resolved once. A file that has gone keeps its entry until unmount,\n // which costs a map entry and saves re-resolving a file that comes back.\n useEffect(() => {\n let live = true\n for (const ref of refs ?? []) {\n if (ref.id in urls) continue\n void Promise.all([files.url(ref, { thumbnail: true }), files.url(ref)]).then(\n ([thumbnail, full]) => {\n if (live) setUrls((current) => ({ ...current, [ref.id]: { full, thumbnail } }))\n },\n () => {\n if (live) setUrls((current) => ({ ...current, [ref.id]: null }))\n },\n )\n }\n return () => {\n live = false\n }\n }, [refs, files, urls])\n\n return { refs, error, urls }\n}\n\nfunction ProgressBar({ fraction }: { fraction: number }) {\n const percent = Math.round(fraction * 100)\n return (\n <div\n role=\"progressbar\"\n aria-valuenow={percent}\n aria-valuemin={0}\n aria-valuemax={100}\n className=\"mt-1 h-1.5 w-full overflow-hidden rounded-full bg-neutral-200\"\n >\n <div\n style={{ width: `${percent}%` }}\n className=\"h-full rounded-full bg-neutral-900 transition-[width] duration-150\"\n />\n </div>\n )\n}\n\nfunction Queue({ rows, onDismiss }: { rows: QueueItem[]; onDismiss: (key: string) => void }) {\n if (rows.length === 0) return null\n return (\n <ul className=\"mt-3 space-y-2\">\n {rows.map((row) => (\n <li\n key={row.key}\n data-golem-queue-item={row.name}\n className={`rounded-lg border p-2.5 ${\n row.error === null ? 'border-neutral-200 bg-white' : 'border-red-300 bg-red-50'\n }`}\n >\n <div className=\"flex items-baseline gap-2\">\n <span className=\"min-w-0 flex-1 truncate text-sm\">{row.name}</span>\n <span className=\"shrink-0 text-xs text-neutral-500\">\n {row.error === null ? `${Math.round(row.progress * 100)}%` : megabytes(row.size)}\n </span>\n <button\n type=\"button\"\n onClick={() => onDismiss(row.key)}\n aria-label={`Dismiss ${row.name}`}\n className=\"shrink-0 text-neutral-400 hover:text-neutral-700\"\n >\n ×\n </button>\n </div>\n {row.error === null ? (\n <ProgressBar fraction={row.progress} />\n ) : (\n <p role=\"alert\" className=\"mt-1 text-xs text-red-800\">\n {row.error}\n </p>\n )}\n </li>\n ))}\n </ul>\n )\n}\n\n/**\n * The three ways a file gets in: dropped on the zone, chosen from the file picker, or taken with\n * the camera. All three land in the same `add`.\n */\nfunction DropZone({\n config,\n onFiles,\n}: {\n config: UploadConfig\n onFiles: (picked: File[]) => void\n}) {\n const [over, setOver] = useState(false)\n const chooser = useRef<HTMLInputElement>(null)\n const camera = useRef<HTMLInputElement>(null)\n\n const accept = config.accept.join(',')\n const take = (list: FileList | null) => onFiles([...(list ?? [])])\n\n const onDrop = (event: DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n setOver(false)\n take(event.dataTransfer.files)\n }\n\n return (\n <div\n data-golem-dropzone=\"\"\n onDragOver={(event) => {\n event.preventDefault()\n setOver(true)\n }}\n onDragLeave={() => setOver(false)}\n onDrop={onDrop}\n className={`rounded-xl border-2 border-dashed p-5 text-center ${\n over ? 'border-neutral-900 bg-neutral-100' : 'border-neutral-300 bg-white'\n }`}\n >\n <p className=\"text-sm text-neutral-600\">Drop files here, or pick them from this device.</p>\n <p className=\"mt-1 text-xs text-neutral-400\">\n Up to {config.maxFiles} files, {config.maxSizeMb} MB each\n {config.accept.length > 0 ? ` · ${config.accept.join(', ')}` : ''}\n </p>\n\n <div className=\"mt-3 flex flex-wrap justify-center gap-2\">\n <button\n type=\"button\"\n onClick={() => chooser.current?.click()}\n className=\"rounded-lg bg-neutral-900 px-4 py-2.5 text-sm font-medium text-white\"\n >\n Choose files\n </button>\n {config.capture !== 'off' && (\n <button\n type=\"button\"\n onClick={() => camera.current?.click()}\n className=\"rounded-lg border border-neutral-300 bg-white px-4 py-2.5 text-sm font-medium\"\n >\n 📷 Take a photo\n </button>\n )}\n </div>\n\n <input\n ref={chooser}\n type=\"file\"\n multiple\n accept={accept || undefined}\n aria-label=\"Choose files\"\n className=\"hidden\"\n onChange={(event) => {\n take(event.target.files)\n event.target.value = ''\n }}\n />\n {config.capture !== 'off' && (\n <input\n ref={camera}\n type=\"file\"\n accept=\"image/*\"\n capture={config.capture}\n aria-label=\"Take a photo\"\n className=\"hidden\"\n onChange={(event) => {\n take(event.target.files)\n event.target.value = ''\n }}\n />\n )}\n </div>\n )\n}\n\nfunction Caption({ file, files }: { file: FileRef; files: FilesAdapter }) {\n const [text, setText] = useState(file.caption ?? '')\n\n return (\n <input\n value={text}\n onChange={(event) => setText(event.target.value)}\n // Saved when the reader leaves the box, not on every keystroke: a caption is a sentence.\n onBlur={() => {\n if (text !== (file.caption ?? '')) void files.caption(file, text)\n }}\n placeholder=\"Add a caption\"\n aria-label={`Caption for ${file.name}`}\n className=\"mt-1.5 w-full rounded border border-neutral-200 px-2 py-1 text-xs\"\n />\n )\n}\n\nfunction DeleteButton({\n file,\n files,\n onDeleted,\n}: {\n file: FileRef\n files: FilesAdapter\n onDeleted: () => void\n}) {\n const [asking, setAsking] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n if (!asking) {\n return (\n <button\n type=\"button\"\n onClick={() => setAsking(true)}\n className=\"text-xs font-medium text-neutral-500 hover:text-red-700\"\n >\n Delete\n </button>\n )\n }\n\n return (\n <span role=\"alertdialog\" aria-label={`Delete ${file.name}?`} className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n void files.remove(file).then(onDeleted, (cause: unknown) => setError(message(cause)))\n }}\n className=\"rounded bg-red-600 px-2 py-1 text-xs font-medium text-white\"\n >\n Delete permanently\n </button>\n <button\n type=\"button\"\n onClick={() => setAsking(false)}\n className=\"rounded border border-neutral-300 px-2 py-1 text-xs font-medium\"\n >\n Keep it\n </button>\n {error !== null && (\n <span role=\"alert\" className=\"text-xs text-red-800\">\n {error}\n </span>\n )}\n </span>\n )\n}\n\nfunction Lightbox({\n file,\n url,\n onClose,\n}: {\n file: FileRef\n url: string | undefined\n onClose: () => void\n}) {\n useEffect(() => {\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onClose()\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [onClose])\n\n return (\n <div\n role=\"dialog\"\n aria-label={file.name}\n data-golem-lightbox={file.name}\n onClick={onClose}\n className=\"fixed inset-0 z-50 flex flex-col items-center justify-center gap-3 bg-black/80 p-4\"\n >\n {isImage(file) && url ? (\n <img\n src={url}\n alt={file.caption ?? file.name}\n onClick={(event) => event.stopPropagation()}\n className=\"max-h-[75vh] max-w-full rounded-lg bg-white object-contain\"\n />\n ) : (\n <div\n onClick={(event) => event.stopPropagation()}\n className=\"rounded-xl bg-white p-6 text-center\"\n >\n <p className=\"text-4xl\">{iconFor(file.contentType, file.name)}</p>\n <p className=\"mt-2 text-sm font-medium\">{file.name}</p>\n <p className=\"mt-1 text-xs text-neutral-500\">\n {file.contentType || 'unknown type'} · {megabytes(file.size)}\n </p>\n {url && (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-3 inline-block text-sm font-medium underline\"\n >\n Open it\n </a>\n )}\n </div>\n )}\n <p className=\"max-w-full truncate text-sm text-white\">{file.caption ?? file.name}</p>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg bg-white px-4 py-2 text-sm font-medium\"\n >\n Close\n </button>\n </div>\n )\n}\n\nfunction Gallery({\n config,\n files,\n refs,\n urls,\n}: {\n config: UploadConfig\n files: FilesAdapter\n refs: FileRef[]\n urls: Record<string, GalleryUrls | null>\n}) {\n const [open, setOpen] = useState<string | null>(null)\n const openFile = refs.find((ref) => ref.id === open)\n\n const grid = config.layout === 'grid'\n // A picture the store gave us no URL for is drawn as its icon: an empty frame would say the\n // file is blank rather than that the thumbnail did not arrive.\n const asPicture = (file: FileRef) => isImage(file) && urls[file.id] !== null\n\n const tile = (file: FileRef) => (\n <div className=\"min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={() => setOpen(file.id)}\n aria-label={`Open ${file.name}`}\n className=\"block w-full text-left\"\n >\n {grid ? (\n // Every tile is the same square, picture or not, so a row of the grid is one line high.\n <span className=\"block aspect-square overflow-hidden rounded-lg bg-neutral-100\">\n {asPicture(file) ? (\n urls[file.id] && (\n <img\n src={urls[file.id]!.thumbnail}\n alt={file.caption ?? file.name}\n className=\"size-full object-cover\"\n />\n )\n ) : (\n <span className=\"flex size-full flex-col items-center justify-center gap-1\">\n <span className=\"text-3xl leading-none\">\n {iconFor(file.contentType, file.name)}\n </span>\n <span className=\"text-xs text-neutral-500\">{megabytes(file.size)}</span>\n </span>\n )}\n </span>\n ) : (\n <span className=\"flex items-center gap-2\">\n <span className=\"text-2xl leading-none\">{iconFor(file.contentType, file.name)}</span>\n <span className=\"min-w-0\">\n <span className=\"block truncate text-sm font-medium\">{file.name}</span>\n <span className=\"block text-xs text-neutral-500\">{megabytes(file.size)}</span>\n </span>\n </span>\n )}\n </button>\n\n {grid && <p className=\"mt-1 truncate text-xs text-neutral-600\">{file.name}</p>}\n {config.captions && <Caption file={file} files={files} />}\n </div>\n )\n\n return (\n <>\n <ul\n className={\n config.layout === 'grid'\n ? 'mt-4 grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4'\n : 'mt-4 space-y-2'\n }\n >\n {refs.map((file) => (\n <li\n key={file.id}\n data-golem-file={file.id}\n className={`rounded-xl border border-neutral-200 bg-white p-2 ${\n config.layout === 'list' ? 'flex items-center gap-3' : ''\n }`}\n >\n {tile(file)}\n {config.deleteAllowed && (\n <div className={config.layout === 'list' ? 'shrink-0' : 'mt-1'}>\n <DeleteButton\n file={file}\n files={files}\n onDeleted={() => setOpen((id) => (id === file.id ? null : id))}\n />\n </div>\n )}\n </li>\n ))}\n </ul>\n\n {openFile && (\n <Lightbox file={openFile} url={urls[openFile.id]?.full} onClose={() => setOpen(null)} />\n )}\n </>\n )\n}\n\nfunction UploadPanel({ config, adapters }: GolemProps<UploadConfig, UploadAdapters>) {\n const { refs, error, urls } = useGallery(adapters.files, config.folder)\n\n // The count limit is about the folder, so what is already in the gallery counts towards it.\n const countHeld = useCallback(() => refs?.length ?? 0, [refs])\n const noop = useCallback(() => {}, [])\n\n const { queue, add, dismiss } = useQueue(adapters.files, config, noop, countHeld)\n\n let body: ReactNode\n if (refs === null) {\n body = <p className=\"mt-6 text-center text-sm text-neutral-500\">Loading…</p>\n } else if (error !== null) {\n body = (\n <p role=\"alert\" className=\"mt-6 text-center text-sm text-red-900\">\n {error}\n </p>\n )\n } else if (refs.length === 0) {\n body = <p className=\"mt-6 text-center text-sm text-neutral-500\">{config.emptyState}</p>\n } else {\n body = <Gallery config={config} files={adapters.files} refs={refs} urls={urls} />\n }\n\n return (\n <div data-golem-component=\"Upload\" className=\"golem-upload w-full text-neutral-900\">\n <DropZone config={config} onFiles={add} />\n <Queue rows={queue} onDismiss={dismiss} />\n {body}\n </div>\n )\n}\n\nfunction PickerButton({\n config,\n adapters,\n onPicked,\n}: GolemProps<UploadConfig, UploadAdapters, PickerSlots>) {\n const [picked, setPicked] = useState<FileRef[]>([])\n const input = useRef<HTMLInputElement>(null)\n\n // The parent hears about the list, never about the change: one place to report from, and the\n // callback is read out of a ref so a composer that writes its slot inline does not re-report on\n // every render of the composer.\n const report = useRef(onPicked)\n useEffect(() => {\n report.current = onPicked\n })\n useEffect(() => {\n report.current?.(picked)\n }, [picked])\n\n const onUploaded = useCallback((ref: FileRef) => setPicked((held) => [...held, ref]), [])\n const countHeld = useCallback(() => picked.length, [picked])\n\n const { queue, add, dismiss } = useQueue(adapters.files, config, onUploaded, countHeld)\n\n return (\n <div data-golem-component=\"Upload.Picker\" className=\"golem-upload-picker w-full\">\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <button\n type=\"button\"\n onClick={() => input.current?.click()}\n className=\"shrink-0 rounded-full border border-neutral-300 bg-white px-3 py-1.5 text-sm\"\n >\n 📎 Attach\n </button>\n {picked.map((file) => (\n <span\n key={file.id}\n data-golem-picker-chip={file.name}\n className=\"inline-flex max-w-full items-center gap-1 rounded-full border border-neutral-200 bg-neutral-50 px-2 py-0.5 text-xs\"\n >\n <span aria-hidden=\"true\">{iconFor(file.contentType, file.name)}</span>\n <span className=\"truncate\">{file.name}</span>\n <button\n type=\"button\"\n onClick={() => setPicked((held) => held.filter((one) => one.id !== file.id))}\n aria-label={`Remove ${file.name}`}\n className=\"shrink-0 opacity-60 hover:opacity-100\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n\n <Queue rows={queue} onDismiss={dismiss} />\n\n <input\n ref={input}\n type=\"file\"\n multiple\n accept={config.accept.join(',') || undefined}\n capture={config.capture === 'off' ? undefined : config.capture}\n aria-label=\"Attach files\"\n className=\"hidden\"\n onChange={(event) => {\n add([...(event.target.files ?? [])])\n event.target.value = ''\n }}\n />\n </div>\n )\n}\n\nconst UploadComponent = defineComponent<typeof uploadConfigSchema, UploadAdapters>({\n name: 'Upload',\n schema: uploadConfigSchema,\n render: UploadPanel,\n})\n\nconst Picker = defineComponent<typeof uploadConfigSchema, UploadAdapters, PickerSlots>({\n name: 'Upload.Picker',\n schema: uploadConfigSchema,\n render: PickerButton,\n})\n\n/**\n * Two surfaces on one config. `Upload` is the whole thing — a place to drop files, a queue while\n * they go up, and the gallery they land in. `Upload.Picker` is the same picking and the same\n * limits with no gallery: a button a composer hosts, handing the file refs back to whatever is\n * about to send them.\n */\nexport const Upload = Object.assign(UploadComponent, { Picker })\n"],"names":["issuesFromZodError","error","issue","ConfigErrorCard","component","issues","jsxs","jsx","i","defineComponent","spec","Render","Component","props","config","rest","parsed","useMemo","FIELD_TYPES","common","z","listFieldSchema","fieldOptionSchema","formFieldSchema","RecordRefusedError","message","fields","refusedFields","named","one","VERSION_FIELD","VersionConflictError","current","conflictingRecord","thrown","fileId","ref","parseLocation","location","match","start","createEmitter","listeners","listener","value","matches","wanted","compare","a","b","left","right","hit","row","text","needle","field","fakeRecords","seed","options","store","name","rows","emitters","nextId","collection","emitterFor","indexOf","id","refuseIfAsked","refuse","insert","next","query","result","entries","key","want","direction","sign","total","offset","limit","end","found","data","patch","at","STEPS","placeholderImage","label","hue","svg","objectUrlFor","file","loadImage","url","graceMs","resolve","image","timer","settle","THUMB_EDGE","thumbnailOf","contentType","scale","canvas","context","fakeFiles","failOn","uploadMs","now","folders","folder","emitter","content","stored","onProgress","full","step","urlOptions","caption","fakeUser","fakeIdentity","password","code","defaultRole","inviteBase","resetBase","members","member","user","invites","resets","find","email","enter","given","input","role","invited","token","userId","index","entry","DEFAULT_REPLIES","fakeChat","initial","replies","tokenDelayMs","messages","sent","emit","stream","reply","words","written","tick","attachments","line","command","c","arg","fakeClock","fixed","timeZone","fakeNavigation","route","path","frontMatter","pair","RESERVED","dirOf","dir","fakeBrain","files","list","prefix","seen","slash","own","lines","description","hits","excerpt","brainAdapterDocs","chatAdapterDocs","clockAdapterDocs","filesAdapterDocs","identityAdapterDocs","navigationAdapterDocs","recordsAdapterDocs","brainDocs","authDocs","chatDocs","editorDocs","recordFormDocs","recordListDocs","reportDocs","shellDocs","timelineDocs","uploadDocs","componentDocs","adapterDocs","adapterDocsFor","slug","docs","adapterDocsNamed","componentsUsing","adapter","roleSchema","copySchema","authConfigSchema","useSession","identity","setUser","useState","ready","setReady","useEffect","live","unsubscribe","useRouteToken","navigation","setToken","roleFor","labelForRole","canManage","messageOf","fieldClass","primaryClass","quietClass","Field","Card","children","SignInScreen","adapters","invite","reset","signUpOffered","signUpReachable","chose","setChose","setName","setEmail","secret","setSecret","codeSent","setCodeSent","setError","notice","setNotice","busy","setBusy","resetting","creating","creatingNow","codeStage","run","event","action","failure","submit","title","Fragment","useMembers","setMembers","reload","useCallback","MembersScreen","manages","inviteRole","setInviteRole","link","setLink","copied","setCopied","mint","minting","AccountMenuPanel","open","setOpen","initialsOf","part","DeniedCard","roles","allowed","AuthScreen","AuthGuard","AuthComponent","Guard","AccountMenu","Auth","INLINE","inlineNodes","keyPrefix","piece","external","href","headingSlug","cells","cell","isTableRule","blocksOf","blocks","fence","blank","heading","table","bullet","numbered","last","ordered","item","HEADING_SIZES","Markdown","hardWraps","resolveLink","inline","block","Tag","cellIndex","rowIndex","List","windowWidth","useContainerWidth","measured","setMeasured","viewport","setViewport","element","observer","onResize","brainConfigSchema","PHONE","BRAIN_LINK","ROOT","same","locationOf","f","resolvePath","from","relative","parts","close","meta","m","Skeleton","_","Tree","onOpen","version","setEntries","collapsed","setCollapsed","Hits","setFound","Reader","frame","marked","useRef","body","mark","box","to","head","BackIcon","SearchIcon","BrainPanel","toolbar","root","article","phone","stack","setStack","scrolls","setVersion","doc","setDoc","setQuery","seenLocation","setSeenLocation","s","push","back","v","cause","depth","useLayoutEffect","onArticleClick","document","Brain","chatConfigSchema","PIN_SLACK","SLASH_LINE","CMD_RE","ARG_RE","slashMatches","commands","args","q","useConversation","setMessages","loadError","setLoadError","receivedUpdate","useStickToBottom","dependency","feed","pinned","onScroll","hhmm","sizeLabel","bytes","AttachmentChips","onRemove","sourceLabel","range","Bubble","onRetry","onOpenSource","mine","pending","failed","copy","Thinking","onStop","stopping","setStopping","delay","ChatPanel","attach","draft","setDraft","staged","setStaged","setSent","sendError","setSendError","composer","fileInput","commandRows","setCommandRows","setCommands","selected","setSelected","pickerClosed","setPickerClosed","pickerWanted","pickerOpen","pick","visible","thinking","max","send","count","stamp","retry","messageId","interrupt","Chat","MAX_LINES","splitLines","matchedPairs","cols","j","pairs","editsOf","base","side","which","edits","baseAt","sideAt","baseEnd","sideEnd","baseIndex","sideIndex","merge3","baseText","mineText","theirsText","theirs","chunks","conflicted","mineAt","theirsAt","keepUntouched","until","group","baseStart","sides","edit","sliceOf","cursor","mineLines","theirsLines","chunk","mergedText","choose","theirLines","taken","isFormattableLocale","locale","formattableZone","editorConfigSchema","ctx","scanSource","fenced","headingsOf","LINE_CLASS","MARKER","SourceRow","SPLIT_ABOVE","EMPTY","decode","passageOf","focus","atVersion","eol","first","clamp","holds","COALESCE_MS","useHistory","state","remember","history","delta","EditorBody","openWith","records","clock","textarea","overlay","preview","sourcePane","setDocState","docRef","mode","setMode","outlineOpen","setOutlineOpen","selection","record","opened","parked","loaded","reads","setReads","wide","split","showPreview","showSource","applyIncoming","merged","save","expected","here","away","saved","shelf","read","load","opening","ask","asked","spot","setSpot","scrollToSpot","request","passage","pane","origin","top","bottom","scanned","headings","shown","write","caret","wrap","marker","indent","out","stop","shifted","moved","onKeyDown","jumpTo","sum","choices","_chunk","later","Toolbar","Outline","Conflict","focused","button","onOutline","onMode","onWrap","onSave","Status","time","onJump","onChoose","Side","chosen","Editor","DATE_STYLES","dateStyle","format","fallback","userName","initials","formatValue","digits","CHIP_TONES","chipTone","hash","recordFormConfigSchema","blankValue","isEmpty","toInput","fromInput","below","bound","validateField","option","validateAll","values","failures","dataOf","patchOf","ONE_COLUMN_BELOW","controlClass","okBorder","badBorder","useLoadedRecord","recordId","setState","ReadOnlyValue","RecordFormBody","onDone","onCancel","formId","useId","setEdits","committed","setCommitted","errors","setErrors","formError","setFormError","setSaved","asking","setAsking","conflict","setConflict","controls","baseOf","editable","dirty","width","columns","put","prev","check","land","versionOf","refused","takeTheirs","keepMine","cancel","remove","control","bad","described","shared","keep","numeric","actions","RecordForm","recordListConfigSchema","keys","CARDS_BELOW","isFacet","useRecordPages","sort","search","setResult","loadingMore","setLoadingMore","facets","setFacets","filter","absorb","changed","fetchPage","refresh","page","nextCursor","loadMore","Chip","Cell","moveFocus","RecordListBody","listId","setSort","setChosen","setSearch","loading","stale","cards","primary","chipField","secondary","searchable","activatable","pad","toggleSort","rowProps","keyOf","sortControl","active","RecordList","reportConfigSchema","INDEX_LENGTH","isoDay","startOf","period","daysOf","days","month","length","periodLabel","dayOf","sectionTitles","useReport","openId","setLoaded","setIndex","fetchReport","report","Chrome","stepButton","ReportBody","anchor","setAnchor","setOpenId","reportDate","latest","move","author","sections","dateLine","past","Report","menuItemSchema","shellConfigSchema","Placeholder","useCurrentUser","CHAT_WIDTH_KEY","CHAT_OPEN_KEY","THEME_KEY","CHAT_MIN","CANVAS_MIN","useTheme","theme","setTheme","kept","paths","Icon","IconButton","on","onClick","expanded","Setting","icon","ShellFrame","chat","account","settings","onSelect","isMobile","toggleTheme","setRoute","gear","setGear","gearRef","setChatOpen","wasMobile","setWasMobile","onKey","onPointer","chatWidth","setChatWidth","clampChat","w","startResize","handle","widthAt","e","up","resetResize","activeId","chatPane","canvasPane","ShellComponent","Shell","TONES","timelineKindSchema","timelineConfigSchema","kind","TOP_SLACK","MARKS","dayKeyOf","type","dayLabel","today","yesterday","timeLabel","minutes","hours","filesOf","useEntries","actors","setActors","wantsActors","loadOlder","useArrivals","atTop","arrived","setArrived","firstId","known","clear","previousFirst","cut","Attachments","setFailed","ChipRow","TimelineBody","isNew","jumpToTop","kindOf","composerBusy","setComposerBusy","composerText","setComposerText","composerKind","setComposerKind","composerError","setComposerError","addEntry","actor","Timeline","MIME","uploadConfigSchema","FileRejectedError","fileName","reason","MB","megabytes","accepts","accept","sub","wantGroup","wantSub","checkFile","position","iconFor","isImage","useQueue","onUploaded","held","queue","setQueue","nextKey","dismiss","add","picked","refusal","advance","fraction","useGallery","refs","setRefs","urls","setUrls","thumbnail","ProgressBar","percent","Queue","onDismiss","DropZone","onFiles","over","setOver","chooser","camera","take","Caption","setText","DeleteButton","onDeleted","Lightbox","onClose","Gallery","openFile","grid","asPicture","tile","UploadPanel","countHeld","noop","PickerButton","onPicked","setPicked","UploadComponent","Picker","Upload"],"mappings":"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"}