Repository navigation
Conversation
Every handler built its group from options itself and passed the readings into shallow positional helpers, so one roving-stop rule was spread over four modules and re-tested through five suites. - Add a Group module (`readGroups` in group.ts). The four readings (root, items, skip, roving) resolve once per options object, with the attribute defaults inside. `at(target, listener)` reads a group from an event and `of(root)` from a root. A group answers lazily for its items, focused item, own items, roving items and stop, and holds the one navigable-item rule: neither skipped nor disabled. - `group.move()` lands a move and decides which item the stop is carried from; `group.cross()` lands an exit or enter with the destination group's own stop. `stopSource`, `stopHolder` and focus landing are private to the module, and no handler computes the stop's source. - keyRove, rove, createTypeahead, followFocus and initRovingTabindex keep only their own logic. crossBoundary calls the Group module and returns the group it lands in, and resolveTarget takes the group's navigable test. - A group is one class instance with its getters on the prototype, and options that name none of the readings share one reader, so keydown cost stays within noise against HEAD (`pnpm bench:headless`). - Test the group rules once, in a new group suite, and drop the handler cases that re-tested them, keeping one end-to-end case per rule. Public exports, types and behaviour are unchanged.
refactor: resolve each group once and let it carry the roving stop
A demo's behaviour was spread over five tables keyed by its name in three files, and the runtime checked the DOM again for what the build had decided. - build/demo-definitions.ts holds one entry per demo: group options, extra handlers and icons. The build stamps icons from it and src/demos.ts wires the demo from it; HISTORY, BAND, ITEMS, EXTRAS and CONFIGS are gone. - Every demo is a panel: the unused history and line logs, their markup and src/demos.css are removed. - src/readout.ts is the one readout for the panels and the hero, with its initial markup, nameOf and isMoveResult written once. The hero keeps its easter eggs behind a flag and uses the panels' format. - Placeholders parse in any attribute order, and a malformed one fails the build. hasDemos is removed. - The page's first live demo takes focus, so the hero no longer has to mount before the panels.
Three callers chained demo expansion, meta expansion and rendering by hand, and search allocated heading ids with a second parser over a different expansion, so a search result could link to a heading the page did not have. - build/page-render.ts owns the pipeline. renderPage(page, demos) parses the page once and returns the rail headings, the search sections and the .md twin, plus tree(), which renders the same tokens through Shiki on its first call and keeps the result. - Search reads the html-mode demo expansion, which gives the same section text on every page, so its ids come from the parse the page renders. searchParser and collectSearchSections are gone. - markdown.ts builds its renderer synchronously and loads Shiki on the first render, so parsing never waits for it. The resolveHref hook, which the only caller passed as the identity, is removed. - The content plugin parses every page when the site loads and serves the page modules, the search index, the twins and llms-full.txt from those renders; its own page-module cache is gone. toSearchIndex and toLlmsFullTxt take rendered pages, and toMarkdown moves into the module. - tests/ is type-checked. The search tests call renderPage instead of copying the pipeline, and a new test pins the twin's shape. dist/client is unchanged apart from the timestamp the router writes into each prerendered page.
Six hand-written walks decided which group an element is in, which is around a nested root and which is nested in an item, each with its own start and stop rule. A new internal nesting module answers the three questions with one walk up the tree and one down it, and applies the listener's reach in one place. - nesting.ts owns rootAt, ownerRoot, outerRoot, innerRoot and listenerElement, tested at its interface. - GroupReader gains `around` and `inside` and drops `isRoot`, so no handler walks the tree itself. - The group an event is in still looks past the listener's element: a wrapper root above it wins. Documented under Roots and pinned. - Every walk from a group or item to the group around it now stops at the listener's element. The focus key's group and followFocus no longer reach a root above the listener, and followFocus reaches a shadow-root listener's group from a top-level nested root, as exit already did.
refactor: walk group nesting in one module
refactor(docs): define each demo in one place
refactor(docs): render every page projection from one parse
The markers src/components/markdown.tsx swaps for React and the elements build/html-tree.ts keeps opaque were two lists with nothing linking them, and the build stamped widget markup that React threw away. - build/placeholders.ts lists every placeholder: its marker (a data-* attribute or a class, with a tag where needed) and whether its contents stay opaque. html-tree.ts and markdown.tsx both read it through placeholderOf, and neither keeps a list of its own. - markdown.tsx draws each swapped placeholder from DRAW, a table keyed by the registry's names, so an entry with no component does not compile. CodeTabs finds its copy button through the registry too. - Code tabs are stamped with their labels and which one is selected, and copy buttons with their label. The tabs' ids, aria-controls, tabindex, data-keyrove-* and hidden, the copy button's SVGs, type and data-copy-code are gone, and so is the per-page id counter. - tests/placeholders.test.ts renders every page and checks that each registry entry is written on one, and that every data-* hook is a placeholder's marker or named as not one. A page-render test pins the code-tab and copy-button stamps. - The twin test's demo placeholder gets the data-demo-label that #31 made required; the test failed on develop. Every non-asset file in dist/client is unchanged apart from the router's timestamp and the chunk hashes, and the client's JS is 64 kB smaller.
Absolute URLs were put together in four places, the twin routes rebuilt the .md rule by hand, and adding a generated file meant editing four. - build/addresses.ts replaces build/paths.ts. It owns the page and twin paths, the landing page's /index.md included, and turns a site path into an href under the deploy base or a URL on the site. head.ts and plaintext.ts take its addresses instead of a bare base. - GENERATED_FILES declares each file's path and type once. The prerender list and the served files both read it, and a file declared without a body does not typecheck. - Files are keyed by site path, and every file route, the twins included, serves its handler's pathname, so no route builds a file key. - The browser shares one base-only instance for links, the twin fetch, the search index, the favicon and the llms.txt links. markdownHref, routeToPath and createHrefResolver are gone. dist/client is unchanged for the default build and DOCS_BASE=/keyrove/, apart from the router's timestamps and the JS chunks that carry the address code.
CodeTabs reads a pattern tab's label and aria-selected, and CopyCommandButton reads the command and its label. The ids, aria-controls, tabindex, data-keyrove-* and hidden on the hand-written pattern tabs, and type on the install buttons, never reached the page. content/index.md now writes the same markup build/markdown.ts stamps. The rendered pages are unchanged; only the landing page's .md twin is.
The sidebar, the drawer and the Toc each put the same rail together from a ref, useRovingStop, useNavShortcut, aria-keyshortcuts, the keyRove and followFocus handlers and a KeysHint. The shortcut also found the search dialog by an id set in search-dialog.tsx. - keyboard-rail.tsx (was nav-keys.tsx) exports useKeyboardRail. A rail declares its links, its shortcut and the link its stop rests on; the hook returns the root's props, the hint and findStop. - The stop moves whenever the link it rests on changes, and is set as a rail's links arrive, so the drawer needs no code of its own for it. It is still left alone while focus is inside. - The shortcut is off while search is open, read from search's context instead of the dialog's id. - The drawer now opens itself on the chord below `lg`: Sidebar loses onReveal, SidebarDrawer gains onOpen, and the drawer carries aria-keyshortcuts. - navGroup is removed.
refactor(docs): build the sidebars' keyboard rails from one module
refactor(docs): keep the placeholder registry in one place
refactor(docs): answer every site address in one module
Remove unused docs and benchmark assets, dependency, and exports. Import the docs stylesheet directly and fix the demo test fixture.
packages/ now holds only what is published to npm. The docs site and the benchmark, which are not published, move to apps/. The workspace lists both folders, and the docs deploy uploads apps/docs/dist/client. The apps are renamed @mixedrays/keyrove-docs and @mixedrays/keyrove-bench. A scoped package name allows one slash, so pnpm read @mixedrays/keyrove/docs as a folder inside the library and wrote a broken docs symlink into packages/keyrove, which is removed. Every docs page's sitemap lastmod will date from this commit: build/git.ts finds dates by path and does not follow renames. Also bumps the docs version to 3.1.0 and documents the branch flow in CONTRIBUTING.md.
Releases the behaviour change in 7fd93e4: the focus key's group and followFocus no longer reach a root above the listener, and followFocus reaches a shadow-root listener's group from a top-level nested root.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.