Skip to content

Commit 6531cf9

Browse files
committed
Improve search functionality
- Fixed: The search dialog now correctly receives focus when opened in the current block. - Implemented direct search for selected text.
1 parent f6c47f9 commit 6531cf9

File tree

4 files changed

+80
-5
lines changed

4 files changed

+80
-5
lines changed

frontend/app/element/search.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type SearchProps = SearchAtoms & {
1616
onSearch?: (search: string) => void;
1717
onNext?: () => void;
1818
onPrev?: () => void;
19+
blockId?: string;
1920
};
2021

2122
const SearchComponent = ({
@@ -32,6 +33,7 @@ const SearchComponent = ({
3233
onSearch,
3334
onNext,
3435
onPrev,
36+
blockId,
3537
}: SearchProps) => {
3638
const [isOpen, setIsOpen] = useAtom<boolean>(isOpenAtom);
3739
const [search, setSearch] = useAtom<string>(searchAtom);
@@ -144,7 +146,7 @@ const SearchComponent = ({
144146
<>
145147
{isOpen && (
146148
<FloatingPortal>
147-
<div className="search-container" style={{ ...floatingStyles }} ref={refs.setFloating}>
149+
<div className="search-container" style={{ ...floatingStyles }} ref={refs.setFloating} data-blockid={blockId}>
148150
<Input
149151
placeholder="Search"
150152
value={search}
@@ -188,6 +190,7 @@ type SearchOptions = {
188190
regex?: boolean;
189191
caseSensitive?: boolean;
190192
wholeWord?: boolean;
193+
blockId?: string;
191194
};
192195

193196
export function useSearch(options?: SearchOptions): SearchProps {
@@ -212,7 +215,7 @@ export function useSearch(options?: SearchOptions): SearchProps {
212215
};
213216
}
214217
}, [options?.viewModel]);
215-
return { ...searchAtoms, anchorRef };
218+
return { ...searchAtoms, anchorRef, blockId: options?.blockId };
216219
}
217220

218221
const createToggleButtonDecl = (

frontend/app/store/keymodel.ts

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -628,15 +628,86 @@ function registerGlobalKeys() {
628628
return true;
629629
});
630630
}
631+
function getSelectedText(): string {
632+
// Check for terminal selection first
633+
const bcm = getBlockComponentModel(getFocusedBlockInStaticTab());
634+
if (bcm?.viewModel?.viewType === "term") {
635+
const termViewModel = bcm.viewModel as any;
636+
if (termViewModel.termRef?.current?.terminal) {
637+
const terminalSelection = termViewModel.termRef.current.terminal.getSelection();
638+
if (terminalSelection && terminalSelection.length > 0) {
639+
return terminalSelection.trim();
640+
}
641+
}
642+
}
643+
// Check for regular text selection
644+
const selection = window.getSelection();
645+
if (selection && selection.rangeCount > 0 && !selection.isCollapsed) {
646+
const selectedText = selection.toString().trim();
647+
if (selectedText.length > 0) {
648+
return selectedText;
649+
}
650+
}
651+
return "";
652+
}
653+
654+
function focusSearchInput() {
655+
setTimeout(() => {
656+
const blockId = getFocusedBlockInStaticTab();
657+
if (!blockId) {
658+
return;
659+
}
660+
661+
// Directly find the search container by data-blockid attribute
662+
const searchContainer = document.querySelector(
663+
`.search-container[data-blockid="${blockId}"]`
664+
) as HTMLElement;
665+
if (searchContainer) {
666+
const searchInput = searchContainer.querySelector("input") as HTMLInputElement;
667+
if (searchInput) {
668+
searchInput.focus();
669+
searchInput.select();
670+
return;
671+
}
672+
}
673+
674+
// Fallback: if we can't find the specific container, try to find any open search input
675+
const searchInput = document.querySelector(".search-container input") as HTMLInputElement;
676+
if (searchInput) {
677+
searchInput.focus();
678+
searchInput.select();
679+
}
680+
}, 0);
681+
}
682+
631683
function activateSearch(event: WaveKeyboardEvent): boolean {
632684
const bcm = getBlockComponentModel(getFocusedBlockInStaticTab());
633685
// Ctrl+f is reserved in most shells
634686
if (event.control && bcm.viewModel.viewType == "term") {
635687
return false;
636688
}
637689
if (bcm.viewModel.searchAtoms) {
638-
globalStore.set(bcm.viewModel.searchAtoms.isOpen, true);
639-
return true;
690+
const searchAtoms = bcm.viewModel.searchAtoms;
691+
const isOpen = globalStore.get(searchAtoms.isOpen);
692+
const selectedText = getSelectedText();
693+
694+
if (isOpen) {
695+
// Reset search dialog when already open
696+
globalStore.set(searchAtoms.searchValue, selectedText || "");
697+
globalStore.set(searchAtoms.resultsIndex, 0);
698+
globalStore.set(searchAtoms.resultsCount, 0);
699+
focusSearchInput();
700+
return true;
701+
} else {
702+
// Open search dialog
703+
globalStore.set(searchAtoms.isOpen, true);
704+
// Set search value to selected text if available
705+
if (selectedText) {
706+
globalStore.set(searchAtoms.searchValue, selectedText);
707+
}
708+
focusSearchInput();
709+
return true;
710+
}
640711
}
641712
return false;
642713
}

frontend/app/view/term/term.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ const TerminalView = ({ blockId, model }: ViewComponentProps<TermViewModel>) =>
178178
caseSensitive: false,
179179
wholeWord: false,
180180
regex: false,
181+
blockId: blockId,
181182
});
182183
const searchIsOpen = jotai.useAtomValue<boolean>(searchProps.isOpen);
183184
const caseSensitive = useAtomValueSafe<boolean>(searchProps.caseSensitive);

frontend/app/view/webview/webview.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ const WebView = memo(({ model, onFailLoad, blockRef, initialSrc }: WebViewProps)
821821
}
822822

823823
// Search
824-
const searchProps = useSearch({ anchorRef: model.webviewRef, viewModel: model });
824+
const searchProps = useSearch({ anchorRef: model.webviewRef, viewModel: model, blockId: model.blockId });
825825
const searchVal = useAtomValue<string>(searchProps.searchValue);
826826
const setSearchIndex = useSetAtom(searchProps.resultsIndex);
827827
const setNumSearchResults = useSetAtom(searchProps.resultsCount);

0 commit comments

Comments
 (0)