Skip to content

Releases: ngrok/mantle

@ngrok/mantle@0.76.5

19 Jun 00:55
90ab13e

Choose a tag to compare

Patch Changes

  • #1263 3703172 Thanks @cody-dot-js! - Add expandable-row support to DataTable for inline, per-row detail panels — restoring the click-to-expand record inspection pattern that the migration off GenericTable lost.

    New API, all driven by native TanStack Table expansion state (no invented state):

    • DataTable.Row gains an optional renderExpanded prop — the primary, ergonomic path. When the row is expanded it renders its data row plus a sibling detail row holding the returned content, so consumers no longer hand-write the Fragment, the getIsExpanded() conditional, or the extra <tr>. It is called lazily (only while open), so collapsed rows never build their panel. DataTable.Row also now sets data-expanded as a styling hook.
    • DataTable.RowExpandButton — an accessible +/ toggle (@phosphor-icons/react Plus/Minus) for a leading columnHelper.display column. Renders a real <button> that sets aria-expanded and (only while expanded) aria-controls, stops click propagation so it never fires a row-level onClick, lets a consumer onClick veto the toggle via event.preventDefault(), and renders nothing when row.getCanExpand() is false.
    • DataTable.ExpandHeader — a narrow <th> for the toggle column, mirroring DataTable.ActionHeader.
    • DataTable.ExpandedRow — the sibling <tr> renderExpanded wraps content in, also exported for full control (custom colSpan, multiple panels, bespoke markup). Spans every visible column, carries the id that RowExpandButton targets via aria-controls, suppresses its top divider so it reads as one block with its parent row, sits on an opaque surface so it coexists with a sticky action column, and exposes data-expanded-content for styling.
    • expandedRowId(row) — the exported helper that derives the shared, stable DOM id used by the toggle's aria-controls and the expanded row's id.

    Add jsonCodeBlockValue and jsonToShikiHtml to @ngrok/mantle/code-block: an ultralight (~1 KB) client-side JSON syntax highlighter whose output is byte-for-byte identical to Mantle's server/build-time Shiki highlighting — without shipping Shiki, any grammar, or WASM to the browser. Because Mantle highlights with a CSS-variables theme, only tokenization needs to happen client-side; the colors already live in CSS. Ideal for rendering a row's underlying object as highlighted JSON inside a DataTable.ExpandedRow with no build-time plugin and no server roundtrip. Multi-line objects and arrays are collapsible by default (foldable, on), reusing the same computeJsonFoldRanges + fold runtime as every other JSON CodeBlock, so the fold markup and behavior are identical.

@ngrok/mantle-vite-plugins@1.0.15

19 Jun 01:22
90ab13e

Choose a tag to compare

Patch Changes

@ngrok/mantle-server-syntax-highlighter@1.1.5

19 Jun 01:22
90ab13e

Choose a tag to compare

Patch Changes

@ngrok/mantle@0.76.4

18 Jun 15:07
b94630f

Choose a tag to compare

