github saberzero1/motions 1.2.0

4 hours ago

Added

  • Local typings for Obsidian's core-plugin view trees — obsidian-typings declares Tree<T>, TreeItem and TreeCollapsibleItem but attaches tree to FileExplorerView only. Measured on 1.13.7, five views own an equivalent navigable tree: file-explorer, outline, tag, all-properties and bookmarks. changeFocusedItem() moves focusedItem in all five, while setCollapsed is present only in file-explorer, outline and tag. Resolution is by allowlist rather than by shape, because backlink (view.backlink.backlinkDom) and search (view.dom) carry a ResultDom — a different interface that also has changeFocusedItem, so duck-typing finds it. ResultDom is not inert: its changeFocusedItem takes 'forwards' just as Tree's does, and on search it moves focus. backlink is unverified, its view having rendered no rows in two attempts. No user-visible behaviour changes; this is the groundwork for navigating those views, and follows the TableEditor route of declaring locally, proving in the plugin, then upstreaming.
    • Plugin: src/types/core-view-trees.d.ts (new), src/workspace/core-view-tree.ts (new)
  • Native File Explorer Vim navigation — when workspace navigation is enabled and the native File Explorer has focus, unmodified h/j/k/l reuse its arrow-key behavior, including counted movement capped at 100. They are ordinary GlobalMappingRegistry entries, so they remap and unmap like every other global binding: h/l under a new explorer gate that keeps them from being swallowed outside the tree, and j/k as the existing scroll entries with an explorer branch, since the registry stores one entry per key. Interaction tracking preserves navigation when Obsidian targets BODY and clears it on focus or pointer movement outside the explorer; pending chords such as <C-w>h are untouched. Rename controls, composition, modified keys, modals, editors, and other views retain their original keystrokes. (#191)
    • Plugin: src/workspace/global-key-handler.ts, src/workspace/global-defaults.ts, src/workspace/global-mapping-registry.ts, src/workspace/file-explorer-context.ts (new)

Fixed

  • A focused sidebar pane no longer scrolls the main editor — with the tag or outline pane focused, j/k/G/gg/H/L/<C-d>/<C-u>/<C-f>/<C-b> were intercepted and applied to the note behind them: five presses of j scrolled the editor 196 px. workspaceNavViewTypes does not list those view types, so the standard gate should have declined, but isPluginLeafActive() asked getMostRecentLeaf(), which is documented as returning the root-split leaf "while a sidebar leaf might be active" and so reported markdown in every case. The handler now tracks the leaf that actually gained focus through active-leaf-change and prefers it, falling back to getMostRecentLeaf(). The search pane was already protected by the focused-input guard, and the File Explorer by its own navigation. (#191)
    • Plugin: src/workspace/global-key-handler.ts
  • File Explorer j/k no longer depend on that bug — they are the standard scroll entries branching on explorer context, so once the gate correctly rejected sidebar leaves they stopped moving the tree. isPluginLeafActive() now declines to veto when the keystroke belongs to the File Explorer.
    • Plugin: src/workspace/global-key-handler.ts
  • Multi-digit counts now reach global key bindings — every count typed outside the editor was truncated to its first digit, so 12gt went to tab 1 and 30j scrolled three lines instead of thirty. The first digit is captured in the gateApplies === null branch of the gate block, and that branch returns unconditionally for any key it does not consume. The continuation accumulator sat below the gate block, so it was unreachable for a second digit — an unmapped digit always resolves to gateApplies === null and returns there. Single-digit counts were unaffected, which is why this went unnoticed; 3gt and its test have always passed. The accumulator now runs before the gate block, and carries a note that the ordering is load-bearing.
    • Plugin: src/workspace/global-key-handler.ts

Tests

  • keeps source-rendered frontmatter fully navigable is fixed, and it was test-side — the spec set propertiesInDocument to source, but something reverted it during the reconnect that follows, so the walk ran with visible and stalled at the first body line. The product was correct throughout: the fork's gate reads getVaultConfig(app, 'propertiesInDocument') === 'source' live and reported false because that is what the config said. The tell was already in the spec — the visible test expects exactly the 7,6,6,6,6,6 the source test was failing with, so the source walk had silently become the visible walk. Forced on Linux, where it had never failed, by injecting setPropertiesMode('visible') before the walk: byte-identical to both CI observations. measureFrontmatterWalk now takes the expected mode and re-asserts it at walk time, and the forcing probe goes green with the injection still in place.
  • 3 further unit cases pin that File Explorer navigation is independent of workspaceNavViewTypes (both gates), and that the explorer is still driven when it lives in a main-area tab, where getMostRecentLeaf() does report file-explorer and would otherwise veto it. Negative controls: removing the explorer exception failed both main-area and sidebar cases with [] instead of ['ArrowDown']; making the explorer gate consult getNavViewTypes() failed the h case and both h/l translations with prevented 0 instead of 1.
  • 16 unit cases in test/unit/workspace/core-view-tree.test.ts cover the allowlist, tree resolution, count handling and collapsibility reporting; 12 e2e cases in test/specs/core-view-trees.e2e.ts re-measure every declared row against a real Obsidian and assert backlink/search stay excluded. Negative controls: adding backlink to the navigable list failed both the pinned-list case and the exclusion case, claiming all-properties collapsible failed the pinned-list case, and adding backlink to the e2e's own list failed only that view while the real five stayed green. Two defects in the first draft of the e2e were caught this way and are worth recording — hoisting changeFocusedItem into a local detached it from its receiver and threw on this.focusedItem, and comparing focus by label reported "did not move" for bookmarks, whose rows render no text. A third, found later: the original survey reported search and backlink as inert because it read focusedItem.selfEl, which ResultDom items do not have — they expose el. search navigates fine when called correctly, so the exclusion stands on the two interfaces being different, not on one being dead.
  • 5 unit cases and 3 e2e cases in test/specs/global-nav.e2e.ts cover the sidebar gate: tag/outline/search/backlink must not scroll the editor, a main-area nav view still must, and the File Explorer's j still moves the tree. Negative controls: removing the active-leaf preference scrolled the editor 77 px in the tag and outline e2e cases and failed 4 unit cases; removing the explorer exception left the File Explorer j dispatching no arrow. The unit mock also now collects every active-leaf-change subscriber, because capturing only the last one bound the FileExplorerContext listener instead of the handler's and hid the fix entirely.
  • 4 unit cases in test/unit/global-key-handler.test.ts cover two-digit, four-digit, and trailing-zero counts dispatched to a builtin action, plus 12gt across a multi-key chord — a separate path, since the count has to survive a partial match and a timeout restart. Each was negative-controlled by restoring the pre-fix file verbatim: 12x delivered 1, 9999x delivered 9, 30x delivered 3, and 12gt delivered 1. All four returned green once the fix was restored.
  • 25 File Explorer unit cases and 7 Obsidian E2E scenarios cover arrow translation, counts and the 100-movement cap, focus/pointer/contenteditable gates, physical-key observation, <C-w>h, and the new registry behaviour: h/l are not swallowed outside the tree, j/k still scroll there, <C-w>j/k/l still reach their pane commands, h disappears when workspace navigation is off, and a user gmap h overrides the default. Every case was negative-controlled: registering h/l at the standard gate gave prevented 1, dispatched 1 instead of 0, 0; an unconditional explorer branch gave scrolls 0 instead of 1; removing the <C-w>j entry gave [] instead of ['editor:focus-bottom']; forcing wsNav true left h registered; blocking registry overwrite gave commands [] instead of ['app:go-back']; ordering explorer ahead of structural gave prevented 0 instead of 1; removing the focusin listener dispatched one unwanted ArrowDown; removing the synthetic-event guard reported <ArrowDown> as a physical key; removing the cap dispatched 9,999 instead of 100; and removing the contentEditable gate gave prevented 1, stopped 1 instead of 0, 0. (#191)
  • A truncated plugin-tarball download no longer kills a CI shard — scripts/fetch-test-plugins.sh piped curl into tar, so a response cut mid-stream was unrecoverable: codeload truncated a flash.nvim tarball, tar died with gzip: stdin: unexpected end of file, and the whole Windows shard failed before any test ran. A truncated body is not an HTTP error, so curl -f never caught it. Each tarball now downloads to disk with --retry 3 --retry-all-errors before extraction.
    • Plugin: scripts/fetch-test-plugins.sh

Documentation

  • AGENTS.md, CONTRIBUTING.md: the registry rule for global keys, src/workspace/file-explorer-context.ts, src/workspace/core-view-tree.ts, the locally declared core-plugin view trees, and the constraint that test/specs/** cannot value-import a src/**/*.ts module.
  • docs/configuration/remapping.md, docs/configuration/settings.md: the File Explorer keys are remappable like any other global binding, and Workspace navigation gates them.
  • test/flaky-inventory.md: three CI failures observed during this work. keeps source-rendered frontmatter fully navigable reproduced on Windows with diagnostics byte-identical to the inventoried macOS case, which ruled out the environment and led to the forced failure and fix recorded under Tests. It is now resolved as test-side, and is added to the main inventory table, which had been missing it. The other two — paints an embedded note the same way (macOS) and leaves the stock behaviour when the setting is disabled (Windows). Both are recorded as Unknown with the observed values; a passing re-run is not a classification.
  • AGENTS.md, CONTRIBUTING.md: File Explorer key handling.
  • README.md, docs/features/workspace-navigation.md, docs/reference/keybindings.md: native File Explorer navigation, counts, focus gates, and editable-control exclusions.

Full Changelog: 1.1.1...1.2.0

Don't miss a new motions release

NewReleases is sending notifications on new releases.