ENG-2111 Add keyboard-only node type filtering with tag chips - #1321
Merged
trangdoan982 merged 4 commits intoAug 22, 2026
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
PR size/scope checkThis PR is over our review-size guideline.
Please split this into smaller PRs unless there is a clear reason the changes need to land together. If keeping it as one PR, please add a brief justification covering:
|
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
trangdoan982
force-pushed
the
eng-2111-add-keyboard-only-node-type-filtering-with-tag-chips
branch
from
August 20, 2026 02:40
ebfe6c2 to
9001408
Compare
trangdoan982
force-pushed
the
eng-2111-add-keyboard-only-node-type-filtering-with-tag-chips
branch
from
August 20, 2026 02:57
9001408 to
64c376f
Compare
trangdoan982
force-pushed
the
eng-2111-add-keyboard-only-node-type-filtering-with-tag-chips
branch
from
August 20, 2026 02:58
64c376f to
620c623
Compare
1 task
mdroidian
approved these changes
Aug 21, 2026
trangdoan982
force-pushed
the
eng-2111-add-keyboard-only-node-type-filtering-with-tag-chips
branch
from
August 22, 2026 04:27
559baa4 to
57d6429
Compare
trangdoan982
force-pushed
the
eng-2111-add-keyboard-only-node-type-filtering-with-tag-chips
branch
from
August 22, 2026 04:34
57d6429 to
0ec6373
Compare
trangdoan982
force-pushed
the
eng-2111-add-keyboard-only-node-type-filtering-with-tag-chips
branch
from
August 22, 2026 04:39
0ec6373 to
ee6066d
Compare
Base automatically changed from
eng-2110-add-node-type-filter-dropdown-menu
to
main
August 22, 2026 04:45
trangdoan982
force-pushed
the
eng-2111-add-keyboard-only-node-type-filtering-with-tag-chips
branch
from
August 22, 2026 04:45
ee6066d to
a7b1683
Compare
Chips and the query caret now share one field, so a keyboard-first user can narrow by type without reaching for the mouse. Typing offers the best prefix match as ghost text and Tab commits it to a chip; text left unconfirmed stays a plain keyword query, so filtering never happens by accident. Roam's advanced search is the design and behaviour reference. Obsidian has no tag-input component at all, so this is a build rather than a port: the filter semantics carry over, the BlueprintJS structure does not. - nodeTypeChipCompletion.ts holds the matching rules as pure functions, prefix only — the suggestion is drawn as a completion of what was typed, and a substring match has no suffix to render. - NodeTypeChipsSearchInput writes the raw selectedNodeTypeIds ENG-2110 owns, so chips and the filter dropdown are two views of one state. It skips that module's canonicalisation on purpose: collapsing a full selection to "no filter" would make a chip the user just added vanish. - Chips and the query are inline siblings in a block box, so they flow together left to right and top to bottom and a long query wraps beside the last chip rather than into a column of its own. That rules out an input or a textarea, both of which can only wrap inside their own box, so the query is an editable span. Its text is uncontrolled, since re-rendering it on every keystroke would move the caret. - The Tab hint is inline after the caret rather than an overlay, so it follows the text and wraps with it. It shows whenever Tab would commit, including once the query spells a type name in full and no suffix is left to ghost — Roam hides it there, precisely when the user is about to press it. - Arrow, Enter and Escape are left to bubble to NodeSearch, which already navigates and opens results. Roam forwards them through a prop instead. Enter's default is suppressed here so the editable gains no line break: the modal deliberately leaves modified Enter unhandled for the insert and dock actions. - Backspace on an empty field highlights the last chip and only removes it on a second press, so a stray keystroke cannot silently drop a filter. - Styling is Tailwind throughout and adds no CSS. Two rules this build makes non-obvious: `border-solid` is required because `@tailwind base` is omitted, so nothing sets a default border style and `border` alone renders nothing; and `ring-*` is inert for the same reason, so the focused chip takes an outline. The chip's remove button carries `clickable-icon` to dodge Obsidian's `button:not(.clickable-icon)` rule, which outranks a utility class and would otherwise paint a box behind the ×. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Chip removal and keyboard bounds used positions in `selectedNodeTypeIds` while the index came from `chips`, the rendered list. The two cannot diverge today — the modal reads settings once with no subscription — but the assumption was load-bearing for nothing, so removal is by id and navigation is bounded by what is actually on screen. - `max-w-40` replaces the inline max-width, per apps/obsidian/AGENTS.md:100. The chip's colours stay inline: they are computed per type. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The field's click handler focused the query and forced the caret to the end on every click, including clicks on the text itself — so clicking into the middle of a query dragged the caret to the end. It now runs only for clicks on the field's own padding, where there is no caret position to preserve. Measured: clicking 60% into "hello world" leaves the caret at 7 rather than 11, and clicking the blank padding still places it at the end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Keeps only what the code cannot say: the Obsidian and Tailwind quirks (`clickable-icon` beating a utility class, `border-solid` with no preflight), the deliberate choices (uncontrolled text so the caret survives a re-render, Backspace highlighting before it deletes, Enter's default suppressed but still bubbling), and the inline-flow layout that is easy to "tidy" into a flex row. Drops the rest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
trangdoan982
force-pushed
the
eng-2111-add-keyboard-only-node-type-filtering-with-tag-chips
branch
from
August 22, 2026 04:49
a7b1683 to
92fa691
Compare
trangdoan982
deleted the
eng-2111-add-keyboard-only-node-type-filtering-with-tag-chips
branch
August 22, 2026 04:53
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.
https://www.loom.com/share/7eae3997668c43829896b093d005401a
What this does
Chips and the query caret now share one field, so a keyboard-first user can narrow by type without reaching for the mouse. Typing offers the best prefix match as ghost text with a
⇥hint;Tabcommits it to a chip. Text left unconfirmed stays a plain keyword query, so filtering never happens by accident.Roam's advanced search is the design and behaviour reference. Obsidian has no tag-input component at all, so this is a build rather than a port.
Notable decisions
selectedNodeTypeIdsstays the single source of truth, so chips and the filter dropdown are two views of one state. No change toQueryEngine. The chips write the raw list and skipfromPanelSelectedIds' canonicalisation on purpose: collapsing a full selection to "no filter" would make a chip the user just added vanish.NodeSearch, whose handler is already an ancestor and already navigates and opens results. Roam threads them through anonSearchKeyDownprop; here that would only duplicate it.Backspacehighlights before it deletes. The ticket said "removes the most recent chip"; this follows Roam instead, so a stray keystroke cannot silently drop a filter. The chip-focus state it needs is the same state the arrow keys use.Tabhint shows wheneverTabwould commit — including once the query spells a type name in full and no suffix is left to ghost. Roam hides it there, which is exactly when the user is about to press it.Enteris suppressed there: the modal deliberately leavesMod/Ctrl/Alt+Enterunhandled for the insert and dock actions, so the query would otherwise gain a line break.--background-modifier-form-field,--input-radius). Two rules were dropped as dead:--input-shadowisnonein Obsidian 1.13 and--input-shadow-activeis undefined — native fields there are marked by border colour alone.ring-*utilities are inert in this build.--tw-ring-shadowand its offset counterpart are never defined, so the compositebox-shadowcollapses tononeand a focused chip rendered no state at all. The focused chip takes anoutlinein CSS instead — no variables needed, and unlike a border it does not shift the chips beside it as focus moves.tabIndex={-1}.Tabis the commit key, so focusable remove buttons would wedge themselves into the tab order ahead of the footer actions; keyboard users delete via the highlighted chip.Known edge case, documented rather than fixed
Chipping every configured type makes
selectedNodeTypeIds.length === allTypeIds.length, whichhasActiveTypeFilterreads as "no filter" — the count badge disappears while the chips stay on screen. Results are identical either way, and the dropdown shows all rows checked, so the two surfaces do not disagree. Suppressing the last chip would be worse: it would silently refuse a type the user asked for.Verification
pnpm check-typesandpnpm lintclean inapps/obsidian(0 errors; pre-existing warnings elsewhere untouched).Tabcommit, immediate narrowing, two-chip union, unconfirmed text applying no filter,Backspacehighlight-then-remove with the marking asserted on the rendered outline, arrow navigation across chips and back to the caret, results navigation with a chip focused, modifiedEnterinserting no newline,Tabfall-through to the filter trigger, field growth without clipping, and chip↔dropdown sync in both directions.apps/obsidianhas no test harness — notestscript, no vitest config, no test files — the same call ENG-2110 made. The matching rules were split into a pure util so they are ready for one.🤖 Generated with Claude Code