Skip to content

Commit eb678b6

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 eb678b6

File tree

4 files changed

+66
-5
lines changed

4 files changed

+66
-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: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -628,14 +628,71 @@ 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+
}
671+
}
672+
}, 0);
673+
}
674+
631675
function activateSearch(event: WaveKeyboardEvent): boolean {
632676
const bcm = getBlockComponentModel(getFocusedBlockInStaticTab());
633-
// Ctrl+f is reserved in most shells
677+
// Ctrl+f is reserved in most shells.
634678
if (event.control && bcm.viewModel.viewType == "term") {
635679
return false;
636680
}
637681
if (bcm.viewModel.searchAtoms) {
638-
globalStore.set(bcm.viewModel.searchAtoms.isOpen, true);
682+
const searchAtoms = bcm.viewModel.searchAtoms;
683+
const isOpen = globalStore.get(searchAtoms.isOpen);
684+
const selectedText = getSelectedText();
685+
686+
// Open search dialog if not already open
687+
if (!isOpen) {
688+
globalStore.set(searchAtoms.isOpen, true);
689+
}
690+
// Set search value (use selected text if available, otherwise empty string)
691+
globalStore.set(searchAtoms.searchValue, selectedText || "");
692+
// Reset search results
693+
globalStore.set(searchAtoms.resultsIndex, 0);
694+
globalStore.set(searchAtoms.resultsCount, 0);
695+
focusSearchInput();
639696
return true;
640697
}
641698
return false;

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)