Skip to content

Releases: teylabs/kit

v0.7.8

Choose a tag to compare

@jaspertey jaspertey released this 02 Nov 13:44
d311cad

Added

  • docs/upgrading-0.5-to-0.7.md: one pass from v0.5.117 to v0.7.7, now that 0.5.x is frozen. It covers the order of the --lineage runs, the 0.6.0 ownership moves, and a checklist of the Upgrading steps that need a person.

Upgrading

  • Run kit:update plan|apply for core (re-pinned sha256:dc02fc76…f634 → sha256:71423bf5…6132): Kit/Layout/StickyBar.vue takes the fix below. Ported from 0.5.x, which is frozen.

Fixed

  • A request no longer queries the database schema to learn whether the workspaces table has admitted and deleting_at. The kit asked once per request, since PHP-FPM drops static memos between requests, and asked again each time a column was missing. A column found is now cached (kit-workspace-columns, in the default cache store) until the next migrate forgets it. One not found yet is still asked again, so a table migrated later is seen at once.
  • A StickyBar at the viewport's foot counts as pinned, however it got there. Until now a bar counted as pinned only when it was scrolled to the foot or held there with pinned. A host that lays the Workbench out itself (:fill="false" in its own full-height column) put the actions bar at the foot without either, so bottom toasts weren't lifted above it. A bar whose bottom is within 12px of the viewport's (room for its container's gap) now counts as pinned, re-checked on scroll, window resize and layout resize.
  • auth.passkeys counts the kit's own passkey offer. The shell's pages/Workspace/Layout.vue renders <PasskeyOffer /> on every signed-in page through @kit/ui, but the row counted only a host import of it, so an app using the kit shell read missing. The row now also counts the installed Layout; importing the offer again in the host would show it twice.

What's Changed

Full Changelog: v0.7.7...v0.7.8

v0.7.7

Choose a tag to compare

@jaspertey jaspertey released this 02 Nov 13:27
d4d82d1

