Skip to content

Astryx v0.6.4

Choose a tag to compare

@cixzhang cixzhang released this 01 Oct 21:20
· 415 commits to main since this release
a51b1fb

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

npx astryx upgrade --apply

@astryxdesign/core

New Features

  • Add Timer for standardized elapsed durations without React tick renders. (#6438)
    Use Timer for active-operation elapsed time. It starts from mount by default, accepts an earlier Unix-millisecond startTime, offers elapsed and clock formats with adaptive cadence, and matches Timestamp typography props.

  • DialogHeader: expose the start- and end-content wrappers as theme targets (#6415)
    Adds dialog-header-start-content and dialog-header-end-content so themes can style the existing content-slot wrappers without relying on their DOM positions. The end-content slot also contains the optional close button. Default layout and behavior are unchanged.

  • DialogHeader title and subtitle now accept any ReactNode, not only strings.
    A title can carry inline markup and still renders inside the focusable h2 that receives focus on open and names the Dialog; the accessible name is the title's text content. A subtitle can carry inline content such as a Link. String callers are unchanged. An empty-string, boolean, or nullish subtitle renders nothing, and a numeric 0 subtitle now renders inside the subtitle text.

  • DropdownMenuItem, DropdownMenuCheckboxItem and DropdownMenuRadioItem forward a ref to the row root (#6687).
    The ref reaches the element carrying role="menuitem" (or menuitemcheckbox / menuitemradio), the way Item and DropdownMenuDivider already forward one, so a menu row can be registered with an element-keyed observer or overlay — an IntersectionObserver for an impression, a measurement, a debug outline — without a wrapper between role="menu" and the row.

  • Add presentation to DateInput, DateTimeInput, and TimeInput (spec:AST-043) (#6628).
    presentation names every picker surface, distinguishing Astryx's desktop surface, Astryx's bottom sheet (including a new TimeInput sheet), the browser/OS picker, and — for TimeInput only — a plain typed field. DateInput and DateTimeInput accept five values: 'popover' | 'bottom-sheet' | 'native' | 'adaptive-bottom-sheet' | 'adaptive-native' (default). TimeInput accepts those five plus 'text-input', the typed field on every pointer, because that is the surface its released nativePicker="never" already was. presentation="native" always shows native; adaptive-native keeps the released native fallbacks.

    nativePicker is deprecated but keeps working exactly as released (touch→adaptive-native, always→native, never→adaptive-bottom-sheet, or text-input for TimeInput); presentation wins when both are set. astryx upgrade ships migrate-native-picker-to-presentation for static callsites.

  • List: add edgeCompensation="inline" to compensate for item content inset within container padding (#2626)
    ListItem insets its content by a density-dependent horizontal padding, so a list under a section heading reads as misaligned and consumers reach for negative-margin custom CSS. edgeCompensation="inline" on List cancels, per inline edge, the smaller of that inset and the container's published padding on that edge; zero-padding and full-bleed surfaces therefore stay in place, asymmetric container padding never over-cancels an edge, and headers, hover backgrounds, and selection backgrounds keep their existing geometry.

    Item now publishes its inline inset as --_item-inset-inline and derives its own paddingInline from it, and the clamped cancelling margin reads the same variable — so the cancel tracks density and theme overrides instead of mirroring hardcoded values. Themes that set paddingInline on item feed the variable automatically via the derived var registry.

  • Markdown: add the first-party soft-breaks plugin (#6459)
    Use markdownSoftBreaksPlugin from @astryxdesign/core/Markdown/plugins to render soft line endings as hard breaks without preprocessing source. The plugin matches the real remark-breaks package through Astryx's supported adapter path while keeping code and other opaque content unchanged.

  • Table: add a selection-aware bulk-actions wrapper that consumes useTableSelectionState output while keeping the selection plugin behavior-only. (#6474)

Fixes

  • Preserve authored heading and text component styles when theme adaptations change the type scale (#6710)
  • AvatarGroup keeps its overlap when avatars are wrapped in a HoverCard or Tooltip. Every avatar and the overflow indicator now take the overlap margin, and the group pads its start edge to match, so the overlap no longer depends on each avatar being a direct child of the group (#6736).
  • Avatar initials skip punctuation: each word's initial is its first letter, digit or emoji, so Northwind Workbench (automation) renders NA instead of N( and “Ada” Lovelace renders AL instead of “L. Words with none of those, such as a lone -, are ignored, and a name made only of punctuation shows the default icon rather than a stray symbol (#6698).
  • Size an Astryx Icon without an explicit size to match its Button or
    IconButton: 16px for small and medium controls, and 20px for large controls. (#5762)
  • ChatComposerInput: an empty input no longer shrinks by 8px when isDisabled flips to true, which shifted anything bottom-aligned beside it (e.g. a send button in a grid row) (#6654).
    The root's minHeight was set to the shared line-height only, not the padding editable and placeholder both add on top of it — normally immaterial, since the editable region reserves its own padded box even when empty. A disabled, empty contentEditable region stops reserving that empty line at all in Chromium, and the absolutely positioned placeholder standing in for it doesn't contribute to layout height, so the root fell back to just the line-height and lost the padding. minHeight now explicitly accounts for both.
  • ChatComposerInput no longer scrolls for a short single-line draft with maxRows={1}. Removing vertical padding also keeps the empty input and placeholder aligned when disabled. (#6733)
  • Keep numeric zero aligned in ChatMessageBubble name and metadata slots. (#6600)
    The aligned wrappers are omitted for non-rendering scalar values (null, undefined, booleans, and the empty string), while numeric 0 remains visible inside the same inset as other slot content.
  • Render numeric zero when it is passed as the ChatMessageList empty state. A valid ReactNode should not disappear when the transcript is empty. (#6636)
  • Omit empty ChatMessageMetadata slots and their separators (#6637).
    Boolean and empty-string timestamp or footer values no longer leave a blank row or a stray dot. Numeric zero remains visible, and all delivery statuses keep their existing labels and icons.
  • Compose accepted onClick handlers with ChatSendButton's send and stop actions instead of replacing them (#6653). Consumers that used onClick to replace sending should move that logic to onSend, because both handlers now run.
  • Allow long ChatSystemMessage content to wrap within narrow chat layouts instead of crossing the container edge (#6655).
  • ChatTokenizedText now ignores empty token values so tokenized messages always finish rendering (#6679).
  • ChatToolCalls now displays custom group labels, keeps collapsed details out of keyboard navigation, adds visible focus treatment, and uses readable secondary text for neutral metadata (#6680).
  • Stop ChatLayout's scroll-to-bottom button from being a tab stop while it is invisible. At rest the layout's default scrollButton renders hidden, but opacity: 0 and pointer-events: none leave it in sequential focus navigation — so a keyboard user's first Tab into any chat landed on a control with no visible focus indicator (WCAG 2.2 SC 2.4.7), and Enter scrolled the transcript. The hidden state now also sets visibility: hidden, which the fade transition carries so the animation is unchanged; the visible button keeps its keyboard access. The pill's height and collapsed width now track --size-element-md instead of a hardcoded 32px, so a theme that retunes the element scale can no longer make the pill clip its own Button. ChatLayout's consumer docs also gain the density prop, which was undocumented, and drop the claim that density adapts automatically to container width — it never did. A Chromium evidence spec pins the hidden/visible/re-hidden keyboard contract and the pill's containment of its Button under a size-retuning theme, and the RTL applicability ledger records why ChatLayout and its scroll button have no applicable RTL dimension. (#6466)
  • Make a theme that styles chat-layout-scroll-button actually restyle the chat scroll-to-bottom pill. The documented target sat on the invisible full-width row that centres the pill, so a backgroundColor override painted a band across the chat dock while the pill kept the surface the theme asked to replace — and every automated check passed, because the override did reach an element. The target now rides the pill, which is what paints the fill, elevation, and radius (architecture:component-theming-surface INV4). The target keeps its name, stays a single target, and the component's props, DOM shape, ref target, and consumer passthrough are unchanged. A Chromium spec pins the placement and proves the repair by moving the target back and showing the pill go unstyled under a theme. (#6482)
  • Correct CheckboxIndicator theming, replacement-content, and focus guidance (#6745).
  • Align CheckboxInput theming and label-icon guidance with shipped behavior (#6747).
  • A CheckboxList option that is saving through changeAction now keeps its spinner, busy state, and re-toggle guard when another option is toggled before it settles (#6777).
  • Clicking a read-only CheckboxList option that has an onClick now fires that handler once, instead of re-dispatching the click until the browser's call stack overflows (#6777).
  • Give inert Citation references a supported accessible name, and document their linked and inert root contract.
  • CodeBlock no longer crashes on custom tokenizer types outside the built-in grammar (a dotted type such as keyword.control.sql threw from insertRule and took the whole block down). The generated ::highlight() name and --color-syntax-* custom property now pass through CSS.escape before entering the dynamic stylesheet, so every type the CSS parser accepts (dotted, digit-led, _private, non-ASCII) keeps its colour, and no token type can reach outside its own highlight rule. A rule the engine still refuses costs that type its colour, never the block. (#5528)
  • Dialog and 30 other components no longer lose their border and background resets in the shipped CSS. The sources used the border: 'none' and background: 'none' | 'transparent' shorthands, which StyleX's default property-specificity mode drops silently, so the declarations never reached astryx.css; a consumer that does not load reset.css saw the UA <dialog> frame. They are now the borderWidth / borderStyle / backgroundColor longhands. No API change.
  • Apply the same narrow blocked-scheme rule to native links, custom routers, clickable surfaces, and Markdown links. Rejected destinations stay visible without navigating or invoking a router, including structured URLs with a separate protocol. Ordinary URLs, safe custom schemes, downloads, and accepted router-object identity are preserved; Markdown image/resource policy is unchanged. (#5524)
  • Preserve Tailwind font weights when using the theme bridge. (#6479)
  • Ignore IME key events (isComposing or keyCode 229) in useHotkeys, even with allowInInputs enabled, without preventing their default behavior (#6773).
  • Keep hug-layout segmented controls content-sized in flex containers (#6643)
  • Keep a reopened top layer at the front of Escape dismissal order. (#6073)
  • Prevent ancestor text formatting and surface/group context from leaking into Layer content. (#6457)
    Layer content now starts with theme body typography and neutral text formatting. Core layer content no longer inherits accidental ancestor surface/group membership, including group-owned disabled state, selection, callbacks, and label associations. Intentional groups and required providers created inside the layer still apply. Unrelated contexts, explicit props, themes, and styling overrides remain unchanged.
  • List with hasDividers no longer draws a divider after the last ListItem. The last-item reset used the borderBlockEnd shorthand, which StyleX's default property-specificity mode drops silently, so it never reached the shipped CSS; it is now the borderBlockEndWidth longhand. (#6420)
  • Localize PowerSearch boolean operators in Japanese and Korean and use locale-appropriate ellipses in Chinese. (#6783)
  • Markdown: keep lazy continuation lines inside blockquotes and list items (#6752)
    Wrapped paragraph lines may omit repeated blockquote markers or list indentation without escaping their owning container. Nested, ordered, unordered, and task-list continuations now preserve their rendered structure, text projection, and source range.
  • Markdown tables now use the spacing-2 token (8px) on every header and body cell edge. The tighter inline spacing leaves more room for content in narrow reading columns while keeping rows comfortably readable.
  • Markdown: render escaped table pipes literally in code spans (#6642)
    A \| used to keep a pipe inside a table cell now displays as | in inline code, matching prose cells without exposing the structural backslash. Completed inline-code spans render only their parsed contents inside <code>, without source backticks; standalone inline code otherwise remains unchanged.
  • Markdown: table columns keep a content-derived width floor and headers stop truncating (#6708).
    A Markdown table in a narrow reading column no longer squashes every column to a few characters. Each column keeps a floor derived from its own content, measured in characters on the cell's text box, so a column is never narrower than its longest unbreakable token and cell padding does not eat into the floor. Identifiers, URLs, and inline code stay whole, header labels wrap instead of ellipsizing, and a table that is genuinely wider than its container scrolls in Table's own scroll region — which is now the table's only scroll viewport, accessible name, and keyboard stop.
  • Align titled sections in DropdownMenu and ContextMenu bottom sheets with their spacious action rows (#6694).
  • Keep MetadataList side labels readable beside long badges in narrow containers by allowing value columns to shrink, including numeric columns and custom label widths. (#6598)
  • PowerSearch: enum_list value menus now show every value instead of the typeahead default of 10. (#6481)
  • Selector keeps compact single-line triggers aligned with their size tokens, including icons and clear controls.
  • Name the no-search bottom-sheet Selector listbox from the component's label so Chromium exposes it with an accessible name; the searchable sheet and popover paths keep their existing trigger relationship. (#6395)
  • Keep Tailwind bridge tokens reference-only so utilities follow the active theme without emitting competing runtime declarations.
  • Keep authored theme declarations inside their CSS boundaries. Drop only an unsafe declaration, preserve valid CSS values and legacy token generation, and continue compiling the rest of the theme. Runtime reports dropped declarations on the console; theme builds include them in the existing receipt warnings. CSS generators accept an optional warning-text array for build collectors, without a callback API or additional exported diagnostic types. (#5529)

Documentation

  • Document CheckboxList's isReadOnly prop, and separate the select-all block example's rows with hasDividers instead of placing a Divider inside the options list (#6777).
  • Clarify that CheckboxListItem reads isChecked and onCheck inside a CheckboxList without value (such as a select-all item), and requires value only when the parent CheckboxList has a value array (#6778).
  • Clarify that CheckIndicator is the selection mark itself and does not render persistent control chrome.

@astryxdesign/cli

New Features

  • Add a reusable Item document tabs block template (#6652)

  • astryx build "<idea>" now always names a page template to start from (#6707).
    A page template carries the page frame, the spacing, and the section rhythm. A page composed from components has to rediscover them. Before, the kit recommended scaffolding only when a template matched almost by name. Other ideas got "use it as a layout reference", which pointed at a 35-line --skeleton, and an idea that no template matched got "frame with AppShell, then compose". Now:

  • Link docs by identity, and let integrations add to the docs tree. (#6626)
    A doc links another doc inside its text with {@link [<provider>:]<kind>:<name>}, such as {@link command:doctor}. The CLI prints each link as the astryx docs command that opens the doc; a link that names no doc prints as written, and astryx doctor warns on it. An older CLI prints the link as plain text. reference, workflow, and collection blocks stay in namespace docs.

    An integration can ship namespace docs and place its guides in them. They show up in astryx docs beside cli, with the same moves, links, and search, and astryx doctor integration docs checks them before the package ships. A namespace doc in an integration's docs directory no longer fails to load, and astryx integration add doc <name> --parent <namespace> writes a placed guide. A CLI release that does not read the docs tree can hide every doc topic of a package that ships a namespace doc or a placed guide, with nothing saying why, so --parent declares the CLI that reads them as an optional @astryxdesign/cli peer, and astryx integration pack --check fails a package that ships either one without it.

    The CLI keeps its own routes: an integration's flat topic or namespace named cli, unorganized, or after one of the CLI's topics (compared without case) is withdrawn from the tree, its name opens the CLI's doc, and doctor warns.

    Every flat topic now sits in astryx docs unorganized, under its own name, so every doc has a place in the tree with a way up and across. Search hits for a topic name the level and the package that wrote it.

    New guide: astryx docs cli/writing-docs.

  • In text, astryx docs <topic> lists the topic's sections when it has more than one. (#6626)
    Read one with astryx docs <topic> <section>, or print the whole topic with --full. --dense still prints the whole dense doc, so the agent bootstrap in AGENTS.md reads the same as before. The JSON contract is unchanged: astryx docs <topic> --json and docs(topic) in @astryxdesign/cli/api still return the whole topic (docs.detail), and --index returns its section list.

  • Read the CLI's docs as a tree, one level at a time. (#6498, #6626)
    astryx docs cli lists the CLI's guides and reference. astryx docs cli/commands lists every command, astryx docs cli/api lists the API's functions, schemas, and enums, and a route such as astryx docs cli/api/functions/search prints one doc. --json returns docs.node for a namespace or typed doc, identified by its doc identity (a generated level has id: null). The text of astryx docs lists the docs tree's namespaces first; its --json keeps data as the topic list and adds them in meta.namespaces. Every command, API function, schema, and enum doc the CLI ships declares the namespace that reads it: astryx doctor fails when one has none or names one nothing reads, and warns when one has no route in the tree.

  • Export themeTemplate() from @astryxdesign/cli/api, matching the documented API behind astryx theme template. (#6626)

  • The integration guide moved from astryx docs cli-integrations to astryx docs cli/integrations. (#6626)
    Documentation names and routes are mutable catalog data under spec:AST-017/FR45, so the move is nonbreaking and needs no compatibility alias. The guide now lives in the CLI's docs tree, under cli. Use astryx docs cli/integrations, including section reads such as astryx docs cli/integrations components. The old name no longer resolves. The docsite page stays at /docs/cli-integrations.

  • astryx search finds the smallest doc part that answers. (#6626)
    A doc hit is now one section (astryx docs cli/integrations codemods), one docs-tree route (astryx docs cli/api/functions/assert-response), or a topic's index, never a whole-topic read. Typed docs match by their own name and by the identifiers they define, such as error codes. astryx search --type doc works without @astryxdesign/core. astryx docs lists the docs tree first, and a namespace shows each child's own name when its route name differs.

    Every docs read now ends with its moves: Up, plus Previous and Next for a section or a docs-tree page, and Related for a typed doc (its command or API function, and its related docs). --json carries them as links (up, previous, next, related). Each search hit carries parent, the command that opens the level above it, and a docs-tree hit carries package.

  • A doc section can include another doc instead of copying it. Put a reference block in the section, such as {type: 'reference', target: '@astryxdesign/cli:schema:integration', projection: {fields: ['components', 'docs']}}, and astryx docs prints those two fields of the integration manifest from the schema's own doc, then the command that opens it.
    A reference block includes a schema, command, function, or enum doc. projection.fields keeps only the named fields of a schema, and presentation is full (the default), compact (no code blocks), or summary. A reference to any other doc shows its title and summary. A read inlines the block, so --json still returns only the stable block kinds. astryx doctor integration docs fails when a block names a doc, field, or projection it cannot include, and a read marks what is missing. Links also find the CLI's authoring schemas now: schema:integration opens astryx docs authoring integration.

  • astryx doctor now fails when a type that @astryxdesign/cli/authoring exports has no doc in astryx docs authoring, or when a listed self-doc documents nothing the package exports. The message names each type, the module that declares it, and the self-doc that is missing or not listed. (#6498)

  • Add presentation to DateInput, DateTimeInput, and TimeInput (spec:AST-043) (#6628).
    presentation names every picker surface, distinguishing Astryx's desktop surface, Astryx's bottom sheet (including a new TimeInput sheet), the browser/OS picker, and — for TimeInput only — a plain typed field. DateInput and DateTimeInput accept five values: 'popover' | 'bottom-sheet' | 'native' | 'adaptive-bottom-sheet' | 'adaptive-native' (default). TimeInput accepts those five plus 'text-input', the typed field on every pointer, because that is the surface its released nativePicker="never" already was. presentation="native" always shows native; adaptive-native keeps the released native fallbacks.

    nativePicker is deprecated but keeps working exactly as released (touch→adaptive-native, always→native, never→adaptive-bottom-sheet, or text-input for TimeInput); presentation wins when both are set. astryx upgrade ships migrate-native-picker-to-presentation for static callsites.

  • Prepare integration template replacement before its supported package boundary. (#6265, #6626)
    An integration template can set replaces in its own metadata to a Core template id. Unqualified template lookup and discovery surfaces use a valid replacement, while --package @astryxdesign/core still selects the original. Missing targets, type mismatches, a declaration on a template that cannot be used, and duplicate declarations fail closed and are reported by astryx doctor integration templates.

    When separate configured packages replace one target, the package configured later wins with a warning. Explicitly configured packages always precede autolinked ones. When only autolinked packages conflict, the dependency listed later in package.json wins with a warning and the CLI recommends explicit configuration. Invalid contribution kinds remain reportable without hiding other valid kinds, and invalid template or component files do not hide valid siblings.

    This implementation may ship in final 0.6.x for forward validation, but an integration package that uses replaces must still require @astryxdesign/cli >=0.7.0. Earlier CLIs reject the field and withhold that package's templates and doc topics. No supported latest-stable integration consumer can enter the replacement path yet, so replacement selection and its mutable catalog results are patch-compatible pre-publication behavior.

    Existing same-id IntegrationTemplateConflict responses keep their released warning-only shape on 0.6.x. The optional TemplateListEntry.replaces field is additive; at or after 0.7.0, replacement-specific relationship, replaces, and severity: 'info' conflict fields require one deliberate projection update across runtime, types, generated reference, terminal output, documentation, and tests. A package-version bump alone leaves the warning-only shape unchanged.

  • Add ScrollableArea block templates so the component page has worked examples. All six share one content vocabulary — a workspace file panel of labelled sections holding a two-column grid of muted cards — so the only thing that changes between examples is the behavior each one demonstrates: block-axis scrolling, axis="inline" with isFullBleed, sticky section labels, axis="both", sticky pass-through against stickyContainment="always", and overscroll allow against contain. (#6490)

  • Add the Tree Table page template (#6195)
    A hierarchical table where every parent row is derived from its children, shown as a code repository: folders roll up the newest commit beneath them, columns resize, sorting is scoped to siblings so branches never interleave, arrow keys walk the tree per the APG treegrid pattern, and search prunes to the branches that match. Selecting a folder drives a header trail that folds its middle into a menu once the path outgrows the bar. Supporting repository chrome — header actions, an About sidebar and a README rendered with Markdown — puts the table in the context that makes its rollups legible.

  • Add opt-in typed documentation graph contracts and stable provider-aware documentation identity without breaking existing topic readers. (#6471)
    New NamespaceDoc, AuthoredDocKind, provider identity types, semantic graph-block types, section IDs, astryx docs <topic> --index, and section-key reads are additive. The public ReferenceContentBlock union keeps its 0.6.x members so existing exhaustive renderers continue to compile; graph-only workflow, collection, and reference blocks are exported separately as GraphContentBlock and are accepted by NamespaceDoc.

    Existing authored topics continue to load and read as before. Duplicate title-derived keys receive deterministic suffixed index keys, titles with no Latin letters or digits receive deterministic section-N keys, ambiguous title queries keep returning the first match, and legacy extension sections without IDs continue to merge by exact title. Explicit new section IDs remain validated. The new progressive-disclosure Doctor audit reports compatibility issues as warnings, and existing full-topic text output keeps its 0.6.x formatting.

    Every doc section can opt into a stable id. astryx docs <topic> --index (docs(topic, undefined, {index: true})) returns the topic's section index (docs.index), and astryx docs <topic> <key> reads one section. A topic read still returns the whole doc. astryx docs authoring documents every authoring schema, one section each.

    Provider-ID conflicts are now visible instead of being dropped without a word: the package being authored wins, otherwise the first-loaded provider wins, and every command plus astryx doctor reports the set-aside package.

  • Integration themes use typed same-stem descriptors, not a central catalog. (#6498)
    A themes root no longer holds manifest.json, the theme catalog that astryx integration add theme wrote in 0.6 (stable since 0.6.3). Each theme carries a strongly typed <name>Theme.doc.mjs beside its source instead, and a themes root that still holds the catalog is refused. To migrate an integration package, run astryx upgrade --from 0.6.3 --path . --apply in it: a codemod writes each theme's descriptor from its catalog entry and removes the catalog. Until a package is migrated, apps that install it get none of its themes or doc topics.

    Theme discovery reads descriptors and checks integration theme sources without executing them, and theme add copies an integration theme's complete directory. astryx doctor integration validate warns about a folder in the themes root that looks like a theme but is not read as one. New component, topic, and template scaffolds also emit type-annotated .doc.mjs; released .template.* inputs remain readable.

Fixes

  • The published authoring types now type-check in projects that use "moduleResolution": "nodenext" or have no Node types installed. Relative imports inside them name their files, and PostCodemodCommand's env no longer needs Node's types. (#6492)

  • Drop gpt-tokenizer from the CLI's peer dependencies. Nothing in the CLI imports it, but npm and pnpm install a required peer by default, so every install of the CLI pulled in about 53 MB it never used. (#6508)

  • astryx init, astryx init --remove-agents and astryx upgrade --apply no longer edit or delete a file outside the project through an agent file or .claude/ directory that is a symlink pointing there. Init reports a path-safety error for that file and exits 1, init --remove-agents fails with ERR_PATH_TRAVERSAL and exits 1 instead of reporting the block removed, and upgrade reports the refresh as failed, before any file is written. (#6524)

  • The text output of astryx component and astryx hook no longer adds non-ASCII characters of its own. Empty table cells show - instead of an em dash, the brief view's import hint reads <- from, derived properties and deprecated targets use ->, and brief prop and parameter lists are separated by commas instead of middle dots. Text that comes from the docs themselves is unchanged. (#6553)

  • astryx init human output is now plain ASCII, including the per-file lines init --remove-agents prints. Status lines use [ok] instead of a check glyph, and dashes, bullets and arrows print as - and ->. --json output is unchanged. (#6540)

  • astryx upgrade human output is now plain ASCII. Progress and codemod lines use [ok], ! and !! instead of check, warning and cross glyphs, and dashes and arrows print as - and ->, including in codemod titles listed by --list. --json output is unchanged. (#6539)

  • Commands that write files no longer follow a dangling symlink out of the project: when the target, or a directory on the way to it, links to a missing path outside the project root, the command now fails with ERR_PATH_TRAVERSAL instead of creating the file there. A symlink escape reported by integration add now carries ERR_PATH_TRAVERSAL too, instead of an unregistered PATH_TRAVERSAL code. (#6513)

  • astryx blog text output now labels the feed URL feedUrl, matching its --json key, and prints every post field the JSON carries: the list adds each post's description, date, authors and link, and a post read adds its metadata above the body. (#6526)

  • astryx build "<idea>" no longer prints a setup: line in its text output. That field existed only in the text, never in the --json kit, so the two views disagreed. The same guidance is in the no-query astryx build playbook. (#6570)

  • astryx build --json with no query, and build() with no query, now return the playbook itself — a title, the ordered steps with their commands, the on-system rules, and related lookups — instead of only {playbook: true}. The terminal output is rendered from the same data, and playbook: true is still there. (#6566)

  • The programmatic component() API now rejects detail and lang values that the astryx component command rejects, with the same codes (ERR_INVALID_DETAIL, ERR_INVALID_LANG). It used to fall back silently: an unknown detail returned the name list, and an unknown lang returned English. (#6576)

  • The programmatic component(name, {cwd, blocks: true}) now discovers blocks from the cwd it is given, as every other slice already does. It used to read blocks from the process working directory, so a caller pointing at another project got that directory's blocks, or none. (#6580)

  • parentDoc in astryx component <Name> --json is now a documented part of the component.detail response. The field appears when a sub-component such as HStack is scoped out of its parent's doc. It is in the published response type and the component() reference, and the text output now shows it as parentDoc: Stack. (#6575)

  • astryx component <Name> no longer prints a "Related block templates" list that --json never carried, so the text output shows only what the JSON result holds. The same blocks are still listed by astryx component <Name> --blocks, in text and JSON. (#6574)

  • astryx component <Name> --package <pkg> no longer ignores --source and --blocks when the package publishes docs through the legacy astryx.docs field. --source now fails with ERR_NO_SOURCE, and --blocks returns the blocks, the same answers as without --package. Before, both flags silently returned the plain doc. (#6577)

  • astryx component --list now prints the right import for components from packages that publish docs through the legacy astryx.docs field. It used to show an @astryxdesign/core path for them. The JSON list entries now carry the same import that astryx component <Name> reports for each of those components. (#6578)

  • astryx integration add component <Name> now refuses with ERR_FILE_EXISTS when a component doc anywhere under the components root already uses that name, for example components/<Name>/<Name>.doc.mjs. Before, it wrote a second <Name> beside the first and reported success, and astryx component <Name> then showed the new scaffold instead of the authored component. --dry-run refuses the same way. (#6557)

  • The debug entry of the AstryxConfig type and of astryx docs authoring config now states how handlers from integrations combine with the app's own: the app's runs first, then each integration's in load order, a handler that throws is skipped without affecting the others or the command, and {"astryx": {"inheritDebug": false}} refuses inherited handlers. The type no longer claims that leaving debug out records nothing. (#6514)

  • The text output of astryx discover (the package list and a single package) now shows every field its --json entry carries, including category and version, which only the JSON used to include. (#6521)

  • astryx doctor text output now uses the same field names as --json: each check prints its id and label (the label was shown as check, and the id was missing), and the summary prints pass, warn, fail, and info under a summary heading instead of a prose line. (#6516)

  • astryx template --help and astryx layout expand --help now explain how the path argument is read: a path ending in a source-file extension is the file to write, anything else is a directory that gets page.tsx, the block's file name, or <Name>.tsx. layout expand and layout check also document - for stdin and that --file wins over the argument, and --overwrite no longer mentions a prompt the CLI never shows. (#6571)

  • The CLI no longer reads or sets the ASTRYX_LATEST_VERSION environment variable. Its only effect was an FYI: A newer version of @astryxdesign/core ... line on stderr after astryx component and astryx docs, and a CLI run cannot set a variable for later runs, so the line appeared only when the variable was set by hand. Commands now print the same output whether or not it is set. (#6554)

  • The @astryxdesign/cli/json types now declare apiVersion on CLIError, CLIUnsupportedError, and the success envelope that parseResponse and assertResponse return, matching what every --json envelope carries. Code that constructs a CLIError value by hand, for example in a test double, now has to include apiVersion. (#6555)

  • astryx <command> --help (including astryx manifest --help) now ends with the command's documented exit codes, and each command in astryx manifest --json carries them as exitCodes: [{code, when}]. astryx doctor --help shows them once, and the layout and discover exit codes now say when they apply: bare astryx layout exits 1, and a blank discover query exits 1 when packages are discovered. (#6586)

  • astryx gap-report --help and astryx manifest now describe the component argument and say that component, --category, and --reason are required unless --list-categories is set, with the character limits the command enforces. The manifest listed the argument with an empty description, and nothing said these inputs were required. (#6518)

  • astryx theme build now fails with ERR_THEME_INVALID, before writing anything, when a custom Heading type's standalone rule has no usable declaration: every value is blank, or the compiler dropped every declaration. It previously wrote CSS with an empty or missing rule and still added the type to the generated HeadingTypeMap. (#6547)

  • With --json, astryx help <unknown-command> and a command group run without a subcommand (such as astryx layout --json) now return an error envelope, with ERR_UNKNOWN_COMMAND or ERR_MISSING_ARGUMENT, instead of a success-shaped help envelope. Both still exit 1, as they do without --json. (#6550)

  • astryx hook <name> text output no longer lists block templates that the --json envelope does not carry. It now names the related components and points to astryx component <name> --blocks, which returns their block templates as JSON. (#6537)

  • "astryx": {"inheritDebug": false} in package.json now also refuses the debug handler of an autolinked integration in a project that has no astryx.config. The setting used to be read only beside a config file, so without one those handlers still received every run. (#6520)

  • Programmatic init() now confines the starter template it scaffolds with templateName to the project directory. A src symlink that points outside the project is rejected with ERR_PATH_TRAVERSAL before anything is written. (#6538)

  • astryx integration add --help and the CLI manifest now define every control: the name format for each kind, that --type defaults to page, that --to takes an exact semver version, and that --replaces and --extends can't be combined. The integrationAdd() docs say the same. Behavior is unchanged. (#6558)

  • One integration that cannot load at all (a configured package that is not installed, has no manifest or more than one, or a package being authored with two manifests) no longer takes every other integration down with it. It is reported as that package's integration issue, the other integrations keep contributing, and astryx discover no longer exits 1 because of it. (#6519)

  • One integration whose templates root cannot be read, for example a manifest that points templates at a file, no longer makes astryx template fail with a raw filesystem error. That package's templates are skipped with the usual one-line warning, and core templates and every other integration's templates still list and resolve. (#6523)

  • The --json option's description in astryx --help, in the manifest, and in the CLI README now lists every envelope field: { apiVersion, type, data, meta? } on success and { apiVersion, error, code, suggestions? } on failure. It used to omit apiVersion, meta, and the stable code field that consumers branch on. (#6549)

  • astryx layout expand <expr> <dir> now refuses the write, with ERR_PATH_TRAVERSAL, when <dir>/<Name>.tsx is a symlink to an existing file outside the project. Before, only the directory was checked, so the generated TSX replaced the file the link pointed at. (#6564)

  • astryx layout expand <expr> <path> now labels its text fields componentsUsed and todos, the keys the --json output uses, instead of Components and TODOs. (#6569)

  • astryx layout expand now caps every *N repeat at 10000 copies. Repeated table rows skipped the cap, and a huge count on any element was still walked copy by copy before the cap applied, so B*999999999 could hang or run out of memory. astryx layout grammar now states the cap. (#6565)

  • astryx layout check - and astryx layout expand - now stop reading stdin at 5 MB and fail with ERR_INVALID_ARGUMENT, the same size cap --file already had. Before, an endless or oversized pipe was buffered whole until memory ran out. (#6572)

  • When a command's module fails to load, running that command with --json now prints one error envelope (ERR_UNKNOWN, with the load error in the message) instead of printing nothing to stdout. Without --json the error is still printed to stderr, and the exit code is still 1 in both modes. (#6551)

  • astryx manifest without --json now labels each command's name name:, the same key the JSON manifest uses, instead of command:. (#6552)

  • astryx theme build --out now reports a path that leaves the working directory with ERR_PATH_TRAVERSAL, and an output directory it cannot create with ERR_WRITE_FAILED, instead of unregistered codes such as PATH_TRAVERSAL or EEXIST. The programmatic themeBuild() throws the same codes as an AstryxError. (#6543)

  • astryx theme palette generate now writes candidate JSON in the canonical form the astryx-oklch-v1 recipe pins, so a JSON candidate and the candidateSha256 in its receipt match the recipe's reference fixtures byte for byte. Before, the stops array was printed on one line, which changed the bytes and the digest of every JSON candidate. (#6531)

  • astryx theme palette generate and generateTonalPalette() now reject a neutralProfile the recipe does not define, even when the request has no neutral family. Before, such a request produced a candidate whose receipt recorded the unknown profile as part of the normalized request. (#6534)

  • astryx theme palette generate now reports an output or preview path it cannot use, such as one below a regular file, with the stable ERR_WRITE_FAILED code. Before, the --json error envelope carried the raw system error name, such as ENOTDIR, as its code. (#6533)

  • --json output is one envelope again when astryx.config or an integration manifest prints while it loads. Anything a project module writes to stdout during its load now goes to stderr. (#6581)

  • A --json error envelope's code is now always one of the documented error codes. A failure that carried a Node.js system code, such as ENOTDIR or EACCES from a failed write, used to put that code in the envelope; it now reports ERR_UNKNOWN, and the original message is unchanged. (#6548)

  • The 0.6 rename-resizable-pixel-bounds upgrade codemod now also renames minSizePx/maxSizePx in static inline useResizable configurations called through a namespace import (Astryx.useResizable({...})) or wrapped in as const or satisfies. These were left unchanged before. (#6541)

  • astryx search now fails with ERR_CORE_NOT_FOUND, like component and hook, when @astryxdesign/core cannot be found, instead of the catch-all ERR_UNKNOWN. (#6525)

  • astryx search --limit now refuses a value that is not a positive integer, such as 1.5 or 5abc, with ERR_INVALID_ARGUMENT and exit 1, as search({limit}) already did, instead of silently truncating it. (#6528)

  • astryx search text output now prints every field its --json results carry: title for doc results and kind for template results were missing. The --verbose help now says what it adds: each result's score and match reason. (#6527)

  • search() from @astryxdesign/cli/api is now declared to return SearchResponse, as its docs say, so TypeScript sees each result's SearchResultEntry fields instead of a bare object. (#6529)

  • astryx swizzle no longer writes outside the project through a symlink in the output folder. When the component folder or one of its files is a symlink that points outside the project, the command now fails with ERR_PATH_TRAVERSAL before it writes anything. (#6573)

  • The astryx swizzle --overwrite help and manifest entry no longer says it skips a prompt. The CLI never prompts. The entry now says that without --overwrite, existing files fail the command with ERR_FILE_EXISTS and nothing is written. (#6579)

  • astryx template <name> <dir> now refuses the write, with ERR_PATH_TRAVERSAL, when the file it would create in that directory is a symlink to an existing file outside the project. Before, only the directory was checked, so --overwrite followed the link and replaced the file it pointed at. (#6563)

  • astryx template <name> <path> and astryx layout expand now replace demo media only when a path starts with /template-assets/, so third-party URLs and product paths that merely contain that text are left alone. Demo media in a subdirectory, with a query string, or with characters such as @ in the file name is now replaced whole instead of being corrupted or left behind. A demo media reference that cannot be replaced safely, such as one with no file suffix or one built at runtime, now fails the copy with its path. (#6596)

  • astryx theme add now copies every file a theme catalog lists byte for byte, so a theme that ships a font or an image arrives intact. Before, it decoded each file as text, which corrupted any file that was not UTF-8. (#6530)

  • astryx theme add no longer writes through a symlink that already sits at the temporary name it stages each file under. A link that leads outside the project now fails with ERR_PATH_TRAVERSAL, and any other entry at that name fails the copy instead of being overwritten. (#6535)

  • astryx theme build and astryx theme palette generate now print plain ASCII: status lines use [ok], [warn], [error], [fail], and [note] markers instead of symbol glyphs, and theme build messages drop em dashes and ellipses. The reworded font and private-variable messages also appear in the --json receipt's notices and warnings. Generated theme files are unchanged. (#6544)

  • astryx theme build --help and the capability manifest now state each flag's default and which flag combinations are refused (--family with --out or --watch, --check with --watch, --watch with --json, --out with more than one file), with the error code the refusal returns. (#6546)

  • The themeBuild() reference and the theme.build response-type entry (and the CLI README table generated from it) now document every field of the receipt by name, including notices, the advisories (such as a font the theme names but does not load) that had no documentation. (#6545)

  • astryx theme template now reports a file it cannot write with the stable ERR_WRITE_FAILED code. Before, the --json error envelope carried the raw system error name, such as EEXIST or EISDIR, as its code. (#6532)

  • astryx upgrade --json now prints exactly one JSON envelope when a post-codemod hook prints output. Anything a hook's buildCommand writes goes to stderr, so stdout carries only the result. (#6536)

  • The published UpgradeListEntry type now declares optional, the boolean every astryx upgrade --list --json entry already carries, so typed callers can read it without a cast. (#6542)

  • astryx doctor integration validate, templates, components, and docs now show the invalid_package_json error when a local integration's package.json can't be parsed. Before, the text output said no astryx.integration.* file was found and hid the error, because the JSON reported a null name, which means no manifest. In that case data.name is now (local package). (#6559)

  • astryx docs authoring now says which docs-graph features are not built yet: the placement, aliases and audience fields, the workflow, collection and reference blocks, namespace docs, and the artifact, doc and instance identities. It had described them as working, but a topic that uses them fails to load. A namespace doc in an integration's docs directory now fails with a message that names it, instead of reporting missing topic fields. (#6492)

  • astryx doctor integration validate now ends each finding about a misplaced contribution, a stray codemod file, a mis-named codemod folder, a component doc without its source, or a component source without a doc with a fix that works when followed as written: where to move the file and the manifest line to add, the version folder a codemod belongs in, or the file to add. Codemod files are named by their path inside the package instead of an absolute path. A hidden component doc no longer draws the missing-doc warning, and a codemods root at the package root no longer reports the manifest as a stray codemod. (#6498)

  • An integration topic that extends another no longer renames it. astryx docs theme with an extension installed used to print the extension's own title and description; the topic now keeps its own, and the extension only adds or replaces sections. A topic that replaces another still renames it. (#6498)

  • Use extensionless subpath specifiers for generated integration imports (#6288)
    integrationAddComponent and integrationAddTemplate now emit extensionless public import specifiers (@pkg/components/MyWidget instead of @pkg/components/MyWidget.tsx) and map them to source files through the package exports field. Consumer imports no longer expose the package's source extension or require allowImportingTsExtensions.

    integrationPackCheck now rejects public specifiers ending in .tsx or .ts and validates each exact import from the packed artifact through Node's package resolver. Packages without a usable public export fail the check, and the result does not depend on project-local TypeScript.

  • Make every command's text output match its --json data (#6616)
    upgrade --list text now renders each codemod from the JSON result (name, title, version, optional) instead of the API logger, with no (undefined) rows. theme targets prints one line per target via the formatter kit's inline layout and now shows className. docs list shows the package field. A manifest-driven parity test covers the commands this PR fixes and catches future regressions in the same family; commands with known deferred divergences are covered by envelope and exit-code checks and have allowlist entries that explain the gap.

  • Correct the palette authoring types. TonalPaletteCandidate's description had landed on TonalPaletteAnchor, so the generated .d.ts documented the wrong type and left the candidate bare. TonalPaletteFamilyInput — the type an author writes by hand — had no property descriptions, and neutralProfile never said what its four values do. (#6168)
    [fix] Give the generation receipt a real type. generationReceipt was Record<string, unknown>; it is now TonalPaletteGenerationReceipt, with TonalPaletteRampDiagnostics, TonalPaletteCoordinationDiagnostics, and TonalPaletteNormalizedRequest beside it. The generator's internal typedefs point at the same types, so the compiler holds the documentation true instead of letting it drift.

    [docs] Replace the stale xds command name with astryx across 79 lines of API type docs in 11 files, and realign the invocation tables. Codemods and changelogs that reference the old name are untouched — migrating it is their job.

  • Protect generated, vendored, ignored, linked, dependency, and out-of-root files from upgrade codemods using working-tree declarations. Upgrades now run declared regeneration hooks, recheck protected outputs, and report incomplete changes in human and JSON results (#6692).

  • Restrict the authoring factory codemod to Astryx imports (#6335)

  • Restrict the status-variant and Avatar-size upgrade codemods to static props on verified imported JSX components, avoiding unrelated literal rewrites. (#6445)

  • Test and fixture files under a codemod version folder are no longer loaded as codemods. Every .ts/.mjs/.js file under a version folder was loaded and validated, so a test colocated with its transform failed validation and — a definition error being a hard error — took every codemod in that version with it, while upgrade applied nothing and reported success. Reserved names: *.test.*, *.spec.*, *.fixture.*, and anything under __tests__/ or __fixtures__/. (#6230)

  • A stamped component doc (type: 'component') that documents several components with components now loads, as the published ComponentDoc type allows. It used to fail with "props: expected array". Each entry must name its component; an entry without a name fails at load instead of later in a reader. (#6492)

  • Keep authored theme declarations inside their CSS boundaries. Drop only an unsafe declaration, preserve valid CSS values and legacy token generation, and continue compiling the rest of the theme. Runtime reports dropped declarations on the console; theme builds include them in the existing receipt warnings. CSS generators accept an optional warning-text array for build collectors, without a callback API or additional exported diagnostic types. (#5529)

  • Make the Toolbar — Table Filter block's filters actually filter, and bring the row up to the pattern the Filterable Table page template demonstrates: each closed selector doubles as its own filter chip, clauses fold from the end into a count as the row narrows, and the result count, clear all, and a column picker follow the clauses. (#6478)

Documentation

  • astryx docs authoring now documents the DebugEvent a debug handler receives and the GapReportHandler contract, field by field. Both types were exported from @astryxdesign/cli/authoring with no section of their own. (#6498)
  • astryx docs authoring now matches the published authoring types field for field, and a test keeps it that way: every field is listed, with its real type and whether it is required. Three entries were wrong: a component doc's usage is optional on sub-component docs, a command option's default may also be a boolean or a list, and a codemod's type is 'code' or 'config'. (#6492)
  • Document CheckboxList's isReadOnly prop, and separate the select-all block example's rows with hasDividers instead of placing a Divider inside the options list (#6777).
  • The documented exit codes for astryx build now say that a query exits 1 when @astryxdesign/core cannot be found, while the playbook (astryx build with no query) needs no core. (#6592)
  • astryx discover --components is now documented as what it does: in the package list it prints every component of each package instead of the first 10 and a "+N more" count, and it changes nothing in --json. It was described as "List components only". The help also gains an example. (#6522)
  • astryx init --help and the manifest now say how init's flags interact: --remove-agents only removes the managed block and ignores the install flags, --all overrides --features, and --agent and --agent-docs-path apply only when agent docs are installed, with an explicit path taking precedence. The documented exit codes now include an --agent-docs-path outside the project (exit 1) and no longer list two template cases the CLI cannot reach. (#6589)
  • astryx theme add --overwrite and astryx upgrade --install-deps no longer describe a prompt the CLI never shows; each now says what happens without the flag (ERR_FILE_EXISTS with nothing written, or ERR_DEP_MISSING). ERR_FILE_EXISTS is described as "Refused to overwrite an existing file." without the "non-interactive mode" qualifier; its meaning is unchanged. (#6593)
  • The CLI README now shows apiVersion in every hand-written envelope shape and example, and lists astryx search --verbose in place of --detail, which needs a level and does not add a result's score or reason. (#6587)
  • The response-type docs, and the CLI README table generated from them, now name the fields of the component.detail, docs.index, search, build.kit, gap-report.file, theme.build, theme.targets, and integration.pack-check responses by their JSON keys, including parentDoc, hint, notices, deprecatedFor, each gap-report delivery's fields, and the pack-check contribution identities and issue fields. The component.detail response type declares parentDoc. (#6588)
  • astryx template --help and the manifest now say which flags win when they are combined: --cdn overrides everything else and writes to its value, else to <path>, else cdn.template.html; --list ignores a name, a path, --skeleton and --overwrite; and --skeleton needs a name and writes nothing. (#6591)
  • astryx upgrade --help and the manifest now state how its flags combine: --list ignores every other flag, --registry refuses --list and the migration flags, and --codemod is the only way to run an optional codemod and also skips the ShadCN composition check. The --from help names the legacy @xds/core fallback, and the documented exit codes now include a missing core, a missing jscodeshift, an invalid astryx.config, a post-codemod hook failure and the refused flag combinations. (#6590)
  • astryx docs authoring now says where loading a doc is looser than its published type (stamped component and function docs, older generic docs, and templates), and which doc kinds accept fields they do not know. Nothing about how docs load has changed. (#6492)

Other Changes

  • start names the template to scaffold, with the template <id> --type page <path> command that selects it (an integration replacement through the Core id it replaces), a basis, a one-line reason, and alternatives: the next two templates. A page ranker built for the long ideas builders write ("ops dashboard with a KPI row, a sortable table and a trend chart") picks it. Every matched word counts, weighted by how rare it is among page templates. The words before the first "with", ":" or "," name the page's family, a container ("in a modal") names its frame, and a word that only modifies another ("product" in "product response") counts half. A family's base template (dashboard, settings) leads its family unless a variant's own words outweigh it. Family words come from the templates' own ids. basis is direct when the ranker's pick is also search's direct match, closest when it is not, and fallback when nothing has the evidence to lead and the page starts from the shell-top-nav app shell (blank when that is not available). A template that is not ready yet is never the start.

  • Search matches words more strictly. A term matches inside a name or keyword only at the start of one of its words ("input" finds TextInput, "file" no longer finds "profile"), plurals and stems count as the same word, and typo tolerance applies only to one-word lookups of words long enough that one edit rarely makes another word ("site" no longer matches "side", nor "cable" "table").

  • Blocks and components that matched only one description word of a multi-word idea are no longer offered.

  • The text output has four sections: TEMPLATE (with its reason and command), OTHER TEMPLATES, BLOCKS, and COMPONENTS (with the frame and foundation names). They replace RECOMMENDED START and PAGE TEMPLATES. Search's page matches (pages) move to one line, with their full entries under --verbose. Descriptions stop at their first sentence, and blocks and components show the top three, each with one shared command line. --verbose shows every block and component, with full descriptions, import paths, and match reasons. The recommended command always scaffolds (template <id> --type page <path>), where for most ideas it used to print a --skeleton or component AppShell.

    The build playbook, the generated agent docs, and the working-with-ai and layout guides now start every page from a template.

    Compatibility: the build.kit JSON only gains start. Every existing field keeps its shape and meaning. Which pages, blocks, and components are listed, and directMatch, change with the stricter matching, as ranking results do. The human-readable output is reorganized. Options, exit codes, and the build.help shape are unchanged, and build still writes nothing. The same matching changes the ranking of search results.

  • Docs reads go through one internal compiler. astryx docs (and docs()), astryx doctor and astryx search read compiled topic nodes instead of each loading, merging, translating and linking doc files on its own. Output is unchanged. (#6484)

@astryxdesign/build

Fixes

  • Keep PostCSS and Vite processing inside @astryxdesign/build's declared dependency boundary (#6605)
    Packed consumers no longer depend on workspace hoisting to find PostCSS helpers or CSS compatibility processors. The package now owns Autoprefixer, Browserslist, and Lightning CSS, and a clean isolated-install test exercises the published tarball's PostCSS helper and Vite output.
  • Make source builds fail on unsupported StyleX declarations and load shared StyleX output from every Vite HTML entry.
    Nested pseudo-elements and stylex.keyframes() now have production-build regression coverage, and the maintained capability registry tracks the installed StyleX version.

@astryxdesign/theme-butter

New Features

  • Ship a typed ThemeDoc descriptor beside each first-party theme source. (#6498)

@astryxdesign/theme-chocolate

New Features

  • Ship a typed ThemeDoc descriptor beside each first-party theme source. (#6498)

@astryxdesign/theme-gothic

New Features

  • Ship a typed ThemeDoc descriptor beside each first-party theme source. (#6498)

@astryxdesign/theme-matcha

New Features

  • Ship a typed ThemeDoc descriptor beside each first-party theme source. (#6498)

@astryxdesign/theme-neutral

New Features

  • Ship a typed ThemeDoc descriptor beside each first-party theme source. (#6498)

Documentation

  • Describe Neutral as Figtree typography and add the font-loading snippet; the README claimed system fonts while the theme declares Figtree. (#5991)

@astryxdesign/theme-stone

New Features

  • Ship a typed ThemeDoc descriptor beside each first-party theme source. (#6498)

@astryxdesign/theme-y2k

New Features

  • Ship a typed ThemeDoc descriptor beside each first-party theme source. (#6498)

Contributors

Thanks to everyone who contributed to this release:

@AKnassa @bhamodi @cixzhang @ejhammond @ernestt @fullstackhacker @harjothkhara @HelloOjasMutreja @humbertovirtudes @imdreamrunner @jiunshinn @josephfarina @korkt-kim @ksying @kyu-rong @light-merlin-dark @nynexman4464 @rubyycheung @vjeux

Full Changelog: v0.6.3...v0.6.4