@astryxdesign/cli 0.7.12-canary.f543ec7 → 0.7.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +107 -0
- package/api/build/blocks/blocks.d.mts +41 -0
- package/api/build/blocks/blocks.mjs +118 -68
- package/api/component/__fixtures__/resolutions/modules.txt +3 -3
- package/api/docs/_adapter.d.mts +22 -0
- package/api/docs/_adapter.mjs +81 -3
- package/api/docs/docs.doc.mjs +5 -5
- package/api/docs/docs.type.d.mts +21 -0
- package/api/docs/docs.type.mjs +11 -0
- package/api/docs/list/list.d.mts +5 -1
- package/api/docs/list/list.mjs +72 -6
- package/api/docs/node/node.mjs +2 -40
- package/api/doctor/doctor.d.mts +4 -4
- package/api/doctor/doctor.mjs +248 -78
- package/api/icon/_authoring.mjs +8 -1
- package/api/icon/add/add.mjs +45 -5
- package/api/icon/icon.type.d.mts +24 -0
- package/api/icon/icon.type.mjs +6 -0
- package/api/icon/iconAdd.doc.mjs +1 -1
- package/api/icon/map/map.mjs +11 -4
- package/api/index.d.mts +1 -0
- package/api/index.mjs +1 -0
- package/api/integration/add-helpers.d.mts +5 -1
- package/api/integration/add-helpers.mjs +1 -1
- package/api/render/_adapter.mjs +9 -0
- package/api/render/render.doc.mjs +3 -3
- package/api/render/render.type.d.mts +1 -1
- package/api/render/render.type.mjs +1 -1
- package/api/search/ORDERING.md +3 -0
- package/api/search/_adapter.d.mts +32 -3
- package/api/search/_adapter.mjs +50 -22
- package/api/search/_fit-order.d.mts +47 -0
- package/api/search/_fit-order.mjs +257 -0
- package/api/search/_main-searcher.mjs +15 -5
- package/api/search/exportSearchIndex.doc.d.mts +10 -0
- package/api/search/exportSearchIndex.doc.mjs +58 -0
- package/api/search/rankSearchIndex.doc.d.mts +10 -0
- package/api/search/rankSearchIndex.doc.mjs +79 -0
- package/api/search/search-index.d.mts +44 -0
- package/api/search/search-index.mjs +270 -0
- package/api/search/search.type.d.mts +28 -0
- package/api/search/search.type.mjs +17 -0
- package/api/swizzle/_moduleGraph.d.mts +7 -0
- package/api/swizzle/_moduleGraph.mjs +191 -83
- package/api/swizzle/copy/copy.mjs +28 -6
- package/api/template/skeleton/skeleton.mjs +1 -1
- package/api/template/template.mjs +1 -0
- package/assets/codemods/transforms/v0.1.0/drop-xds-prefix-imports.mjs +1 -1
- package/assets/codemods/transforms/v0.1.0/index.mjs +1 -1
- package/assets/discover/official-packages.json +16 -0
- package/assets/docs/styling-overview.doc.mjs +3 -3
- package/assets/docs/tree/icon-sets.doc.mjs +4 -4
- package/assets/docs/tree/render-pages.doc.mjs +3 -3
- package/assets/docs/tree/tailwind.doc.mjs +41 -1
- package/assets/docs/tree/type-scale.doc.mjs +19 -2
- package/assets/templates/blocks/components/Hooks/useInputStatusIconHookUsage.tsx +1 -0
- package/assets/templates/blocks/components/Hooks/useInteractiveRoleHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Hooks/useInteractiveRoleHookUsage.tsx +56 -0
- package/assets/templates/blocks/components/Hooks/useStreamingTextHookUsage.doc.mjs +2 -2
- package/assets/templates/blocks/components/Hooks/useStreamingTextHookUsage.tsx +66 -10
- package/assets/templates/blocks/components/Link/useLinkifyHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Link/useLinkifyHookUsage.tsx +39 -0
- package/assets/templates/blocks/components/PowerSearch/usePowerSearchConfigHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/PowerSearch/usePowerSearchConfigHookUsage.tsx +83 -0
- package/assets/templates/blocks/components/Table/RowsVirtualizationHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/RowsVirtualizationHookUsage.tsx +41 -0
- package/assets/templates/blocks/components/Table/useTableColumnSettingsStateHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/useTableColumnSettingsStateHookUsage.tsx +97 -0
- package/assets/templates/pages/table-filter/page.tsx +15 -23
- package/assets/templates/pages/table-grouped/page.tsx +30 -30
- package/assets/templates/pages/table-inbox/page.tsx +3 -6
- package/assets/templates/themes/butter/butterTheme.ts +1 -1
- package/assets/templates/themes/gothic/gothicTheme.ts +16 -0
- package/assets/templates/themes/matcha/matchaTheme.ts +1 -1
- package/assets/templates/themes/neutral/neutralTheme.ts +6 -0
- package/assets/templates/themes/y2k/y2kPaletteRefs.generated.ts +1 -1
- package/assets/templates/themes/y2k/y2kTheme.ts +1 -1
- package/clients/cli/commands/docs.doc.mjs +5 -1
- package/clients/cli/commands/docs.mjs +30 -2
- package/clients/cli/commands/experimental/icon.mjs +25 -4
- package/clients/cli/commands/experimental-render.doc.mjs +1 -1
- package/clients/cli/commands/template.mjs +204 -181
- package/clients/cli/lib/resolve-theme.mjs +16 -7
- package/foundation/discovery/component-discovery.mjs +1 -1
- package/foundation/discovery/hook-discovery.mjs +31 -8
- package/foundation/discovery/icon-set-writer.d.mts +21 -5
- package/foundation/discovery/icon-set-writer.mjs +114 -13
- package/foundation/discovery/template-adapter.mjs +2 -2
- package/foundation/doc-compiler/import.d.mts +5 -1
- package/foundation/doc-compiler/import.mjs +40 -6
- package/foundation/fs/import-url.d.mts +20 -0
- package/foundation/fs/import-url.mjs +22 -0
- package/foundation/fs/module-loader.d.mts +24 -3
- package/foundation/fs/module-loader.mjs +69 -47
- package/foundation/render/charts.d.mts +59 -0
- package/foundation/render/charts.mjs +216 -0
- package/foundation/render/embedded-imports.d.mts +7 -7
- package/foundation/render/embedded-imports.mjs +63 -54
- package/foundation/render/page-host.d.mts +4 -0
- package/foundation/render/page-host.mjs +45 -0
- package/foundation/render/page-worker.mjs +350 -57
- package/foundation/render/render-page.d.mts +11 -6
- package/foundation/render/render-page.mjs +164 -54
- package/foundation/render/ts-paths.d.mts +63 -0
- package/foundation/render/ts-paths.mjs +138 -0
- package/foundation/response/error-codes.doc.mjs +3 -3
- package/foundation/response/error-codes.mjs +3 -2
- package/foundation/text/levenshtein.d.mts +6 -1
- package/foundation/text/levenshtein.mjs +25 -9
- package/foundation/text/suggest-order.d.mts +8 -1
- package/foundation/text/suggest-order.mjs +72 -6
- package/foundation/xle/registry-core.mjs +1 -1
- package/package.json +7 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,112 @@
|
|
|
1
1
|
# @xds/cli
|
|
2
2
|
|
|
3
|
+
# 0.7.12
|
|
4
|
+
|
|
5
|
+
#### Experimental APIs
|
|
6
|
+
|
|
7
|
+
- `discover` lists the Lab ChatVirtualizer among the official `@astryxdesign/lab` components. (#5461)
|
|
8
|
+
- `astryx discover` lists the experimental TabBar and TabBarItem components from `@astryxdesign/lab`. (#9700)
|
|
9
|
+
- `integration check` and `experimental icon new-set` follow `export const X = Y` to where `Y` is declared: an icon-set theme map or icon export written that way is now checked and recorded. (#10471)
|
|
10
|
+
- Icon sets: when a wrapped library ships no license file, `integration check` on an icon set, `experimental icon new-set --from`, and a refresh name the NOTICE file to add and where, and the icon-sets guide lists the file names that count. A refresh from a library root moves icons added one file at a time from that library to the root import, keeping their ids, exports, map entries, variants, and words, and the receipt lists them under `moved`. Each keeps that id and export on every later refresh, and refreshing again changes nothing. A refresh keeps an id you chose that is not a spelling of the library name (`dismiss` for `XIcon`), also for an icon that already reads the root; it renames only an id the current rule spells differently. A library icon whose id or name a moved icon already has is left out. An icon whose root export another icon of the set already reads stays on its own import, and the receipt says why. A variant from another file of the package, such as a solid look, stays on its own import, and every export the refresh does not write again stays in the generated module. A `LICENSE` directory counts as a license notice in neither the receipt nor the check. (#10458)
|
|
11
|
+
- Icon sets: wrapping a large icon library that names its looks in its exports works in seconds and gives one icon per glyph. Only the experimental icon commands, the icon-set guide, and `integration check` on an icon set change. (#8013)
|
|
12
|
+
|
|
13
|
+
- **Large libraries.** `experimental icon new-set --from` reads a library of 20,000 exports in about ten seconds instead of minutes, and in text mode says on stderr that it is reading and what it found. `iconNewSet` and `iconAdd` report reading progress for large modules through the shared `logger` (stderr, text mode only); called directly, they stay silent until `logger.setSilent(false)`.
|
|
14
|
+
- **Libraries of looks.** A library whose export names end in style, size and weight words (`XOutline24WGHT500Icon`), never direction words like `Up` and `Down`, gives one icon per glyph, named `x` (`XIcon`), in the look the set's default variant names, else the library's usual default; suggestions and `add` name icons by glyph, `add --variant filled --all` picks each glyph's filled look, a refresh keeps every icon an earlier release wrapped, and a refresh with a new `--default-variant` moves each glyph to that look. Every command that records the default variant takes it from the look each icon's export draws, refuses a look word the library does not have, and never adds a second export of an icon.
|
|
15
|
+
- **Variants on an existing set.** `experimental icon new-set <set> --variants outline,filled` without `--from` declares variants (a set with icons keeps its default variant, or, with none declared, takes the look its icons draw; a set whose library's export names name no looks keeps it on a refresh too, refuses `--variants` that leave it out, and the refusal names the `add --variant` run that adds the other look), and `--display-name` and `--description` rewrite the descriptor, in place; `add --variant` names an undeclared variant with that command.
|
|
16
|
+
- **Commands that run as printed.** Every command `new-set`, `add` and `map` print names the set, with `--set` where the command takes it; the commands `add` lists repeat `--default` and `--id`; `--new-definition` and `new-definition` take kebab-case as the definitions list shows it, and an unknown `--definition` suggests a name the next command takes.
|
|
17
|
+
- **Search.** `experimental icon star` no longer offers `start`, a plural matches its singular, `go home` finds the icon mapped to `home`, and a letter or two never matches inside a word.
|
|
18
|
+
- **Checks.** A set's own definitions pass `integration check --strict`, and the generated module and its declaration file pass after a formatter rewrites them.
|
|
19
|
+
- **Theme icons.** Each `themeIcons` entry is sized to its box by CSS, so a component that writes its own size attributes still fills the theme's icon box.
|
|
20
|
+
|
|
21
|
+
- `astryx experimental render` matches `compilerOptions.paths` keys and targets by TypeScript's rules, from the project's own tsconfig.json or jsconfig.json (a config it `extends` is not read). Newly, a `*` may sit anywhere in a key or a target (`"theme-*-x"`, `"./icons/icon-*"`), and a key's targets are tried in order, so a module, stylesheet, image or JSON file under a later target is found; `--reverse` puts a file back under the target its sent path names (the one whose folders match the most of the path above the file, a target with text before its `*` included), else under the first target. Only bare specifiers are matched, an import whose targets do not exist resolves as a package, and a catch-all `"*"` key never turns a missing package into a path error. A module the page loads from outside its own files and outside `node_modules` (a sibling package's source, say) resolves a bare import through the same aliases first, as the page's own files and TypeScript do, so the page and that module load one copy of a package; an import no alias places resolves as a package, so an installed package that a prefix key also names (`"@scope/*"` and `@scope/realpkg`) still loads. `--reverse` of a rendered file that lost its embedded source on the way (and has no Astryx components to rebuild from) now fails with `ERR_RENDER_SOURCE_DAMAGED`, saying so, instead of `ERR_RENDER_NOT_ASTRYX`. (#8012)
|
|
22
|
+
- Icon sets: an icon whose name would start with a digit once the library prefix comes off, such as a library's `fa0`, now keeps that prefix: it gets id `fa-0` and is exported as `Fa0Icon` (`fa500px` becomes `Fa500pxIcon`). The CLI wrote `0Icon` before, which is not a valid JavaScript name, so the generated module did not parse. An id you give that starts with a digit, with no prefix to keep, is refused with a message that asks for an id that starts with a letter. Only `experimental icon new-set --from` and `experimental icon add` change; a set they write now passes `integration check`. (#10895)
|
|
23
|
+
- Icon sets: `experimental icon add --variant <look> --from <module> --all` now works for a library that ships each look as its own package, such as Font Awesome's solid and regular packages of icon data objects. Before, it found no icon to give the variant, because every icon came from another package. An icon gains the variant from the export of the same name and kind, and the receipt lists as missing the icons of that library that the look's package does not export. Wrapping such a library also no longer turns a pack export (`fas`, an object of the other exports) into an icon, so the shared `fa` prefix comes off the names again. A name that would then start with a digit keeps the prefix (`fa0` becomes `Fa0Icon`), so the generated module still parses. Only the experimental icon commands and `integration check` on an icon set change. (#10896)
|
|
24
|
+
- Icon sets: after you rename an icon-set package in package.json, the next `experimental icon` command that writes the data file (`new-set` on the existing set, with or without `--from`, `add`, `map`, or `new-definition --icon`, even one that changes nothing else) writes the new package name to the data file's `module`, and keeps any subpath (`@acme/old-icons/outline` becomes `@acme/new-icons/outline`). Before, the old name stayed, and `integration check` failed until you edited the file by hand. The receipt of each of these commands names the new and the old `module` (`Updated module to "@acme/new-icons/outline" (was "@acme/old-icons/outline"), from the package name in package.json.`), and `map` words each `map` entry from what it replaced, so an entry that did not change says so. Only the experimental icon commands and the icon-sets guide change. (#10897)
|
|
25
|
+
- `astryx experimental render` draws recharts charts. A recharts chart draws only after its effects run, and a `ResponsiveContainer` measures its parent in a browser, so a rendered page showed an empty box for every chart. The render now draws each chart and `ResponsiveContainer` a page imports from recharts by name, after the server render, in a small document with no layout. A container starts at its own width and height, or at a width that follows its height, and the drawing scales to the width the page gives it. The `chart-size` warning counts the charts the render could not draw, and says when charts were drawn by themselves (when the page cannot be drawn again without a browser, or its effects change its charts or re-render the component that makes them). Charts show the data and props of the page's first render; a value a chart's parts read from a React context that an effect changes may show its later value. (#10382)
|
|
26
|
+
- `exportSearchIndex()` and `rankSearchIndex()` from `@astryxdesign/cli/api` store and rank search's candidates. `exportSearchIndex({cwd, package})` returns `{version, candidates}`: what `search()` ranks in a folder, as plain JSON (with `package`, only that package's entries). `rankSearchIndex(query, indexes, {type, limit})` ranks stored indexes with search's own tree: an index made in a project answers as `search(query)` does there, with the same response; for an index made outside a project, theme results name `theme add` where `search` there names `theme eject`. A catalog that lists packages it must not load makes the index where loading is safe and ranks it per query. An index is ranked only by the CLI version that made it. Both are experimental (they may change in a patch release), and their docs are not search results, so `search` and `build` answer exactly as before. When they are promoted to stable, their docs join search. (#10375)
|
|
27
|
+
|
|
28
|
+
#### New Features
|
|
29
|
+
|
|
30
|
+
- `astryx discover` lists the Lab DialogHeroHeaderShowcase template from `@astryxdesign/lab`. (#10109)
|
|
31
|
+
- `astryx discover` lists the Lab DialogHeroHeader component from `@astryxdesign/lab`. (#10108)
|
|
32
|
+
- BottomSheet: at rest, a sheet's content starts below the grab bar whatever its `padding`, so the first line is no longer drawn through the bar's fade. The block-start inset is never less than the bar's height (`--spacing-6`): the band under the bar is not padding a child escapes, so a lone `Section` starts below the bar too; scrolled content still passes beneath it. A sheet at the default padding starts its content 8 px lower, one with `padding={0}` starts it below the bar, and one whose only child is a `Section` starts that Section 24 px lower and grows by the same amount. Core's own `padding={0}` sheets (the touch date and time pickers, the DropdownMenu and ContextMenu sheets, and the PowerSearch and Tokenizer sheets) no longer reserve that band themselves, so the pickers, PowerSearch and Tokenizer look the same and a menu's first row sits 8 px lower. The CLI's `table-filter` and `table-inbox` page templates drop their own band too, so it is not doubled. (#8558)
|
|
33
|
+
- ChatLayout keeps its composer on top of the software keyboard where the visual viewport shrinks for it, instead of leaving it underneath until the browser scrolls the whole page. In Chrome on Android the dock rides Gboard's measured slides up and down, in step with the keyboard. In Safari on iOS (in the browser and as a home-screen app) and in Firefox for Android, it moves above the keyboard once the browser reports it. Where a page sets `interactive-widget=resizes-content`, Chrome shrinks the page for the keyboard itself and the composer stays above it without moving. A conversation at its newest message stays there, and the rest of the transcript can still be scrolled above the composer. On a touch screen, in a top-level page, every way into the composer's text (a tap, the keyboard's previous and next buttons, Tab, the input's `focus()`) reaches it without the page scrolling; under a mouse or inside an iframe the browser's own focus applies. In a top-level page on a touch screen, the composer's wrapper (without the scroll-to-bottom button) is a group with no name. When the keyboard's Done ends typing, focus rests there (it is no tab stop), so the next tap or Tab starts from the composer; it shows the focus ring after keyboard input only. A tap that focuses the composer still raises its click once. Under a mouse or inside an iframe, the wrapper is a plain container. Under Reduce Motion the composer never travels with Gboard: it cross-fades to its place above the keyboard. Under a mouse, with no software keyboard, nothing changes. The browser-support guide's section on the on-screen keyboard covers ChatLayout's composer. (#8176)
|
|
34
|
+
- `astryx` lists a `create…` factory documented as a hook, such as `createReorderable`, with the hooks. (#8139)
|
|
35
|
+
- `astryx docs` also opens a docs-tree node by its bare name, or by a route that drops the prefix its name repeats, when exactly one node matches: `astryx docs tokens-color` and `astryx docs tokens/color` both open `tokens/tokens-color`. Two or more nodes answering one name still fail with the suggestions they always listed, and an experimental doc still opens only by its marked route. (#10387)
|
|
36
|
+
- `astryx docs --depth <levels>` now names the docs below each namespace on the topic list, by the route that opens them, so one read learns every route instead of one read per namespace. Without `--depth` the list is unchanged, and the JSON keeps its shape: the new `children` sit inside each `meta.namespaces` entry. (#10393)
|
|
37
|
+
- The official-packages discovery list now includes the Lab `LineClamp` component, so `astryx` can find and describe it. (#4259)
|
|
38
|
+
- Tables can window fixed-height flat data: pass `useTableRowsVirtualization({rowHeight, initialViewportHeight})` to `Table.virtualization`. Table keeps the complete data and row identity, mounts only the rows in its viewport plus a few overscan rows, keeps native table semantics with full-table row positions, keeps the focused row mounted, and falls back to rendering every row for content or geometry it cannot window. Tables that do not opt in are unchanged. (#8280)
|
|
39
|
+
- Table plugins can now read where a row sits. Table passes each body row and cell transform an immutable `TableRowContext` (the row's key, its zero-based `logicalIndex` in the complete data, the row and header counts, and the row model). It passes the scroll-wrapper transform a `TableRenderContext` with a stable `viewport` reference. A plugin declares `rowModel: 'flat'` when its body transforms keep one row per data item. `useTableSelection` and `useTableStickyColumns` declare it. `useTableRowIndex()` without options now numbers rows from the context (passing `data` still numbers from that array), and rows that move re-render only for plugins that read the index. Read context values during the transform; a later read from a callback warns in development. The CLI's numbered-rows block no longer passes `data`. (#10171)
|
|
40
|
+
- Replace the theme showcase's custom layout, text and icon styling with Astryx components. The embedded preview paints the theme's body behind card-token surfaces instead of a surface-colored card band. The new standalone top-navigation shell uses the theme's surface canvas; the embedded view stays shell-free. Make demo inputs, cart feedback, inventory filters and chat respond locally, with clearable filters and no payment submission. Keep the template id, gallery visibility and demo content. (#10404)
|
|
41
|
+
|
|
42
|
+
#### Fixes
|
|
43
|
+
|
|
44
|
+
- On Android, text follows the reader's system font scale in every theme with no setup, in browsers that report `env(preferred-text-scale)` (verified on Chromium and on Chrome stable). The root `Theme` reads `env(preferred-text-scale)` and scales every font size by it, as it already follows Dynamic Type on iPhone. Before, the system font scale reached Astryx text only when the page added `<meta name="text-scale" content="scale">`. A page with `content="scale"` keeps working as it did: its root font size carries the scale, and the root `Theme` adds none. Browsers without either see no change. `astryx docs typography/type-scale` describes it. (#10727)
|
|
45
|
+
- Comments, a codemod note and the grouped-table template's sample data no longer use internal-format identifiers. (#10958)
|
|
46
|
+
- `astryx swizzle --list` no longer lists Core's shared folders (`animator`, `gestures`, `i18n`, `internal`, `keyboard`, `motion`, `privateAttributes`, `__tests__`) as components. A copy of `gestures` imports `@astryxdesign/core/motion`, which the core package does not export. (#10185)
|
|
47
|
+
- `astryx component` prints a theme warning as one line: control and format characters in the configured `astryx.theme` value or the generated theme record print as `�`, so a newline or an escape sequence in a checkout cannot rewrite the terminal. (#10764)
|
|
48
|
+
- `astryx docs <topic>` reads a topic authored as `.doc.ts` from an installed integration on every supported Node, and from a linked integration below Node 22.18. Topic and docs-tree files now load `.ts` through the same TypeScript loader as component docs, where a plain import failed under `node_modules` ("Stripping types is currently unsupported") and below Node 22.18 ("Unknown file extension .ts"). `astryx search` also reads an installed `.doc.ts` component doc, so it finds the component by its keywords, shows its description, and lists a family's members. `.doc.mjs` topics load as before. (#10763)
|
|
49
|
+
- A long-running process that calls the CLI's API, such as a docs site's search route, now reads an edited CommonJS `.doc.js` file again in docs, search and template reads. It read the doc as first loaded. (#10760)
|
|
50
|
+
- A command that reads a project module again after changing it, such as `astryx integration add` checking the manifest it just wrote, now reads the change for an ES module `.js` file in a package without a `"type"` field. It read the module as first loaded. (#10745)
|
|
51
|
+
- Gothic: accent text and accent icons stay readable on a light media surface. They used Gothic's light accent there, near-white on a light band (1.14:1); now they follow the surface's dark accent (18.7:1 against a white surface). The CLI's Gothic theme template carries the same change. (#10852)
|
|
52
|
+
- Icon sets: `integration check` now accepts a generated declaration file (`*.generated.d.ts`) whose long type arguments a formatter such as Prettier re-wrapped. Before, the re-wrapped line made the check report `icon_generated_types_mismatch` and fail. A changed declaration still fails the check. The experimental icon commands are unchanged. (#10898)
|
|
53
|
+
- Neutral: an unselected SegmentedControl label meets 4.5:1 in dark mode on AppShell's surface. The track is translucent, so it lightens the surface under the label: `#9e9e9e` on `#3c3c3c` measured 4.11:1. The label now takes `neutral.dark[80]` (6.46:1), the same lift Neutral already gives secondary text on its selected rows and pills. Light mode is unchanged, and the CLI's Neutral theme template matches. (#10881)
|
|
54
|
+
- `astryx build`: when an idea names a family only after its head ("a saml for a login") and the family's base template matches no other word, the variant that outranks the base now stays the start (`login-sso`, not `login`), so adding page templates no longer flips such ideas to the base. Ideas that name the family in their head ("saml login") start as before. (#10431)
|
|
55
|
+
- `astryx experimental render` no longer dies silently with exit 139 when Node itself crashes while it renders a page. The page now renders in a separate process, so a crash (a crash signal, or a crash status on Windows) ends only that process, and the render fails with `ERR_RENDER_FAILED` and a message that names the Node version and path; a signal that stops it names them too. That process ends when the CLI ends, so a page that never finishes does not outlive a stopped CLI. Some Node builds crash in `Intl.Segmenter`, which components such as Avatar use to split text: on such a build the render runs the page once more without `Intl.Segmenter` and adds a `node-crash` warning that names the Node build and says the output may differ slightly (a joined emoji, a flag, or a letter with a separate accent mark in initials), so pages with Avatar render again. (#10915)
|
|
56
|
+
- `astryx search` puts the results that fit the whole query higher. The first result stays the same. At the default limit, and at any limit of 10 or more, the set of results stays the same. A smaller `--limit` shows the top of the new order, so it can show a different result. Below the first result, inside each group of equally strong matches, a result whose docs fit the query much worse than the others in its group moves after them. A result whose name spells two or more of the query's words never moves down; it can still move up. The fit uses the same word-meaning weights `astryx build` uses to pick blocks, so a new component or template is placed from its own docs. A query that is an entry's whole name keeps its answer. (#10926)
|
|
57
|
+
- Every import in an `astryx swizzle` copy now names a file the copy holds or a path the owner package exports. Before, the copy sent every import that left the component folder to the package's folder entry, which does not export the internal helpers most components use, so `Button`, `Dialog`, `Table` and about 100 other copies failed to type-check or bundle. Now the copy imports what the package exports from the package, carries every internal module it needs beside it in the package's folder layout, and copies the nested folders it imports (such as `Table`'s `plugins/`). Test helpers and notes are no longer copied. A helper an earlier swizzle wrote is reused when it is the same and is never replaced by a copy that carries it, even with `--overwrite`: a different one stays, and the new copy writes its own under a version-suffixed name. Swizzling the component whose folder holds that helper treats it as one of that component's own files, which `--overwrite` replaces. A private attribute owner module and the style modules it imports are the exception, as before: a changed copy fails the command without `--overwrite` and is replaced with it. Fixes #3506. (#10197)
|
|
58
|
+
- A swizzled component whose source imports another Astryx package's internal module, such as a Lab component that uses Core's gesture primitives, no longer imports that private path, except a StyleX module that package exports, which the copy keeps importing by that path because its identity is its path. Swizzle carries every other such module, and what it reaches, into the copy under a folder named after its package (for example `components/astryx/core/internal/touch.ts`); that package's public names are still imported from the package. (#10595)
|
|
59
|
+
- `astryx swizzle` reports the npm packages a copy imports, such as `d3-scale` for a chart, with the version range the owner package declares, so the copy builds once you install them. The `swizzle.copy` receipt lists them in `dependencies`, and the text output names them to install. Node's built-in modules and the owner package itself are not listed. (#10502)
|
|
60
|
+
- `astryx swizzle` writes an `astryx-swizzle.json` marker into an output folder it owns (one the copy creates, an empty one, or one it marked before), naming each package the folder's copies came from and its version. `@astryxdesign/build`'s Babel, PostCSS and Vite integrations, and `withAstryx()` under webpack, compile the copies under that folder below `astryx-theme`, so a theme's component overrides reach a swizzled component as they reach the package component. For a folder that already holds other files it writes no marker and says what it found and how to add the marker by hand; the `swizzle.copy` receipt reports this in a new optional `marker` field. Under Turbopack and with the stock StyleX plugins, a copy's styles still land above the theme. The marker is not listed among the copy's files. (#10483)
|
|
61
|
+
- The `table-filter` template's link-style header actions set `minHeight: 0` with their auto height, so no minimum holds them taller than their text. (#10588)
|
|
62
|
+
- `astryx template <name>` puts the template a typo slips from first in its suggestions: a name one or two slips away (a letter added, dropped or changed, or two letters swapped, as in `AppShellCotnentOnly` or `dasbhoard`) ranks just below the templates that carry every typed word. The suggestion list is never longer than before. (#10712)
|
|
63
|
+
- In Safari on iPhone, text follows the reader's text size (Dynamic Type) in every theme, branded included. Where the browser exposes that size through `font: -apple-system-body`, as Safari on iPhone does, the theme's font sizes scale with the size the reader picked in Settings (Display & Text Size, then Larger Text), each size on the curve of the iOS text style it plays: body text like Body, `heading-1` like Title 1, display text like Large Title, with every role kept in order. A semantic size a theme fixes at a length, such as the 0.75rem supporting text of Butter, Stone and Y2K, scales on the curve of the step it reads by default. At the default size every font size is the theme's own; the family, spacing, line-height ratios and the root font size never change, text inputs keep their 16px floor on iOS, and browsers without the keyword see no change. There is no option, prop or document attribute: the root `Theme` reads the reader's size from a hidden element. `astryx docs typography/type-scale` describes it. (#10416)
|
|
64
|
+
- Matcha: light-mode secondary text is slightly darker (Neutral Light 30), so it meets 4.5:1 on the body (5.11:1, was 3.83:1), the surface and the card. (#10932)
|
|
65
|
+
[fix] Y2K: light-mode secondary text is slightly darker (Neutral Light 35), so it meets 4.5:1 on the Periwinkle body (5.14:1, was 4.25:1). The CLI's Matcha and Y2K theme templates match.
|
|
66
|
+
- The Butter, Stone and Y2K themes set supporting text (`--text-supporting-size`) in rem instead of a fixed 12px, so it grows when a reader raises the browser's default font size, like the rest of the text. At the default size it is still 12px. The CLI's copies of these themes match. (#10320)
|
|
67
|
+
|
|
68
|
+
#### Documentation
|
|
69
|
+
|
|
70
|
+
- A new `Course Detail` page template: a long-form sales page for a single offering in two columns — the long read (a preview poster with a fact row, an about block with skill chips, a module syllabus whose sections collapse module by module, an instructor card, reviews with a scored-aspects panel, and a FAQ) beside a sticky enroll rail (the price with a compare-at, a self-paced or cohort start picker, and a weekly-pace slider that computes a finish date). Copy it with `astryx template course-detail <path>`. (#9753)
|
|
71
|
+
- The File Explorer page template scrolls its columns with `StackItem` and `HStack` `isScrollable` instead of custom overflow and flex styles, and names each column after the folder it lists while the column scrolls. Its chevrons now mirror in right-to-left, and a detail pane scrolled to its end keeps its bottom padding. (#10421)
|
|
72
|
+
- Hook docs for `useCalendarConstraints`, so `astryx hook useCalendarConstraints` and `astryx search` return it, and a `useCalendarConstraints — Hotel Booking Range` example block: a booking range picker that enforces min/max dates, disables closed Mondays, and caps reservations at 7 nights once check-in is selected. Copy it with `astryx template useCalendarConstraintsHookUsage <path>`. (#10151)
|
|
73
|
+
- Hook docs for `useCalendarNavigation`, so `astryx hook useCalendarNavigation` and `astryx search` return it, and a `useCalendarNavigation — Month Pager` example block: a month-by-month calendar with Previous/Next controls, localized month-year header, and cross-month arrow key navigation using `pendingFocus`. Copy it with `astryx template useCalendarNavigationHookUsage <path>`. (#10154)
|
|
74
|
+
- Hook docs for `useLinkify`, so `astryx hook useLinkify` and `astryx search` return it, and a `useLinkify — Links in a Comment Thread` example block: plain-text comments where the URL and email become Links and a custom `#123` issue-reference pattern shows the patterns option, while a comment without matches stays plain. (#9969)
|
|
75
|
+
- Add usePowerSearchConfig hook docs and a filtered directory table example block (#10660)
|
|
76
|
+
- Add useTableColumnSettingsState hook docs and a column visibility picker example block (#10652)
|
|
77
|
+
- A `useInteractiveRole — One Tile, Four Roles` example block for the existing hook docs: a polymorphic Tile rendered four ways — href link, onClick button, Popover-trigger context button, inert content — showing the resolution order in one place. (#9972)
|
|
78
|
+
- Fix the `useStreamingText — Streaming Response` example block: it passed `isStreaming` as `false`, so the streaming reveal it exists to show never ran. The block is now a chat-style reply card with a Regenerate button that streams the response in bursts — the hook reveals it steadily while streaming and snaps to the full text when it ends. (#9975)
|
|
79
|
+
- `RichTextView`, `RichTextEditorToolbar` and `RichTextEditorAutoLinkPlugin` each ship a doc file, so `astryx component`, `astryx discover` and the docs list their props, defaults and usage. (#10407)
|
|
80
|
+
- Tab's docs show how to give a tab help text: anchor a `Tooltip` on the `Tab` through its ref, so the description lands on the tab, and put a decorative info `Icon` in `endContent`. `endContent` is documented as non-interactive: a button, link, or `InfoTip` inside it would nest a control inside the tab's button. (#10734)
|
|
81
|
+
- The grouped table page template groups its rows with `useTableGroupedRows` instead of hand-rendered header rows and a hand-written `colgroup`. Its columns now have headers, each group's toggle is a button that reports whether its group is expanded, and the row menu's Move to project arrow mirrors in right-to-left. (#10423)
|
|
82
|
+
- The Tailwind guide (`astryx docs styling-libraries/tailwind`) adds a recipe for an app that already uses Tailwind: keep the app's own `@import "tailwindcss"`, put the `@layer` line above it, and add the Astryx stylesheets after it. It explains why the `@layer` line must come before the Tailwind import: below it, the Astryx reset outranks every utility class, and nothing reports it. The Core README's Next.js + Tailwind section points to it. (#10532)
|
|
83
|
+
- The `useInputStatusIcon — Coupon Code Status` example block's field applies `inputWrapperStyles.textField`. A field copied from it now shows its focus look at once when the primary pointer is coarse, as Core's text fields do. (#10649)
|
|
84
|
+
|
|
85
|
+
#### Other Changes
|
|
86
|
+
|
|
87
|
+
- Internal: doctor.mjs and template.mjs are formatted with Prettier. No change to any command's output. (#10988)
|
|
88
|
+
- Internal: the icon-set export reader reuses the shared top-level helper, and a source comment on look refreshes is shorter. No change to any command's output. (#10449)
|
|
89
|
+
- Internal: the doc importers import each doc file through one small module that the CLI's own test project loads natively, so its tests no longer time out on a busy machine. No change to any command's output. (#10740)
|
|
90
|
+
|
|
91
|
+
#### Contributors
|
|
92
|
+
|
|
93
|
+
Thanks to everyone who contributed to this release:
|
|
94
|
+
|
|
95
|
+
- @bhamodi
|
|
96
|
+
- @cixzhang
|
|
97
|
+
- @HelloOjasMutreja
|
|
98
|
+
- @humbertovirtudes
|
|
99
|
+
- @jiunshinn
|
|
100
|
+
- @josephfarina
|
|
101
|
+
- @LetMeSleep8h
|
|
102
|
+
- @mego1123
|
|
103
|
+
- @rubyycheung
|
|
104
|
+
- @thecodebasedot
|
|
105
|
+
- @utkarshpatwa393-sys
|
|
106
|
+
- @vjeux
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
3
110
|
# 0.7.11
|
|
4
111
|
|
|
5
112
|
#### New Features
|
|
@@ -22,6 +22,47 @@ export function wordsOf(text: string): string[];
|
|
|
22
22
|
* @returns {string[]}
|
|
23
23
|
*/
|
|
24
24
|
export function termsOf(text: string): string[];
|
|
25
|
+
/**
|
|
26
|
+
* A text's docs side for `matchOf`: its word vector and that vector through
|
|
27
|
+
* the doc matrix. Search scores its entries' own docs with it.
|
|
28
|
+
* @param {string} text
|
|
29
|
+
* @param {BlockPickerWeights} weights
|
|
30
|
+
* @returns {{vector: Map<number, number>, projected: Float64Array}}
|
|
31
|
+
*/
|
|
32
|
+
export function docSideOf(text: string, weights: BlockPickerWeights): {
|
|
33
|
+
vector: Map<number, number>;
|
|
34
|
+
projected: Float64Array;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* A request's side for `matchOf`: its word vector and that vector through the
|
|
38
|
+
* request matrix.
|
|
39
|
+
* @param {string} request
|
|
40
|
+
* @param {BlockPickerWeights} weights
|
|
41
|
+
* @returns {{vector: Map<number, number>, projected: Float64Array}}
|
|
42
|
+
*/
|
|
43
|
+
export function requestSideOf(request: string, weights: BlockPickerWeights): {
|
|
44
|
+
vector: Map<number, number>;
|
|
45
|
+
projected: Float64Array;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The picker's match of a request to a docs text: their projections' dot
|
|
49
|
+
* product plus the lexical weight times their plain overlap (the cosine of
|
|
50
|
+
* their word vectors). Build's ranking also reads the overlap alone.
|
|
51
|
+
* @param {{vector: Map<number, number>, projected: Float64Array}} request
|
|
52
|
+
* @param {{vector: Map<number, number>, projected: Float64Array}} doc
|
|
53
|
+
* @param {BlockPickerWeights} weights
|
|
54
|
+
* @returns {{match: number, overlap: number}}
|
|
55
|
+
*/
|
|
56
|
+
export function matchOf(request: {
|
|
57
|
+
vector: Map<number, number>;
|
|
58
|
+
projected: Float64Array;
|
|
59
|
+
}, doc: {
|
|
60
|
+
vector: Map<number, number>;
|
|
61
|
+
projected: Float64Array;
|
|
62
|
+
}, weights: BlockPickerWeights): {
|
|
63
|
+
match: number;
|
|
64
|
+
overlap: number;
|
|
65
|
+
};
|
|
25
66
|
/**
|
|
26
67
|
* The blocks build names for a request, ranked by the picker.
|
|
27
68
|
* @param {string} request
|
|
@@ -163,34 +163,10 @@ export function termsOf(text) {
|
|
|
163
163
|
return out;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
/**
|
|
167
|
-
* The weights, decoded once per weights object.
|
|
168
|
-
* @type {WeakMap<BlockPickerWeights, {index: Map<string, number>, idf: Float64Array, request: Int8Array, doc: Int8Array}>}
|
|
169
|
-
*/
|
|
170
|
-
const decoded = new WeakMap();
|
|
171
|
-
|
|
172
|
-
/** @param {BlockPickerWeights} weights */
|
|
173
|
-
function decode(weights) {
|
|
174
|
-
let d = decoded.get(weights);
|
|
175
|
-
if (!d) {
|
|
176
|
-
// Each byte is a signed weight stored offset by 128.
|
|
177
|
-
const bytes = (/** @type {string} */ s) =>
|
|
178
|
-
Int8Array.from(Buffer.from(s, 'base64'), b => b - 128);
|
|
179
|
-
d = {
|
|
180
|
-
index: new Map(weights.vocab.map((w, i) => [w, i])),
|
|
181
|
-
idf: Float64Array.from(weights.idf, x => x / 1024),
|
|
182
|
-
request: bytes(weights.request),
|
|
183
|
-
doc: bytes(weights.doc),
|
|
184
|
-
};
|
|
185
|
-
decoded.set(weights, d);
|
|
186
|
-
}
|
|
187
|
-
return d;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
166
|
/**
|
|
191
167
|
* A text's sparse unit vector over the vocabulary: (1 + ln count) x idf.
|
|
192
168
|
* @param {string[]} terms
|
|
193
|
-
* @param {
|
|
169
|
+
* @param {{index: Map<string, number>, idf: Float64Array}} d
|
|
194
170
|
* @returns {Map<number, number>}
|
|
195
171
|
*/
|
|
196
172
|
function vectorOf(terms, d) {
|
|
@@ -214,20 +190,116 @@ function vectorOf(terms, d) {
|
|
|
214
190
|
}
|
|
215
191
|
|
|
216
192
|
/**
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
* @
|
|
193
|
+
* The vocabulary index and idf, read once per weights object, and each matrix
|
|
194
|
+
* row decoded when a side first uses it.
|
|
195
|
+
* @type {WeakMap<BlockPickerWeights, {index: Map<string, number>, idf: Float64Array, rows: Map<string, Int8Array>}>}
|
|
196
|
+
*/
|
|
197
|
+
const vocabularies = new WeakMap();
|
|
198
|
+
|
|
199
|
+
/** @param {BlockPickerWeights} weights */
|
|
200
|
+
function vocabularyOf(weights) {
|
|
201
|
+
let v = vocabularies.get(weights);
|
|
202
|
+
if (!v) {
|
|
203
|
+
v = {
|
|
204
|
+
index: new Map(weights.vocab.map((w, i) => [w, i])),
|
|
205
|
+
idf: Float64Array.from(weights.idf, x => x / 1024),
|
|
206
|
+
rows: new Map(),
|
|
207
|
+
};
|
|
208
|
+
vocabularies.set(weights, v);
|
|
209
|
+
}
|
|
210
|
+
return v;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* One row of a byte matrix, decoded from the base64 text alone: four
|
|
215
|
+
* characters hold three bytes, so the row's bytes sit inside the characters
|
|
216
|
+
* from its first byte's group to its last byte's group.
|
|
217
|
+
* @param {BlockPickerWeights} weights
|
|
218
|
+
* @param {'request' | 'doc'} matrix
|
|
219
|
+
* @param {number} i
|
|
220
|
+
* @returns {Int8Array}
|
|
221
|
+
*/
|
|
222
|
+
function rowOf(weights, matrix, i) {
|
|
223
|
+
const {rows} = vocabularyOf(weights);
|
|
224
|
+
const key = `${matrix}:${i}`;
|
|
225
|
+
let row = rows.get(key);
|
|
226
|
+
if (!row) {
|
|
227
|
+
const {dims} = weights;
|
|
228
|
+
const first = Math.floor((i * dims) / 3);
|
|
229
|
+
const last = Math.ceil(((i + 1) * dims) / 3);
|
|
230
|
+
const bytes = Buffer.from(
|
|
231
|
+
weights[matrix].slice(first * 4, last * 4),
|
|
232
|
+
'base64',
|
|
233
|
+
).subarray(i * dims - first * 3, i * dims - first * 3 + dims);
|
|
234
|
+
// Each byte is a signed weight stored offset by 128.
|
|
235
|
+
row = Int8Array.from(bytes, b => b - 128);
|
|
236
|
+
rows.set(key, row);
|
|
237
|
+
}
|
|
238
|
+
return row;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* A text's side for `matchOf` through one matrix: its word vector, and that
|
|
243
|
+
* vector times the matrix rows its words use, scaled.
|
|
244
|
+
* @param {string} text
|
|
245
|
+
* @param {BlockPickerWeights} weights
|
|
246
|
+
* @param {'request' | 'doc'} matrix
|
|
220
247
|
* @param {number} scale
|
|
221
|
-
* @
|
|
248
|
+
* @returns {{vector: Map<number, number>, projected: Float64Array}}
|
|
222
249
|
*/
|
|
223
|
-
function
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
250
|
+
function sideOf(text, weights, matrix, scale) {
|
|
251
|
+
const vector = vectorOf(termsOf(text), vocabularyOf(weights));
|
|
252
|
+
const {dims} = weights;
|
|
253
|
+
const projected = new Float64Array(dims);
|
|
254
|
+
for (const [i, x] of vector) {
|
|
255
|
+
const row = rowOf(weights, matrix, i);
|
|
256
|
+
for (let k = 0; k < dims; k++) projected[k] += x * row[k];
|
|
228
257
|
}
|
|
229
|
-
for (let k = 0; k < dims; k++)
|
|
230
|
-
return
|
|
258
|
+
for (let k = 0; k < dims; k++) projected[k] *= scale;
|
|
259
|
+
return {vector, projected};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* A text's docs side for `matchOf`: its word vector and that vector through
|
|
264
|
+
* the doc matrix. Search scores its entries' own docs with it.
|
|
265
|
+
* @param {string} text
|
|
266
|
+
* @param {BlockPickerWeights} weights
|
|
267
|
+
* @returns {{vector: Map<number, number>, projected: Float64Array}}
|
|
268
|
+
*/
|
|
269
|
+
export function docSideOf(text, weights) {
|
|
270
|
+
return sideOf(text, weights, 'doc', weights.docScale);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* A request's side for `matchOf`: its word vector and that vector through the
|
|
275
|
+
* request matrix.
|
|
276
|
+
* @param {string} request
|
|
277
|
+
* @param {BlockPickerWeights} weights
|
|
278
|
+
* @returns {{vector: Map<number, number>, projected: Float64Array}}
|
|
279
|
+
*/
|
|
280
|
+
export function requestSideOf(request, weights) {
|
|
281
|
+
return sideOf(request, weights, 'request', weights.requestScale);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* The picker's match of a request to a docs text: their projections' dot
|
|
286
|
+
* product plus the lexical weight times their plain overlap (the cosine of
|
|
287
|
+
* their word vectors). Build's ranking also reads the overlap alone.
|
|
288
|
+
* @param {{vector: Map<number, number>, projected: Float64Array}} request
|
|
289
|
+
* @param {{vector: Map<number, number>, projected: Float64Array}} doc
|
|
290
|
+
* @param {BlockPickerWeights} weights
|
|
291
|
+
* @returns {{match: number, overlap: number}}
|
|
292
|
+
*/
|
|
293
|
+
export function matchOf(request, doc, weights) {
|
|
294
|
+
let s = 0;
|
|
295
|
+
for (let k = 0; k < weights.dims; k++)
|
|
296
|
+
s += request.projected[k] * doc.projected[k];
|
|
297
|
+
let overlap = 0;
|
|
298
|
+
for (const [i, x] of request.vector) {
|
|
299
|
+
const y = doc.vector.get(i);
|
|
300
|
+
if (y !== undefined) overlap += x * y;
|
|
301
|
+
}
|
|
302
|
+
return {match: s + weights.lexical * overlap, overlap};
|
|
231
303
|
}
|
|
232
304
|
|
|
233
305
|
/**
|
|
@@ -261,7 +333,6 @@ function readCatalog(catalog, components, weights) {
|
|
|
261
333
|
]);
|
|
262
334
|
const cached = catalogs.get(weights);
|
|
263
335
|
if (cached && cached.key === key) return cached;
|
|
264
|
-
const d = decode(weights);
|
|
265
336
|
const names = components.map(c => c.name).sort((a, b) => b.length - a.length);
|
|
266
337
|
const keywords = new Map(components.map(c => [c.name, c.keywords ?? []]));
|
|
267
338
|
/** @type {Map<string, {component: string, vector: Map<number, number>, projected: Float64Array}>} */
|
|
@@ -276,12 +347,7 @@ function readCatalog(catalog, components, weights) {
|
|
|
276
347
|
wordsOf(component).join(' '),
|
|
277
348
|
(keywords.get(component) ?? []).join(' '),
|
|
278
349
|
].join(' ');
|
|
279
|
-
|
|
280
|
-
blocks.set(b.name, {
|
|
281
|
-
component,
|
|
282
|
-
vector,
|
|
283
|
-
projected: project(vector, d.doc, weights.docScale, weights.dims),
|
|
284
|
-
});
|
|
350
|
+
blocks.set(b.name, {component, ...docSideOf(text, weights)});
|
|
285
351
|
}
|
|
286
352
|
// A component is named by its own words, or by a keyword at most three
|
|
287
353
|
// components share (weighted by how few share it).
|
|
@@ -378,26 +444,12 @@ export function pickBlocks(request, signals, weights) {
|
|
|
378
444
|
/** @type {Map<string, SearchResultEntry>} */
|
|
379
445
|
const entries = new Map();
|
|
380
446
|
if (own.length === 0) return {names: [], entries};
|
|
381
|
-
const d = decode(weights);
|
|
382
447
|
const cat = readCatalog(signals.catalog, signals.components, weights);
|
|
383
|
-
const
|
|
384
|
-
const eq = project(q, d.request, weights.requestScale, weights.dims);
|
|
385
|
-
const dot = (/** @type {Map<number, number>} */ v) => {
|
|
386
|
-
let s = 0;
|
|
387
|
-
for (const [i, x] of q) {
|
|
388
|
-
const y = v.get(i);
|
|
389
|
-
if (y !== undefined) s += x * y;
|
|
390
|
-
}
|
|
391
|
-
return s;
|
|
392
|
-
};
|
|
448
|
+
const side = requestSideOf(request, weights);
|
|
393
449
|
/** @type {Map<string, {match: number, overlap: number}>} */
|
|
394
450
|
const scored = new Map();
|
|
395
|
-
for (const [name, b] of cat.blocks)
|
|
396
|
-
|
|
397
|
-
for (let k = 0; k < weights.dims; k++) s += eq[k] * b.projected[k];
|
|
398
|
-
const overlap = dot(b.vector);
|
|
399
|
-
scored.set(name, {match: s + weights.lexical * overlap, overlap});
|
|
400
|
-
}
|
|
451
|
+
for (const [name, b] of cat.blocks)
|
|
452
|
+
scored.set(name, matchOf(side, b, weights));
|
|
401
453
|
const get = (/** @type {string} */ b) =>
|
|
402
454
|
scored.get(b) ?? {match: -Infinity, overlap: 0};
|
|
403
455
|
const ranked = [...scored.keys()].sort((a, b) => get(b).match - get(a).match);
|
|
@@ -480,7 +532,7 @@ export function pickBlocks(request, signals, weights) {
|
|
|
480
532
|
name,
|
|
481
533
|
package: block.package ?? '',
|
|
482
534
|
score: Math.max(1, Math.round(100 * Math.exp(x.score - max))),
|
|
483
|
-
reason: reasonFor(request, cat.blocks.get(name),
|
|
535
|
+
reason: reasonFor(request, side, cat.blocks.get(name), weights),
|
|
484
536
|
description: block.description ?? '',
|
|
485
537
|
displayName: block.displayName,
|
|
486
538
|
kind: 'block',
|
|
@@ -493,11 +545,11 @@ export function pickBlocks(request, signals, weights) {
|
|
|
493
545
|
/**
|
|
494
546
|
* Why a block fits: the request words that tie it to its docs most.
|
|
495
547
|
* @param {string} request
|
|
548
|
+
* @param {{vector: Map<number, number>, projected: Float64Array}} side the request's side
|
|
496
549
|
* @param {{vector: Map<number, number>, projected: Float64Array} | undefined} block
|
|
497
|
-
* @param {ReturnType<typeof decode>} d
|
|
498
550
|
* @param {BlockPickerWeights} weights
|
|
499
551
|
*/
|
|
500
|
-
function reasonFor(request,
|
|
552
|
+
function reasonFor(request, side, block, weights) {
|
|
501
553
|
if (!block) return 'fits by meaning';
|
|
502
554
|
const raw =
|
|
503
555
|
String(request)
|
|
@@ -507,16 +559,14 @@ function reasonFor(request, block, d, weights) {
|
|
|
507
559
|
const wordOf = new Map();
|
|
508
560
|
for (const w of raw)
|
|
509
561
|
if (!STOP.has(w) && !wordOf.has(stemOf(w))) wordOf.set(stemOf(w), w);
|
|
510
|
-
const q = vectorOf(termsOf(request), d);
|
|
511
562
|
/** @type {[string, number][]} */
|
|
512
563
|
const parts = [];
|
|
513
|
-
for (const [i, x] of
|
|
564
|
+
for (const [i, x] of side.vector) {
|
|
514
565
|
const term = weights.vocab[i];
|
|
515
566
|
if (term.includes('_')) continue;
|
|
567
|
+
const row = rowOf(weights, 'request', i);
|
|
516
568
|
let s = 0;
|
|
517
|
-
|
|
518
|
-
for (let k = 0; k < weights.dims; k++)
|
|
519
|
-
s += d.request[row + k] * block.projected[k];
|
|
569
|
+
for (let k = 0; k < weights.dims; k++) s += row[k] * block.projected[k];
|
|
520
570
|
s =
|
|
521
571
|
x * s * weights.requestScale +
|
|
522
572
|
weights.lexical * x * (block.vector.get(i) ?? 0);
|
|
@@ -271,7 +271,7 @@ useIsomorphicLayoutEffect useIsomorphicLayoutEffect hooks/useAnimatedLayout.doc.
|
|
|
271
271
|
useLayerDismissal useLayerDismissal Layer/Layer.doc.mjs Layer/useLayerDismissal.ts
|
|
272
272
|
useLightbox useLightbox Lightbox/useLightbox.doc.mjs Lightbox/useLightbox.tsx
|
|
273
273
|
useLinkComponent useLinkComponent Link/useLinkComponent.doc.mjs Link/useLinkComponent.ts
|
|
274
|
-
useLinkify useLinkify Link/
|
|
274
|
+
useLinkify useLinkify Link/useLinkify.doc.mjs Link/useLinkify.tsx
|
|
275
275
|
useListFormatter useListFormatter i18n/InternationalizationProvider.doc.mjs i18n/useListFormatter.ts
|
|
276
276
|
useMenuDrillIn useMenuDrillIn DropdownMenu/DropdownMenu.doc.mjs DropdownMenu/useMenuDrillIn.tsx
|
|
277
277
|
useMenuHover useMenuHover hooks/useAnimatedLayout.doc.mjs hooks/useMenuHover.ts
|
|
@@ -282,7 +282,7 @@ useOverlay useOverlay Overlay/useOverlay.doc.mjs Overlay/useOverlay.tsx
|
|
|
282
282
|
useOwnScrollGesture useOwnScrollGesture DateInput/DateInput.doc.mjs DateInput/useOwnScrollGesture.ts
|
|
283
283
|
usePager usePager hooks/useAnimatedLayout.doc.mjs hooks/usePager.ts
|
|
284
284
|
usePointerDragScroll usePointerDragScroll DateInput/DateInput.doc.mjs DateInput/usePointerDragScroll.ts
|
|
285
|
-
usePowerSearchConfig usePowerSearchConfig PowerSearch/
|
|
285
|
+
usePowerSearchConfig usePowerSearchConfig PowerSearch/usePowerSearchConfig.doc.mjs PowerSearch/usePowerSearchConfig.ts
|
|
286
286
|
usePowerSearchSource usePowerSearchSource PowerSearch/PowerSearch.doc.mjs PowerSearch/usePowerSearchSource.ts
|
|
287
287
|
useRenamedProp useRenamedProp hooks/useAnimatedLayout.doc.mjs hooks/useRenamedProp.ts
|
|
288
288
|
useResolvedRequired useResolvedRequired hooks/useAnimatedLayout.doc.mjs hooks/useResolvedRequired.ts
|
|
@@ -298,7 +298,7 @@ useSpeechRecognition useSpeechRecognition Chat/Chat.doc.mjs Chat/useSpeechRecogn
|
|
|
298
298
|
useSwipeAction useSwipeAction Item/Item.doc.mjs Item/useSwipeAction.ts
|
|
299
299
|
useSwitchTouch useSwitchTouch Switch/Switch.doc.mjs Switch/useSwitchTouch.ts
|
|
300
300
|
useTableCellStyles useTableCellStyles Table/Table.doc.mjs Table/useTableCellStyles.ts
|
|
301
|
-
useTableColumnSettingsState useTableColumnSettingsState Table/
|
|
301
|
+
useTableColumnSettingsState useTableColumnSettingsState Table/plugins/columnSettings/useTableColumnSettingsState.doc.mjs Table/plugins/columnSettings/useTableColumnSettingsState.tsx
|
|
302
302
|
useTableSortableState useTableSortableState Table/plugins/sortable/useTableSortableState.doc.mjs Table/plugins/sortable/useTableSortableState.tsx
|
|
303
303
|
useToastGesture useToastGesture Toast/Toast.doc.mjs Toast/useToastGesture.ts
|
|
304
304
|
useTouchTrigger useTouchTrigger Layer/Layer.doc.mjs Layer/useTouchTrigger.ts
|
package/api/docs/_adapter.d.mts
CHANGED
|
@@ -319,6 +319,28 @@ export function nameOwner(tree: DocsTree, catalog: DocsCatalog, name: string): {
|
|
|
319
319
|
kind: "node";
|
|
320
320
|
node: TreeNode;
|
|
321
321
|
} | null;
|
|
322
|
+
/**
|
|
323
|
+
* `stability: 'experimental'` for a node whose doc is marked experimental, or
|
|
324
|
+
* that sits under an experimental namespace (spec:AST-017 FR27), the way help
|
|
325
|
+
* marks an experimental command; nothing for a stable one, so stable entries
|
|
326
|
+
* read as they always have.
|
|
327
|
+
* @param {DocsTree} tree
|
|
328
|
+
* @param {TreeNode} node
|
|
329
|
+
* @returns {{stability?: 'experimental'}}
|
|
330
|
+
*/
|
|
331
|
+
export function stabilityOf(tree: DocsTree, node: TreeNode): {
|
|
332
|
+
stability?: "experimental";
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* The fields every child carries: what it is, and the route that opens it.
|
|
336
|
+
* Shared, so a child reads the same whether a namespace read or the topic
|
|
337
|
+
* list names it — including the package, which is the integration's for a
|
|
338
|
+
* guide an integration replaced, not the package whose route it took.
|
|
339
|
+
* @param {DocsTree} tree
|
|
340
|
+
* @param {TreeNode} child
|
|
341
|
+
* @returns {import('./docs.type.mjs').DocsNodeChild}
|
|
342
|
+
*/
|
|
343
|
+
export function childIdentity(tree: DocsTree, child: TreeNode): import("./docs.type.mjs").DocsNodeChild;
|
|
322
344
|
/**
|
|
323
345
|
* Whether a topic answers to its own name: what the topic list and search
|
|
324
346
|
* offer must open that topic.
|
package/api/docs/_adapter.mjs
CHANGED
|
@@ -1765,9 +1765,87 @@ export function nameOwner(tree, catalog, name) {
|
|
|
1765
1765
|
return entry == null ? null : {kind: 'topic', entry};
|
|
1766
1766
|
}
|
|
1767
1767
|
const entry = catalog.resolve(name);
|
|
1768
|
-
if (entry
|
|
1769
|
-
|
|
1770
|
-
|
|
1768
|
+
if (entry != null) {
|
|
1769
|
+
const owner = routeOwner(tree, entry);
|
|
1770
|
+
return owner == null ? {kind: 'topic', entry} : {kind: 'node', node: owner};
|
|
1771
|
+
}
|
|
1772
|
+
return childOwner(tree, name);
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
/**
|
|
1776
|
+
* The child a reader names without its namespace, or with a route that leaves
|
|
1777
|
+
* out the prefix the child's own name repeats (spec:AST-046 FR5). A namespace
|
|
1778
|
+
* whose children repeat its name makes its own routes stutter: `tokens` holds
|
|
1779
|
+
* `tokens-color`, so the route is `tokens/tokens-color`, while the namespace
|
|
1780
|
+
* listing shows the bare `tokens-color`. Both spellings name one child, so
|
|
1781
|
+
* that child answers. Two or more children answering is ambiguous, and the
|
|
1782
|
+
* read falls through to the suggestion list, unchanged. An experimental doc
|
|
1783
|
+
* never answers to a bare name: its project reaches it only by a marked route
|
|
1784
|
+
* (spec:AST-017 FR27).
|
|
1785
|
+
* @param {DocsTree} tree
|
|
1786
|
+
* @param {string} name
|
|
1787
|
+
* @returns {{kind: 'node', node: TreeNode} | null}
|
|
1788
|
+
*/
|
|
1789
|
+
function childOwner(tree, name) {
|
|
1790
|
+
const wanted = name.toLowerCase();
|
|
1791
|
+
const slash = wanted.lastIndexOf('/');
|
|
1792
|
+
const parent = slash > 0 ? wanted.slice(0, slash) : null;
|
|
1793
|
+
const unstuttered =
|
|
1794
|
+
parent == null
|
|
1795
|
+
? null
|
|
1796
|
+
: `${parent}/${parent.slice(parent.lastIndexOf('/') + 1)}-${wanted.slice(slash + 1)}`;
|
|
1797
|
+
const matches = [...tree.nodes.values()].filter(
|
|
1798
|
+
node =>
|
|
1799
|
+
node.ref?.flatTopic == null &&
|
|
1800
|
+
node.route.includes('/') &&
|
|
1801
|
+
// An experimental doc is reachable only by a route its project marked
|
|
1802
|
+
// as experimental (spec:AST-017 FR27): a bare name never opens one.
|
|
1803
|
+
!isExperimentalTreeNode(tree, node) &&
|
|
1804
|
+
(node.name?.toLowerCase() === wanted ||
|
|
1805
|
+
(unstuttered != null && node.route.toLowerCase() === unstuttered)),
|
|
1806
|
+
);
|
|
1807
|
+
return matches.length === 1 ? {kind: 'node', node: matches[0]} : null;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/**
|
|
1811
|
+
* `stability: 'experimental'` for a node whose doc is marked experimental, or
|
|
1812
|
+
* that sits under an experimental namespace (spec:AST-017 FR27), the way help
|
|
1813
|
+
* marks an experimental command; nothing for a stable one, so stable entries
|
|
1814
|
+
* read as they always have.
|
|
1815
|
+
* @param {DocsTree} tree
|
|
1816
|
+
* @param {TreeNode} node
|
|
1817
|
+
* @returns {{stability?: 'experimental'}}
|
|
1818
|
+
*/
|
|
1819
|
+
export function stabilityOf(tree, node) {
|
|
1820
|
+
return isExperimentalTreeNode(tree, node) ? {stability: 'experimental'} : {};
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
/**
|
|
1824
|
+
* The fields every child carries: what it is, and the route that opens it.
|
|
1825
|
+
* Shared, so a child reads the same whether a namespace read or the topic
|
|
1826
|
+
* list names it — including the package, which is the integration's for a
|
|
1827
|
+
* guide an integration replaced, not the package whose route it took.
|
|
1828
|
+
* @param {DocsTree} tree
|
|
1829
|
+
* @param {TreeNode} child
|
|
1830
|
+
* @returns {import('./docs.type.mjs').DocsNodeChild}
|
|
1831
|
+
*/
|
|
1832
|
+
export function childIdentity(tree, child) {
|
|
1833
|
+
return {
|
|
1834
|
+
route: child.route,
|
|
1835
|
+
name: child.route.slice(child.route.lastIndexOf('/') + 1),
|
|
1836
|
+
package: child.guideOverlay?.replacement?.package ?? child.provider,
|
|
1837
|
+
// A guide an integration's doc took the place of names whose it was.
|
|
1838
|
+
...(child.guideOverlay?.replacement
|
|
1839
|
+
? {
|
|
1840
|
+
replaces: child.route,
|
|
1841
|
+
replacedPackage: child.guideOverlay.replacedPackage ?? child.provider,
|
|
1842
|
+
}
|
|
1843
|
+
: {}),
|
|
1844
|
+
kind: child.kind,
|
|
1845
|
+
title: child.title,
|
|
1846
|
+
summary: child.summary,
|
|
1847
|
+
...stabilityOf(tree, child),
|
|
1848
|
+
};
|
|
1771
1849
|
}
|
|
1772
1850
|
|
|
1773
1851
|
/**
|