From 3286b80029ee7df23581d5ae3f3ac030f7486fa0 Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Sun, 18 Oct 2026 17:10:36 +0100 Subject: [PATCH] Build Cobble project structure from the handoff - Remove the preset document suite (letters, emails, invoices, quotes, PDF generation, line items) with its routes, API handlers and tables - Rebrand to Cobble: site data, Lato, handoff design tokens (light and dark) in main.css, brand assets in public/brand, ember stone logo and regenerated favicons - Add bots, bot_messages, bot_tasks and bot_task_steps tables, regenerate DB types (BUN_SQLITE_COBBLE_*) and add a sample crew seed (seed:bots) - Add the chat shell at /admin/bots: rail with pinned coordinator and user popover, bottom-anchored conversation with composer, working-on panel, and a send-message API scoped to the session user - Add BotStone and Wordmark components, crew status and relative time utils with tests, and update AGENTS.md Co-Authored-By: Claude Opus 5.5 --- .dockerignore | 2 +- .gitignore | 2 +- AGENTS.md | 40 +- db/schema.ts | 485 ++++---------- db/types/db.ts | 260 +++----- package.json | 3 +- public/apple-touch-icon.png | Bin 3944 -> 3732 bytes public/brand/app-icon/ember-app-1024.png | Bin 0 -> 15701 bytes public/brand/app-icon/ember-app-128.png | Bin 0 -> 1700 bytes public/brand/app-icon/ember-app-256.png | Bin 0 -> 3524 bytes public/brand/app-icon/ember-app-32.png | Bin 0 -> 532 bytes public/brand/app-icon/ember-app-64.png | Bin 0 -> 872 bytes public/brand/coordinator/coordinator-dark.svg | 9 + .../brand/coordinator/coordinator-light.svg | 9 + public/brand/icons/amber.svg | 9 + public/brand/icons/cobalt.svg | 9 + public/brand/icons/ember.svg | 9 + public/brand/icons/moss.svg | 9 + public/brand/icons/rose.svg | 9 + public/brand/icons/teal.svg | 9 + public/brand/icons/violet.svg | 9 + public/brand/wordmark/cobble-display.svg | 16 + public/brand/wordmark/cobble-endorsed.svg | 37 ++ public/brand/wordmark/cobble-horizontal.svg | 21 + public/brand/wordmark/cobble-plain.svg | 14 + public/favicon.ico | Bin 2693 -> 2072 bytes public/favicon.png | Bin 679 -> 538 bytes public/logos/logo.png | Bin 15161 -> 14697 bytes public/logos/logo.svg | 11 +- src/components/general/bot-stone-face.tsx | 60 ++ src/components/general/bot-stone.tsx | 63 ++ .../general/copy-from-existing-select.tsx | 131 ---- src/components/general/logo-icon.tsx | 35 +- src/components/general/logo.tsx | 44 +- src/components/general/pdf-preview.tsx | 107 ---- .../general/sortable-line-item-row.tsx | 185 ------ .../general/sortable-line-items-editor.tsx | 155 ----- src/components/general/stone-eye-arc.tsx | 25 + src/components/general/stone-eye-dot.tsx | 11 + src/components/general/stone-eye-flat.tsx | 22 + src/components/general/stone-eye-lidded.tsx | 21 + src/components/general/wordmark.tsx | 43 ++ src/data/site-data.ts | 6 +- src/data/stone-geometry.ts | 34 + src/data/wordmark-paths.ts | 15 + src/dict/bot-dict.ts | 120 ++++ src/dict/bot-hue-classes-dict.ts | 24 + src/dict/document-dict.ts | 79 --- .../backend/auth/check-user-access.test.ts | 4 +- .../backend/auth/check-user-access.ts | 4 +- .../backend/auth/create-signup-user.ts | 8 +- .../backend/auth/delete-user-sso-codes.ts | 4 +- .../backend/auth/find-user-by-identifier.ts | 4 +- .../backend/auth/generate-unique-username.ts | 4 +- .../backend/auth/grab-user-auth-types.ts | 4 +- .../backend/auth/grab-user-password-hash.ts | 4 +- .../backend/auth/issue-sso-login-code.tsx | 4 +- src/functions/backend/auth/login-user.ts | 8 +- .../backend/auth/set-user-password.ts | 4 +- src/functions/backend/auth/verify-sso-code.ts | 4 +- .../backend/db/bots/grab-active-bot-task.ts | 35 + .../backend/db/bots/grab-bot-messages.ts | 31 + src/functions/backend/db/bots/grab-bots.ts | 39 ++ .../backend/db/bots/grab-user-bot.ts | 22 + .../backend/db/bots/insert-user-message.ts | 50 ++ .../backend/db/bots/mark-bot-read.ts | 16 + .../backend/db/check-allowed-joins.ts | 8 +- .../db/documents/grab-email-attachments.ts | 41 -- .../backend/db/documents/grab-email.ts | 14 - .../backend/db/documents/grab-letter.ts | 14 - .../grab-next-document-sequence.test.ts | 102 --- .../documents/grab-next-document-sequence.ts | 41 -- .../backend/db/invoices/grab-invoice.ts | 64 -- .../backend/db/media/append-users-to-media.ts | 6 +- .../backend/db/media/delete-media.ts | 8 +- .../db/media/grab-media-from-paraidigm.ts | 8 +- src/functions/backend/db/media/grab-media.ts | 10 +- src/functions/backend/db/quotes/grab-quote.ts | 38 -- .../backend/db/users/grab-user-types.ts | 6 +- src/functions/backend/db/users/grab-users.ts | 6 +- .../db/users/media/upload-and-record-media.ts | 26 +- .../backend/email/document-emails.tsx | 351 ---------- src/functions/backend/media/grab-media-url.ts | 8 +- src/functions/backend/pdf/html-blocks.test.ts | 93 --- src/functions/backend/pdf/html-blocks.ts | 262 -------- src/functions/backend/pdf/invoice-pdf.ts | 606 ------------------ src/functions/backend/pdf/letter-date.ts | 38 -- src/functions/backend/pdf/letter-pdf.ts | 233 ------- src/functions/backend/pdf/pdf-helpers.ts | 568 ---------------- src/functions/backend/pdf/render-blocks.ts | 257 -------- src/functions/frontend/admin-crud-handler.ts | 4 +- src/functions/frontend/admin-fetch-handler.ts | 4 +- .../frontend/client-upload-media-handler.ts | 4 +- src/functions/frontend/document-actions.ts | 121 ---- .../server/media/handle-media-server.ts | 8 +- src/hooks/use-admin-crud-get.ts | 6 +- src/hooks/use-fetch-get.ts | 6 +- src/hooks/use-form-init.ts | 6 +- src/hooks/use-local-user.ts | 6 +- src/hooks/use-users-get.ts | 8 +- .../admin/(dict)/admin-aside-groups-dict.tsx | 15 +- .../bots/(dict)/rail-user-menu-dict.ts | 34 + src/layouts/bots/(partials)/bot-row.tsx | 61 ++ .../bots/(partials)/coordinator-row.tsx | 27 + .../bots/(partials)/new-bot-button.tsx | 21 + src/layouts/bots/(partials)/rail-header.tsx | 20 + .../bots/(partials)/rail-user-menu-entry.tsx | 36 ++ .../bots/(partials)/rail-user-menu.tsx | 27 + src/layouts/bots/(partials)/rail-user-row.tsx | 61 ++ src/layouts/bots/(partials)/unread-dot.tsx | 19 + src/layouts/bots/(sections)/bots-rail.tsx | 46 ++ src/layouts/bots/index.tsx | 23 + src/pages/__root.tsx | 11 +- src/pages/admin/(data)/dashboard-shortcuts.ts | 35 +- .../admin/bots/(sections)/empty-crew.tsx | 18 + .../[bot_id]/(functions)/send-bot-message.ts | 17 + .../[bot_id]/(partials)/bot-message-group.tsx | 39 ++ .../[bot_id]/(partials)/bot-message-line.tsx | 19 + .../(partials)/composer-attach-button.tsx | 19 + .../(partials)/composer-send-button.tsx | 26 + .../bots/[bot_id]/(partials)/composer.tsx | 75 +++ .../(partials)/conversation-header.tsx | 41 ++ .../[bot_id]/(partials)/group-timestamp.tsx | 10 + .../(partials)/message-group-block.tsx | 17 + .../[bot_id]/(partials)/message-thread.tsx | 33 + .../[bot_id]/(partials)/needs-you-chip.tsx | 12 + .../(partials)/open-computer-button.tsx | 19 + .../[bot_id]/(partials)/task-step-mark.tsx | 28 + .../[bot_id]/(partials)/task-step-row.tsx | 34 + .../bots/[bot_id]/(partials)/user-bubble.tsx | 12 + .../(partials)/user-message-group.tsx | 18 + .../bots/[bot_id]/(sections)/conversation.tsx | 34 + .../[bot_id]/(sections)/working-on-panel.tsx | 49 ++ .../[bot_id]/(utils)/group-messages.test.ts | 35 + .../bots/[bot_id]/(utils)/group-messages.ts | 50 ++ src/pages/admin/bots/[bot_id]/index.server.ts | 50 ++ src/pages/admin/bots/[bot_id]/index.tsx | 23 + src/pages/admin/bots/index.server.ts | 37 ++ src/pages/admin/bots/index.tsx | 12 + .../emails/(functions)/client-send-email.ts | 48 -- .../emails/(hooks)/use-emails-list-init.tsx | 36 -- .../admin/emails/(partials)/email-actions.tsx | 102 --- .../admin/emails/(partials)/email-card.tsx | 56 -- .../admin/emails/(partials)/emails-list.tsx | 42 -- .../emails/(sections)/emails-list-section.tsx | 13 - src/pages/admin/emails/(sections)/hero.tsx | 36 -- .../[id]/(partials)/email-attachment-row.tsx | 76 --- .../emails/[id]/(partials)/email-details.tsx | 90 --- .../[id]/(sections)/email-view-section.tsx | 36 -- .../admin/emails/[id]/(sections)/hero.tsx | 98 --- .../(sections)/edit-email-form-section.tsx | 18 - .../emails/[id]/edit/(sections)/hero.tsx | 21 - .../admin/emails/[id]/edit/index.server.ts | 41 -- src/pages/admin/emails/[id]/edit/index.tsx | 24 - src/pages/admin/emails/[id]/index.server.ts | 38 -- src/pages/admin/emails/[id]/index.tsx | 24 - .../emails/create/(functions)/submit-email.ts | 106 --- .../email-form/email-form-action.tsx | 22 - .../email-form/email-form-attachment-row.tsx | 78 --- .../email-form/email-form-attachments.tsx | 292 --------- .../(partials)/email-form/email-form-body.tsx | 30 - .../email-form/email-form-details.tsx | 79 --- .../create/(partials)/email-form/index.tsx | 171 ----- .../create/(sections)/email-form-section.tsx | 10 - .../admin/emails/create/(sections)/hero.tsx | 22 - .../create/(utils)/email-attachment-draft.ts | 9 - .../emails/create/(utils)/file-to-data-url.ts | 37 -- src/pages/admin/emails/create/index.server.ts | 17 - src/pages/admin/emails/create/index.tsx | 20 - src/pages/admin/emails/index.tsx | 20 - .../(data)/email-template-content.tsx | 186 ------ .../(partials)/email-template-detail-row.tsx | 20 - .../(partials)/email-template-preview.tsx | 21 - .../(sections)/email-template-section.tsx | 34 - .../admin/emails/template/(sections)/hero.tsx | 10 - .../admin/emails/template/index.server.tsx | 33 - src/pages/admin/emails/template/index.tsx | 20 - .../invoices/(data)/invoice-form-types.ts | 34 - .../(hooks)/use-invoices-list-init.tsx | 34 - .../invoices/(partials)/invoice-actions.tsx | 118 ---- .../invoices/(partials)/invoice-card.tsx | 98 --- .../invoices/(partials)/invoices-list.tsx | 36 -- .../(partials)/send-invoice-email-modal.tsx | 103 --- src/pages/admin/invoices/(sections)/hero.tsx | 26 - .../(sections)/invoices-list-section.tsx | 13 - .../[id]/(partials)/invoice-item-row.tsx | 29 - .../admin/invoices/[id]/(sections)/hero.tsx | 38 -- .../[id]/(sections)/invoice-view-section.tsx | 153 ----- .../(sections)/edit-invoice-form-section.tsx | 49 -- .../invoices/[id]/edit/(sections)/hero.tsx | 21 - .../admin/invoices/[id]/edit/index.server.ts | 50 -- src/pages/admin/invoices/[id]/edit/index.tsx | 24 - src/pages/admin/invoices/[id]/index.server.ts | 36 -- src/pages/admin/invoices/[id]/index.tsx | 24 - .../create/(functions)/submit-invoice-form.ts | 164 ----- .../create/(partials)/invoice-form/index.tsx | 68 -- .../invoice-form/invoice-form-action.tsx | 23 - .../invoice-form/invoice-form-client.tsx | 64 -- .../invoice-form/invoice-form-items.tsx | 34 - .../invoice-form/invoice-form-quote-link.tsx | 134 ---- .../invoice-form-short-description.tsx | 23 - .../invoice-form-status-notes.tsx | 94 --- .../invoice-form/invoice-form-title.tsx | 25 - .../admin/invoices/create/(sections)/hero.tsx | 23 - .../(sections)/invoice-form-section.tsx | 10 - .../admin/invoices/create/index.server.ts | 17 - src/pages/admin/invoices/create/index.tsx | 27 - src/pages/admin/invoices/index.tsx | 20 - .../letters/(hooks)/use-letters-list-init.tsx | 34 - .../(partials)/download-letter-modal.tsx | 120 ---- .../letters/(partials)/letter-actions.tsx | 89 --- .../admin/letters/(partials)/letter-card.tsx | 44 -- .../admin/letters/(partials)/letters-list.tsx | 34 - src/pages/admin/letters/(sections)/hero.tsx | 29 - .../(sections)/letters-list-section.tsx | 13 - .../[id]/(partials)/letter-details.tsx | 94 --- .../admin/letters/[id]/(sections)/hero.tsx | 38 -- .../[id]/(sections)/letter-view-section.tsx | 38 -- .../(sections)/edit-letter-form-section.tsx | 18 - .../letters/[id]/edit/(sections)/hero.tsx | 17 - .../admin/letters/[id]/edit/index.server.ts | 41 -- src/pages/admin/letters/[id]/edit/index.tsx | 24 - src/pages/admin/letters/[id]/index.server.ts | 31 - src/pages/admin/letters/[id]/index.tsx | 24 - .../create/(functions)/submit-letter.ts | 74 --- .../create/(partials)/letter-form/index.tsx | 110 ---- .../letter-form/letter-form-action.tsx | 22 - .../letter-form/letter-form-body.tsx | 30 - .../letter-form/letter-form-letter-parts.tsx | 86 --- .../letter-form/letter-form-recipient.tsx | 45 -- .../letter-form/letter-form-sender.tsx | 45 -- .../letter-form/letter-form-title.tsx | 24 - .../admin/letters/create/(sections)/hero.tsx | 18 - .../create/(sections)/letter-form-section.tsx | 10 - .../admin/letters/create/index.server.ts | 17 - src/pages/admin/letters/create/index.tsx | 20 - src/pages/admin/letters/index.tsx | 20 - .../admin/logs/(hooks)/use-logs-list-init.tsx | 8 +- src/pages/admin/logs/(partials)/log-card.tsx | 4 +- .../logs/(partials)/log-details-modal.tsx | 4 +- .../admin/logs/(partials)/log-table-row.tsx | 6 +- .../logs/(utils)/get-log-category-style.ts | 4 +- .../admin/logs/(utils)/get-log-user-name.ts | 4 +- .../admin/logs/(utils)/get-logs-query.ts | 4 +- .../(hooks)/use-all-people-list-init.tsx | 4 +- .../(partials)/all-people-list-search.tsx | 4 +- .../(partials)/all-people-list-selections.tsx | 4 +- .../people/(partials)/people-list-card.tsx | 4 +- .../(sections)/edit-person-form-section.tsx | 6 +- .../create/(functions)/submit-user-form.ts | 8 +- .../create/(partials)/user-form/index.tsx | 4 +- .../(partials)/user-form/user-form-action.tsx | 4 +- .../(partials)/user-form/user-form-bio.tsx | 4 +- .../(partials)/user-form/user-form-email.tsx | 4 +- .../(partials)/user-form/user-form-image.tsx | 4 +- .../(partials)/user-form/user-form-name.tsx | 4 +- .../user-form/user-form-username.tsx | 4 +- .../admin/quotes/(data)/quote-form-types.ts | 28 - .../quotes/(hooks)/use-quotes-list-init.tsx | 34 - .../admin/quotes/(partials)/quote-actions.tsx | 118 ---- .../admin/quotes/(partials)/quote-card.tsx | 99 --- .../admin/quotes/(partials)/quotes-list.tsx | 36 -- .../(partials)/send-quote-email-modal.tsx | 103 --- src/pages/admin/quotes/(sections)/hero.tsx | 26 - .../quotes/(sections)/quotes-list-section.tsx | 13 - .../quotes/[id]/(partials)/quote-item-row.tsx | 29 - .../admin/quotes/[id]/(sections)/hero.tsx | 38 -- .../[id]/(sections)/quote-view-section.tsx | 158 ----- .../(sections)/edit-quote-form-section.tsx | 34 - .../quotes/[id]/edit/(sections)/hero.tsx | 21 - .../admin/quotes/[id]/edit/index.server.ts | 46 -- src/pages/admin/quotes/[id]/edit/index.tsx | 24 - src/pages/admin/quotes/[id]/index.server.ts | 36 -- src/pages/admin/quotes/[id]/index.tsx | 24 - .../create/(functions)/submit-quote-form.ts | 127 ---- .../create/(partials)/quote-form/index.tsx | 62 -- .../quote-form/quote-form-action.tsx | 23 - .../quote-form/quote-form-client.tsx | 64 -- .../quote-form/quote-form-items.tsx | 34 - .../quote-form-short-description.tsx | 23 - .../quote-form/quote-form-status-notes.tsx | 95 --- .../quote-form/quote-form-title.tsx | 25 - .../admin/quotes/create/(sections)/hero.tsx | 23 - .../create/(sections)/quote-form-section.tsx | 10 - src/pages/admin/quotes/create/index.server.ts | 17 - src/pages/admin/quotes/create/index.tsx | 20 - src/pages/admin/quotes/index.tsx | 20 - .../(functions)/submit-account-form.ts | 4 +- .../(partials)/account-form-action.tsx | 4 +- .../settings/(partials)/account-form.tsx | 4 +- .../(sections)/account-form-section.tsx | 6 +- src/pages/api/admin/bots/send-message.ts | 63 ++ .../crud/(tables)/email_attachments/index.ts | 29 - .../api/admin/crud/(tables)/emails/index.ts | 29 - .../crud/(tables)/invoice_items/index.ts | 29 - .../crud/(tables)/invoice_quotes/index.ts | 29 - .../api/admin/crud/(tables)/invoices/index.ts | 107 ---- .../api/admin/crud/(tables)/letters/index.ts | 29 - .../api/admin/crud/(tables)/media/del.ts | 12 +- .../api/admin/crud/(tables)/media/post.ts | 4 +- .../admin/crud/(tables)/quote_items/index.ts | 29 - .../api/admin/crud/(tables)/quotes/index.ts | 107 ---- .../api/admin/crud/(tables)/user_types/del.ts | 4 +- .../api/admin/crud/(tables)/users/get.ts | 6 +- src/pages/api/admin/crud/[[...paths]].ts | 24 - src/pages/api/admin/emails/send-email.tsx | 244 ------- .../api/admin/emails/sync-attachments.ts | 134 ---- src/pages/api/admin/invoices/generate-pdf.ts | 76 --- src/pages/api/admin/invoices/send-email.tsx | 111 ---- src/pages/api/admin/letters/generate-pdf.ts | 73 --- src/pages/api/admin/quotes/generate-pdf.ts | 80 --- src/pages/api/admin/quotes/send-email.tsx | 115 ---- src/pages/api/auth/resend-login-code.ts | 4 +- src/pages/api/auth/signup.tsx | 6 +- src/schemas/bot-schema.ts | 12 + src/scripts/seed-bots.ts | 329 ++++++++++ src/scripts/seed-super-admin.tsx | 12 +- src/styles/main.css | 151 +++-- src/types/index.ts | 88 ++- src/types/sql-joins.ts | 20 +- src/utils/crew-status.test.ts | 63 ++ src/utils/crew-status.ts | 78 +++ src/utils/grab-dir-names.ts | 4 +- src/utils/hex-to-rgb-fractions.test.ts | 28 - src/utils/hex-to-rgb-fractions.ts | 24 - src/utils/rbac/can-create-user.ts | 4 +- src/utils/rbac/can-list-users.ts | 4 +- src/utils/rbac/can-manage-user-types.ts | 4 +- src/utils/rbac/can-revoke-user.ts | 6 +- src/utils/rbac/can-update-user.ts | 6 +- .../rbac/filter-assignable-user-types.ts | 4 +- src/utils/rbac/is-pass-through-user.ts | 4 +- src/utils/rbac/is-read-only-user.ts | 4 +- src/utils/relative-time.test.ts | 33 + src/utils/relative-time.ts | 30 + src/utils/searchable-field-to-query-field.ts | 4 +- src/utils/text-to-html.test.ts | 20 - src/utils/text-to-html.ts | 15 - .../(functions)/check-session-access.ts | 4 +- src/websocket/types.ts | 4 +- 340 files changed, 3161 insertions(+), 12060 deletions(-) create mode 100644 public/brand/app-icon/ember-app-1024.png create mode 100644 public/brand/app-icon/ember-app-128.png create mode 100644 public/brand/app-icon/ember-app-256.png create mode 100644 public/brand/app-icon/ember-app-32.png create mode 100644 public/brand/app-icon/ember-app-64.png create mode 100644 public/brand/coordinator/coordinator-dark.svg create mode 100644 public/brand/coordinator/coordinator-light.svg create mode 100644 public/brand/icons/amber.svg create mode 100644 public/brand/icons/cobalt.svg create mode 100644 public/brand/icons/ember.svg create mode 100644 public/brand/icons/moss.svg create mode 100644 public/brand/icons/rose.svg create mode 100644 public/brand/icons/teal.svg create mode 100644 public/brand/icons/violet.svg create mode 100644 public/brand/wordmark/cobble-display.svg create mode 100644 public/brand/wordmark/cobble-endorsed.svg create mode 100644 public/brand/wordmark/cobble-horizontal.svg create mode 100644 public/brand/wordmark/cobble-plain.svg create mode 100644 src/components/general/bot-stone-face.tsx create mode 100644 src/components/general/bot-stone.tsx delete mode 100644 src/components/general/copy-from-existing-select.tsx delete mode 100644 src/components/general/pdf-preview.tsx delete mode 100644 src/components/general/sortable-line-item-row.tsx delete mode 100644 src/components/general/sortable-line-items-editor.tsx create mode 100644 src/components/general/stone-eye-arc.tsx create mode 100644 src/components/general/stone-eye-dot.tsx create mode 100644 src/components/general/stone-eye-flat.tsx create mode 100644 src/components/general/stone-eye-lidded.tsx create mode 100644 src/components/general/wordmark.tsx create mode 100644 src/data/stone-geometry.ts create mode 100644 src/data/wordmark-paths.ts create mode 100644 src/dict/bot-dict.ts create mode 100644 src/dict/bot-hue-classes-dict.ts delete mode 100644 src/dict/document-dict.ts create mode 100644 src/functions/backend/db/bots/grab-active-bot-task.ts create mode 100644 src/functions/backend/db/bots/grab-bot-messages.ts create mode 100644 src/functions/backend/db/bots/grab-bots.ts create mode 100644 src/functions/backend/db/bots/grab-user-bot.ts create mode 100644 src/functions/backend/db/bots/insert-user-message.ts create mode 100644 src/functions/backend/db/bots/mark-bot-read.ts delete mode 100644 src/functions/backend/db/documents/grab-email-attachments.ts delete mode 100644 src/functions/backend/db/documents/grab-email.ts delete mode 100644 src/functions/backend/db/documents/grab-letter.ts delete mode 100644 src/functions/backend/db/documents/grab-next-document-sequence.test.ts delete mode 100644 src/functions/backend/db/documents/grab-next-document-sequence.ts delete mode 100644 src/functions/backend/db/invoices/grab-invoice.ts delete mode 100644 src/functions/backend/db/quotes/grab-quote.ts delete mode 100644 src/functions/backend/email/document-emails.tsx delete mode 100644 src/functions/backend/pdf/html-blocks.test.ts delete mode 100644 src/functions/backend/pdf/html-blocks.ts delete mode 100644 src/functions/backend/pdf/invoice-pdf.ts delete mode 100644 src/functions/backend/pdf/letter-date.ts delete mode 100644 src/functions/backend/pdf/letter-pdf.ts delete mode 100644 src/functions/backend/pdf/pdf-helpers.ts delete mode 100644 src/functions/backend/pdf/render-blocks.ts delete mode 100644 src/functions/frontend/document-actions.ts create mode 100644 src/layouts/bots/(dict)/rail-user-menu-dict.ts create mode 100644 src/layouts/bots/(partials)/bot-row.tsx create mode 100644 src/layouts/bots/(partials)/coordinator-row.tsx create mode 100644 src/layouts/bots/(partials)/new-bot-button.tsx create mode 100644 src/layouts/bots/(partials)/rail-header.tsx create mode 100644 src/layouts/bots/(partials)/rail-user-menu-entry.tsx create mode 100644 src/layouts/bots/(partials)/rail-user-menu.tsx create mode 100644 src/layouts/bots/(partials)/rail-user-row.tsx create mode 100644 src/layouts/bots/(partials)/unread-dot.tsx create mode 100644 src/layouts/bots/(sections)/bots-rail.tsx create mode 100644 src/layouts/bots/index.tsx create mode 100644 src/pages/admin/bots/(sections)/empty-crew.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(functions)/send-bot-message.ts create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/bot-message-group.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/bot-message-line.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/composer-attach-button.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/composer-send-button.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/composer.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/conversation-header.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/group-timestamp.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/message-group-block.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/message-thread.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/needs-you-chip.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/open-computer-button.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/task-step-mark.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/task-step-row.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/user-bubble.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/user-message-group.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(sections)/conversation.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(sections)/working-on-panel.tsx create mode 100644 src/pages/admin/bots/[bot_id]/(utils)/group-messages.test.ts create mode 100644 src/pages/admin/bots/[bot_id]/(utils)/group-messages.ts create mode 100644 src/pages/admin/bots/[bot_id]/index.server.ts create mode 100644 src/pages/admin/bots/[bot_id]/index.tsx create mode 100644 src/pages/admin/bots/index.server.ts create mode 100644 src/pages/admin/bots/index.tsx delete mode 100644 src/pages/admin/emails/(functions)/client-send-email.ts delete mode 100644 src/pages/admin/emails/(hooks)/use-emails-list-init.tsx delete mode 100644 src/pages/admin/emails/(partials)/email-actions.tsx delete mode 100644 src/pages/admin/emails/(partials)/email-card.tsx delete mode 100644 src/pages/admin/emails/(partials)/emails-list.tsx delete mode 100644 src/pages/admin/emails/(sections)/emails-list-section.tsx delete mode 100644 src/pages/admin/emails/(sections)/hero.tsx delete mode 100644 src/pages/admin/emails/[id]/(partials)/email-attachment-row.tsx delete mode 100644 src/pages/admin/emails/[id]/(partials)/email-details.tsx delete mode 100644 src/pages/admin/emails/[id]/(sections)/email-view-section.tsx delete mode 100644 src/pages/admin/emails/[id]/(sections)/hero.tsx delete mode 100644 src/pages/admin/emails/[id]/edit/(sections)/edit-email-form-section.tsx delete mode 100644 src/pages/admin/emails/[id]/edit/(sections)/hero.tsx delete mode 100644 src/pages/admin/emails/[id]/edit/index.server.ts delete mode 100644 src/pages/admin/emails/[id]/edit/index.tsx delete mode 100644 src/pages/admin/emails/[id]/index.server.ts delete mode 100644 src/pages/admin/emails/[id]/index.tsx delete mode 100644 src/pages/admin/emails/create/(functions)/submit-email.ts delete mode 100644 src/pages/admin/emails/create/(partials)/email-form/email-form-action.tsx delete mode 100644 src/pages/admin/emails/create/(partials)/email-form/email-form-attachment-row.tsx delete mode 100644 src/pages/admin/emails/create/(partials)/email-form/email-form-attachments.tsx delete mode 100644 src/pages/admin/emails/create/(partials)/email-form/email-form-body.tsx delete mode 100644 src/pages/admin/emails/create/(partials)/email-form/email-form-details.tsx delete mode 100644 src/pages/admin/emails/create/(partials)/email-form/index.tsx delete mode 100644 src/pages/admin/emails/create/(sections)/email-form-section.tsx delete mode 100644 src/pages/admin/emails/create/(sections)/hero.tsx delete mode 100644 src/pages/admin/emails/create/(utils)/email-attachment-draft.ts delete mode 100644 src/pages/admin/emails/create/(utils)/file-to-data-url.ts delete mode 100644 src/pages/admin/emails/create/index.server.ts delete mode 100644 src/pages/admin/emails/create/index.tsx delete mode 100644 src/pages/admin/emails/index.tsx delete mode 100644 src/pages/admin/emails/template/(data)/email-template-content.tsx delete mode 100644 src/pages/admin/emails/template/(partials)/email-template-detail-row.tsx delete mode 100644 src/pages/admin/emails/template/(partials)/email-template-preview.tsx delete mode 100644 src/pages/admin/emails/template/(sections)/email-template-section.tsx delete mode 100644 src/pages/admin/emails/template/(sections)/hero.tsx delete mode 100644 src/pages/admin/emails/template/index.server.tsx delete mode 100644 src/pages/admin/emails/template/index.tsx delete mode 100644 src/pages/admin/invoices/(data)/invoice-form-types.ts delete mode 100644 src/pages/admin/invoices/(hooks)/use-invoices-list-init.tsx delete mode 100644 src/pages/admin/invoices/(partials)/invoice-actions.tsx delete mode 100644 src/pages/admin/invoices/(partials)/invoice-card.tsx delete mode 100644 src/pages/admin/invoices/(partials)/invoices-list.tsx delete mode 100644 src/pages/admin/invoices/(partials)/send-invoice-email-modal.tsx delete mode 100644 src/pages/admin/invoices/(sections)/hero.tsx delete mode 100644 src/pages/admin/invoices/(sections)/invoices-list-section.tsx delete mode 100644 src/pages/admin/invoices/[id]/(partials)/invoice-item-row.tsx delete mode 100644 src/pages/admin/invoices/[id]/(sections)/hero.tsx delete mode 100644 src/pages/admin/invoices/[id]/(sections)/invoice-view-section.tsx delete mode 100644 src/pages/admin/invoices/[id]/edit/(sections)/edit-invoice-form-section.tsx delete mode 100644 src/pages/admin/invoices/[id]/edit/(sections)/hero.tsx delete mode 100644 src/pages/admin/invoices/[id]/edit/index.server.ts delete mode 100644 src/pages/admin/invoices/[id]/edit/index.tsx delete mode 100644 src/pages/admin/invoices/[id]/index.server.ts delete mode 100644 src/pages/admin/invoices/[id]/index.tsx delete mode 100644 src/pages/admin/invoices/create/(functions)/submit-invoice-form.ts delete mode 100644 src/pages/admin/invoices/create/(partials)/invoice-form/index.tsx delete mode 100644 src/pages/admin/invoices/create/(partials)/invoice-form/invoice-form-action.tsx delete mode 100644 src/pages/admin/invoices/create/(partials)/invoice-form/invoice-form-client.tsx delete mode 100644 src/pages/admin/invoices/create/(partials)/invoice-form/invoice-form-items.tsx delete mode 100644 src/pages/admin/invoices/create/(partials)/invoice-form/invoice-form-quote-link.tsx delete mode 100644 src/pages/admin/invoices/create/(partials)/invoice-form/invoice-form-short-description.tsx delete mode 100644 src/pages/admin/invoices/create/(partials)/invoice-form/invoice-form-status-notes.tsx delete mode 100644 src/pages/admin/invoices/create/(partials)/invoice-form/invoice-form-title.tsx delete mode 100644 src/pages/admin/invoices/create/(sections)/hero.tsx delete mode 100644 src/pages/admin/invoices/create/(sections)/invoice-form-section.tsx delete mode 100644 src/pages/admin/invoices/create/index.server.ts delete mode 100644 src/pages/admin/invoices/create/index.tsx delete mode 100644 src/pages/admin/invoices/index.tsx delete mode 100644 src/pages/admin/letters/(hooks)/use-letters-list-init.tsx delete mode 100644 src/pages/admin/letters/(partials)/download-letter-modal.tsx delete mode 100644 src/pages/admin/letters/(partials)/letter-actions.tsx delete mode 100644 src/pages/admin/letters/(partials)/letter-card.tsx delete mode 100644 src/pages/admin/letters/(partials)/letters-list.tsx delete mode 100644 src/pages/admin/letters/(sections)/hero.tsx delete mode 100644 src/pages/admin/letters/(sections)/letters-list-section.tsx delete mode 100644 src/pages/admin/letters/[id]/(partials)/letter-details.tsx delete mode 100644 src/pages/admin/letters/[id]/(sections)/hero.tsx delete mode 100644 src/pages/admin/letters/[id]/(sections)/letter-view-section.tsx delete mode 100644 src/pages/admin/letters/[id]/edit/(sections)/edit-letter-form-section.tsx delete mode 100644 src/pages/admin/letters/[id]/edit/(sections)/hero.tsx delete mode 100644 src/pages/admin/letters/[id]/edit/index.server.ts delete mode 100644 src/pages/admin/letters/[id]/edit/index.tsx delete mode 100644 src/pages/admin/letters/[id]/index.server.ts delete mode 100644 src/pages/admin/letters/[id]/index.tsx delete mode 100644 src/pages/admin/letters/create/(functions)/submit-letter.ts delete mode 100644 src/pages/admin/letters/create/(partials)/letter-form/index.tsx delete mode 100644 src/pages/admin/letters/create/(partials)/letter-form/letter-form-action.tsx delete mode 100644 src/pages/admin/letters/create/(partials)/letter-form/letter-form-body.tsx delete mode 100644 src/pages/admin/letters/create/(partials)/letter-form/letter-form-letter-parts.tsx delete mode 100644 src/pages/admin/letters/create/(partials)/letter-form/letter-form-recipient.tsx delete mode 100644 src/pages/admin/letters/create/(partials)/letter-form/letter-form-sender.tsx delete mode 100644 src/pages/admin/letters/create/(partials)/letter-form/letter-form-title.tsx delete mode 100644 src/pages/admin/letters/create/(sections)/hero.tsx delete mode 100644 src/pages/admin/letters/create/(sections)/letter-form-section.tsx delete mode 100644 src/pages/admin/letters/create/index.server.ts delete mode 100644 src/pages/admin/letters/create/index.tsx delete mode 100644 src/pages/admin/letters/index.tsx delete mode 100644 src/pages/admin/quotes/(data)/quote-form-types.ts delete mode 100644 src/pages/admin/quotes/(hooks)/use-quotes-list-init.tsx delete mode 100644 src/pages/admin/quotes/(partials)/quote-actions.tsx delete mode 100644 src/pages/admin/quotes/(partials)/quote-card.tsx delete mode 100644 src/pages/admin/quotes/(partials)/quotes-list.tsx delete mode 100644 src/pages/admin/quotes/(partials)/send-quote-email-modal.tsx delete mode 100644 src/pages/admin/quotes/(sections)/hero.tsx delete mode 100644 src/pages/admin/quotes/(sections)/quotes-list-section.tsx delete mode 100644 src/pages/admin/quotes/[id]/(partials)/quote-item-row.tsx delete mode 100644 src/pages/admin/quotes/[id]/(sections)/hero.tsx delete mode 100644 src/pages/admin/quotes/[id]/(sections)/quote-view-section.tsx delete mode 100644 src/pages/admin/quotes/[id]/edit/(sections)/edit-quote-form-section.tsx delete mode 100644 src/pages/admin/quotes/[id]/edit/(sections)/hero.tsx delete mode 100644 src/pages/admin/quotes/[id]/edit/index.server.ts delete mode 100644 src/pages/admin/quotes/[id]/edit/index.tsx delete mode 100644 src/pages/admin/quotes/[id]/index.server.ts delete mode 100644 src/pages/admin/quotes/[id]/index.tsx delete mode 100644 src/pages/admin/quotes/create/(functions)/submit-quote-form.ts delete mode 100644 src/pages/admin/quotes/create/(partials)/quote-form/index.tsx delete mode 100644 src/pages/admin/quotes/create/(partials)/quote-form/quote-form-action.tsx delete mode 100644 src/pages/admin/quotes/create/(partials)/quote-form/quote-form-client.tsx delete mode 100644 src/pages/admin/quotes/create/(partials)/quote-form/quote-form-items.tsx delete mode 100644 src/pages/admin/quotes/create/(partials)/quote-form/quote-form-short-description.tsx delete mode 100644 src/pages/admin/quotes/create/(partials)/quote-form/quote-form-status-notes.tsx delete mode 100644 src/pages/admin/quotes/create/(partials)/quote-form/quote-form-title.tsx delete mode 100644 src/pages/admin/quotes/create/(sections)/hero.tsx delete mode 100644 src/pages/admin/quotes/create/(sections)/quote-form-section.tsx delete mode 100644 src/pages/admin/quotes/create/index.server.ts delete mode 100644 src/pages/admin/quotes/create/index.tsx delete mode 100644 src/pages/admin/quotes/index.tsx create mode 100644 src/pages/api/admin/bots/send-message.ts delete mode 100644 src/pages/api/admin/crud/(tables)/email_attachments/index.ts delete mode 100644 src/pages/api/admin/crud/(tables)/emails/index.ts delete mode 100644 src/pages/api/admin/crud/(tables)/invoice_items/index.ts delete mode 100644 src/pages/api/admin/crud/(tables)/invoice_quotes/index.ts delete mode 100644 src/pages/api/admin/crud/(tables)/invoices/index.ts delete mode 100644 src/pages/api/admin/crud/(tables)/letters/index.ts delete mode 100644 src/pages/api/admin/crud/(tables)/quote_items/index.ts delete mode 100644 src/pages/api/admin/crud/(tables)/quotes/index.ts delete mode 100644 src/pages/api/admin/emails/send-email.tsx delete mode 100644 src/pages/api/admin/emails/sync-attachments.ts delete mode 100644 src/pages/api/admin/invoices/generate-pdf.ts delete mode 100644 src/pages/api/admin/invoices/send-email.tsx delete mode 100644 src/pages/api/admin/letters/generate-pdf.ts delete mode 100644 src/pages/api/admin/quotes/generate-pdf.ts delete mode 100644 src/pages/api/admin/quotes/send-email.tsx create mode 100644 src/schemas/bot-schema.ts create mode 100644 src/scripts/seed-bots.ts create mode 100644 src/utils/crew-status.test.ts create mode 100644 src/utils/crew-status.ts delete mode 100644 src/utils/hex-to-rgb-fractions.test.ts delete mode 100644 src/utils/hex-to-rgb-fractions.ts create mode 100644 src/utils/relative-time.test.ts create mode 100644 src/utils/relative-time.ts delete mode 100644 src/utils/text-to-html.test.ts delete mode 100644 src/utils/text-to-html.ts diff --git a/.dockerignore b/.dockerignore index 9e6e4cb..ecf365f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,7 +9,7 @@ node_modules .data .bunext .bun-sqlite -db/bsqlite +db/cobble db/.backups db/.exports coverage diff --git a/.gitignore b/.gitignore index f93edd7..860307a 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,7 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json /.bunext # bun-sqlite local database + artifacts -/db/bsqlite +/db/cobble /db/.backups /db/.exports .bun-sqlite diff --git a/AGENTS.md b/AGENTS.md index cde5981..043fee0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,7 @@ # Agent Instructions +This is **Cobble** (`cobble.moduletrace.org`): an open-source crew of small AI worker bots. Product and design source of truth is the handoff in `assets/handoff/` (gitignored locally): `PRODUCT.md`, `IMPLEMENTATION.md`, `tokens/`. Unit noun is **bot**, never "agent" or "expert". + ## Stack - **Runtime:** Bun - not Node. Use `bun` commands, never `npm`/`npx`. @@ -8,7 +10,7 @@ - **UI:** Tailwind CSS v4 + the `@moduletrace/twui` component library (installed package) + shared components in `src/components/general/`. There is **no** shadcn/ui, no `tailwind.config`, and no `components.json` - twui is the only component system. CSS chain: `src/pages/__root.tsx` imports `src/styles/main.css`, which imports `@moduletrace/twui/base.css` (`@import "tailwindcss"`). Theme tokens (colors/radius/fonts) are overridden via the `@theme inline` block in `src/styles/main.css`. - **Icons:** `lucide-react`. - **Validation:** `zod` v4 (`zod@4.1.12`). -- **Branding:** Site name/slug live only in `src/data/site-data.ts`. Logo source of truth is `public/logos/logo.svg` (rendered inline by `src/components/general/logo-icon.tsx`); browser favicon/assets are generated from it. +- **Branding:** Site name/slug live only in `src/data/site-data.ts`. Logo source of truth is `public/logos/logo.svg` (the ember stone; rendered inline via `BotStone` in `src/components/general/logo-icon.tsx`); browser favicon/assets are generated from it. Handoff brand assets (stones, coordinator, wordmarks, app icon) live in `public/brand/`. ## Entrypoint @@ -32,6 +34,7 @@ | DB admin UI | `bun run db:admin` | | DB backup | `bun run db:backup` | | Seed super admin user | `bun run seed` (runs `src/scripts/seed-super-admin.tsx`) | +| Seed sample bot crew | `bun run seed:bots [email]` (runs `src/scripts/seed-bots.ts`, idempotent) | | Query DB (CLI) | `bun llm-code-assistant/query-db.ts ""` | | Install | `bun install` | @@ -74,19 +77,14 @@ Inspect live data with `bun llm-code-assistant/query-db.ts ""` - a read-onl `api/admin/crud/[[...paths]].ts` dispatches `GET/POST/PUT/DELETE` by table name: `userAuth` → `read_only` GET-only gate → per-table switch → generic fallback (super admin only). Tables without custom handlers fall through to generic CRUD with zero code. Customize a table by adding `src/pages/api/admin/crud/(tables)//` with `index.ts` + per-method files, then add a `case` in the router. -### Documents (Letters / Emails / Invoices / Quotes) +### Bots (the chat shell) -The preset ships a complete document suite on top of the generic CRUD: - -- **Tables:** `letters`, `emails`, `email_attachments`, `invoices`, `invoice_items`, `quotes`, `quote_items`, `invoice_quotes` (statuses/enums in `src/dict/document-dict.ts`) -- **Admin sections:** `/admin/letters`, `/admin/emails`, `/admin/invoices`, `/admin/quotes` - full list/create/view/edit CRUD, line items, quote-to-invoice linking, PDF download, send-email modals -- **PDF:** `src/functions/backend/pdf/` - `pdf-helpers.ts`, `letter-pdf.ts`, `invoice-pdf.ts` (drives both invoices and quotes via `kind`), `render-blocks.ts` + `html-blocks.ts` (TinyMCE HTML → PDF blocks), `letter-date.ts` -- **Email:** `src/functions/backend/email/` - `email-client.ts` (nodemailer SMTP), `send-email.tsx` (renders ReactNode into the `src/email/root.tsx` shell), `document-emails.tsx` (invoice/quote bodies) -- **API routes:** `api/admin/{letters,invoices,quotes}/generate-pdf.ts` (streams PDF binary), `api/admin/{emails,invoices,quotes}/send-email.tsx`, `api/admin/emails/sync-attachments.ts` -- **DB grabs:** `db/documents/`, `db/invoices/grab-invoice.ts`, `db/quotes/grab-quote.ts` -- **Client helpers:** `src/functions/frontend/document-actions.ts` (PDF download + send-email actions) -- **Numbering:** invoice/quote numbers auto-generate as `-YYYYMM-###` / `Q-YYYYMM-###` from `SiteData["SiteSlug"]` in the `invoices`/`quotes` CRUD handlers -- **Shared UI:** `src/components/general/` `pdf-preview.tsx`, `sortable-line-items-editor.tsx` (+ `sortable-line-item-row.tsx`), `copy-from-existing-select.tsx`, `main-hero-section.tsx` +- **Tables:** `bots` (per-user workspace; `kind` worker|coordinator, `hue`, `role`, `face`, `status_line`, `unread`), `bot_messages`, `bot_tasks`, `bot_task_steps`. Enums in `src/dict/bot-dict.ts`. +- **Routes:** `/admin/bots` redirects to the first bot (coordinator pinned first); `/admin/bots/[bot_id]` renders conversation + working-on panel. Both render in the full-bleed `src/layouts/bots/` layout (rail of conversations + user popover), selected in `__root.tsx` before the admin layout. +- **Server:** `src/functions/backend/db/bots/` (`grab-bots`, `grab-bot-messages`, `grab-active-bot-task`, `grab-user-bot`, `mark-bot-read`, `insert-user-message`). `api/admin/bots/send-message.ts` posts a user message to a bot owned by the session user (zod: `src/schemas/bot-schema.ts`). +- **Avatars:** `BotStone` (`src/components/general/bot-stone.tsx`) renders the stone inline from `src/data/stone-geometry.ts`, hue as a fill token, face (`idle|working|done|asleep|needs-you`) drawn as eyes. The coordinator is hue-less (ink body, paper eyes; inverts in dark). Never put a stone in a circle/badge or add outline/shadow. Wordmark is vector (`Wordmark`, `src/data/wordmark-paths.ts`), never live text. +- **Copy helpers:** `src/utils/crew-status.ts` (`3 working · 1 needs you`, "Three bots are working. One needs you."), `src/utils/relative-time.ts` (relative timestamps only). +- **Not built yet:** add-bot flow, attachments, routines, help, live updates over WebSocket, the model layer. ## Admin Route Hierarchy @@ -94,10 +92,8 @@ Actual preset routes under `/admin`: - `/admin` - dashboard - `/admin/people` - admin users list, plus `create/`, `[user_id]/` (profile), `[user_id]/edit/` -- `/admin/letters` - letter templates, plus `create/`, `[id]/`, `[id]/edit/` -- `/admin/emails` - email records/drafts, plus `create/`, `[id]/`, `[id]/edit/`, `template/` (live email-shell preview) -- `/admin/invoices` - invoices, plus `create/`, `[id]/`, `[id]/edit/` -- `/admin/quotes` - quotes, plus `create/`, `[id]/`, `[id]/edit/` +- `/admin/bots` - the Cobble chat shell, plus `[bot_id]/` +- `/admin/logs` - audit log (super admin; also the "Activity" item in the bots popover) - `/admin/backups` - super admin only: create/list/download database snapshots in `db/.backups`, plus `restore/` (restore a listed backup, or upload a SQLite database file which is saved to `.backups` then restored). Shells out to the `bun-sqlite` CLI (`backup`, and `import ` since `restore` is interactive) via `src/functions/backend/backups/`; API routes in `api/admin/backups/` - `/admin/settings` - current user profile, plus `password/` (only when `AUTH_LOGIN_METHOD=password`; redirects to `/admin/settings` otherwise). The aside renders it as a collapsible sub-nav via `children` in `(dict)/admin-aside-groups-dict.tsx` @@ -124,8 +120,8 @@ Server functions use `index.server.ts` alongside `index.tsx` (e.g. `single-docum - **`src/hooks/`** - Shared React hooks: `use-admin-crud-get`, `use-fetch-get`, `use-form-init`, `use-local-user`, `use-users-get` - **`src/functions/`** - Shared functions split by target: - - `src/functions/backend/` - Server-only: auth (`backend/auth/`), DB queries (`backend/db/` incl. `documents/`, `invoices/`, `quotes/`), email client + templates (`email/`), PDF generation (`pdf/`), media upload, `logger.ts` (audit logging) - - `src/functions/frontend/` - Client-safe: CRUD handlers, fetch handlers, media upload/delete handlers, `document-actions.ts` (PDF download/send actions) + - `src/functions/backend/` - Server-only: auth (`backend/auth/`), DB queries (`backend/db/` incl. `bots/`), email client + templates (`email/`), media upload, `logger.ts` (audit logging) + - `src/functions/frontend/` - Client-safe: CRUD handlers, fetch handlers, media upload/delete handlers - `src/functions/server/` - Server utilities (media server handler etc.) - **`src/utils/`** - Utility modules: cookies, crypt, ejson, money, safe-compare, text-to-html, and more. Most have corresponding `.test.ts` files. - **`src/utils/rbac/`** - RBAC engine: `user-type-assignment` (matrix interpreter), `is-pass-through-user`, `can-*` permission helpers, `filter-assignable-user-types` - with `bun:test` suites driven by fixture matrices @@ -140,7 +136,7 @@ Server functions use `index.server.ts` alongside `index.tsx` (e.g. `single-docum | Directory | Description | | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `@moduletrace/twui` | **The** design system - external package. Contains layout primitives, elements, editors (TinyMCE), form components, hooks, SVGs, and utilities. Import from `@moduletrace/twui` or a deep path such as `@moduletrace/twui/layout/Button` | -| `src/components/general/` | Shared page components built on twui: `logo`, `logo-icon`, `inner-page-hero-section`, `main-hero-section`, `admin-hero`, `login-shell`, `tagline-section`, `pdf-preview`, `sortable-line-items-editor`, `copy-from-existing-select`, media/image helpers | +| `src/components/general/` | Shared page components built on twui: `logo`, `logo-icon`, `wordmark`, `bot-stone` (+ `bot-stone-face`, `stone-eye-*`), `inner-page-hero-section`, `main-hero-section`, `admin-hero`, `login-shell`, `tagline-section`, media/image helpers | ## UI & Styling Rules (MANDATORY) @@ -151,8 +147,8 @@ Server functions use `index.server.ts` alongside `index.tsx` (e.g. `single-docum - Shared: `Logo`, `LogoIcon`, `InnerPageHeroSection`, `AdminHero`, `LoginShell` (auth pages/forms), `TaglineSection` - **UNCHANGEABLE RULE - twui components always win over raw JSX elements.** Use twui components (`H1`-`H5`, `P`, `Span`, `List`, `Img`, `Row`, `Stack`, `Container`, `Section`, `Card`, `Button`, `Tag`, `Link`, etc.) before basic JSX elements (`

