1 hours ago
astro

astro-vscode@2.17.4

Patch Changes

  • #18692 ad77e9e Thanks @Princesseuh! - Republishes the extension to the VS Code Marketplace and Open VSX, which were missed in the previous release
1 hours ago
scalar

Release 2026-11-10

Releases

@scalar/api-client@3.25.0

Minor Changes

  • #10871: Add schema-aware file controls for multipart/mixed and multipart/related parts, preserving part order and encodings. Restrict multipart/form-data uploads to file fields when a request body schema is available.

    Honor binary encoding content types on multipart fields and share file-schema detection with example generation, including nullable, composed, and typeless file placeholders.

  • #10731: Support OpenID Connect flow defaults, including client ID, selected scopes, and PKCE, after discovery. Prefer a sole configured flow while retaining other discovered flows. Add typed OpenID Connect authentication configuration for .NET.

@scalar/api-reference@1.78.0

Minor Changes

  • #10892: Allow defaultHttpClient to select custom code samples with targetKey: 'custom'.
  • #10891: Support optional display labels for SDK installation tabs while retaining language-based selection and icons.
  • #10902: Render self-contained Avro, RAML, and Protobuf schemas as native field and type documentation, retaining copyable source for unsupported schemas.

Patch Changes

  • #10890: Add an Elixir language icon and show it on Elixir SDK installation tabs.

  • #10936: Mark hosting waitlist sign-ups from the developer tools Deploy card with an oss contact property.

  • #10898: Preserve browser-native plugin URL imports when bundling API Reference with Next.js Turbopack.

  • #10908: Expose Scalar.releaseUnusedStyles() in the standalone bundle to release orphaned CSS without mounting a temporary reference or changing body theme classes. Preserve styles owned by active references and restore detached styles on the next mount.

  • #10873: Display the API description version verbatim without adding a v prefix, so commit hashes and other version identifiers remain accurate.

  • #10874: Automatically group deep-object query parameters with expandable fields in a collapsed row.

    Keep expanded fields open during navigation within a group, and mark flattened fields as required only when every ancestor is required.

  • #10949: Show response header examples, including media type examples and OpenAPI 3.2 cookie examples, with explicit examples taking precedence over schema examples. Preserve this precedence through composed schemas and fall back to media type examples when header examples have no usable values.

    Keep external example URLs visible and preserve line breaks in displayed string examples. Across all schemas, array-level examples take precedence over item examples.

    Preserve x-example and x-examples on OpenAPI 2.0 response headers during upgrades.

@scalar/blocks@0.7.0

Minor Changes

  • #10894: Substitute explicitly mapped reader credentials into custom code samples with default masking, public field opt-outs, and real values on copy.
  • #10893: Allow custom code samples to use separate picker headings with an optional group field.

Patch Changes

  • #10949: Show response header examples, including media type examples and OpenAPI 3.2 cookie examples, with explicit examples taking precedence over schema examples. Preserve this precedence through composed schemas and fall back to media type examples when header examples have no usable values.

    Keep external example URLs visible and preserve line breaks in displayed string examples. Across all schemas, array-level examples take precedence over item examples.

    Preserve x-example and x-examples on OpenAPI 2.0 response headers during upgrades.

  • #10902: Render self-contained Avro, RAML, and Protobuf schemas as native field and type documentation, retaining copyable source for unsupported schemas.

@scalar/components@0.34.0

Minor Changes

  • #10894: Substitute explicitly mapped reader credentials into custom code samples with default masking, public field opt-outs, and real values on copy.
  • #10890: Add an Elixir language icon and show it on Elixir SDK installation tabs.

@scalar/json-magic@0.19.0

Minor Changes

  • #10897: Resolve proxy references within their schema resource using the shared bundle resolver, and expose resolved targets to lifecycle hooks for scope-aware transformations.

Patch Changes

  • #10904: Share confined document loading between Markdown converters and include HTTP status codes in load errors.

@scalar/release-notes@0.3.0

Minor Changes

  • #10935: feat(release-notes): add a per-product maxHighlights option (1 to 20, default 5) that sets how many highlights a generated note may carry. The limit is stated in the default system prompt, enforced when the response is validated, and passed to custom system prompts, and the default output token budget grows with it.

Patch Changes

  • #10900: Upgrade the CLI to Commander 15 and require Node.js 22.12.0 or later.

@scalar/schemas@0.17.0

Minor Changes

  • #10894: Substitute explicitly mapped reader credentials into custom code samples with default masking, public field opt-outs, and real values on copy.
  • #10892: Allow defaultHttpClient to select custom code samples with targetKey: 'custom'.
  • #10891: Support optional display labels for SDK installation tabs while retaining language-based selection and icons.
  • #10893: Allow custom code samples to use separate picker headings with an optional group field.

@scalar/types@0.28.0

Minor Changes

  • #10894: Substitute explicitly mapped reader credentials into custom code samples with default masking, public field opt-outs, and real values on copy.
  • #10892: Allow defaultHttpClient to select custom code samples with targetKey: 'custom'.
  • #10891: Support optional display labels for SDK installation tabs while retaining language-based selection and icons.
  • #10731: Support OpenID Connect flow defaults, including client ID, selected scopes, and PKCE, after discovery. Prefer a sole configured flow while retaining other discovered flows. Add typed OpenID Connect authentication configuration for .NET.
  • #10893: Allow custom code samples to use separate picker headings with an optional group field.

Patch Changes

  • #10871: Add schema-aware file controls for multipart/mixed and multipart/related parts, preserving part order and encodings. Restrict multipart/form-data uploads to file fields when a request body schema is available.

    Honor binary encoding content types on multipart fields and share file-schema detection with example generation, including nullable, composed, and typeless file placeholders.

@scalar/workspace-store@0.71.0

Minor Changes

  • #10894: Substitute explicitly mapped reader credentials into custom code samples with default masking, public field opt-outs, and real values on copy.

  • #10871: Add schema-aware file controls for multipart/mixed and multipart/related parts, preserving part order and encodings. Restrict multipart/form-data uploads to file fields when a request body schema is available.

    Honor binary encoding content types on multipart fields and share file-schema detection with example generation, including nullable, composed, and typeless file placeholders.

  • #10891: Support optional display labels for SDK installation tabs while retaining language-based selection and icons.

  • #10731: Support OpenID Connect flow defaults, including client ID, selected scopes, and PKCE, after discovery. Prefer a sole configured flow while retaining other discovered flows. Add typed OpenID Connect authentication configuration for .NET.

  • #10893: Allow custom code samples to use separate picker headings with an optional group field.

Patch Changes

  • #10928: Fill the discriminator property with the selected variant's mapping value in generated examples.

  • #10813: fix: keep the required properties of an object that the example depth cap truncates, so a deeply nested mock response no longer serves {} where its schema requires keys, including keys required through allOf members or the selected oneOf/anyOf variant

    Preserve explicit values equal to the depth marker and exclude requirements from unselected union branches.

    Keep required primitive compositions on the normal traversal and collect discriminator requirements from the branch rendered after depth truncation.

    Reuse evaluated discriminator values for required-property collection and keep inferred tuple arrays empty past the depth cap.

    Preserve shared requirements and required primitive values through dynamic references, including requirements declared in dynamically referenced composition members.

    Collect requirements separately when composition branches reuse a shared template under different dynamic bindings.

    Exclude requirements from composition members omitted by the request or response mode.

  • #10938: fix: merge array items whose allOf describes an object without type: object on the first member into one example instead of one partial object per member

  • #10955: fix: respect array length bounds when generating schema examples

    Preserve cycle detection for selected array branches, pad unrestricted annotated items, and prefer a contains sample when no item constraints are declared.

@scalar/docusaurus@0.8.57

Patch Changes

  • #10908: Load Scalar only on API reference routes and destroy its instances when leaving, preventing standalone styles from affecting other Docusaurus pages.

    The CDN bundle is no longer injected on every page. Sites using window.Scalar.createApiReference outside an API reference route must load the bundle explicitly. Upgrade pinned CDN bundles to a release with Scalar.releaseUnusedStyles for cleanup when navigation interrupts loading.

@scalar/agent-chat@0.12.51

Patch Changes

  • #10879: Omit response URLs and redact saved credentials from response headers and bodies before sending request tool output to the chat model.
  • #10876: Require approval before executing any AI-generated API request, including GET requests that can send stored credentials or data.
  • #10878: Validate AI request paths and require a usable selected server before attaching stored credentials or sending a request.

@scalar/asyncapi-to-markdown@0.2.7

Patch Changes

  • #10904: Block private-network document requests by default and require an explicit basePath for local files. Apply the same permissions to external references, with an opt-in for trusted private-network inputs.
  • #10904: Share confined document loading between Markdown converters and include HTTP status codes in load errors.

@scalar/code-highlight@0.4.13

Patch Changes

  • #10923: Update the bundled mdast-util-to-hast to 13.2.1 to fix CVE-2025-66400 (unsanitized class attribute).

@scalar/helpers@0.17.1

Patch Changes

  • #10894: Substitute explicitly mapped reader credentials into custom code samples with default masking, public field opt-outs, and real values on copy.
  • #10882: Skip proxying requests whose origin matches the proxy origin to avoid an unnecessary forwarding hop.
  • #10880: Preserve empty upstream response headers when restoring headers for scripts, request history, and agent results.

@scalar/json-schema-validator@0.1.19

Patch Changes

  • #10951: Use shortest round-trip decimal values consistently for multipleOf checks, including large JSON integers and positive exponents. Fast-path safe integers and share keyword registration across validators.
  • #10951: Validate decimal multipleOf constraints with exact decimal arithmetic, accepting values such as 19.99 and 1000000.19 for multipleOf: 0.01 while rejecting nonmultiples without a floating-point tolerance.
  • #10959: Reduce repeated schema compilation in decimal validation tests to avoid CI timeouts.

@scalar/mock-server@0.21.1

Patch Changes

  • #10951: Use shortest round-trip decimal values consistently for multipleOf checks, including large JSON integers and positive exponents. Fast-path safe integers and share keyword registration across validators.

  • #10951: Validate decimal multipleOf constraints with exact decimal arithmetic, accepting values such as 19.99 and 1000000.19 for multipleOf: 0.01 while rejecting nonmultiples without a floating-point tolerance.

  • #10813: fix: keep the required properties of an object that the example depth cap truncates, so a deeply nested mock response no longer serves {} where its schema requires keys, including keys required through allOf members or the selected oneOf/anyOf variant

    Preserve explicit values equal to the depth marker and exclude requirements from unselected union branches.

    Keep required primitive compositions on the normal traversal and collect discriminator requirements from the branch rendered after depth truncation.

    Reuse evaluated discriminator values for required-property collection and keep inferred tuple arrays empty past the depth cap.

    Preserve shared requirements and required primitive values through dynamic references, including requirements declared in dynamically referenced composition members.

    Collect requirements separately when composition branches reuse a shared template under different dynamic bindings.

    Exclude requirements from composition members omitted by the request or response mode.

  • #10950: Ignore declared Content-Length, Content-Encoding, and Content-Type response headers so generated bodies remain readable and empty responses do not inherit a declared content type.

    Ignore other transport-managed headers, including Transfer-Encoding and Trailer. Skip and warn about malformed declared header names and values instead of failing the response.

  • #10938: fix: merge array items whose allOf describes an object without type: object on the first member into one example instead of one partial object per member

  • #10940: Route path segments that mix parameters with literal text, such as {id}.json, {id}:cancel and v{version}, only to requests that carry the literal text, and pass their parameters to request validation, x-handler and x-variable.

  • #10930: Accept request bodies that leave out required readOnly properties during request validation.

  • #10875: Keep mock store collections and item IDs isolated from inherited host properties, and validate keys passed through the script sandbox.

@scalar/nextjs-openapi@0.3.57

Patch Changes

  • #10898: Preserve path parameters when generating OpenAPI descriptions from Next.js handlers with asynchronous params, and mark path parameters as required.

    Infer missing path parameters from route paths when handlers use named parameter aliases or generated Next.js RouteContext types.

@scalar/openapi-to-markdown@1.7.5

Patch Changes

  • #10904: Share confined document loading between Markdown converters and include HTTP status codes in load errors.

@scalar/openapi-types@0.9.9

Patch Changes

  • #10921: Isolate declaration compatibility tests from ambient workspace types.

@scalar/openapi-upgrader@0.4.8

Patch Changes

  • #10736: Exclude benchmark fixtures from published build output.

  • #10925: Keep type: file inside Swagger 2.0 example data when upgrading to OpenAPI 3.0. Continue converting file schemas in reusable responses and parameters named like data keywords. Preserve example and default data on schemas and reusable parameters whose names match schema-map keywords. Continue upgrading response headers whose names match data keywords. Limit Swagger-specific data guards to the 2.0 upgrade so OpenAPI 3.0 request bodies and callbacks named x-example still upgrade. Preserve example payloads on reusable objects named headers.

  • #10948: Convert Swagger 2.0 discriminator strings to Discriminator Objects so upgrades preserve the discriminating property name.

    Preserve x-ms-discriminator-value and x-discriminator-value aliases from allOf subtypes in discriminator mappings, including inherited subtypes, without replacing authored mappings.

  • #10949: Show response header examples, including media type examples and OpenAPI 3.2 cookie examples, with explicit examples taking precedence over schema examples. Preserve this precedence through composed schemas and fall back to media type examples when header examples have no usable values.

    Keep external example URLs visible and preserve line breaks in displayed string examples. Across all schemas, array-level examples take precedence over item examples.

    Preserve x-example and x-examples on OpenAPI 2.0 response headers during upgrades.

  • #10920: Prefer operation-level produces over the global produces when upgrading Swagger 2.0 response content.