Patch Changes

  • #1257 7a32ee9 Thanks @cody-dot-js! - Simplify several costly CSS selectors to cheaper, equivalent forms. All changes are behavior-, theming-, and a11y-preserving — no public API changes.

    • CodeBlock fold gutter (mantle.css + decorate-highlighted-html.ts): the gutter-reservation rule no longer probes every line with a per-line relational :not(:has(> .mantle-code-fold-toggle)) (whose style-recalc cost scaled with line count). A single per-<pre> :has() now scopes the rule, content gets the gutter margin by default, and the sparse opener lines are tagged mantle-code-line-opener so CSS can reset theirs. Measured in Chromium on a fold-enabled block, this removes ~0.5 ms of style-recalc at 5,000 lines and ~3.6 ms at 20,000 lines, while keeping HTML payload flat (only opener lines gain a class).
    • Command group heading (command.tsx): the five **:[[cmdk-group-heading]]:* deep-descendant scans are now direct-child [&>[cmdk-group-heading]]:* selectors (cmdk renders the heading as a direct child of the group). Identical specificity, smaller match scope.
    • Command item icons (command.tsx): the brittle [&_svg:not([class*='size-'])] / [&_svg:not([class*='text-'])] substring-attribute scans are replaced with :where()-wrapped defaults ([:where(&_svg)]:size-5 / [:where(&_svg)]:text-muted), matching the Label precedent. Consumer icon size/color classes still override the defaults cleanly, without the substring scan or its false matches (e.g. text-sm).
    • HorizontalSeparatorGroup (separator.tsx): the universal-descendant [&_*:not([data-separator])]:shrink-0 is now a direct-child [&>*:not([data-separator])]:shrink-0. flex-shrink only affects direct flex items, so this collapses an unbounded subtree walk to a single parent check with no rendered difference.
  • #1256 1a48962 Thanks @cody-dot-js! - Button and IconButton now default type to "button" instead of requiring it.

    The type prop is relaxed from required to optional on both components; when omitted it renders type="button", matching the wider React ecosystem (Radix, shadcn, MUI, …) and neutralizing the native <button> accidental-form-submit footgun. This is backward compatible — every existing call site already passes type, so nothing changes for them; omitting type simply stops being a compile error.

    Forms note: because the default is now "button", a Button/IconButton that relies on native form submission must opt in with type="submit".

    When asChild is used, type continues to have no effect and is not forwarded to the child.

  • #1254 b5b72bc Thanks @cody-dot-js! - Stop publishing source maps. The .js.map files were ~58% of the package's unpacked size (and compressed tarball) and embedded the full original source via sourcesContent, despite src/ not being shipped. Dropping them more than halves the published package.

    This affects nothing consumers import at runtime. The typed .d.ts surface (with its JSDoc and @example blocks) and the bundled agent-discovery artifacts (dist/agent.json, dist/llms.txt) are unchanged; only stepping into mantle's original source in a debugger is no longer possible.

@ngrok/mantle@0.76.3

17 Jun 17:37
441af56

Choose a tag to compare

Patch Changes

  • #1241 f7be346 Thanks @cody-dot-js! - Update react-day-picker to 10.0.1 (from 9.14.0) and bump the tailwindcss peer dependency to ^4.3.1. The Calendar component already used the v9+ API surface — no removed navigation/focus/event props, formatter or label aliases, or renamed classNames keys — so this is a transparent dependency bump with no public API changes.

  • #1247 846f5bd Thanks @cody-dot-js! - Fix two issues with Checkbox's indeterminate handling:

    • A controlled checkbox toggling through "indeterminate" (e.g. a table "select all" header cycling unchecked → indeterminate → checked) logged React's "changing a controlled input to be uncontrolled" warning. The indeterminate frame now keeps checked a boolean, so the input stays controlled for its entire lifetime.
    • A controlled checked="indeterminate" set on mount did not render the indeterminate visual — two competing effects clobbered each other. The native indeterminate DOM property is now driven by a single effect keyed on the effective checked state, so it renders correctly on first paint and on every update.

    Add selectAllChecked to @ngrok/mantle/checkbox — a helper that resolves the tri-state checked value (true / "indeterminate" / false) for a "select all" checkbox from { allSelected, someSelected } selection counts, ready to pass straight to Checkbox. Encapsulates the correctness-prone tri-state branch every multi-select list reinvents. Also exports the CheckedState type.

  • #1247 846f5bd Thanks @cody-dot-js! - Update the DataTable source JSDoc @example blocks so the regenerated .d.ts, llms.txt, and agent component manifest teach canonical patterns instead of throwaway ones: the two empty states (an Empty hosted in DataTable.EmptyRow — "no data yet" vs. "no results for the active filter", with a Clear filters reset) and pagination via CursorPagination with a page-size dropdown (replacing hand-rolled prev/next Buttons). No runtime API changes.

  • #1249 44ee551 Thanks @cody-dot-js! - Drive the scroll-fade-x edge-fade utility with a CSS scroll-driven animation instead of JS-toggled data-scroll-left / data-scroll-right attributes, and add a matching scroll-fade-y utility for vertical scroll containers. Single-edge variants — scroll-fade-t, scroll-fade-b, scroll-fade-l, scroll-fade-r — are also available for containers that should fade only one edge (e.g. a sidebar that fades content scrolling under a sticky header but stays flush at the bottom). Table and Tabs now render their horizontal edge fades purely in CSS: the Tabs.List effect no longer runs a scroll listener, ResizeObserver, or MutationObserver for the fade (it only keeps a keyboard-focused trigger scrolled into view), and Table no longer writes the fade attributes. Consumers can still pin an edge opaque by overriding --_fade-left / --_fade-right (the animation drives the upstream --_scroll-fade-* vars). Where animation-timeline: scroll() is unsupported (Firefox stable as of mid-2026) the fade is simply absent — content is never clipped.

  • #1248 3a234e0 Thanks @cody-dot-js! - Update Radix UI dependencies to their latest patch/minor releases: @radix-ui/react-accordion to 1.2.14, @radix-ui/react-dialog to 1.1.17, @radix-ui/react-dropdown-menu to 2.1.18, @radix-ui/react-hover-card to 1.1.17, @radix-ui/react-popover to 1.1.17, @radix-ui/react-progress to 1.1.10, @radix-ui/react-select to 2.3.1, @radix-ui/react-slider to 1.4.1, @radix-ui/react-slot to 1.3.0, @radix-ui/react-switch to 1.3.1, @radix-ui/react-tabs to 1.1.15, and @radix-ui/react-tooltip to 1.2.10. No public API changes.

  • #1239 9b00072 Thanks @cody-dot-js! - Surface each component's JSDoc @example blocks in the agent-facing component manifest (/api/components.json), giving agents copy-pasteable canonical usage without a network lookup. Fix two malformed examples caught in the process: Sheet examples used opOpenChange instead of onOpenChange, and SandboxedOnClick's example was missing its closing code fence. Also documents the Field slot-order anti-pattern — help text (Field.Description) renders below Field.Control, not above it.

