Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9708e95
ENG-2108 Add ranked search to QueryEngine with a vault-iteration fall…
trangdoan982 Aug 7, 2026
e1ee6eb
ENG-2109 Create node search modal with ranked results and preview
trangdoan982 Aug 7, 2026
59de16a
ENG-2109 Use the search highlight colour for matched substrings
trangdoan982 Aug 8, 2026
73e812b
Keep preview text paired with its file to avoid stale render
trangdoan982 Aug 8, 2026
6891784
Show node type as a badge and resolve author names from the shared cache
trangdoan982 Aug 8, 2026
33fb1fc
Tighten comments on the node type badge and author name cache
trangdoan982 Aug 9, 2026
dd75cb4
Cycle the node type palette and correct author fallbacks
trangdoan982 Aug 9, 2026
bd14efd
ENG-2109 Navigate results by keyboard from anywhere in the modal
trangdoan982 Aug 9, 2026
032083d
ENG-2109 Derive the badge from the title when the node type is gone
trangdoan982 Aug 9, 2026
948e909
ENG-2109 Keep colorUtils out of this change
trangdoan982 Aug 9, 2026
ab1f1db
ENG-2109 Restore colorUtils byte-for-byte
trangdoan982 Aug 9, 2026
24388a8
ENG-2109 Drop the remaining explanatory comments
trangdoan982 Aug 9, 2026
da528fb
Clamp the active index while results are being replaced
trangdoan982 Aug 10, 2026
01dc74a
ENG-2113 Add footer action bar with open in new tab and split (#1292)
trangdoan982 Aug 18, 2026
84818c7
ENG-2109 Address review: pointer guard, modal sizing utilities
trangdoan982 Aug 18, 2026
6686156
ENG-2114 Insert active result as a link at cursor
trangdoan982 Aug 19, 2026
38393d7
ENG-2114 Shorten comments on the insert-link path
trangdoan982 Aug 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions apps/obsidian/src/components/NodeSearchFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { type ReactElement } from "react";
import { getHintKeys, type HintKey } from "~/utils/keyboardHints";

type NodeSearchFooterProps = {
canAct: boolean;
canInsertLink: boolean;
onClose: () => void;
onInsertLink: () => 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,
canInsertLink,
onClose,
onInsertLink,
onOpenInNewTab,
onOpenInSplit,
}: NodeSearchFooterProps): ReactElement => (
<div className="prompt-instructions dg-search-footer shrink-0 justify-start px-0 pb-0 text-left">
{/* Absent, not disabled: with no cursor there is nothing to insert into. */}
{canInsertLink && (
<FooterAction
disabled={!canAct}
keys={["Mod", "Enter"]}
label="insert link at cursor"
onClick={onInsertLink}
/>
)}
<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>
);
Loading