Upgrading

  • Run kit:update plan|apply for core (re-pinned sha256:e238b630…7408 → sha256:dc02fc76…f634) and shell (sha256:93780271…035d → sha256:c7036fbf…8b21). The breadcrumb trail moves from the shell's row into <Page>, above the heading (see below). Three things for an app:
    • Pages without <Page> lose their trail, because the shell no longer draws one. Render <Breadcrumbs /> (from @kit/ui) above the page's own heading.
    • An edited Partials/Breadcrumbs.vue conflicts on update. The shell's copy is now a thin wrapper around core's Breadcrumbs, kept for pages that import it; the trail itself is Kit/Layout/Breadcrumbs.vue, which you edit or replace through @kit/ui.
    • Headings move up. The 40px row under the header is gone, and a 20px trail line plus an 8px gap sits above the heading. So every page's H1 is about 30px higher than before, and at the same height on every page.
  • Run kit:update plan|apply for session-ui (re-pinned sha256:c5b8919e…e94e → sha256:d96afd41…1b7f) and workspace-ui (re-pinned sha256:5d746402…9202 → sha256:749a17ac…5112), then php artisan migrate: the new 0001_01_02_000505_add_finished_at_to_kit_account_deletions_table migration, and Workspace/Settings/Delete/Page.vue and Workspace/Account/Delete/Page.vue, which show the password field while confirms_password is true. A host that edited those pages adds the field itself. Clients that call DELETE /@{workspace}/settings/delete must now send password, unless kit.lifecycle.confirm_password is false.
  • Run kit:update plan|apply for access (re-pinned sha256:a179066f…6d98 → sha256:98af2e26…2de6) and settings (re-pinned sha256:fb83eb1d…5f1e → sha256:37ad7378…6eba): their 000300 and 000400 migrations now adopt a compatible host table in place. A host that has already run them is unaffected (migrate doesn't re-run them). A host with its own entitlements, complimentary_grants or spatie settings table should run kit:adopt --json (or kit:add's plan) first: an incompatible table now blocks with its columns named instead of failing in migrate.
  • Run kit:update plan|apply for core (re-pinned sha256:6e87fc26…fe56 → sha256:e67b1978…efa5): kit.css takes the change below. A host that defined its own --container-2k can drop it; its own value still wins if kept.
  • Run kit:update plan|apply for core (re-pinned sha256:b7a9b9ba…5e9d → sha256:6e87fc26…fe56): Kit/Form/Slider.vue takes the fix below.
  • No feature is re-pinned: the mail views and account mail ship in the package. To show the brand's lockup in mail, add a PNG of it for a light background (2x the 220px it shows at, or more) and set KIT_BRAND_MAIL_LOCKUP (kit.brand.mail_lockup) to its path in public/ or an absolute file path. Without it, the mail header is the product's name, centred.
  • A host that published its own resources/views/vendor/mail keeps its views. To leave the manage-notifications link off security mail there too, give its message and footer views the manage prop (see docs/mail.md › Account mail).
  • A host that set its own ResetPassword::toMailUsing() or VerifyEmail::toMailUsing() keeps it, and doesn't get the kit's account mail.
  • Run kit:update plan|apply for workspaces (re-pinned sha256:40fdd76c…81c4 → sha256:56912cc3…eafd) and session-ui (re-pinned sha256:abbeb463…c64f → sha256:a4581c16…377d), then php artisan migrate: the new 0001_01_02_000210_add_deleting_at_to_kit_workspaces_table (nullable deleting_at and deleting_by on workspaces, recorded as kit-added on an adopted table) and 0001_01_02_000504_create_kit_account_deletions_table migrations below. Existing workspaces stay open. Nothing changes for a host whose purge handler doesn't opt into deferral.
  • Run kit:update plan|apply for session-ui (re-pinned sha256:a4581c16…377d → sha256:c5b8919e…f94e): 0001_01_02_000501_add_user_foreign_key_to_kit_security_events_table.php and 0001_01_02_000503_add_user_foreign_key_to_kit_user_state_table.php take the type fix below. It's a type-only change, so there's nothing to migrate: a host that already ran them keeps its schema, and kit:update plan's note that a changed migration won't reach the database needs no action here.
  • Run kit:update plan|apply for workspace-ui (re-pinned sha256:2f226d38…39e7 → sha256:5d746402…9202): Workspace/Placeholder/Page.vue, Workspace/Settings/Placeholder/Page.vue and Workspace/Settings/People/Page.vue take the type fix below. No behaviour changes.
  • The capability fix below is in the package and re-pins nothing. A host that adopted settings only so People would open, or so owners could manage others' MCP connections, no longer needs it for that. Keep it if you use its General section or its MCP settings tools, and for the AI assistants page while kit.mcp.connection_ability is the default settings.read (kit:status flags this).
  • The kit now refuses to boot when an ability is gated on a capability no adopted feature declares, naming the ability, the capability and the feature. A host whose own provider calls Registry::abilities() keeps booting: the check covers what the kit's features contribute.
  • kit:status now flags declared permissions missing from the database as a blocking step. php artisan migrate --force puts them back.
  • Run kit:update plan|apply for core (re-pinned sha256:e67b1978…efa5 → sha256:145cf85d…412e), mcp (re-pinned sha256:e35e2879…7ae0 → sha256:760d2e0a…d924), workspace-ui (re-pinned sha256:5d746402…9202 → sha256:0e1a0d0e…3320) and notes (re-pinned sha256:1a0455e7…551b → sha256:cc6e2adb…bb95): DataTable.vue, ActivityEntryRow.vue, the Connections, People and Security pages and NoteCard.vue take the table and date fixes below. A host that copies types/kit.d.ts re-copies it: TableColumn has date. Nothing to migrate.

Tests

  • The resources check (composer test:resources) no longer copies vendor, node_modules, browser or typecheck into each disposable package copy. It reads only src and resources. All 15 tamper cases still run, and the check goes from about 50s to about 6s locally. CI gives each test step its own timeout-minutes (4 for the composer checks, 6 for the browser run), so a hang fails its own step quickly instead of holding the job until its 10 to 15 minute limit.
  • Run kit:update plan|apply for session-ui (re-pinned sha256:c5b8919e…f94e → sha256:fae5ea29…bcb6): this adds Auth/Register/Page.vue; hosts with their own register page can drop it. Auth/Login/Page.vue takes the "Create an account" link below. A host that copies types/kit.d.ts re-copies it: LoginViewModel has register, and RegisterViewModel is new. Registration stays closed by default, so nothing changes until an app opens it.

