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
Changelog
Unreleased
- Add opt-in
--with-ui(createPlugin({ withUi: true })) scaffolding: shared catalog extension, typed server field factory, accessible React component, published././catalog/./reactsubpaths 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
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, sot('Title') === 'Title'is false. Resolve withresolveTranslations()/stripTranslationMarkers()before strict comparison, string transformation or delivery outside Pupitre responses, and usecaptureTranslation()/restoreTranslation()to persist translation intent.Error.message, logs and regex/includeschecks 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 anddefinePlugin({ 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 optionallocaleusers-column migration stub;localeis 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_KEYrotation);tools/i18n-check(npm run i18n:check) enforces Indonesian coverage of every literalt()key in CI. - Catalog extensions:
defineCatalogExtension()/mergeCatalogExtensions()in@rikology/pupitre/cataloganddefineField(),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 })andpage.catalogExtensionsmetadata. Elementtypein specs is now an open string validated by the client registry. ADR-0024. - Tables:
table.filter.queryBuilder(name).constraints([...])withqb.text/number/date/boolean/select/relationshipconstraints (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 URLsort=title,-pricewith deterministic qualified primary-key tiebreakers; existing single-sort links keep working andsort=clears defaults. - Validation: public
compileValidator()/FormValidatorso 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 bypupitre:inspect. - Panels:
.environmentIndicator({ visible?, label?, color? })shares anenvironmentprop (shown outside production by default);.impersonation({ canImpersonate?, canBeImpersonated?, redirectTo? })withactions.impersonate()(fail-closedimpersonatepolicy ability, scoped tenant lookup, actor MFA required, rotated sessions, leave endpoint, profile security mutations blocked while impersonating,pupitre:impersonation:started|stoppedevents);.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-protectedloginHistoryWidget();pupitre:pruneapplies session/history retention. Migration stubs 1791700000045/46. - Navigation:
defineCluster(slug)with.label/.icon/.navigationGroup/.sort/.subNavigationPosition,resource.cluster()andpage.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()andt.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-closedmanagePublicViewsfor 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()andtextEntry.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 helperscreatePupitreStorage,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 itsnot— instead of spreadingnot: trueonto non-condition values (which produced an invalid{"not":true}spec, e.g. the media resource’s hiddenpathupload). 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):
RepeaterandBuilderaccept the common field props; the schema compiler now emitsname,label,helperText,hint,requiredandcolumnSpanfor 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.descriptionis now forwarded. Camel-case built-in labels are split into words (“Force Delete”, not “ForceDelete”).
Added
- Shield (WP37): official
@rikology/pupitre-shieldplugin 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;--forceoverwrites, while--package [--dir]executes the standalone scaffolder pinned to the core major without a shell. - Registry (WP40):
pupitre:ui addvalidates names and runs shadcn@4. Explicit root-relative file targets preserve the Inertia source tree; unsafe flattening--pathpassthrough 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, andboot({ 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
auditadapter configuration and lazydrivers/auditreader 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/AuditFilterscontract; 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
tmpdeclarations 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:parentIdonly when the route carries it. - Release 1.0 (WP42):
@rikology/pupitre,-reactand-uimove lockstep to1.0.0; official plugins andcreate-pupitre-pluginstart at1.0.0; inter-package peer ranges are^1.0.0;catalogVersionis1.0(clients on a different major reload once); npm dist-taglatestvia the newrelease.ymltrusted-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
StatsOverviewWidgetparity); an explicit.span()before or after.stats()still wins. - Passkeys: deleting your own passkey from the profile no longer fails with “
limithas no effect on adeletestatement”. The ownership lookup and the delete now use separate query builders. - Option labels:
table.text()/table.badge()columns andtextEntry/badgeEntryentries accept.options(...)(same shapes as select options) and display the label of the stored enum value instead of the raw value (Published, notpublished). 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.resourcescaffolding emits the options for enum columns.BadgeEntryBuilderis exported and typescolors().
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
viewAnybefore 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 withcreate. - Tenancy: host
.scope(),table.query(), custom filter/tab and relationship scope callbacks are applied as one AND-groupedWHEREclause, so a top-levelorWhere,orHas/orDoesntHaveor an OR inside a model scope (withScopes/apply) can no longer widen results past the tenant predicate. Non-WHEREcalls (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
beforeSavein 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-Urlis validated by one helper for resource, relation-manager, page and export requests; relation managers previously used it unvalidated, and/\hostforms are now rejected everywhere. - Records: identifiers that PostgreSQL cannot parse for the primary-key type (e.g.
/activity/createon 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_uploadstable 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=24hremoves 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). Runnode ace pupitre:installandnode ace migration:runto 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
existsround-trip per cell. - Tests: the PostgreSQL suite passes (relation fixtures no longer replay DDL); c8 enforces 85% line coverage on
src/compiler,src/queryandsrc/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:widgetgeneration. - Testing (WP34): completed
@rikology/pupitre/testingwith 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(), andmake: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, immutableexportColumn()formatting, prefix-aware filtered exports, schema-derived header mapping (including Skip), per-row VineJS validation/upsert transactions and failed-row CSVs. AddedmaxSyncRowshand-off, chunked scoped export jobs, boot-registered serializable import/bulk/export handlers, nested parent authorization/ownership,pupitre_exportsprogress/download lifecycle, private storage, notifications and pruning. - MfaPasskeys (WP32a): added the
completeLoginMFA 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
afterStateUpdatedform closures, guarded tenant-scoped evaluation endpoint, and completepupitre:inspectclosure/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 synchronizedhasManyrepeaters, 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:
catalogVersionis now0.3(new catalog components, descriptor fields, page props and endpoints listed above); packages released lockstep as0.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 asreplicate → createapply 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
.datapaths 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 newnav.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/limiterwhen configured.MailAdapter+@rikology/pupitre/drivers/mailover@adonisjs/mail. Migration stubs published byconfigure. - Navigation & panels (WP18): permission-filtered nav (
viewAny, pagecanAccess), boot-time validation of nav slugs, multi-panel support,make:panel. - Authorization (WP19): full Bouncer mapping incl.
authorizeModel()for related models, self-handledE_PUPITRE_FORBIDDEN(JSON 403 / Inertia redirect back + flash),pupitre:inspect [--json]reporting missing policies/abilities. - Tenancy (WP20):
TenancyAdapter, defaulttenant_idcolumn driver,@rikology/pupitre/drivers/tenantover@rikology/adonisjs-tenant, tenant-prefixed routes (modeurl) or user current tenant (modeimplicit), switch endpoint, automatic ownership fill, resource.scope(false); every resource query goes throughscopedQuery(). - Notifications (WP21):
NotificationsAdapter,@rikology/pupitre/drivers/notificationsover@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 inindexAdmin(),make:page. make:resource(WP24): Lucid model introspection (columns, types, enum meta, relations) or--from-migration.- Dynamic options (WP25): select
options(closure)(withvaluesfor 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.panelgainsprofileUrl,userMenu,search;pupitre.tenant/tenants(TenantOption),pupitre.notifications({ unread, url, readUrl, interval }),navGroup.collapsed,brand.logoDark; new response schemas for search, notifications and options.catalogVersionis now0.2. page.keyincludes 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),defineResourcewith model-typed form/infolist schema, table, actions, pages and lifecycle hooks (fill,mutateBeforeCreate/Save,before/after Create/Save/Delete,create/updateoverrides; hooks receive the Lucid transaction afterctx),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 andassertRegistryComplete.- Compilers: schema → json-render spec with closure lifting (expressions/
$computed, static fallback,E_PUPITRE_UNLIFTABLE_CLOSURE), record/user-independent spec cache, VineJS validator (onevine.compileper definition, nested repeater paths, model-awareunique, 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-leveldatafor tables, JSONActionResultprotocol for actions, Bouncer mapping (fail-closed when a policy exists without Bouncer), uploads (/_pupitre/uploadwith 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
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
activityicon and clarify that custom audit adapters provide timelines, not Activity resource queries.
pupitre-backup
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:runcommand. - Document administrator-only access and manual restoration procedures; no web restoration endpoint is exposed.
pupitre-calendar
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
@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
@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
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
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
Changelog
Unreleased (1.1.0)
Added
- Toasts: optional
<Pupitre toastDuration={5_000}>auto-dismiss delay in milliseconds; default0retains manual dismissal, with per-toast deadlines preserved across navigation. - Localization: client
t(key, params?)resolves synchronously from the compactpupitre.messagesshared 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:
useTablesupports 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 andWidgetRequestError; component widgets receive unwrapped loader data. - Tables:
useTableViews(descriptor, onColumns)lists, applies (prefix-preserving Inertia visits) and mutates saved views. - Shell: optional
environment,impersonationand 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
uploadOwnerinstead of deriving resource context from the action name.
Unreleased (1.0.0, milestone M4)
Added
- Realtime (WP39): optional
Pupitre/PupitreShellclient context, SSR-safeuseRealtimeSubscriptionwith stable callbacks and cleanup, coalesced descriptor-scopeduseTableRealtime, 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 callswindow.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):
useTablereads descriptor-selected top-level data props and namespaces table query keys while preserving list-pagedatabehavior. - 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
evaluateactions 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
useTableURL-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 registryAuthShell;kind: 'custom'pages and host pages can keep the panel shell throughPupitreShell.ShellPropsgainstenant,tenantsandnotifications;ShellRegistrygainsAuthShell.- Hooks:
useOptions(debounced, abortable, paged dynamic select options),useNotificationsPolling(pauses while the tab is hidden); sharedjsonFetch/xsrfTokenbridge helpers.
Unreleased (0.1.0-beta, milestone M1)
Added
PupitreInertia page,PupitreRenderer(store reset only onpage.keychange; reloads refresh/errors,/meta/record,/options),SpecRendererfor nested specs,defineRegistry(catalog + shell components, built-in$computedfunctions,$rawdirective).- Inertia bridge: form submission with 422 →
/errors/**, action protocol (link/direct/confirm/form) over JSONfetchwithX-Pupitre-Action,X-Pupitre-Url, XSRF header and descriptor HTTP method,ActionResulthandling (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 seamssetRouter,setFetch.
pupitre-settings
@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
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
shieldCanpage authorization andshieldRolesFieldUser-resource relationship helper. - Add configure migration publishing and lazy Ace command registration;
shield:syncidempotently 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
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:notificationandpupitre:tablewire pings.
pupitre-ui
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/infotokens with derived-soft/-inkvariants (mixed in oklab so atheme.primaryoverride re-skins every state),--input-strongfor checkbox/radio/switch boundaries, chart palette, elevation and density (--control-h,--row-py) tokens. See the rootDESIGN.md. - Primitives (M4):
Buttonnow takesvariant(solid/outline/ghost/link) ×color(primary/gray/success/warning/danger/info) ×size(xs–lg) plussquareandloading; the olddefault/secondary/outline/destructivevariants 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 newCollapseprimitive, 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/*.jsonpayloads and index, CI drift check, and copy-ability tests. - Audit (WP38):
Widget.Timelinecatalog renderer and exportedWidgetTimelinedisplay server-formatted activity times, actors, icons/colors, structured field changes, and empty states;WidgetGridsupports 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 withtailwind-merge, so a component’sclassNamereliably overrides primitive defaults instead of depending on CSS order. Template-literal class concatenations with conflicts now go throughcn(). - Selects (M4): table filters, grouping, rows-per-page, editable cells, widget filters and import mapping use the new
SelectMenuprimitive, the same Base UI trigger and listbox as formSelect, 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-selectremains forSelect native, drawing thechevrons-up-downicon 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-thumbtoken) 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
dirtyGuardprompt (“Discard unsaved changes?”, Stay / Leave page) now renders the registryConfirmDialog, the same dialog as action confirmations such as Delete, instead ofwindow.confirm; passkey deletion inProfileSecurityusesConfirmDialogtoo. 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.csspoints every border at--borderin the base layer; bareborderutilities 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
NavLinkprimitive) 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/testingPlaywright fixture and reusableAdminPage; 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):
pupitreTesttakes apupitre: { panelPath, basePath?, landingPath?, email?, password? }fixture option (set it inplaywright.config.tsuse);AdminPageno 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.Tablenow delegates to the sharedDataTableinstead 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=stepvariant. - 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) andShellSidebar(collapsible groups persisted per panel, badges, dark logo);Shellcomposes them.Selectasync 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 (
DataTablewith 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.cssfor hosts (import after Tailwind),componentsregistry map, Playwright component tests for every catalog fixture.