Skip to content

Changelog

Release notes aggregated directly from the package changelogs.

These notes are imported from each package’s CHANGELOG.md at build time. Unreleased entries describe work on the current source branch, not a published npm release.

create-pupitre-plugin

Package source

Changelog

Unreleased

  • Add opt-in --with-ui (createPlugin({ withUi: true })) scaffolding: shared catalog extension, typed server field factory, accessible React component, published ././catalog/./react subpaths and generated tests; generated factory names avoid collisions.

0.1.0

  • Added CLI and reusable createPlugin API with kebab-case/scoped names, safe directory handling and force overwrites.
  • Ship a working navigation plugin template with TypeScript, tsdown and a public-manager Japa integration test.
  • Security (WP42): reject parent traversal before path normalization and refuse symlink destinations/output files, including forced writes.
  • Generated packages run npm test against published build exports without source-only conditions; regression coverage uses an extracted Pupitre tarball outside the workspace.

pupitre

Package source

Changelog

Unreleased (1.1.0)

Changed

  • Localization (breaking for strict comparisons): server t() no longer returns its input unchanged. It returns the readable, interpolated English text followed by invisible signed translation metadata, so t('Title') === 'Title' is false. Resolve with resolveTranslations() / stripTranslationMarkers() before strict comparison, string transformation or delivery outside Pupitre responses, and use captureTranslation() / restoreTranslation() to persist translation intent. Error.message, logs and regex/includes checks keep reading the English text.
  • Tables: toggle/checkbox inline-edit cells display “Enabled”/“Disabled” instead of “On”/“Off”; Shield permission checkboxes show human labels (“View any”, “View”) instead of ability names.
  • Tenancy: the default column tenancy scope binds the tenant id as a string so TEXT ownership columns match on SQLite (better-sqlite3 binds JS numbers as REAL); integer ownership columns keep comparing and filling as integers on SQLite and PostgreSQL.

Added

  • Localization: request-local English/Indonesian localization. Server t(key, params?) markers are resolved at every Pupitre response boundary (structured Inertia props including full-page/SSR visits, JSON actions/options/search/notifications, auth mail, CSV/progress output); plural { one, other, zero? } messages and {name} interpolation; defineConfig({ i18n }) host dictionaries and definePlugin({ messages }) plugin dictionaries; localized VineJS messages with field labels (host Vine providers keep precedence for untranslated rules); panel .locale(string) / .locales([...]), user/session/Accept-Language resolution, profile language selector and an optional locale users-column migration stub; locale is a reserved page/resource slug. Record data is never translated. ADR-0023.
  • Localization: persisted notifications and queued CSV headers store readable text plus translation intent and are translated at read/render time (survives APP_KEY rotation); tools/i18n-check (npm run i18n:check) enforces Indonesian coverage of every literal t() key in CI.
  • Catalog extensions: defineCatalogExtension() / mergeCatalogExtensions() in @rikology/pupitre/catalog and defineField(), defineColumn(), defineEntry() factories with typed custom methods, inherited field methods, Zod-validated props, VineJS value validation, server formatting/dehydration and live/repeater/action-modal support; definePlugin({ catalog }) and page.catalogExtensions metadata. Element type in specs is now an open string validated by the client registry. ADR-0024.
  • Tables: table.filter.queryBuilder(name).constraints([...]) with qb.text/number/date/boolean/select/relationship constraints (qb.number(...).integer() for integer columns), negation, AND-within/OR-between groups, strict allowlist validation (400 on malformed, fractional-for-integer, out-of-range or year-zero values), size caps, scope-safe OR wrapping and timezone-aware calendar comparisons across table data, summaries, groups, relation tables, table widgets and exports. ADR-0025.
  • Tables: opt-in .multiSort(max = 3), ordered .defaultSort([...]) and URL sort=title,-price with deterministic qualified primary-key tiebreakers; existing single-sort links keep working and sort= clears defaults.
  • Validation: public compileValidator() / FormValidator so plugins, commands and jobs can validate data with a form’s VineJS rules.
  • Authorization: opt-in panel .strictAuthorization() validates after panel/plugin resolution that every resource, nested resource and relation-manager related model has an explicit or discovered policy (E_PUPITRE_MISSING_POLICY), denies policy-less checks at runtime, and is reported by pupitre:inspect.
  • Panels: .environmentIndicator({ visible?, label?, color? }) shares an environment prop (shown outside production by default); .impersonation({ canImpersonate?, canBeImpersonated?, redirectTo? }) with actions.impersonate() (fail-closed impersonate policy ability, scoped tenant lookup, actor MFA required, rotated sessions, leave endpoint, profile security mutations blocked while impersonating, pupitre:impersonation:started|stopped events); .sessions({ history?, retentionDays? }) tracks hashed browser-session tokens per login method, revokes unknown/revoked sessions on the next request, records authentication history (failed logins never store unknown emails), adds profile session management (password/passkey-confirmed “log out other sessions”) and the policy-protected loginHistoryWidget(); pupitre:prune applies session/history retention. Migration stubs 1791700000045/46.
  • Navigation: defineCluster(slug) with .label/.icon/.navigationGroup/.sort/.subNavigationPosition, resource.cluster() and page.cluster(); clusters are discovered from their children, collapse to one authorized navigation entry, prefix child routes (tenancy URL mode and nested resources included), render sibling sub-navigation and breadcrumbs, and reject slug collisions at boot. node ace make:cluster.
  • Widgets: .component(name, loader?) delivers loader data to host components; .actions([...]) runs authorized, Vine-validated widget actions through the JSON action transport; radar and scatter chart types with typed loaders.
  • Tables: model-free custom-data tables via t.records(), t.recordKey() and t.resolveRecord() with URL-backed validated search/sort/filter/pagination, count-free pagination, server-resolved row/header/bulk actions and definition-time rejection of Lucid-only features (ADR-0026); authorizeAbility() passes the resolved row to host abilities.
  • Tables: opt-in saved views (t.savedViews(), t.presetViews([tableView(name).query(...).columns(...).default()])) for resource and relation-manager tables, scoped per user/panel/tenant/table with validated stored queries, per-user defaults, explicit column visibility and fail-closed managePublicViews for public views. Migration stub 1791700000044.
  • Exports: actions.export().formats(['csv', 'xlsx']) with a dependency-free streaming XLSX writer (inline strings, typed cells, bold header, CSV formula-injection policy), persisted format metadata, locale-resolved headers and private queued downloads; queued CSV output now streams too.
  • Forms: s.repeater(...).table([s.tableColumn(label).width().alignment().markAsRequired()]) table repeaters (positional or derived headers, E_PUPITRE_INVALID_REPEATER_TABLE); rich editor .mergeTags(), .mentions([mentionProvider(char).options()/.search()]) through the protected options endpoint with save-time forgery stripping, and Vine-validated .customBlocks([richBlock(id)...]); renderRichContent() and textEntry.html()/.richContent() render escaped merge values and block HTML.
  • Plugin support: public parseInZone()/toIsoInZone() timezone helpers; custom and resource pages accept structural widget builders.
  • Plugin support: public panelRouteGroups() (authenticated, tenant-resolved panel route groups for plugin endpoints) and storage/upload helpers createPupitreStorage, consumeFormUploads, sniffUploadMime, isUploadMimeAllowed, DEFAULT_UPLOAD_MAX_BYTES.

