v0.7.2
Astryx 0.7.2 updates the stable @astryxdesign/* package set to this version.
OLD_VERSION=0.7.1 # replace with the Core version your project uses today
npm install @astryxdesign/core@0.7.2 @astryxdesign/cli@0.7.2 # plus every other stable @astryxdesign/* package you use, at 0.7.2
npx @astryxdesign/cli upgrade --from "$OLD_VERSION" --apply --path .
-
Promote
DrawerandDrawerHeaderfrom the canary-only Lab package to Core, imported from@astryxdesign/core/Draweror the package root. The stable package now ships the full-height side panel with modal and non-modal presentation, the shared dismissal stack, Dialog'spurposedismissal policy, aDrawerHeaderwhose close button appears when givenonOpenChange, and Dialog-style container padding (--spacing-4by default,paddingprop, or a theme'sdrawerpadding), plus Core documentation and runnable examples. Canary users importing from@astryxdesign/labswitch to@astryxdesign/core/Drawer.Also released in
@astryxdesign/cli.
-
An integration can set the command its users run the CLI with: a new
cliCommandfield inastryx.integration.*, such ascliCommand: 'acme astryx'. In a project that loads that integration, every command the CLI prints starts with it instead of the package-manager prefix (npx astryx,pnpm exec astryx): next-step hints, errors, suggestions, Doctor fixes,--help, the setup reminder, the agent-docs block, and doc text. If two loaded integrations set different commands, neither is used, and the newcli-commandcheck inastryx doctorwarns. An invalidcliCommandis ignored with aninvalid_cli_commandwarning; the rest of the manifest still loads. Docs, including an integration's, are now written with bareastryx ...commands, andastryx docsprefixes them for the project when it prints them. JSON output keeps every command bare, as before. The install-time setup reminder from@astryxdesign/coreand@astryxdesign/clinames the command the project's package manager runs, instead of alwaysnpx @astryxdesign/cli.Also released in
@astryxdesign/cli. -
Adds the
@astryx.diffViewer.*keys to theencatalog so the labDiffViewerreads its file-status badges, line counts, line-number button labels, per-line "Added"/"Removed" labels, and empty-state messages from the translation runtime. No existing string changes; locales without a translation fall back to English. -
Itemacceptsalign="first-line", which centers each start and end slot on the label's first line. A small status dot, radio, checkbox, or timestamp beside a wrapping label now stays level with the first line, wherecenterdrops it to the middle of the row andstartleaves it above the line. A slot taller than the first line moves the text down rather than leaving the row, and a one-line row with no description looks the same ascenter.ListItem,RadioListItem, andCheckboxListItemaccept the samealignand pass it to their row. Themes can style these rows through theitemtarget'salignaxis. The default stayscenter, so existing rows don't change. -
i18n catalog: add the English messages used by the Lab CommentComposer for its input, image upload, inline quote, emoji action, cancel action, and retry affordance.
-
i18n catalog: add the English messages used by the Lab CommentThread for replies, reactions, edit history, resolution, errors, comment actions, and the unsaved-draft warning it announces when a dismissal would discard a reply or edit.
-
i18n catalog: add the English messages used by the Lab Comments panel for its region, empty/loading states, pagination, and posting errors.
-
i18n catalog: add the English messages used by the Lab EmojiPicker so locale catalogs and provider overrides can replace its labels, search status, loading state, and group names.
-
List: addedgeto choose where each row's text, dividers, and hover and selection backgrounds sit.padded(default) keeps today's look.flushdrops the rows' side padding, so text and dividers start on the same line as the list header and a heading above, with no container needed.outsetkeeps backgrounds' room around the text while reaching into the padding of a Card, Section, or Dialog, so text lands on the heading's line.bleedreaches rows to the container's edges with square corners, like Table rows in a Card.edgeCompensation="inline"is deprecated in favor ofedge="outset"and keeps working; its rows' outer room is no longer cut off by the list's swipe clip. Runastryx upgradeto rename it. -
Pagination:variant="compact"shows first/last buttons around previous/next whenhasFirstLastistrueand the page count is known. Compact still omits them by default, and theinputvariant still shows them unlesshasFirstLastisfalse. -
reset.css: in Chromium, the dropdown marker of an<input>with alistsits in the middle of the field. Chromium sets the marker on the field's line-height, so above a line-height of 1 it sits low, and the reset makes every field inherit the page's 1.5: the marker sat 3.5px below the middle of a 40px field, and further down on taller lines. Date and time fields, whose calendar icon uses the same part, keep their icon where it was. To keep the old placement, addinput[list]::-webkit-calendar-picker-indicator { line-height: inherit; }after the reset. -
SideNavItem: nested items now span the full width of the nav list, like top-level items, so their hover and selected fill lines up with the rows above them. Before, a nested item started 24px in from the list edge. The indent now sits inside the row, one--spacing-6step per nesting level, so icons and labels stay exactly where they were. The indent area is now part of the nested row, so it shows hover feedback and clicking it activates the item. Content placed directly inside aSideNavItemthat is not itself aSideNavItemis no longer inset by the children group. There is no supported way to keep the old inset look. -
TabMenupasses host attributes and DOM event handlers to its trigger button. The trigger keeps its button semantics, tab stop, and menu disclosure attributes; a caller'sonClickruns after the menu toggle. -
Tables draw a line on every cell edge, not only between rows, on both
Markdownand the RichText surfaces, and the grid closes at the table's rim. A reader following one row across never needed vertical lines, but someone editing a table has to aim at a cell and judge where a selection stops, and the empty 3×3 a toolbar insert produces was three horizontal rules with no sign it had three columns. The line is the same--color-borderalready used between rows, the rim merges with it into one rule rather than doubling at the edge, and both surfaces move together — the read and edit renderings match block for block, more closely than before.Also released in
@astryxdesign/richtext. -
TreeList: nested rows now span the full width of the list, like root rows, so their hover and selected fill lines up with the rows above them. Before, a nested row started at its indent. The indent now sits inside the row, so chevrons, icons and labels stay exactly where they were. The indent area is now part of the row, so it shows hover feedback, and clicking it activates the row: clicking a folder's indent toggles the folder, and clicking a row whose only action is its link anywhere outside its label follows the link, as Enter already did: modifier keys are kept, so Cmd/Ctrl-click opens a new tab, and a middle-click opens one too. Controls in a row's start or end content keep their own clicks, including role-based ones such as arole="switch"element, which no longer also toggle the row or run itsonClick. Guide lines draw above the rows, so a hover or selected fill doesn't break them, and they let clicks pass through to the row underneath. The one exception is a row with keyboard focus: it draws above the guides so they never cut its focus ring, which means that if it is also filled (selected or hovered), its fill covers the guides at that row. Padding set on a row through thetree-list-itemtheme target or an item'sclassName,styleorxstylestill composes with the indent.--tree-list-indentstill retunes the indent step. There is no supported way to keep the old inset rows.
-
Allow MetadataList label width without an explicit position while preserving single- and multi-column label defaults (#6310)
-
AppShell: defer hiding the mobile nav's Activity boundary until its close animation finishes (#5701)
AppShellmounts the mobile nav drawer inside aReact.Activityboundary that flipped tomode="hidden"in the same commit as the drawer closing. React'sActivityhides its subtree withdisplay: none !importantsynchronously and re-runs the cleanup of every effect inside it, which cut offMobileNav's own slide-out CSS transition and cancelled its delayeddialog.close()before either could run. The drawer still closed correctly (a safety-net effect force-closed the native dialog on that same cleanup), just with the close animation skipped entirely.AppShellnow holds theActivityboundary visible for a beat after the drawer closes, strictly longer thanMobileNav's own worst-case close delay, soMobileNav's normal close effect gets a real chance to run its transition and its own timeddialog.close()beforeActivityever hides the subtree.MobileNavalso keeps its dialog rendered from the commit that closes it until its delayedclose()has run, instead of relying on the dialog's discretedisplaytransition to do so. A browser that does not hold that transition (WebKit) dropped the dialog todisplay: noneat once while it was still an open modal, so for about 200ms nothing was visible and the page stayed inert. The slide-out itself is unchanged. -
AspectRatio'sfit="cover"andfit="contain"now fill the box on the documented Tailwind setups. The fit rules lived inreset.css, in theresetlayer, and the setup guides put Tailwind's preflight in a layer above it, so preflight'simg, video { height: auto }won and a fitted image kept its own aspect ratio. The fit sizing now ships with the component's own styles inastryx-base: above preflight, and below Tailwind's utilities, so a utility class on the child still wins.reset.cssno longer carries AspectRatio rules. Withfit="cover"orfit="contain", a direct child's own Astryx styles (an Astryx component's own size orobject-fit) no longer override the fill. To keep a child's own size, set it with an inline style, unlayered CSS or a rule in a later layer, or leavefitunset. -
The static
AvatarGroupOverflowchip (noonClick) is exposed to assistive technology as an image named by its count label ("5 more"). Before, it was a span with anaria-labelbut no role, a name the generic role does not allow, so screen readers could skip the count. This meets WCAG 2.2 SC 4.1.2 Name, Role, Value. Nothing changes visually; the clickable chip stays a button, and arolepassed by the caller still wins. -
Button(andIconButton) no longer use the nativedisabledattribute while a busy state (isLoading, or a non-interruptibleclickActionin flight) is the only reason the button can't be clicked. A natively disabled element can't hold focus, so the browser moved focus to<body>the instant an action started and never restored it — the classic reason busy state should be visual-only. Busy now usesaria-disabled(button stays focusable) witharia-busyand the spinner, matching the documented "Disabled vs Busy" API convention and the behavior input components already follow.isDisabled(andisDisabledtogether with a busy state) is unchanged — that still uses nativedisabled.isInterruptibleis also unchanged.aria-disabled, unlike nativedisabled, still lets the button dispatch a click — the busy guard already calledpreventDefault()to block the button's own action, but a click on a busy button could still bubble to a wrapping ancestor's own click handler (a clickable row, card, or backdrop), which the previous native-disabledbehavior never allowed.handleClicknow also callsstopPropagation()in that guard, containing an unavailable click at the Button boundary the way a natively disabled control always did.The
href-rendered anchor path had the same gap for keyboard activation: it only wiredonClickto the busy guard, notonKeyDown, so a consumer's ownonKeyDownstill received Enter/Space on a busy-only anchor Button while it was announcedaria-disabled. The anchor path now wires the samehandleKeyDownsuppression the button path already had, so Enter/Space are swallowed and every other key still reaches the consumer, on both render paths. -
ChatLayoutScrollButton: the scroll-to-bottom pill now gives up keyboard focus, and refuses new focus, as soon as it starts to hide. Previously focus stayed on the fading pill for about half a second, untilvisibility: hiddenapplied, so it could sit on a control that was fading out or already transparent (WCAG 2.2 SC 2.4.7 Focus Visible). (#7738) -
Preserve caller-provided IDs on CheckboxInput and keep labels associated with the native input.
-
CheckboxList keeps a caller's
aria-labelledby. The group always replaced it with its own label id, so the caller's ids never reached the accessible name. The group is now named by its label and then the caller's ids, the same order RadioList and TimeInput use. -
The setup reminder, the swizzle StyleX note, and one doctor line say only what is true
Also released in
@astryxdesign/cli. -
CommandPaletteInput: the query field now draws the shared keyboard focus ring. Previously it suppressed the browser outline without replacing it, so keyboard users could not see which field held focus. -
Source builds through Next.js's Babel no longer fail with
Unknown property: L(orZs,Letter) when the app sets no browserslist or targets a browser without Unicode property escapes.Avatar's initials and the Markdown parser's emphasis and heading-link rules now build their\p{…}regular expressions at runtime, asNumberInputalready does, so there is no literal for Babel to rewrite. Matching is unchanged. -
DrawerHeadernow renders numeric zerostartContentinside its start slot and preserves numeric zeroendContentwhen no close button is present. -
Drawernow slides from the correct logical edge whendir="rtl"is set directly on the drawer, matching inherited right-to-left regions. -
DropdownMenuDividernow forwards host attributes and DOM handlers (id,data-*,aria-*, event handlers) to its separator, as other DOM-owning components do. Before, it accepted onlyxstyle,className,styleandrefand dropped everything else, so a separator could not carry a test id or an accessible label. Itsrole="separator"and orientation stay component-owned. -
DropdownMenu: load the touch bottom sheet only when a menu shows it
DropdownMenuimported its touch sheet (BottomSheet, its gestures, and the touch action list) up front, so every page with a dropdown shipped it, even when the menu only ever opens as an anchored popover. The sheet now loads on demand, the wayContextMenualready loads it. A popover-only dropdown's own code drops by about a third (about 23 KB gzipped). Bottom-sheet and adaptive menus behave as before. The sheet starts loading when the trigger mounts, and a menu opened from the keyboard before it arrives still focuses its first action once it loads. -
EmptyStateno longer leaves blank icon or action spacing when those optional slots receive a boolean or an empty string. An empty description also omits its theme target. The docs now describe configurable heading levels and map the container anatomy to the public theme target. -
Keep
Field's horizontal-label layout marker authoritative when callers pass conflicting data attributes, and keep hidden descriptions visually hidden in that layout. Correct its custom label-icon type, all three status placements, complete anatomy, and guidance for controls that cannot safely use attached status. -
FieldStatus(and any element usinguseEntryAnimation) no longer causes a hydration mismatch on server-rendered pages. An element that arrives in the server's HTML now hydrates without the entry style and never plays its entry animation, even when hydration runs after the page has painted; elements mounted after hydration still animate.useEntryAnimationno longer documents server rendering as unsupported. -
Keep
FormLayout's direction metadata authoritative when callers pass conflicting data attributes. -
GridSpankeeps its content inside the span. A child whose min-content width is wider than the span, such as aTokenizerwith a long token, used to stretch the span's column past the grid cell; it now gets the span's width and truncates the way it does outside a Grid (#7438) -
Keep
Grid's theming metadata authoritative when callers pass conflicting data attributes. -
useHoverCard: closing a hover card withhide()or Escape while the pointer rests on the card no longer leaves the next open stuck. The hook kept believing the pointer was still over the card, because a card closed that way unmounts without amouseleave, so the trigger's next hover-open never closed on leave or afterhideDelay. Both paths now clear that state, as the touch dismissal already did. -
Compute ISO week numbers (
Calendar hasWeekNumbers,getWeekNumber) on UTC anchors instead of local-timeDatearithmetic. In timezones whose DST ends after New Year (Sydney, Auckland, Santiago), the local-millisecond difference between the week's Thursday and January 1 carried the DST offset, and in years whose January 1 is a Friday that extra hour rounded a whole week up: the entire ISO week 14 of April 2021 displayed as week 15. Week numbers are now identical in every timezone. -
Anchored layers (
Popover,DropdownMenu, and every component built on them) now keep their safe-area gutter and dynamic-viewport size cap in the shipped stylesheet. The compiled CSS referenced an undefined variable for the preferred value, so every browser used the fallback: on a phone with a dynamic toolbar or a notch, a tall layer could run past the visible screen or under a declared bottom bar. Desktop sizes are unchanged. -
createMarkdownFrontmatternow hands itsparsefunction a plain object, so a parser that returns the decoded fields unchanged (parse: fields => fields) is accepted as metadata. Before, the record had no prototype, failed the JSON-like metadata check, and the document fell back to readable text with its frontmatter still showing. -
MultiSelector: the row action cell the keyboard reaches with the inline-end arrow draws the standard focus ring. Before, its outline read four CSS variables that nothing declared, so no ring showed on the active action cell. -
PowerSearch: clear every filter with one press of the clear-all button instead of removing only the last filter.
-
Prevent pressing a built-in PowerSearch filter token with the mouse from opening search suggestions before the filter editor. Token editing, removal, and keyboard navigation retain their existing behavior.
-
Keep built-in PowerSearch value controls disabled in read-only filter editors, matching custom editors.
-
RadioList keeps a caller's
aria-labelledby. The radiogroup always replaced it with its own label id, so the caller's ids never reached the accessible name. The group is now named by its label and then the caller's ids, the same order TimeInput uses. -
Restore focus to the element that opened a Dialog when its content mounts in the open commit. The opener was captured in the Dialog's passive effect, which runs after a freshly mounted DialogHeader's autofocus effect had already moved focus to the dialog's own title — closing then restored focus to that detached title and the user's focus landed on
<body>. The capture now runs in a layout effect, before any descendant's passive effect. (#5637) -
astryx searchfinds more of what a reader means when the words differ from the catalog's.Also released in
@astryxdesign/cli. -
astryx searchranks a match on more of your words above a shorter named entry.Also released in
@astryxdesign/cli. -
SideNavItemkeeps a caller'sclassNameon its row element (the link or button, and the collapsed rail item) next to its own classes. Before, the row's generated class replaced it, so a class passed to style the row (for example, a cursor while a row is dragged) never reached the DOM, even thoughstyle, ARIA, data attributes and handlers did. -
Shrink Token's invisible remove-button target from 44px to 24px without changing the visible icon. Clicking adjacent text or values in PowerSearch now edits the filter instead of accidentally removing it.
-
DropdownMenu submenu flyouts close with their menu when it is light-dismissed: previously a submenu opened by tap or a fast click stayed open (and re-anchored to the viewport corner) after the menu was dismissed and reopened. A hover-open scheduled on a submenu trigger before the menu closed from elsewhere is also cancelled, so it can no longer reopen a flyout behind the dismissed menu
-
Switchnow adds a caller'saria-describedbyto the switch control, ahead of its own description, status, and disabled-reason ids. Before, the value landed on the field wrapper, so a hint rendered elsewhere on the page could not describe the switch to assistive technology. -
Switchnow puts a caller'saria-labelledbyon the switch control. Before, the value landed on the field wrapper, so a visible title elsewhere in the row could not name the switch for assistive technology. -
A truncated
TextorHeading(maxLines) now re-measures when its content changes, not only when its box resizes. A fixed-width single-line status whose message changed kept showing the first message in its truncation tooltip, and a new short message could keep a tooltip while a new long one lacked one.useTruncationnow also observes the element's text, so the tooltip text and the truncated state follow the current content. -
ToggleButtonkeeps toggling when it also receives anonClick, for example from a wrapper that renders it as its trigger. ThatonClickused to replace the toggle's own click handling, soonPressedChangenever ran and aToggleButtonGroupmember never asked its group to toggle (pressedChangeActionstill ran). The caller'sonClicknow runs first and the toggle request follows. On a standaloneToggleButton, callingpreventDefault()in thatonClickcancels the request: neitheronPressedChangenorpressedChangeActionruns. -
useTooltip(and every component built on it, such asButton'stooltipprop) now drops a show that is still waiting out its delay whenisEnabledturnsfalse. Before, hovering a trigger and then disabling its tooltip within the delay let the timer fire anyway: the tooltip opened after its triggers were turned off, with no pointer-leave listener left to close it. A pending hide still runs, and an enabled tooltip is unchanged. -
Tooltip: an icon-only trigger now opens instead of throwing. The layer passed its trigger to
showPopoveras the invokersource, which the Popover API declares as anHTMLElement; a caller-rendered icon is anSVGElement, whose dictionary conversion throws before the popover opens, so the hint never appeared. An icon trigger now reachesshowPopoverwithout a source — positioning is unchanged, since the layer is anchored by its anchor-name pair, and only the invoker's focus order is given up. -
A collapsed
TopNavMenuorTopNavMegaMenusection in the mobile drawer is nowinertand hidden from assistive technology, so Tab no longer moves focus onto links that are not shown and screen readers no longer list them under a section announced as collapsed. -
An expanded
TopNavMenuorTopNavMegaMenusection in the mobile drawer no longer clips the focus ring of its items. The section still clips while collapsed and while it animates open or closed. -
Typeahead,Tokenizer, andPowerSearch: clear loading state and stop busy indicator when search source is replaced (#7457)
TextandHeadingwithmaxLinesandhasTruncateTooltip={false}no longer measure their text or observe it for resizes and content changes. Only the tooltip uses that measurement, so with the tooltip off the text is cut by CSS alone, and a long table of truncated cells no longer pays a layout read per cell. Rendering is unchanged; with the tooltip enabled (the default) nothing changes.
-
AvatarandAvatarGroupdocs list the numeric sizessizeaccepts (16, 20, 24, 32, 36, 40, 48, 60, 64, 72, 96, 128, 144, 180) instead of implying any pixel value, and say that anAvatarGroup's size always overrides a childAvatar's size, including the group's default. -
Docs and CLI examples import each Core export from the path
astryx component <Name>prints.Also released in
@astryxdesign/cli. -
ContextMenudocs now state that data-drivenitemsand compoundmenuContentare mutually exclusive choices instead of markingitemsas unconditionally required. -
Document DropdownMenuGroup's complete wrapper props, group semantics, and non-interactive heading guidance.
-
Document FieldLabel's complete public prop surface, group-label semantics, and supporting descriptions.
-
Make
FormLayoutdocumentation match logical fractional grid tracks and viewport collapse, and make the public layout target discoverable from component anatomy. -
Make
GridandGridSpandocumentation match fractional track sizing, responsive repeat behavior, implicit rows, and explicit-column spans. -
useCombobox, the single-select combobox hook exported from@astryxdesign/core/Selectorthat powersSelectorandCommandPalette, now has hook docs, soastryx hook useComboboxandastryx search comboboxreturn it with its options, its return fields, and how to wirearia-activedescendantwhile focus stays on the control. -
The
useMultiComboboxhook docs now say that ArrowDown or ArrowUp opening the list lands on the first or last option even withhasSearchor a disabled end option, and that the hook still handles Home and End withhasSearch, so a search input that keeps those keys for the caret stops them first and relies on PageUp and PageDown. -
useMultiCombobox, the multi-select combobox hook exported from@astryxdesign/core/MultiSelectorthat powersMultiSelector, now has hook docs, soastryx hook useMultiComboboxandastryx search comboboxreturn it with its options, including the grid rows with an action cell, its return fields, and how to wirearia-activedescendantwhile focus stays on the control. -
InternationalizationProvider documentation now names its nonvisual provider boundary so the new component contract can map its anatomy without implying rendered DOM. (#6048)
-
ListItem's docs now listlabelas aReactNode, matching its props type: a string is truncated to one line, and any other node renders as given. They used to saystring, which told readers a rich label had to be restructured away.
-
VStackandHStackcome from@astryxdesign/core/VStackand@astryxdesign/core/HStack,Headingfrom@astryxdesign/core/Heading,Textfrom@astryxdesign/core/Text, andThemeanddefineThemefrom@astryxdesign/core/theme.Also released in
@astryxdesign/cli. -
This covers the guides, the
Carddocs, the next stepsastryx initprints, the install exampleastryx theme buildprints, the wiring exampletheme addprints, the build rules, the theme-module fixastryx doctorprints, and the annotated theme template.Also released in
@astryxdesign/cli. -
Every path was already exported. Nothing else changes.
Also released in
@astryxdesign/cli. -
The "run init to finish setup" reminder no longer prints where agents already have the Astryx prompt. In a git repository it looks for init's block in the agent docs from the project up to the repository root, because coding agents read every agent doc in between, so a package in a monorepo that ran
initat its root stays quiet. This holds for every command and for the reminder printed when@astryxdesign/coreor@astryxdesign/cliis installed. Running the CLI from its own source checkout, inside that checkout, prints no reminder. A project with no block in reach still gets it, and--jsonnever prints it.Also released in
@astryxdesign/cli. -
astryx swizzleno longer says StyleX components "render unstyled (no error)" without a StyleX compiler. Importing them throws StyleX's "Unexpected 'stylex.create' call at runtime" error and the page does not render, and the note now says so.Also released in
@astryxdesign/cli. -
In an integration package, where the package's own manifest loads,
astryx doctor's implicit-integrations line no longer says that "every loaded integration is named in astryx.config". It names the package's own manifest: as the only loaded integration, or beside the integrations astryx.config names.Classification: contract-restoring. Only message text changes: JSON keys, check ids, statuses, and exit codes are unchanged. The doctor line's new text also appears in its
--jsonmessage.Also released in
@astryxdesign/cli. -
Internal groundwork for a sub-menu's header and footer:
DropdownMenu's Tab walk through its header, menu and footer moves, unchanged, into a module of its own, so another menu level with regions can share it.DropdownMenu's behavior does not change. -
Adjacent query words that join into a component, hook, or template name, or into a one-word keyword, match it:
table header cell with sort arrowfindsTableHeaderCell,tree view foldersthetreeviewkeyword, and a hook answers to its words withoutuse. Most of a name, a name's leading words, or a run of its words also count for components and hooks, a step below the full name.Also released in
@astryxdesign/cli. -
A query that is one camelCase identifier and matches nothing as typed is read as the words it spells, so an unknown API name such as
useTablePaginationStatereachesuseTablePagination. In a query of several words each identifier stays one term.Also released in
@astryxdesign/cli. -
When the whole query is an entry's name, these readings add no near siblings beside it. A match on a joined keyword never ranks above an entry the query names, and a match on part of another name does only when it spans more query words than that entry's name.
Also released in
@astryxdesign/cli. -
A gerund matches its verb (
spacingandspace,dragginganddrag), and a plural can start a longer word through its singular (tokensandTokenizer).Also released in
@astryxdesign/cli. -
Core component docs declare more keywords: other libraries' names (
scrollarea,togglegroup,pageheader,splitpane,circularprogress,linearprogress,commandmenu,relativetime,sidesheet,descriptionlist,inlinecode) and common glyph names onIcon, since any icon library's glyph renders through it.Also released in
@astryxdesign/cli. -
The JSON shape is unchanged.
Also released in
@astryxdesign/cli. -
A match on part of a longer name that reads more of the query than the entry the query names is no longer held below it:
dropdown menu radio optionsleads withDropdownMenuRadioItem, notDropdownMenu.Also released in
@astryxdesign/cli. -
useResizabledrops thesplitviewkeyword, which put it ahead of the side-panels guide.Also released in
@astryxdesign/cli.
- The
RichTextEditordocs now listisOptional,isRequired,labelTooltipandnamespace, four props the editor already accepted, soastryx component RichTextEditorand the docsite props table show the Required/Optional indicator,aria-required, the label tooltip and the Lexical namespace.
-
An integration component doc can name a differently named export of the file beside it: when
Slides.tsxexportsAcmeCarouseland nothing namedSlides,Slides.doc.mjscan setname: 'AcmeCarousel', and a kebab-caseacme-widget.doc.mjscan nameAcmeWidget.component --list,component <Name>,search,swizzle, thedoctor integrationchecks andintegration verifyknow the component by that name, and its file name still opens it. A name that Core or another doc's file already has stays theirs on every surface unless--packagenames the integration. Docs and sources are read without running them; a doc named after its file, or beside a file that exports its own name, reads exactly as before. -
Each full entry an integration's family doc lists in
components, such asAcmeSlidein anAcmeCarouseldoc, is a component under its own name when the module the doc'simportnames exports it. Its source is the.tsxfile in the family's folder that declares it; a member that only built output or a file outside that folder declares has no source to show or copy.component --list,component <Name>,search,swizzle, thedoctor integrationchecks,integration verifyandintegration addknow these members. A member's detail is scoped to its own entry, as a Core sub-component's is, and it never carries the family'sreplaces. A member named like a Core component or another doc's file opens with--package. -
The CLI installed for the computer keeps one short Astryx skill for every agent on the computer:
~/.agents/skills/astryx/SKILL.md, which most coding agents read, linked into the skill folder of each agent that reads only its own (~/.claude/skillsalways; Kiro, Windsurf, Letta, Xcode's coding assistant, and~/.config/agents/skillswhen that folder exists) with a folder link and a relative target, a junction on Windows, or a copy where no link can be made. Continue, whose scanner skips links, gets a managed copy. The text tells agents that Astryx is on the computer, when it is relevant (building or changing UI), to start withastryx search, and to read docs withastryx docs; it names no version and only commands every release keeps. It is written on the global copy's first run and after each upgrade, in the background; package install scripts never write it.astryx init --userwrites it now, in any folder, andastryx init --user --remove-agentsremoves the file, every link Astryx made, and the copy, and turns it off. The setting and the links Astryx made are kept inastryx/settings.jsonin the per-user configuration folder. The file records a hash of its text, so a file a person edited is never overwritten or removed, and nothing Astryx did not create at a link folder is replaced.init --userwarns, while still writing, when noastryxcommand on the computer runs the Astryx CLI, and itsinit.userreceipt lists each path and what happened to it. Outside a project,astryx doctoradds acomputer-agent-textcheck (info only) that says whether the text is present, current, edited, or turned off, where it is linked, and any link folder it skipped or whose skill folder is missing. A cache or configuration folder set throughXDG_CACHE_HOME,XDG_CONFIG_HOME,LOCALAPPDATA, orAPPDATAis used only when it is an absolute path. -
Integrations installed for the computer. An integration package installed in the same place as the CLI installed for the computer (
npm i -g,pnpm add -g,yarn global add, orbun add -gbeside@astryxdesign/cli), or installed with the CLI in one package set by a tool that runs it, is a computer-level integration. Outside a project, it loads the way a project's integrations do: its components, templates, docs, themes, discover source, andcliCommandapply, with the same validation, and one whose@astryxdesign/coreor@astryxdesign/clipeer range excludes the CLI's release is skipped. pnpm 11 and later, which give each global package a folder of its own, count every one of those folders. Inside a project, only the project's own integrations load: a computer-level one adds nothing there, andastryx discoverlists it undermeta.availablewith sourceinstalled for this computerand the project's install command ininstall, when its peer range admits the project's release and it loads. A project that installs the same integration uses only its own copy.astryx doctoradds acomputer-integrationscheck: outside a project, each one it uses, each the folder replaces with its own copy, and a warning for each it skips or cannot load; inside one, the ones the project could add. -
astryx gap-report --to <package>sends a report to the issues URL of the loaded integration the person chose, under the same--confirm-publicrule, instead of the default handlers. When loaded integrations name their own issues URLs,--list-categoriesand the receipt list every destination and tell the agent to ask the person where to file. The default route is unchanged. -
Install the CLI once for your computer (
npm i -g @astryxdesign/cli) and runastryxin any folder. In a project, the project's own CLI answers, whatever its version, as before: a copy started anywhere else (the global install,npx, a typed version) hands the run to it. A project with no CLI of its own (only@astryxdesign/core, or a fresh clone) is answered by the copy you started, as before; when that copy is not the Core's release, one hint after the command, at most once a day per project, names the command that adds the CLI at Core's version, and JSON reportsmeta.answeredBy.copy: "starting". Only a project whose own CLI cannot start is answered by the CLI release equal to its Core, installed once into a per-user cache (from the installed Core, else the lockfile, else the declared range).astryx doctorwarns when a project's CLI and Core are different releases, or when a project has no CLI and the copy that answered differs from its Core, and prints the command that aligns them.astryx --helpand the manifest's newglobalModefield point toastryx docs global. The hand-off keeps the arguments, folder, standard streams, and exit status. Outside a project, the copy installed for your computer answers with its own release; commands that read Core use the Core of that release there: the global copy downloads it into the cache in a background step it starts as a run ends (never in CI, and not on Windows, where a background process would hold the caller's pipes and the first component command downloads it instead), so component reads work offline after one online run. Nothing that worked before breaks: when that release cannot be obtained (offline, a registry without it, an unwritable cache), the copy you started answers, as before global mode, with one warning line on standard error (astryx: could not get Astryx <version> to match this project's Core (<reason>); answering with CLI <version> instead) andmeta.answeredBy.copy: "fallback"in JSON. A copy counts as installed for the computer only in npm's global folder or in a global folder a package manager's own settings name (Bun'sBUN_INSTALLorglobalDir, Yarn'sglobal-folder, pnpm'sPNPM_HOMEorglobal-dir, or their defaults), never by a folder's layout alone, so an app made withyarn addis never mistaken for one; a moved Bun or Yarn global folder counts, and its notice reinstalls there. A failed install is remembered in the cache, so offline runs do not start npm again each time: it is retried after 2 minutes, then after 10 once it fails again, and a success forgets it. A stale install lock ends the process it records only when that process is exactly this CLI's install of that release (npm, or node running npm-cli.js, installing@astryxdesign/cli@<version>, started after the lock); otherwise the lock is simply taken over. The newERR_VERSIONS_UNAVAILABLEis kept for runs nothing can answer, such as a command that needs Core outside a project that cannot install it. The CLI starts npm from npm's own entry file with the running Node, never through a shell, and reads a project's declared Core range only when it is a plain version range. Every successful--jsonresult now carriesmeta.answeredBy(cli,core,copy:project,starting,matched,carried, orfallback), typed inCLIResponseand described in the output envelope docs. In text mode, a run answered by anything other than the project's own CLI prints oneastryx: answered by …line on standard error. Printed commands start with a name that reaches the same release: the project's runner when its own CLI answers,astryxoutside a project for the copy installed for the computer, and otherwise the CLI package pinned to the answering version (npx @astryxdesign/cli@0.7.2). The newERR_PROJECT_REQUIREDcode names a command that needs a project. Seeastryx docs global/install-once. -
astryx doctor integrationandastryx integration checkcatch more doc mistakes, and stop flagging one pattern Core itself uses. -
The integration manifest accepts an optional
docsiteUrl, a link to the integration's docs site, as a known field, so setting it no longer raises an unknown-key warning. -
Update notices, the way
ghand the Vercel CLI do them. Once a day, never in the first day after its first run, the CLI installed for the computer checks the registry'slatestrelease in the background without delaying the command; it never installs a release by itself. When a newer release exists, a command that finishes successfully prints two lines on standard error, at most once a day:A new release of Astryx is available: <current> → <newest>andTo upgrade, run: <command>, with the command of the package manager that installed the CLI (npm i -g,pnpm add -g,yarn global add,bun add -g, orvolta install), after the Node the release needs when the computer runs an older one. Inside a project the notice names the project's install andupgradecommands instead, and anastryxfirst on the search path from before the hand-off gets a notice naming its upgrade. No notice prints with--json, after a failed command, or whenCIorNO_UPDATE_NOTIFIERis set, and neither variable lets the check run; unlikegh, a notice prints when standard error is not a terminal, because agents run the CLI without one. What the check found lives inastryx/state.jsonin the per-user configuration folder.astryx doctorreports the same: thenewest-releasecheck names the upgrade commands, and a newcli-on-pathcheck lists everyastryxcommand on the search path. -
A
{@link <target>}in a component doc's text now reads as the command that opens its doc, inastryx component <Name>, its--propsview, and component lists, in text and JSON: the description, usage, best practices, anatomy and accessibility notes, and each prop's and example's description. A topic's description reads the same way inastryx docs, its list, and the docs tree. Code fields and code ticks show the link as written. A Core component's links resolve against the CLI's docs; an integration's against its own.astryx doctor integration componentswarns on a link in an integration's component docs that names no doc, anddoctor integration docsdoes the same for a topic's description. The written-pointer warning also reads component docs and topic descriptions, and each finding now names itskindandfield. -
Component prop docs accept
stability: 'experimental', the machine-readable marker for a prop outside the stable compatibility promise, such as a component'sexperimentalprop object. -
doctor integration docs(and sointegration check) lists each pair of topics that repeat 10 or more words of prose word for word: two of the package's own topics, or one of them and a Core topic other than the one it replaces. Each entry names the topic that keeps the text (a Core topic, or of two package topics the one the passage fits), the line to change in the other, and the exact link to put there, and is in the newcopiedPassagesfield. A run with fewer than 4 distinct content words (words other than function words such as "the", "in" or "it"), or one that three or more topics share, is not listed. Each pair is also acopied_passagewarning, sointegration check --strict(new in this release) fails until it is fixed;doctor integration docsstill exits 0. The CLI's own guides no longer repeat 18 passages that another guide owns; each copy is now a link to the guide that owns the fact. -
astryx discoverlists the official Astryx packages, first, from a list each CLI release carries, so a project with no discover source sees the official themes, even offline. Each entry inmeta.availablecarries the command that adds it (install), so a project that loads no integration and has no discover source of its own, where every query still answers with the list, can add a package straight from it. An official entry'slatestis the newest release the CLI knows (its own version on a release CLI, or a newer Core release the project has), and itsversionsalso list the project's Core version, so both can be looked up. Its add command pins the project's Core version (installed, or declared exactly), else the Core range the project declares (quoted), else the latest release; on a canary CLI with no Core it pins none for packages released with Core, and canary-only packages are listed only beside a canary Core or no Core. Official entries nameAstryxas theirsource, andmeta.sourcesreports the list last. A project's or an integration's source keeps its entry for an official package, and for any package of the same integration. A theme package that the project's own package.json declares as a dependency, under an npm alias too, shows as installed from any folder of the project, and a theme's add text ends withastryx theme add <slug>. A version or item that the official list does not show still answersERR_UNKNOWN_PACKAGE, as before. -
doctor integration docs(and sointegration check) lists fluff in a package's prose, in the newflufffield: each padding phrase such as "in order to", "note that", or "it is worth noting", with its file line and what to write instead, and each topic of 80 or more prose words in which fewer than half the words carry content. Each is also adoc_fluffwarning, sointegration check --strict(new in this release) fails until it is cut;doctor integration docsstill exits 0. The phrase list and the floor are in the check-your-docs guide (astryx docs cli/integrations/building-blocks/docs/check-your-docs). Core'sbrowser-supportguide loses its one padding phrase, and three of Core's guides (debug-and-gap-reports,sections-and-placement,publishing) are tightened to meet the density floor. -
astryx doctor integration validatenow checks every doc an integration ships under its roots (components, templates, docs topics, themes, and their translations, served or not) against the published authoring type for its kind, with the project's own TypeScript, the waytscwould. Each unknown field, missing required field, or value outside its field's type is a warning that names the doc, the line, the field, and a fix; the exit code is unchanged. A doc it cannot fully check, or a themes root that does not load, is listed with the reason; test fixtures under__tests__in the components or docs root are counted, not checked. JSON addsdata.docTypes. Without TypeScript the check prints one[skip]line instead of passing silently; with a TypeScript it cannot read types with (one with no JavaScript compiler API, such as TypeScript 7), that line names the installed version. With no doc files, it says there is nothing to check. -
astryx doctor integration componentscompares each component doc's props with the component's TypeScript props, read with the project's owntypescript. It warns about a documented prop the code does not have, a prop the package declares that the doc leaves out, and a wrongrequired. Without TypeScript it prints one line saying the comparison was skipped; with a TypeScript it cannot read types with (one with no JavaScript compiler API, such as TypeScript 7), that line names the installed version. The findings are warnings, so the exit code does not change; the JSON response gainsdata.propDrift, and nothing else in it changes. -
astryx doctor integration componentsalso judges each documented prop's type against the code, for props the package declares, a documented field such asstatus.typeand a name the package's own index signature takes included, and warns when they differ (type_mismatch). A type is judged only where both sides read exactly, with the project's owntypescript: the same text once whitespace, quotes and union order are set aside, or primitives, literals, arrays and unions of those, and names for them. Every other type (a callback, an object, a generic with arguments, an intersection,keyof, a type that does not resolve, a name two index signatures take) is listed as not compared, never reported. The comparison needs TypeScript 4.8 or later: with an older one, no type is judged and the text says so in one[skip]line. JSON addstypesCompared,typesNotComparedand, when the types were not judged,typesSkippedtopropDrift. The findings are warnings, so the exit code does not change. -
New
astryx integration check, the CI check for an integration package's own build. It takes no package argument and checks the integration package in the current folder. It runs the fourdoctor integrationchecks (validate,components,templates,docs), prints each result, then a summary, and exits 1 when any of them would.--strictalso exits 1 on any warning and on any check that did not run, such as a package with noastryx.integration.*: add"typecheck": "tsc --noEmit && astryx integration check --strict"to package.json so every build keeps the package's docs in sync. Its exit codes and JSON are a contract for the builds that run it: from this first release on, a new check, or a new failure code, joins only in a minor release named in the release notes. JSON: a newintegration.checkenvelope carries each check's own envelope unchanged. Its docs say to run it in CI, soastryx search cifinds it. Thedoctor integrationsubcommands keep their exit codes; their JSON gains only additions. -
Three new published types describe an integration package's templates:
IntegrationPageTemplateDocandIntegrationBlockTemplateDoc, each with every field of itsTemplateDocmember butcategoryas the package's own string instead of aTemplateCategoryvalue, and their union,IntegrationTemplateDoc.@astryxdesign/cli/authoringexportsIntegrationTemplateDoc;@astryxdesign/cli/docand@astryxdesign/cli/templateexport all three.TemplateDocis unchanged. -
astryx upgraderenames a staticedgeCompensation="inline"onListtoedge="outset", and dropsedgeCompensationbeside anedgethat is a fixed string. It keepsedgeCompensationwith aTODO(astryx upgrade)comment where it can't prove the result: whereedgeis computed (it may be undefined, and thenedgeCompensationstill applies), on a dynamicedgeCompensationvalue, and where theListalso spreads props, which may setedge. The deprecated prop keeps working there. -
astryx doctor integration docslists each code span in an integration's prose that is exactlyastryx docs <name>for a doc that exists, and says which{@link}to write instead:{@link generic:<name>}for the package's own topic,{@link @astryxdesign/cli:generic:<name>}for Core's. The link prints the command that opens the doc, and doctor's link check fails it if the doc goes away. A command with more words, a route, or a code block is not listed. Each is astray_doc_pointerwarning, soastryx integration check --strictfails on it, and is in the newstrayPointersfield;doctor integration docskeeps exit 0. Core's own guides point with links, and a test keeps them that way. -
astryx doctor integration validateandastryx integration checknow warnsource_without_template_docwhen a.tsxdirectly in an integration's templates root has no same-stem template doc, soastryx templatewould never list it. The warning names the file and the doc to add. Files in subfolders, such as helpers a template imports, and test, spec or story files such asstray.test.tsxare not checked; a dotted template id such aslanding.page.tsxis, andintegration check --strictfails on the warning. -
The doc-type and prop checks in
integration checkanddoctor integration validate|componentsread types with@typescript/typescript6, the TypeScript 6 API package TypeScript publishes for tools, when the project installs it and its owntypescriptis missing or has no JavaScript compiler API, as TypeScript 7 does. When no usable TypeScript is found, the skip reason now ends with the remedy: add@typescript/typescript6on TypeScript 7, or install TypeScript 5.0 through 6 (npm install -D typescript@6).
-
extractComponentsreported phantom components from locally-declared helper functions in template files — e.g. "Timeline", derived from afunction TimelineSection() {...}helper declared directly in the detail-page template, which isn't a real component. It now excludes any name bound by a top-levelfunction,class,const,letorvardeclaration in the same file (read from the parsed source, so comments, strings and type annotations don't count), before suffix-stripping runs, so a locally-declared helper is never mistaken for an imported component. A component whose whole name is a stripped suffix, such as a bareSection, now stays in the list instead of emptying out. -
Global and project copies of the CLI meet more predictably. A repository that builds the CLI itself, with
packages/clibesidepackages/core, answers with that CLI instead of failing withERR_VERSIONS_UNAVAILABLE. On a cache folder shared between computers, a release install renews its lock every 10 seconds, and a lock from another computer that goes a minute without renewal is taken over, instead of blocking runs for 20 minutes. Folder names are compared without letter case on macOS and Windows, a Yarn 1 global install on Windows is recognized as a global install, and the shims npm, pnpm (including pnpm 12's), and PowerShell write are read when doctor and the reminders look for theastryxcommands on the search path. -
A CLI installed with npm under a prefix of its own (
npm i -g --prefix ~/.local,NPM_CONFIG_PREFIX, orprefix=in an.npmrc) is now upgraded into that same prefix: the update notice andastryx doctorprintnpm i -g --prefix <prefix> @astryxdesign/cli@latest, with~for the home folder. npm's default prefixes (Node's own,/usr/local,/usr, and%APPDATA%\npmon Windows) keep the plain command. -
The CLI now uses
postcss-selector-parser7.1.6, which parses flat selectors in linear time and closes a CPU-exhaustion vulnerability (#6985). -
integration verifyreads a package's exports with the same reader discovery uses, so the two agree: a component re-exported through a folder's index file passes, and a type-only re-export no longer counts as the component. -
astryx component --listandastryx searchfind VStack, HStack and Heading Each has its own import path, but its doc lives in its family's folder: Stack's doc documents HStack and VStack, and Heading's doc sits next to Text's. The list only counted a doc in the component's own folder, so the three were missing from the list and from search, andastryx component VStackfound its doc only through fuzzy matching. A component whose doc is elsewhere is now listed and searchable with its own import path, andastryx component VStackopens its doc directly. -
A doc link to a component, page, or block (
{@link component:AcmeButton},{@link @astryxdesign/core:component:Button},{@link block:acme-card}) now resolves, and reads as the command that opens it:astryx component <Name>orastryx template <id> --type <page|block>, with--packagefor anything not from Core. Before, every such link printed as written, andastryx doctoranddoctor integration docswarned that it named no doc, for Core's components as for a package's own. A link that names no component or template still warns, and its hint now searches components or templates instead of topics. A resolved link'srouteis null for these kinds, which noastryx docsroute opens. -
astryx doctorno longer fails "Theme owners and built imports", andastryx componentno longer stops with "Could not load the recorded theme", when an added theme's built module imports a stylesheet, as inimport './ocean.css'. Node cannot load a stylesheet, so the CLI checks that each imported.cssfile exists, then treats it as empty. It looks for each stylesheet from the theme module's folder, also when a module that the theme imports from another folder names it. Doctor still fails when an imported stylesheet is missing, and when the module has any other problem, such as a missing dependency, a syntax error, or an error thrown while it loads. -
astryx searchkeeps a guide first when the query is a phrase of two or more words that the guide declares as a keyword.font sizeandfont weightnow open the font setup guide before the Text component, anddark modeopens the theme guides before the theme hooks. A component whose name is the whole query still comes first, one-word queries are unchanged, and the code terms and table cells in a reference doc don't count as its keywords. -
astryx integration check --strictprints each finding once. The checks' own output shows its message and fix, and the closing "Failing under --strict" list names each failure by check, code and place only, so a long report is no longer doubled. -
The
table-filterpage template's phone-width view-options panel is now labelled by its selected tab (aria-labelledby) instead of a fixedaria-label, so screen readers get the panel's name from the tab that controls it and the tab–panel relationship resolves in both directions. -
astryx theme build --checkandthemeBuild({check: true})keep comparing outputs outside the working directory, as the latest release does, rather than refusing them withERR_PATH_TRAVERSALthe way a build does. Check mode writes nothing, so the write confinement ofarchitecture:cli-surface/INV10andINV10adoes not apply to it: an absolute--outpath outside the working directory, a theme file outside it whose outputs sit beside it, and--familywith its root theme file outside it are each checked read-only. A relative--outpath that escapes the working directory is still refused, as before. Builds keep refusing every one of these writes, so when such outputs are stale, the report no longer ends with aRebuild with:command the build refuses. It names the nearest directory that contains the theme files and the outputs, and the build and check commands to run from there: each output'sSource:header records the directory its build ran from, so the check passes only from that same directory. Stale outputs inside the working directory keep theRebuild with:line. -
astryx theme buildandthemeBuild()now write only inside the working directory (cwdfor the API), asarchitecture:cli-surface/INV10requires; underarchitecture:cli-surface/INV10athe refusal is a compatible fix. Each of these builds now fails withERR_PATH_TRAVERSALbefore anything is written: an absolute--outpath outside the working directory, a theme file outside it whose outputs would go beside it, and--familywith its root theme file outside it. Paths inside the working directory still build, including through a symlink. To migrate, run the build from a folder that holds the theme file and its outputs, or pass an--outpath inside that folder; API callers pass acwdthat holds them. -
astryx upgradeno longer reports your code as up to date when the CLI is older than the installed@astryxdesign/core. Core codemods ship inside the CLI, so an older CLI has none for the releases after its own version. It used to print "No changes needed" and "No codemods available for this version range" and exit 0 while the code still needed those releases' migrations. It now warns once that it cannot finish, prints the same upgrade pinned to the CLI release that matches your Core (such ascd <project> && npx @astryxdesign/cli@<core-version> upgrade --from <old-version> --path <source-dir> --apply), and ends with "Upgrade incomplete". That command changes to the folder the run used and names the directory it scanned as an absolute path, both with symbolic links resolved, so it upgrades the same code wherever you paste it. It is written for a POSIX shell, or for PowerShell on Windows.upgrade.status(no_codemodsandconfig_fixable) andupgrade.rungain an optionalcliBehindCorefield withcliVersionandcommand, present only in that case. The codemods the CLI has still run, and the exit status and every existing field are unchanged. -
astryx integration verifyaccepts a theme module that loads the theme's own styles. Such a module imports the theme's exported stylesheet and font stylesheet for their side effects, and re-exports from the moduleastryx theme buildwrites either everything or only its theme, under the theme's export name. Verify also finds that built module by the nametheme buildgives it, the theme's own name, so a theme whose name differs from its slug no longer fails to rebuild. Any other difference from the built module still fails withtheme_module_stale, and for a module that re-exports the theme, the message now says what is wrong.
- The CLI loads Core's runtime (
@astryxdesign/coreand@astryxdesign/core/theme) only when it builds or checks a theme (theme build, and thedoctorandintegration packtheme checks), not on every start. In a project with Core installed, every other command, and an import of@astryxdesign/cli/api, skips loading Core's components and React:astryx --versiondrops from about 1.3 s to 0.5 s. Output is unchanged.
-
Global mode docs:
astryx docs globalis a top-level docs section (and one docsite page, under Guide, which Getting Started introduces and links) explaining what global mode is, the one install command, what you get, and which version runs where. Its pages, each answering one question: project, global, or both (both is recommended;global/project-global-or-both), install (global/install-once, with what to do aboutEACCES), which version runs and why (global/who-answers), updates and notices (global/updates), the agent guide on your computer (global/computer-agent-guide), integrations installed globally (global/computer-integrations), monorepos and nested folders (global/monorepos), CI and containers (global/ci-and-containers), troubleshooting every line global mode prints (global/problems), uninstall and reset (global/uninstall), and a reference of paths, environment variables, JSON fields, and error codes (global/reference).astryx docs globaland its pages are answered by the copy you start, even inside a project whose version predates them. Integration authors get a checklist with the commands to try a package globally (cli/integrations/global-integrations). Every command that does something else outside a project says exactly what in its "In global mode" note, checked against the command run in an empty folder, and every terminal line the global-mode pages show is checked against the CLI's own strings. A project's update notice now installs the CLI beside Core, so a project that installed only Core gets its own CLI too. -
The integration guides make
integration check --stricta required step in every integration's build: the quick start installstypescript@6(the checks need its JavaScript compiler API, which TypeScript 7 does not ship) and adds "Check it in every build (required)", the Build an integration overview says so, and the checks guide's section reads "Run every check in every build (required)". -
The
astryx docs cli/integrationsguides andastryx docs authoringnow match the current CLI, and the guides point to the matchingastryx docs authoring <section>for every field instead of repeating its tables. -
Fix the MCP setup snippet in working-with-ai — "type": "url" is not a transport type, and Claude Desktop adds remote servers through Connectors
-
A
docsZhordocsDenseoverlay may setnameanddisplayName, as Core's own docs do; both are ignored when the doc renders, so--strictno longer fails on them. -
doctor integration componentsreports a documenteddefaultthat differs from the literal default the component writes in its props destructuring (size = 'md') asdefault_mismatch. Quotes and spacing are ignored; where either side's default is not a literal, the prop is listed as not compared. -
doctor integration validatewarns, each with a fix, on a prop documented twice (duplicate_prop), apropDescriptionskey that matches no prop entry's name as written (unknown_translated_prop), a section with no content (empty_section), two sections of one topic with the same key (duplicate_section_key, whose fix names the key the second one is already served at), and a.doc.zh.mjsor.doc.dense.mjsoverlay beside no topic file (orphan_doc_overlay). -
A topic that takes the route of one of Astryx's own docs is named by its topic name, not an internal id, with the rename to make. A link written as a bare name (
{@link deploying}) gives the exact link to write, or says no topic has that name. -
When a doc is missing several required fields, each finding's message names only its own field.
-
doctor integration validateprints each issue's fix and file in text output, as JSON already carries them. -
Internal groundwork for
astryx doctor integration components: a comparison of the props a component doc documents with the props its TypeScript source declares, read with the project's owntypescript. Nothing reaches it yet, so the CLI's behavior and output do not change; the command starts running it in a later change, which describes what users see. -
Internal groundwork for
astryx doctor integration components: reading two written TypeScript types as one type when they differ only in how they are written (whitespace, comments, quotes, member order,Array<T>forT[], parameter names,React.qualifiers), and judging a documented prop type against the type the code declares where both read exactly. Nothing reaches either yet, so the CLI's behavior and output do not change. -
Internal groundwork for
astryx doctor integration components: finding the project's owntypescript, and reading a package's TypeScript project the waytscreads it (its owntsconfig.jsonor a monorepo root's, a solution-style config's references, and the declaration files the config includes), and finding the references in that project the compiler cannot resolve. Nothing reaches it yet, so the CLI's behavior and output do not change. -
Old-CLI notes name the published releases that behave that way: template
replaces, links, docs sections, sectionids and typed theme descriptors work from 0.6.4, and templatekeywordsfrom 0.6.6.integration verifystill names the CLI range to declare. -
The quick start makes the package an ES module, so a theme it builds can be imported.
-
Samples and output that had drifted are fixed, among them the subcomponent imports, a theme that extends another, and the upgrade output.
-
The Authoring Reference corrects how an invalid
astryx.configis handled, theissuesUrlformat, where codemod files go, and which fields nothing reads yet. Its examples use theexport default {type: ...}form thatintegration addwrites. -
astryx docs cli/integrationslinks to the Authoring Reference, and the reference links back to the guides. -
astryx docs styling-overviewandtokens-and-setupgive the real symptom of a missing StyleX compiler: importing a swizzled component throws an error that startsUnexpected 'stylex.create' call at runtime.
-
withAstryx()builds again under pnpm's default isolated layout. It no longer turns off webpack's symlink resolution, which hid each Astryx package's own dependencies (Can't resolve 'intl-messageformat','lucide-react'). Astryx modules are still matched by their real path, so a workspace-linked package keeps resolving itssourceentries. -
The PostCSS plugin now scans what each installed Astryx package publishes (its
filesfield, or the whole package minus nestednode_moduleswhen it has none) instead of everything undernode_modules/@astryxdesign. A package linked from a workspace no longer drags in its ownnode_modules, whose links can cycle back to it and made the build run out of memory, or its unpublished test fixtures. Registry installs scan the same files as before.
- The PostCSS plugin no longer loads the app's root Babel config when it compiles Astryx source. In the documented Next.js setup that config (
next/babelplus Astryx's StyleX plugin) ran over every installed Astryx module on each build only for the plugin to read StyleX metadata, roughly doubling the CSS pass. The emitted CSS is unchanged, and the app's own files still compile with its config.
Thanks to everyone who contributed to this release:
@abu-abdullah22 @azure-553 @cixzhang @dependabot @ernestt @Geervan @harjothkhara @HelloOjasMutreja @imdreamrunner @josephfarina @kentonquatman @korkt-kim @kyu-rong @Kyujenius @latxh @ManoharPaturi @nynexman4464 @potatowagon @Pushpak731 @rapsealk @vjeux @WeAreResilience
Full Changelog: https://github.com/facebook/astryx/compare/v0.7.1...v0.7.2
@astrojs/svelte@9.1.1
-
#18475
cf27e70Thanks @ematipico! - Bumps the minimum supported Node.js version to 22.23.4, which includes the fixes from the October 2026 Node.js security releasesThis is a patch release because the Node.js support and upgrade policy allows the Core team to raise the minimum Maintenance LTS version in a patch release when a disclosed and fixed Node.js security flaw affects Astro.
@astrojs/telemetry@3.4.1
-
#18475
cf27e70Thanks @ematipico! - Bumps the minimum supported Node.js version to 22.23.4, which includes the fixes from the October 2026 Node.js security releasesThis is a patch release because the Node.js support and upgrade policy allows the Core team to raise the minimum Maintenance LTS version in a patch release when a disclosed and fixed Node.js security flaw affects Astro.
@astrojs/prism@4.1.1
-
#18475
cf27e70Thanks @ematipico! - Bumps the minimum supported Node.js version to 22.23.4, which includes the fixes from the October 2026 Node.js security releasesThis is a patch release because the Node.js support and upgrade policy allows the Core team to raise the minimum Maintenance LTS version in a patch release when a disclosed and fixed Node.js security flaw affects Astro.
create-astro@5.3.1
-
#18475
cf27e70Thanks @ematipico! - Bumps the minimum supported Node.js version to 22.23.4, which includes the fixes from the October 2026 Node.js security releasesThis is a patch release because the Node.js support and upgrade policy allows the Core team to raise the minimum Maintenance LTS version in a patch release when a disclosed and fixed Node.js security flaw affects Astro.
@astrojs/react@7.1.1
-
#18475
cf27e70Thanks @ematipico! - Bumps the minimum supported Node.js version to 22.23.4, which includes the fixes from the October 2026 Node.js security releasesThis is a patch release because the Node.js support and upgrade policy allows the Core team to raise the minimum Maintenance LTS version in a patch release when a disclosed and fixed Node.js security flaw affects Astro.
astro@7.4.2
-
#18430
dda9ec3Thanks @Abhirup-99! - Speeds up content sync withexperimental.collectionStorage: "chunked"by computing each character's UTF-8 width from its code point instead of encoding every character throughTextEncoder. On a store of ~44k entries this cuts the chunking step from about 60 seconds to well under a second, with byte-identical output. -
#18438
5e0eaa4Thanks @astro-factory! - Fixesastro devwith@astrojs/cloudflarefailing withCalling `require` for "node-fetch"when a server dependency such ascross-fetchrequiresnode-fetch -
#18376
a4e201dThanks @okxint! - Fixesastro devtreating a stale lock file containing a Linux thread ID as a running Astro server. A successful process lookup with no matching PID now marks the lock as stale, preventingastro dev stopandastro dev --forcefrom killing the unrelated process that owns the thread. -
#18467
d1fb7d3Thanks @astro-factory! - Fixes thedefineConfig()types fori18n.defaultLocale,i18n.fallback, andi18n.domainsso they accept a custom locale'spathinstead of itscodes, matching how Astro validates and uses these values -
#18475
cf27e70Thanks @ematipico! - Bumps the minimum supported Node.js version to 22.23.4, which includes the fixes from the October 2026 Node.js security releasesThis is a patch release because the Node.js support and upgrade policy allows the Core team to raise the minimum Maintenance LTS version in a patch release when a disclosed and fixed Node.js security flaw affects Astro.
-
#18451
7a0ba6fThanks @DenisCDev! - Fixesastro sync,astro checkandastro buildreplacing the optimized dependencies of a dev server running in the same project, which broke framework islands in that dev server until it was restarted with--force -
#18336
e943003Thanks @astro-factory! - Fixes inline styles in dev so that framework-transformed CSS is used, such as scoped styles andv-bind()in Vue single-file components -
#18399
829fc00Thanks @astro-factory! - Fixes a dev server error (Could not find server component name) for aserver:deferisland that importsastro:actionsorastro:config/server, when its page was not the first island page loaded after starting the server -
#18330
4db9e0dThanks @Samuteg! - Fixes theastro:env/servervirtual module being generated with invalid JS in dev when any environment variable contains$replacement patterns ($`,$&,$',$1, …). The inlined env JSON was passed as a string toString.replace, so e.g. theKITTY_PUBLIC_KEYvalue set by default in Kitty terminals corrupted the module and broke dev withFailed to parse source for import analysis -
Updated dependencies [
cf27e70]:- @astrojs/telemetry@3.4.1
- @astrojs/markdown-satteri@0.4.5
@astrojs/upgrade@0.8.1
-
#18475
cf27e70Thanks @ematipico! - Bumps the minimum supported Node.js version to 22.23.4, which includes the fixes from the October 2026 Node.js security releasesThis is a patch release because the Node.js support and upgrade policy allows the Core team to raise the minimum Maintenance LTS version in a patch release when a disclosed and fixed Node.js security flaw affects Astro.
-
#18466
d7f5c54Thanks @astro-factory! - Fixes@astrojs/upgrade [version]ignoring an exact version number (for examplenpx @astrojs/upgrade 7.3.8) and upgrading to the latest release instead
@astrojs/vue@7.1.2
-
#18475
cf27e70Thanks @ematipico! - Bumps the minimum supported Node.js version to 22.23.4, which includes the fixes from the October 2026 Node.js security releasesThis is a patch release because the Node.js support and upgrade policy allows the Core team to raise the minimum Maintenance LTS version in a patch release when a disclosed and fixed Node.js security flaw affects Astro.
@astrojs/solid-js@7.1.1
-
#18475
cf27e70Thanks @ematipico! - Bumps the minimum supported Node.js version to 22.23.4, which includes the fixes from the October 2026 Node.js security releasesThis is a patch release because the Node.js support and upgrade policy allows the Core team to raise the minimum Maintenance LTS version in a patch release when a disclosed and fixed Node.js security flaw affects Astro.