@ngrok/mantle-vite-plugins@1.0.14

17 Jun 17:37
441af56

Choose a tag to compare

Patch Changes

  • #1241 f7be346 Thanks @cody-dot-js! - Update the oxc-parser dependency to 0.136.0 (from 0.135.0).

  • Updated dependencies [f7be346]:

    • @ngrok/mantle-server-syntax-highlighter@1.1.4

@ngrok/mantle-server-syntax-highlighter@1.1.4

17 Jun 17:37
441af56

Choose a tag to compare

Patch Changes

@ngrok/mantle@0.76.2

11 Jun 20:32
810dc6e

Choose a tag to compare

Patch Changes

  • #1233 fa978b0 Thanks @cody-dot-js! - Refine Empty and Well styling: Empty uses tighter padding (p-6), Empty.Icon is smaller with a neutral color, Empty.Title and Empty.Description adopt smaller sans-serif text with a muted description, and Well uses the muted card border.

@ngrok/mantle@0.76.1

11 Jun 17:01
400aadb

Choose a tag to compare

Patch Changes

  • #1229 a569ca3 Thanks @cody-dot-js! - Remove the unused @uidotdev/usehooks dependency. It was never imported by any mantle code; consumers no longer install it transitively.

@ngrok/mantle@0.76.0

11 Jun 14:24
2af2101

Choose a tag to compare

Minor Changes

  • #1227 fbec001 Thanks @cody-dot-js! - fix(mantle): Dialog.Footer renders children in DOM order

    Dialog.Footer previously applied flex-row-reverse, laying its children out
    right-to-left so the DOM order was the visual mirror of the source. It now uses
    flex justify-end and renders children in DOM order, matching Sheet.Footer
    and AlertDialog.Footer. This also makes keyboard tab order follow visual
    order.

    Breaking visually: footers with two or more children that relied on the
    reversal will now render in the opposite order. Reverse the direct children of
    each affected Dialog.Footer (conventionally secondary/cancel first, primary
    last) to preserve the previous appearance. See the migration guide at
    https://mantle.ngrok.com/migrations/dialog-footer-dom-order-migration.