Fixed

  • Action modals: uploads inside action forms use the server-issued uploadOwner (resource/record context) and action validation passes the resource slug to owned-upload validation, so session-owned uploads in header-action forms validate.
  • Forms: .hidden() closures compile to a correct negated visibility condition — boolean literals flip (.hidden(() => true) → visible: false), &&/|| conditions negate via De Morgan, and !get(x) drops its not — instead of spreading not: true onto non-condition values (which produced an invalid {"not":true} spec, e.g. the media resource’s hidden path upload). Lifted .visible()/.hidden() results that are not valid json-render visibility conditions now fall back to server evaluation.

Unreleased (1.0.0, milestone M4)

Changed

  • Catalog (M4): Repeater and Builder accept the common field props; the schema compiler now emits name, label, helperText, hint, required and columnSpan for repeaters, as it already did for builders.
  • Actions (M4): confirmation descriptors default the submit label to the action label (“Delete”, “Restore”) instead of “Confirm”, and destructive confirmations carry a default “This action cannot be undone.” description. confirmation.description is now forwarded. Camel-case built-in labels are split into words (“Force Delete”, not “ForceDelete”).

Added

  • Shield (WP37): official @rikology/pupitre-shield plugin with Lucid roles/permissions, grouped role-management forms, request-cached Bouncer policies, super-admin bypass, explicit-policy preservation, custom action and nested resource grants, shield:sync, User-role assignment helper and custom-page access helper.
  • Shield review (M4): hidden relationship fields skip preload authorization/options queries using the current request context; Shield inventories are panel-local and include related-model custom abilities only for enforced resources.
  • Documentation (WP41): English docs site (Cloudflare Nimbus) at pupitre.rikology.com with M1–M3 guides, plugin authoring, Filament API migration, source-generated TypeScript references and package changelogs; static output publishes the generated shadcn registry index and payloads.
  • Plugin generators (WP36): make:plugin <name> creates an idempotent local plugin; --force overwrites, while --package [--dir] executes the standalone scaffolder pinned to the core major without a shell.
  • Registry (WP40): pupitre:ui add validates names and runs shadcn@4. Explicit root-relative file targets preserve the Inertia source tree; unsafe flattening --path passthrough is removed. Hook item names use kebab-case. Real CLI smoke type-checks copied Select and its dependency closure.
  • Plugins (WP36): definePlugin({ name, register, resources, pages, widgets, middleware, navigation, extendResource, boot }) and panel .plugins([...]). Plugins apply in declaration order at boot: register(panel) returns a new immutable panel builder, contributions are appended to the panel’s own entries, extendResource() runs for every (nested) resource, and boot({ app, manager, panel }) runs before routes are registered. Plugin navigation merges into explicit panel navigation by group label, or follows the derived navigation and claims the resources it references.
  • Audit (WP38): optional audit adapter configuration and lazy drivers/audit reader over audit-trail, configurable model discriminators, normalized changes and pagination; timeline widgets format timestamps in the panel/user timezone and require record view authorization even on edit pages.
  • Read-only activity resources omit mutation routes and implicit Delete actions. Text-column/text-entry URLs support authorized asynchronous subject links, with request context passed through table rendering. Audit timestamps handle the peer’s real SQLite ISO storage and UTC-normalized filter bounds.
  • Realtime (WP39): optional lazy RealtimeAdapter, boot initialization, URI-encoded user/resource channels, tenant-scoped table subscriptions, authenticated shared client metadata, and best-effort post-commit mutation/inbox pings (including queued imports and bulk actions). Existing polling remains available without a realtime client.
  • Realtime review (M4): namespace wire pings as pupitre:notification / pupitre:table, notify every enabled panel’s user channel, and share authorization patterns plus panel guard resolution with transport plugins.
  • Audit review (M4): remove unused recent/AuditFilters contract; custom adapters serve record timelines while Activity queries the audit-trail model.
  • Import/export lifecycle records persist canonical string tenant identifiers, keeping queued worker ownership checks consistent across SQLite and PostgreSQL.
  • Security audit (WP42): enumerate all example mutating routes for missing/invalid XSRF rejection, enforce explicit 403 CSRF responses, disable browser Zod JIT probes under strict CSP, and publish the 1.x private disclosure policy.
  • Restrict UI installer registry URLs and validate component names before network access; block sensitive paths, symlinks and recognizable embedded credentials from registry publication.
  • Release performance audit (WP42): enforce measured gzip/Brotli budgets and lazy heavy-feature boundaries after the example CI build; load the example Transmit client only for an enabled realtime driver.
  • Packaging audit (WP42): verify every built runtime import uses a declared dependency/peer and resolves under ESM conditions; validate all seven package tarballs and release assets, and exclude configure-test tmp declarations from the core build.
  • Harden MIME validation and bounded tempfile buffering for resource/plugin uploads, avatars and CSV imports; apply default ceilings and serve CSV attachments with nosniff.
  • Throttle sensitive panel and example-host authentication endpoints with bounded local fallback or installed shared limiter, propagating limiter backend failures instead of bypassing protection.
  • Bind synthetic queued HttpContext into the Bouncer container resolver so injected authorization policies work in production jobs.
  • Login and passkey-options quotas: the IP-wide auth budget is 60/minute (it counts successful sign-ins, so shared IPs are not locked out) while the per-email failure window stays at 5/minute and clears on success.
  • Nested resources reused by a parent’s relation manager (or reached outside nested routes) no longer fail with .where expects value to be defined: the nested scope constrains :parentId only when the route carries it.
  • Release 1.0 (WP42): @rikology/pupitre, -react and -ui move lockstep to 1.0.0; official plugins and create-pupitre-plugin start at 1.0.0; inter-package peer ranges are ^1.0.0; catalogVersion is 1.0 (clients on a different major reload once); npm dist-tag latest via the new release.yml trusted-publishing workflow.
  • Final confirmation remediation: nested child scopes now fail closed outside an explicitly bound parent; owning relation managers bind that parent without leaking global-search or relationship-option rows.
  • Preserve preloaded options for client-reactive visibility while still skipping server-hidden relationship authorization and queries.
  • Align passkey login verification and social callbacks with the 60/minute shared-IP sign-in budget; evict the oldest local limiter key at the 10,000-key cap rather than locking out every new identity.
  • Initialize browser CSP jitless configuration before every catalog schema module and probe the built catalog in a fresh process during verify:exports; retain repeated-success login and version-derived scaffolder regressions.
  • Widgets: stats widgets span the full dashboard row by default (Filament StatsOverviewWidget parity); an explicit .span() before or after .stats() still wins.
  • Passkeys: deleting your own passkey from the profile no longer fails with “limit has no effect on a delete statement”. The ownership lookup and the delete now use separate query builders.
  • Option labels: table.text()/table.badge() columns and textEntry/badgeEntry entries accept .options(...) (same shapes as select options) and display the label of the stored enum value instead of the raw value (Published, not published). Array values map item by item, badge colors stay keyed by the raw value, and table group headings use the options of the same-named column. resource scaffolding emits the options for enum columns. BadgeEntryBuilder is exported and types colors().

