Four changes for 200+ themes, and one mistake worth recording. ORDERING IS A ROTATION, not a sort. The registry order is the list's only structure — families sit together in it — so lifting the selected theme to the top answers "which am I on" by destroying "what is next to it". Rotating keeps every neighbour and moves the starting point. Both lists use the same `ordered` array, which is the point: "the one below the one I have on" has to mean the same thing in the dropdown and in the ctrl+shift+space browser, or one teaches an order the other contradicts. FAVOURITES are a table, not a JSON column. theme_favorites is a row per star, so a toggle is an INSERT or a DELETE and cannot race with itself; an array in user_settings would be read-modify-written and two tabs would lose one. No foreign key to a theme, because themes are not rows — most are compiled in, the rest are directories discovered at request time — so a star can outlive its theme and the reader intersects with the live registry rather than trusting the table. Favourites-first is a second rotation-preserving pass rather than a filter, so the list can still answer "what else is there". Filtering to favourites only is a separate, explicit toggle. THE INSTRUCTIONS NO LONGER SCROLL AWAY. DropdownMenuContent carried overflow-y-auto itself, so the one line telling you the shortcuts exist was visible for half a second and then gone under two hundred entries — which is the same as not having written it. Header and footer are now siblings of a scroll box instead of passengers in it, and the header names alt+space too. THE MISTAKE: the routes went on /themes first, and that prefix is deliberately UNAUTHENTICATED — the sign-in screen paints in the chosen theme, before any account exists to ask. An anonymous GET answered 500, which was the consequence arriving politely. They now live on /theme-favorites, claimed by the `account` permission, which is core and already owns /user, /dock and /api-keys: a starred theme is the same shape as a dock preference. I then mounted it one line off, in the unprotected block beside the thing I was moving it away from, and caught that by reading rather than by testing. Verified after: /api/theme-favorites is 401 unauthenticated on both verbs, and /api/themes still answers 200 so the login screen keeps its colours. 10 tests on the pure ordering — including the property that makes a rotation a rotation: every adjacency in the original survives it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
67 lines
4.7 KiB
TypeScript
67 lines
4.7 KiB
TypeScript
// What `db:push` creates.
|
|
//
|
|
// This barrel is drizzle-kit's view of the schema — `drizzle.config.ts` points `schema` straight at
|
|
// this file. It is NOT the runtime's view: every query imports its table object from the schema file
|
|
// directly and calls `db.select().from(table)`, and nothing uses drizzle's relational API (`db.query.X`),
|
|
// which is the only thing the `schema` passed to `drizzle()` in db.ts is for.
|
|
//
|
|
// So a commented line here removes a table from the DATABASE without removing a line of code. That is
|
|
// deliberate and it is what makes the split below possible.
|
|
//
|
|
// ── Core, and plugins ──
|
|
//
|
|
// A fresh install creates the core tables only. Everything under "plugins" belongs to a sidecar a
|
|
// baseline install does not run.
|
|
//
|
|
// A plugin's tables are NOT among them any more: a plugin owns its schema at
|
|
// `plugins/<name>/db/schema.ts`, and installing it regenerates `plugin-schemas.gen.ts` (imported at the
|
|
// bottom of this file) and runs `db:push`. Offscale was the first out, on 2026-08-15.
|
|
//
|
|
// The commented lines below are kept rather than deleted. They record what each remaining sidecar's
|
|
// tables are called and which file defines them — which is exactly what each one needs when its turn to
|
|
// be extracted comes.
|
|
|
|
// ── Core ─────────────────────────────────────────────────────────────────────────────────────────
|
|
|
|
export * from './auth/schema'; // users, passkeys, passkey_challenges, token_blacklist
|
|
export * from './permissions/schema'; // role_permissions — what each ROLE may reach
|
|
export * from './api-keys/schema'; // api_keys
|
|
export * from './user-data/schema'; // user_settings, user_state, user_integrations, dock_configs
|
|
export * from './dashboards/schema'; // dashboards, screens, dashboard_defaults
|
|
export * from './server/schema'; // server_config (SMTP lives here), server_integrations
|
|
export * from './pipeline-jobs/schema'; // pipeline_jobs
|
|
export * from './chat-events/schema'; // chat_session_events
|
|
export * from './chat-session-models/schema'; // chat_session_models
|
|
export * from './chat-notification-prefs/schema'; // chat_notification_prefs
|
|
export * from './ai-provider-usage/schema'; // ai_provider_usage — last usage reading per AI account
|
|
export * from './anthropic-accounts/schema'; // anthropic_accounts — switchable OAuth credentials
|
|
export * from './theme-favorites/schema'; // theme_favorites — starred themes, per account
|
|
export * from './command-hq/schema'; // command_hq_categories, command_hq_sessions, command_hq_tags, command_hq_session_tags
|
|
export * from './agent-panels/schema'; // agent_panels
|
|
|
|
// Core because the tailnet is the perimeter — a security model resting on it cannot treat administering
|
|
|
|
// The app store itself, and the credentials it stores for what it installs. `app-store/effects.ts`
|
|
// reads service_connections, so this is core however few plugins are installed.
|
|
export * from './app-store/schema'; // sidecar_installs
|
|
export * from './plugins/schema'; // plugin_installs — core: the plugin system is the platform's own
|
|
export * from './service-connections/schema'; // service_connections
|
|
export * from './notify/schema'; // push_devices — core since 2026-08-19, see docs/push-notifications.md
|
|
export * from './nudges/schema'; // nudges — the Nudger schedule
|
|
|
|
// ── Plugins — uncomment when the plugin is installed ─────────────────────────────────────────────
|
|
|
|
export * from './dav/schema'; // dav_app_passwords officer-caldav
|
|
|
|
// ── Plugin tables are NOT here ───────────────────────────────────────────────────────────────────
|
|
//
|
|
// They live in `drizzle-schema.ts`, which re-exports this file and adds the generated barrel. That file
|
|
// is drizzle-kit's entry; this one is the runtime's, and the split exists because the barrel names plugin
|
|
// directories that may not be on this machine. Importing it here put a file that is ALLOWED to be wrong
|
|
// into the module graph of the process that must always start — and on 2026-08-16 it stopped a server
|
|
// booting after a pull deleted `plugins/`. See `drizzle-schema.ts`.
|
|
//
|
|
// The barrel still follows DIRECTORIES rather than installs: push drops what it cannot see, so tracking
|
|
// installs would delete a plugin's tables on uninstall. Following the directory means uninstall keeps
|
|
// every row and only deleting a plugin's source can lose data.
|