`-`

`, `

`, ``, `

    `, `
  • `, ``, ``, ` - } - className="xl:p-6" - > - - -

    - {title || "Select Record"} -

    - Select a record to duplicate -
    - - - {records?.[0] ? ( - records?.map((rec, index) => { - const date = dayjs( - twuiNumberfy(rec.created_at), - ); - - return ( - { - onSelect?.(rec); - setOpen(false); - }} - className="w-full cursor-pointer" - > - - - -

    - {rec.title} -

    -
    - - - - {date - .toString() - .substring(0, 16)} - - -
    -
    - ); - }) - ) : ( - - )} -
    -
    - - - ); -} diff --git a/src/components/general/logo-icon.tsx b/src/components/general/logo-icon.tsx index 62f3657..dc13d3a 100644 --- a/src/components/general/logo-icon.tsx +++ b/src/components/general/logo-icon.tsx @@ -1,35 +1,22 @@ import type { ComponentProps } from "react"; -import { twMerge } from "tailwind-merge"; +import BotStone from "./bot-stone"; type Props = { icon_size?: number; img_props?: Pick, "className">; }; +/** + * The Cobble mark: the ember stone. Source of truth is + * `public/logos/logo.svg` (copied from `public/brand/icons/ember.svg`). + */ export default function LogoIcon({ icon_size = 40, img_props }: Props) { return ( - - - - + ); } diff --git a/src/components/general/logo.tsx b/src/components/general/logo.tsx index f754ecd..20baeea 100644 --- a/src/components/general/logo.tsx +++ b/src/components/general/logo.tsx @@ -1,17 +1,23 @@ import Row from "@moduletrace/twui/layout/Row"; -import Span from "@moduletrace/twui/layout/Span"; -import type { ComponentProps, PropsWithChildren } from "react"; +import type Span from "@moduletrace/twui/layout/Span"; +import type { ComponentProps } from "react"; import { twMerge } from "tailwind-merge"; import Stack from "@moduletrace/twui/layout/Stack"; import LogoIcon from "./logo-icon"; -import { SiteData } from "@/src/data/site-data"; +import Wordmark from "./wordmark"; type Props = { icon_size?: number; text_wrapper_props?: ComponentProps; + /** + * Only `className` applies: it sets the wordmark colour. + */ text_props?: ComponentProps; }; +/** + * Horizontal lockup: the ember stone beside the `cobble` wordmark. + */ export default function Logo({ icon_size = 36, text_props, @@ -19,38 +25,18 @@ export default function Logo({ }: Props) { return (
    - + - - - {SiteData["SiteName"]} - - + ); } - -function HeroText({ - text_props, - children, -}: PropsWithChildren & { - text_props?: ComponentProps; -}) { - return ( - - {children} - - ); -} \ No newline at end of file diff --git a/src/components/general/pdf-preview.tsx b/src/components/general/pdf-preview.tsx deleted file mode 100644 index 4f8b9a0..0000000 --- a/src/components/general/pdf-preview.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import Loading from "@moduletrace/twui/elements/Loading"; -import Center from "@moduletrace/twui/layout/Center"; -import Span from "@moduletrace/twui/layout/Span"; -import { useEffect, useState } from "react"; - -type Props = { - url: string; - body: any; - download_name?: string; - height?: number; -}; - -export default function PdfPreview({ - url, - body, - download_name, - height = 1200, -}: Props) { - const [blob_url, setBlobUrl] = useState(); - const [error, setError] = useState(); - - useEffect(() => { - let object_url: string | undefined; - let cancelled = false; - - setBlobUrl(undefined); - setError(undefined); - - (async () => { - try { - const res = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(body || {}), - }); - - const content_type = res.headers.get("Content-Type") || ""; - - if (content_type.includes("application/json")) { - const json = await res.json(); - throw new Error(json.msg || "Couldn't generate PDF"); - } - - if (!res.ok) { - throw new Error(`Request failed with status ${res.status}`); - } - - const blob = await res.blob(); - - if (cancelled) return; - - object_url = URL.createObjectURL(blob); - setBlobUrl(object_url); - } catch (error: any) { - if (!cancelled) { - setError(error.message || "Couldn't generate PDF preview"); - } - } - })(); - - return () => { - cancelled = true; - - if (object_url) { - URL.revokeObjectURL(object_url); - } - }; - }, [url]); - - if (error) { - return ( -
    - {error} -
    - ); - } - - if (!blob_url) { - return ( -
    - -
    - ); - } - - return ( -
    - {download_name ? ( - - Download PDF - - ) : null} -