Fixed

  • Schema cache: field actions (prefixAction/suffixAction/hintAction), page actions, select action links, option/upload URLs and other request-derived props are re-evaluated per request on cache hits; previously the first request’s record, URL and authorization result could be served to later users and tenants in production.
  • Actions: header actions (no record, not bulk) require viewAny before running, matching the action form endpoint; custom actions without .can() could otherwise run for users denied the resource. Form field actions on the create page (no record) authorize with create.
  • Tenancy: host .scope(), table.query(), custom filter/tab and relationship scope callbacks are applied as one AND-grouped WHERE clause, so a top-level orWhere, orHas/orDoesntHave or an OR inside a model scope (withScopes/apply) can no longer widen results past the tenant predicate. Non-WHERE calls (orderBy, preload, join, select, withCount) still apply to the main query. Nested resources keep the parent constraint outside the host scope group.
  • Tenancy: related models that are not panel resources but carry the ownership column are tenant-scoped in relationship options, relationship validation and inline option creation, matching relation managers.
  • CSV: exported cells and headers starting with =, +, -, @, tab or carriage return are prefixed with ' to prevent spreadsheet formula execution (sync, queued and failed-row CSVs). Numeric values are unchanged.
  • Imports: CSV imports call beforeSave in the same lifecycle position as resource create/update.
  • Exports: queued exports persist the export action name; progress and download recheck that action’s current .can() grant and fail closed when it is missing. Exports queued before this change must be recreated.
  • Return URLs: X-Pupitre-Url is validated by one helper for resource, relation-manager, page and export requests; relation managers previously used it unvalidated, and /\host forms are now rejected everywhere.
  • Records: identifiers that PostgreSQL cannot parse for the primary-key type (e.g. /activity/create on a UUID resource) return 404 instead of 500 on record, relation-manager, nested-parent, upload and bulk lookups.
  • Uploads: permanent deletes (destroy, force delete, bulk and relation-manager deletes) remove the record’s file-upload objects after the transaction commits when no other row references them; soft deletes keep files and storage errors are logged without failing the delete. Reference checks are batched per field.
  • Uploads: new optional pupitre_uploads table tracks uploaded keys until a resource, list-page modal, custom page or action-form submission consumes them (after validation, before the save handler). node ace pupitre:prune-uploads --older-than=24h removes abandoned uploads: it claims each row before deleting its object and skips, without consuming, file fields whose references it cannot check (relationship repeaters, custom pages, removed fields). Run node ace pupitre:install and node ace migration:run to enable it; without the table uploads keep working and the command exits with an error.
  • Queued bulk actions validate their form in the request, so field errors reach the action modal before anything is enqueued.
  • Tables: image URL resolution is memoized per page and resolved concurrently instead of one storage exists round-trip per cell.
  • Tests: the PostgreSQL suite passes (relation fixtures no longer replay DDL); c8 enforces 85% line coverage on src/compiler, src/query and src/authz.

Unreleased (0.3.0-beta, milestone M3)

Added

  • Widgets (WP27): added immutable widget definitions, dashboard/custom/resource-page widget grids, deferred props, an authorized widget endpoint, and make:widget generation.
  • Testing (WP34): completed @rikology/pupitre/testing with controller-backed Japa resource/custom-page visits, VineJS form submission assertions, and action execution; added package integration and example functional coverage.
  • Relations (WP28): added .relations()/defineRelationManager() with grouped hasMany/manyToMany table/form tabs, tenant/parent scoping, authorization fallbacks, attach/detach/associate/dissociate and related CRUD actions; nested resources, record sub-navigation, pages.list().modals(), and make:relation-manager.
  • Social login (WP32b): panel .social() provider configuration, lazy Ally loading, guest redirect/callback, verified-email matching/registration, and MFA-aware completion.
  • Import/export (WP30): added actions.export()/actions.import() CSV actions, immutable exportColumn() formatting, prefix-aware filtered exports, schema-derived header mapping (including Skip), per-row VineJS validation/upsert transactions and failed-row CSVs. Added maxSyncRows hand-off, chunked scoped export jobs, boot-registered serializable import/bulk/export handlers, nested parent authorization/ownership, pupitre_exports progress/download lifecycle, private storage, notifications and pruning.
  • MfaPasskeys (WP32a): added the completeLogin MFA continuation seam, panel .mfa()/.passkeys() configuration, TOTP enrollment/challenge with single-use recovery codes, profile security UI, and passkey registration/management plus usernameless login; WebAuthn assertions consume one-time challenges and update credential counters/last-use timestamps.
  • Theme (WP33): added validated defineTheme() definitions and panel theme compilation for font, density, colors, sidebar modes, content width, and safe stylesheet/font URLs; indexAdmin() now discovers theme files.
  • Table interaction (WP29b): added immutable inline-edit columns with Vine validation and scoped PATCH transactions, scoped reorder endpoints, polling, persisted column toggles, responsive card layout, and deferred filter application.
  • Timezone (WP35): date/time parsing now respects effective component/user/panel timezone, rejects DST gaps, selects the earlier overlap offset, and keeps native datetime inputs independent of the browser zone.
  • Editors (WP26b): added MarkdownEditor and CodeEditor schema builders, image editor metadata, sanitized server-side Markdown rendering for TextEntry, and Vine string validation.
  • TableData (WP29a): added filtered-query summaries, group results, relation counts, per-column search, safe row classes, manual soft-delete scoping/actions, and deferred list data; resolved deferred response validation and now serialize column summary/image metadata.
  • Action extras (WP32c): action deep links, per-user/IP action rate limits, modal footer actions and submit/cancel visibility, and action-bearing flash/database notifications.
  • Server evaluation (WP31): added server-backed afterStateUpdated form closures, guarded tenant-scoped evaluation endpoint, and complete pupitre:inspect closure/index diagnostics.
  • WizardBuilder (WP26a): added immutable s.builder() typed block schemas, per-block Vine validation and form wizard steps with server-error step mapping.
  • SchemaParity (M1–M3): added masked/tel/URL text validation, textarea autosize and field hint actions, bounded s.slider(), transactionally synchronized hasMany repeaters, and related-model policy checks for Select validation.
  • SchemaParity/WizardBuilder (M3): apply builder field validation, metadata, defaults, and visibility; share schema traversal across wizard steps, builder blocks, and repeaters, and preserve explicit nested item bindings and form-field actions.

