🛠️ Release v1.5.0
Univer 1.5.0 adds dedicated chart sheets, stacked line charts, and error bars. Pro's free tier now covers more editing and local output workflows. This release also reduces memory use during formula calculation, adds controls for Sheets interactions and Board layout, and improves Office file compatibility.
Give a chart its own tab while keeping its source cells in regular worksheets in the same workbook. Move charts between sheets with undo and redo. With the Sheets Chart Facade loaded, use workbook.insertChartSheet(info, { name, index }) to set the tab name and zero-based position, and getPrimaryChart() to access the main chart. A chart sheet can also continuously fit its main chart to the viewport using zoomToFit. Older snapshots without this setting use fixed zoom. Chart sheets guide
Create stacked and 100% stacked line charts. The chart editor and the builder and live chart APIs also support per-series error bars for basic 2D column, bar, line, and scatter charts. Use setErrorBars() and clearErrorBars() to control direction, end caps, styling, and error amounts. Supported amounts include fixed values, percentages, standard deviation, standard error, and positive or negative values from custom fields in the chart's data source. Scatter charts support separate X and Y error bars. Error bars guide
Use planLayout() to arrange a diagram or adjust connector routes, endpoints, labels, and text fitting. Inspect the returned diagnostics before applying a ready plan with applyLayoutPlan(). Applying a plan is undoable, and the resulting shapes and connectors remain editable. Imported tree branches can be collapsed or expanded with setTreeBranchCollapsed(). Rendered analysis reports issues such as label overflow and text that does not fit, and screenshot bounds can account for rendered paths and labels. Board Facade reference
The new @univerjs-pro/boards-latex-ui package renders LaTeX in Board text. Register UniverBoardsLatexUIPlugin after the Boards UI plugin. Plugin reference
Load @univerjs-pro/slides-ui/facade to check presentation.isTextEditing() or call await presentation.endTextEditingAsync() before a custom save action. Save after it returns true. A return value of false means the draft could not be committed or IME composition is still in progress. Slides Facade reference
Set these options directly on UniverSheetsUIPlugin, or under sheets in UniverSheetsCorePreset.
| Option | Default | Behavior |
|---|---|---|
selectionAnimation |
true |
Animates the desktop selection outline when clicking between cells. The system's reduced-motion preference takes precedence. |
enableFillHandle |
true |
Shows the desktop fill handle. Fill commands and Facade APIs still work when it is hidden. |
warnBeforeDragOverwrite |
true |
Confirms before a dragged range overwrites existing cells. Move validation and undo/redo still apply when the prompt is disabled. |
FBoard.planLayout()acceptsgraph,routes,ports,labels, andtextmodes. Graph layout acceptselementIds,fixedElementIds, andfeedbackConnectorIdsfor scope, plusdirection,layerGap,itemGap,origin, andmaxNodesfor placement. It defaults to horizontal layout, a layer gap of 100, an item gap of 48, and a limit of 500 nodes. Route, port, and label passes can target specificconnectorIds. Graph and text layout acceptmaximumWidthGrowthto cap text-driven widening, with a range of 1–2 and a default of 2. Board layout optionsanalyzeRenderedLayout()acceptsmaximumAutomaticLabelDistancein Board units for checking automatic label placement. The limit is opt-in. Board models also exposeconnectionPositionModefor automatic or fixed connector endpoints,lineJumpfor crossing styles, andlayoutOccupancy: "frame"to allow routes through a container’s interior. UsesetContainerStyle()withtitleOffsetXto offset a container caption, or set a swimlane’sheaderPositiontoleftortop. Rendered layout analysis- Custom canvas floating DOM layers can set
IFloatDom.preventBrowserZoomto suppress browser zoom gestures while forwarding events.eventOptionsconfigures nativepassiveandcapturesettings per wheel or pointer event. Events without an override continue to use React forwarding. Floating DOM layer API
These changes require the corresponding native Exchange artifacts.
- Charts. Preserve dedicated chart sheets in XLSX and supported stacked line chart groupings in XLSX, DOCX, and PPTX.
- PivotTables. Preserve more saved report layouts, merged headers, shared caches, and slicer, timeline, and PivotChart connections. Editable imported reports are stored as PivotTable resources so a second static copy does not appear in worksheet cells. Applications reading those report values should load the snapshot in a compatible frontend and read the rendered report. Re-import the original XLSX if an older snapshot retained a static copy or lost merge information.
- Optional Excel refresh. Set
deferPivotCalculation: trueon a Sheet XLSX export through Exchange Node to let Excel calculate eligible PivotTables on refresh. It defaults tofalse, which exports static results as usual. The supported profile is a new, simple PivotTable with one unfiltered, unsorted text row field, one numeric SUM measure, and grand totals, sourced from a separate two-column worksheet range. The matching Office binding is required for this option. - DOCX equations. Supported legacy Equation Editor and desktop MathType equations import as editable formulas and export as native Word equations. Objects that cannot be converted are preserved as OLE attachments.
- XLSX fidelity and memory. Preserve saved external-link values during offline conversion, including files saved after a failed link refresh. Retain supported custom OOXML metadata, significant whitespace, and implicit intersection formulas. Large exports of plain cell values use less peak memory.
- Native free exports. With the updated adapters, supported XLSX, DOCX, PPTX, and PDF exports can continue with a license watermark after the monthly allowance of 10 successful watermark-free exports. Watermark decisions use the completed export count when a conversion starts, so concurrent conversions can produce additional unwatermarked files.
- Formulas. Reduce calculation memory in JavaScript and native engines and reuse work for repeated criteria and range lookups. Improve Excel compatibility for complex numbers,
ISOWEEKNUM,WEEKNUMwith type 21, andPV. - Sheets. Reuse more rendering work during scrolling, including on mobile. Keep open cell drafts through external updates and preserve whole-row and whole-column references through structural edits. Improve validation dropdowns for merged cells and preserve dropdown ranges when pasting. Prevent PivotTable expansion from overwriting occupied cells and reduce the work needed to check for those cells.
- Editing and accessibility. Improve keyboard access, menu dismissal, RTL separators, gradient stop editing, and IME confirmation in dialogs. Context menu buttons execute their configured
commandId, using the menu item ID when no override is specified. - Collaboration and history. Improve incremental Slides updates and selective undo, and keep Sheets notes attached to the correct cells through concurrent row and column operations. Version comparisons retain a visible sheet.
- Embeds and PDF. Improve embedded editor interactions on mobile and harden PDF tile and annotation rendering. Matching native Office and PDF bindings also fix Worker lifecycle issues.
The deprecated root Worker exports announced in 1.4.0 have been removed. Built-in Worker presets already use the dedicated entries. Update custom browser and Node.js Worker scripts using the following table.
| New entry point | API |
|---|---|
@univerjs/rpc/worker |
UniverRPCWorkerThreadPlugin |
@univerjs/rpc-node/worker |
UniverRPCNodeWorkerPlugin |
@univerjs/sheets-formula/worker |
UniverRemoteSheetsFormulaPlugin |
@univerjs/sheets-filter-ui/worker |
UniverSheetsFilterUIWorkerPlugin |
@univerjs/engine-render/worker |
DocumentLayoutSession |
@univerjs-pro/bases/worker |
UniverRemoteBasesPlugin |
@univerjs-pro/bases-dashboard/worker |
UniverRemoteBaseDashboardPlugin |
@univerjs-pro/sheets-pivot-chart/worker |
UniverSheetsPivotChartWorkerPlugin |
@univerjs/docs/worker |
Register UniverDocsWorkerPlugin in place of startDocsLayoutWorker. |
@univerjs-pro/pdfs-ui/worker |
Register UniverPdfsUIWorkerPlugin in place of startPdfPageThumbnailWorker. |
- Register
UniverBoardsLatexUIPluginexplicitly for Boards. ImportDocsLatexRenderServicefrom@univerjs-pro/docs-latex. Font injection is handled by the renderer, and theinjectRatexFontsexport has been removed. - Replace
IDrawingObjectListItem,IDrawingObjectListPanelLabels, andIDrawingObjectListPanelPropswithIObjectListPanelItem,IObjectListPanelLabels, andIObjectListPanelBasePropsfrom@univerjs/drawing-ui. - Remove direct imports of
CELL_INVERTED_INDEX_CACHEandFORMULA_REF_TO_ARRAY_CACHEfrom@univerjs/engine-formula. Cache ownership now belongs to the formula engine instance.
Upgrade the Univer and Univer Pro packages used by your application together. For self-hosted deployments, update collaboration, rendering, and native Exchange components before writing chart sheets or the new stacked line chart types. Older readers may misinterpret the new data. Use matching native binaries for the Office/PDF changes and the native formula engine.
v16.5.0-canary.50
- Enable turbopackMinifyBeforeChunking by default on canary releases: #101113
- Turbopack: write smaller/faster client reference manifests: #101312
- Generate managed agent instructions on next dev and respect opt-outs: #101746
- fix(next/image): check the LCP element loading state: #101737
- Run render error reporting outside the work-unit scope: #101597
- Fix RSC navigation and runtime prefetches in
next start: #101357 - Expose full-route RSC prerender classification to adapters: #101309
- Distinguish dynamic rendering from required prerendering: #101511
- turbopack: size chunk items by their minified code when minifying before chunking: #101010
- Prevent repeated prerenders during fallback shell revalidation: #101756
- Enable Turbopack GC by default: #101591
- Exit next dev when the server process is killed by a signal: #101752
- Store "use cache" durability once per entry in server reference manifest: #101674
- Remove
durabilityproperty fromcreateServerModuleMapresults: #101730 - Resolve webpack dev HMR test callback when a build has nothing to apply: #101682
- turbo-tasks: capture effects on multiple workers: #101514
- Turbopack: report routes nested inside catch-all segments instead of panicking in dev: #101697
- docs: standalone env file precedence and with-docker-multi-env secrets: #101728
- examples: bump Alpine Docker base images to Node 24: #101215
- docs: place shell param-order rule with build-time shells: #101680
- turbo-persistence: don't modify the directory on read-only open: #101779
- Optimize inline_atom: #101778
- docs: clarify standalone .env copying and the next-server tracing exclude key: #101699
- docs: Add more clarification about SVG security policies: #101311
- Add a 48-hour Cargo dependency cooldown: #101765
- Update Rust nightly to 2026-11-13: #101764
- Upgrade React from
df6aa4ec-20261112todf6aa4ec-20261112: #101760 - turbo-tasks-backend: store task edges in flat EdgeSets: #101489
- turbo-tasks-backend: add a profile driver to the keyed dependencies benchmark: #101731
- docs: correct deploymentId environment variable precedence: #101740
- Turbopack: list compact re-export namespace keys in code unit order: #101494
- turbo-tasks-backend: fix transient root test race: #101700
- Scaffold separate entrypoint for Turbopack NFT functionality: #94356
- Turbopack: read UnusedReferences by key in ESM export code generation: #101626
- Require real deployment coverage for application behavior: #101510
- Turbopack: read UnusedReferences by key in pattern mapping: #101725
- test: replace files atomically in patchFile: #101683
- turbo-persistence: faster key block sorting: #100335
- turbopack: return small free ranges to the OS after project shutdown: #101623
- Turbopack: return freed memory eagerly while shutting down the project: #101589
- turbo-persistence: validate sst_inspect block reads with BlockOffsets: #100313
Huge thanks to @lukesandberg, @icyJoseph, @aurorascharff, @bgw, @wbinnssmith, @unstubbable, @sokra, and @mischnic for helping!
Astryx v0.7.16
Astryx 0.7.16 — all @astryxdesign/* packages ship at this version.
OLD_VERSION=0.7.15 # replace with the Core version your project uses today
npm install @astryxdesign/core@0.7.16 @astryxdesign/cli@0.7.16 # plus every other @astryxdesign/* package you use, at 0.7.16
npx @astryxdesign/cli upgrade --from "$OLD_VERSION" --apply --path .
- Adds the English strings of the Lab media player (
@astryx.mediaPlayer.*): control names, spoken times, chapter and marker context, and previous and next track. They are Lab strings shipped in Core's locale file, as Lab ListInput's strings are; they add no public API to@astryxdesign/core. (#11438)
-
NumberInput takes
entryErrorBehavior, so a form can pass one value to all of its date, time, and number fields. A typed number is complete only at Enter or blur, so both values keep NumberInput's current timing: its rejection,aria-invalid, announcement, andonEntryErrorChangereport appear at Enter or blur, never while a person types. Setting the prop, or FormLayout'sdefaultEntryErrorBehavior, changes nothing a person sees in a NumberInput. Pagination's page box passes'complete', so a FormLayout around it never changes it. (#11492) -
DateInput, DateTimeInput, and TimeInput take
entryErrorBehavior, and FormLayout takesdefaultEntryErrorBehavior. The prop sets when a field shows its own entry error, such as a date the calendar does not have or a time outsideminandmax. It does not set when validation runs, and it is not React Aria'svalidationBehavior.'complete', the default, keeps each field's current timing.'blur-or-enter'holds the message,aria-invalid, the invalid styling, the announcement, and theonEntryErrorChangereport until the person presses Enter or leaves the field; an edit that makes the entry valid or empty still clears the error at once. A field's own prop wins over the form default, a nested FormLayout replaces it, and a Dialog opened from the form starts without it. PowerSearch value editors and Table filter editors keep'complete'. The newEntryErrorBehaviortype names the two values. (#11485) -
A Lightbox gallery's caption sits in a band two lines tall under every photo, so the photo keeps one size and place from item to item. A photo with no caption keeps the band's space without its backing. A longer caption shows two lines, the second ending in an inline "more" link, which opens the whole caption over the photo. The whole caption is always the viewer's accessible description. (#9810) The viewer is a media surface: its controls, caption and counter take the theme's dark surface tokens through
MediaTheme. The caption is bodyTextwhose lines start at one edge, "more" and "less" areLink's button form in the caption's own type, and the band clears the screen's bottom edge and its safe area. A gallery's caption sits on a backing that keeps it at 4.5:1 over any photo. A tap that opens the viewer paints no focus ring on its first control, and paging from a focused "more" or "less" keeps focus in the viewer. -
NumberInput:
onEntryErrorChangereports why the field rejects typed text, so an app can word its own message. It receives aNumberInputEntryErrorwith akindthat does not depend on the locale (unreadable,fractionalfor a fraction whenisIntegerOnly, oroutOfRangefor any number whileminis abovemax), the typedtext, and, forfractionalandoutOfRange, the number asvalue. It runs each time the rejection changes: at Enter or blur, when an edit makes the text valid or empty, when a newvalue,min,max, orisIntegerOnlychanges or ends it, and when disabling the field drops the text. It reportsnullwhen the rejection ends, or when the field unmounts while one stands, and never twice for the same rejection. Pass anerrorstatuswith a message to word the rejection; that message is the one announced. (#11415) -
New message
@astryx.richTextEditor.link.urlPlaceholder("example.com"), the placeholder of the RichText toolbar's link field. The descriptions of the other@astryx.richTextEditor.link.*messages and@astryx.richTextEditor.toolbar.linknow describe the link popover that replaces the link dialog; their messages are unchanged. (#8717) -
Column windowing keeps focus and place: with
useTableColumnsVirtualization, a focused cell whose column scrolls out of the window stays mounted with its original element until focus leaves the table, moves to another cell, or its column is removed, when focus returns to the Table's scroll area. Focus in a menu that a cell opens keeps the cell's column too. Changing column order, widths, visibility or pinning keeps the first visible column and how far it is scrolled. Tables that do not opt in are unchanged. (#11018) -
Tables can window their columns: pass
useTableColumnsVirtualization({initialViewportWidth})toTable.virtualization. Table keeps every column definition and width, mounts only the unpinned columns near the inline viewport plus two overscan columns per side, keeps pinned columns mounted, and gives mounted cells full-tablearia-colindexvalues witharia-colcounton the table. When the scroll area is at least as wide as the table's minimum width, every column mounts.Table.virtualizationalso accepts a list with at most one rows and one columns capability. Unsupported content or structure renders every column with a development warning. Adding or removing the columns capability remounts the table. Tables that do not opt in render as before; their Table import carries about 0.5 KB gzip of column windowing wiring, and the column planner loads only with the hook. (#11014) -
Add Table infinite loading with
useTableInfiniteScroll({hasMore, isLoading, onLoadMore}). One Promise request latch coordinates automatic and manual loading, failures pause automatic retry, and the persistent busy Button owns progress announcements and focus continuity. (#10570)Also released in
@astryxdesign/cli. -
Tables can window rows and columns together: pass both
useTableRowsVirtualizationanduseTableColumnsVirtualizationtoTable.virtualizationas a list. Table mounts only the cells near the viewport on both axes, keeps full-tablearia-rowindexandaria-colindexvalues, and keeps a focused cell's row and column mounted while either scrolls away. Tables that do not opt in are unchanged. (#11019) -
TextArea
htmlMaxLengthsets the nativemaxlengthattribute, so a form can cap typing in UTF-16 code units, including an SSR form before hydration. It is independent ofmaxLength, the soft counter, which still counts user-perceived characters and never blocks typing. TextArea adds no truncation or validation of its own, and renders no nativemaxlengthwhenhtmlMaxLengthis unset. (#11295) -
TimeInput:
onEntryErrorChangereports why the field rejects typed text, so an app can word its own message. It receives aTimeInputEntryErrorwith akindthat does not depend on the locale (unreadable, oroutOfRangefor a time beforeminor aftermax), the typedtext, and, foroutOfRange, the ISO timevalue. It runs each time the shown rejection changes, including when a newmin,max, orvalue, or disabling the field, changes it, never twice for the same rejection, and withnullwhen the rejection ends or the field unmounts while it stands. The native and bottom-sheet surfaces take no typed text, so they never report. (#11413) -
useTruncationtakesisExpanded: pass true while the text is shown in full; the hook keeps reporting whether it is cut off when clamped. A Lightbox caption uses it, so its "less" stays while the caption is open. (#9810)
-
AppShell keeps server-rendered content when a screen is narrower than the server assumed. On a phone, the shell learned its mobile layout in an urgent update right after it hydrated. When the content inside the shell had not hydrated yet, because its code streams in or loads later, React rendered that content again in place of its server HTML: a visitor lost scroll position and focus, and images inside loaded twice. The shell now takes the client's breakpoint answer in a transition, which waits for that content to hydrate. A client-rendered app and a correct
mobileNav.defaultIsMobilehint behave as before. No API changes. (#11441) -
BottomSheet syncs its focus return target at commit, and no longer copies its stack view during render. A sheet removed after a render React discards, as a suspended transition does, returns focus to its committed
finalFocusRef. (#10918) -
BottomSheet: when a touch flick closes a sheet, the dim behind it now clears in step with the sheet. Before, the sheet was gone in about a tenth of a second while the dim was still mostly there, and it kept fading for over half a second more. The fade now follows the close spring's own progress. Reduced motion and every other close are unchanged. (#11400)
-
BottomSheet keeps its snap points and its reactivated entrance in state, and syncs its drag's stop resolver at commit. A render React discards that changes
snapPoints, as a suspended transition can, no longer changes the stop a released drag rests at. (#10873) -
ChatComposerInput: an empty input keeps its one-line height and takes typing after a page restyle, for example after an open Dialog that holds a SegmentedControl unmounts. Before, its editable area could collapse to 0px, so a click landed beside it and typing did nothing until a reload. (#11428)
-
useContainerReveal keeps revealed content always visible only when the primary pointer is coarse (a phone or tablet). A touchscreen laptop used with a mouse or trackpad now reveals on hover and keyboard focus like any other mouse device. Previously any touchscreen kept the content visible at rest, so a Thumbnail's hover remove button, Markdown heading links, and every app's hover-revealed row actions never hid. (#11218)
-
A theme's padding written with
clamp(),calc(),min(),max()orvar()with a fallback now builds as written. The theme compiler splitspadding,paddingBlockandpaddingInlineon the spaces between values only, never on a space inside parentheses or quotes, sopadding: clamp(8px, 2vw, 16px)stays one value instead of becoming broken CSS. No API changes. (#11347) -
DateInput, DateTimeInput: a
valuethat the app passes at an Enter or blur that keeps a rejected date no longer erases it, so the entry meets WCAG 2.2 3.3.1 (Error Identification). Before, when an app setvaluein the same update as an Enter or blur that kept unreadable, incomplete, nonexistent, or out-of-range date text, as an app does when it restores the date it had before the edit, the field dropped the text, its message, andaria-invalid, and reportednullthroughonEntryErrorChange; a native date control lost its rejection message the same way. Now the text, its rejection, and its message stay at every such Enter or blur, a blur after an Enter included, so the person can correct the entry, and nothing is reported. A later newvaluereplaces the text, as before. No app code changes are needed. (#11470) -
DateInput, DateTimeInput: in a standalone field, rejected dates now meet WCAG 2.2 1.4.1 (Use of Color) and 3.3.1 (Error Identification); inside an InputGroup they set
aria-invalidand are announced. Before, text that is not a date was marked invalid and announced on every keystroke, with no visible message, and Enter or blur reverted it. A native date control flagged each partial year while the year was typed, showed no message, and dropped the rejection when it restored the committed date at blur. Now text that is not a date shows nothing while you type; Enter or blur keeps it in the field and shows "Invalid date" as the field's error status, witharia-invalidand one announcement. Its message replaces a callerstatus, as the field's other rejections already do; withonEntryErrorChange, a callererrorstatus with a message still wins. A native date control's rejection waits for Enter or blur, shows the out-of-range message a typed date shows, under the samestatusrules, and stays shown after the control restores the committed date at blur. A shown rejection stays through an edit that leaves the date wrong or unfinished, and clears once the date is valid or empty. What a form posts does not change: while kept text is not a date, a hidden input posts the committed date under the field'sname, and the next Enter submits the form. (#11407) -
SideNav, TopNav, Breadcrumbs: a Ctrl-, Cmd-, Shift- or Alt-click on a link in the mobile navigation drawer opens it the way the browser decides, a new tab for instance, and leaves the drawer open. A plain click still closes it, and so does a modified click that the row's own
onClickcancels, since nothing opens elsewhere. (#8979) (#9390) -
DropdownMenu syncs the refs its show and hide call at commit. After a render React discards, as a suspended transition does, its entrance under a finger holds input as the committed menu says. (#10905)
-
In forced-colors mode (Windows contrast themes),
primaryandsecondaryButtons gain aButtonTextborder, anddestructivekeeps itsCanvasTextborder; in a connectedButtonGroupthe solid buttons keep the group's dividers and close the group's outer edge, with no doubled seam. Floating surfaces that separate from the page with a box shadow alone gain aCanvasTextborder:Popoverand every surfaceusePopoverpaints (includingDropdownMenu),DropdownMenuSubMenuflyouts,ContextMenu, and anisInlineDialog. Forced colors removes fills and box shadows, which left these controls and surfaces without an edge. The borders exist only in forced colors, so nothing changes elsewhere. A theme that sets a solid variant's own border width keeps it, so Gothic'ssecondaryButton stays borderless. (#7677) -
A layer with several triggers now opens at another trigger when the press on that trigger is the one that closes it, with a mouse or a finger. A press on the open trigger still closes it. (#11337)
-
An anchored layer flush against its trigger, or over its edge, no longer slides over it: its entrance scales in place, as it does for a layer over its trigger's center. A menu at the pointer still slides from the side it lands on. (#11417)
-
An anchored layer that has room to its trigger no longer starts its entrance over it: its first step toward the trigger stops at that room. So a tall tooltip 4 px beside its trigger no longer covers it on its first frame in Safari. (#11396)
-
The layer runtime syncs its close-start and replayed-show refs at commit, not during render, and its keep-open press reads the committed open state or the shown layer. A render React discards no longer leaves its values for later callbacks. (#10912)
-
A light-dismiss layer opened while a press is released no longer closes at that release. A DropdownMenu or ContextMenu row that opens a Popover when chosen with a mouse now leaves it open, as it already did with a finger; the next press outside, or Escape, closes it as before. (#11258)
-
A mouse click on the dark space beside a Lightbox photo now closes the viewer. Before, the viewer stayed open after a click there within the photo's natural width, or within a wider caption's width. (#11354)
-
A single photo's caption in a
Lightboxnow shows as a gallery's does. It sits in the band at the bottom of the viewer, on the caption's backing, which keeps 4.5:1 over any page behind it. The photo centres above it. A caption longer than two lines ends in "more", which opens it in full over the photo. A single photo with no caption shows no band. (#10284) -
When a reorderable
Listrow does not render again, its handle's name still follows it. A handle named after its place ("Reorder item N") follows that place when rows above it go or the rows reorder. A label that gains or loses its text renames the handle. (#10815) -
DropdownMenu, ContextMenu, Selector, TabList, TopNav, SideNav, Breadcrumbs: when a finger picks a menu row, the click guard cancels only that finger's
touchendonce the menu has named the touch, so another finger'stouchendpasses. The menu names the touch from thetouchstartthat follows itspointerdown. AtouchendwithoutchangedTouches, or a lift whose touch the menu could not name, keeps the old rule: the nexttouchendis cancelled. (#11164) -
MoreMenu, InfoTip, PowerSearch (with its PowerSearchToken and PowerSearchFilterEditor defaults), Toast, and InputClearButton now pass host
data-*attributes to their own element, as other components do: the trigger button for MoreMenu and InfoTip, the primary control for PowerSearch, the token and editor root for the PowerSearch defaults, the toast element for Toast, and the button for InputClearButton. Component-owned attributes stay with the component. A new test renders every exported component that renders an element of its own with adata-*attribute, and fails when the attribute does not reach the DOM. (#11406) -
NumberInput: a rejected entry now shows a message and stays in the field, so the field meets WCAG 2.2 1.4.1 (Use of Color) and 3.3.1 (Error Identification). Before, every draft that was not an accepted number, an unfinished one such as
-included, setaria-invalidand was announced on each keystroke, while the text only turned secondary; blur then reverted a draft that did not parse, with no message. Now nothing is marked or announced while a person types, because a typed number is complete only at Enter or blur. At Enter or blur, text that is not a number, a fraction whenisIntegerOnly, or any number whileminis abovemaxstays in the field: the field shows "Invalid number" as its error status, tied to the input byaria-describedby, setsaria-invalid, and announces it once. Inside an InputGroup the rejection setsaria-invalidand is announced. An edit that makes the draft valid or empty clears it at once. A number outside an ordinary range still clamps and commits throughonChange. A callererrorstatus with a message still shows in place of the field's message; awarningorsuccessstatus no longer hides a rejection. What a form posts does not change: while blur keeps a rejected draft, a hidden input posts the committed number as the field showed it before. Pagination'sinputvariant shows the message on its page box's status icon and widens the box beside the typed text while a rejected entry stays, so the bar keeps its height. No app code changes are needed. (#11415) -
Under a finger, a Popover that overlaps its trigger now grows from where it meets the trigger, as the pointer entrance does. A popover a few pixels over its trigger's side grows from that side, not from below. A popover over its trigger's center grows from that center by scale only. Every other landing grows as before. (#10949)
-
Toast announces the rendered message when a body's text comes only from a component, and from custom layouts (#7853) (#11175)
-
SegmentedControl: where the control has no layout, as in a unit test's DOM, a click on a later segment keeps that segment selected instead of switching to the first segment a moment later. Browsers are unaffected. (#11487)
-
Selector's presentation syncs its show, hide and popover-content refs at commit. After a render React discards, as a suspended transition does, opening the popover runs the committed show. (#10917)
-
Slider syncs its values, change-end callback and touch update refs at commit. After a render React discards, as a suspended transition does, a press on the track reports the committed value to the committed
onChangeEnd, and a finger dragging a thumb reports to the committedonChange. (#10913) -
Table:
useTableSortableStatecalls theonSortChangeof the last committed render. Before, a sort click during a transition that had not committed yet called the callback from that pending render. (#11433) -
TimeInput, DateTimeInput: a rejected typed time now meets WCAG 2.2 1.4.1 (Use of Color) and 3.3.1 (Error Identification). Before, every draft that was not an accepted time, an unfinished one such as
1included, was marked invalid and announced on each keystroke, shown only as secondary-color text, and blur reverted it with no message; a native time edit outsideminormaxshowed no message and its rejection cleared at blur. Now an unfinished time is never flagged: a time outsideminormaxshows once it is complete (its hours, both minute digits, both second digits when the field shows seconds, and in 12-hour text its day period unless the hour reads 0 or 13–23), and text that is not a time shows at Enter or blur. The rejected text stays in the field, and a standalone field shows the "Invalid time" message as its error status, witharia-invalidand one announcement; inside an InputGroup the rejection setsaria-invalidand is announced. Enter in TimeInput and in DateTimeInput's time field commits the typed time and still submits a surrounding form; in DateTimeInput's open preset-time list, Enter picks a time as before. A native time control restores the committed time at blur and keeps its rejection shown. A callererrorstatus with a message still shows over the field's message, and the rejection is announced once; awarningorsuccessstatus no longer hides a rejection. In DateTimeInput, a date rejection's message comes first and a time rejection under it is announced. What a form posts does not change: while blur keeps a rejected TimeInput draft, a hidden input carries the field'snamewith the committed time. (#11413) -
TimeInputandDateTimeInput: a bound with no whole minute inside the range keeps its own seconds, so the clamp stays inside the range. Before,clampTime('12:00','23:59:30')answered23:59— below the minimum the caller set. In aTimeInputsheet a wheel rest past such a bound committed nothing; in aDateTimeInputsheet, changing the date onto such a bound committed the out-of-range23:59. (#10159) (#10159) -
Tooltip's and HoverCard's touch trigger, and Tooltip's handlers with the controlled and enabled flags they carry, sync at commit. After a render React discards, as a suspended transition does, a tap answers with the committed props, and a press outside still closes a shown tooltip. (#10892)
- Markdown text transforms that declare
requiredSubstringslook for those substrings in one pass over the source. Before, each paragraph searched the rest of the document again, so long Markdown (such as a long streamed chat answer) rendered more slowly with each paragraph. Output is unchanged. (#11403)
-
The docs playgrounds for TextInput, TextArea, NumberInput, CheckboxInput, Switch, ProgressBar and StatusDot open with realistic values instead of prop-name placeholders, the Heading playground opens as a level-1 heading instead of inheriting Text's sample with
type: 'body', and the ButtonGroup, TabList, SegmentedControl and TopNav playgrounds open with named sample items instead of numbered ones. (#11351) -
Hook docs for
useOutlineFromDOM, soastryx hook useOutlineFromDOMandastryx searchreturn it, and auseOutlineFromDOM — Articleexample block: an article and an Outline built from its headings through the hook, with a button that adds a section and its linked entry. (#11234)Also released in
@astryxdesign/cli. -
ListItem's
startContentandendContentdocs now say that the content sits inside the row, before the label or after the label and description, not before or after the item. (#11336) -
MetadataList's guidance now says that assistive technology reads each label with its value, and that the title does not name the list or add a heading. Its prop docs now list
classNameandstyle, and thelabeldocs say thatposition: 'start'places a label at the inline start of its value, not on the left. (#11391)Also released in
@astryxdesign/cli.
RichTextEditorToolbar's Link button, and Cmd/Ctrl+K in the editor, open a popover anchored to the button instead of a modal dialog. The popover holds one URL field, applied with Enter, and Apply, Remove (when the selection is in a link), and Cancel icon buttons with tooltips. It is not modal: Escape or Cancel closes it and returns focus and the selection to the editor, and a URL that cannot be linked shows an error under the field, which keeps focus. The URL rules (sanitizeUrl),hasNewTabLinks, andpromptForUrlare unchanged; withpromptForUrlthe Link button opens no popover, as it opened no dialog. (#8717) The popover opens above the toolbar and flips below it only when there is no room above. Apply and Cancel use the sharedcheckandcloseicons, so a theme that restyles those icons restyles them too; Remove's glyph is fixed artwork.RICHTEXT_ICON_KEYSis unchanged.
-
Pasting into a
RichTextEditortable no longer drops the paste or empties cells. A pasted table fills the cells one by one from the caret's cell, or within the selected cells. A merged cell in it fills only the cell where it starts instead of emptying the cells it covered, and a table whose rows differ in length arrives whole. A table pasted together with other content, as a web page copies it, goes into the cell as text instead of being discarded. (#8577) -
A link typed without a scheme, whose host has a dot or is
localhost, followed by a port, such asexample.com:8080,www.example.com:8080/docsorlocalhost:3000, now gets the defaulthttps://likeexample.comdoes, instead of being read as an unknown scheme and turned intoabout:blank(#11042)
-
astryx experimental theme editor: a new accent or neutral color keeps the base theme's own secondary text shade on selected items and rows, a dropzone, numbered citations and segmented-control labels while that shade still reads at 4.5:1; it moves only when it no longer does, and the note says it was kept readable. A write that repeats what the base theme already shows is no longer listed as a styled change. (#11468) -
astryx experimental theme editor: the app copy insrc/themes/<theme>/is written exactly astheme buildof its source writes it (the build header names that source), sotheme build --checkandastryx doctorread it as current, and a stable rebuild of it leaves it the editor's (the next change writes it again). A change that fails after its build no longer leaves the refused build in the app copy: the copy is written only once the change is saved. (#11468) -
astryx experimental theme editor show: a show that is refused (or fails to render) leaves the app copy insrc/themes/<theme>/as it was; the copy is written once show succeeds. System files such as .DS_Store in the copy folder no longer stop the copy; a file of yours there does, andwiringthen gives the steps by hand, naming that file. (#11468) -
astryx experimental theme editor: CSS read with the current specifications' grammar. (#11378)- The editor also reads the current specifications' grammar from
@csstools/css-syntax-patches-for-csstree(a new dependency, pinned). It adds about 0.1 MB unpacked to the install (one 79 KB JSON file, no dependencies of its own), and loads only when the editor checks a CSS value; no other command loads it, and startup is unchanged. A property MDN marks obsolete but the specifications still define, as browsers ship it, is taken:font-stretch, so the font shorthand split names a command that works. - A negative number the current grammar ranges at 0 or more is refused on a part (
line-height -1,gap -2px), with the positive value as the suggestion. - A
color-mix()share outside 0% to 100% is refused for tokens, part colors and the color dials, and the mix with the share at its limit is suggested. - A font size or weight given alone to
fonton a part (small,1rem,700) is suggested asfont-sizeorfont-weight, never as a family. - A relative color that keeps its channels (
rgb(from red r g b)) andlight-dark()with the same color twice are read as that color. Acolor-mix()in a space the editor does not mix in, or a relative color that changes its channels, says so and suggests the nearest color it can work out.
- The editor also reads the current specifications' grammar from
-
astryx experimental theme editor: more refusals and notes that are true. (#11378)- A negative percentage the current CSS grammar ranges at 0 or more is refused on a part (
line-height -50%,gap -10%). - A
color-mix()share may be written as any CSS percentage (5e1%,+50%,calc(50%)). - Line-height tokens (
--text-*-leading) take a number with no unit, as Core multiplies them by a text size;1.5emand150%suggest1.5. headingsin other letters or as a weight number suggests the word meant (BOLDis bold), and a weight number the nearest weight rung (800and900are heavy), never a lighter one.- A whole-theme font under fonts set for body and headings says which role fonts it sits under and how to clear them.
- A font one of the editor's own presets leads with (Comic Sans MS) gets no "must load" note.
text-size moreorlessat the end of the dial's range suggests the base font size token, never a size the dial refuses.- A CSS font value on a part made of keywords (
italic bold,bold 1rem) is split into its longhands, never a family; after the size, the rest is the family, as CSS reads it (1rem boldis size 1rem with the family bold).
- A negative percentage the current CSS grammar ranges at 0 or more is refused on a part (
-
astryx experimental theme editor: number tokens read as CSS reads them. (#11378)- Only the tokens Core's stylesheet multiplies by a size inside calc() (the line heights) refuse a unit; weight tokens take the font-weight keywords again (
bold,normal), so a theme saved with one stays editable. - Numbers are read as the browser reads them:
1.5e0,+1.5,15E-1and1.5EMare numbers, and acolor-mix()share like50.%is refused. - A CSS font value on a part with a size and line height (
1rem/1.5) or a size written as math (calc(1rem + 2px)) is split into its longhands; a part named twice (bold bold) is refused, never written as a family.
- Only the tokens Core's stylesheet multiplies by a size inside calc() (the line heights) refuse a unit; weight tokens take the font-weight keywords again (
-
astryx experimental theme editor: weights on phones, and values read as CSS reads them. (#11419)- Heading and text weights (the
headingsweight dial,--text-*-weighttokens) now apply on phones too: they are written into the phone rules the base theme sets, as font families are. - A token set to
var(--x, <fallback>)checks the fallback as the token's own kind. - Size tokens and color channels read numbers as CSS does:
1.pxandrgb(255. 0 0)are refused (with1pxsuggested where that is all), and+4pxand4e+1pxare taken. vibrancysuggests the value meant (MOREis more,70%and7e1are 70), and no value when the theme has no color to saturate.- The
headingsresult lists its weights in words, not[object Object]. - The whole-theme and role
fonttake a family alone: a size, a weight or a whole CSS font value is refused with the family, and the base size or headings command where it applies.
- Heading and text weights (the
-
astryx experimental theme editor set: when a part does not read the token named, the suggestions name only tokens of the same kind (a weight goes to a weight token, never to a size or a line height). Avar()fallback is checked wherever it sits (insidecalc(),max(),color-mix()), and an empty fallback on a typed token is refused. A size token given a bare number suggests that number in px (16→16px), andtext-size 20px,2e1,+20and1.25remsuggesttext-size 20. (#11419) -
astryx experimental theme editor: fallbacks, saved font dials and size suggestions. (#11419)- A part value's
var()fallbacks are checked against the property (padding 'var(--nope, red)'is refused), as token fallbacks are. - A saved
fontdial thatsetwould refuse (a CSS keyword such asinherit, a size, a whole CSS font value) is reported with the commands that fix it, and never built. - A size one slip from a valid one suggests that size:
16 pxand16pxxsuggest16px,1,5remsuggests1.5rem.text-sizesuggests the nearest size it takes:20 pxand125%are 20, and a size past either end is that end.
- A part value's
-
astryx experimental theme editor:font mathis accepted as the CSS generic family it is; the generic families are read from the CSS grammar. With an exact--font-size-baseset,how 'bigger text'steps that token (14px to 17.5px) instead of offering atext-sizethe token would mask. A color dial edited by hand to a see-through, system or variable color (#2563ebcc,transparent,Canvas,var(--x)) is refused by name with the undo that removes it, instead of building a gray accent. A value typed with full-width characters, a trailing dot or a CSS comment (18,18.,#2563eb,#2563eb /* brand */) is suggested as the value meant.setrefuses avar()loop that closes through a token in the theme's own<theme>.theme.ts.shownotes each token the theme's own source sets that a whole-theme dial also writes. (#11419) -
astryx experimental theme editor: suggestions keep the value meant, and fractional text sizes say how they build. (#11419)- An exact token refused only for how its value was typed (a trailing
;, a comment, quotes around it, full-width#) suggests that same value, not a stock one. - A font name that needs quotes (
Font Awesome 6 Free) on a font token or a part'sfont-familyis suggested quoted, as the font dial writes it. - A fractional
text-size(15.5) is saved as the whole pixel the type scale builds, and the result says so.
- An exact token refused only for how its value was typed (a trailing
-
astryx experimental theme editor how: mode phrases, questions about what is set, the focus ring, and changes by an amount. (#11378)- "The same accent in light and dark" sets both modes, and a trailing "and dark mode only" still makes the theme dark-only.
- A step that cannot move (badges already fully round) says so, with no command that would be refused.
- Questions about what is set now or the choices ("how round are the cards now?", "what are the available corner values") get
showand the lookup, never a change. - "Remove the focus outline" sets its width to 0 and says what that costs; "outline offset" and "focus ring offset" set
--focus-outline-offset. - "Decrease the radius by 2px" moves the corners from the theme's own radius now (its corners setting applied); a change by an amount on a part points to its current value instead of setting the amount.
- "Caret color" sets
caret-color; placeholder text and the selection highlight say they have no setting. - Screen-size, print and device conditions ("below 600px", "on tablets", "the print version") say the editor has no such conditions, instead of reading "600px" as a radius or "tablets" as tables.
- "Text less black" lightens the text.
- Each item of a list keeps its own values: "headings 32px bold, body 16px regular, captions 12px" sets the headings and the body as asked, with no contradiction, and "captions" (no part) takes nothing from its neighbors.
-
astryx experimental theme editor how: a negated mode, the CSS cursor, device words beside an ask, and labels. (#11378)- "Don't touch dark mode, make the accent orange" changes light mode only.
cursor: pointersets the CSS cursor; only "caret" or "cursor color" is the text cursor's color.- A device or print word ("cramped on mobile", "when printing") no longer drops the ask: the rest is answered, with a note that the editor has no such conditions.
- A question about one part's state or group ("which parts are in the inputs group", "how round are the cards now?") gets that part's lookup as well as
show. - "Not so round" is one step less; "avoid pure black text" picks a near-black; "flat cards, not flat buttons" changes only the cards.
NaNpxandInfinitypxare refused as not numbers;1e3pxis 1000px.- Labels are the field label part (a checkbox's or switch's own label when named); "visited links" says a link has no visited state set can name.
- Tailwind
border-2is a 2px border width androunded-fulla pill;.btn-primary { … }is the primary button only. - "Remove all my changes" takes back every setting; a question in a script how cannot read gets no guess.
-
astryx experimental theme editor how: "2px rounder" is a change from the corners now, and "all corners like the buttons" copies the buttons' radius. (#11378)- An amount with a comparative ("2px rounder", "2px sharper", "8px larger") is a change from the value now: the theme's corners move from its own current radius (its settings applied, not the base theme's), and a part's corners move from the radius token it reads. Anywhere else ("4px wider") how says set takes the new value instead of setting the amount.
- "All corners like the buttons" sets every other radius token to the one buttons read.
-
astryx experimental theme editor how: a negated dark mode, undo of what is set, typed times and transforms, and the right targets. (#11419)- "Except in dark mode", "but not in dark mode" and "leave dark mode alone" change light mode only; "not pure black" is a near-black and "not pure white" a near-white.
- "Undo the corners" on a theme that sets no corners says there is nothing to undo.
- "animation-duration 0s" is 0ms; "translateY(-2px) on hover" is a transform on hover; "shrink headings by 20%" says set takes the new value; "fade in tooltips" says the tooltip has no motion of its own.
- "Why are my buttons black" is a lookup, never a change; "is my theme valid", "how many dials" and "list every change" get the command that answers them.
- "radius sm/lg", "setRadius(4)" and "½rem" are read as values; "#22c55e in light, #4ade80 in dark" sets the accent for each mode; two-word CSS color names ("midnight blue") are the CSS color.
- "Text on the accent is hard to read" sets the on-accent text color that reads in each mode; "5 lines tall text box" sets the text area's height in lines; "dropdown shadow" is the dropdown's menu; "checkboxes green" is the checked fill.
-
astryx experimental theme editor how: an amount in rem or em with a comparative ("0.5rem rounder") is a change from the value now, counted at a 16px root, and "tighter", "roomier", "looser", "bolder" and "heavier" read as comparatives too. Where how cannot work out the new value, it says to set the value now plus or minus the amount. (#11419) -
astryx experimental theme editor how: a confidence gate, and dislikes, take-backs and dark-mode selectors read. (#11419)- When a question is a question ("is the card too round?"), says "tell me" or "but not too", or asks to "stop" something, how gives the lookup and asks for the change in words instead of guessing a write; "explain X" is the lookup of X.
- "I hate the shadows", "skip …" and "not a fan of …" ask for none of it when that is the whole ask.
- A change on one member of a group ("the text area") carries
--only, so it does not reach the whole group. - "3 pixels sharper", "take 2px off" and "remove 4px from" move the value down from where it is.
- "darkmode",
html.dark .card {…}and@media (prefers-color-scheme: dark) {…}scope the change to dark mode. - "Take back the shadow change" and "shadows like before" undo only what the theme sets, else say there is nothing to take back.
- "Pointer cursor on hover" sets the cursor; "opened accordion title" is the open state; "squre" and "eckiger" are square; "cards look flat" adds depth; "darker while being clicked" is the pressed state.
-
astryx experimental theme editor how: every change by an amount or a direction starts from the theme itself, not the base theme. A darker or lighter color starts from the theme's own color in each mode, and says when it is already as dark or as light as it goes; a bigger text size on a part starts from the size the theme set there; a part's corners moved by an amount start from the radius the theme set on it. Token lookups show the theme's own value. (#11419) -
astryx experimental theme editor how: the confidence gate works per clause, and a one-mode change never removes a mode. (#11419)- A clause that is a question, a "tell me", a "stop …" or a "don't touch …" holds back only itself: "stop making the buttons so round, make them square" makes them square, "don't explain, just set the accent to crimson" sets crimson, "don't touch spacing, just make text larger" changes only the text. A whole question ("can the cards be rounder?", "are buttons too round?!") gets the lookup; "can you make …" is an ask.
- Questions without a question mark ("are my corners too round", "does it use a shadow on cards") are lookups; suggestions ("why not square buttons", "what about a teal accent", "is there a way to …") are asks. Sarcasm ("yeah, because …", "just what I wanted", "…, nice") gets no write.
- "Enough with X", "quit with X", "nobody asked for X", "who needs X", "loathe X", "X 👎" and "❌ X" ask for none of X; "I hate it" after a description leaves the description as the ask.
- "Too airy" is tighter, "headings too thin" bolder, "not bright enough" brighter.
- "Keep the dark mode accent, change only light to violet" sets the light accent; a light-only or dark-only theme only when the ask is about the theme's modes.
- "Links not underlined until hover" removes the underline and adds it on hover.
-
astryx experimental theme editor how: "bolder" or "a bit lighter" headings move one rung from the headings rung the theme sets (and say so when there is no rung further), and "a bit bigger" text moves a numeric text size by the dial's own step.how <theme> <question>takes a first word that names an existing theme as the theme, asset,showandundodo. (#11419) -
astryx experimental theme editor how: a write only for a direct ask. A clause that is a condition ("if the corners are round, …", "unless …"), an indirect question ("I wonder if …", "is 4px a good radius", "would #333 work", "should corners be 6px or 8px"), a double negative ("don't not round …") or a check ("can you check if …", "just checking: …") gets the lookup and a request to say it as an ask, not a guessed change. Politeness ("if you can, …") is not a condition. "Can the buttons be rounder?" and "can I get a serif font?" are asks; a question followed by an ask ("whats the padding on cards make it 12", "do badges have borders? remove them either way") answers the ask, with "it" or "them" read as what the question named; "dark page #0b0b0f" sets the dark page. (#11419) -
astryx experimental theme editor how: "more muted", "pastel", "duller" and "subdued" lower vibrancy and "less muted" raises it (a token name such as--color-background-mutedis never read as the word); "less light" or "less thin" headings are one rung bolder, "less heavy" one rung lighter, "less tight" wider letter spacing; a weight question lists only the weight tokens a part reads; "too dark" asks for lighter and "too light" for darker; "no big gaps" is less spacing; sarcasm anywhere in a question ("great, even more shadows, just what I wanted", "…, nice") holds back every clause. (#11419) -
astryx experimental theme editor how: a write for a clause that reads as an ask: an imperative, a thing with a value, a style word with a part, a wish about a named thing, or a description of the look wanted ("the page is white"). A tag question ("cards have a shadow, right?"), a hypothetical ("imagine …", "consider …"), a condition ("whenever …", "once …", "should the cards …"), sarcasm or a double negative gets the lookup instead. "nuke", "zero out", "sharpen", "flatten",X++/X--andX: offsay which way to go; "dark.page=#…" and "dark mode only: accent #…" set dark mode. (#11419) -
astryx experimental theme editor how: "show me …" and "give me …" are lookups. "decrease", "shrink", "trim", "minimize" and "scale down" mean less. "Only when dark mode is on" and "except in dark mode" keep their mode. Removal words in other languages and 🚫 remove; "X are heavy" lightens; "don't touch the buttons" and "leave the corners" never write the excluded part. Inline list numbers ("1. … 2. …") are breaks, a color asked for a part in a long note stays with that part, and "tooltips darker" stays a color. (#11419) -
astryx experimental theme: the thirteenth pass of fixes. (#11378)undoleaves out what this Core refuses in the step it goes back to. A saved setting that names no design token or CSS property, or a property with no part, is reported.showbuilds an own change to<name>.theme.ts, and names a theme source that no longer builds. Its colors say where the theme source wins over the generated shade.- A setting covered by a later one that spreads over its part is named in
setandshow. corners 2e1pxand+4pxare sizes. A state is refused for padding on a part whose padding Core sets from the plain value only.--state disabledalso matches a wrapper whose input inside is the one disabled.how <part>lists the statessettakes there.newsuggestions keep every option given;--liketwice is refused. More suggestions run as printed in JSON. The merge snippet's paths start from the generated themes module's folder. The help namesexperimental theme compileto build an editor theme again.
-
astryx experimental theme editor: corners set for a state (corners pill --on button --state hover) are accepted, and the result names the part's derived radius variable, which Core sets from the plain value only. Padding that a part expands into container spacing is refused with a state, judged on the part named (--onlykeeps it to one member of a group).focus-withinis taken on a part that holds content. A one-mode value that is not a color is refused for that reason first. (#11378)showleaves every file in place when the theme source does not build, and names a token value that is not text. A theme named by a path or in capitals (use ./themes/t,use T) gets the editor-theme answer. -
astryx experimental theme:showbuilds a change in a module the theme source imports, and an unreadable build with a broken source line says what to put back.compileof an editor theme from a subfolder names the run from the app root.--state disabledreaches the inner control only on form controls styled on a wrapper, never a whole button group. A step back writes the same color files. (#11378) -
astryx experimental theme editor: every suggestion a refusal gives runs and does what it says.undo t Cornersreads the setting name in any case, and a near name (undo t corner) suggestsundo t corners, never a bareundo tthat would step back another change. A hand-edited setting suggests a value to set only for a token whose value has the wrong type. A flag given twice suggests its first value that the flag takes (--like neutral,--corners small,--section actions, and for a color dial the first that is a color:--accent banana --accent redsuggests--accent red), or drops it. A folder of your own files suggests a free, valid theme name (u-2). After damaged or lost settings, the note saysundosteps back to the settings from before the last change, and that the last change itself is not kept. A correctednewkeeps every other option given, with--likein another case read as the built-in theme it names. (#11378) -
astryx experimental theme editor:--state disabledreaches the control inside only on a part that holds one control (a typeahead), never a radio list, input group or field.corners none,sharp,zeroand0%are square;spacing nonenames the tightest rung. Undoing a part's state setting names the group's setting for the same state.focus-withinis taken on a part that holds content in a prop (a chat composer's input) or children (a popover). (#11419) A token in your own theme source that is not text, alone or in a pair, is refused before anything is written, with an example of the token's own type. A helper imported as./x.jscounts when onlyx.tsexists. Asetrefused for your own file suggests reading it. The compile help lists the editor-theme refusals. -
astryx experimental theme editor: an invalid theme name suggests a namenewtakes (my-neutralfor a built-in theme's name, a free numbered name for a taken folder), and says "a theme here already" only for the same name in another case.newon an existing theme suggestsset <theme> base <the --like given>and a free name with the same options. A setting listed twice by hand names both values, and setting it to one value or undoing it fixes the list, without stepping back another change. With the built module missing,setandundosay the token changes are not listed, and never that the change shows nothing. (#11419) -
astryx experimental theme editor showfollows every local file the theme source reads, as the build resolves it (./toktotok/index.tsx,./c.cjstoc.cts,./j.jsxtoj.tsx), including your own files in the theme'stokens/folder; only the editor's generated palette files there are left to their own stamps. A pair of values in quotes such as['600', '700']is no longer called "not text"; a size given as a number without a unit is named, with an example that passes, and a{light, dark}object is named instead of a raw error.experimental theme templatein a folder the theme editor made guards only the editor's own files: an earlier template there is replaced with--overwrite, and for an editor file the skip names a free path instead of--overwrite.undorefuses an--onlythe setting was not made with, matches a value whatever its comma spacing (rgb(255,0,0)), andundo <theme> mode/basesays when the history does not reach back to how the theme was made. A build that fails puts the files back and says nothing was changed, andshowno longer says it built a theme its source cannot carry. (#11419) -
astryx experimental theme editor: a bare number on a length (padding 4) suggests4pxwith every flag kept, never the property's reset value. A part named by its class (.astryx-card) or its component (<Card>) suggests the part. What the buttons and surfaces dials write is read from the build, so an exact property under them is named insetandshow, and undoing it names the dial that applies. An unknown state suggests the one meant (pressedis active), or how to see the part's states. After a change and its undo, the sheet no longer says it is out of date. (#11419) -
astryx experimental theme editor show: a light/dark pair with a missing half is named even when the other half holds commas (rgb(0, 0, 0)). A helper the theme source reaches withrequire()orimport x = require()counts as part of the source. A stylesheet or module that another theme's build wrote (compile other.theme.ts --out themes/g/g.css) is built again from the editor's settings. (#11419) A number given to a part property gets px on each bare number (4 8px,8 / 4,4 px), within the property's range, and never falls back to its reset value.--on checkboxesnames the checks group ashowdoes.corners sharperandspacing roomierstep from the value now. A state in another spelling (focusVisible,focus_visible) is that state.When
themesin the app is a file (or a link to one), every editor command refuses, saying to rename or move it, instead of failing with a raw error. -
astryx experimental theme: template overwrite on any disk, and undo answers that are true. (#11419)experimental theme template … --overwritenever replaces a theme editor file under another spelling of its name (themes/t/T.theme.tson a case-insensitive disk, or through a link).undo <theme> modeorbaseon a theme with no saved change says so, instead of claiming its history does not reach back; after every step is undone it says the value was never changed.undo <theme> headings <word>with only the other half set names the half that is set.- A mistyped
basesuggests only a base this project has installed, and says when the name is a theme made here (which cannot be a base). undo <thing> <value>matches the saved value in either quote style and with any spacing around/.
-
astryx experimental theme editor: a mistyped--likeorbasesuggests only a base the app has installed, and with none near,base noneand the installed bases.undo <theme> baseormodeafter a change and its reverse says it is back at the value the theme was made with.newrefuses an option it does not take with its help, like the other editor commands. A theme folder copied or renamed by hand names the files to rename. A padding (or other) shorthand set after its sides on a part removes them, as its note says, andundobrings them back. A hand-edited part property value its CSS grammar refuses is named, like a dial's.showbuilds again when an output is missing or was edited by hand, ascompile --checkfinds them. (#11419) When your own<theme>.theme.tsdoes not parse,set,undoandshowrefuse before writing or removing anything (the palette files undertokens/stay), withcatof the file; a failed change that removed thetokens/folder puts it back. A build temp file left by a command stopped mid-build is swept after ten minutes. -
astryx experimental theme editor showrenders its preview pages again only when what they show changed (the CLI, Core, the page or the theme's build): a show with nothing new takes about a second. A built output that cannot be read, or a folder in its place, is refused by name with a command to inspect it, inshowandcompilealike. Every file the editor's build writes counts when show checks the build, the generated color references too, and a hand edit inside the generated settings module is built over from the settings (andcompilerefuses it). A check of the outputs that cannot run is no longer read as a match. A theme busy with another command suggests this same command again, once the other ends. (#11419) -
astryx experimental theme editor: a theme file that is a folder or cannot be read (its settings, source, stylesheet, module or undo history) is named with what is wrong andls -la <file>, instead of a crash or "damaged"/"not valid JSON"; no message suggests deleting a file (a damaged history is moved out of the theme folder instead)../tand.\tname the themet. A mode or base written in other letters (Dark-Only,Neutral) is offered back fixed, and a base is suggested only when it is installed. A missing argument names what the command takes and what it was given (set corners smallreadscornersas the theme), with its usage and--help. (#11419) -
astryx experimental theme editor: a settings file whoseliftedfield is not a list of text is named, with the undo that puts it back, instead of a crash. Setting a whole-theme dial notes each token your own<theme>.theme.tssets itself, which wins over the dial, so a change that does not show is explained. (#11419) -
astryx experimental theme editor showrenders its pages again when the Core or base theme package it runs from changes, even at the same version (a linked or patched copy), and when a page was edited by hand: the page records a hash of itself beside its render key. A folder in place of an output the build writes for some themes only (the variants types) is refused by name, like the other outputs. (#11419) -
astryx experimental theme editor: a built type file (<theme>.d.ts,.css.d.ts,.variants.d.ts) replaced by a folder is named withls -la <file>, never with an--outthe editor does not have. A theme name one letter off an existing theme suggests the same command with that theme's name first (set brnad corners small→set brand corners small), andnewwith the typed name only after. An empty theme name ('',-, an unset variable) says the name is empty and names the themes here instead ofnew my-theme. (#11419) -
astryx experimental theme editor: a token your own<theme>.theme.tssets only in a scope (onDark,components) no longer counts as page-wide, so it never masks a dial or closes a token loop; only its top-leveltokensdo. A preview page with text added after its render stamp is rendered again. Theeditor showdocs list ERR_FILE_EXISTS and ERR_WRITE_FAILED, say ERR_UNKNOWN_THEME also covers a base theme package that is not installed, and the exit-code row names every refusal. (#11419) -
astryx experimental theme editor: a render that fails inshow(two copies of React, a component that throws) is refused withdoctorand "Nothing was written" wherever it happens; outputs thatshowmade again are put back first. Two copies of React are now named as the cause (Core's own React is found from its entry). In a workspace whose root does not use@astryxdesign/core(Core hoisted to the root), the root and folders without apackage.jsonof their own are refused, naming the workspace's apps that use Core with thecdto run from, so no theme lands outside an app.undo <theme> radius-elementandundo <theme> paddingsuggest the token (--radius-element) or the property with its--on <part>, never a step back that undoes another change. (#11419) -
astryx experimental theme editor: a word that names a JavaScript object built-in (constructor,__proto__,toStringand the like), given as a value, a scope, a state, a flag or ahowquestion, gets the same refusal as any other unknown word instead of anERR_UNKNOWNcrash. Every word table the editor reads with typed text now answers only for its own entries. (#11419) -
astryx experimental theme editorfinds the app by one rule: the nearest folder at or above the working folder whose package.json lists@astryxdesign/core(in dependencies, devDependencies or peerDependencies). A workspace member that does not use Core, a subfolder of an app that does not list it, a workspace root with deep globs or any pnpm-workspace.yaml form are refused with where Core resolves from and the workspace's apps that list it, each with thecdto run from; nothing is written into the wrong folder. A nested repository inside an app no longer hides the app above it. (#11440) -
astryx experimental theme editor: the home folder is the app when its own package.json lists Core; a folder inside node_modules is never the app; a dependency field that is not an object reads as no Core instead of failing; install suggestions use the app's package manager; a Yarn Plug'n'Play app gets its own refusal from any of its folders; the workspace refusal names apps up to six folders deep and writes a folder starting with-as./-name; a package.json that does not parse is named with the parser's error and its place. (#11440) -
astryx experimental theme editor: a token set on one part (--on) that reads a whole-theme token is checked for a loop only within the scope it is set in. The part reads its own settings first and the whole theme's after, and a whole-theme value never reads a part's, so--color-accent var(--color-text-primary)with--color-text-primary var(--color-accent) --on top-nav-headingis accepted in either order, and the saved settings no longer leaveshowand everysetrefusing; a loop within one part or within the whole theme is still refused. An exact accent (or other fill color) given asvar()of another token, a two-colorlight-dark()or a color function of tokens is read as the color it shows in each mode, so the text on it is picked to read on that color and the notes name it (var(--color-warning) = #ffce2f); a value that still cannot be read as one color is named as unchecked instead of the generated shade. (#11419) -
astryx experimental theme editor: a theme's own<theme>.theme.tsdeleted by hand is written again byshowor the nextset, which say so, andcompileof it suggestseditor show.text-sizeandtypename an exact role size they keep (--text-body-size, heading sizes), and a role or whole-theme font names an exact--font-family-<role>it keeps, with the undo that frees it; undoing a role font names that token as what applies. A value pasted with!importantsays it is not used and suggests the value without it, and the font dial reads a pastedGeorgia, serif;or a comment as the font meant. (#11440) -
astryx experimental theme editorreads the tokens your own<theme>.theme.tssets from what the build made of it, so any way of writing the file counts (a namespace import, a renameddefineTheme, a template, a spread of a shared object); before its first build the file is read as written, and a change the next build shows to make tokens read each other is refused with the files put back. The base theme's own values are never counted as yours, and they close a token loop and make a part read a token as the base theme's rules do. An accent your theme file sets is the color the palette picks text for, and an exact token your theme file also sets says the file's value shows. (#11440) -
astryx experimental theme editor: an exact token hand-edited in the settings file to avar()that names no design token (and has no fallback) is named byshowand every later command, with theundothat removes it, assetrefuses it; it is never built. Thefontdial reads any run of spaces or tabs inside a family name as one space, sofont 'Open Sans'names Open Sans. (#11440) -
astryx experimental theme editor: a Yarn Plug'n'Play workspace (its.pnp.cjsat the workspace root) gets the Plug'n'Play refusal from a member app or its subfolders; a repository inside an app in the home folder is refused naming that app and itscd, never an install into the repository; a suggestednew my-<name>is a free, valid name, and names the user's ownmy-<name>when there is one; a base typed as its package name (@astryxdesign/theme-butter,theme-butter) or in other letters stays that base in the suggestion, after its install line when it is not installed. (#11440) -
astryx experimental theme editor: the font, text-size and type dials no longer say your theme file sets the tokens they write; a palette token your theme file sets gets no shade note, and your theme file comes first in every note (before an exact setting, before a dial) and after an undo. A token loop inside one part is refused when it closes through the base theme's or your theme file's rule for that part, a setting on a part that your theme file also sets on it says the file's value shows, and undo counts only the steps it can still go back to. (#11440) -
astryx experimental theme editor set: a font family list missing a comma ("Open Sans" serif) is refused with the comma put back ("Open Sans", serif), read with css-tree; a suggestion never folds the list into one family holding an escaped quote. A font pasted with its property name (font-family: Georgia, serif;, or afont:shorthand) is read as the family list it names, for thefontdial and exact font tokens. Avar()with a fallback is never refused as reading nothing; when its name is a slip from a design token, a note says the fallback always shows. (#11440) -
astryx experimental theme editor: your theme file comes first in more places. A token loop inside a part's state or variant rule is refused when it closes through the part's plain rule (the base theme's or your file's); a part dial, a group or a variant setting that your file sets on a part says so; undoing a part setting your file also sets names the file's value;shownames every exact setting your file sets itself; and a note that says to take a token out of your file names the exact setting that then shows. Aftercompileof an edited theme file,showreads the build as current whencompile --checkwould, instead of building it again. (#11453) -
astryx experimental theme editor:undo <theme> headings Tracking(orTRACKING,Tight) removes the tracking half, never the weight half; a change removes only the pictures the editor took (and only while they are as it wrote them), never a file of the user's, and marks the section sheetsshow --sectionmade as out of date (insheetStale) instead of deleting them; a link in the theme folder whose target is gone is named with where it points, bysetand byundoalike, instead of a copy step that fails or "Nothing to undo". (#11453) -
astryx experimental theme editor set: a font file or link (./fonts/My.woff2,Inter.woff2, a font service URL,url(…)) and a name holding a colon are refused as font family names, for thefontdial and exact font tokens, with how to load a file (@font-faceor a font package) and never a suggestion that quotes them. A value pasted with its property name (color: #e11d48;,font-weight: 700,font-size: 18px) is offered as the value given, avar()with a fallback as its fallback, andlight-dark()on a color dial as its light color with a note;font-family: ;with no value is refused as no value. A size suggestion is the token's own default instead of6px, and a token typed with its colon (--color-accent:) is offered without it first. (#11453) -
astryx experimental theme editor: its phone rule repeats only your exact font settings, and the notes say what phones show (on top of a base theme, phones show the base theme's type for fonts your file sets as plain tokens; the note says to set them on the theme's typography, or in a rule for the same screens). A[light, dark]pair your theme file gives a token that is not a color (a font, a size, a weight) is refused by name, as--modeis on those tokens, and a font your file sets throughtypographyis seen as the file's. (#11453) -
astryx experimental theme editor:--pngwrites a picture only over one the editor took (unchanged since); a file of the user's by that name is named and left as it is, and no picture is taken. The record of the editor's pictures is a file of the theme (<theme>.pictures.json), so it moves and is copied with the theme. Anewrefused for its name never suggests a dial value the dial refuses: the value is left out and the refusal says so. A section sheet's out-of-date notice namesshow <theme> --section <section>, the command that makes it again. A theme folder renamed by hand is refused with every step it takes to edit it under the new name: the file renames and the names inside<theme>.theme.ts. (#11453) -
astryx experimental theme editor set: in a font family name, every Unicode space (a no-break space, a thin space) is one space and zero-width characters are dropped, soOpen<no-break space>Sansnames Open Sans. A dial's value pasted with its property name (border-radius: pill,font-size: 18px;for text-size,color: #e11d48for accent) is read as the value, one path for every dial. A declaration pasted as one argument (set b '--color-accent: #e11d48'or--color-accent:#e11d48) sets that token to that value;set's usage now shows the value as[value...], and a set with no value gives the same missing-value error as before. (#11453) -
astryx experimental theme editor: a[light, dark]pair on a token that is not a color (a shadow too) is refused however your theme file writes it (a constant,as const,satisfies, a spread from an imported module), byset,showandundoand byexperimental theme compileand--check. A state rule your theme file sets on a part (:hover) is named like a variant: inset,show(with no contrast caveat for a setting that does not show) andundo.compileof a theme whose saved settings hold a value the editor refuses names theundothat removes it, and the phone note says to set fonts on the theme's typography, or in a rule for the same screens, to keep them on phones. (#11468) -
astryx experimental theme editor: one entry normalizer reads every setting before any check, typed or saved. Every Unicode space (no-break, thin) is a plain space and zero-width characters go, for every value type; a saved value holding one is named with the clean value to set. A declaration typed whole or split (accent: #e11d48,--color-accent=#e11d48,font:inter,accent:then the value) is the setting and its value, for dials and tokens; a token typed with an em or en dash or asvar(--x)is that token, and one typed without its dashes is offered with them. A refused value's suggestion comes from that setting's own check:text-size 14ptsuggests the size in pixels,type largeortype 18pxsuggests text-size (never corners),modelists its modes, and a saved size token suggests its own value with a unit. (#11468) -
astryx experimental theme editor: an empty theme folder (or one holding only system files such as .DS_Store) is no theme yet:set,showandundosay so and suggestnew <theme>, which makes it there, instead of calling it a theme the editor did not make. (#11468) -
astryx experimental theme editor:newandshowend withwiring, the steps that put the theme into this app:astryx theme add <theme>for its app copy (a one-mode theme'smodefor the<Theme>too), and what builds it; only when a folder of yours holds the copy's place does it give the root file's imports and the<Theme>by hand. The editor's help says the same. A token set on a group names the members that read it and take the change, and what the others read instead; a part that never reads it is still refused. (#11468) -
astryx experimental theme editor: every build of a theme (new, set, undo, show, andexperimental theme compileof its source) also writes its app copy,src/themes/<theme>/: a standard local theme (descriptor, a source that re-exports the built module, the built module, its stylesheet and font stylesheet), marked as written by the editor and never edited by hand.astryx theme add <theme>then puts the theme into the app, andtheme use,removeandlisttake it as any local theme;newandshowsay so inwiring. The first time the copy is written (bynew, or by the first build of a theme made before), the result names its files (appCopyin JSON); later builds write it quietly. A folder there that is not the editor's own copy is never written, and the wiring gives the imports by hand instead. (#11468) -
astryx experimental theme editor: a part setting saved for one mode no longer locks a theme switched to one mode (it stays saved, is named as not applying, and applies again with both modes); a part's length (padding, a corner radius, a gap) that reads a design token of another kind (a color, a font, a duration) is refused like the token form, naming a length token that works; and avar()whose name is not a CSS variable (var(color-x),var(-x),var()) is refused for every value, with the repaired form. (#11468) -
astryx experimental theme editor: a saved setting written in a form the editor cleans up when typed (Corners,var(--radius-element), an em dash,backgroundColor, stateHover) is removed by the undo its refusal suggests, and only that setting: a valid setting the cleaned name also matches (corners = pill, the real hover rule) stays. A saved value's cleaned form is offered as a set line only when set takes it. Curly quotes in any value are the straight ones CSS reads (a font family in curly quotes is no longer saved with the marks in its name), a quoted CSS string keeps its no-break spaces, and any run of dash-like characters at the start of a token name (em or en dash, non-breaking hyphen, minus sign, full-width hyphen) is its two dashes. (#11468) -
astryx experimental theme:list,add,use,removeandejectgiven an editor theme with no app copy yet name the same stepseditor newandshowgive inwiring(build it once, thentheme add <theme>, and a one-mode theme'smodefor the<Theme>). A color change stopped before its settings were saved no longer leaves its color config andtokens/behind: the editor records the config before it writes it, and the next build removes it when the saved settings set no color; a config of your own is never removed. (#11468) -
astryx experimental theme editor how: a focus ring request keeps its scope ("focus ring on inputs only" sets the ring's tokens--on inputs, the other parts keep theirs), and an exact amount ("half", "double", "twice", "50% strength", "x0.5") is worked out from the theme's own value: the ring's width, a part's corner radius or its padding when the theme sets one. An amount alone after a comma ("…, at half strength") belongs to the ask before it. A comparison ("cards with twice the padding of buttons") is of the other part's value, when the theme sets one; otherwise the answer is a step and says so. The answer names the value it starts from and, for "strength", that it reads it as the ring's width. (#11490) -
astryx experimental theme editor how: "header(s)" or "title(s)" with a text word (bolder, bigger, font, weight, size, serif) means the headings' type, and with a surface word (background, bar, border, sticky, height) the app header part; a part that owns its header ("table header") keeps it. "just" and "only" keep the scope they name ("just the headers bolder" changes the headings, never the body text). (#11490) -
astryx experimental theme editor: adark-onlyorlight-onlytheme keeps both halves of its colors, and its mode is the one the app's<Theme>is given. (#11468)- The theme's colors keep their light and dark values, so a toast, a lightbox or an overlay Core draws in the other mode stays readable; the generated settings module carries only names Core's types take, so the app type-checks.
set <theme> mode dark-only(orlight-only) says to give the app's<Theme>mode="dark"(or"light"), and the wiring aftertheme addsays where it goes; the preview passes it already.- Settings for the other mode stay out of a one-mode theme and are named, as before.
-
astryx experimental theme editor newends with how to look at the theme after each change:editor show <name> --pngwritesthemes/<name>/preview.pngandsheet.pngfrom real components and changes nothing in the app; open the pictures and check them against what was asked. (#11500) -
astryx experimental theme editorreads the page-wide tokens of your own<theme>.theme.tshowever they are written (inline, a const, a spread,as constorsatisfies), from thedefineThemethe build loads, never a sibling theme in the same file; a value it cannot read in the file comes from the build. Theexperimental theme template --overwriteandcompile --outhelp match what they do. (#11419) -
astryx experimental theme editor: shorthands by what they set, and part values Core can carry. (#11419)- A shorthand set after its longhands replaces only the longhands it really sets, read from MDN's data (
overflowkeepsoverflow-wrap,transformkeepstransform-origin,flexkeepsflex-direction), and a part'sbackgroundkeeps reaching its variants whenbackground-blend-modecomes later. - A group's shorthand replaces its members' longhands as a part's does, so the later setting wins either way.
- On a part whose padding Core writes one variable per side (card), a variant's padding after a side set for every variant is refused, with the commands that work: it would not show.
--on cards,dialogs,tabsand other plurals name the part, with the set command.- Word hints keep the value given:
shadow noneisbox-shadow none,rounded 8pxiscorners 8px,bold falseisfont-weight normal.
- A shorthand set after its longhands replaces only the longhands it really sets, read from MDN's data (
-
astryx experimental theme editor: card padding and radius written exactly. (#11419)- On a part whose padding Core writes one variable per side (card and the other containers), the editor writes each side itself, split as CSS splits a shorthand:
calc(4px + 2px) 8px,clamp(12px, 2vw, 24px)and four different sides reach the part as typed. - A variant's padding after a side set for every variant now shows: the editor writes the variant's side variables too, instead of refusing it.
- A card radius of more than one value (
12px 12px 0 0,10px / 20px) gives each corner its own radius; the content that reaches the card's edges takes one radius, the first corner's, and the result says so.
- On a part whose padding Core writes one variable per side (card and the other containers), the editor writes each side itself, split as CSS splits a shorthand:
-
astryx experimental theme editor: variant values win over lifted base values, and card padding keywords written exactly. (#11419)- A part's base value lifted onto a variant (so Core's own variant rule does not hide it) now goes before the variant's own declarations, so a longhand or shorthand set for the variant wins (
border-colorfor the destructive button after a baseborder).
- A part's base value lifted onto a variant (so Core's own variant rule does not hide it) now goes before the variant's own declarations, so a longhand or shorthand set for the variant wins (
-
astryx experimental theme editor: padding sides and inner radii that win as written. (#11419)- A later part padding replaces a side its group set.
- A part's own multi-value radius keeps the content's radius it gives over a single radius from a group, a dial or the base theme's lifted onto a variant.
-
astryx experimental theme editor: the chat composer's box is reached as it is drawn. (#11419)paddingon chat-composer goes to the padding of the visible composer box, with no gap outside it. Background, border, shadow, a side padding or a padding for one density, which would land on the composer's transparent wrapper, are refused with the reason.
-
astryx experimental theme editor: one padding model for parts whose box reads Core's padding variables. (#11440)- On card, clickable-card, selectable-card, dialog, drawer, bottom-sheet, section and number-input, each part's padding is worked out per variant and per state from every padding setting in the order made, side by side for each text direction (a physical left is the start in a left-to-right box and the end in a right-to-left one; so is the
paddingshorthand's left), and written to the variables the box reads. The later setting wins in both directions, a variant or a state out-ranks the base, and the content that reaches the box's edges stays in line. - A clickable or selectable card is padded as a card.
- A part's base rules come before its variants', and its rules for a direction before its states', whatever order the settings were made in.
paddingon chat-composer takes one length, as Core reads it inside calc(): a length, a variable such asvar(--spacing-3)(Core's own default), orcalc(),min(),max()orclamp()giving one. Several values, a bare number or a percentage is refused with one that works.
- On card, clickable-card, selectable-card, dialog, drawer, bottom-sheet, section and number-input, each part's padding is worked out per variant and per state from every padding setting in the order made, side by side for each text direction (a physical left is the start in a left-to-right box and the end in a right-to-left one; so is the
-
astryx experimental theme editor: padding that stays where it is set, and one length wherever Core reads it inside calc(). (#11453)- Padding set on a clickable or selectable card, or on a card's variant or state, no longer reaches plain cards nested inside it: each side the narrower rule writes is reset on a plain card inside it, so Core's own fallback holds, and the narrower rule writes each side rather than the whole padding.
- Every part whose padding Core reads inside calc() (found from Core's own stylesheet: chat-composer, dropdown-menu, context-menu, segmented-control, and an item's inline padding) takes one length there, as chat-composer does, also through a group (
--on menus). - A padding with
--stateon a card, dialog or a group of containers is now taken: the padding model writes the state's own sides.
-
astryx experimental theme editor: nested sections that follow their parent, and lengths Core can calculate with. (#11453)- A section padding the same on every side is written as the section's whole padding, which Core reads after the padding a parent section passes down, so nested sections keep following their parent.
- A padding of
0is written as0pxon parts where Core calculates with it (card, section, dialog, drawer, bottom-sheet);initialandunsetleave the padding to Core's own default. - A hand edit to the settings file that
setwould refuse is reported by the nextsetwith the command that removes it, ascompiledoes, instead of being built.
-
astryx experimental theme editor: a part's padding replaces the base theme's, and every padding writes a length Core can calculate with. (#11453)- A padding on a card, section, dialog, drawer, bottom-sheet or number-input replaces the base theme's padding there, whichever sides the base theme set: the editor writes it as padding properties Core works out together with the base theme's, where the later one wins.
- A clickable or selectable card's padding wins over a card's left or right padding in each text direction.
- A plain part nested in a narrower one is reset only on the sides its own rule leaves unset, so a padding in your own theme source on that part still applies.
- A zero written any way (
0e0,0.0) is written as0px;initialandunsetname the part's own default padding from Core. - A hand-edited padding on one part is reported when a
setchanges padding on another part too.
-
astryx experimental theme editor: what the editor builds,compile --checktakes. (#11468)- Two to four corner radii on card, button, dialog, avatar, banner, popover and the other parts with an inner-corner radius are written as CSS properties Core reads (the whole radius, which Core turns into the content's radius, then each corner), never as a private variable, so
compileandcompile --checkpass. - A padding on chat-composer is refused with the reason: the box inside its transparent wrapper reads its padding only from a variable of Core's own, which a theme cannot set, and a padding on the wrapper would add a gap outside the box.
- Two to four corner radii on card, button, dialog, avatar, banner, popover and the other parts with an inner-corner radius are written as CSS properties Core reads (the whole radius, which Core turns into the content's radius, then each corner), never as a private variable, so
-
astryx experimental theme editor: a padding in your own theme source wins over the editor's on the same part, whatever sides each names. (#11490)- A padding your
<theme>.theme.tssets on a part's plain rule, a variant or a state wins over an editor padding there (padding 20px --on cardunder your own card padding of 6px shows 6px), and the result says so, also right after an edit to that file, before any build. - An editor padding on a part's plain rule that differs by text direction keeps the plain rule's weight, so any variant, your own included, out-ranks it.
padding inherit(andrevert) on parts whose padding Core keeps in its own variables is taken likeinitialandunset, and the result names the default it means.
- A padding your
-
astryx experimental theme editor: the spacing dial keeps the small, medium and large control sizes apart and in order at every step. (#11490)- A denser spacing still makes controls shorter by the plain scale, but no size meets the one below it: one that would keeps its own step, scaled, above it, and none goes under 24px (
spacing tighton Neutral gives 24px, 27px and 30px, where small and medium both ended at 24px). The dial's help says control heights follow it.
- A denser spacing still makes controls shorter by the plain scale, but no size meets the one below it: one that would keeps its own step, scaled, above it, and none goes under 24px (
-
astryx experimental theme editor: every picture--pngtakes carries the editor's stamp (a PNG text chunk naming the theme), so it is known as the editor's even without its record; a picture an earlier version took, with no record yet, is taken over when it is the editor's 1280-pixel window, and the result says so once. Only links among the files the editor reads or writes are refused, each named with where it points; a link to a file of yours (notes, a mockup) is left alone. Anewrefused for its name leaves out of its suggestion a vibrancy that would change nothing (no color given), and says so. The--pnghelp of new, set, show and undo names the picture record and when no picture is taken. (#11453) -
astryx experimental theme preview: the route and pages it writes type-check withnoUncheckedIndexedAccess,noImplicitOverrideandnoUnusedLocals.add --pathrefuses a folder name that is no plain URL segment (a space, an accent,#,%), suggesting a slugged one. Folder names Windows does not allow are refused in full: reserved characters, a trailing dot or space, andCOM¹-style device names. The free folder a URL conflict suggests is one whose URL no other page serves.preview appleaves out a temp file a stopped command left in the theme folder. (#11419) -
astryx experimental theme preview: in the preview's nav, only a plain left click switches the page in place; a Ctrl/Cmd, Shift, Alt or middle click opens the page's URL as a link does. Theseeline after an editor change now leads with a look that changes nothing in the app (the editor's ownpreview.html, current after every change it renders, and--png), then the in-app preview page when there is one; adding the preview to the app comes last, as an option. (#11490) -
astryx experimental theme preview add: a folder name that Next serves at a plain URL (letters, digits and. _ ~ ! $ & ' ( ) + , ; = @ -) is accepted. For any other name, every odd segment is slugged and the suggested folder is a free one. A folder that differs from another only in case is refused, since macOS and Windows treat them as one. In an app with no TypeScript, the result names the install line. When the app's tsconfig turns onnoUncheckedIndexedAccess, the result names the template pages written as their template ships. The Colors page shows a seed color as it is stored. (#11419) -
astryx experimental theme: going back to the preview's bare URL shows its first page, as a reload does.editor undopoints itsseeline at the page about what it reverted. A preview whose URL depends on the app's routing, or whose folder was renamed by hand, is named where it is, byseeand bypreview list, instead of suggesting a new one.preview add --themetakes a pasted label (theme: t,name: t) as the theme t; an editor command given one refuses it and suggestsshow t. A change the editor could not render says why in itsseeline instead of pointing at apreview.htmlit did not write. (#11490) -
astryx experimental theme preview: a default page's id beside another page asked for is refused ("drop one of them") instead of dropping the other.preview add colorson a theme with no generated colors says how to get them.preview listmarks a page whose file is gone and notes a change that was stopped part way, andaddwrites a missing page back as added. Running a stoppedpreview removeagain finishes it and exits 0.basePathis certain only when set as a literal on the exported config at the top of next.config; one set in anifor a function, deleted, or added by a wrapper is named as set from code. (#11378) -
astryx experimental theme preview: the preview route renders its pages only in the browser, so a component that crashes a server render no longer stopsnext buildwhen Components is the first page. next.config is read the way Next loads it:.jsbefore.mjs,.mts,export {config as default}, a key set after the object, and the last of a repeated key; a config changed in other ways gets a note instead of a guess. Table pieces (TableHeader, TableBody, TableFooter) are no longer shown alone on the Components page.preview add overview(or another default page's id) brings back the default pages that are missing. (#11378) -
astryx experimental theme preview app --theme <t>: the new app installs the Core and base theme versions this project has, the ones the copied build was made on, so the theme is current after install; a note names them and how to move to this CLI's release. (#11468) -
astryx experimental theme preview:addrebuilds the theme whenever its build is not current (an output missing, or a build that does not match the settings), the same rulepreview appuses, so the route never imports a missing file. A pasted--theme 'theme: t'names the theme t. AdistDirwith a subfolder blocks only that folder.remove --pathon a folder with no preview names the preview there is, with its remove. Thepreview apphelp says that with--themeCore and the base theme are pinned to the project's versions. (#11468) -
astryx experimental theme: every in-app preview page has its own URL (/theme-preview?page=components), which the nav links to, so a link, a reload, the back button or a screenshot reaches any page;preview add,listandappgive each page's URL. Every editor change carries aseeline: first a look that changes nothing in the app (the editor'spreview.html, current after the change, and--png, which takes pictures with no dev server), then the in-app preview page most about it (Colors after a color, Components after a part, Overview otherwise) at its URL when there is a preview, and adding one last, as an option. The--pnghelp says it needs a Playwright or a Chrome and adds a few seconds. (#11490) -
astryx experimental theme preview: a--paththat goes through a file, has a folder name longer than 255 bytes, or is longer than 3,500 bytes, is refused with a reason. A folder that differs from another only in case is refused forpreview appand for a--pathoutside the routes folder too.preview removeleaves a page file it cannot read in place, says so and keeps it recorded. A preview flag given twice with different values is refused. (#11419) -
astryx experimental theme preview remove: a page the app's own code imports is kept, and the importing file is named, for one page and for the whole preview. The check reads the whole project, following relative imports,@/and tsconfig path aliases. (#11453) -
astryx experimental theme preview: popovers shown open on the Overview and Components pages no longer take focus, so the page stays where it opened. The Overview points to the Colors page only when the preview has one, and a colorseton a theme whose preview has no Colors page says whichpreview addadds it.preview removetakes away the empty folders the preview's firstaddmade, never one that was there before. The Overview reads its font line again when the theme changes while the page is open. Thepreview appstarter lists@types/node, so its firstnpm run devinstalls nothing. (#11440) -
astryx experimental theme preview add: an app frame's exact name as the description (blank,shell-top-nav) is refused with a clear reason and the--templatecommand. An exact template name is called the page named, not the closest page. With no routes folder, the refusal names the Next apps below with a--pathinto each, and a--paththat would make a routes folder with no root layout is refused before writing. Page packages are read from the app the preview is in. The Core stylesheet check starts from the root layout over the preview, route groups included, and follows tsconfig path aliases. (#11440) -
astryx experimental theme preview add: a short description that names a page template (checkout,hero,a landing page) takes the pagebuildranks first, instead of being refused. A description that is exactly a template's id or display name adds that page, or says why it was skipped. The Core stylesheet note follows the root layout's imports, so a stylesheet imported from a rootstyles/file counts. A theme font whose package the app lacks gets a note with the install line, and the Overview marks a font that is not installed. (#11440) -
astryx experimental theme preview remove: the check that keeps a page your code imports now reads the whole project (every source file outside installs, build output and dot folders), parses each file's imports, exports,import()andrequire()calls, template-string ones included, and also guards the route a file re-exports. A project too big to check is refused rather than half-checked. A route changed by hand is told to move out of the preview folder.preview add --template overview|components|colorsadds just that page when it is missing, or says why not. Temp files a stopped preview command left are cleaned by the next one and by a full remove. (#11453) -
astryx experimental theme preview: when both spellings of a folder are already there, the case-clash refusal suggests a new folder beside them, and namespreview removefor a preview already inside. Atheme-preview.jsonentry that leads outside the folder (../) is reported as damaged byaddandlist.removedrops such an entry and deletes nothing outside, and its messages show paths from the project. (#11419) -
astryx experimental theme preview: the route of a one-mode theme no longer imports the mode switch it does not draw, so apps withnoUnusedLocalsbuild. A refused--themesays exactly what leaving it out does in this app: the theme the preview keeps wearing, or the one theme underthemes/it picks. (#11453) -
astryx experimental theme preview: no demo on the Components page takes focus as it mounts. A dialog part such as DialogHeader shows inside an open inline Dialog, and an open Popover is named. When the preview's theme is gone, its Colors page goes with it.preview removerefuses while a file of yours in the folder imports the preview pages, and its advice for a route changed by hand is to move it out of the folder.preview listwith no preview says whyaddwould refuse the folder instead of suggesting it.removetakes away the empty foldersaddmade outside the routes folder too. Every page-template list suggestion names the command the way the app runs the CLI, in--jsonas in text. (#11453) -
astryx experimental theme preview: the base theme's fonts stylesheet is imported only while the theme's build still names a family it loads, so a theme onfont systemor its own fonts fetches nothing it does not use.preview addnames every page it writes again, the Colors page included, when the preview switches theme. (#11419) -
astryx experimental theme preview:add --pathrefuses an intercepting route folder ((.)x,(..)x,(..)(..)x,(...)x), where a page breaks the whole app. The preview loads the fonts stylesheet of the theme it is built on (Figtree for neutral), and so does thepreview appstarter, so the Overview shows the family it names.preview listtext marks a page whose file is gone as missing, matching--json. (#11419) -
astryx experimental theme: padding sides by direction, a failed show leaves the folder as it was, andhowreads "not dark enough". (#11419)- On dropdown-menu, context-menu and segmented-control, one logical padding side is written for each direction (the left in a left-to-right box, the right in a right-to-left one), so it changes that side only; under a state it is one rule per direction (
:hover:dir(ltr)), and a later padding or physical side replaces it. showwhose render fails puts back every output it touched, removing one it made new and restoring the palette files intokens/.how "not dark enough"/"not light enough"asks for darker or lighter, not for one mode.
- On dropdown-menu, context-menu and segmented-control, one logical padding side is written for each direction (the left in a left-to-right box, the right in a right-to-left one), so it changes that side only; under a state it is one rule per direction (
-
astryx experimental theme editor: a card's physical padding side beside others, a failed show beside your own folders, and the busy-lock suggestion from the API. (#11419)- A card
padding-leftorpadding-rightset beside another side, as a keyword (initial) or as math (calc()), reaches that side. showwhose render fails leaves a folder you put intokens/alone.- When another command holds the theme, the suggestion to run again names the call's own command with every option it got (
--only,--likeand each dial ofnew,--png), never the arguments of the process the API runs in; a preview command waiting on another namespreview list.
- A card
-
astryx experimental theme editor: everyseta refusal suggests is now checked the waysetchecks it, without writing anything, and a suggestionsetwould refuse is left out. Suggestions carry what was asked instead of a fixed example.headings light --on headingoffersheadings lightand weight 300 on one level.text-size large --on cardoffers that size on the card. A number token reads the number meant (700pxis 700,1,5is 1.5,calc(1rem+2px)gets its spaces). A word dial given a number offers its own words, or the nearesttypepreset for a scale ratio. An invalid color is never copied into the text color token. A color property refuses avar()fallback insidecolor-mix()that is not a color.how "more washed out colors"lowers vibrancy. (#11419) -
Icon sets: a project picks one look for every icon.
astryx experimental icon use <main> --variant <variant>writesexperimental.iconVariantbesideexperimental.iconSet, in one write, and refuses a variant the main set does not declare; without--variantit removes the key. Each set in the list draws its variant of that name, or its default look when it declares none, and the receipt (icon.use) addsvariant,previousVariant,looks, andthemeMap. A copied template imports each icon in that look where its set has it, reports the look asicons.variant, and moves an icon a template imports from the main set's own library, or from a listed set in another look (by: 'variant'), to that look. A set's generated module also writes a theme map for each other look an icon draws (themeIconsFilled), andintegration checkholds it tomap.astryx doctorfails when the main set does not declare the variant, andexperimental icon --listshows the variant and each set's look. A CLI before 0.7.16 refuses an astryx.config that setsexperimental.iconVariant, sousewarns when the project's CLI range admits one. An icon-set package whose generated module an earlier CLI wrote, and whose icons draw a look other than the default, failsintegration checkuntil it runsexperimental icon add --variant <variant> --from <module> --allagain; the finding names that run. Projects that do not setexperimental.iconSetsee no change. (#11499) -
astryx experimental theme editorcounts a base theme as installed only when the project installs it, in itsnode_modulesfolders or through Yarn Plug'n'Play, as the app's bundler sees it. A theme found only throughNODE_PATHor Node's global folders is no longer taken as installed:newandset baseoffer to install it instead of writing a theme the app could not build. (#11444)
-
astryx doctorwarns when the@astryxdesign/corepeer range of a loaded integration, other than the packages of added themes, whichtheme-core-peersalready checks, excludes the installed Core (integration-core-range). A package made for another Core installs with only a package manager warning, and its components, themes and docs may not work; the warning names the package, its version and range, and the installed Core, and the fix names the release to look for. A project whose other integrations all declare a range that includes its Core gets a passing row naming them; a project where none of them declares one sees no new row. It is a warning, so no project that passed before fails. (#10350) -
Doc examples take an optional
kind: 'demo' | 'setup'(default'demo'), on component, sub-component and function examples. Asetupexample is code that cannot run on its own, such as an app setup file, a server route, or a snippet that uses variables defined elsewhere:astryx component <Name>labels it "(setup, code only)", and a docs site shows it as code with no live preview. Inastryx component <Name> --json, every example carries itskind. Any other value fails to load with a message that names both kinds.astryx docs authoringexplains when to usesetup. (#11434)
-
In the AI Chat page template, the chevron on the artifact card now points toward the end of the line in right-to-left layouts, as it does in left-to-right. (#11477)
-
In the AI Chat page template, the message list's scrollbar no longer sits under the artifact panel's resize handle. On desktop the chat column keeps a small gap before the handle, so the whole scrollbar scrolls the messages; before, a press on the half of the scrollbar nearest the handle resized the panel. Mobile, where the handle is hidden, is unchanged. (#11473)
-
astryx buildstarts more whole-page ideas from a page template instead of the empty app shell. When build would start an idea on an app shell and search matched no template directly, a fixed decision over the scores build already computes (plus a fixed word table) gives a whole-page probability; at 0.30 or more, the start is build's best-ranked page template. Ideas with an edit phrase keep the shell, and block suggestions do not change. The decision picks only templates build's word tables list, so an integration's own template is never the lifted start; its other results are unchanged. (#11519) -
Running any command from a folder that no longer exists now gives a plain error with a
cdto the nearest existing folder (text and--json, codeERR_PROJECT_REQUIRED) instead of a stack trace;--help,--versionanddocsstill answer. (#11440) -
astryx doctor's theme stylesheet check no longer fails on a theme's own files when the theme it extends is added too (theme add dandtheme add neutral): a theme source, its build and the editor's generated files only extend the other theme, so they are not asked to import its stylesheet. An app file that imports an added theme's module without its stylesheet still fails the check. (#11506) -
A refusal longer than a pipe holds (64 KiB) reaches a slow reader whole, such as bash
$(…)or a reader that takes its time: the--jsonenvelope stays valid JSON with its code, and the text refusal keeps its last line. The CLI writes its final error output out before it exits. This is a small change to the shared error output, made for the theme editor's long refusals. (#11419) -
No public API function takes an undocumented
__-prefixed option.themeBuild()reads only its documentedout,check, andiconsSpecifieroptions; whattheme buildhands a batch, family, or package-check build now travels through the CLI's own internal path, sotheme buildandintegration pack-checkbehave and print exactly as before. A test fails if a hidden option comes back on any exported function. (#11446) -
Kept catalogs and check results no longer depend on the value of every environment variable when code only lists the variables' names (as a TypeScript transform does). Tools that set a new value per call, such as AI agent harnesses, missed every kept answer on every call; they now hit like any other run. A variable whose value is read still decides the kept answer, including one read inside a kept gather that another one wraps. (#11427)
-
astryx doctor integration validateandastryx integration checkname unreachable contribution metadata with forward slashes on every platform. On Windows the path in "Found contribution metadata ... outside every declared integration root" used backslashes, while the fix in the same message used forward slashes. (#11084) -
A kept entry in the per-user cache whose value is missing or has the wrong shape for what it holds is now worked out again and stored over, instead of being used: a damaged entry could make
search,doctorortemplate --listfail until the folder was cleared. (#11474) -
astryx theme palette generateandgenerateTonalPalette()honordarkChromaTaper, so every official theme palette regenerates from its saved config exactly as committed. Neutral's config asks for a muted dark edge on its chromatic families, but the generator dropped the field: 99 of Neutral's 420 colors came out more saturated than the committed ones (blue.dark[25]was#003978, not#253c5a). A dark stop now keepsedgeMultiplierof its in-gamut chroma throughthroughStop, then recovers along a smoothstep to the standard recipe atrecoverAtStop;familyMultiplierssets the edge share for one family. Light ramps, neutral families, the black and white endpoints, and configs without the field generate the same colors as before. A taper with a field it does not read, a stop outside the layout, a family the request does not have, a neutral family, or a multiplier outside 0 to 1 is refused. (#11491) -
astryx experimental rendersays why a page ended without an answer: the stage it reached (such as importing the page or rendering on the server), how long it ran, the memory left, and the page's last error output. A page process that loses its channel or reaches its own time limit is reported as that, never as a clean exit. (#11398) -
theme build: the refusal for a quoted glob keeps--familyand--family-keyin a family run, and builds its suggestion with the same builder as the build's other refusals of a source; the experimental compile's refusals for the editor's settings file and for a subfolder keep--out(rebased onto the app root for thecdone), so the suggested check reads the output you asked about. (Minimal change to shared code:buildSuggestiongains an optional shape for a glob or a family run, and the build command passes--outto a group's preflight.) (#11461) -
On Windows,
astryx theme buildprints install snippets that import the built theme with a working path. The import path joined the output folder with backslashes and kept a stray one after the droppedsrc/, so the snippet readimport { oceanTheme } from './\themes\ocean/ocean';, which is not a valid specifier (and\tis a tab in a JS string). It now reads./themes/ocean/oceanon every platform. (#11288) -
From a project subfolder,
theme templateandtheme ejectprint a build step that works from that folder (cd ../.. && … theme build theme.template.tswhen the file is above it), and a blank template path is refused withtheme template(no path) suggested, which writes at the project root; an absolute template path is told that paths are read from the current folder.theme build --outwarns that it replaces another source's output only once every output may be written, reads a backslash in--outas a folder separator, and its--check"Rebuild with" line uses the project's invocation and quotes a path with a space. Building a theme the theme editor made withtheme buildstill works, as released, and now warns (once per--watchsession), namingexperimental theme editor show <theme>. A theme file that is a link and fails on a relative import is named with the file it points to, with a build of that file that keeps its outputs in the working folder; no load error shows Node's require stack. (#11422) -
A command
theme build(orexperimental theme compile) suggests after refusing a source (a Windows-style path, a folder, a file that is not a theme module, or a build output) keeps the--check,--outand--icons-specifierit was given, so following it still checks instead of writing outputs. This holds for a family check, a theme file that is a link, a plain-object family member and a file that changed while loading, and the suggested command is quoted for the shell it runs in (PowerShell too). (#11461) -
The install example
theme build --detail full(andexperimental theme compile --detail full) prints says its paths are relative to a file in src/, and now they are, from whichever folder you run it: an output beside src/, such asthemes/ocean/ocean, is imported as../themes/ocean/ocean, not./themes/ocean/ocean, which does not resolve from src/. Thetheme build --familysnippet uses the same paths and says the same thing in its header. (#11471) -
theme buildandexperimental theme compilerefuse a theme that sets a private--_*variable: the build fails withERR_THEME_INVALID(exit 1, a JSON error envelope) and writes nothing, a--checkincluded. Before, it printed the errors and exited 1 but still wrote the outputs and reported[ok]and a success receipt, so a reader of the JSON saw a pass. (#11475) -
theme templaterun from a folder inside a project reads its path from that folder within the project root, and with no path writestheme.template.tsat the project root, as its help says; a backslash in the path separates folders, as fortheme eject;astryx init --features theme, run below the project root, writestheme.template.tsat that root too, because it uses the same template step. Outside any project (nopackage.jsonin the folder or above it) it writes in the current folder, as released CLIs did, and when it writes the file it warns once on stderr in text mode, naming how to write it at a project root (run it inside a project, ornpm init -yfirst);--jsonstays silent. A staletheme build --check(andexperimental theme compile --check) result carriescode: "ERR_THEME_STALE"besideupToDate: false, the code it exits 1 with. A file at an--outpath that the build did not write is left alone with its real reason and, when one exists, an--outpath where no output of this build exists yet (a new folder when the theme's module beside it is taken); the build never suggests deleting it. The build-beside-the-source suggestion keeps--icons-specifier.--outinto a folder the theme editor made, for a theme source outside that folder, writes there as released CLIs did, and warns once on stderr in text mode, naming how to keep the editor's folder to its own theme (build beside the source, or pick another--out);--jsonstays silent, and--watchprints it once per session, not on every rebuild. (#11389) -
theme build --check(andexperimental theme compile --check) names every output the build writes as checked, the.css.d.tsstylesheet types too. When the only missing output is a.css.d.tsstub, as in projects that keep it out of version control, the check prints one warning line in text mode naming it, and the stub alone does not fail it;--jsonstays silent and the result is unchanged. Every output the check failed on before still fails it, a missing.d.tsor.variants.d.tsincluded.theme ejectinto a subfolder of a folder the theme editor made (themes/g/sub, wherethemes/g/g.settings.jsonexists) writes there as released CLIs did, and warns once on stderr in text mode, naming how to keep the editor's folder to its own theme (fork into the project's local themes folder, or pick a path outside the editor's folder);--jsonstays silent and the receipt is unchanged. (#11410) -
The dashboard page template typechecks in an app with
noUncheckedIndexedAccess(it renders the same), soexperimental theme preview add --template dashboardno longer adds a type error to a strict app.themeBuild()no longer takes internal hand-off options fortheme build --watchor the private-var exit code;--watchstill warns once per session about a theme editor folder. (#11420) -
astryx theme template --overwriteno longer replaces a theme editor file reached through a link to its folder. (#11419) -
An installed dependency that ships an Astryx integration manifest which fails to load because a package it requires (a
dependenciesentry or a peer not marked optional) is missing (a broken install) is now reported as that package's integration issue:component,template,theme,discoverandsearchprint the usual one-line warning, anddoctor's integration contributions check names it with the load error. Exit codes are unchanged. A command that cannot load also reports why whatever options it was given (component --listused to answer "unknown option"). (#11486) -
A dry run of
astryx upgradeno longer tells you to review a.astryx-conflictfile it has not written. When a copied ShadCN composition conflicts with a newer release, the dry run now says that--applywrites the file for review, and an apply still names the file it wrote. The JSON result is unchanged. (#11356)
-
In a project with integrations,
docskeeps the integration check results it gathers, with the theme records they include, so a repeateddocscall reads them back instead of checking every integration again. An edit to a doc topic shows on the very next call.doctor's cache row namesdocsamong the commands that keep what they gather. (#11483) -
Once the per-user cache folder exists,
doctorkeeps its three checks that read only files (the docs tree, progressive disclosure and the CLI's docs) beside search's catalogs, and reads each back while every file it read is unchanged, so a laterdoctorskips them (about 0.2 s, more with large integrations). An edit to any doc shows on the very next run. Its cache row names doctor among the commands that keep what they gather, anddoctor --helpno longer calls it read-only. (#11394) -
In a project where no integration can load (no Astryx config, no integration manifest of its own, and no installed dependency that ships one), help and commands that don't need the project no longer load it first just to read the commands integrations declare:
--help,manifestandbloganswer about 65 ms sooner. Output is unchanged. (#11495) -
In a project with integrations, each integration's discovered themes are kept beside search's catalogs and read back while every file they read is unchanged, so
theme listand the other theme commands skip re-reading an integration's themes on later calls (about 0.1 s with a large integration). An edit to a theme, or to a module it imports, shows on the next run.doctor's cache row names the kept theme records. (#11469) -
In a project with integrations, the whole integration issue list is kept beside search's catalogs (the template and component replacement checks over Core and every integration, as well as each integration's own checks) and read back while every file it read is unchanged, so
component,template,themeandsearchskip those passes on later calls (about 0.1–0.25 s each with a large integration). (#11384) -
In a project with integrations, template discovery's records are kept beside search's catalogs and read back while every file and folder discovery read is unchanged, so
template --list,template <name>andbuildskip discovering templates on later calls (about 0.1 s with a large integration), and a template added, renamed or edited in an integration shows on the next call. A record that would not read back exactly (a function, a Map, a shared object, -0 and the like) is never kept.doctor's cache row names template discovery's records. (#11393) -
Importing
@astryxdesign/cli/api, as integration codemods and other integration code do, no longer loads Babel, recast, jscodeshift and postcss-selector-parser up front: each loads the first time it is used (about 0.25 s and 350 fewer modules per import). The API's exports are unchanged. (#11432) -
component --listfinds a component that has no folder of its own about 20 ms sooner: one index of Core's folders replaces a check of each folder in turn. Output is unchanged. (#11504) -
The published CLI reads the icon definitions and built-in theme descriptors it ships from values stored at publish instead of parsing each file, so
docsanswers about 45 ms sooner anddoctorandtheme listabout 10 ms sooner. An edited or unknown descriptor is parsed as before. Output is unchanged. (#11496) -
experimental themecommands read each folder of Core's sources once while they build Core's component docs.experimental theme preview appandexperimental theme preview adddo the same as they write the Overview and Components pages, instead of once per component name. Every run writes and prints the same. (#11426)
-
The CLI points agents and people to the experimental theme editor for making or changing a theme, labeled experimental each time: the agent block
astryx initwrites (its one theme line, which also keeps extending an imported theme before ejecting one to fork it), one added line inastryx init's next steps (its other steps stay),astryx --help, the theme docs (astryx docs theme, the author-a-theme guide, in English and Chinese) and thethemecommand's help and summary nameastryx experimental theme editor new <name> --like neutral, theneditor set/editor how,theme add <name>to wire it, andeditor show <name> --pngto look at each change.theme ejectadds one line,experimental theme editor new <name> --like <base>(the ejected theme when it is a built-in base the editor knows and its package is installed, elseneutral), andtheme palette generateadds one line,experimental theme editor set <name> accent <color>. (#11500) -
astryx docs styling/tokens-and-setuphas a Next.js with webpack section. It showsexperimental.optimizePackageImports: ['@astryxdesign/core']innext.config.mjsand recommends a component entry, such as@astryxdesign/core/Button, in Server Components. Without either, a webpack build ships every Astryx client component to a page whose Server Component imports from the package root. The theme preview starter app thatastryx experimental theme preview appcopies has the same setting. No API changes. (#11305) -
A new example block shows
useTableColumnsVirtualization: a 121-column Table with a pinned first column that mounts only the columns near its horizontal scroll position. (#11014)
-
Fix the theme README usage examples to import
Themefrom@astryxdesign/core/themeinstead of the removedXDSThemealias, and refresh the bundled theme lists in the package index and contributor guide. (#11016)Also released in
@astryxdesign/theme-chocolate,@astryxdesign/theme-gothic,@astryxdesign/theme-matcha,@astryxdesign/theme-neutral,@astryxdesign/theme-stone,@astryxdesign/theme-y2k.
Thanks to everyone who contributed to this release:
@aashish254 @charlie5188 @cixzhang @ejc3 @emilyhorsman @ernestt @HelloOjasMutreja @humbertovirtudes @josephfarina @karlmoriaty @kentonquatman @kevin9327 @korkt-kim @LekhNayak @lzcmian @nynexman4464 @parinith-web @popsiclelmlm @prath47 @shashank03-dev @vjeux
Full Changelog: https://github.com/meta/astryx/compare/v0.7.15...v0.7.16
@astrojs/starlight@0.43.1
- #4396
46a5020Thanks @trueberryless! - Fixes a regression where the icon of the<Aside>component was rendered smaller than the icon of Markdown asides.
create-astro@5.3.2
-
#17477
a475ac0Thanks @alexanderdombroski! - Fixes create astro to use correct project name when creating in current directory "." -
#17487
3cb7043Thanks @colinhacks! - Adds support for the nub package manager
astro@7.4.12
-
#18409
2917650Thanks @matthewp! - Fixes a typo in the error message shown when a fontcssVariableis invalid -
#18709
6f1a2a8Thanks @ematipico! - Reduces memory usage duringastro buildby releasing content collection entries from memory once the content layer sync is complete. -
#18713
59052e7Thanks @delucis! - Unpins@astrojs/*dependencies to allow versions matching the^range to be installed -
Updated dependencies [
59052e7]:- @astrojs/markdown-satteri@0.4.10
- @astrojs/vite-plugin-astro@0.1.3