Added

  • Deleting a workspace asks for your password (docs/lifecycle-events.md › Delete a workspace). Settings › Delete workspace now requires the current password with the typed name. A missing or wrong one is a 422 and nothing changes. Account deletion already asked for it. Both are on by default. kit.lifecycle.confirm_password (KIT_DELETE_CONFIRM_PASSWORD) turns them off, but only when it is false exactly: any other value keeps the password required.

  • A host can keep the user row when an account is deleted (docs/lifecycle-events.md › Keeping the user row). Bind Tey\Kit\Features\SessionUi\Contracts\DeletesAccountRow::delete(Model $user) to anonymise the row instead of deleting it, for tables that keep history on RESTRICT foreign keys to users. Unbound, the user is hard-deleted as before. After the kit's rows go, an anonymised row keeps its Concierge account deactivated, so it never signs in. Its kit_account_deletions record is marked finished_at, and FinishAccountDeletion returns false on a retry instead of failing.

  • Adopt-in-place for the access and settings tables. A host's own entitlements and complimentary_grants tables, and spatie/laravel-settings' table, are adopted when compatible (the columns the kit reads and writes in a compatible type, nullable where the kit writes null, an auto-incrementing id, the kit's unique index, and no other required host column on a table the kit inserts into): nothing is created or changed on them, and each is recorded in kit_adoptions. Settings then creates only settings_intents. Plans list each under adoptions, and kit:adopt --json with no feature lists each such table under tables as adopted, adoptable or conflict. See Adopting existing access and settings tables. composer test:table-adopt covers it on SQLite and PostgreSQL.

  • max-w-2k (160rem), the wide page measure the apps share, as a --container-2k token in kit.css under @theme default, so a host's own value wins.

  • /mcp storm guards (kit.mcp.limits, kit.mcp.list_cache_seconds). An assistant that reconnects in a burst no longer fills every PHP worker. Each access token may have 4 requests in flight and 120 a minute; over either, the request gets a 429 with Retry-After (a JSON-RPC error body) before it does any work. An app-wide in-flight cap (total) is off by default: set it below your PHP worker count. Slots live in the cache store and free when the request ends, or after hold_seconds if the worker died. tools/list answers are reused per connection for 60 seconds; a call still checks its tool's gate, so a list stale by a permission change only lists. A host that published config/kit.php gets these defaults without adding the keys. composer test:mcp-limits covers both.

  • Deferred workspace and account deletion ([docs/lifecycle-events.md › Deferred deletion](docs/lifecycle-events.md#deferred-d...

Read more

v0.5.117

Choose a tag to compare

@jaspertey jaspertey released this 02 Nov 13:15
4348dc2

Upgrading

  • Run kit:update plan|apply for core (re-pinned sha256:727e9746…192e → sha256:b4ae55de…cb8b): Kit/Form/submitForm.ts, Kit/Form/formFrame.ts, Kit/Form/FormActions.vue, Kit/Workbench/Workbench.vue and Kit/Workbench/workbench.ts take the Save gate and the status fix below, and the kit's ui lang file gains form.submit_blocked.*. A host that disabled Save by hand during an upload, or watched its form to clear "Not saved", can drop that.

Added

  • Save is disabled while it can't succeed (docs/submit-form.md › When Save is disabled). FormActions and a Workbench with :form disable Save, and ignore Enter and ⌘S, while pending work is in flight (an upload), a 409 stale-write conflict is open, or the host's canSubmit is false. canSubmit is a new prop on both (can-submit) and a useSubmitForm option, a boolean, Ref or getter, true by default; while it's false submit() resolves false without calling the handler. The button carries data-blocked and an aria-describedby with the reason, from kit::ui form.submit_blocked.* ("Waiting for uploads to finish"). submitBlock() and submitBlockMessages() are exported. tests/js/submit-form.test.mjs and workbench-form.test.mjs cover each reason; the browser job checks a pending upload, can-submit false and an open 409 on the Workbench.

Fixed

  • A failed save no longer stays "Not saved" after further edits (docs/workbench.md › Form frame). A Workbench with :form whose form was already dirty kept "Not saved" through every later edit. The next edit now puts the bar back to "Unsaved changes" (editedSaveState), for a useSubmitForm and a useForm alike. The browser job checks both.
  • Run kit:update plan|apply for core (re-pinned sha256:727e9746…192e → sha256:4be76339…aa64), mcp (re-pinned sha256:e35e2879…7ae0 → sha256:760d2e0a…d924), workspace-ui (re-pinned sha256:bb66501a…3253 → sha256:429f11b2…67b5) and notes (re-pinned sha256:1a0455e7…551b → sha256:cc6e2adb…bb95): DataTable.vue, ActivityEntryRow.vue, the Connections, People and Security pages and NoteCard.vue take the table and date fixes below. A host that copies types/kit.d.ts re-copies it: TableColumn has date. Nothing to migrate.

Fixed

  • DataTable sits on the card surface. The table and its two empty states had no background, so rows showed the page ground. They now use bg-card text-card-foreground, the sticky first column matches, and column headers no longer wrap ("Connected by").
  • Dates show through DateTime, not as raw ISO values. A TableColumn (and TableQuery::column()) takes date, a DateTime format, and DataTable renders that column through DateTime: the viewer's zone, the full value in title. AI assistants' Connected (a raw 2026-…T…+00:00) and Expires, People's joined, sent and expiry dates, Security's sessions and log, Notes and activity entries now all use it.
  • Removing one MCP grant is no longer styled destructive. On AI assistants only Revoke stays destructive; "Remove " is a plain row action.
  • Avatar upload and removal work on PostgreSQL with string or uuid morphs. Avatars compared media.model_id (varchar there) with an integer, a 500 ("operator does not exist: character varying = integer"). The lookups and the avatar holder's media relation now bind the id as a string.

What's Changed

  • DataTable card surface, date columns, nowrap headers; plain grant removal; string avatar model_id (0.5.x) by @jaspertey in #516
  • Disable form Save while it can't succeed; clear Not saved on the next edit (0.5.x) by @jaspertey in #502
  • Release v0.5.117 by @jaspertey in #529

Full Changelog: v0.5.116...v0.5.117

v0.5.116

Choose a tag to compare

@jaspertey jaspertey released this 02 Nov 12:24
abc849d

Fixed

  • A request no longer queries the database schema to learn whether the workspaces table has admitted and deleting_at. The kit asked once per request, since PHP-FPM drops static memos between requests, and asked again each time a column was missing. A column found is now cached (kit-workspace-columns, in the default cache store) until the next migrate forgets it. One not found yet is still asked again, so a table migrated later is seen at once.

What's Changed

Full Changelog: v0.5.115...v0.5.116

v0.5.115

Choose a tag to compare

@jaspertey jaspertey released this 02 Nov 01:54
2366574

Added

  • /mcp storm guards (kit.mcp.limits, kit.mcp.list_cache_seconds). An assistant that reconnects in a burst no longer fills every PHP worker. Each access token may have 4 requests in flight and 120 a minute; over either, the request gets a 429 with Retry-After (a JSON-RPC error body) before it does any work. An app-wide in-flight cap (total) is off by default: set it below your PHP worker count. Slots live in the cache store and free when the request ends, or after hold_seconds if the worker died. tools/list answers are reused per connection for 60 seconds; a call still checks its tool's gate, so a list stale by a permission change only lists. A host that published config/kit.php gets these defaults without adding the keys. composer test:mcp-limits covers both.

What's Changed

Full Changelog: v0.5.114...v0.5.115

v0.7.6

Choose a tag to compare

@jaspertey jaspertey released this 30 Oct 16:19
be8c67f

Upgrading

  • Run kit:update plan|apply for core (re-pinned sha256:5b4af9a6…b7df → sha256:b7a9b9ba…5e9d): this adds Kit/Form/pendingWork.ts, and Kit/Form/formFrame.ts, Kit/Form/FormActions.vue, Kit/Form/BulkDrop.vue, Kit/Workbench/Workbench.vue and Kit/index.ts take the leave-guard changes below. A host with a hand-rolled Back/Forward or pending-upload guard beside a kit form frame can drop it, or keep it with warnOnLeave: false.

Added

  • Pending work counts as unsaved (docs/submit-form.md › Pending work). usePendingWork() is the page's registry of work the form doesn't see yet: track(promise) holds it until it settles, begin() returns an idempotent end(), and pending and count are readable. While any is registered, the leave guard of FormActions, FormCard and a Workbench with :form treats the page as unsaved, even with a clean form. BulkDrop's direct uploads register themselves. createPendingWork() makes a separate registry. Both are exported from the kit barrel. tests/js/pending-work.test.mjs covers the registry.

Fixed

  • The leave guard holds Back and Forward (docs/submit-form.md › Leaving the page). It caught only Inertia GET visits and closing the tab, so Back from a dirty form left it without asking. Where the browser has the Navigation API, a same-document traverse is now cancelled and asked about, and made again on Leave. Elsewhere the guard keeps one extra history entry for the page while it's unsaved, so Back lands on the page itself and asks; the entry is taken off when the page is clean, when a save starts and before a confirmed visit, so no duplicate entry is left behind. Stay keeps the page and its edits; Leave always goes.
  • A save's own follow-up visit is no longer held. The guard let a visit through only in the same task as the form's submit event, so a router.visit() in onSuccess, where Inertia's useForm() is still dirty, asked "Leave this page?" after a successful save. The guard is now saving from the submit (or processing turning on) until processing is off and every visit started meanwhile has finished. The browser job checks all of this on the real @inertiajs/vue3 (leave.html): Back dirty and clean, with and without window.navigation, an upload in flight, and a save whose onSuccess visits another page.

What's Changed

Full Changelog: v0.7.5...v0.7.6

v0.7.5

Choose a tag to compare

@jaspertey jaspertey released this 30 Oct 16:02
e4359b3

Upgrading

  • Run kit:update plan|apply for core (re-pinned sha256:9a290690…555b → sha256:bda1ecaa…c43e) and session-ui (sha256:c59375ef…dd74 → sha256:287b46fe…d2ed): Kit/Form/PasswordInput.vue follows its form's reset, and Auth/Login, Auth/ForgotPassword, Auth/ResetPassword, Auth/ConfirmPassword and Auth/VerifyEmail take the two fixes below.
  • tey/standards ^0.11.54 is required. Earlier releases reject the adoption inventory's new features.<id>.composer key (exit 2), so kit raises its floor with it.
  • Run kit:update plan|apply for core (re-pinned sha256:bda1ecaa…c43e → sha256:87f1548e…8cde): Kit/Form/submitForm.ts adds httpSubmit, and Kit/index.ts exports it. A handler that awaits Inertia's useHttp directly should move to httpSubmit (below).
  • Run kit:update plan|apply for core (re-pinned sha256:87f1548e…8cde → sha256:5b4af9a6…b7df): Kit/Form/Slider.vue is new, Kit/Form/RadioCardGroup.vue gains the option slot, image tiles and columns, and Kit/contract.ts and Kit/index.ts export them. RadioCardOption now needs a label or an image (with alt); an option with a label is unchanged.
  • Run kit:update plan|apply for session-ui (re-pinned sha256:287b46fe…d2ed → sha256:abbeb463…c64f): Auth/Login, Auth/ForgotPassword, Auth/ResetPassword, Auth/ConfirmPassword and Auth/VerifyEmail take the type fix below. No behaviour changes.

Fixed

  • A rate-limited auth submit no longer opens Inertia's error modal. The guest auth pages handed their 429 handler to <Form> as @http-exception, but <Form> before Inertia 3.8 has no onHttpException prop: the listener was never called, so Laravel's 429 page (from throttle:) still opened in Inertia's modal. The pages now pass it as the visit option (:options="{ onHttpException }"), which every Inertia 3 <Form> honours, and the in-card "Too many requests" alert shows. The browser checks now run these pages on the real @inertiajs/vue3 (3.7.1) and shadcn-vue Input, answering each submit with Laravel's 429 HTML or an Inertia response with errors.
  • A failed sign-in, reset or confirmation really clears the password. reset-on-error resets the input in the DOM only, and PasswordInput kept its own value, so the next re-render (showing the password, an error appearing) wrote the old password back. The pages now hold their password fields (v-model) and clear them on an error or a 429, and PasswordInput follows its form's reset event (a native reset, or Inertia's whole-form reset), so a host form's reset clears it too.
  • The adoption inventory lists each feature's composer dependencies. resources/adoption/surfaces.json features.<id>.composer now carries every feature's feature.json requires.packages keys (less php and laravel/framework), and the schema allows the key. tey/standards read spatie/laravel-multitenancy in an app's composer.json as a hand-rolled workspaces.tenancy signal, because kit carries it only in require-dev; tey/standards 0.11.54 exempts a package an installed feature declares here. The drift test rebuilds the list from the feature.json files.
  • A useSubmitForm handler over Inertia's useHttp no longer reads a 422 as a success. useHttp doesn't reject on a validation failure: it calls onError and resolves undefined, so a handler that awaited it returned normally and the form showed Saved for a refused save. The docs said a useHttp error is read from what it throws, which is only true of the other statuses; they now say to use httpSubmit (docs/submit-form.md › With Inertia's useHttp).
  • The guest auth pages type-check on every Inertia 3. They bound :options="{ onHttpException: tooMany }" inline, and <Form>'s options type (FormComponentOptions, a Pick of VisitOptions without onHttpException) failed vue-tsc's excess-property check (TS2353) in a host. Each page now types the handler as the visit's onHttpException and widens it to <Form>'s options type (:options="formOptions"), both read off @inertiajs/vue3's own Form and router.visit, so no stub imports @inertiajs/core; at runtime <Form> still spreads it into the visit. The components type-check (bin/typecheck-components.sh, CI's vue-tsc job) now covers these five pages and runs a second time against @inertiajs/vue3 3.7.1 (installed as @inertiajs/vue3-37, typecheck/tsconfig.inertia-3.7.json) as well as the pinned current version.

Added

  • httpSubmit((data, hooks) => request), a useSubmitForm handler over a useHttp request: pass it the hooks it gives you (http.post(url, hooks)). A 422 rejects with a SubmitError of the field errors; a 409 stale_write rejects as the conflict (form.conflict); any other status, a network failure, and a resolve of undefined with no onError reject with the generic message. Only a 2xx resolves, with the response data. Exported from the kit barrel. node --test tests/js/submit-form.test.mjs covers each outcome over a stand-in for useHttp 3.9.1, including a bare useHttp handler that reads a 422 as success.
  • <RadioCardGroup> tiles (docs/radio-card.md). The option slot ({ option, checked, disabled }) replaces a card's body while the native radio, its label and the focus ring stay the kit's. An option with image: { src, alt } is an image tile: a lazy, async image in a square object-cover box, so ~100 tiles load without layout shift, named by its label or its image's alt. A checked image or slot tile shows the primary ring and an aria-hidden check mark, so the state isn't colour alone. columns (2, 3, 4, 6 or auto, at least 7rem a tile) lays the cards in a grid. A disabled option stays a native disabled radio the arrow keys skip.
  • <Slider> (docs/slider.md), a native range input: v-model number, min, max, step, disabled, invalid, valueText for aria-valuetext, input emitted live and change on release, and show-value for a visible <output> linked by for. Caller attributes land on the input; in a Field it is described by the Field's description and error; in a locked LockedForm it is read-only.
  • The adoption inventory counts Slider under form.inputs, and a raw <input type="range"> in a Vue or Blade template as hand-rolled (it also marks the app has-forms).

What's Changed

  • Keep a 429 out of Inertia's modal and clear auth passwords for real by @jaspertey in #478
  • List each feature's composer dependencies in the adoption inventory by @jaspertey in #480
  • Fail useSubmitForm on a useHttp 422 with httpSubmit by @jaspertey in #483
  • Add RadioCardGroup option slot, image tiles and columns, and a Slider by @jaspertey in #439
  • Type the auth pages' onHttpException for every Inertia 3 by @jaspertey in #485
  • Release v0.7.5 by @jaspertey in #488

Full Changelog: v0.7.4...v0.7.5

v0.7.4

Choose a tag to compare

@jaspertey jaspertey released this 30 Oct 13:48
6dfc0b3

Upgrading

  • Run kit:update plan|apply for session-ui (re-pinned sha256:592e21c4…7353 → sha256:c59375ef…dd74): Auth/Login, Auth/ForgotPassword, Auth/ResetPassword, Auth/ConfirmPassword and Auth/VerifyEmail take the auth-state fixes below. ResetPasswordViewModel gains status; a host that hand-copies types/kit.d.ts copies it again. A host overriding lang/vendor/kit/{locale}/ui.php can add form.throttled and form.throttled_retry.
  • kit.people.hide_grants needs tey/concierge 0.9.3 or later, for MembersIndex::scopeMembersUsing(). With no hidden grants nothing changes on any Concierge the workspaces feature allows (^0.8.4 || ^0.9); with some on an older one, the app refuses to boot ("kit.people.hide_grants needs tey/concierge 0.9.3 or later"), never a page. The kit's own checks now resolve Concierge ^0.8.4 || ^0.9 (was ^0.8.4), the range the workspaces feature already allows. No stub changes, no re-pin.

Added

  • kit.people.hide_grants: a list of Concierge grants (roles) whose holders the people lists leave out, such as a support role (docs/dashboard.md › Hiding people by grant). A member holding one of them in the workspace is absent from Settings › People (rows and the Members count alike), the People widget and people search, and their own Change role and Transfer ownership pages, role change and removal by id are a 404. The default is []; a value that isn't a list of names is refused at boot. composer test:people-hide-grants (new, in CI) covers each surface.

Changed

  • The auth forms validate in the card, not in the browser. Auth/Login, Auth/ForgotPassword, Auth/ResetPassword and Auth/ConfirmPassword carry novalidate, so the server's field errors render in the card (an address like "nobody" gets the "valid email address" error) instead of the browser's own bubble. The fields keep type="email", required and autocomplete.

Fixed

  • The guest auth pages keep a rate-limited submit in the card. A 429 from a limiter (Fortify's login limiter, the verification resend, a reset or reset-link request) opened Inertia's error modal over the page. Auth/Login, Auth/ForgotPassword, Auth/ResetPassword, Auth/ConfirmPassword and Auth/VerifyEmail now refuse the modal for a 429 only (onHttpException returns false), clear the stale field errors and show an in-card alert (role="alert"): "Too many requests. Try again in N seconds." when the response carries Retry-After, else "Too many requests. Try again in a moment." The copy is in the kit's ui lang file (form.throttled, form.throttled_retry). Any other HTTP error still gets Inertia's modal; nothing the server allows or limits changes.
  • A failed sign-in, reset or confirmation clears the password and marks the field. Auth/Login, Auth/ResetPassword and Auth/ConfirmPassword reset their password fields on error, set aria-invalid on the fields with an error, and move focus to the first one (the email after bad credentials).
  • A bad or expired reset token is visible. Fortify reports it on email, a hidden field on Auth/ResetPassword, so nothing showed. It now shows as an in-card alert with a "Request a new link" link to /forgot-password.
  • The reset-password page's status comes from its view model. ResetPasswordViewModel takes status (the flashed session status), so the FormStatus on that page shows the server's status without the host supplying it.
  • The sign-in page focuses the email field on load, as Auth/ConfirmPassword already focuses the password.

What's Changed

Full Changelog: v0.7.3...v0.7.4

v0.7.3

Choose a tag to compare

@jaspertey jaspertey released this 30 Oct 13:09
1c154db

Upgrading

  • Run kit:update plan|apply for core (re-pinned sha256:909d632f…822e → sha256:9a290690…555b): feature.json adds class-variance-authority and tw-animate-css to requires.npm. No stub changes.
  • The dev floor for tey/standards is now ^0.11.52, the first release that reads an inventory's features block.

Added

  • CI guards the adoption inventory's completeness (contributor-facing). composer test:adoption-completeness (tests/AdoptionCompletenessCheck.php, in the same CI job as the other adoption checks) fails when a page, layout or shell component a feature installs under resources/js/pages is named by no surfaces.json row's uses.files, uses.components or uses.imports and has no entry in the new resources/adoption/internal.json, and when a row with handRolled signals has no adoption fixture expecting it hand-rolled (or an unproven entry there). Stale internal.json entries fail too, and every entry needs a reason. Rows adopted in no fixture are listed as a note. To pass, uses.files now also names the shell partials (UserMenu, WorkspaceSwitcher, Breadcrumbs, GlobalSearch, UserAvatar, Wordmark, FocusShell under shell.workspace; GuestShell and Public/Markdown under shell.public), the remaining auth screens (ResetPassword, ConfirmPassword, VerifyEmail), the dashboard partials, Sections/Index, Search, the placeholder, error and no-workspace pages (workspace.pages), Settings/Delete (workspace.settings) and the People pages and invitation page (access.members); internal.json lists the sample notes pages and the welcome steps. New fixtures pin a hand-rolled outcome for the 19 rows that had none: public-shell-hand-rolled, split-hand-rolled, search-hand-rolled, action-bar-hand-rolled, menu-hand-rolled, modal-hand-rolled, rich-text-hand-rolled, list-hand-rolled, badge-hand-rolled, avatar-hand-rolled, alert-hand-rolled, activity-hand-rolled, timezone-hand-rolled, auth-hand-rolled, passkeys-hand-rolled, teams-hand-rolled (workspace.pages, access.members, workspaces.tenancy) and mcp-hand-rolled. No stub changes, no re-pin.

Changed

  • A workspace without the dashboard opens its first navigation page. With workspace.dashboard off, the workspace root (/@{workspace}, and / in the short form) was a 404. It now redirects (302) to the first primary navigation page the viewer may open, or to /me/workspaces when there is none. At the kit's home, a person with several workspaces still goes to the root of the one they last used, so with the dashboard off they land on its first page instead of the workspace list (docs/dashboard.md › Without a dashboard). Give your page order < -10 to make it first. Only the capability being off redirects: a role that lacks dashboard.read, a non-member and a JSON client are refused as before. An accepted invitation's landing uses the same lookup, unchanged. No stub changes, no re-pin. composer test:workspace-root (new, in CI) covers it; test:capability-guard now expects the root's redirect.

Fixed

  • standards:adoption no longer reads the kit's own dependencies and host UI as gaps. resources/adoption/surfaces.json now carries a top-level features object: for each feature with requires.npm or requires.host_ui, its npm package names and its host UI primitives, exactly as its feature.json lists them. tey/standards 0.11.52 treats those packages as the kit's (never a package.json gap or a third-party import) and those components/ui files as kit-owned (never an app component, gap or hand-rolled hit) for each installed feature. The schema allows the field, and the inventory test fails when it drifts from the feature.json files.

  • core declares every npm package its components need. Kit/Feedback/alertVariants.ts (like the shadcn button it pairs with) imports class-variance-authority, and the kit's menus, toasts and select animate with tw-animate-css utilities (animate-in, fade-in-0, zoom-in-95); neither was in requires.npm, so a host without them got no plan attention for it. Both are now required, at ^0.7.1 and ^1.4.0.

  • The adoption inventory reads hand-rolled account and settings pages wherever they live. account.pages and workspace.settings applied only when a route URI said settings, so an app serving its own account page at /account, or workspace settings at /w/{workspace}/deletion, read n/a. Two facts computed from file names close that: has-account-pages (pages under Account/, Me/, Profile/, Security/, Preferences/, Settings/{Account,Profile,Security,Password,Preferences}/, and *AccountSettings*, *ProfileSettings*, *UserSettings*, *UserPreferences* pages) and has-workspace-settings-pages (Teams/Show.vue, Team*/Settings/, Workspace*/Edit, Workspace*/Deletion/, Organisation/Organization settings, Settings/Workspace*, Settings/Team*, and *TeamSettings*, *WorkspaceSettings*, *OrganizationSettings* pages). Both read inside tey/mod module page folders too, and never flag the kit's own Workspace/Account/** and Workspace/Settings/** pages or components/ui/**. A domain component such as SlideSettings.vue matches none of them. account.pages also counts the kit's Password and Delete pages as adoption.

  • New surfaces: settings.frame and mcp.connections. settings.frame is the account and workspace settings frame (Workspace/Account/Layout.vue, Workspace/Settings/Layout.vue); an app's own Account/Layout.vue, Settings/Layout.vue, Settings/Index or layouts/settings/** reads hand-rolled. mcp.connections is the AI assistants page (Workspace/Settings/Connections/Page.vue); *Connection*, *ConnectedApp*, *Assistant* and *Token* pages under Settings/, Account/, Profile/ or Me/, ConnectedApp*/, ApiTokens/ and Jetstream's API/ pages, and an app controller, view model, action or Livewire class referencing Passport's Token/TokenRepository or Sanctum's PersonalAccessToken read hand-rolled.

  • platform.timezone flags an app's own timezone picker. A *Timezone*.vue outside the kit's components and Welcome timezone pages reads hand-rolled. The kit ships no passkey management page (only the PasskeyOffer), so auth.passkeys is unchanged.

The fixtures account-hand-rolled, account-kit-installed and slide-settings-domain pin these outcomes. No stub changes, no re-pin.


What's Changed

  • Declare feature npm and host UI deps in the adoption inventory by @jaspertey in #465
  • Read hand-rolled account and settings pages in the adoption inventory by @jaspertey in #468
  • Guard the adoption inventory's completeness in CI by @jaspertey in #470
  • Open the first navigation page at a workspace root without a dashboard by @jaspertey in #457
  • Release v0.7.3 by @jaspertey in #473

Full Changelog: v0.7.2...v0.7.3

v0.7.2

Choose a tag to compare

@jaspertey jaspertey released this 30 Oct 12:05
11aba1b

Changed

  • A person with several workspaces lands on the one they last used. The kit's home (kit.home) sends a signed-in person with several workspaces to the workspace they last opened (the session's current one), while they are still an active member and may open its dashboard; with none, or a stale one (removed, suspended, not admitted, deleted), it is the workspace list as before. For a signed-in person the logo (shell.product.url) and shell.links.home now link to kit.home too, not /me/workspaces; the switcher and a guest's shell are unchanged. One workspace still shows its dashboard there, and a guest still signs in first. Sign-in lands on kit.home with workspace-ui too: the kit's fortify.home default is now kit.home with or without workspace-ui (it was /workspaces, the list). A host with its own config/fortify.php keeps its home; set it to your kit.home (/ by default) to land on the last workspace. No stub changes, no re-pin.

What's Changed

Full Changelog: v0.7.1...v0.7.2