Changed

  • Wire protocol: catalogVersion is now 0.3 (new catalog components, descriptor fields, page props and endpoints listed above); packages released lockstep as 0.3.0-beta. New optional peers: @adonisjs/ally, @adonisjs/queue, @simplewebauthn/server, otplib, qrcode.
  • Authorization: Pupitre authenticates through ctx.auth.authenticateUsing([panelGuard]), so Bouncer policies see the panel user without relying on the host’s silent-auth middleware; a policy that defines an ability method is final (fallbacks such as replicate → create apply only when the method is missing).
  • Widgets/TableData (WP27/WP29a): preserve resource/custom-page context with pagination-safe widget refresh URLs, require explicit record-page context, and recheck nested parent/page access; normalize SQL group keys through Lucid attribute serialization, merge colliding groups with weighted averages, and correlate relation summary expressions so filtered-set totals survive pagination.
  • MfaPasskeys (WP32a): bound MFA guesses atomically per actual user primary key across renewed password-login sessions, recovery-code regeneration and MFA disable; retain a fail-closed in-memory budget without an available optional limiter, and consume recovery codes under a transactional row lock.
  • WizardBuilder (WP26a): retain owning-resource metadata and full nested upload paths during block validation, persist transformed block output and propagate validation backend errors; match uploads and nested image entries by full schema path and resolve typed block .data paths consistently for persisted files and cleanup.
  • Import/export/Table interaction (WP30/WP29b): remove ordering from export counts, use primary-key batch tiebreakers and tolerate queued result-count drift; persist setup failures, keep completed exports downloadable after notification failures, and respect explicit reorder denials.
  • Relations/Security (WP28/WP30): require record view permission and server-side visibility for custom relation actions, resolve bulk selections through parent/tenant scopes with per-record authorization, preserve explicit policy denials, skip ownership columns for tenant-less unregistered related models, and re-check nested parent access in queued bulk workers.
  • Server evaluation (WP31): accept incomplete form state containing empty strings and nulls without rejecting unrelated fields.
  • Auth (WP32): clear the login-attempt window after valid credentials while retaining failed-password and MFA throttles.
  • MfaPasskeys/SocialLogin (WP32): cap MFA continuation at five failed attempts, preserve MFA enforcement for passkeys without verified user verification, expose enrollment routes without .profile(), and publish social identity migration with query-free callback redirects.
  • Widgets (WP27): table widgets now run model-backed table queries through the compiler, prefix-aware query parser, and shared server formatter; custom pages accept .widgets() and .filters().
  • Widgets/TableData (WP27/WP29a): calculate summaries in SQL over scoped, filtered queries; support grouped summaries without loading all matching rows. Widget registries accept lazy barrel objects, widget descriptors expose their GET data endpoint, and generated table widgets bind a model.
  • Testing (WP34): isolate fixture rows in integration tests, represent deferred props in helper contexts, and resolve notification inbox database access from the active request app instead of a cached app binding.
  • Security (M3): enforce record and relation authorization, tenant-scope unregistered related models, preserve soft-delete hooks for relation deletes, and make panel-guard users visible to Bouncer policies.
  • Action extras (WP32c): resolve record-page header deep links independently of record row actions while preserving scoped record authorization.
  • Table interaction (WP29b): reorder requests accept a page-window offset so moving page-two rows preserves preceding global positions.

Unreleased (0.2.0-beta, milestone M2)

Added

  • Panel builder: .login(), .registration(), .passwordReset(), .emailVerification(), .profile({ avatar }) (.login(false) keeps host auth), .navigation([...]) with the new nav.group()/nav.item() builders (sort, badge closures, visibility, collapsible groups, external links), .userMenu([...]), .domain(), .theme({ primary, darkMode, radius, vars }), .tenancy({ mode: 'url' | 'implicit', ... }), .notifications({ interval }), .globalSearch({ keyBinding } | false), .pages([...]).
  • Auth (WP17): spec-based login/logout, forgot/reset password (pupitre_password_resets, SHA-256 one-time tokens per guard with TTL, pupitre:prune), optional registration and signed email verification gate, profile (name/email/password with current-password check, avatar via the storage adapter), login/reset throttling through @adonisjs/limiter when configured. MailAdapter + @rikology/pupitre/drivers/mail over @adonisjs/mail. Migration stubs published by configure.
  • Navigation & panels (WP18): permission-filtered nav (viewAny, page canAccess), boot-time validation of nav slugs, multi-panel support, make:panel.
  • Authorization (WP19): full Bouncer mapping incl. authorizeModel() for related models, self-handled E_PUPITRE_FORBIDDEN (JSON 403 / Inertia redirect back + flash), pupitre:inspect [--json] reporting missing policies/abilities.
  • Tenancy (WP20): TenancyAdapter, default tenant_id column driver, @rikology/pupitre/drivers/tenant over @rikology/adonisjs-tenant, tenant-prefixed routes (mode url) or user current tenant (mode implicit), switch endpoint, automatic ownership fill, resource .scope(false); every resource query goes through scopedQuery().
  • Notifications (WP21): NotificationsAdapter, @rikology/pupitre/drivers/notifications over @rikology/adonisjs-notifications, /_pupitre/notifications (+ /read), notify.*().sendToDatabase(user?).
  • Global search (WP22): resource .globallySearchable([...]) / .globalSearchDetails(), GET /_pupitre/search (parallel, 5 authorized results per resource, tenant/policy scoped).
  • Custom pages (WP23): definePage() (path, form, fill/save, actions, component() escape hatch), page discovery in indexAdmin(), make:page.
  • make:resource (WP24): Lucid model introspection (columns, types, enum meta, relations) or --from-migration.
  • Dynamic options (WP25): select options(closure) (with values for validation/label resolution), relationship(name, title, scope?), searchable(), preload(), createOptionForm()/createOptionUsing(), prefixAction()/suffixAction(); GET/POST /_pupitre/options/:resource/:field; server-side validation of submitted option values.

Changed

  • Wire protocol: pupitre.panel gains profileUrl, userMenu, search; pupitre.tenant/tenants (TenantOption), pupitre.notifications ({ unread, url, readUrl, interval }), navGroup.collapsed, brand.logoDark; new response schemas for search, notifications and options. catalogVersion is now 0.2.
  • page.key includes the tenant id; spec cache keys include the request-bound panel path.

Unreleased (0.1.0-beta, milestone M1)