@scalar/snippetz@0.10.16

Patch Changes

  • #10942: Keep application/x-www-form-urlencoded bodies in the ofetch and undici snippets, preserve the order of repeated, empty and prototype-named fields in the fetch, Go, F#, Julia and Clojure snippets, and escape apostrophes in the PHP cURL body.

@scalar/use-codemirror@0.14.18

Patch Changes

  • #10925: Stabilize the color-picker edit-position regression test by completing syntax parsing after document changes.

@scalar/astro@0.4.36

@scalar/express-api-reference@0.10.36

@scalar/fastify-api-reference@1.78.0

@scalar/hono-api-reference@0.12.19

@scalar/nestjs-api-reference@1.2.36

@scalar/nextjs-api-reference@0.12.17

@scalar/nuxt@0.6.89

@scalar/starlight@0.2.21

@scalar/sveltekit@0.3.36

@scalar/api-client-react@2.0.65

@scalar/api-reference-react@0.9.89

@scalar/asyncapi-upgrader@0.1.20

@scalar/asyncapi-validator@0.1.19

@scalar/client-side-rendering@0.4.18

@scalar/core@0.5.50

@scalar/import@0.5.36

@scalar/localization@0.2.11

@scalar/mermaid-plugin@0.1.6

@scalar/oas-utils@0.20.20

@scalar/object-utils@1.3.35

@scalar/openapi-parser@0.29.21

@scalar/openapi-validator@0.1.19

@scalar/postman-to-openapi@0.7.30

@scalar/pre-post-request-scripts@0.4.63

@scalar/server-side-rendering@0.1.68

@scalar/sidebar@0.12.8

@scalar/use-hooks@0.4.24

@scalar/void-server@2.5.21

@scalar/aspire@0.14.0

Bundled API Reference

  • @scalar/api-reference@1.78.0

Minor Changes

  • #10731: Support OpenID Connect flow defaults, including client ID, selected scopes, and PKCE, after discovery. Prefer a sole configured flow while retaining other discovered flows. Add typed OpenID Connect authentication configuration for .NET.

@scalar/aspnetcore@2.20.0

Bundled API Reference

  • @scalar/api-reference@1.78.0

Minor Changes

  • #10731: Support OpenID Connect flow defaults, including client ID, selected scopes, and PKCE, after discovery. Prefer a sole configured flow while retaining other discovered flows. Add typed OpenID Connect authentication configuration for .NET.

@scalar/aws-lambda@0.5.0

Bundled API Reference

  • @scalar/api-reference@1.78.0

Minor Changes

  • #10731: Support OpenID Connect flow defaults, including client ID, selected scopes, and PKCE, after discovery. Prefer a sole configured flow while retaining other discovered flows. Add typed OpenID Connect authentication configuration for .NET.

@scalar/azure-functions@0.5.0

Bundled API Reference

  • @scalar/api-reference@1.78.0

Minor Changes

  • #10731: Support OpenID Connect flow defaults, including client ID, selected scopes, and PKCE, after discovery. Prefer a sole configured flow while retaining other discovered flows. Add typed OpenID Connect authentication configuration for .NET.

@scalar/dotnet-shared@0.5.0

Minor Changes

  • #10731: Support OpenID Connect flow defaults, including client ID, selected scopes, and PKCE, after discovery. Prefer a sole configured flow while retaining other discovered flows. Add typed OpenID Connect authentication configuration for .NET.

scalar-ninja@0.3.2

Patch Changes

  • #10934: Keep embedded API descriptions inside the API reference configuration script and escape the Django Ninja page title.

scalar-fastapi@1.9.2

Patch Changes

  • #10934: Keep embedded API descriptions inside the API reference configuration script and escape the Django Ninja page title.

galaxy-scalar-com@0.2.30

Patch Changes

  • #10905: Configure a separate agent key for each Galaxy and Petstore API description.

scalar-app@1.1.48

Patch Changes

  • #10903: Upgrade Monaco Editor and YAML support, migrate language APIs, and use Vite-managed workers in web and desktop editors.

@scalarapi/docker-api-reference@0.6.23

Bundled API Reference

  • @scalar/api-reference@1.78.0

@scalar/java-integration@0.7.1

Bundled API Reference

  • @scalar/api-reference@1.78.0

scalar_api_reference@0.2.23

Bundled API Reference

  • @scalar/api-reference@1.78.0

@scalar/mock-server-docker@0.2.78

2 hours ago
tiptap

v3.33.1

@tiptap/core

Patch Changes

  • Deleting a text selection that spans the whole document no longer leaves the deleted text's marks active, so text typed into the emptied editor is no longer bold, linked or otherwise marked.

@tiptap/extension-find-and-replace

Patch Changes

  • The first goToNextResult() or goToPreviousResult() after a new search now selects the highlighted first result instead of skipping it.

@tiptap/y-tiptap

Minor Changes

  • yCursorPlugin now publishes a lastSelection awareness field with each collaborative field's latest selection and whether its editor is focused. Unlike cursor, it stays after the editor loses focus, so other clients can still read the selection. lastSelection is now reserved and can't be used as the cursor field.
4 hours ago
rspack

v2.3.2

Highlights 💡

More reliable HMR, fewer full-page reloads

HMR now handles code-splitting changes more reliably, reducing unnecessary full-page reloads when chunks are added or rearranged. Newly required chunks are loaded before HMR accept callbacks run, and changes to async chunk dependencies no longer cause unnecessary update propagation.

Experimental rule.as

