-
Notifications
You must be signed in to change notification settings - Fork 7
ENG-2113 Add footer action bar with open in new tab and split #1292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
trangdoan982
merged 7 commits into
eng-2109-create-node-search-modal-with-ranked-results-and-preview
from
eng-2113-add-footer-action-bar-with-open-in-active-pane-and-split
Aug 18, 2026
+201
−6
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
c719fae
ENG-2113 Add platform-aware keyboard hint symbols
trangdoan982 d9f017e
ENG-2113 Add footer action bar with open in active pane and split
trangdoan982 f79c3c1
ENG-2113 Open the active result in a new tab rather than the current one
trangdoan982 e54e2f1
ENG-2113 Style footer keys as caps, matching Roam
trangdoan982 74a642b
ENG-2113 Drop the results list tooltip
trangdoan982 005b5a2
ENG-2113 Make close clickable and drop the duplicate badge tooltip
trangdoan982 48c234e
ENG-2113 Let focused footer buttons handle their own Enter
trangdoan982 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| import { type ReactElement } from "react"; | ||
| import { getHintKeys, type HintKey } from "~/utils/keyboardHints"; | ||
|
|
||
| type NodeSearchFooterProps = { | ||
| canAct: boolean; | ||
| onClose: () => void; | ||
| onOpenInNewTab: () => void; | ||
| onOpenInSplit: () => void; | ||
| }; | ||
|
|
||
| type FooterActionProps = { | ||
| disabled?: boolean; | ||
| keys: HintKey[]; | ||
| label: string; | ||
| onClick: () => void; | ||
| }; | ||
|
|
||
| const KeyHints = ({ keys }: { keys: HintKey[] }): ReactElement => ( | ||
| <> | ||
| {getHintKeys(keys).map((symbol) => ( | ||
| <kbd className="dg-search-footer-key" key={symbol}> | ||
| {symbol} | ||
| </kbd> | ||
| ))} | ||
| </> | ||
| ); | ||
|
|
||
| const FooterAction = ({ | ||
| disabled = false, | ||
| keys, | ||
| label, | ||
| onClick, | ||
| }: FooterActionProps): ReactElement => ( | ||
| <button | ||
| type="button" | ||
| className="prompt-instruction dg-search-footer-action inline-flex h-auto cursor-pointer items-center gap-1 rounded-none border-0 p-0 disabled:cursor-not-allowed disabled:opacity-50" | ||
| disabled={disabled} | ||
| onClick={onClick} | ||
| // Clicking must not move focus out of the query input, or the arrow keys stop | ||
| // reaching the result list. | ||
| onMouseDown={(event) => event.preventDefault()} | ||
| > | ||
| <KeyHints keys={keys} /> | ||
| <span className="ms-1">{label}</span> | ||
| </button> | ||
| ); | ||
|
|
||
| // Sits in Obsidian's `prompt-instructions` container for its type and spacing. | ||
| // Obsidian centres that row for the narrow quick switcher; this footer spans a | ||
| // full-width result list, so the actions start at its left edge instead. | ||
| export const NodeSearchFooter = ({ | ||
| canAct, | ||
| onClose, | ||
| onOpenInNewTab, | ||
| onOpenInSplit, | ||
| }: NodeSearchFooterProps): ReactElement => ( | ||
| <div className="prompt-instructions dg-search-footer shrink-0 justify-start px-0 pb-0 text-left"> | ||
| <FooterAction | ||
| disabled={!canAct} | ||
| keys={["Enter"]} | ||
| label="open in new tab" | ||
| onClick={onOpenInNewTab} | ||
| /> | ||
| <FooterAction | ||
| disabled={!canAct} | ||
| keys={["Shift", "Enter"]} | ||
| label="open in split" | ||
| onClick={onOpenInSplit} | ||
| /> | ||
| {/* The Escape key itself is handled by Obsidian's modal scope; this button | ||
| is the pointer equivalent, so every footer item responds to a click. */} | ||
| <FooterAction keys={["Escape"]} label="close" onClick={onClose} /> | ||
| </div> | ||
| ); |
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { Platform } from "obsidian"; | ||
|
|
||
| export type HintKey = "Mod" | "Alt" | "Shift" | "Enter" | "Escape"; | ||
|
|
||
| // Obsidian shows glyphs on macOS and spelled-out words everywhere else. | ||
| const MAC_SYMBOLS: Record<HintKey, string> = { | ||
| Mod: "⌘", | ||
| Alt: "⌥", | ||
| Shift: "⇧", | ||
| Enter: "↵", | ||
| Escape: "esc", | ||
| }; | ||
|
|
||
| const NON_MAC_SYMBOLS: Record<HintKey, string> = { | ||
| Mod: "Ctrl", | ||
| Alt: "Alt", | ||
| Shift: "Shift", | ||
| Enter: "Enter", | ||
| Escape: "Esc", | ||
| }; | ||
|
|
||
| /** Takes `isMacOS` so the non-mac branch can be checked without that platform. */ | ||
| export const formatHintKeys = ({ | ||
| keys, | ||
| isMacOS, | ||
| }: { | ||
| keys: HintKey[]; | ||
| isMacOS: boolean; | ||
| }): string[] => | ||
| keys.map((key) => (isMacOS ? MAC_SYMBOLS : NON_MAC_SYMBOLS)[key]); | ||
|
|
||
| export const getHintKeys = (keys: HintKey[]): string[] => | ||
| formatHintKeys({ keys, isMacOS: Platform.isMacOS }); |
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a keyboard user Tabs into a footer button, its bubbling Enter keydown is still intercepted here,
preventDefault()suppresses the button's native click, and the selection shortcut runs instead. Consequently, Enter on the close button opens the active result in a new tab, while Enter on the split button also opens a new tab; ignore Enter events originating from footer buttons or scope this shortcut to the search input.Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@trangdoan982
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Valid, and worse than described now that close is also a button — fixed in 48c234e.
Enter on a focused footer button bubbled to the modal handler, whose `preventDefault()` suppressed the button's native click, so Tab→close and Tab→split both opened a new tab instead. Rather than scope the shortcut to the input (ENG-2109 deliberately moved it to the wrapper so arrow keys work anywhere in the modal), the Enter branch now returns early when the event originates inside a button:
Arrow keys are unaffected — they return before this guard — and the buttons stay Tab-reachable.