Added

  • Builders: definePanel (path, guard, middleware, resources, brand, theme, timezone, breadcrumbs), defineResource with model-typed form/infolist schema, table, actions, pages and lifecycle hooks (fill, mutateBeforeCreate/Save, before/after Create/Save/Delete, create/update overrides; hooks receive the Lucid transaction after ctx), schema (inputs, layouts, repeater, file upload, rich editor, infolist entries), table (text/badge/icon/image/color/custom/fragment columns; select/ternary/dateRange/custom filters), action/actions (create/edit/view/delete/bulk), pages.
  • @rikology/pupitre/catalog: zod-only component catalog, wire-protocol schemas (pageProps, actionDescriptor, actionResult, tableDescriptor, tableData, …), dynamic()/raw() expression helpers, fixtures and assertRegistryComplete.
  • Compilers: schema → json-render spec with closure lifting (expressions/$computed, static fallback, E_PUPITRE_UNLIFTABLE_CLOSURE), record/user-independent spec cache, VineJS validator (one vine.compile per definition, nested repeater paths, model-aware unique, upload key ownership, disabled/hidden dehydration), table query/descriptor/data with server-side formatting, action descriptors/forms/execution.
  • HTTP runtime: per-panel routes (<panel>.<slug>.<page>), Inertia page props with top-level data for tables, JSON ActionResult protocol for actions, Bouncer mapping (fail-closed when a policy exists without Bouncer), uploads (/_pupitre/upload with MIME sniffing, size limits, session ownership, cleanup of replaced files), HTML sanitization.
  • node ace configure @rikology/pupitre / pupitre:install, pupitre:routes, indexAdmin() discovery hook.

pupitre-audit

Package source

Changelog

Unreleased

  • Localize Activity navigation, fields and timeline headings.
  • Keep historical audit entries visible without subject links when PostgreSQL cannot parse their IDs as the subject model’s primary-key type.

0.1.0

  • Add record activity timelines backed by the configured Pupitre audit adapter.
  • Add a read-only Activity resource with event, subject, and date-range filters, old/new detail, and authorized subject links.
  • Require explicit authorization or AuditPolicy/ActivityPolicy for activity access.
  • Security (WP42): add regression coverage for Activity tenant isolation and subject links requiring scoped records and list/detail authorization.
  • M4 review: use the supported activity icon and clarify that custom audit adapters provide timelines, not Activity resource queries.

pupitre-backup

Package source

Changelog

Unreleased

  • Add server-only Backups resource with fail-closed authorization, tenant-scoped metadata, private streamed downloads, and file-removing deletion.
  • Add consistent SQLite, PostgreSQL custom-format, and transactional MySQL snapshots, gzip-streamed to Drive with credential-safe errors and guaranteed ordinary-path temporary cleanup.
  • Add durable cross-process concurrency exclusion, post-success count/age retention, optional jobs dispatch, and schedulable backup:run command.
  • Document administrator-only access and manual restoration procedures; no web restoration endpoint is exposed.

pupitre-calendar

Package source

Changelog

Unreleased

  • Add immutable calendar widgets and calendar page plugin, custom event loaders, tenant-scoped resource event sources, policy-aware navigation, and validated UTC rescheduling.
  • Add lazy month/week/day/agenda UI in pupitre-ui with responsive agenda defaults, drag moves, accessible keyboard Move dialogs, and optimistic rollback.

pupitre-health

Package source

@rikology/pupitre-health

Unreleased

  • Add a fail-closed AdonisJS health dashboard with process-local TTL caching, concurrent-run coalescing, forced refresh, summary statistics and custom-data check tables.
  • Filter diagnostic metadata to known numeric measurements; publish a host-owned disk/memory/database check registry.

pupitre-jobs

Package source

@rikology/pupitre-jobs

Unreleased

  • Monitor all AdonisJS Queue jobs through supported lifecycle tracing channels, preserving each execution attempt.
  • Add tenant-scoped Jobs lists, status tabs, payload/error details and 24-hour stats.
  • Encrypt original retry payloads, recursively redact credentials, and fail closed on authorization and retry eligibility.
  • Add terminal-run retention pruning, an Ace command, migration publishing and a runnable host-job example.

pupitre-kanban

Package source

Changelog

Unreleased

  • Add resource-scoped Kanban board pages, policy-aware card/create links, immutable business-rule gates, and transactional status/position moves.
  • Add lazy sortable board UI with keyboard and pointer interaction, per-column loading, announcements, optimistic rollback, and bilingual copy.

pupitre-media

Package source

Changelog

Unreleased

  • Add a fail-closed, tenant-aware Media resource with thumbnail grids, search/type/date filters, multi-file uploads, metadata editing, and original/conversion cleanup on delete.
  • Add immutable mediaPicker/mediaEntry/mediaColumn catalog extension factories, ordered multi-selection, MIME/count/ownership validation, and private signed thumbnails.
  • Reuse Pupitre’s owned-upload pipeline and expose an authenticated media browse/adoption protocol for inline uploads.
  • Add lazy optional Sharp conversions, a default 320px WebP thumbnail, and original-only fallback without Sharp.

pupitre-react

Package source

Changelog

Unreleased (1.1.0)

Added

  • Toasts: optional <Pupitre toastDuration={5_000}> auto-dismiss delay in milliseconds; default 0 retains manual dismissal, with per-toast deadlines preserved across navigation.
  • Localization: client t(key, params?) resolves synchronously from the compact pupitre.messages shared prop and follows locale changes across Inertia visits.
  • Catalog extensions: defineRegistry(components, { extensions }) merges extension schemas, requires a component per extension, and reports unregistered or unknown spec types with an actionable developer error.
  • Tables: useTable supports JSON QueryBuilder filter state and ordered multi-column sort, including explicit default clearing.
  • Widgets: useWidget(descriptor, initialData) returns { data, loading, errors, reload(params?), runAction(name, data?) } with stale-response suppression and WidgetRequestError; component widgets receive unwrapped loader data.
  • Tables: useTableViews(descriptor, onColumns) lists, applies (prefix-preserving Inertia visits) and mutates saved views.
  • Shell: optional environment, impersonation and cluster navigation props are forwarded through built-in and host-owned shells; export confirmations support XLSX downloads and format choice.

Fixed

  • Action modals: uploads use the server-issued uploadOwner instead of deriving resource context from the action name.

Unreleased (1.0.0, milestone M4)

Added

  • Realtime (WP39): optional Pupitre/PupitreShell client context, SSR-safe useRealtimeSubscription with stable callbacks and cleanup, coalesced descriptor-scoped useTableRealtime, and notification push refresh with polling fallback.
  • Security (WP42): preserve Request-input methods and headers in jsonFetch, ensuring XSRF headers on all mutating methods; add multipart/upload/deletion regressions.

Changed

  • Navigation guard (M4): useNavigationGuard(shouldBlock) no longer calls window.confirm. It returns { pending, proceed, cancel } and holds the blocked Inertia visit, so the caller can render its own confirmation; proceed() re-issues the visit with its original options. Prefetch visits are no longer blocked. Tab close/reload still use the browser’s native prompt.

Fixed

  • Options: abort superseded searches during debounce and discard late responses without changing options, paging, loading, or errors.