The experimental rule.as option lets a matched resource use a different path for subsequent rule matching while preserving the original resource for file reading and loaders. For example, as: '[resource-path].txt' lets later .txt rules also process example.source.js. (#15917)

// rspack.config.mjs
export default {
  module: {
    rules: [
      {
        test: /\.source\.js$/,
        type: 'asset/source',
        as: '[resource-path].txt',
      },
      { test: /\.txt$/, use: './text-loader.js' },
    ],
  },
};

Here, example.source.js matches the later .txt rule as example.source.js.txt; text-loader.js still receives the contents of the original file.

Skip side-effect-free export * passthrough modules

Rspack can now bypass side-effect-free modules that only forward exports with export *, connecting directly to the underlying module.

Mark the package as side-effect-free in package.json:

{
  "sideEffects": false
}
// src/value.js
export const value = 42;

// src/passthrough.js
export * from './value.js';

// src/index.js
export * from './passthrough.js';

With optimization.sideEffects enabled (the default in production), the export * connection from index.js can now go directly to value.js, skipping passthrough.js.

What's Changed

New Features 🎉

Performance 🚀

Bug Fixes 🐞

Refactor 🔨

Document 📖

Other Changes

New Contributors

Full Changelog: https://github.com/web-infra-dev/rspack/compare/v2.3.1...v2.3.2

5 hours ago
astryx

v0.7.12

Astryx 0.7.12 — all @astryxdesign/* packages ship at this version.

OLD_VERSION=0.7.11  # replace with the Core version your project uses today
npm install @astryxdesign/core@0.7.12 @astryxdesign/cli@0.7.12  # plus every other @astryxdesign/* package you use, at 0.7.12
npx @astryxdesign/cli upgrade --from "$OLD_VERSION" --apply --path .

@astryxdesign/core

New Features

  • BottomSheet: at rest, a sheet's content starts below the grab bar whatever its padding, so the first line is no longer drawn through the bar's fade. The block-start inset is never less than the bar's height (--spacing-6): the band under the bar is not padding a child escapes, so a lone Section starts below the bar too; scrolled content still passes beneath it. A sheet at the default padding starts its content 8 px lower, one with padding={0} starts it below the bar, and one whose only child is a Section starts that Section 24 px lower and grows by the same amount. Core's own padding={0} sheets (the touch date and time pickers, the DropdownMenu and ContextMenu sheets, and the PowerSearch and Tokenizer sheets) no longer reserve that band themselves, so the pickers, PowerSearch and Tokenizer look the same and a menu's first row sits 8 px lower. The CLI's table-filter and table-inbox page templates drop their own band too, so it is not doubled. (#8558)

    Also released in @astryxdesign/cli.

  • ChatLayout keeps its composer on top of the software keyboard where the visual viewport shrinks for it, instead of leaving it underneath until the browser scrolls the whole page. In Chrome on Android the dock rides Gboard's measured slides up and down, in step with the keyboard. In Safari on iOS (in the browser and as a home-screen app) and in Firefox for Android, it moves above the keyboard once the browser reports it. Where a page sets interactive-widget=resizes-content, Chrome shrinks the page for the keyboard itself and the composer stays above it without moving. A conversation at its newest message stays there, and the rest of the transcript can still be scrolled above the composer. On a touch screen, in a top-level page, every way into the composer's text (a tap, the keyboard's previous and next buttons, Tab, the input's focus()) reaches it without the page scrolling; under a mouse or inside an iframe the browser's own focus applies. In a top-level page on a touch screen, the composer's wrapper (without the scroll-to-bottom button) is a group with no name. When the keyboard's Done ends typing, focus rests there (it is no tab stop), so the next tap or Tab starts from the composer; it shows the focus ring after keyboard input only. A tap that focuses the composer still raises its click once. Under a mouse or inside an iframe, the wrapper is a plain container. Under Reduce Motion the composer never travels with Gboard: it cross-fades to its place above the keyboard. Under a mouse, with no software keyboard, nothing changes. The browser-support guide's section on the on-screen keyboard covers ChatLayout's composer. (#8176)

    Also released in @astryxdesign/cli.

  • Kbd shows meta as ⌘ on Apple platforms, where it showed "META". (#10528)

  • Kbd shows ctrl, alt and shift as text ("Ctrl", "Alt", "Shift") off Apple platforms and before hydration, and meta as "Win" off Apple platforms and before hydration, instead of Apple's ⌃ ⌥ ⇧ symbols and "META". Apple platforms keep the symbols, and spoken names are unchanged. The text comes from the catalog, so other locales can translate it. (#10524)

  • A LayoutPanel that takes a useResizable region in its resizable prop slides closed and open from the edge facing its ResizeHandle when the region collapses or opens, under every pointer, with the content beside it moving on the same motion: through collapse(), expand(), Enter, a double-click, the grow arrow, a controlled isCollapsed, or a lift past the collapse threshold. A drag that reopens the panel follows the pointer, and Reduce Motion is honored. A hand-built ResizableProps object does not slide. No prop or option is added. (#9989)

  • On a phone or tablet, drag a Lightbox photo down to close the viewer, as in the iOS Photos viewer. The photo follows your finger and shrinks a little as you pull, while the backdrop fades and the controls fade faster. The backdrop fades without its blur, which comes back once it is at rest. Let go moving down, however short the pull, and the photo carries on down as the viewer closes. Flick it back up, or bring it back to where you started, and it springs back into place. A zoomed photo still pans when you drag it; the drag to close starts from the fitted size. Videos are unchanged. (#8215) The viewer's controls now sit in their own layers, which fade together during the drag.

  • A Lightbox gallery now pages the way the iOS Photos viewer does. On a phone or tablet, drag or flick between items: the row follows your finger, and the release lands on the next or previous item, one at most. A gap of 40 px separates the items while they move. A zoomed photo pans with your finger until it reaches its edge; a new drag from that edge moves to the next item. A photo you leave zoomed returns to its fitted size once the gallery rests. Under a finger, the previous and next buttons and the arrow keys slide to the item instead of swapping it in place. (#8197) Each item's caption moves with its item, right under its photo. onIndexChange also reports a swipe.

    Lightbox now loads its touch code with it, as Toast and Slider do. Its import grows from 51.4 KB to 99.6 KB gzip with the zoom and paging beneath, mostly shared touch modules that a page loads once; Lightbox's own code adds 14.1 KB minified.

  • List takes animatedLayout, the handle from useAnimatedLayout({containerRef}), and ListItem takes itemKey, the row's identity. With the handle, when rows are inserted, removed or reordered, an inserted row enters, a removed row (a swiped one with its action showing) collapses in place, and a row that crosses rows moving the other way rides above them with the surface color behind it, as an iOS table's opaque cell does. The rows after a change and the content after the list, up to the handle's container, glide to their new places. All of it runs on one shared motion and starts together, under every pointer; under Reduce Motion nothing travels. A row without an itemKey changes at once, and without the handle the whole list does. (#8106)

  • While a reorderable List row is in reorder mode, a hint anchored to its handle, in the keyboard hint's layer and theme roles, shows the moves and Done. Each is a button that does what its key does and leaves the focus on the handle, so a row can be reordered by taps alone: the handle, the moves, then Done. (#8139)

  • createReorderable({onReorder, isReorderable}) makes a collection's reorder behavior, and List takes it as reorderable: each ListItem with an itemKey gets a reorder handle at its end, and each committed move calls onReorder({key, to}) once, with the row's key and its index after the move. On a touch screen a finger held still on a handle picks up its row the way an iOS table's reorder control does (a flick on it still scrolls): the row stays in its place, dimmed, while a copy of it follows the finger and an accent line marks where it would land; the other rows stay still, the page scrolls near the edges, and on release the rows rearrange, gliding to their new places. A pen held on a handle picks up its row the same way where the primary pointer is coarse. A mouse, or a pen where the primary pointer is fine, drags a handle at once with the same copy and line, and rows take their places without motion; a cancelled drag puts everything back at once. Without a drag, a click, Space or Enter on a handle turns on reorder mode for its row: the insertion line marks the row's place, each arrow key moves it a place at once, announced with its place and neighbours, and Escape or another activation ends the mode, keeping the moves. The object's start(key) enters the same mode from the app's own UI, such as a row's context menu. A List without reorderable loads none of the reorder code. (#8139)

  • A ResizeHandle that any pointer pulls past the maximum, or under the minimum, of a region whose pane a LayoutPanel renders now moves the handle with resistance, at most about 8 px past the limit and over the content beside it, while the panes keep their sizes, and the handle springs back to the limit after the lift. A panel sized by the region's size alone stops at the limit. A collapsible region no longer collapses mid-drag: only the lift collapses it, when the pointer is past collapsedSize, or released fast enough to come to rest past it, and a LayoutPanel's pane slides shut on a spring from the release velocity. Any other lift under the minimum returns to the minimum. A pointer that grabs the handle again catches it where it shows. Arrow keys still stop at the limit. Reduce Motion lands both at once. (#10292)

  • Tables can window fixed-height flat data: pass useTableRowsVirtualization({rowHeight, initialViewportHeight}) to Table.virtualization. Table keeps the complete data and row identity, mounts only the rows in its viewport plus a few overscan rows, keeps native table semantics with full-table row positions, keeps the focused row mounted, and falls back to rendering every row for content or geometry it cannot window. Tables that do not opt in are unchanged. (#8280)

    Also released in @astryxdesign/cli.

  • useTableRowsVirtualization's viewportHeight is renamed initialViewportHeight before the hook's first stable release. It only sizes the first window, on the server and before the viewport is measured; it no longer sets the Table's height. A windowed Table scrolls inside the height its parent gives it, so give the parent a height. rowHeight is unchanged. (#10894)

  • Table plugins can now read where a row sits. Table passes each body row and cell transform an immutable TableRowContext (the row's key, its zero-based logicalIndex in the complete data, the row and header counts, and the row model). It passes the scroll-wrapper transform a TableRenderContext with a stable viewport reference. A plugin declares rowModel: 'flat' when its body transforms keep one row per data item. useTableSelection and useTableStickyColumns declare it. useTableRowIndex() without options now numbers rows from the context (passing data still numbers from that array), and rows that move re-render only for plugins that read the index. Read context values during the transform; a later read from a callback warns in development. The CLI's numbered-rows block no longer passes data. (#10171)

    Also released in @astryxdesign/cli.

  • Windowed tables stay windowed with row selection and row numbers. A windowed row-number column widens to fit its last number before that row mounts, at the table's font and density, and pinned columns after it move with it. (#10658)

  • Under the touch presentation, ToggleButtonGroup tracks a finger's or a pen's press across its toggles, like a menu. Press a toggle and slide across the others: the highlight follows, and only the toggle you lift on changes. Lifting outside the group changes nothing. A mouse press is unchanged: it acts as a native toggle's, on the toggle it began on. With a finger, a toggle the group tracks keeps its 44 px target, now drawn by the group. A toggle without a value keeps its own press, and the keyboard is unchanged. Before, each toggle tracked only its own press, and sliding off it changed nothing. An enabled ToggleButtonGroup root is position: relative under a coarse pointer, so absolutely positioned children anchor to the group. With a fine primary pointer, the group is unchanged. (#8660)

  • Under a coarse primary pointer, expanding a TreeList row glides the rows below it to their new places on the shared layout glide while its children fade in on the same timing, so the tree moves as one. A collapse fades the children out as the rows below glide up, and the chevron turns on that timing. The rows used to appear and leave in a single frame. Without an animatedLayout handle the motion stays inside the tree, so content after the tree takes its place at once, and the tree clips its bottom edge while it moves so no row paints over that content; pass a handle for it to glide too. The guide lines beside a group now show and hide with its rows. With reduced motion on and a finger, the children fade in where they land. With a mouse the rows still change at once. Under every pointer, a guide line now ends at the last row it joins instead of running on beside an expanded last row's children. (#8088)

  • BaseTypeahead takes menu, which chooses what its anchored menu shows while Astryx keeps the layer: its position, top layer, outside press, Escape, focus return and ARIA. {role: 'listbox', render} places information such as a header or a key hint around the result list, and anything a person acts on stays a result, so the arrow keys reach it (a control outside the list warns in development). {role: 'dialog', label, render} shows content that takes focus in a dialog named by label; closing it returns focus to the element focused when it opened, or to the input. A new handleRef exposes open() and close(), and onOpenChange reports each change; there is no controlled open state. Calling open() from onChange keeps the menu open, so a pick can switch the list to a dialog on the same layer. Typeahead forwards menu and handleRef. (#10544)

Fixes

  • a month position past the end of a Gregorian-aligned calendar's year constrains to that year's last month in calendarMonthFromYearMonth and useCalendarDays, instead of returning a NaN CalendarMonth and laying out the next year's January (#10461)

  • Declare renderer-owned DOM boundaries so decorator overlays mount outside managed content without changing safe inline hosting. RichTextEditor also mirrors its dir prop onto the wrapper around the editable surface, so the editing surface and its content blocks, the placeholder, UI rendered through plugins, and relocated overlays follow dir; the editing surface previously kept the surrounding direction. (#7633)

    Also released in @astryxdesign/richtext.

  • On Android, text follows the reader's system font scale in every theme with no setup, in browsers that report env(preferred-text-scale) (verified on Chromium and on Chrome stable). The root Theme reads env(preferred-text-scale) and scales every font size by it, as it already follows Dynamic Type on iPhone. Before, the system font scale reached Astryx text only when the page added <meta name="text-scale" content="scale">. A page with content="scale" keeps working as it did: its root font size carries the scale, and the root Theme adds none. Browsers without either see no change. astryx docs typography/type-scale describes it. (#10727)

    Also released in @astryxdesign/cli.

  • Motion: in WebKit on Linux, the last frame of an animatedLayout change shows where each motion ends once a frame has drawn the motion near its end. Under load, that frame, and every frame of a long task over its end, could show a glide, an exit's fade or an indicator's turn part of the way through: a collapsing TreeList drew the rows below 44 px back, its leaving rows half faded and its chevron mid-turn. A long task that starts before that frame, or a motion near its end only at its last keyframe, can still show it. At normal speed the motion is unchanged. (#10663)

  • Motion: a Switch's value change mid-travel keeps the thumb's running speed. A rebase toward the new rest shifts the curve whole. (#10369)

  • Avatar: initials are drawn as generated content with empty alternative text instead of text, so axe's Label in Name check (label-content-name-mismatch) no longer flags a link or button avatar that shows initials. The accessible name, the name tooltip and the initials' appearance are unchanged. (#10816)

  • Under reduced motion, a BottomSheet that a finger's swipe dismisses fades out where the finger left it. Before, it jumped back to its open position in one frame and faded from there. With motion, the next open after such a swipe rises from below the screen again, instead of starting where the finger left the sheet. (#10925)

  • BottomSheet: a drag reads its release velocity from Core's velocity tracker, on each event's own timestamp, also when a touch drag starts on the sheet's content. The release depends on the last two moves, so a move that reaches the page late earlier in a flick no longer slows the flick. A hold weighs on the first move after it, as iOS's filter does. Two rules differ from iOS: a finger that holds still for more than 40 ms before the lift releases nothing in motion, and a release needs two moves. (#10463)

  • A stacked BottomSheet whose close waits in a suspended transition takes over the dim when the sheet beneath it closes, so the page behind it stays dimmed. (#10776)

  • BottomSheet: an open sheet that renders again no longer reads the viewport height. It keeps the viewport height that its resize listener reads, so a tap that renders the sheet again forces no style recalculation or layout in React's commit. The sheet's height, reserve and detents are unchanged. (#10363)

  • Button: disabled buttons no longer render the native disabled attribute. They stay focusable in keyboard tab order, keep focus when they become disabled while focused (a link Button, which becomes a <button> when disabled, still moves focus), and expose aria-disabled="true". All activation paths (mouse clicks, Enter, Space) and clickAction/onClick callbacks remain blocked without losing focus to the document body. A disabled named submit Button withholds its name so new FormData(form, submitter) omits its value. A ButtonGroup whose members are all disabled (whether on the group or individually) keeps its one Tab stop instead of dropping out of the Tab order or giving every member its own stop, and Carousel's hidden scroll buttons are never Tab stops. Tests that assert toBeDisabled() or :disabled should query [aria-disabled="true"] instead. (#7906)

  • A labelled Button grows with its label when the text size grows: its height is its size, or one line of its label when that is taller, so the label stays inside the button. At the default text size Buttons render exactly as before, a caller's height still applies, and icon-only buttons keep their fixed square. (#10588)

  • Calendar day buttons grow with their numbers when the person's text size grows, such as a larger browser default font size. Their size is now a minimum, so a day's number stays inside its own tap target and a tap no longer lands on the day below. At the default text size nothing changes. (#10596)

  • Under snap paging (hasSnap={useSnapPaging()}), Carousel's edge buttons and handleRef's scrollNext, scrollPrev and scrollTo move the row through the paging, also from rest. A touch that catches such a move now leaves the row on a page, as a flick's settle does. On iOS a still touch used to stop the browser's smooth scroll between two items. (#10384)

  • Under snap paging (hasSnap={useSnapPaging()}), Carousel's edge scroll buttons and edge fades follow the row as it moves. They change in the frame where it leaves an edge. A flick used to leave them as they were until the settle ended and the row went back to native scrolling, so the start button came back about half a second after the row had visibly left the start. (#10359)

  • ChatComposerInput: composing with an input method (for example Japanese or Chinese) right after picking a mention, inserting a token, or inserting text after a token now commits only the composed text. The composer places its caret inside the trailing text, not on an element boundary, so the first raw letter of the composition is no longer committed. Right after a mention pick, one Backspace now removes the mention with its space, and no stray line break is left. (#6423)

  • ChatLayout's scroll-to-bottom button now appears when scrollRef names the document's scroller and the reader scrolls the page up. The stream scroll also stops following there, so a streaming reply no longer pulls that reader back to the newest message. (#10779)

  • ChatLayout with a scrollRef now rests the newest message above the fixed composer dock at the scroll's end. The message area's end padding holds the dock's height and follows it as the composer grows. (#10676)

  • useChatStreamScroll's scrollToMessage now puts the message at the top of the viewport when the scroller is the page itself. It landed off by the page's own scroll. (#10781)

  • CommandPalette stays open with no results when a search or bootstrap request rejects (#10675)

  • A Table, or a List with edge="bleed", that is the first or last child of a rounded Card or Dialog that holds no other Table or bleeding List deeper inside now rounds with the corner it reaches, so the table's focus ring stays whole at the container's corners instead of being cut off. A bleeding List with a header rounds only its last row. Every other corner stays square: standalone, inside a square container or a wrapper element, between other content, at the top of a BottomSheet (below its grab bar), or while another Table or bleeding List sits deeper in the same container. (#10586)

  • CodeBlock's "Failed to copy value to clipboard." announcement now uses the assertive live region instead of the polite one, as status announcements require for errors, so screen readers report a failed copy right away. A successful copy is still announced politely. (#10448)

  • A swizzled Outline (or another copy that carries Core's Markdown plugin protocol) now type-checks against the package's Markdown plugins: MarkdownPluginEntry and MarkdownTransform carry their node type on a shared, versioned key instead of a module-local symbol, so the package's and the copy's types match. The member exists only in types; runtime trust and claim checks are unchanged. No API changes. (#10641)

  • A swizzled copy and the package now agree on the animated-layout handle, code-language values and snap paging. A handle from useAnimatedLayout passed to a swizzled AppShell, List, Table, TreeList, Collapsible or SideNav no longer throws when the layout changes, and those copies type-check: each brand is read through a registered, versioned key at runtime, and the types carry one optional brand member that is never present. No API changes. (#10576)

  • Core registers every React context, and the English its components register, under a versioned key, so two copies of Core's modules on one page share them: a provider from one copy reaches consumers from the other, a layer boundary from either resets the layer-scoped contexts of both, and a component from either shows its English. A copy built against another version of a context keeps its own. No API changes. (#10233)

  • A swizzled component that carries Core's touch code, such as a swizzled Switch or Slider, now joins the package's gesture host, window input listeners, click guard, layer input holds and moving-element hit testing for the page instead of keeping a second set, so the copy and package components do not both claim the same touches. No API changes. (#10599)

  • A swizzled List, TransferList or ListInput and the package now agree on a createReorderable object: its reorder machinery is read through a registered, versioned key. Before, a swizzled List found no machinery on an object from the package and showed no reorder handles. No API changes. (#10741)

  • A DateInput or DateTimeInput with presentation="bottom-sheet" no longer causes a hydration mismatch when the server and the browser are on different dates. The closed sheet's calendar now renders right after hydration, once the browser's today is known. (#10400)

  • Dialog: under React StrictMode, a Dialog with a DialogHeader that is first mounted open and then removed while open (rendered only while open) now returns focus to the element that opened it, instead of leaving focus on the page body. The opener is captured only on the closed-to-open edge. (#5642)

  • Dialog: a DialogHeader title now takes focus only after the dialog is modal, also when the header mounts in the render that opens the dialog. When no other control asks for focus, initial focus now starts on the title in every browser. Before, a header with a start control, and any header in Firefox, started on the first control. (#10408)

  • Drawer: a non-modal drawer (hasScrim={false}) that unmounts while it is open, for example one rendered only while open ({isEditing && <Drawer isOpen …>}) or removed with the rest of a view, now returns focus to the control that opened it instead of dropping it to the page. A modal drawer already did. (#9672)

  • DropdownMenu: a finger's hold-slide-lift no longer flashes the focus outline on the menu button. When a held press opens the menu, the button takes focus first, so focus can return to it when the menu closes; that focus now shows the outline only after keyboard input. A keyboard open and close still show the outline on the button. (#10079)

  • Dialog, CommandPalette, BottomSheet, Lightbox, Drawer, MobileNav, Popover: Tab no longer escapes a modal through a radio group. The browser gives a same-name group of native radios one Tab stop: its checked radio, or, when none is checked, its first moving forward and its last moving backward. The focus region now finds its first and last stops the same way. Before, Tab from a group's checked radio left the modal for the browser's toolbar when the group ended the modal and that radio was not last in it, and Shift+Tab did the same when a group started it. Reported by @duytbp. (#10304)

  • Gestures: a finger's lift is read on the clock its moves came on. In WebKit on Linux, a page pointerup listener that runs long no longer makes a fast flick read as a resting finger, or a tap lifted in time read as a long press. (#10583)

  • HoverCard and useHoverCard no longer open on keyboard focus of the trigger while isOpen={false} is passed. The controlled false already refused hover, but tabbing onto the trigger opened the card and reported onOpenChange(true) against the owner's state; focus now takes the same refusal as hover. (#10181)

  • HoverCard: a labelled card no longer puts aria-haspopup="dialog" on a role-less trigger, such as text or a <time>. Such a trigger names the open card through aria-controls only; a button or link trigger still gets aria-haspopup and aria-expanded. (#10733)

  • A HoverCard rendered into another document, such as an iframe or a Picture-in-Picture window, now closes when Escape is pressed there, and Escape from inside the card returns focus to its trigger. Moving focus from the trigger into such a card also keeps it open. Before, the card read only the page's document, so Escape in the other document never closed it. The card now also ranks on the shared dismissal stack by its trigger's place, so a card whose trigger sits inside a focus-trap container takes Escape before that container, as the topmost layer. (#9592)

  • HoverCard and Tooltip: a controlled isOpen={true} (or isDefaultOpen) no longer strands the layer in the viewport corner when its trigger mounts with no layout box yet, for example inside a Dialog that has not called showModal() yet. (#5398) Dialog mounts its children before it opens (isOpen gates showModal(), not rendering), so a HoverCard or Tooltip inside one with a controlled isOpen={true} runs its show effect while the trigger sits in a still non-modal <dialog> and has no box. A trigger with no box is not a valid CSS anchor: the popover resolves to its fallback position in the viewport corner, and unlike a live layout change that resolution is not revisited once the trigger later gets a box.

    Layer's show() now opens immediately when the trigger already has a layout box (an empty one, such as a ContextMenu's cursor anchor, counts), so a menu or popover still opens in the same task as the press that asked for it. Only a trigger with no layout box (display: none, or inside a <dialog> that is not showing) waits: first for it to get a layout box and for its dialog to show, then for any running animation on the document timeline that ends on its own, on the trigger or an ancestor, to finish, so the layer anchors to the trigger's settled position and not to a mid-transition one. Paused, infinite, and scroll- or view-driven animations are not waited on.

    The box wait is driven by signals and polls nothing: a ResizeObserver for a trigger that gains a layout box, an IntersectionObserver for a purely display: inline trigger (such as the wrapper <span> of a text-only trigger), which a ResizeObserver never delivers an entry for, and the enclosing dialog's open attribute and toggle event. A dialog counts as showing when it is open or shown as a popover, so triggers inside a non-modal Drawer still open their layers. The wait has no time limit and ends when the trigger gets a box inside a showing dialog or the open is cancelled (a later show() or hide(), or unmount). If the trigger element is replaced while waiting, the wait restarts on the new one, and a trigger that unmounts for good stops observing. Without a layout engine to wait on (no ResizeObserver or no Web Animations API, as in jsdom) the layer opens immediately.

    A transient null then same-element call on the trigger ref, as a combined ref utility makes when a handler changes identity, no longer removes and re-adds the element's anchor-name, which could also leave an open layer stranded in the corner.

  • Layers with an entrance input hold: a finger or pen that was already down when a light-dismissing layer opened, and lifts while it enters, now lifts through the hold's shield onto what is under it in WebKit, so the layer dismisses as it already did in Chromium. The shield leaves a small opening at each such press until it lifts. New presses during the entrance are still held. (#10398)

  • Layer: a light-dismissible layer that opens from a right click's contextmenu event stays open when that same press is released. Before, the release counted as a press outside, so a DropdownMenu opened through isMenuOpen from a right click closed at once. A later press outside still dismisses the layer. (#10128)

  • A popover taller than the room on both sides of its trigger keeps its top when the keyboard shortens it. The field being typed into stays under the caret. It used to jump below the trigger as the keyboard arrived, and back as it went. (#10497)

  • Once the keyboard caps a layer such as a popover, a scroll a person starts in it afterwards is no longer undone. On a busy page, the layer could scroll back to the focused field just after. A later keyboard change still brings the field being typed into back into view. (#10457)

  • A Tooltip, including a Slider's value bubble, no longer jumps to another side of its trigger in the middle of a scroll. Before, when a scroll carried the trigger toward the edge, the layer runtime took the Tooltip out of rendering for one forced layout so the browser would choose its position again, and it did this again on later frames of the scroll. Now the Tooltip has one fallback list from the start, and no option in it covers the trigger: it tries the opposite side first, then beside the trigger, then a side sized to the room. During a scroll, the Tooltip keeps its side: it is not checked or placed afresh until the scroll stops. In the first frame painted after the scroll stops (at scrollend, or 150 ms after the last scroll event where the browser has no scrollend), it is checked once and moves to the side with room if it needs to. At a show, a remount or a size change it is checked as before. (#10439)

  • A Tooltip's content scrolls by CSS alone, and nothing watches its trigger move. A Tooltip now has overflow-y: auto from its first frame, with no overscroll containment. It scrolls only while its content overflows, so a wheel over a Tooltip that fits still scrolls the page. Once a tall Tooltip has scrolled to its end, the next wheel moves on to the page, where before the page held. A Tooltip is now always a scroll container, so position: sticky and scroll(nearest) timelines inside its content resolve against the Tooltip. The layer runtime no longer reads the Tooltip's scroll size after its trigger moves, and it no longer observes a trigger that moves without resizing. (#10472)

  • A swizzled LayerProvider now shows the package's toasts: the viewport the package's Toast registers renders in a copied provider's outlet, and the package's provider renders a copy's. Before, each copy kept its own registry, so toasts added under a swizzled provider never showed. The registry lives under a registered, versioned key. No API changes. (#10800)

  • Layout: a false, true, or empty-string slot, such as false from isOpen && <LayoutPanel />, now counts as empty. The neighbouring regions move out to the layout edge, the same as when the slot is omitted, and with contentWidth a closed side panel no longer stops the content area at the content-width frame. The docs now list children and ref, show how to hide a region, and say that landmark roles go on the region components. (#10500)

  • LayoutContent: the edge away from a single start panel now takes the Layout-edge inset, so padding={0} on the Layout makes it full bleed, and with contentWidth its children end one Layout-edge inset inside the content-width frame, as they do without panels. Each edge now publishes the padding it applies, so a full-bleed child such as a Table lines up beside an end panel and under a header without a divider. A Layout nested under a header or footer without a divider keeps its own content spacing instead of taking the outer collapse. (#10516)

  • A Layout with no contentWidth no longer caps its LayoutHeader and LayoutFooter at the viewport's width. In a frame wider than the viewport, such as a scrolling canvas or a wide embed, their content now spans the frame like the rest of the Layout. (#10715)

  • LayoutFooter: a data-divider attribute passed by the caller no longer replaces the divider state the footer reflects, so the content above a footer without a divider keeps its collapsed spacing, and a footer with a divider keeps its spacing above the line. The docs now list padding and ref, and say that an unset hasDivider follows the Layout's defaultHasDividers. (#10674)

  • LayoutHeader: a data-divider attribute passed by the caller no longer replaces the divider state the header reflects, so the content below a header without a divider keeps its collapsed spacing, and a header with a divider keeps its spacing below the line. The docs now list padding and ref, and say that an unset hasDivider follows the Layout's defaultHasDividers. (#10686)

  • LayoutPanel: on an edge that takes the Layout's own inset, the panel now tells its children the padding it applies there. Children that read their container's padding, such as a full-bleed Divider, Table, or TabList, or a nested Layout, now follow the panel's outer edge whenever the Layout's padding differs from the inner inset. Before, they used the inner inset: with padding={6} a full-bleed Divider stopped 8px short of the edge, and with padding={0} it ran 16px past it. The docs now say which edge hasDivider draws on, that the Layout's defaultHasDividers does not apply, and that padding={0} is full bleed, and they list ref. (#10774)

  • Lightbox: a screen reader now hears the viewer's own messages while it is open. Moving to another item ("Mountain lake, 2 of 4") and zooming in or out are announced inside the open viewer; before, they went to the page behind the modal viewer, where a screen reader does not read them. The 0 key now returns a zoomed photo to its fitted size. The docs list the 0 key, and no longer say that the lightbox is only for images. (#10775)

  • Lightbox's drag to close now plays at the theme's tempo, like the viewer's other motion. At the default tempo it plays exactly as before. (#10630)

  • An open Lightbox shows the nearest item when index or defaultIndex is NaN or a fraction between the first and last item. A fraction rounds to the nearest item, and NaN shows the first item. Before, the viewer rendered nothing for those values. (#10632)

  • A reorderable List in its own scroll box keeps the dragged row under the pointer when a wheel during a mouse or pen drag scrolls the page, as a wheel at the box's end does. The wheel scrolls as before. (#10742)

  • Markdown: each paragraph, heading, quote and list item lays out in the direction of its own text. A Hebrew or Arabic paragraph in a left-to-right page now starts on the right, and an English paragraph in a right-to-left page starts on the left. A block with no letters, such as digits, punctuation or an image, takes the page's direction from the InternationalizationProvider, also inside a quote or list item that reads the other way. Lists, code blocks and tables keep the direction around them too, and the text in each table cell reads in its own direction. A numbered item that reads against its list shows its number beside its own text, and a table cell that reads against its table keeps the table's column lines and any explicit column alignment. A task list now keeps to the same prose width as other lists. (#10293)

  • Menus: the arrow keys, Home, End and type-ahead now reach a disabled row in DropdownMenu, its sub-menus, MoreMenu, ContextMenu and NavHeadingMenu, so a keyboard or screen-reader user finds the row and its description. The row takes focus, is announced as disabled, and Enter, Space or a click on it still does nothing and leaves the menu open. Opening a menu from the keyboard still lands on an enabled row. (#10160)

  • DropdownMenu: a held mouse dragged from the trigger onto a row keeps that row lit. On a loaded page the menu's open focus could land after the drag reached the row and take the highlight back to the menu container until the pointer moved again. (#10590)

  • DropdownMenu: a menu that a mouse press opens stays open when that press is released quickly away from the trigger, or after the trigger moved away under the pointer. Before, the browser's light dismiss read that release as a press outside and closed the menu at once. A press released after the settle time outside the menu still closes it, and a later press outside dismisses it. (#10689)

  • Under a finger, the month and year wheels behind the calendar title in the DateInput and DateTimeInput bottom sheets now behave like the iOS date picker's: (#8209)

    • Turning a wheel moves a chosen date to the new month and keeps its day, so October 15 becomes May 15. A day the month lacks becomes its last day, the result stays within min and max, and DateTimeInput keeps the time. A date that dateConstraints refuses is not chosen; the calendar still moves.
    • With no date chosen, the wheels only move the calendar.

    With a mouse, the wheels still move only the calendar.

  • Motion: elements that start moving from rest in the same frame now start at one time. Before, each element took its own reading of the clock in that frame, so items starting together, such as a carousel's pages as they settle, could start apart by as long as the frame callbacks between them ran. (#10584)

  • Motion: a spring retarget that the browser commits late, behind a busy main thread, now starts at that frame, from where the element is on screen. Before, it kept the clock of the call, so the element jumped to where the new curve had got to: 42 px back in one frame for a retarget behind a 200 ms block. Motion calls made in one callback now share one clock, so the children of a pager line up after a retarget. A retarget made inside a page's own frame or ResizeObserver callback now shows its new curve one frame later. (#10607)

  • MultiSelector: opening the closed list never highlights a disabled option. ArrowDown, Enter, Space and a press highlight the first enabled option in the order the list opens in, and ArrowUp the last, so Enter no longer lands on an option it cannot toggle. A list with search still highlights nothing when a press or Enter opens it. (#10820)

  • Carousel paging: a finger or a pen that lands on a row still settling after a swipe no longer reaches the item under it: no press highlight and no click, as on iOS. A mouse click is unchanged. (#10370)

  • Carousel paging: a finger or a pen that lands on the row during an edge button's move now stops it at the touch, as on iOS, instead of letting it run on for about 85 ms. The item under the finger still takes the tap. A mouse is unchanged. (#10545)

  • A page that a swipe releases now settles at the theme's tempo, as the rest of its motion does: in a Carousel given hasSnap={useSnapPaging()}, and in a Lightbox gallery. At the default tempo the settle is unchanged. (#8197)

  • Pagination's ellipsis grows with a larger text size, as its page buttons now do through Button. Its size is now a minimum height, so the "…" stays inside its own box. At the default text size nothing changes. (#10567)

  • Pagination wraps its row when its controls don't fit, as at a larger text size on a phone, instead of scrolling the page sideways and pushing Next off-screen. Previous stays first and Next stays last. Beside other content in a flex row, Pagination can now shrink to its widest control and wrap at the default text size too, where it held one line before; give it flexShrink: 0 through xstyle to keep one line there. (#10552)

  • parseColor answers null for constructor and __proto__ (in any case), as for any unknown name, instead of a truthy empty RGBA that made composed callers such as chart colors.alpha return rgba(NaN, NaN, NaN, …) (or #NANNANNAN at full opacity) instead of the original string (#10468)

  • A closed Popover, and any usePopover composition, no longer leaves a hidden role="dialog" with its label in the DOM. The surface stays mounted but carries the dialog role, name and aria-modal only while open, so [role="dialog"] selectors and tests that include hidden elements find no dialog that is not on screen. The accessibility tree already omitted the hidden surface. Reopening restores them. (#10487)

  • A dialog Popover starts focus on its first enabled tab stop. It skips a disabled control, lets an element that holds tabindex="-1" yield to a later tab stop, and starts on its labelled surface when every control is disabled. Before, a DateInput or DateTimeInput calendar whose range starts in the shown month opened on the disabled Previous month button. Now it opens on Next month, or, in a one-month range, on the selected day, else today, else the first enabled day. Disabled controls stay reachable by Tab. (#10503)

  • Under a finger, Popover's growth shows its content from its first frame. The growth starts at opacity 0.001 rather than 0: Chromium skips drawing a layer at exactly 0, so the first frames of a growth from 0 showed the popover without its content. No 8-bit pixel differs from transparent at 0.001. (#10757)

  • In PowerSearch's phone sheet, a date range is now picked on a calendar inside the sheet, instead of a calendar popover opening over it. The desktop editor popover is unchanged. (#10618)

  • PowerSearch's phone sheet now edits single and multiple choices with the standard RadioList and CheckboxList at their usual spacing, instead of its own 48 px rows, and its Operator radios put the radio at the start of the row, as every RadioList does. The choices use the shared touch band, so their touch targets are smaller than before. End, Home and Enter no longer pick a choice; the arrow keys and Space work as in any RadioList or CheckboxList. Adds a "Sheet presentation · realistic config" story that covers every value type. The desktop editor popover is unchanged. (#10608)

  • PowerSearch: a filter token's field and operator now come from one catalog message (@astryx.powersearch.token.fieldOperator, "{field}: {operator}" in English), and its full text, which also names its remove button and editor, joins that label and the value with another (@astryx.powersearch.token.labelValue, "{label} {value}"). A locale can reorder the parts and change the punctuation. PowerSearchToken uses the same message. English tokens read as before, except that a filter with no operator text no longer shows a trailing colon. (#10437)

  • Press feedback: a pointerup that carries no pointerId, such as a bare event a test helper or drag script dispatches, no longer throws Cannot read properties of null (reading 'live') when no press is held. It is ignored, as pointercancel already does. (#10695)

  • Inside a scroll view, a pressable's highlight now comes 150 ms after the touch, even when a busy page handles the touch late. Before, the wait started when the page handled the touch, so the highlight came late by that delay. This covers every pressable, and SegmentedControl, Switch, Slider and usePressTracker controls, in a scroll view. (#10260)

  • A theme duration token written with calc() now times a press's release as the browser resolves it. Before, the release clock fell back to 175 ms while the fade played the token's value. (#10069)

  • Two loads of Core's scroll-lock modules on one page now share one page scroll lock: closing the overlay that opened first no longer unlocks the page while the other overlay is still open. Text-selection locks, which a swizzled copy carries, are shared the same way, so a copy's release no longer restores a selection lock the package still holds. No API changes. (#10822)

  • useScrollableArea: an ancestor's inline style change measures the scroll area again only when it changes the axis mapping or the boxes' existence (direction, writing-mode, display, visibility, content-visibility); a size change still reaches it through the resize observers. A BottomSheet writes its transform, and its scrim's custom property, on every input sample of a drag, and each write forced a full measurement (its computed-style reads and the scroll geometry) in the next frame, a style recalculation and a layout on every frame of the drag. (#10034)

  • getContentProps from useScrollableArea now applies a caller's xstyle instead of leaking it to the DOM. (#6314) A viewport that lost overflow while focused drops its leftover tabindex as soon as it is blurred, and a focused viewport in another document keeps focus when it loses overflow. ScrollableArea's viewport also sheds the obsolete -webkit-overflow-scrolling and initial-value scrollbar declarations.

  • Selector: when the options move or reload while the list is open, the highlight stays on its option instead of naming whatever option now sits at its old position. If its option leaves, the highlight moves to the next remaining enabled option after it, or the previous one when none remains after it, and with none left nothing is highlighted. (#10818)

  • Selector and useCombobox: opening a closed list starts on the selected option, as the WAI-ARIA listbox pattern asks, and never on a disabled option. Before, ArrowDown and ArrowUp opened on the first or last option even when it was disabled, and ArrowDown then Enter changed "Hard" to the first option. Now opening highlights the selected option when it is enabled. Otherwise ArrowUp opens on the last enabled option and ArrowDown, a press, Enter or Space on the first; with no enabled option nothing is highlighted. A search list opened by a press, Enter or typing still highlights nothing. MultiSelector is unchanged. (#8868)

  • A Selector whose trigger sits near the bottom of the screen now opens its list on screen. The selected-item overlay's clamp did not count the layer's viewport gutter, so near the bottom edge the browser flipped the list below the viewport and its last options could not be reached. (#10697)

  • In a searchable Selector or MultiSelector, typing now clears the highlight, so Enter never picks an option you did not highlight. Before, the highlight kept its old position, which could point at a different option of the filtered list or at nothing. ArrowDown then starts at the first row: the first enabled match in Selector, and Select all or the create row when shown in MultiSelector. (#8871)

  • Selector with hasSearch: a required field no longer puts aria-required on its trigger, a button where the attribute is not allowed. The search field carries it while the list is open, and the closed trigger still announces "Required", through the field label or, when the label shows no marker, through its description. (#10878)

  • SideNav no longer adds space below a footer when it has no footerIcons and no built-in collapse button. The sidebar rendered an empty footer row there, and the gap of the sticky bottom area added space after the footer. The row now renders only when it holds the collapse button or footerIcons, as the drawer already does. (#10721)

  • Navigation rows grow with a larger text size. The shared nav item styles (navItemStyles, used by SideNavItem and by TopNavItem and the mega menu in the mobile drawer) now take their size as a minimum height instead of a fixed one, so above 200% text a row's label no longer overlaps the next row, and its end content (such as a count Badge) stays inside the row. Collapsed rail rows keep their fixed square. At the default text size, TopNav drawer menu and mega menu rows with a description now fit their text instead of overlapping the next row; other rows are unchanged. A caller height on these styles no longer brings a row below its size; override minHeight instead. (#10550)

  • Carousel snap paging: a stop that sits within half a step of the row's end is part of the last page, so a flick, a wheel or a key never rests a pixel short of the end, and a step back from the end moves a full step. (#10591)

  • Carousel paging: a row that narrows while a finger holds it now rests where the row ends, instead of short of its end by as much as it narrowed. (#10425)

  • Carousel paging: while the paging holds a row, the pager measures its pages and travel from the row's layout, as at rest. (#10860)

  • A Step keeps its own aria-current over a caller's. Before, an aria-current passed to a Step replaced the reflected value, so a step that was not in progress could be marked current and the in-progress step could lose aria-current="step". Now only the in-progress step carries aria-current="step", whatever the caller passes. No API changes. (#10640)

  • Under a mouse, a key that cannot scroll a DateInput, TimeInput or DateTimeInput wheel, as Tab, no longer ends the wheel's key step. On a busy page, Tab right after an arrow key could commit the row the wheel was leaving. (#10924)

  • Table's column settings plugin keeps its active columns in state. When the table re-renders on its own after a render React discards, such as a suspended transition, it shows the committed columns. (#10911)

  • Table: three of its keyboard tab stops draw the theme's focus ring instead of the browser's default: the scroll wrapper once the table scrolls inline, and the tree plugin's row expander and expand-all toggle. A theme's --focus-outline-* tokens now reach them. The scroll wrapper's ring sits inside its box, clear of a bordered Card's border, so a Card no longer cuts off its edges (a Card's rounded corners still trim the ring's square corners); at density="compact" the tree toggles' ring sits inside the toggle, so the row does not clip it. The row-expansion, grouped-rows and filter buttons are unchanged. (#8873)

  • Table: a row expansion panel with panelVariant="muted" now paints its wash on its cell, so with an animatedLayout handle the wash slides in with the panel's content. On iOS Safari the panel's edge trailed the rows gliding below it for a few frames; now they stay together. At rest the panel looks the same. (#10403)

  • A Table column-resize handle now announces its column's rendered width from the first render after layout, and its announced range always holds that width, also when the layout renders a column past its maximum or minimum width. Before, until a column was first resized, its handle announced the column's minimum width, so a screen reader read the wrong width. (#10593)

  • A Table column-resize handle on a column with no maximum width now announces a maximum at or above its value, so a screen reader no longer reads the width past ARIA's default maximum of 100. It uses the window's width as the stand-in maximum, as an unbounded Resizable region does, and announces its value in words, for example "133 pixels". A column with maxWidth is unchanged. (#10577)

  • A Table plugin's transformScrollWrapper className now reaches the table's scroll container beside the wrapper's own class; before, the wrapper replaced it. (#10702)

  • TableTree rows no longer carry aria-expanded, which is valid on a row only in a treegrid. Each row's expand and collapse button still exposes its expanded state, and rows keep aria-level for their depth. (#10538)

  • A swizzled Table now honors virtualization from the package's useTableRowsVirtualization, and the package Table honors a capability from a swizzled copy. Before, each copy recognized only its own capability, so a swizzled Table rendered every row and its code failed to type-check. The capability is read through a registered, versioned key, and its type carries one required brand member that is never present at runtime, so only the hook makes a capability. A virtualization value the hook did not make now gets its own development warning. No API changes. (#10798)

  • TabList tabs, including a TabMenu tab, grow with a larger text size. Their size now sets a minimum height instead of a fixed one, so at 200% text and above a tab label is no longer clipped. At the default text size nothing changes. (#10534)

  • TextInput, TextArea, NumberInput, DateInput, DateRangeInput, DateTimeInput, TimeInput, Tokenizer, Typeahead, PowerSearch, ChatComposer and RichTextEditor: when the primary pointer is coarse (a phone or tablet), the focus look shows at once, with no fade, so a tapped field repaints once while the keyboard rises. With a fine primary pointer, such as a mouse, a click and the Tab key keep the fade. ChatComposer's focus depth also shows at once under reduced motion, as the other fields' focus look already does. (#10611)

    Also released in @astryxdesign/richtext.

  • TextInput: the clear button now runs changeAction with the empty value and shows the optimistic empty value and busy state, as typing does. The clear goes through the input's own change handler, so onChange and changeAction receive a real change event (before, onChange received null as its event and changeAction did not run), onChange runs first, and its preventDefault() cancels the Action. (#6425)

  • A truncated Text or Heading mounts its tooltip on the first hover, tap or focus, not as soon as it renders, so a long table or list of truncated cells mounts no closed tooltip layers at load. The hover delay, the tap and the focus rules are the tooltip's own: a touch that becomes a scroll opens nothing, and a tap outside closes it. No visual change. (#10290)

  • Theming: a theme's :active override now follows the touch press model. With a mouse it still paints on :active. Under a finger it paints only while Astryx believes the press, so a themed control no longer flashes the theme's press color on the touch itself or while the page scrolls. (#10420)

  • In Safari on iPhone, text follows the reader's text size (Dynamic Type) in every theme, branded included. Where the browser exposes that size through font: -apple-system-body, as Safari on iPhone does, the theme's font sizes scale with the size the reader picked in Settings (Display & Text Size, then Larger Text), each size on the curve of the iOS text style it plays: body text like Body, heading-1 like Title 1, display text like Large Title, with every role kept in order. A semantic size a theme fixes at a length, such as the 0.75rem supporting text of Butter, Stone and Y2K, scales on the curve of the step it reads by default. At the default size every font size is the theme's own; the family, spacing, line-height ratios and the root font size never change, text inputs keep their 16px floor on iOS, and browsers without the keyword see no change. There is no option, prop or document attribute: the root Theme reads the reader's size from a hidden element. astryx docs typography/type-scale describes it. (#10416)

    Also released in @astryxdesign/cli.

  • Theme keeps its runtime-injected CSS in the document until the last mounted Theme using that theme unmounts. Before, the Theme that injected the CSS first owned the shared <style> tags, so unmounting it (a nested Theme around a dialog that mounted first, for example) stripped the component overrides and prose defaults from every other Theme still using that theme. Built themes, which import their CSS as a file, are unaffected. (#10178)

  • TimeInput shows the stepped time after an arrow-key adjustment made while a typed draft is pending, and leaving the field keeps that step. Before, the text typed before pressing ArrowUp or ArrowDown stayed in the box while the committed value and the screen-reader announcement had moved on, and blurring re-parsed that stale text and committed it over the step, silently undoing it. (#10179)

  • Timestamp exposes its details popup on the focusable time element. (#6298)

  • Toast: the viewport is each toast's only announcement owner. While an open modal hosts a top-layer viewport (the default), it announces each toast through that modal's own live regions, so a toast shown over a Dialog is announced once. Elsewhere it announces through the shared channel. A viewport rendered in place announces through useAnnounce, which delivers inside an enclosing Astryx modal. The toast card is no longer a live region inside a viewport, including while a modal hosts it. (#7871)

  • Tokenizer forwards cancel to its searchSource, so a newer search, closing the menu or the sheet, focus leaving the field, or unmounting cancels the consumer's in-flight search (for example, to abort its request). Previously the inline field wrapped the source to filter selected items and dropped cancel, and the bottom-sheet presentation never called it. (#8777)

  • A Toolbar with centerContent wraps its row when its lanes don't fit, as at a larger text size on a phone, instead of letting the center title spill over the buttons beside it and past the screen's edge. The end lane, and the center lane when it does not fit beside the start lane, move to a following line. The center sits at the toolbar's middle as before while each side lane fits in half of the width it leaves. In a narrow container, a center TabList now shows all its tabs and the row wraps, and its tabs scroll only when they are wider than the toolbar. (#10563)

  • A controlled Tooltip held closed (isOpen={false}) no longer opens on keyboard focus or a click into a text-field trigger. It reports that focus as onOpenChange(true) and shows nothing, so the caller decides, the same way Escape on a held-open tooltip reports onOpenChange(false) without hiding. Before, focus opened a held-closed tooltip anyway, and Escape could not close it. useTooltip reports the same focus through onShow under isOpen={false}. (#10490)

  • on a touch screen, keys typed into a text field no longer make the focus a closing dialog returns to a Tooltip or HoverCard trigger open its layer; Tab, Escape and arrow keys still count as keyboard input. useTouchTrigger's isTouchInteraction() from @astryxdesign/core/Layer stays true in that case too. A field in an iframe or Picture-in-Picture window is not read as a text-entry field, so typing there still opens the tooltip as before. (#10639)

  • A disabled TopNavItem now keeps its Tab stop, in both the bar and the drawer. Before, it rendered with tabIndex={-1}, so Tab skipped it and keyboard and screen-reader users could not reach it. Now it is a focusable link that exposes aria-disabled="true", while a click, Enter or Space still runs no onClick or onKeyDown, does not navigate, and does not close the mobile drawer. No API changes. (#9208)

  • TopNav's heading can now shrink, so a heading wider than the whole bar ends in an ellipsis instead of running off the bar and scrolling the page sideways. TopNavHeading keeps its logo whole, and independently linked lines wrap and keep their longest word; a plain line beside them still keeps its full width. (#9551)

  • TopNav wraps its bar when its regions don't fit one row, as at a larger text size, instead of letting the end content (such as buttons) cover the navigation items. The end content moves to a second line. While the regions fit, the bar and its centered content look as before. (#10571)

  • DateInput's touch sheet keeps keyboard focus when the month reaches the end of the range, by an arrow press or a swipe, while a month arrow holds focus. That arrow hides there, so focus now moves first to the other month arrow, or to the month-and-year button when both arrows hide. Before, focus fell to the page. (#10231)

  • Toast: the useToast fallback warning and its server-rendering error no longer tell you to wrap the app in <AppShell>. AppShell provides no layer context, so that advice left the warning in place. They now name only <LayerProvider>, which removes it. (#10875)

  • With a mouse or trackpad, a date or time sheet's wheel no longer turns back to the row a key step is leaving. A key step commits its row at once and then scrolls there. On a busy page or a slow device the scroll could draw no frame for longer than the wheel's quiet period, or the browser could end it part-way, and the wheel took that pause for rest: it committed the row under the band and turned back there. A rest before the step's row is reached now finishes the scroll instead. A scroll a person makes still rests where it stops. (#10610)

Performance

  • Closing a Popover or DropdownMenu with Escape, or a DropdownMenu with a pick or a press on its trigger, runs one layout again, not two. A closed trigger stops being a CSS anchor, and these closes took the trigger's anchor name away after the hide's own layout, so the browser laid out once more. Our hide now drops the name before its layout, unless the layer's closed state keeps it shown to fade out. A close the browser runs itself, such as a light dismiss, still drops the name at its toggle event. A layer that fades out keeps the name until its fade ends. (#10346)

  • Popover, DropdownMenu and its submenus, Selector, ComplexSelector, PowerSearch and Breadcrumbs' menu play their entrance on the surface inside the layer, not on the anchored layer. Chromium laid the anchored layer out again on every frame of its 150 ms entrance, two to four times per frame, whenever the page had other frame work. The entrance looks the same. layerAnimations is unchanged; its docs now say to apply it to the surface. Applied to the layer, it still plays as before. (#10561)

  • While a Tooltip or another anchored layer plays its entrance, most reads of the layer's box write no styles. Before, each read during the entrance overrode the layer's transform, read the box and put the transform back, so the browser recomputed style and layout twice in that frame (measured in Chromium: 8 style writes, 2 style recalculations and 2 layouts per read). Now a read on the middle frames of an entrance takes the entrance transform out of one box read by calculation, and gets the same box. The read that shows the layer and any read while its entrance is pending, a read whose box does not yet carry the entrance (in Chromium, a layer at a fallback position), the last frames of an entrance, a layer under a CSS zoom other than 1, and a translate given as a percentage still use the override, as before. Placement, the entrance animation and the keyboard cap do not change. (#10240)

  • Motion: a Switch at rest holds no compositing layer under a coarse pointer. Its colour fade and its lift's shadow take a layer only while they move. (#10542)

  • Motion: the motions that one callback starts on elements that came in the same task read their stylesheets' will-change in one style pass, after the callback. Before, each element forced a style pass of its own: eight for a list whose eight rows glide together. (#10728)

  • Under a finger, Popover plays its growth on the surface inside the layer, not on the anchored layer. Chromium restyles the growth's animations twice as they start and once as they end, and each restyle laid the anchored layer out twice, its backdrop included. Under load those layouts landed inside the growth. The popover looks the same. (#10717)

  • Under snap paging, a Carousel's release bakes the settle's keyframes once, not once per item, and plays the settle only on the items that can show. With 9 items the release takes about a third less main-thread time, and with 30 items about two thirds less. (#10568)

  • useTableRowIndex builds its number column once per plugin, so a parent re-render no longer re-renders every row of a table that shows row numbers. (#10654)

  • Truncating Text and Heading take a newly mounted element's first measurement from the shared resize observer's first notification, after the browser's own layout, instead of forcing a layout pass after each commit. Pages and windowed tables that mount many truncating texts commit faster; the tooltip behaves as before. (#10668)

Documentation

  • The source header of Layout/containerCorners.stylex.ts (shipped in the package) now says that the --container-corner-* variables Card and Dialog set are in astryx.css, undocumented, and read only by Table and List; it had said they stay out of public API. (#10701)

  • Hook docs for useCalendarConstraints, so astryx hook useCalendarConstraints and astryx search return it, and a useCalendarConstraints — Hotel Booking Range example block: a booking range picker that enforces min/max dates, disables closed Mondays, and caps reservations at 7 nights once check-in is selected. Copy it with astryx template useCalendarConstraintsHookUsage <path>. (#10151)

    Also released in @astryxdesign/cli.

  • Hook docs for useCalendarNavigation, so astryx hook useCalendarNavigation and astryx search return it, and a useCalendarNavigation — Month Pager example block: a month-by-month calendar with Previous/Next controls, localized month-year header, and cross-month arrow key navigation using pendingFocus. Copy it with astryx template useCalendarNavigationHookUsage <path>. (#10154)

    Also released in @astryxdesign/cli.

  • Hook docs for useLinkify, so astryx hook useLinkify and astryx search return it, and a useLinkify — Links in a Comment Thread example block: plain-text comments where the URL and email become Links and a custom #123 issue-reference pattern shows the patterns option, while a comment without matches stays plain. (#9969)

    Also released in @astryxdesign/cli.

  • Add usePowerSearchConfig hook docs and a filtered directory table example block (#10660)

    Also released in @astryxdesign/cli.

  • Add useTableColumnSettingsState hook docs and a column visibility picker example block (#10652)

    Also released in @astryxdesign/cli.

  • HoverCard: a trigger inside SVG <foreignObject> cannot be a CSS anchor in Chromium, so its card opens at the viewport corner. The docs now say so and show the fix: render the trigger in an HTML layer positioned over the SVG node. (#10908)

  • LayoutPanel's JSDoc example wraps its Layout in a Card instead of LayoutContainer, a component Astryx does not have. (#10716)

  • ChatLayout's guidance explains how a reply gets one right edge: Markdown prose keeps its 680px measure while code blocks and tables span the message column, so pass contentWidth="100%" to the reply's Markdown. Markdown's default measure is unchanged. (#6608)

  • useClickableContainer documents a pressable surface without Card chrome: the hook on the surface, usePressFeedback with interactionOverlayStyles.backgroundColor for the hover tint and touch press, a visually hidden <button> or <a> as the named control, and focusOutlineStyles.focusWithin for the shared focus ring that ClickableCard draws. ClickableCard's docs point chrome-free use to it. (#10880)

  • The collapsible, collapsedSize, isCollapsed and onCollapseChange descriptions in the Resizable and useResizable docs name what collapses a region, a lift or a key step under the collapsed size rather than a drag alone, and what reports a collapse change. (#10635)

  • Section's docs show how to center it: set marginInlineStart and marginInlineEnd to 'auto' in xstyle. (#10207)

  • SegmentedControl: the label contrast guidance says the track is translucent, so label contrast depends on the background behind the control, and asks you to check the labels on every background it sits on, including a custom page color. (#10882)

  • SideNavCollapseButton's docs now list every key its collapsible prop takes: the whole controlled config SideNav gets, not only isCollapsed and onCollapsedChange. (#10282)

  • The Skeleton radius prop's description no longer says 4 is 16px. 4 maps to the same container radius as 3 (12px), so the two values draw the same corners. No behavior change. (#10750)

  • Tab's docs show how to give a tab help text: anchor a Tooltip on the Tab through its ref, so the description lands on the tab, and put a decorative info Icon in endContent. endContent is documented as non-interactive: a button, link, or InfoTip inside it would nest a control inside the tab's button. (#10734)

    Also released in @astryxdesign/cli.

  • Tab's docs show how to run the selected indicator edge to edge in one strip: a nested Theme that takes the app's color mode, extends the app's theme and sets insetInline: '0' on the tab-indicator target, so other TabLists keep the default indicator. The override is best effort across releases. (#10814)

  • TableSelectionToolbar documents how to select every row that matches a query across server pages with today's API: the app keeps "every matching row minus exclusions" and passes the toolbar its own count and label. In this mode the toolbar's selectedKeys holds only the loaded rows. (#10271)

  • The Tailwind guide (astryx docs styling-libraries/tailwind) adds a recipe for an app that already uses Tailwind: keep the app's own @import "tailwindcss", put the @layer line above it, and add the Astryx stylesheets after it. It explains why the @layer line must come before the Tailwind import: below it, the Astryx reset outranks every utility class, and nothing reports it. The Core README's Next.js + Tailwind section points to it. (#10532)

    Also released in @astryxdesign/cli.

Other Changes

  • Internal: a ChatComposerInput source comment that restated the code is removed. No change to behavior or output. (#10811)

  • Internal: Core's internal touch entry re-exports the action-group tracking hook, which gains a no-paint option and press callbacks for Lab's TabBar. A tab-bar string joins Core's message catalog. Apps import nothing new. (#9699)

  • Internal: the undocumented @astryxdesign/core/internal/reorder entry also gives Lab's ListInput List's reorder session for rows that are not ListItems: useListReorderScope and ListReorderScope. List runs its own rows' session through the same session hooks, so its callers see no change. A ListInput string joins Core's message catalog. Internal entries are outside the public API and its compatibility promise. (#8163)

  • Internal: the undocumented @astryxdesign/core/internal/reorder entry lets Lab's TransferList and TransferListSelector reorder through a createReorderable object: adaptReorderable runs the owner's engine with the collection's own moves, and the owner's start(key) still finds the collection's list. Existing List callers are unchanged. Internal entries are outside the public API and its compatibility promise. (#8143)

  • Internal: Core's internal touch entry re-exports its scroll test for Lab's TabBar. Apps import nothing new. (#10791)

  • Internal: per-instance warning deduplication and temporary focus targets each have one shared owner in Core. Resizable, Dialog and TabList dedupe their development warnings through it, and Chat's Done button parks focus through it. Warning text and focus behavior are unchanged; apps import nothing new. (#10655)

  • Internal: the More tab's label, a tab-bar string, joins Core's message catalog for Lab's TabBar. Apps import nothing new. (#10791)

  • Internal: Core's internal finger-scroll reader can answer for a past time on the page's clock, for Lab's NavigationStack scroll edge. Apps import nothing new. (#10540)

  • Internal: the undocumented @astryxdesign/core/internal/line-clamp entry gives Lab's LineClamp Core's focusable selector, the shared mutation observer and its flush, and the shared truncation tooltip. The shared mutation observer now passes each registration the records that reached it, and existing callers ignore them, so their behaviour is unchanged. Internal entries are outside the public API and its compatibility promise. (#4259)

  • Internal: the layer's outside-press watch can follow chosen pointer types, close as a press that starts outside begins to pan (on either axis, at core's pan threshold) or when the browser takes it, and judge a press inside a closed shadow root, which the document sees as its host. No component passes these options yet, so every watch keeps today's behavior. Not exported from the package entry point. (#9814)

  • Internal: Popover keeps its chosen presentation (the sheet or the anchored layer, the touch grow or the pointer entrance) from the open until its close settles through the layer runtime's one presentation hold (Layer/presentationHold.ts). No behavior change. (#8587)

  • Internal: one owner module now turns the browser's scroll anchoring off on a container whose position a component owns. useChatStreamScroll uses it while it follows the end, with the same behaviour as before, and the undocumented @astryxdesign/core/internal/scroll-anchoring entry gives its writer to Lab. Internal entries are outside the public API and its compatibility promise. (#10173)

  • Internal: Selector and MultiSelector keep the presentation an opening chose, and a closing sheet mounted until its focus handoff, through the layer runtime's presentation hold (Layer/presentationHold.ts). No behavior change. (#8591)

  • Internal: Core's shared-realm module gains helpers for state that the package and a swizzled copy share (state, maps, stores, brands and sequences). No API or behavior changes. (#10817)

  • Timer's tick pipeline, format ladder, cadence switch, and resource cleanup were audited with no behavior defect found. Two previously untested guards are now pinned: the wait for a distant future startTime is capped at the 32-bit setTimeout ceiling, and an unknown format value coerces to elapsed. The Default story now asserts in a real browser that the display advances on its own. (#6609)

@astryxdesign/richtext

New Features

  • In RichTextEditor, Tab and Shift+Tab in a code block indent and outdent every line the selection touches, by the indent the block already uses (a tab when one of its lines starts with a tab, otherwise the fewest leading spaces of any indented line, kept between 2 and 8, otherwise 2 spaces). The selection stays on the same characters and each press is one undo step. Tab used to put a tab character in place of the selection, and Shift+Tab did the same. Escape then Tab still moves focus out of the editor. (#8579)

Fixes

  • RichTextEditorAutoLinkPlugin no longer turns a URL inside a code block into a link. A URL typed or pasted into a code block, or one in an imported fence, now stays code. Before, the link nested inside the code block, and the exported Markdown fence lost it. (#10664)

  • In RichTextEditor, pasting rich content into a code block inserts plain text: the plain text on the clipboard, or, when there is none, each pasted block as one line of its text. Headings, lists, tables, and formatting used to land inside the code block, where the Markdown export wrote them as plain lines of the fence, so the document read back differently from what the editor showed. (#8580)

  • A link inside a disabled RichTextEditor no longer navigates, shows no pointer cursor, and is not a tab stop. Before, a disabled editor's links acted as live links: a click or Enter opened them, and Tab stopped on each one. (#10679)

Documentation

  • RichTextView, RichTextEditorToolbar and RichTextEditorAutoLinkPlugin each ship a doc file, so astryx component, astryx discover and the docs list their props, defaults and usage. (#10407)

    Also released in @astryxdesign/cli.

  • A source comment in the editor theme now says that core Markdown gives each block the direction of its own text, while the editor keeps blocks in the surface's direction. (#10293)

  • RichTextEditor documents how to switch between reading and editing in place without reflow: set the field's inset and border outside the reading width with xstyle, so every paragraph keeps its line count across the switch. A standalone editor keeps its form width. (#10429)

@astryxdesign/cli

New Features

  • astryx discover lists the Lab DialogHeroHeaderShowcase template from @astryxdesign/lab. (#10109)

  • astryx discover lists the Lab DialogHeroHeader component from @astryxdesign/lab. (#10108)

  • astryx lists a create… factory documented as a hook, such as createReorderable, with the hooks. (#8139)

  • astryx docs also opens a docs-tree node by its bare name, or by a route that drops the prefix its name repeats, when exactly one node matches: astryx docs tokens-color and astryx docs tokens/color both open tokens/tokens-color. Two or more nodes answering one name still fail with the suggestions they always listed, and an experimental doc still opens only by its marked route. (#10387)

  • astryx docs --depth <levels> now names the docs below each namespace on the topic list, by the route that opens them, so one read learns every route instead of one read per namespace. Without --depth the list is unchanged, and the JSON keeps its shape: the new children sit inside each meta.namespaces entry. (#10393)

  • The official-packages discovery list now includes the Lab LineClamp component, so astryx can find and describe it. (#4259)

  • Replace the theme showcase's custom layout, text and icon styling with Astryx components. The embedded preview paints the theme's body behind card-token surfaces instead of a surface-colored card band. The new standalone top-navigation shell uses the theme's surface canvas; the embedded view stays shell-free. Make demo inputs, cart feedback, inventory filters and chat respond locally, with clearable filters and no payment submission. Keep the template id, gallery visibility and demo content. (#10404)

Fixes

  • Comments, a codemod note and the grouped-table template's sample data no longer use internal-format identifiers. (#10958)

  • astryx swizzle --list no longer lists Core's shared folders (animator, gestures, i18n, internal, keyboard, motion, privateAttributes, __tests__) as components. A copy of gestures imports @astryxdesign/core/motion, which the core package does not export. (#10185)

  • astryx component prints a theme warning as one line: control and format characters in the configured astryx.theme value or the generated theme record print as �, so a newline or an escape sequence in a checkout cannot rewrite the terminal. (#10764)

  • astryx docs <topic> reads a topic authored as .doc.ts from an installed integration on every supported Node, and from a linked integration below Node 22.18. Topic and docs-tree files now load .ts through the same TypeScript loader as component docs, where a plain import failed under node_modules ("Stripping types is currently unsupported") and below Node 22.18 ("Unknown file extension .ts"). astryx search also reads an installed .doc.ts component doc, so it finds the component by its keywords, shows its description, and lists a family's members. .doc.mjs topics load as before. (#10763)

  • A long-running process that calls the CLI's API, such as a docs site's search route, now reads an edited CommonJS .doc.js file again in docs, search and template reads. It read the doc as first loaded. (#10760)

  • A command that reads a project module again after changing it, such as astryx integration add checking the manifest it just wrote, now reads the change for an ES module .js file in a package without a "type" field. It read the module as first loaded. (#10745)

  • Icon sets: integration check now accepts a generated declaration file (*.generated.d.ts) whose long type arguments a formatter such as Prettier re-wrapped. Before, the re-wrapped line made the check report icon_generated_types_mismatch and fail. A changed declaration still fails the check. The experimental icon commands are unchanged. (#10898)

  • astryx build: when an idea names a family only after its head ("a saml for a login") and the family's base template matches no other word, the variant that outranks the base now stays the start (login-sso, not login), so adding page templates no longer flips such ideas to the base. Ideas that name the family in their head ("saml login") start as before. (#10431)

  • astryx experimental render no longer dies silently with exit 139 when Node itself crashes while it renders a page. The page now renders in a separate process, so a crash (a crash signal, or a crash status on Windows) ends only that process, and the render fails with ERR_RENDER_FAILED and a message that names the Node version and path; a signal that stops it names them too. That process ends when the CLI ends, so a page that never finishes does not outlive a stopped CLI. Some Node builds crash in Intl.Segmenter, which components such as Avatar use to split text: on such a build the render runs the page once more without Intl.Segmenter and adds a node-crash warning that names the Node build and says the output may differ slightly (a joined emoji, a flag, or a letter with a separate accent mark in initials), so pages with Avatar render again. (#10915)

  • astryx search puts the results that fit the whole query higher. The first result stays the same. At the default limit, and at any limit of 10 or more, the set of results stays the same. A smaller --limit shows the top of the new order, so it can show a different result. Below the first result, inside each group of equally strong matches, a result whose docs fit the query much worse than the others in its group moves after them. A result whose name spells two or more of the query's words never moves down; it can still move up. The fit uses the same word-meaning weights astryx build uses to pick blocks, so a new component or template is placed from its own docs. A query that is an entry's whole name keeps its answer. (#10926)

  • Every import in an astryx swizzle copy now names a file the copy holds or a path the owner package exports. Before, the copy sent every import that left the component folder to the package's folder entry, which does not export the internal helpers most components use, so Button, Dialog, Table and about 100 other copies failed to type-check or bundle. Now the copy imports what the package exports from the package, carries every internal module it needs beside it in the package's folder layout, and copies the nested folders it imports (such as Table's plugins/). Test helpers and notes are no longer copied. A helper an earlier swizzle wrote is reused when it is the same and is never replaced by a copy that carries it, even with --overwrite: a different one stays, and the new copy writes its own under a version-suffixed name. Swizzling the component whose folder holds that helper treats it as one of that component's own files, which --overwrite replaces. A private attribute owner module and the style modules it imports are the exception, as before: a changed copy fails the command without --overwrite and is replaced with it. Fixes #3506. (#10197)

  • A swizzled component whose source imports another Astryx package's internal module, such as a Lab component that uses Core's gesture primitives, no longer imports that private path, except a StyleX module that package exports, which the copy keeps importing by that path because its identity is its path. Swizzle carries every other such module, and what it reaches, into the copy under a folder named after its package (for example components/astryx/core/internal/touch.ts); that package's public names are still imported from the package. (#10595)

  • astryx swizzle reports the npm packages a copy imports, such as d3-scale for a chart, with the version range the owner package declares, so the copy builds once you install them. The swizzle.copy receipt lists them in dependencies, and the text output names them to install. Node's built-in modules and the owner package itself are not listed. (#10502)

  • astryx swizzle writes an astryx-swizzle.json marker into an output folder it owns (one the copy creates, an empty one, or one it marked before), naming each package the folder's copies came from and its version. @astryxdesign/build's Babel, PostCSS and Vite integrations, and withAstryx() under webpack, compile the copies under that folder below astryx-theme, so a theme's component overrides reach a swizzled component as they reach the package component. For a folder that already holds other files it writes no marker and says what it found and how to add the marker by hand; the swizzle.copy receipt reports this in a new optional marker field. Under Turbopack and with the stock StyleX plugins, a copy's styles still land above the theme. The marker is not listed among the copy's files. (#10483)

    Also released in @astryxdesign/build.

  • The table-filter template's link-style header actions set minHeight: 0 with their auto height, so no minimum holds them taller than their text. (#10588)

  • astryx template <name> puts the template a typo slips from first in its suggestions: a name one or two slips away (a letter added, dropped or changed, or two letters swapped, as in AppShellCotnentOnly or dasbhoard) ranks just below the templates that carry every typed word. The suggestion list is never longer than before. (#10712)

Documentation

  • A new Course Detail page template: a long-form sales page for a single offering in two columns — the long read (a preview poster with a fact row, an about block with skill chips, a module syllabus whose sections collapse module by module, an instructor card, reviews with a scored-aspects panel, and a FAQ) beside a sticky enroll rail (the price with a compare-at, a self-paced or cohort start picker, and a weekly-pace slider that computes a finish date). Copy it with astryx template course-detail <path>. (#9753)

  • The File Explorer page template scrolls its columns with StackItem and HStack isScrollable instead of custom overflow and flex styles, and names each column after the folder it lists while the column scrolls. Its chevrons now mirror in right-to-left, and a detail pane scrolled to its end keeps its bottom padding. (#10421)

  • A useInteractiveRole — One Tile, Four Roles example block for the existing hook docs: a polymorphic Tile rendered four ways — href link, onClick button, Popover-trigger context button, inert content — showing the resolution order in one place. (#9972)

  • Fix the useStreamingText — Streaming Response example block: it passed isStreaming as false, so the streaming reveal it exists to show never ran. The block is now a chat-style reply card with a Regenerate button that streams the response in bursts — the hook reveals it steadily while streaming and snaps to the full text when it ends. (#9975)

  • The grouped table page template groups its rows with useTableGroupedRows instead of hand-rendered header rows and a hand-written colgroup. Its columns now have headers, each group's toggle is a button that reports whether its group is expanded, and the row menu's Move to project arrow mirrors in right-to-left. (#10423)

  • The useInputStatusIcon — Coupon Code Status example block's field applies inputWrapperStyles.textField. A field copied from it now shows its focus look at once when the primary pointer is coarse, as Core's text fields do. (#10649)

Other Changes

  • Internal: doctor.mjs and template.mjs are formatted with Prettier. No change to any command's output. (#10988)

  • Internal: the icon-set export reader reuses the shared top-level helper, and a source comment on look refreshes is shorter. No change to any command's output. (#10449)

  • Internal: the doc importers import each doc file through one small module that the CLI's own test project loads natively, so its tests no longer time out on a busy machine. No change to any command's output. (#10740)

Themes (@astryxdesign/theme-*)

Fixes

  • Gothic: accent text and accent icons stay readable on a light media surface. They used Gothic's light accent there, near-white on a light band (1.14:1); now they follow the surface's dark accent (18.7:1 against a white surface). The CLI's Gothic theme template carries the same change. (#10852)

    Also released in @astryxdesign/cli (templates).

  • Neutral: an unselected SegmentedControl label meets 4.5:1 in dark mode on AppShell's surface. The track is translucent, so it lightens the surface under the label: #9e9e9e on #3c3c3c measured 4.11:1. The label now takes neutral.dark[80] (6.46:1), the same lift Neutral already gives secondary text on its selected rows and pills. Light mode is unchanged, and the CLI's Neutral theme template matches. (#10881)

    Also released in @astryxdesign/cli (templates).

  • Matcha: light-mode secondary text is slightly darker (Neutral Light 30), so it meets 4.5:1 on the body (5.11:1, was 3.83:1), the surface and the card. (#10932) [fix] Y2K: light-mode secondary text is slightly darker (Neutral Light 35), so it meets 4.5:1 on the Periwinkle body (5.14:1, was 4.25:1). The CLI's Matcha and Y2K theme templates match.

    Also released in @astryxdesign/cli (templates).

  • The Butter, Stone and Y2K themes set supporting text (--text-supporting-size) in rem instead of a fixed 12px, so it grows when a reader raises the browser's default font size, like the rest of the text. At the default size it is still 12px. The CLI's copies of these themes match. (#10320)

    Also released in @astryxdesign/cli (templates).

@astryxdesign/build

Fixes

  • A swizzled copy can now compile as Astryx source in the Babel, PostCSS, and Vite integrations. Every module under a folder that holds an astryx-swizzle.json marker gets the library class prefix and lands in @layer astryx-base, below astryx-theme, so a theme's component overrides reach it. astryx swizzle does not write the marker yet: add the file to the swizzle output folder. Under webpack, withAstryx() rebuilds a cached copy when its marker appears or disappears. Turbopack builds do not read the marker yet. No API changes. (#10334)

@astryxdesign/theme-butter

Fixes

  • Butter's buttons take inline padding only and use Button's own block padding. Their height is unchanged. (#10588)

  • Theme packages publish the declarations of their ./theme.css export. The export pointed types at theme.css.d.ts, which the package didn't ship, so TypeScript found no types for import '@astryxdesign/theme-<name>/theme.css'. The export is unchanged; the file is now published. (#10903)

    Also released in @astryxdesign/theme-chocolate, @astryxdesign/theme-gothic, @astryxdesign/theme-matcha, @astryxdesign/theme-neutral, @astryxdesign/theme-stone, @astryxdesign/theme-y2k.

  • Theme packages declare "type": "module", so bundlers and Node read the ./built entry as the ES module it is. Before, it was an ES module in a .js file in a package without a type, which tools that follow the package's type read as CommonJS. Every export subpath stays the same. The CommonJS build of the source entry is now dist/source.cjs behind the same require condition, with its own CommonJS declarations (dist/source.d.cts), so TypeScript types each condition in the format it loads. As before, require('…/built') loads an ES module, so it needs a Node version with require(esm) (22.12 or later). Deep imports that reach past exports into dist/ change too: dist/source.js is now dist/source.cjs, and a require of dist/<theme>.js gets an ES module (require(esm), Node 22.12 or later). (#10876)

    Also released in @astryxdesign/theme-chocolate, @astryxdesign/theme-gothic, @astryxdesign/theme-matcha, @astryxdesign/theme-neutral, @astryxdesign/theme-stone, @astryxdesign/theme-y2k.

Known issue

  • If your bundler lowers CSS light-dark(), media surfaces in themes other than Gothic may lose their color overrides, which can make accent text low contrast. This will be fixed in the next stable release. Gothic does not have this issue in v0.7.12.

Contributors

Thanks to everyone who contributed to this release:

@aashish254 @AKnassa @bhamodi @cixzhang @duytbp @emilyhorsman @ernestt @freddymeta @Geervan @harjothkhara @HelloOjasMutreja @humbertovirtudes @hurrymaplelad @imdreamrunner @jiunshinn @josephfarina @LetMeSleep8h @ManoharPaturi @mego1123 @parinith-web @potatowagon @rubyycheung @thecodebasedot @utkarshpatwa393-sys @Varshavia @vittoriobusatta @vjeux @WeAreResilience

Full Changelog: https://github.com/meta/astryx/compare/v0.7.11...v0.7.12

5 hours ago
socket.io

engine.io-client@6.6.8

Bug Fixes

  • types: avoid MapIterator in generated typings (#5568) (b8b6ef0)
  • honor Max-Age=0 and unparseable expiry attributes (#5585) (303a765)
  • keep '=' characters in query values (#5574) (238db10)
  • match cookie attribute names case-insensitively (#5589) (294ad42)

New Contributors

Thanks a lot to them!

Dependencies

6 hours ago
Leaflet

v2.0.0-beta.3

The third beta of Leaflet 2.0 makes chained view changes like setView(c).setZoom(z) or panTo(c).panBy(o) end where you'd expect, makes adding and removing event listeners up to hundreds of times faster on busy maps, and validates coordinates so bad data fails where it's created instead of somewhere deep in rendering. It also brings a round of keyboard and screen reader fixes for markers, popups and controls, along with over 15 bug fixes.

⚡️ Performance

  • Make adding and removing event listeners constant-time: adding 50,000 markers with popups to a map takes 0.5 s instead of 15.6 s, and on, off, once and fire are faster in the common small cases too (#10537, #6914, #6923)
  • Position tooltips without measuring them, so maps with many permanent tooltips zoom without a forced layout per tooltip (#6948)
  • Construct Point objects ~40% faster, and the array and object forms of Point and LatLng 5–15% faster (#10550)

✨ Improvements

  • Make view changes compose regardless of call order and timing: setView(c) then setZoom(z) (or the reverse) keeps both, panTo(c).panBy(o) ends at c + o, and zoomIn() calls during a zoom animation add up (#10549, #2609, #2915, #7766 by @Falke-Design)
  • Validate Point and LatLng coordinates, accepting numbers and numeric strings, and make Point.validate() and LatLng.validate() match exactly what the constructors accept, so they can filter data (#10550, #7765 and #7767 by @johnd0e, #8214 by @Falke-Design)
  • Allow dragging the map during a zoom animation instead of treating the drag as a click (#8201)
  • Let shift-drag pan the map and drag markers when box zoom is disabled (#2772, #6233, #7088)
  • Honor coords.z in TileLayer#getTileUrl, so it can build URLs for zoom levels other than the current one (#8849, #9201 by @yohanboniface)
  • Style <button> and other role="button" elements in leaflet-bar controls like links (#9462, @someonewithpc)
  • Support numeric strings and a partly automatic size like [100, 'auto'] in iconSize (#9253)
  • Add a getEvents() hook to controls, like the one on layers: the map listeners it returns are added and removed with the control (#10229)

♿️ Accessibility

  • Bring a focused marker to the front whatever riseOnHover is set to, so it's never hidden under its neighbors (#10536, #8078)
  • Return focus to the marker whenever its popup closes with focus inside it, not only on Escape (#10536, #8115)
  • Set aria-expanded and aria-controls on keyboard-accessible markers with a popup (#10536, #7527)
  • Move focus into a popup opened from a marker with the keyboard, and keep focus on a marker activated by a screen reader (#8113, #8114)
  • Keep focus on a control after clicking it instead of moving it to the map (#7947)
  • Make Space activate the zoom, layers and fullscreen buttons and the popup close button (#7369)
  • Open tooltips only on keyboard focus, so a tooltip no longer appears when the window regains focus or a popup closes (#9150)
  • Translate the default marker alt text with the translate map option (#10542 by @nfreear, #10541)
  • Raise the attribution and scale contrast to pass WCAG AA, with solid backgrounds and underlined links under prefers-contrast: more (#6742, #7538, #10536)

🐞 Bug fixes

  • Fix tiles flickering during fractional zoom animations past a layer's maxZoom (#8300, #8311 by @sbquinlan)
  • Fix polygons disappearing when weight is a string (#6072, #7432)
  • Fix tooltip positions in right-to-left containers (#7201, #7782)
  • Fix the expanded layers list sometimes being covered by other controls (#7944)
  • Fix a dragged marker's autoPan panning past maxBounds (#10539 by @A350xt, #6219)
  • Fix DocumentFragment popup and tooltip content being empty after the first open (#10540 by @A350xt, #3922)
  • Fix setIcon keeping the size, anchor, bgPos and crossOrigin of the previous icon when the new one doesn't set them (#10538 by @b84330808)
  • Fix fitBounds and getBoundsZoom zooming all the way in when the padding is larger than the map (#10544 by @jayhemnani9910)
  • Fix overlaps() on Bounds and LatLngBounds returning true for a point or line inside the other bounds (#10547 by @xunguangzlj-cloud)
  • Fix a layer with a NaN minZoom or maxZoom breaking the map's zoom limits (#10548 by @xunguangzlj-cloud, #8354)
  • Fix togglePopup() and toggleTooltip() throwing on a layer that isn't on the map (#10551 by @Enesuygurs)
  • Fix listeners added during fire being called by that same fire, a regression from 1.x (#10537)
  • Fix setView with worldCopyJump leaving the map outside the main world copy when it doesn't animate (#10549)
  • Fix getCenter() returning a center snapped to whole pixels right after the map is created

📝 Docs

  • Mark the text that the translate option translates with a "Translatable" badge in the reference (#10546 by @Falke-Design)
  • Document the flyTo and pinch flags of move and zoom events (#9775), and how to stop control events from reaching the map (#10299)

⚠️ Breaking changes

  • LatLng and Point throw Invalid LatLng object / Invalid Point object for coordinates that aren't numbers or numeric strings. null, '' and booleans used to become 0 or 1, and missing coordinates ({lat: 50}) gave NaN. A non-numeric altitude throws too, and null altitude means no altitude instead of 0. Point converts numeric strings to numbers instead of keeping them (#10550)
  • A non-numeric radius on a circle or circle marker throws, and a numeric string is converted to a number
  • zoomIn(), zoomOut(), setZoom(), panTo() and panBy() build on the view a running animation is heading to: two quick zoomIn() calls (or clicks on the zoom button) zoom in two levels. panBy during a zoom animation waits for it to end (#10549)
  • riseOnHover only controls hover: focused markers always come to the front (#10536)
  • The attribution and scale controls have black text on a 90% white background, and attribution links are a darker blue (#6742, #7538)
  • Control#_refocusOnMap is gone. ZoomControl, ScaleControl and AttributionControl add their map listeners through getEvents(), and ZoomControl and ScaleControl no longer have an onRemove method
  • TileLayer#_getZoomForUrl takes the tile zoom as an argument
10 hours ago
cherry-markdown

cherry-markdown-vscode-plugin@0.4.0

Minor Changes

  • feat(vscodePlugin): 统一上传配置、支持 PicGo multipart,并为重要行为变化提供一次性更新说明

    • 上传配置按当前文档所属文件夹、工作区、用户、默认值的顺序读取。上传器地址和认证头整体取自同一层,避免项目地址混入全局认证信息。
    • 配置无效时阻止上传并提示修正,不静默切换上传目标。
    • 重要行为变化会显示一次性更新说明通知,可查看随插件打包的本地说明或打开上传设置,也可从命令面板重新查看。通知按重要变更标识记录,普通 patch、关闭通知和重载窗口不会重复提示;首次上线无法可靠区分新装和升级,因此首次启用都会提示一次。
    • 仅记录扩展内部通知状态,不迁移用户设置,上传时的配置错误提醒继续保留。
    • CustomUploader.format 支持 binary 和 multipart,省略时仍使用原有的 binary 行为。
    • PicGo 2.4.0+ 用户可设置 ImageUploadMode=remote、format=multipart、fieldName=files 继续上传;插件直接发送文件内容,自动生成表单边界和长度,不根据地址猜测协议,也不在失败后切换路线。
    • 停止使用旧 UploadType、PicGoServer 选择上传方式和地址,不提供旧路径列表或剪贴板协议适配。
    • 仅有旧配置时,上传提醒会说明本地、Base64、远程三种新模式,并提供 PicGo 配置示例,可打开设置或配置说明;此过渡提醒计划在下一个 major 版本移除。不会自动修改或清理用户已有设置。
    • 受限模式只允许用户级上传设置;用户可通过 VS Code 原生工作区信任界面授权,再使用项目配置。预览显示当前上传目标和配置来源。
    • 原有手动主题继续保存在扩展状态中,不再自动读取或迁移已移除的旧主题设置。 @RSS1102(#1943)(59de6c1)
  • feat(vscodePlugin): 支持跟随 VS Code 主题并在预览中搜索 @RSS1102(#1938)(37e4600)

Patch Changes

  • fix(vscodePlugin): 将上传结果绑定到原文档的唯一占位,避免切换文档后误插入其他文件

    • 上传期间切换或关闭预览时,仍可更新尚未关闭的原文档;占位被删除、撤销或复制,或原文档已关闭时取消自动插入。
    • 支持从 Webview 传递文件内容,不再要求浏览器文件对象提供本地路径。
    • 修改保留为未保存编辑,不自动写入磁盘。
    • 同时选择图片“边框”和“无边框”时,继续优先使用“无边框”,并保留阴影、圆角等其他样式。
    • 上传失败时清空原文档中仍唯一存在的临时图片 URL,保留图片名称和周围编辑;已撤销、复制或关闭的目标不再修改。 @RSS1102(#1942)(ac4950f)
10 hours ago
cherry-markdown

@cherry-markdown/client@0.5.3

Patch Changes

10 hours ago
cherry-markdown

@cherry-markdown/milkdown@0.0.2

Patch Changes

  • feat: 优化链接浮层,支持直接打开、复制、编辑和取消链接,外观跟随 Cherry 主题。支持使用 Alt+F10 进入链接操作条,同时保留编辑器原有的 Tab 行为。调整链接编辑表单样式;取消链接可一次撤销,滚动或调整窗口时保留未保存的表单内容。 @RSS1102(#1929)(44f6e87)
  • fix(milkdown): 同步 Cherry 的 URL 协议校验,阻止通过字符实体编码绕过危险协议检查 @RSS1102(#1947)(372b561)
  • Updated dependencies [1be27df, 2318517, 1e76861, 8112362, b2cc414, 464480b]:
    • cherry-markdown@0.11.13