Skip to content

Commit 7afb70d

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 7afb70d

File tree

4 files changed

+67
-5
lines changed

4 files changed

+67
-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: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
WOS,
2222
} from "@/app/store/global";
2323
import { TabBarModel } from "@/app/tab/tabbar-model";
24+
import type { TermViewModel } from "@/app/view/term/term-model";
2425
import { WorkspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
2526
import { deleteLayoutModelForTab, getLayoutModelForStaticTab, NavigateDirection } from "@/layout/index";
2627
import * as keyutil from "@/util/keyutil";
@@ -628,14 +629,71 @@ function registerGlobalKeys() {
628629
return true;
629630
});
630631
}
632+
function getSelectedText(): string {
633+
// Check for terminal selection first
634+
const bcm = getBlockComponentModel(getFocusedBlockInStaticTab());
635+
if (bcm?.viewModel?.viewType === "term") {
636+
const termViewModel = bcm.viewModel as TermViewModel;
637+
if (termViewModel.termRef?.current?.terminal) {
638+
const terminalSelection = termViewModel.termRef.current.terminal.getSelection();
639+
if (terminalSelection && terminalSelection.length > 0) {
640+
return terminalSelection.trim();
641+
}
642+
}
643+
}
644+
// Check for regular text selection
645+
const selection = window.getSelection();
646+
if (selection && selection.rangeCount > 0 && !selection.isCollapsed) {
647+
const selectedText = selection.toString().trim();
648+
if (selectedText.length > 0) {
649+
return selectedText;
650+
}
651+
}
652+
return "";
653+
}
654+
655+
function focusSearchInput() {
656+
setTimeout(() => {
657+
const blockId = getFocusedBlockInStaticTab();
658+
if (!blockId) {
659+
return;
660+
}
661+
662+
// Directly find the search container by data-blockid attribute
663+
const searchContainer = document.querySelector(
664+
`.search-container[data-blockid="${blockId}"]`
665+
) as HTMLElement;
666+
if (searchContainer) {
667+
const searchInput = searchContainer.querySelector("input") as HTMLInputElement;
668+
if (searchInput) {
669+
searchInput.focus();
670+
searchInput.select();
671+
}
672+
}
673+
}, 0);
674+
}
675+
631676
function activateSearch(event: WaveKeyboardEvent): boolean {
632677
const bcm = getBlockComponentModel(getFocusedBlockInStaticTab());
633-
// Ctrl+f is reserved in most shells
678+
// Ctrl+f is reserved in most shells.
634679
if (event.control && bcm.viewModel.viewType == "term") {
635680
return false;
636681
}
637682
if (bcm.viewModel.searchAtoms) {
638-
globalStore.set(bcm.viewModel.searchAtoms.isOpen, true);
683+
const searchAtoms = bcm.viewModel.searchAtoms;
684+
const isOpen = globalStore.get(searchAtoms.isOpen);
685+
const selectedText = getSelectedText();
686+
687+
// Open search dialog if not already open
688+
if (!isOpen) {
689+
globalStore.set(searchAtoms.isOpen, true);
690+
}
691+
// Set search value (use selected text if available, otherwise empty string)
692+
globalStore.set(searchAtoms.searchValue, selectedText || "");
693+
// Reset search results
694+
globalStore.set(searchAtoms.resultsIndex, 0);
695+
globalStore.set(searchAtoms.resultsCount, 0);
696+
focusSearchInput();
639697
return true;
640698
}
641699
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)