Unreleased (0.3.0-beta, milestone M3)

Added

  • Widgets (WP27): added polling-driven partial reloads for widget props and widget grid rendering integration.
  • Relations (WP28): useTable reads descriptor-selected top-level data props and namespaces table query keys while preserving list-page data behavior.
  • Action extras (WP32c): modal deep-link URL synchronization and initial query-param opening, modal footer action execution with form data, and global shortcuts for non-row actions.
  • Server evaluation (WP31): wired debounced evaluate actions to /_pupitre/evaluate, ignored stale responses, and applied only safe /data/** state updates.
  • WizardBuilder (WP26a): action modals navigate wizard steps, submit on the final step, select the first errored step after a 422, and retain Next/Back navigation alongside footer actions when modal submit is disabled.
  • Import/export (WP30): direct CSV downloads and jobs-backed export/import polling now flow through the action bridge, with progress/completion toasts and file delivery.

Changed

  • Confirm fixes (M3): share Cmd/Ctrl shortcut matching across page and UI actions, include field affix/hint shortcuts, and suppress export cancellation errors on navigation.
  • Widgets (WP27): resolve and submit resource forms nested beneath widget layouts, without selecting disconnected forms.
  • Widgets (WP27): widget tables can render outside the spec runtime while retaining useTable URL-prefix sorting, pagination, and prop-only reloads.
  • Action extras (WP32c): import actions now stop before form submission and page-level shortcuts dispatch once; footer confirmations gate requests.
  • WizardBuilder (WP26a): 422 errors select the first invalid step once per submit, without pinning navigation to stale errors.

Unreleased (0.2.0-beta, milestone M2)

Added

  • kind: 'auth' pages render inside the registry AuthShell; kind: 'custom' pages and host pages can keep the panel shell through PupitreShell.
  • ShellProps gains tenant, tenants and notifications; ShellRegistry gains AuthShell.
  • Hooks: useOptions (debounced, abortable, paged dynamic select options), useNotificationsPolling (pauses while the tab is hidden); shared jsonFetch/xsrfToken bridge helpers.

Unreleased (0.1.0-beta, milestone M1)

Added

  • Pupitre Inertia page, PupitreRenderer (store reset only on page.key change; reloads refresh /errors, /meta/record, /options), SpecRenderer for nested specs, defineRegistry (catalog + shell components, built-in $computed functions, $raw directive).
  • Inertia bridge: form submission with 422 → /errors/**, action protocol (link/direct/confirm/form) over JSON fetch with X-Pupitre-Action, X-Pupitre-Url, XSRF header and descriptor HTTP method, ActionResult handling (flash, redirect, reload, patch, deselect), uploads, toasts surviving redirects, catalog-version mismatch reload guard.
  • Hooks: useBoundValue, useFieldError, useStateValue, useSetState, usePanel, usePage, useRunAction, useTable (URL-driven table state with partial visits), useSubmitForm, useEmbedded, useUpload, useToast, useVisit, useNavigationGuard; test seams setRouter, setFetch.

pupitre-settings

Package source

@rikology/pupitre-settings

Unreleased

  • Add immutable typed settings groups, generated custom pages and optional page actions.
  • Every writable form field must have a .defaults() entry; missing defaults fail at plugin registration.
  • Persist JSON values with merged defaults, shared VineJS validation and atomic transactions.
  • Add fail-closed group authorization, tenant isolation and request-scoped caching.
  • Publish an idempotent SQLite/PostgreSQL migration through the configure hook.

pupitre-shield

Package source

Changelog

Unreleased

  • Localize role and permission management labels and user-facing errors.

0.1.0 — Milestone M4

  • Add Lucid Role and Permission models, global role-management resource with resource-grouped permission checkboxes, and configurable host user relation/table integration.
  • Add request/user-cached Bouncer policies, explicit-policy preservation, independent standard ability checks, custom action abilities, nested-resource discovery, and super-admin bypass.
  • Add shieldCan page authorization and shieldRolesField User-resource relationship helper.
  • Add configure migration publishing and lazy Ace command registration; shield:sync idempotently synchronizes resource permissions and can bootstrap a super administrator by ID/email.
  • Add SQLite HTTP integration coverage and seeded example editor/super-admin integration.
  • Security (WP42): forbid non-super-admin escalation through role names or permission delegation, protect the reserved role, and invalidate request-cached grants after edits; expose invalidateShieldPermissions() for host-managed pivot changes.
  • Persist shieldRolesField() assignments and revocations transactionally with authenticated delegation checks, preserving host hooks and invalidating grants after pivot synchronization.
  • Cover Shield-protected production queued workers, permission revocation, changed principals, cross-tenant records and revoked memberships.
  • Thread active transactions through permission checks, isolate request caches from transaction snapshots, and authorize both permission additions and removals while preserving unchanged grants.
  • Restrict Roles form inventories to enforced resources, discover relation-manager abilities on related resources, and isolate inventories when a plugin instance is reused across panels.
  • Hide unauthorized role-assignment fields without blocking User forms or preloading forbidden options; preload existing role selections on edit.
  • Reject deletion of roles containing any resource or host permission the actor lacks, while retaining super-admin bypass and deletion of fully delegated roles.
  • Validate role-assignment IDs as positive PostgreSQL-range integers before querying Roles; normalize equivalent numeric IDs and cover create/update rejection with query-spy regressions.

pupitre-transmit

Package source

Changelog

Unreleased (milestone M4)

  • Add transmitRealtime() with panel-guard, user identity, tenant membership, and resource policy channel authorization.
  • Add SSR-safe transmitRealtimeClient() at /client, shared subscriptions, catalog payload validation, and Shield XSRF request headers.
  • Security (WP42): add malformed-channel and guard-principal/tenant isolation regressions alongside subscribe/unsubscribe XSRF coverage.
  • M4 review: reuse core channel patterns and panel guard helper; accept only namespaced pupitre:notification and pupitre:table wire pings.

pupitre-ui

Package source

Changelog

Unreleased (1.1.0)

Added

  • Localization: accessible, tenant-aware language switcher in the user menu and profile; built-in dialogs, forms, editors, uploads, widgets and accessibility text are translated.
  • Tables: QueryBuilder editor (typed values, negation, AND/OR groups, active summaries) in every filter layout, and keyboard-accessible multi-column sort controls with priority announcements.
  • Catalog extensions: DataTable and nested infolist entries render registered custom columns/entries with formatted values and row/record context.
  • Shell: environment badge and top strip across sidebar/collapsed/top-nav layouts, impersonation banner with CSRF-protected leave, responsive cluster sub-navigation.
  • Tables: Views toolbar (save/update/rename/default/public/delete, saved column visibility), simple pagination and header actions for custom-data tables, CSV/Excel format picker for exports.
  • Forms: accessible table repeater (numbered row controls, drag and keyboard reorder, per-cell errors, horizontal scroll at narrow widths); rich editor merge-tag and mention suggestions and custom-block preview cards with isolated edit modals.
  • Widgets: radar and scatter charts in the lazy chart chunk with screen-reader data tables.
  • Profile: Browser sessions and Recent logins sections.
  • Plugin surfaces (lazy): Calendar (month/week/day/agenda, drag and keyboard rescheduling with rollback), KanbanBoard (dnd-kit sortable cards, keyboard moves, per-column loading, rollback), MediaPicker/MediaEntry/MediaColumn.

Fixed

  • Dark theme primary (oklch(0.545 0.1 163)) raises primary-button text contrast above WCAG AA (4.53:1).
  • Table grid cards: the selection checkbox sits beside the card title instead of being pushed to the opposite edge, and long field values wrap inside the card instead of overflowing it.

Unreleased (1.0.0, milestone M4)

Changed

  • Redesign (M4): new default “writing desk” skin. Warm OKLCH neutrals (canvas, sidebar, card layers), blotter-green default primary, semantic success/warning/danger/info tokens with derived -soft/-ink variants (mixed in oklab so a theme.primary override re-skins every state), --input-strong for checkbox/radio/switch boundaries, chart palette, elevation and density (--control-h, --row-py) tokens. See the root DESIGN.md.
  • Primitives (M4): Button now takes variant (solid/outline/ghost/link) × color (primary/gray/success/warning/danger/info) × size (xs–lg) plus square and loading; the old default/secondary/outline/destructive variants are removed. New exports: buttonClasses, controlClasses, Textarea, InputGroup/InputAddon/InputSlot, FieldDescription, RequiredMark, CheckboxControl, SwitchControl, Avatar, Kbd, Spinner, Skeleton, Tooltip, Panel/PanelHeader, EmptyState, toTone, and popup/dialog/tab class helpers.
  • Shell (M4): sticky topbar (search field, notifications, avatar menu), breadcrumbs above the page title, raised active navigation items, icon-rail tooltips, tenant switcher card, mobile drawer, and header actions laid out by the new ActionList (leading action promoted to primary, overflow in a “More actions” menu).
  • Components (M4): every catalog component restyled: tables in a single panel with toolbar search, filter/column popovers, bulk-action menu and row actions collapsed after two; form sections, tabs and wizards as panels (nested panels flatten through PanelNestingContext); selects and pickers open popups; infolists, widgets, modals, toasts, auth and profile screens follow the same system in light and dark.
  • Testing (M4): AdminPage.openAction(name, row) opens a row’s “More actions” menu when the action is not inline.
  • Motion (M4): shared easing tokens (ease-ui, ease-move, ease-drawer) replace ad hoc curves. The desktop sidebar rail toggle morphs via the View Transitions API (sidebar crossfades and resizes, content slides; instant under reduced motion or without browser support). Section, Repeater item, Builder block and sidebar nav group bodies open and close with a height and fade transition through the new Collapse primitive, with rotating chevrons. Toasts grow in from the bottom edge and close their slot on dismissal instead of disappearing. Top-navigation group dropdowns and the bulk-selection toolbar fade in, and buttons give subtle press feedback.

Added

  • Registry (WP40): deterministic shadcn registry generated from UI sources, versioned npm/import-graph dependencies, preserved relative targets, public /r/*.json payloads and index, CI drift check, and copy-ability tests.
  • Audit (WP38): Widget.Timeline catalog renderer and exported WidgetTimeline display server-formatted activity times, actors, icons/colors, structured field changes, and empty states; WidgetGrid supports timeline spans and deferred data.
  • Realtime (WP39): DataTable and NotificationTray replace polling with configured channel subscriptions, retain polling fallback, and clean up subscriptions and pending reloads on unmount.
  • Realtime inbox (WP39): open notification trays refresh items on user-channel pings through the existing single subscription; superseded requests are aborted and late responses cannot overwrite the current inbox.
  • Accessibility (WP42): strict WCAG 2.1 A/AA axe coverage across registry fixtures, shell/account components, dialogs and interactive popup/editor/table states, plus an example auth/CRUD/settings/security audit.

Fixed

  • DataTable: preserve versioned pending and settled inline edits across older in-flight refreshes (including rollback to a previously saved edit), release saved overrides when the server confirms them, a refresh issued after settlement lands, or host-supplied props change; ignore visits cancelled before they start, revert failures to the latest authoritative value, and memoize column definitions and descriptor lookup.
  • Accessibility: table selection and inline toggle targets are at least 24px without enlarging checkbox visuals, switch tracks, or text-only rows in compact and comfortable density.
  • Class merging (M4): cn() now resolves conflicting utilities with tailwind-merge, so a component’s className reliably overrides primitive defaults instead of depending on CSS order. Template-literal class concatenations with conflicts now go through cn().
  • Selects (M4): table filters, grouping, rows-per-page, editable cells, widget filters and import mapping use the new SelectMenu primitive, the same Base UI trigger and listbox as form Select, instead of native <select> elements. Their heights follow density (--control-h, --control-h-sm), and auto-width triggers size to their widest option like a native select, so a column of editable cells lines up. .pupitre-native-select remains for Select native, drawing the chevrons-up-down icon instead of the UA arrow.
  • Color (M4): the swatch fills its slot instead of collapsing to a sliver; presets are 28px square buttons.
  • Switch (M4): the thumb keeps an even 2px inset in both states instead of overrunning the track when checked, and stays light in dark mode (new --switch-thumb token) instead of reading as a hole in the track.
  • Tags and multi-select (M4): chips and the draft input are centered and compact, so a single row matches the control height.
  • Checkbox layout: keep compact 16px checkbox visuals within 24px form hit targets, align option labels, and separate CheckboxList legends from options. Label clicks, target-edge clicks, and keyboard toggling remain supported.
  • ConfirmDialog: narrower (sm) dialog with the heading centred on the tone icon and full-size description text.
  • Confirmations (M4): the Form dirtyGuard prompt (“Discard unsaved changes?”, Stay / Leave page) now renders the registry ConfirmDialog, the same dialog as action confirmations such as Delete, instead of window.confirm; passkey deletion in ProfileSecurity uses ConfirmDialog too. Closing or reloading the tab still shows the browser’s native prompt.
  • DataTable (M4): an absent data prop no longer re-renders forever (stable empty rows, manual pagination).
  • Wizard (M4): advancing to the last step no longer submits the form when Next becomes the submit button.
  • Repeater/Builder (M4): render their field label, hint, helper text and required marker.
  • Host styling (M4): namespace the example’s card/focus tokens so public-site defaults cannot override Pupitre light/dark surfaces and keyboard focus.
  • Host styling (M4): styles.css points every border at --border in the base layer; bare border utilities no longer paint Tailwind v4’s currentColor (near-black) on cards, tables, chart panels, selects and buttons. Stat cards size to their widget column instead of the viewport, and the table page indicator no longer wraps.
  • Navigation (M4): sidebar, brand, breadcrumb, record sub-navigation and table record links navigate through the Inertia router (new NavLink primitive) instead of reloading the document; modified clicks, new-tab targets, downloads, fragments and cross-origin URLs keep native behavior.
  • Accessibility (M4): move the panel toolbar banner outside the main landmark and cover shell landmark best practices.
  • Realtime inbox (M4): retain existing notification items and keyboard focus during background ping refreshes.
  • Accessibility (WP42): use an opaque auth canvas and strengthen light muted-text contrast so authentication links and muted-surface text meet AA in the built host application.
  • Accessibility (WP42): isolate CheckboxList option field scopes and labels; hide duplicate native inputs correctly, preserve required/error descriptions, and provide 24px checkbox targets with visible keyboard focus.
  • Accessibility (WP42): fall back to field names for unlabelled Text controls, repair standalone/nested infolist description-list semantics, and remove invalid ARIA from decorative hint/color content.
  • Accessibility (WP42): trap and restore mobile navigation/modal/search focus, dismiss menus with Escape, expose skip/landmark structure, uniquely label repeated profile controls and toast dismissal actions, and announce empty search states without invalid empty listbox roles.
  • Accessibility (WP42): add roving ToggleGroup focus, separately named Radio options, sortable/mixed-selection table semantics, labelled upload/editor controls, editor loading status, chart data alternatives, valid icon/toggle table cell roles, and AA destructive-text contrast on hover.

Unreleased (0.3.0-beta, milestone M3)

Added

  • Widgets (WP27): added stats with sparklines, lazy chart rendering, table widgets, custom registry dispatch, deferred placeholders, and polling refresh.
  • Testing (WP34): added the optional @rikology/pupitre-ui/testing Playwright fixture and reusable AdminPage; example E2E specs now import the package fixture.
  • Relations (WP28): added the catalog-backed RelationManager component with relation label/badge, readOnly action suppression, and shared DataTable rendering.
  • AuthShell (WP32b): shows configured social provider sign-in links on the panel login page.
  • Theme (WP33): added top-navigation dropdown groups, persisted collapsed icon rail, responsive content-width shells, density scaling, and per-panel font/stylesheet links.
  • Table interaction (WP29b): added optimistic editable cells with rollback/toasts, keyboard and drag reorder controls, deferred filter Apply, polling-aware reloads, SSR-safe persisted column visibility, and responsive grid cards.
  • Editors (WP26b): added lazy Markdown writing/sanitized preview and attachment upload, lazy CodeMirror with on-demand language extensions, and client-side crop/rotate/flip for image uploads.
  • Action extras (WP32c): notification tray and flash toast action controls, including click-to-mark-read actions.
  • Timezone (WP35): date/time controls display field-zone wall time without browser-local parsing, submit local datetimes for server validation, and show a zone hint when it differs from the browser.
  • WizardBuilder (WP26a): added typed block editing controls and mounted wizard steps with accessible navigation and 422 error-step recovery.
  • SchemaParity (M1–M3): added raw-value text masks, textarea autosizing, accessible scalar/range sliders, hint tooltips/actions, and prefix/suffix action controls.
  • Import/export (WP30): added a CSV import dialog with preview, header-to-form mapping, async-job progress, summary counts, and downloadable failed-row CSV.

Changed

  • Testing (WP34): pupitreTest takes a pupitre: { panelPath, basePath?, landingPath?, email?, password? } fixture option (set it in playwright.config.ts use); AdminPage no longer assumes the example app’s tenant, credentials or seed data.
  • Confirm fixes (M3): inline blur saves no longer steal focus, refreshed rows clear optimistic cells, and page wizards jump once per validation response without blocking subsequent navigation.
  • Table interaction (WP29b): distinguish filter labels from row-specific inline controls for accessible, unambiguous navigation.
  • Table data (WP29a): expose the overall table summary as a named region, distinct from group summaries.
  • Testing (WP34): scope page field helpers to forms so inline table edit buttons cannot be mistaken for loading form fields.
  • Widgets (WP27): Widget.Table now delegates to the shared DataTable instead of a separate compact renderer.
  • Widgets (WP27): render stat colors/icons, support widget spans through all four grid columns, and use the documented widget GET endpoint for polling and filter refresh.
  • Widgets (WP27): preserve polled values across parent wrapper rerenders, reconcile only changed widget props, and resolve semantic stat colors through theme classes while retaining raw CSS colors.
  • Editors (WP26b): text masks consume literal mask positions correctly, Markdown fields link labels and validation descriptions, and CodeMirror syncs external values with readOnly/disabled semantics.
  • Editors (WP26b): reconfigure CodeMirror access state in place so disabled, loading, and read-only toggles retain the view, selection, focus, and undo history.
  • Theme (WP33): density uses scoped minimum sizes that preserve checkbox/radio and icon controls; mobile sidebars regain an accessible navigation toggle.
  • Table interaction (WP29b): editable cells reset drafts and restore focus, preserve component identity through optimistic updates, and page-window reordering preserves global offsets.
  • WizardBuilder (WP26a): render step icons/descriptions, keep Next mounted, and use the valid aria-current=step variant.
  • Import/export (WP30): import dialogs use Base UI modal focus handling, maintain popup/backdrop stacking, and announce progress/results; image crop dialogs use theme tokens.
  • Import/export (WP30): queued imports can be dismissed with Cancel or Escape, abort client polling, and toast that the import continues in the background.
  • Widgets (WP27): charts include series legends and distinct default colors.

Unreleased (0.2.0-beta, milestone M2)

Added

  • AuthShell, TenantSwitcher, NotificationTray, GlobalSearch (cmdk, configurable key binding), UserMenu (custom items, profile, logout) and ShellSidebar (collapsible groups persisted per panel, badges, dark logo); Shell composes them.
  • Select async mode: remote search with paging, selected-label hydration, create-option modal, prefix/suffix actions.
  • Panel theme variables (--primary, --radius, custom vars) are applied to the document root so portaled overlays inherit them.

Unreleased (0.1.0-beta, milestone M1)

Added

  • Default implementations of every M1 catalog component on Base UI + Tailwind v4: inputs (text, textarea, number, select, checkbox(list), radio, toggle(s), date/time, color, tags, key-value, file upload, lazily loaded Tiptap rich editor), layouts (form, grid, section, fieldset, tabs, group, split, placeholder, html, actions), domain components (DataTable with URL-driven sort/search/filters/tabs/pagination, row/bulk actions; Repeater), infolist entries and the shell (Shell, Toaster, Modal, ConfirmDialog, NotFound).
  • live({ onBlur, debounce }) support in text-like inputs, theme CSS variables and dark mode on the document root, favicon, action key bindings for header and bulk actions.
  • styles.css for hosts (import after Tailwind), components registry map, Playwright component tests for every catalog fixture.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close