Repository: raycast/extensions
Pull request #17807: Adding support to move selected items in the finder to a destination folder
Author: login: omarshahine
Labels: name: extension fix / improvement
description: Label for PRs with extension's fix improvements
name: OP is contributor
description: The OP of the PR is a contributor of the extension
name: extension: folder-search
description: Issues related to the folder-search extension
Created: 2025-03-13T04:48:28Z | Merged: 2025-03-13T09:35:36Z

## Description

Added a new command that allows you to move selected files using the same UX as Folder search. 

## Screencast

https://share.cleanshot.com/TTYFCypV

## Checklist

- [x] I read the [extension guidelines](https://developers.raycast.com/basics/prepare-an-extension-for-store)
- [x] I read the [documentation about publishing](https://developers.raycast.com/basics/publish-an-extension)
- [x] I ran `npm run build` and [tested this distribution build in Raycast](https://developers.raycast.com/basics/prepare-an-extension-for-store#metadata-and-configuration)
- [x] I checked that files in the `assets` folder are used by the extension itself
- [x] I checked that assets used in the `README` are located outside the metadata folder if they were not generated with our [metadata tool](https://developers.raycast.com/basics/prepare-an-extension-for-store#how-to-use-it)



Diff:
diff --git a/extensions/folder-search/CHANGELOG.md b/extensions/folder-search/CHANGELOG.md
index 1ecc7ff38b7..6cbbca23de2 100644
--- a/extensions/folder-search/CHANGELOG.md
+++ b/extensions/folder-search/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Folder Search Changelog
 
+## [New Features] - 2025-03-13
+
+- **added** 'Move' action for selected finder items
+
 ## [New Features] - 2024-08-20
 
 - **added** Pins can now be ordered by `Move Pin Up` and `Move Pin Down` actions
diff --git a/extensions/folder-search/README.md b/extensions/folder-search/README.md
index 0809f3f9555..802b970e96b 100644
--- a/extensions/folder-search/README.md
+++ b/extensions/folder-search/README.md
@@ -1,6 +1,6 @@
 # Folder Search
 
-Search for folders on your Mac
+Search for folders on your Mac and move items into folders quickly
 
 ## Plugins
 
diff --git a/extensions/folder-search/package.json b/extensions/folder-search/package.json
index 12db228596f..f955370f91a 100644
--- a/extensions/folder-search/package.json
+++ b/extensions/folder-search/package.json
@@ -25,6 +25,13 @@
       "title": "Search Folders",
       "description": "Search for folders on your Mac",
       "mode": "view"
+    },
+    {
+      "name": "move",
+      "title": "Move to a Folder",
+      "subtitle": "Folder Search",
+      "description": "Move selected files to a folder",
+      "mode": "view"
     }
   ],
   "preferences": [
diff --git a/extensions/folder-search/src/components/FolderListSection.tsx b/extensions/folder-search/src/components/FolderListSection.tsx
new file mode 100644
index 00000000000..bb84d2cf3e6
--- /dev/null
+++ b/extensions/folder-search/src/components/FolderListSection.tsx
@@ -0,0 +1,71 @@
+import { Color, Icon, List } from "@raycast/api";
+import { folderName, enclosingFolderName } from "../utils";
+import { ReactNode } from "react";
+import { SpotlightSearchResult } from "../types";
+
+interface FolderListSectionProps {
+  title: string;
+  results: SpotlightSearchResult[];
+  isShowingDetail: boolean;
+  resultIsPinned: (result: SpotlightSearchResult) => boolean;
+  renderActions: (result: SpotlightSearchResult, resultIndex: number) => ReactNode;
+}
+
+export function FolderListSection({
+  title,
+  results,
+  isShowingDetail,
+  resultIsPinned,
+  renderActions,
+}: FolderListSectionProps) {
+  return (
+    <List.Section title={title}>
+      {results.map((result: SpotlightSearchResult, resultIndex: number) => (
+        <List.Item
+          id={`result-${resultIndex.toString()}`}
+          key={resultIndex}
+          title={folderName(result)}
+          subtitle={!isShowingDetail ? enclosingFolderName(result) : ""}
+          icon={{ fileIcon: result.path }}
+          accessories={resultIsPinned(result) ? [{ icon: { source: Icon.Star, tintColor: Color.Yellow } }] : []}
+          detail={
+            <List.Item.Detail
+              metadata={
+                <List.Item.Detail.Metadata>
+                  <List.Item.Detail.Metadata.Label title="Metadata" />
+                  <List.Item.Detail.Metadata.Label title="Name" text={result.kMDItemFSName} />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label title="Where" text={result.path} />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label title="Type" text={result.kMDItemKind} />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label
+                    title="Created"
+                    text={result.kMDItemFSCreationDate?.toLocaleString()}
+                  />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label
+                    title="Modified"
+                    text={result.kMDItemContentModificationDate?.toLocaleString()}
+                  />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label
+                    title="Last used"
+                    text={result.kMDItemLastUsedDate?.toLocaleString() || "-"}
+                  />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label
+                    title="Use count"
+                    text={result.kMDItemUseCount?.toLocaleString() || "-"}
+                  />
+                  <List.Item.Detail.Metadata.Separator />
+                </List.Item.Detail.Metadata>
+              }
+            />
+          }
+          actions={renderActions(result, resultIndex)}
+        />
+      ))}
+    </List.Section>
+  );
+}
diff --git a/extensions/folder-search/src/hooks/useFolderSearch.tsx b/extensions/folder-search/src/hooks/useFolderSearch.tsx
new file mode 100644
index 00000000000..0b2a9e5c21c
--- /dev/null
+++ b/extensions/folder-search/src/hooks/useFolderSearch.tsx
@@ -0,0 +1,222 @@
+import { useEffect, useRef, useState } from "react";
+import { LocalStorage, Toast, environment, showToast } from "@raycast/api";
+import { usePromise } from "@raycast/utils";
+import { FolderSearchPlugin, SpotlightSearchResult } from "../types";
+import { loadPlugins, lastUsedSort, fixDoubleConcat } from "../utils";
+import { searchSpotlight } from "../search-spotlight";
+
+export function useFolderSearch() {
+  const [searchText, setSearchText] = useState<string>("");
+  const [pinnedResults, setPinnedResults] = useState<SpotlightSearchResult[]>([]);
+  const [selectedItemId, setSelectedItemId] = useState<string>("");
+  const [searchScope, setSearchScope] = useState<string>("");
+  const [isShowingDetail, setIsShowingDetail] = useState<boolean>(true);
+  const [results, setResults] = useState<SpotlightSearchResult[]>([]);
+  const [plugins, setPlugins] = useState<FolderSearchPlugin[]>([]);
+  const [isQuerying, setIsQuerying] = useState<boolean>(false);
+  const [canExecute, setCanExecute] = useState<boolean>(false);
+  const [hasCheckedPlugins, setHasCheckedPlugins] = useState<boolean>(false);
+  const [hasCheckedPreferences, setHasCheckedPreferences] = useState<boolean>(false);
+
+  const abortable = useRef<AbortController>();
+
+  // handle double text issue
+  useEffect(() => {
+    const fixedText = fixDoubleConcat(searchText);
+    if (fixedText !== searchText) {
+      setSearchText(fixedText);
+    }
+  }, [searchText]);
+
+  // check plugins
+  usePromise(
+    async () => {
+      const plugins = await loadPlugins();
+      setPlugins(plugins);
+    },
+    [],
+    {
+      onData() {
+        setHasCheckedPlugins(true);
+      },
+      onError() {
+        showToast({
+          title: "An Error Occurred",
+          message: "Could not read plugins",
+          style: Toast.Style.Failure,
+        });
+
+        setHasCheckedPlugins(true);
+      },
+    }
+  );
+
+  // check prefs
+  usePromise(
+    async () => {
+      const maybePreferences = await LocalStorage.getItem(`${environment.extensionName}-preferences`);
+
+      if (maybePreferences) {
+        try {
+          return JSON.parse(maybePreferences as string);
+        } catch (_) {
+          // noop
+        }
+      }
+    },
+    [],
+    {
+      onData(preferences) {
+        setPinnedResults(preferences?.pinned || []);
+        setSearchScope(preferences?.searchScope || "");
+        setIsShowingDetail(preferences?.isShowingDetail);
+        setHasCheckedPreferences(true);
+      },
+      onError() {
+        showToast({
+          title: "An Error Occurred",
+          message: "Could not read preferences",
+          style: Toast.Style.Failure,
+        });
+
+        setHasCheckedPreferences(true);
+      },
+    }
+  );
+
+  // Save preferences
+  useEffect(() => {
+    (async () => {
+      if (!(hasCheckedPlugins && hasCheckedPreferences)) {
+        return;
+      }
+
+      await LocalStorage.setItem(
+        `${environment.extensionName}-preferences`,
+        JSON.stringify({
+          pinned: pinnedResults,
+          searchScope,
+          isShowingDetail,
+        })
+      );
+    })();
+  }, [pinnedResults, searchScope, isShowingDetail, hasCheckedPlugins, hasCheckedPreferences]);
+
+  // perform search
+  usePromise(
+    searchSpotlight,
+    [
+      searchText,
+      searchScope,
+      abortable,
+      (result: SpotlightSearchResult) => {
+        setResults((results) => [result, ...results].sort(lastUsedSort));
+      },
+    ],
+    {
+      onWillExecute: () => {
+        setIsQuerying(true);
+        setCanExecute(false);
+      },
+      onData: () => {
+        setIsQuerying(false);
+      },
+      onError: (e) => {
+        if (e.name !== "AbortError") {
+          showToast({
+            title: "An Error Occurred",
+            message: "Something went wrong. Try again.",
+            style: Toast.Style.Failure,
+          });
+        }
+
+        setIsQuerying(false);
+      },
+      execute: hasCheckedPlugins && hasCheckedPreferences && canExecute && !!searchText,
+      abortable,
+    }
+  );
+
+  // Handle search text and scope changes
+  useEffect(() => {
+    (async () => {
+      abortable.current?.abort();
+
+      setResults([]);
+      setIsQuerying(false);
+
+      // short-circuit for 'pinned'
+      if (searchScope === "pinned") {
+        setResults(
+          pinnedResults.filter((pin) =>
+            pin.kMDItemFSName.toLocaleLowerCase().includes(searchText.replace(/[[|\]]/gi, "").toLocaleLowerCase())
+          )
+        );
+        setCanExecute(false);
+      } else {
+        setCanExecute(true);
+      }
+    })();
+  }, [searchText, searchScope, pinnedResults]);
+
+  // Helper functions
+  const resultIsPinned = (result: SpotlightSearchResult) => {
+    return pinnedResults.map((pin) => pin.path).includes(result.path);
+  };
+
+  const removeResultFromPinnedResults = (result: SpotlightSearchResult) => {
+    setPinnedResults((pinnedResults) => pinnedResults.filter((pinnedResult) => pinnedResult.path !== result.path));
+  };
+
+  const toggleResultPinnedStatus = (result: SpotlightSearchResult, resultIndex: number) => {
+    if (!resultIsPinned(result)) {
+      setPinnedResults((pinnedResults) => [result, ...pinnedResults]);
+    } else {
+      removeResultFromPinnedResults(result);
+    }
+
+    setSelectedItemId(`result-${resultIndex.toString()}`);
+  };
+
+  const movePinUp = (result: SpotlightSearchResult, resultIndex: number) => {
+    const newIndex = resultIndex - 1;
+    const newPinnedResults = [...pinnedResults];
+    [newPinnedResults[resultIndex], newPinnedResults[newIndex]] = [
+      newPinnedResults[newIndex],
+      newPinnedResults[resultIndex],
+    ];
+    setPinnedResults(newPinnedResults);
+  };
+
+  const movePinDown = (result: SpotlightSearchResult, resultIndex: number) => {
+    const newIndex = resultIndex + 1;
+    const newPinnedResults = [...pinnedResults];
+    [newPinnedResults[resultIndex], newPinnedResults[newIndex]] = [
+      newPinnedResults[newIndex],
+      newPinnedResults[resultIndex],
+    ];
+    setPinnedResults(newPinnedResults);
+  };
+
+  return {
+    searchText,
+    setSearchText,
+    results,
+    isQuerying,
+    isShowingDetail,
+    setIsShowingDetail,
+    searchScope,
+    setSearchScope,
+    selectedItemId,
+    setSelectedItemId,
+    pinnedResults,
+    plugins,
+    resultIsPinned,
+    toggleResultPinnedStatus,
+    removeResultFromPinnedResults,
+    movePinUp,
+    movePinDown,
+    hasCheckedPlugins,
+    hasCheckedPreferences,
+  };
+}
diff --git a/extensions/folder-search/src/move.tsx b/extensions/folder-search/src/move.tsx
new file mode 100644
index 00000000000..0343fa0a1bf
--- /dev/null
+++ b/extensions/folder-search/src/move.tsx
@@ -0,0 +1,185 @@
+import { userInfo } from "os";
+import {
+  Action,
+  ActionPanel,
+  Form,
+  Icon,
+  List,
+  Toast,
+  closeMainWindow,
+  popToRoot,
+  showToast,
+  showHUD,
+  confirmAlert,
+  open,
+  getSelectedFinderItems,
+} from "@raycast/api";
+
+import path from "node:path";
+import fse from "fs-extra";
+
+import { useFolderSearch } from "./hooks/useFolderSearch";
+import { FolderListSection } from "./components/FolderListSection";
+import { folderName } from "./utils";
+import { SpotlightSearchResult } from "./types";
+
+export default function Command() {
+  const {
+    searchText,
+    setSearchText,
+    results,
+    isQuerying,
+    isShowingDetail,
+    setIsShowingDetail,
+    searchScope,
+    setSearchScope,
+    selectedItemId,
+    pinnedResults,
+    resultIsPinned,
+    hasCheckedPlugins,
+    hasCheckedPreferences,
+  } = useFolderSearch();
+
+  // Function to move selected Finder items to the selected folder
+  const moveSelectedFinderItemsToFolder = async (destinationFolder: string) => {
+    const selectedItems = await getSelectedFinderItems();
+
+    if (selectedItems.length === 0) {
+      await showHUD(`⚠️  No Finder selection to move.`);
+      return false;
+    }
+
+    let movedCount = 0;
+    let skippedCount = 0;
+
+    for (const item of selectedItems) {
+      const sourceFileName = path.basename(item.path);
+      const destinationFile = path.join(destinationFolder, sourceFileName);
+
+      try {
+        const exists = await fse.pathExists(destinationFile);
+        if (exists) {
+          const overwrite = await confirmAlert({
+            title: "Overwrite the existing file?",
+            message: sourceFileName + " already exists in " + destinationFolder,
+          });
+
+          if (overwrite) {
+            if (item.path === destinationFile) {
+              await showHUD("The source and destination file are the same");
+              skippedCount++;
+              continue;
+            }
+            await fse.move(item.path, destinationFile, { overwrite: true });
+            movedCount++;
+          } else {
+            await showHUD("Cancelling move for " + sourceFileName);
+            skippedCount++;
+            continue;
+          }
+        } else {
+          await fse.move(item.path, destinationFile);
+          movedCount++;
+        }
+      } catch (e) {
+        console.error("ERROR " + String(e));
+        await showToast(Toast.Style.Failure, "Error moving file " + String(e));
+        return false;
+      }
+    }
+
+    if (movedCount === 0) {
+      await showHUD(`No files were moved`);
+      return false;
+    } else if (skippedCount > 0) {
+      await showHUD(
+        `Moved ${movedCount} item(s) to ${path.basename(destinationFolder)}, skipped ${skippedCount} item(s)`
+      );
+    } else {
+      await showHUD(`Moved ${movedCount} item(s) to ${path.basename(destinationFolder)}`);
+    }
+
+    return movedCount > 0;
+  };
+
+  // Render actions for the folder list items
+  const renderFolderActions = (result: SpotlightSearchResult) => {
+    return (
+      <ActionPanel title={folderName(result)}>
+        <Action
+          title="Move to This Folder"
+          icon={Icon.ArrowRight}
+          shortcut={{ modifiers: ["cmd"], key: "return" }}
+          onAction={async () => {
+            const success = await moveSelectedFinderItemsToFolder(result.path);
+            if (success) {
+              open(result.path);
+              closeMainWindow();
+              popToRoot({ clearSearchBar: true });
+            }
+          }}
+        />
+        <Action.Open
+          title="Open"
+          icon={Icon.Folder}
+          target={result.path}
+          onOpen={() => popToRoot({ clearSearchBar: true })}
+        />
+        <Action.ShowInFinder
+          title="Show in Finder"
+          path={result.path}
+          onShow={() => popToRoot({ clearSearchBar: true })}
+        />
+        <Action
+          title="Toggle Details"
+          icon={Icon.Sidebar}
+          shortcut={{ modifiers: ["cmd", "shift"], key: "d" }}
+          onAction={() => setIsShowingDetail(!isShowingDetail)}
+        />
+      </ActionPanel>
+    );
+  };
+
+  return !(hasCheckedPlugins && hasCheckedPreferences) ? (
+    // prevent flicker due to details pref being async
+    <Form />
+  ) : (
+    <List
+      isLoading={isQuerying}
+      enableFiltering={false}
+      onSearchTextChange={setSearchText}
+      searchBarPlaceholder="Search for destination folder to move files"
+      isShowingDetail={isShowingDetail}
+      throttle={true}
+      searchText={searchText}
+      selectedItemId={selectedItemId}
+      searchBarAccessory={
+        hasCheckedPlugins && hasCheckedPreferences ? (
+          <List.Dropdown tooltip="Scope" onChange={setSearchScope} value={searchScope}>
+            <List.Dropdown.Item title="Pinned" value="pinned" />
+            <List.Dropdown.Item title="This Mac" value="" />
+            <List.Dropdown.Item title={`User (${userInfo().username})`} value={userInfo().homedir} />
+          </List.Dropdown>
+        ) : null
+      }
+    >
+      {!searchText ? (
+        <FolderListSection
+          title="Pinned"
+          results={pinnedResults}
+          isShowingDetail={isShowingDetail}
+          resultIsPinned={resultIsPinned}
+          renderActions={renderFolderActions}
+        />
+      ) : (
+        <FolderListSection
+          title="Results"
+          results={results}
+          isShowingDetail={isShowingDetail}
+          resultIsPinned={resultIsPinned}
+          renderActions={renderFolderActions}
+        />
+      )}
+    </List>
+  );
+}
diff --git a/extensions/folder-search/src/search.tsx b/extensions/folder-search/src/search.tsx
index e24e8b0df79..09d057e1cc6 100644
--- a/extensions/folder-search/src/search.tsx
+++ b/extensions/folder-search/src/search.tsx
@@ -7,40 +7,26 @@ import {
   Form,
   Icon,
   List,
-  LocalStorage,
   Toast,
   closeMainWindow,
-  environment,
   popToRoot,
   showToast,
-  showHUD,
   confirmAlert,
   open,
   getSelectedFinderItems,
   Keyboard,
 } from "@raycast/api";
 
-import { usePromise } from "@raycast/utils";
-import { useEffect, useRef, useState } from "react";
-
 import { runAppleScript } from "run-applescript";
+import path from "node:path";
+import fse from "fs-extra";
 
-import { searchSpotlight } from "./search-spotlight";
-import { FolderSearchPlugin, SpotlightSearchResult } from "./types";
+import { SpotlightSearchResult } from "./types";
 
-import {
-  loadPlugins,
-  folderName,
-  enclosingFolderName,
-  showFolderInfoInFinder,
-  copyFolderToClipboard,
-  maybeMoveResultToTrash,
-  lastUsedSort,
-  fixDoubleConcat,
-} from "./utils";
+import { folderName, showFolderInfoInFinder, copyFolderToClipboard, maybeMoveResultToTrash } from "./utils";
 
-import fse from "fs-extra";
-import path = require("node:path");
+import { useFolderSearch } from "./hooks/useFolderSearch";
+import { FolderListSection } from "./components/FolderListSection";
 
 // allow string indexing on Icons
 interface IconDictionary {
@@ -50,412 +36,199 @@ interface IconDictionary {
 const IconDictionaried: IconDictionary = Icon as IconDictionary;
 
 export default function Command() {
-  const [searchText, setSearchText] = useState<string>("");
-  const [pinnedResults, setPinnedResults] = useState<SpotlightSearchResult[]>([]);
-  const [selectedItemId, setSelectedItemId] = useState<string>("");
-  const [searchScope, setSearchScope] = useState<string>("");
-  const [isShowingDetail, setIsShowingDetail] = useState<boolean>(true);
-  const [results, setResults] = useState<SpotlightSearchResult[]>([]);
-
-  // hack to fix annoying double text during fallback. Typing helloworld results in helloworldhelloworld
-  let fixedText = "";
-  useEffect(() => {
-    fixedText = fixDoubleConcat(searchText);
-
-    if (fixedText !== searchText) {
-      setSearchText(fixedText); // Update the state of searchText
+  const {
+    searchText,
+    setSearchText,
+    results,
+    isQuerying,
+    isShowingDetail,
+    setIsShowingDetail,
+    searchScope,
+    setSearchScope,
+    selectedItemId,
+    pinnedResults,
+    plugins,
+    resultIsPinned,
+    toggleResultPinnedStatus,
+    removeResultFromPinnedResults,
+    movePinUp,
+    movePinDown,
+    hasCheckedPlugins,
+    hasCheckedPreferences,
+  } = useFolderSearch();
+
+  const sendFinderSelectionToFolder = async (destinationFolder: string) => {
+    const selectedItems = await getSelectedFinderItems();
+
+    if (selectedItems.length === 0) {
+      await showToast(Toast.Style.Failure, "No Finder selection to send");
+      return;
     }
-  }, [searchText]);
-
-  const [plugins, setPlugins] = useState<FolderSearchPlugin[]>([]);
-
-  const [isQuerying, setIsQuerying] = useState<boolean>(false);
-  const [canExecute, setCanExecute] = useState<boolean>(false);
 
-  const [hasCheckedPlugins, setHasCheckedPlugins] = useState<boolean>(false);
-  const [hasCheckedPreferences, setHasCheckedPreferences] = useState<boolean>(false);
+    for (const item of selectedItems) {
+      const sourceFileName = path.basename(item.path);
+      const destinationFile = path.join(destinationFolder, sourceFileName);
 
-  const abortable = useRef<AbortController>();
-
-  // check plugins
-  usePromise(
-    async () => {
-      const plugins = await loadPlugins();
-      setPlugins(plugins);
-    },
-    [],
-    {
-      onData() {
-        setHasCheckedPlugins(true);
-      },
-      onError() {
-        showToast({
-          title: "An Error Occured",
-          message: "Could not read plugins",
-          style: Toast.Style.Failure,
-        });
-
-        setHasCheckedPlugins(true);
-      },
-    }
-  );
-
-  // check prefs
-  usePromise(
-    async () => {
-      const maybePreferences = await LocalStorage.getItem(`${environment.extensionName}-preferences`);
-
-      if (maybePreferences) {
-        try {
-          return JSON.parse(maybePreferences as string);
-        } catch (_) {
-          // noop
-        }
-      }
-    },
-    [],
-    {
-      onData(preferences) {
-        setPinnedResults(preferences?.pinned || []);
-        setSearchScope(preferences?.searchScope || "");
-        setIsShowingDetail(preferences?.isShowingDetail);
-        setHasCheckedPreferences(true);
-      },
-      onError() {
-        showToast({
-          title: "An Error Occured",
-          message: "Could not read preferences",
-          style: Toast.Style.Failure,
-        });
-
-        setHasCheckedPreferences(true);
-      },
-    }
-  );
-
-  // perform search
-  usePromise(
-    searchSpotlight,
-    [
-      searchText,
-      searchScope,
-      abortable,
-      (result: SpotlightSearchResult) => {
-        setResults((results) => [result, ...results].sort(lastUsedSort));
-      },
-    ],
-    {
-      onWillExecute: () => {
-        setIsQuerying(true);
-        setCanExecute(false);
-      },
-      onData: () => {
-        setIsQuerying(false);
-      },
-      onError: (e) => {
-        if (e.name !== "AbortError") {
-          showToast({
-            title: "An Error Occured",
-            message: "Something went wrong. Try again.",
-            style: Toast.Style.Failure,
+      try {
+        const exists = await fse.pathExists(destinationFile);
+        if (exists) {
+          const overwrite = await confirmAlert({
+            title: "Overwrite the existing file?",
+            message: sourceFileName + " already exists in " + destinationFolder,
           });
-        }
-
-        setIsQuerying(false);
-      },
-      execute: hasCheckedPlugins && hasCheckedPreferences && canExecute && !!searchText,
-      abortable,
-    }
-  );
-
-  // save preferences
-  useEffect(() => {
-    (async () => {
-      if (!(hasCheckedPlugins && hasCheckedPreferences)) {
-        return;
-      }
 
-      await LocalStorage.setItem(
-        `${environment.extensionName}-preferences`,
-        JSON.stringify({
-          pinned: pinnedResults,
-          searchScope,
-          isShowingDetail,
-        })
-      );
-    })();
-  }, [pinnedResults, searchScope, isShowingDetail]);
-
-  useEffect(() => {
-    (async () => {
-      abortable.current?.abort();
-
-      setResults([]);
-      setIsQuerying(false);
+          if (overwrite) {
+            if (item.path === destinationFile) {
+              await showToast(Toast.Style.Failure, "The source and destination file are the same");
+              continue;
+            }
+            fse.moveSync(item.path, destinationFile, { overwrite: true });
+            await showToast(Toast.Style.Success, "Moved file " + path.basename(item.path) + " to " + destinationFolder);
+          } else {
+            await showToast(Toast.Style.Failure, "Cancelling move");
+          }
+        } else {
+          fse.moveSync(item.path, destinationFile);
+          await showToast(Toast.Style.Success, "Moved file " + sourceFileName + " to " + destinationFolder);
+        }
 
-      // short-circuit for 'pinned'
-      if (searchScope === "pinned") {
-        setResults(
-          pinnedResults.filter((pin) =>
-            pin.kMDItemFSName.toLocaleLowerCase().includes(searchText.replace(/[[|\]]/gi, "").toLocaleLowerCase())
-          )
-        );
-        setCanExecute(false);
-      } else {
-        setCanExecute(true);
+        open(destinationFolder);
+      } catch (e) {
+        console.error("ERROR " + String(e));
+        await showToast(Toast.Style.Failure, "Error moving file " + String(e));
       }
-    })();
-  }, [searchText, searchScope]);
-
-  const resultIsPinned = (result: SpotlightSearchResult) => {
-    return pinnedResults.map((pin) => pin.path).includes(result.path);
-  };
-
-  const removeResultFromPinnedResults = (result: SpotlightSearchResult) => {
-    setPinnedResults((pinnedResults) => pinnedResults.filter((pinnedResult) => pinnedResult.path !== result.path));
-  };
-
-  const toggleResultPinnedStatus = (result: SpotlightSearchResult, resultIndex: number) => {
-    if (!resultIsPinned(result)) {
-      setPinnedResults((pinnedResults) => [result, ...pinnedResults]);
-    } else {
-      // extracted out so we can re-use for maybeMoveResultToTrash calls
-      removeResultFromPinnedResults(result);
     }
 
-    setSelectedItemId(`result-${resultIndex.toString()}`);
-  };
-
-  const movePinUp = (result: SpotlightSearchResult, resultIndex: number) => {
-    const newIndex = resultIndex - 1;
-
-    [pinnedResults[resultIndex], pinnedResults[newIndex]] = [pinnedResults[newIndex], pinnedResults[resultIndex]];
-
-    setPinnedResults([...pinnedResults]);
-  };
-
-  const movePinDown = (result: SpotlightSearchResult, resultIndex: number) => {
-    const newIndex = resultIndex + 1;
-
-    [pinnedResults[resultIndex], pinnedResults[newIndex]] = [pinnedResults[newIndex], pinnedResults[resultIndex]];
-
-    setPinnedResults([...pinnedResults]);
+    closeMainWindow();
+    popToRoot({ clearSearchBar: true });
   };
 
-  // re-usable for results and 'pinned/favourites'
-  const ListSection = (props: { title: string; results: SpotlightSearchResult[] }) => {
+  // Render actions for the folder list items
+  const renderFolderActions = (result: SpotlightSearchResult, resultIndex: number) => {
     return (
-      <List.Section title={props.title}>
-        {props.results.map((result: SpotlightSearchResult, resultIndex: number) => (
-          <List.Item
-            id={`result-${resultIndex.toString()}`}
-            key={resultIndex}
-            title={folderName(result)}
-            subtitle={!isShowingDetail ? enclosingFolderName(result) : ""}
-            icon={{ fileIcon: result.path }}
-            accessories={resultIsPinned(result) ? [{ icon: { source: Icon.Star, tintColor: Color.Yellow } }] : []}
-            detail={
-              <List.Item.Detail
-                metadata={
-                  <List.Item.Detail.Metadata>
-                    <List.Item.Detail.Metadata.Label title="Metadata" />
-                    <List.Item.Detail.Metadata.Label title="Name" text={result.kMDItemFSName} />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label title="Where" text={result.path} />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label title="Type" text={result.kMDItemKind} />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Created"
-                      text={result.kMDItemFSCreationDate?.toLocaleString()}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Modified"
-                      text={result.kMDItemContentModificationDate?.toLocaleString()}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Last used"
-                      text={result.kMDItemLastUsedDate?.toLocaleString() || "-"}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Use count"
-                      text={result.kMDItemUseCount?.toLocaleString() || "-"}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                  </List.Item.Detail.Metadata>
-                }
+      <ActionPanel title={folderName(result)}>
+        <Action.Open
+          title="Open"
+          icon={Icon.Folder}
+          target={result.path}
+          onOpen={() => popToRoot({ clearSearchBar: true })}
+        />
+        <Action.ShowInFinder
+          title="Show in Finder"
+          path={result.path}
+          onShow={() => popToRoot({ clearSearchBar: true })}
+        />
+        <Action
+          title="Send Finder selection to Folder"
+          icon={Icon.Folder}
+          shortcut={{ modifiers: ["cmd", "shift"], key: "s" }}
+          onAction={() => sendFinderSelectionToFolder(result.path)}
+        />
+        <Action.OpenWith
+          title="Open With..."
+          shortcut={{ modifiers: ["cmd"], key: "o" }}
+          path={result.path}
+          onOpen={() => popToRoot({ clearSearchBar: true })}
+        />
+        <Action
+          title="Show Info in Finder"
+          icon={Icon.Finder}
+          shortcut={{ modifiers: ["cmd"], key: "i" }}
+          onAction={() => showFolderInfoInFinder(result)}
+        />
+        <Action
+          title="Toggle Details"
+          icon={Icon.Sidebar}
+          shortcut={{ modifiers: ["cmd", "shift"], key: "d" }}
+          onAction={() => setIsShowingDetail(!isShowingDetail)}
+        />
+        <Action
+          title={!resultIsPinned(result) ? "Pin" : "Unpin"}
+          icon={!resultIsPinned(result) ? Icon.Star : Icon.StarDisabled}
+          shortcut={{ modifiers: ["cmd", "shift"], key: "p" }}
+          onAction={() => toggleResultPinnedStatus(result, resultIndex)}
+        />
+        {resultIsPinned(result) && (
+          <>
+            {resultIndex > 0 && (
+              <Action
+                title="Move Pin Up"
+                icon={Icon.ArrowUpCircle}
+                shortcut={Keyboard.Shortcut.Common.MoveUp}
+                onAction={() => movePinUp(result, resultIndex)}
               />
-            }
-            actions={
-              <ActionPanel title={folderName(result)}>
-                <Action.Open
-                  title="Open"
-                  icon={Icon.Folder}
-                  target={result.path}
-                  onOpen={() => popToRoot({ clearSearchBar: true })}
-                />
-                <Action.ShowInFinder
-                  title="Show in Finder"
-                  path={result.path}
-                  onShow={() => popToRoot({ clearSearchBar: true })}
-                />
-                <Action
-                  title="Send Finder selection to Folder"
-                  icon={Icon.Folder}
-                  shortcut={{ modifiers: ["cmd", "shift"], key: "s" }}
-                  onAction={async () => {
-                    const selectedItems = await getSelectedFinderItems();
-
-                    if (selectedItems.length === 0) {
-                      await showHUD(`⚠️  No Finder selection to send.`);
-                    } else {
-                      for (const item of selectedItems) {
-                        // Source path = item
-                        // Source file name = path.basename(item)
-                        //
-                        // Destination folder = result.path
-                        // Destination file = result.path + '/' + path.basename(item)
-
-                        const sourceFileName = path.basename(item.path);
-                        const destinationFolder = result.path;
-                        const destinationFile = result.path + "/" + path.basename(item.path);
-
-                        try {
-                          const exists = await fse.pathExists(destinationFile);
-                          if (exists) {
-                            const overwrite = await confirmAlert({
-                              title: "Ooverwrite the existing file?",
-                              message: sourceFileName + " already exists in " + destinationFolder,
-                            });
-
-                            if (overwrite) {
-                              if (item.path == destinationFile) {
-                                await showHUD("The source and destination file are the same");
-                              }
-                              fse.moveSync(item.path, destinationFile, { overwrite: true });
-                              await showHUD("Moved file " + path.basename(item.path) + " to " + destinationFolder);
-                            } else {
-                              await showHUD("Cancelling move");
-                            }
-                          } else {
-                            fse.moveSync(item.path, destinationFile);
-                            await showHUD("Moved file " + sourceFileName + " to " + destinationFolder);
-                          }
-
-                          open(result.path);
-                        } catch (e) {
-                          console.error("ERROR " + String(e));
-                          await showToast(Toast.Style.Failure, "Error moving file " + String(e));
-                        }
-                      }
-                    }
-
-                    closeMainWindow();
-                    popToRoot({ clearSearchBar: true });
-                  }}
-                />
-                <Action.OpenWith
-                  title="Open With..."
-                  shortcut={{ modifiers: ["cmd"], key: "o" }}
-                  path={result.path}
-                  onOpen={() => popToRoot({ clearSearchBar: true })}
-                />
-                <Action
-                  title="Show Info in Finder"
-                  icon={Icon.Finder}
-                  shortcut={{ modifiers: ["cmd"], key: "i" }}
-                  onAction={() => showFolderInfoInFinder(result)}
-                />
-                <Action
-                  title="Toggle Details"
-                  icon={Icon.Sidebar}
-                  shortcut={{ modifiers: ["cmd", "shift"], key: "d" }}
-                  onAction={() => setIsShowingDetail(!isShowingDetail)}
-                />
-                <Action
-                  title={!resultIsPinned(result) ? "Pin" : "Unpin"}
-                  icon={!resultIsPinned(result) ? Icon.Star : Icon.StarDisabled}
-                  shortcut={{ modifiers: ["cmd", "shift"], key: "p" }}
-                  onAction={() => toggleResultPinnedStatus(result, resultIndex)}
-                />
-                {props.title === "Pinned" && (
-                  <>
-                    {resultIndex > 0 && (
-                      <Action
-                        title="Move Pin Up"
-                        icon={Icon.ArrowUpCircle}
-                        shortcut={Keyboard.Shortcut.Common.MoveUp}
-                        onAction={() => movePinUp(result, resultIndex)}
-                      />
-                    )}
-                    {resultIndex < props.results.length - 1 && (
-                      <Action
-                        title="Move Pin Down"
-                        icon={Icon.ArrowDownCircle}
-                        shortcut={Keyboard.Shortcut.Common.MoveDown}
-                        onAction={() => movePinDown(result, resultIndex)}
-                      />
-                    )}
-                  </>
-                )}
-                <ActionPanel.Section>
-                  <Action.CopyToClipboard
-                    title="Copy Folder"
-                    shortcut={{ modifiers: ["cmd"], key: "." }}
-                    content={``}
-                    onCopy={() => copyFolderToClipboard(result)}
-                  />
-                  <Action.CopyToClipboard
-                    title="Copy Name"
-                    shortcut={{ modifiers: ["cmd", "shift"], key: "." }}
-                    content={folderName(result)}
-                  />
-                  <Action.CopyToClipboard
-                    title="Copy Path"
-                    shortcut={{ modifiers: ["cmd", "shift"], key: "," }}
-                    content={result.path}
-                  />
-                </ActionPanel.Section>
-                <ActionPanel.Section>
-                  <Action.CreateQuicklink
-                    title="Create Quicklink"
-                    icon={Icon.Link}
-                    shortcut={{ modifiers: ["cmd", "shift"], key: "l" }}
-                    quicklink={{ link: result.path, name: folderName(result) }}
-                  />
-                </ActionPanel.Section>
-                <ActionPanel.Section>
-                  <Action
-                    title="Move to Trash"
-                    style={Action.Style.Destructive}
-                    icon={{ source: Icon.Trash, tintColor: Color.Red }}
-                    shortcut={{ modifiers: ["ctrl"], key: "x" }}
-                    onAction={() => maybeMoveResultToTrash(result, () => removeResultFromPinnedResults(result))}
-                  />
-                </ActionPanel.Section>
-                <ActionPanel.Section title="Plugins">
-                  {plugins.map((plugin: FolderSearchPlugin, pluginIndex) => (
-                    <Action
-                      key={pluginIndex}
-                      title={plugin.title}
-                      icon={IconDictionaried[plugin.icon]}
-                      shortcut={{ ...plugin.shortcut }}
-                      onAction={() => {
-                        popToRoot({ clearSearchBar: true });
-                        closeMainWindow({ clearRootSearch: true });
-                        runAppleScript(plugin.appleScript(result));
-                      }}
-                    />
-                  ))}
-                </ActionPanel.Section>
-              </ActionPanel>
-            }
+            )}
+            {resultIndex < pinnedResults.length - 1 && (
+              <Action
+                title="Move Pin Down"
+                icon={Icon.ArrowDownCircle}
+                shortcut={Keyboard.Shortcut.Common.MoveDown}
+                onAction={() => movePinDown(result, resultIndex)}
+              />
+            )}
+          </>
+        )}
+        <ActionPanel.Section>
+          <Action.CopyToClipboard
+            title="Copy Folder"
+            shortcut={{ modifiers: ["cmd"], key: "." }}
+            content={``}
+            onCopy={() => copyFolderToClipboard(result)}
+          />
+          <Action.CopyToClipboard
+            title="Copy Name"
+            shortcut={{ modifiers: ["cmd", "shift"], key: "." }}
+            content={folderName(result)}
+          />
+          <Action.CopyToClipboard
+            title="Copy Path"
+            shortcut={{ modifiers: ["cmd", "shift"], key: "," }}
+            content={result.path}
           />
-        ))}
-      </List.Section>
+        </ActionPanel.Section>
+        <ActionPanel.Section>
+          <Action.CreateQuicklink
+            title="Create Quicklink"
+            icon={Icon.Link}
+            shortcut={{ modifiers: ["cmd", "shift"], key: "l" }}
+            quicklink={{ link: result.path, name: folderName(result) }}
+          />
+        </ActionPanel.Section>
+        <ActionPanel.Section>
+          <Action
+            title="Move to Trash"
+            style={Action.Style.Destructive}
+            icon={{ source: Icon.Trash, tintColor: Color.Red }}
+            shortcut={{ modifiers: ["ctrl"], key: "x" }}
+            onAction={() => maybeMoveResultToTrash(result, () => removeResultFromPinnedResults(result))}
+          />
+        </ActionPanel.Section>
+        <ActionPanel.Section title="Plugins">
+          {plugins.map(
+            (
+              plugin: {
+                title: string;
+                icon: string;
+                shortcut: Keyboard.Shortcut;
+                appleScript: (result: SpotlightSearchResult) => string;
+              },
+              pluginIndex: number
+            ) => (
+              <Action
+                key={pluginIndex}
+                title={plugin.title}
+                icon={IconDictionaried[plugin.icon]}
+                shortcut={{ ...plugin.shortcut }}
+                onAction={() => {
+                  popToRoot({ clearSearchBar: true });
+                  closeMainWindow({ clearRootSearch: true });
+                  runAppleScript(plugin.appleScript(result));
+                }}
+              />
+            )
+          )}
+        </ActionPanel.Section>
+      </ActionPanel>
     );
   };
 
@@ -483,9 +256,21 @@ export default function Command() {
       }
     >
       {!searchText ? (
-        <ListSection title="Pinned" results={pinnedResults} />
+        <FolderListSection
+          title="Pinned"
+          results={pinnedResults}
+          isShowingDetail={isShowingDetail}
+          resultIsPinned={resultIsPinned}
+          renderActions={renderFolderActions}
+        />
       ) : (
-        <ListSection title="Results" results={results} />
+        <FolderListSection
+          title="Results"
+          results={results}
+          isShowingDetail={isShowingDetail}
+          resultIsPinned={resultIsPinned}
+          renderActions={renderFolderActions}
+        />
       )}
     </List>
   );


Reviews:
user: login: greptile-apps[bot]
body: 
## PR Summary

This PR adds a new command to move selected Finder items to a destination folder, leveraging the existing Folder Search extension's interface and functionality.

- The CHANGELOG entry `## [New Features] - 2025-03-12` should use `{PR_MERGE_DATE}` instead of a hardcoded future date
- The `move` command in `package.json` should include a `subtitle` field with "Folder Search" to maintain consistency across multiple commands
- The `moveSelectedFinderItemsToFolder` function in `move.tsx` should use `showFailureToast` from `@raycast/utils` instead of manual `showToast` for error handling
- Consider adding metadata screenshots for the new `move` command view in the `metadata` folder
- The new feature appears to duplicate functionality from a previous changelog entry (2022-12-15), suggesting potential redundancy or confusion in feature tracking

<sub>💡 (1/5) You can manually trigger the bot by mentioning @greptileai in a comment!</sub>

<sub>7 file(s) reviewed, 15 comment(s)</sub>
<sub>[Edit PR Review Bot Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews&utm_content=extensions_17807)</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T04:53:15Z
user: login: pernielsentikaer
body: Hi 👋 

Looks good to me, approved 🔥
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-13T09:34:26Z

Review comments:
diff_hunk: @@ -25,6 +25,12 @@
       "title": "Search Folders",
       "description": "Search for folders on your Mac",
       "mode": "view"
+    },
+    {
+      "name": "move",
+      "title": "Move to a Folder",
+      "description": "Move selected files to a folder",
+      "mode": "view"
     }
path: extensions/folder-search/package.json
user: login: greptile-apps[bot]
body: style: Consider adding a subtitle to differentiate this command from the search command, e.g. subtitle: 'Folder Search'

```suggestion
      "name": "move",
      "title": "Move to a Folder",
      "subtitle": "Folder Search",
      "description": "Move selected files to a folder",
      "mode": "view"
    }
```
created_at: 2025-03-13T04:49:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,5 +1,9 @@
 # Folder Search Changelog
 
+## [New Features] - 2025-03-12
+
+- **added** 'Move' action for selected finder items
path: extensions/folder-search/CHANGELOG.md
user: login: greptile-apps[bot]
body: syntax: The date '2025-03-12' is in the future. Should use '{PR_MERGE_DATE}' instead of hardcoding a future date.

```suggestion
## [New Features] - {PR_MERGE_DATE}

- **added** 'Move' action for selected finder items
```
created_at: 2025-03-13T04:49:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,71 @@
+import { Action, ActionPanel, Color, Icon, List } from "@raycast/api";
+import { folderName, enclosingFolderName } from "../utils";
+import { ReactNode } from "react";
+import { SpotlightSearchResult } from "../types";
+
+interface FolderListSectionProps {
+  title: string;
+  results: SpotlightSearchResult[];
+  isShowingDetail: boolean;
+  resultIsPinned: (result: SpotlightSearchResult) => boolean;
+  renderActions: (result: SpotlightSearchResult, resultIndex: number) => ReactNode;
+}
+
+export function FolderListSection({
+  title,
+  results,
+  isShowingDetail,
+  resultIsPinned,
+  renderActions,
+}: FolderListSectionProps) {
+  return (
+    <List.Section title={title}>
+      {results.map((result: SpotlightSearchResult, resultIndex: number) => (
+        <List.Item
+          id={`result-${resultIndex.toString()}`}
+          key={resultIndex}
path: extensions/folder-search/src/components/FolderListSection.tsx
user: login: greptile-apps[bot]
body: style: Using index as key can lead to rendering issues if items are reordered. Consider using result.path as it's likely unique

```suggestion
          id={`result-${resultIndex.toString()}`}
          key={result.path}
```
created_at: 2025-03-13T04:49:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,71 @@
+import { Action, ActionPanel, Color, Icon, List } from "@raycast/api";
+import { folderName, enclosingFolderName } from "../utils";
+import { ReactNode } from "react";
+import { SpotlightSearchResult } from "../types";
+
+interface FolderListSectionProps {
+  title: string;
+  results: SpotlightSearchResult[];
+  isShowingDetail: boolean;
+  resultIsPinned: (result: SpotlightSearchResult) => boolean;
+  renderActions: (result: SpotlightSearchResult, resultIndex: number) => ReactNode;
+}
+
+export function FolderListSection({
+  title,
+  results,
+  isShowingDetail,
+  resultIsPinned,
+  renderActions,
+}: FolderListSectionProps) {
+  return (
+    <List.Section title={title}>
+      {results.map((result: SpotlightSearchResult, resultIndex: number) => (
+        <List.Item
+          id={`result-${resultIndex.toString()}`}
+          key={resultIndex}
+          title={folderName(result)}
+          subtitle={!isShowingDetail ? enclosingFolderName(result) : ""}
+          icon={{ fileIcon: result.path }}
+          accessories={resultIsPinned(result) ? [{ icon: { source: Icon.Star, tintColor: Color.Yellow } }] : []}
+          detail={
+            <List.Item.Detail
+              metadata={
+                <List.Item.Detail.Metadata>
+                  <List.Item.Detail.Metadata.Label title="Metadata" />
+                  <List.Item.Detail.Metadata.Label title="Name" text={result.kMDItemFSName} />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label title="Where" text={result.path} />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label title="Type" text={result.kMDItemKind} />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label
+                    title="Created"
+                    text={result.kMDItemFSCreationDate?.toLocaleString()}
+                  />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label
+                    title="Modified"
+                    text={result.kMDItemContentModificationDate?.toLocaleString()}
+                  />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label
+                    title="Last used"
+                    text={result.kMDItemLastUsedDate?.toLocaleString() || "-"}
+                  />
+                  <List.Item.Detail.Metadata.Separator />
+                  <List.Item.Detail.Metadata.Label
+                    title="Use count"
+                    text={result.kMDItemUseCount?.toLocaleString() || "-"}
+                  />
+                  <List.Item.Detail.Metadata.Separator />
path: extensions/folder-search/src/components/FolderListSection.tsx
user: login: greptile-apps[bot]
body: style: Last separator is unnecessary since it's at the end of the metadata list
created_at: 2025-03-13T04:49:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,222 @@
+import { useEffect, useRef, useState } from "react";
+import { LocalStorage, Toast, environment, showToast } from "@raycast/api";
+import { usePromise } from "@raycast/utils";
+import { FolderSearchPlugin, SpotlightSearchResult } from "../types";
+import { loadPlugins, lastUsedSort, fixDoubleConcat } from "../utils";
+import { searchSpotlight } from "../search-spotlight";
+
+export function useFolderSearch() {
+  const [searchText, setSearchText] = useState<string>("");
+  const [pinnedResults, setPinnedResults] = useState<SpotlightSearchResult[]>([]);
+  const [selectedItemId, setSelectedItemId] = useState<string>("");
+  const [searchScope, setSearchScope] = useState<string>("");
+  const [isShowingDetail, setIsShowingDetail] = useState<boolean>(true);
+  const [results, setResults] = useState<SpotlightSearchResult[]>([]);
+  const [plugins, setPlugins] = useState<FolderSearchPlugin[]>([]);
+  const [isQuerying, setIsQuerying] = useState<boolean>(false);
+  const [canExecute, setCanExecute] = useState<boolean>(false);
+  const [hasCheckedPlugins, setHasCheckedPlugins] = useState<boolean>(false);
+  const [hasCheckedPreferences, setHasCheckedPreferences] = useState<boolean>(false);
+
+  const abortable = useRef<AbortController>();
+
+  // handle double text issue
+  useEffect(() => {
+    const fixedText = fixDoubleConcat(searchText);
+    if (fixedText !== searchText) {
+      setSearchText(fixedText);
+    }
+  }, [searchText]);
+
+  // check plugins
+  usePromise(
+    async () => {
+      const plugins = await loadPlugins();
+      setPlugins(plugins);
+    },
+    [],
+    {
+      onData() {
+        setHasCheckedPlugins(true);
+      },
+      onError() {
+        showToast({
+          title: "An Error Occurred",
+          message: "Could not read plugins",
+          style: Toast.Style.Failure,
+        });
+
+        setHasCheckedPlugins(true);
+      },
+    }
+  );
+
+  // check prefs
+  usePromise(
+    async () => {
+      const maybePreferences = await LocalStorage.getItem(`${environment.extensionName}-preferences`);
+
+      if (maybePreferences) {
+        try {
+          return JSON.parse(maybePreferences as string);
+        } catch (_) {
+          // noop
+        }
+      }
+    },
+    [],
+    {
+      onData(preferences) {
+        setPinnedResults(preferences?.pinned || []);
+        setSearchScope(preferences?.searchScope || "");
+        setIsShowingDetail(preferences?.isShowingDetail);
+        setHasCheckedPreferences(true);
+      },
+      onError() {
+        showToast({
+          title: "An Error Occurred",
+          message: "Could not read preferences",
+          style: Toast.Style.Failure,
+        });
+
+        setHasCheckedPreferences(true);
+      },
+    }
+  );
+
+  // Save preferences
+  useEffect(() => {
+    (async () => {
+      if (!(hasCheckedPlugins && hasCheckedPreferences)) {
+        return;
+      }
+
+      await LocalStorage.setItem(
+        `${environment.extensionName}-preferences`,
+        JSON.stringify({
+          pinned: pinnedResults,
+          searchScope,
+          isShowingDetail,
+        })
+      );
+    })();
+  }, [pinnedResults, searchScope, isShowingDetail, hasCheckedPlugins, hasCheckedPreferences]);
+
+  // perform search
+  usePromise(
+    searchSpotlight,
+    [
+      searchText,
+      searchScope,
+      abortable,
+      (result: SpotlightSearchResult) => {
+        setResults((results) => [result, ...results].sort(lastUsedSort));
+      },
+    ],
+    {
+      onWillExecute: () => {
+        setIsQuerying(true);
+        setCanExecute(false);
+      },
+      onData: () => {
+        setIsQuerying(false);
+      },
+      onError: (e) => {
+        if (e.name !== "AbortError") {
+          showToast({
+            title: "An Error Occurred",
+            message: "Something went wrong. Try again.",
+            style: Toast.Style.Failure,
+          });
+        }
+
+        setIsQuerying(false);
+      },
+      execute: hasCheckedPlugins && hasCheckedPreferences && canExecute && !!searchText,
+      abortable,
+    }
+  );
+
+  // Handle search text and scope changes
+  useEffect(() => {
+    (async () => {
+      abortable.current?.abort();
+
+      setResults([]);
+      setIsQuerying(false);
+
+      // short-circuit for 'pinned'
+      if (searchScope === "pinned") {
+        setResults(
+          pinnedResults.filter((pin) =>
+            pin.kMDItemFSName.toLocaleLowerCase().includes(searchText.replace(/[[|\]]/gi, "").toLocaleLowerCase())
+          )
+        );
+        setCanExecute(false);
+      } else {
+        setCanExecute(true);
+      }
+    })();
+  }, [searchText, searchScope, pinnedResults]);
+
+  // Helper functions
+  const resultIsPinned = (result: SpotlightSearchResult) => {
+    return pinnedResults.map((pin) => pin.path).includes(result.path);
+  };
+
+  const removeResultFromPinnedResults = (result: SpotlightSearchResult) => {
+    setPinnedResults((pinnedResults) => pinnedResults.filter((pinnedResult) => pinnedResult.path !== result.path));
+  };
+
+  const toggleResultPinnedStatus = (result: SpotlightSearchResult, resultIndex: number) => {
+    if (!resultIsPinned(result)) {
+      setPinnedResults((pinnedResults) => [result, ...pinnedResults]);
+    } else {
+      removeResultFromPinnedResults(result);
+    }
+
+    setSelectedItemId(`result-${resultIndex.toString()}`);
+  };
+
+  const movePinUp = (result: SpotlightSearchResult, resultIndex: number) => {
+    const newIndex = resultIndex - 1;
+    const newPinnedResults = [...pinnedResults];
+    [newPinnedResults[resultIndex], newPinnedResults[newIndex]] = [
+      newPinnedResults[newIndex],
+      newPinnedResults[resultIndex],
+    ];
+    setPinnedResults(newPinnedResults);
+  };
path: extensions/folder-search/src/hooks/useFolderSearch.tsx
user: login: greptile-apps[bot]
body: logic: No bounds checking for newIndex could cause array out of bounds error if resultIndex is 0

```suggestion
  const movePinUp = (result: SpotlightSearchResult, resultIndex: number) => {
    if (resultIndex <= 0) return;
    const newIndex = resultIndex - 1;
    const newPinnedResults = [...pinnedResults];
    [newPinnedResults[resultIndex], newPinnedResults[newIndex]] = [
      newPinnedResults[newIndex],
      newPinnedResults[resultIndex],
    ];
    setPinnedResults(newPinnedResults);
  };
```
created_at: 2025-03-13T04:52:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,222 @@
+import { useEffect, useRef, useState } from "react";
+import { LocalStorage, Toast, environment, showToast } from "@raycast/api";
+import { usePromise } from "@raycast/utils";
+import { FolderSearchPlugin, SpotlightSearchResult } from "../types";
+import { loadPlugins, lastUsedSort, fixDoubleConcat } from "../utils";
+import { searchSpotlight } from "../search-spotlight";
+
+export function useFolderSearch() {
+  const [searchText, setSearchText] = useState<string>("");
+  const [pinnedResults, setPinnedResults] = useState<SpotlightSearchResult[]>([]);
+  const [selectedItemId, setSelectedItemId] = useState<string>("");
+  const [searchScope, setSearchScope] = useState<string>("");
+  const [isShowingDetail, setIsShowingDetail] = useState<boolean>(true);
+  const [results, setResults] = useState<SpotlightSearchResult[]>([]);
+  const [plugins, setPlugins] = useState<FolderSearchPlugin[]>([]);
+  const [isQuerying, setIsQuerying] = useState<boolean>(false);
+  const [canExecute, setCanExecute] = useState<boolean>(false);
+  const [hasCheckedPlugins, setHasCheckedPlugins] = useState<boolean>(false);
+  const [hasCheckedPreferences, setHasCheckedPreferences] = useState<boolean>(false);
+
+  const abortable = useRef<AbortController>();
+
+  // handle double text issue
+  useEffect(() => {
+    const fixedText = fixDoubleConcat(searchText);
+    if (fixedText !== searchText) {
+      setSearchText(fixedText);
+    }
+  }, [searchText]);
+
+  // check plugins
+  usePromise(
+    async () => {
+      const plugins = await loadPlugins();
+      setPlugins(plugins);
+    },
+    [],
+    {
+      onData() {
+        setHasCheckedPlugins(true);
+      },
+      onError() {
+        showToast({
+          title: "An Error Occurred",
+          message: "Could not read plugins",
+          style: Toast.Style.Failure,
+        });
+
+        setHasCheckedPlugins(true);
+      },
+    }
+  );
+
+  // check prefs
+  usePromise(
+    async () => {
+      const maybePreferences = await LocalStorage.getItem(`${environment.extensionName}-preferences`);
+
+      if (maybePreferences) {
+        try {
+          return JSON.parse(maybePreferences as string);
+        } catch (_) {
+          // noop
+        }
+      }
+    },
+    [],
+    {
+      onData(preferences) {
+        setPinnedResults(preferences?.pinned || []);
+        setSearchScope(preferences?.searchScope || "");
+        setIsShowingDetail(preferences?.isShowingDetail);
+        setHasCheckedPreferences(true);
+      },
+      onError() {
+        showToast({
+          title: "An Error Occurred",
+          message: "Could not read preferences",
+          style: Toast.Style.Failure,
+        });
+
+        setHasCheckedPreferences(true);
+      },
+    }
+  );
+
+  // Save preferences
+  useEffect(() => {
+    (async () => {
+      if (!(hasCheckedPlugins && hasCheckedPreferences)) {
+        return;
+      }
+
+      await LocalStorage.setItem(
+        `${environment.extensionName}-preferences`,
+        JSON.stringify({
+          pinned: pinnedResults,
+          searchScope,
+          isShowingDetail,
+        })
+      );
+    })();
+  }, [pinnedResults, searchScope, isShowingDetail, hasCheckedPlugins, hasCheckedPreferences]);
+
+  // perform search
+  usePromise(
+    searchSpotlight,
+    [
+      searchText,
+      searchScope,
+      abortable,
+      (result: SpotlightSearchResult) => {
+        setResults((results) => [result, ...results].sort(lastUsedSort));
+      },
+    ],
+    {
+      onWillExecute: () => {
+        setIsQuerying(true);
+        setCanExecute(false);
+      },
+      onData: () => {
+        setIsQuerying(false);
+      },
+      onError: (e) => {
+        if (e.name !== "AbortError") {
+          showToast({
+            title: "An Error Occurred",
+            message: "Something went wrong. Try again.",
+            style: Toast.Style.Failure,
+          });
+        }
+
+        setIsQuerying(false);
+      },
+      execute: hasCheckedPlugins && hasCheckedPreferences && canExecute && !!searchText,
+      abortable,
+    }
+  );
+
+  // Handle search text and scope changes
+  useEffect(() => {
+    (async () => {
+      abortable.current?.abort();
+
+      setResults([]);
+      setIsQuerying(false);
+
+      // short-circuit for 'pinned'
+      if (searchScope === "pinned") {
+        setResults(
+          pinnedResults.filter((pin) =>
+            pin.kMDItemFSName.toLocaleLowerCase().includes(searchText.replace(/[[|\]]/gi, "").toLocaleLowerCase())
+          )
+        );
+        setCanExecute(false);
+      } else {
+        setCanExecute(true);
+      }
+    })();
+  }, [searchText, searchScope, pinnedResults]);
+
+  // Helper functions
+  const resultIsPinned = (result: SpotlightSearchResult) => {
+    return pinnedResults.map((pin) => pin.path).includes(result.path);
+  };
+
+  const removeResultFromPinnedResults = (result: SpotlightSearchResult) => {
+    setPinnedResults((pinnedResults) => pinnedResults.filter((pinnedResult) => pinnedResult.path !== result.path));
+  };
+
+  const toggleResultPinnedStatus = (result: SpotlightSearchResult, resultIndex: number) => {
+    if (!resultIsPinned(result)) {
+      setPinnedResults((pinnedResults) => [result, ...pinnedResults]);
+    } else {
+      removeResultFromPinnedResults(result);
+    }
+
+    setSelectedItemId(`result-${resultIndex.toString()}`);
+  };
+
+  const movePinUp = (result: SpotlightSearchResult, resultIndex: number) => {
+    const newIndex = resultIndex - 1;
+    const newPinnedResults = [...pinnedResults];
+    [newPinnedResults[resultIndex], newPinnedResults[newIndex]] = [
+      newPinnedResults[newIndex],
+      newPinnedResults[resultIndex],
+    ];
+    setPinnedResults(newPinnedResults);
+  };
+
+  const movePinDown = (result: SpotlightSearchResult, resultIndex: number) => {
+    const newIndex = resultIndex + 1;
+    const newPinnedResults = [...pinnedResults];
+    [newPinnedResults[resultIndex], newPinnedResults[newIndex]] = [
+      newPinnedResults[newIndex],
+      newPinnedResults[resultIndex],
+    ];
+    setPinnedResults(newPinnedResults);
+  };
path: extensions/folder-search/src/hooks/useFolderSearch.tsx
user: login: greptile-apps[bot]
body: logic: No bounds checking for newIndex could cause array out of bounds error if resultIndex is last index

```suggestion
  const movePinDown = (result: SpotlightSearchResult, resultIndex: number) => {
    if (resultIndex >= pinnedResults.length - 1) return;
    const newIndex = resultIndex + 1;
    const newPinnedResults = [...pinnedResults];
    [newPinnedResults[resultIndex], newPinnedResults[newIndex]] = [
      newPinnedResults[newIndex],
      newPinnedResults[resultIndex],
    ];
    setPinnedResults(newPinnedResults);
  };
```
created_at: 2025-03-13T04:52:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,222 @@
+import { useEffect, useRef, useState } from "react";
+import { LocalStorage, Toast, environment, showToast } from "@raycast/api";
+import { usePromise } from "@raycast/utils";
+import { FolderSearchPlugin, SpotlightSearchResult } from "../types";
+import { loadPlugins, lastUsedSort, fixDoubleConcat } from "../utils";
+import { searchSpotlight } from "../search-spotlight";
+
+export function useFolderSearch() {
+  const [searchText, setSearchText] = useState<string>("");
+  const [pinnedResults, setPinnedResults] = useState<SpotlightSearchResult[]>([]);
+  const [selectedItemId, setSelectedItemId] = useState<string>("");
+  const [searchScope, setSearchScope] = useState<string>("");
+  const [isShowingDetail, setIsShowingDetail] = useState<boolean>(true);
+  const [results, setResults] = useState<SpotlightSearchResult[]>([]);
+  const [plugins, setPlugins] = useState<FolderSearchPlugin[]>([]);
+  const [isQuerying, setIsQuerying] = useState<boolean>(false);
+  const [canExecute, setCanExecute] = useState<boolean>(false);
+  const [hasCheckedPlugins, setHasCheckedPlugins] = useState<boolean>(false);
+  const [hasCheckedPreferences, setHasCheckedPreferences] = useState<boolean>(false);
+
+  const abortable = useRef<AbortController>();
+
+  // handle double text issue
+  useEffect(() => {
+    const fixedText = fixDoubleConcat(searchText);
+    if (fixedText !== searchText) {
+      setSearchText(fixedText);
+    }
+  }, [searchText]);
+
+  // check plugins
+  usePromise(
+    async () => {
+      const plugins = await loadPlugins();
+      setPlugins(plugins);
+    },
+    [],
+    {
+      onData() {
+        setHasCheckedPlugins(true);
+      },
+      onError() {
+        showToast({
+          title: "An Error Occurred",
+          message: "Could not read plugins",
+          style: Toast.Style.Failure,
+        });
+
+        setHasCheckedPlugins(true);
+      },
+    }
+  );
+
+  // check prefs
+  usePromise(
+    async () => {
+      const maybePreferences = await LocalStorage.getItem(`${environment.extensionName}-preferences`);
+
+      if (maybePreferences) {
+        try {
+          return JSON.parse(maybePreferences as string);
+        } catch (_) {
+          // noop
+        }
+      }
+    },
+    [],
+    {
+      onData(preferences) {
+        setPinnedResults(preferences?.pinned || []);
+        setSearchScope(preferences?.searchScope || "");
+        setIsShowingDetail(preferences?.isShowingDetail);
+        setHasCheckedPreferences(true);
+      },
+      onError() {
+        showToast({
+          title: "An Error Occurred",
+          message: "Could not read preferences",
+          style: Toast.Style.Failure,
+        });
+
+        setHasCheckedPreferences(true);
+      },
+    }
+  );
+
+  // Save preferences
+  useEffect(() => {
+    (async () => {
+      if (!(hasCheckedPlugins && hasCheckedPreferences)) {
+        return;
+      }
+
+      await LocalStorage.setItem(
+        `${environment.extensionName}-preferences`,
+        JSON.stringify({
+          pinned: pinnedResults,
+          searchScope,
+          isShowingDetail,
+        })
+      );
path: extensions/folder-search/src/hooks/useFolderSearch.tsx
user: login: greptile-apps[bot]
body: style: LocalStorage.setItem could throw but isn't wrapped in try-catch
created_at: 2025-03-13T04:52:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,222 @@
+import { useEffect, useRef, useState } from "react";
+import { LocalStorage, Toast, environment, showToast } from "@raycast/api";
+import { usePromise } from "@raycast/utils";
+import { FolderSearchPlugin, SpotlightSearchResult } from "../types";
+import { loadPlugins, lastUsedSort, fixDoubleConcat } from "../utils";
+import { searchSpotlight } from "../search-spotlight";
+
+export function useFolderSearch() {
+  const [searchText, setSearchText] = useState<string>("");
+  const [pinnedResults, setPinnedResults] = useState<SpotlightSearchResult[]>([]);
+  const [selectedItemId, setSelectedItemId] = useState<string>("");
+  const [searchScope, setSearchScope] = useState<string>("");
+  const [isShowingDetail, setIsShowingDetail] = useState<boolean>(true);
+  const [results, setResults] = useState<SpotlightSearchResult[]>([]);
+  const [plugins, setPlugins] = useState<FolderSearchPlugin[]>([]);
+  const [isQuerying, setIsQuerying] = useState<boolean>(false);
+  const [canExecute, setCanExecute] = useState<boolean>(false);
+  const [hasCheckedPlugins, setHasCheckedPlugins] = useState<boolean>(false);
+  const [hasCheckedPreferences, setHasCheckedPreferences] = useState<boolean>(false);
+
+  const abortable = useRef<AbortController>();
+
+  // handle double text issue
+  useEffect(() => {
+    const fixedText = fixDoubleConcat(searchText);
+    if (fixedText !== searchText) {
+      setSearchText(fixedText);
+    }
+  }, [searchText]);
+
+  // check plugins
+  usePromise(
+    async () => {
+      const plugins = await loadPlugins();
+      setPlugins(plugins);
+    },
+    [],
+    {
+      onData() {
+        setHasCheckedPlugins(true);
+      },
+      onError() {
+        showToast({
+          title: "An Error Occurred",
+          message: "Could not read plugins",
+          style: Toast.Style.Failure,
+        });
+
+        setHasCheckedPlugins(true);
+      },
+    }
+  );
+
+  // check prefs
+  usePromise(
+    async () => {
+      const maybePreferences = await LocalStorage.getItem(`${environment.extensionName}-preferences`);
+
+      if (maybePreferences) {
+        try {
+          return JSON.parse(maybePreferences as string);
+        } catch (_) {
+          // noop
+        }
+      }
+    },
+    [],
+    {
+      onData(preferences) {
+        setPinnedResults(preferences?.pinned || []);
+        setSearchScope(preferences?.searchScope || "");
+        setIsShowingDetail(preferences?.isShowingDetail);
+        setHasCheckedPreferences(true);
+      },
+      onError() {
+        showToast({
+          title: "An Error Occurred",
+          message: "Could not read preferences",
+          style: Toast.Style.Failure,
+        });
+
+        setHasCheckedPreferences(true);
+      },
+    }
+  );
+
+  // Save preferences
+  useEffect(() => {
+    (async () => {
+      if (!(hasCheckedPlugins && hasCheckedPreferences)) {
+        return;
+      }
+
+      await LocalStorage.setItem(
+        `${environment.extensionName}-preferences`,
+        JSON.stringify({
+          pinned: pinnedResults,
+          searchScope,
+          isShowingDetail,
+        })
+      );
+    })();
+  }, [pinnedResults, searchScope, isShowingDetail, hasCheckedPlugins, hasCheckedPreferences]);
+
+  // perform search
+  usePromise(
+    searchSpotlight,
+    [
+      searchText,
+      searchScope,
+      abortable,
+      (result: SpotlightSearchResult) => {
+        setResults((results) => [result, ...results].sort(lastUsedSort));
+      },
+    ],
+    {
+      onWillExecute: () => {
+        setIsQuerying(true);
+        setCanExecute(false);
+      },
+      onData: () => {
+        setIsQuerying(false);
+      },
+      onError: (e) => {
+        if (e.name !== "AbortError") {
+          showToast({
+            title: "An Error Occurred",
+            message: "Something went wrong. Try again.",
+            style: Toast.Style.Failure,
+          });
+        }
+
+        setIsQuerying(false);
+      },
+      execute: hasCheckedPlugins && hasCheckedPreferences && canExecute && !!searchText,
+      abortable,
+    }
+  );
+
+  // Handle search text and scope changes
+  useEffect(() => {
+    (async () => {
+      abortable.current?.abort();
+
+      setResults([]);
+      setIsQuerying(false);
+
+      // short-circuit for 'pinned'
+      if (searchScope === "pinned") {
+        setResults(
+          pinnedResults.filter((pin) =>
+            pin.kMDItemFSName.toLocaleLowerCase().includes(searchText.replace(/[[|\]]/gi, "").toLocaleLowerCase())
path: extensions/folder-search/src/hooks/useFolderSearch.tsx
user: login: greptile-apps[bot]
body: style: Case-insensitive search performed twice with duplicate toLocaleLowerCase() calls
created_at: 2025-03-13T04:52:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,222 @@
+import { useEffect, useRef, useState } from "react";
+import { LocalStorage, Toast, environment, showToast } from "@raycast/api";
+import { usePromise } from "@raycast/utils";
+import { FolderSearchPlugin, SpotlightSearchResult } from "../types";
+import { loadPlugins, lastUsedSort, fixDoubleConcat } from "../utils";
+import { searchSpotlight } from "../search-spotlight";
+
+export function useFolderSearch() {
+  const [searchText, setSearchText] = useState<string>("");
+  const [pinnedResults, setPinnedResults] = useState<SpotlightSearchResult[]>([]);
+  const [selectedItemId, setSelectedItemId] = useState<string>("");
+  const [searchScope, setSearchScope] = useState<string>("");
+  const [isShowingDetail, setIsShowingDetail] = useState<boolean>(true);
+  const [results, setResults] = useState<SpotlightSearchResult[]>([]);
+  const [plugins, setPlugins] = useState<FolderSearchPlugin[]>([]);
+  const [isQuerying, setIsQuerying] = useState<boolean>(false);
+  const [canExecute, setCanExecute] = useState<boolean>(false);
+  const [hasCheckedPlugins, setHasCheckedPlugins] = useState<boolean>(false);
+  const [hasCheckedPreferences, setHasCheckedPreferences] = useState<boolean>(false);
+
+  const abortable = useRef<AbortController>();
+
+  // handle double text issue
+  useEffect(() => {
+    const fixedText = fixDoubleConcat(searchText);
+    if (fixedText !== searchText) {
+      setSearchText(fixedText);
+    }
+  }, [searchText]);
+
+  // check plugins
+  usePromise(
+    async () => {
+      const plugins = await loadPlugins();
+      setPlugins(plugins);
+    },
+    [],
+    {
+      onData() {
+        setHasCheckedPlugins(true);
+      },
+      onError() {
+        showToast({
+          title: "An Error Occurred",
+          message: "Could not read plugins",
+          style: Toast.Style.Failure,
+        });
+
+        setHasCheckedPlugins(true);
+      },
+    }
+  );
+
+  // check prefs
+  usePromise(
+    async () => {
+      const maybePreferences = await LocalStorage.getItem(`${environment.extensionName}-preferences`);
+
+      if (maybePreferences) {
+        try {
+          return JSON.parse(maybePreferences as string);
+        } catch (_) {
+          // noop
+        }
path: extensions/folder-search/src/hooks/useFolderSearch.tsx
user: login: greptile-apps[bot]
body: style: Empty catch block silently ignores JSON parse errors
created_at: 2025-03-13T04:52:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,412 +53,189 @@ interface IconDictionary {
 const IconDictionaried: IconDictionary = Icon as IconDictionary;
 
 export default function Command() {
-  const [searchText, setSearchText] = useState<string>("");
-  const [pinnedResults, setPinnedResults] = useState<SpotlightSearchResult[]>([]);
-  const [selectedItemId, setSelectedItemId] = useState<string>("");
-  const [searchScope, setSearchScope] = useState<string>("");
-  const [isShowingDetail, setIsShowingDetail] = useState<boolean>(true);
-  const [results, setResults] = useState<SpotlightSearchResult[]>([]);
-
-  // hack to fix annoying double text during fallback. Typing helloworld results in helloworldhelloworld
-  let fixedText = "";
-  useEffect(() => {
-    fixedText = fixDoubleConcat(searchText);
-
-    if (fixedText !== searchText) {
-      setSearchText(fixedText); // Update the state of searchText
-    }
-  }, [searchText]);
-
-  const [plugins, setPlugins] = useState<FolderSearchPlugin[]>([]);
-
-  const [isQuerying, setIsQuerying] = useState<boolean>(false);
-  const [canExecute, setCanExecute] = useState<boolean>(false);
-
-  const [hasCheckedPlugins, setHasCheckedPlugins] = useState<boolean>(false);
-  const [hasCheckedPreferences, setHasCheckedPreferences] = useState<boolean>(false);
-
-  const abortable = useRef<AbortController>();
-
-  // check plugins
-  usePromise(
-    async () => {
-      const plugins = await loadPlugins();
-      setPlugins(plugins);
-    },
-    [],
-    {
-      onData() {
-        setHasCheckedPlugins(true);
-      },
-      onError() {
-        showToast({
-          title: "An Error Occured",
-          message: "Could not read plugins",
-          style: Toast.Style.Failure,
-        });
-
-        setHasCheckedPlugins(true);
-      },
+  const {
+    searchText,
+    setSearchText,
+    results,
+    isQuerying,
+    isShowingDetail,
+    setIsShowingDetail,
+    searchScope,
+    setSearchScope,
+    selectedItemId,
+    pinnedResults,
+    plugins,
+    resultIsPinned,
+    toggleResultPinnedStatus,
+    removeResultFromPinnedResults,
+    movePinUp,
+    movePinDown,
+    hasCheckedPlugins,
+    hasCheckedPreferences,
+  } = useFolderSearch();
+
+  const sendFinderSelectionToFolder = async (destinationFolder: string) => {
+    const selectedItems = await getSelectedFinderItems();
+
+    if (selectedItems.length === 0) {
+      await showToast(Toast.Style.Failure, "No Finder selection to send");
+      return;
     }
-  );
 
-  // check prefs
-  usePromise(
-    async () => {
-      const maybePreferences = await LocalStorage.getItem(`${environment.extensionName}-preferences`);
-
-      if (maybePreferences) {
-        try {
-          return JSON.parse(maybePreferences as string);
-        } catch (_) {
-          // noop
-        }
-      }
-    },
-    [],
-    {
-      onData(preferences) {
-        setPinnedResults(preferences?.pinned || []);
-        setSearchScope(preferences?.searchScope || "");
-        setIsShowingDetail(preferences?.isShowingDetail);
-        setHasCheckedPreferences(true);
-      },
-      onError() {
-        showToast({
-          title: "An Error Occured",
-          message: "Could not read preferences",
-          style: Toast.Style.Failure,
-        });
+    for (const item of selectedItems) {
+      const sourceFileName = path.basename(item.path);
+      const destinationFile = path.join(destinationFolder, sourceFileName);
 
-        setHasCheckedPreferences(true);
-      },
-    }
-  );
-
-  // perform search
-  usePromise(
-    searchSpotlight,
-    [
-      searchText,
-      searchScope,
-      abortable,
-      (result: SpotlightSearchResult) => {
-        setResults((results) => [result, ...results].sort(lastUsedSort));
-      },
-    ],
-    {
-      onWillExecute: () => {
-        setIsQuerying(true);
-        setCanExecute(false);
-      },
-      onData: () => {
-        setIsQuerying(false);
-      },
-      onError: (e) => {
-        if (e.name !== "AbortError") {
-          showToast({
-            title: "An Error Occured",
-            message: "Something went wrong. Try again.",
-            style: Toast.Style.Failure,
+      try {
+        const exists = await fse.pathExists(destinationFile);
+        if (exists) {
+          const overwrite = await confirmAlert({
+            title: "Overwrite the existing file?",
+            message: sourceFileName + " already exists in " + destinationFolder,
           });
-        }
-
-        setIsQuerying(false);
-      },
-      execute: hasCheckedPlugins && hasCheckedPreferences && canExecute && !!searchText,
-      abortable,
-    }
-  );
 
-  // save preferences
-  useEffect(() => {
-    (async () => {
-      if (!(hasCheckedPlugins && hasCheckedPreferences)) {
-        return;
-      }
-
-      await LocalStorage.setItem(
-        `${environment.extensionName}-preferences`,
-        JSON.stringify({
-          pinned: pinnedResults,
-          searchScope,
-          isShowingDetail,
-        })
-      );
-    })();
-  }, [pinnedResults, searchScope, isShowingDetail]);
-
-  useEffect(() => {
-    (async () => {
-      abortable.current?.abort();
-
-      setResults([]);
-      setIsQuerying(false);
+          if (overwrite) {
+            if (item.path === destinationFile) {
+              await showToast(Toast.Style.Failure, "The source and destination file are the same");
+              continue;
+            }
+            fse.moveSync(item.path, destinationFile, { overwrite: true });
+            await showToast(Toast.Style.Success, "Moved file " + path.basename(item.path) + " to " + destinationFolder);
+          } else {
+            await showToast(Toast.Style.Failure, "Cancelling move");
+          }
+        } else {
+          fse.moveSync(item.path, destinationFile);
+          await showToast(Toast.Style.Success, "Moved file " + sourceFileName + " to " + destinationFolder);
+        }
 
-      // short-circuit for 'pinned'
-      if (searchScope === "pinned") {
-        setResults(
-          pinnedResults.filter((pin) =>
-            pin.kMDItemFSName.toLocaleLowerCase().includes(searchText.replace(/[[|\]]/gi, "").toLocaleLowerCase())
-          )
-        );
-        setCanExecute(false);
-      } else {
-        setCanExecute(true);
+        open(destinationFolder);
+      } catch (e) {
+        console.error("ERROR " + String(e));
+        await showToast(Toast.Style.Failure, "Error moving file " + String(e));
       }
-    })();
-  }, [searchText, searchScope]);
-
-  const resultIsPinned = (result: SpotlightSearchResult) => {
-    return pinnedResults.map((pin) => pin.path).includes(result.path);
-  };
-
-  const removeResultFromPinnedResults = (result: SpotlightSearchResult) => {
-    setPinnedResults((pinnedResults) => pinnedResults.filter((pinnedResult) => pinnedResult.path !== result.path));
-  };
-
-  const toggleResultPinnedStatus = (result: SpotlightSearchResult, resultIndex: number) => {
-    if (!resultIsPinned(result)) {
-      setPinnedResults((pinnedResults) => [result, ...pinnedResults]);
-    } else {
-      // extracted out so we can re-use for maybeMoveResultToTrash calls
-      removeResultFromPinnedResults(result);
     }
 
-    setSelectedItemId(`result-${resultIndex.toString()}`);
-  };
-
-  const movePinUp = (result: SpotlightSearchResult, resultIndex: number) => {
-    const newIndex = resultIndex - 1;
-
-    [pinnedResults[resultIndex], pinnedResults[newIndex]] = [pinnedResults[newIndex], pinnedResults[resultIndex]];
-
-    setPinnedResults([...pinnedResults]);
-  };
-
-  const movePinDown = (result: SpotlightSearchResult, resultIndex: number) => {
-    const newIndex = resultIndex + 1;
-
-    [pinnedResults[resultIndex], pinnedResults[newIndex]] = [pinnedResults[newIndex], pinnedResults[resultIndex]];
-
-    setPinnedResults([...pinnedResults]);
+    closeMainWindow();
+    popToRoot({ clearSearchBar: true });
   };
 
-  // re-usable for results and 'pinned/favourites'
-  const ListSection = (props: { title: string; results: SpotlightSearchResult[] }) => {
+  // Render actions for the folder list items
+  const renderFolderActions = (result: any, resultIndex: number) => {
     return (
-      <List.Section title={props.title}>
-        {props.results.map((result: SpotlightSearchResult, resultIndex: number) => (
-          <List.Item
-            id={`result-${resultIndex.toString()}`}
-            key={resultIndex}
-            title={folderName(result)}
-            subtitle={!isShowingDetail ? enclosingFolderName(result) : ""}
-            icon={{ fileIcon: result.path }}
-            accessories={resultIsPinned(result) ? [{ icon: { source: Icon.Star, tintColor: Color.Yellow } }] : []}
-            detail={
-              <List.Item.Detail
-                metadata={
-                  <List.Item.Detail.Metadata>
-                    <List.Item.Detail.Metadata.Label title="Metadata" />
-                    <List.Item.Detail.Metadata.Label title="Name" text={result.kMDItemFSName} />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label title="Where" text={result.path} />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label title="Type" text={result.kMDItemKind} />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Created"
-                      text={result.kMDItemFSCreationDate?.toLocaleString()}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Modified"
-                      text={result.kMDItemContentModificationDate?.toLocaleString()}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Last used"
-                      text={result.kMDItemLastUsedDate?.toLocaleString() || "-"}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Use count"
-                      text={result.kMDItemUseCount?.toLocaleString() || "-"}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                  </List.Item.Detail.Metadata>
-                }
+      <ActionPanel title={folderName(result)}>
+        <Action.Open
+          title="Open"
+          icon={Icon.Folder}
+          target={result.path}
+          onOpen={() => popToRoot({ clearSearchBar: true })}
+        />
+        <Action.ShowInFinder
+          title="Show in Finder"
+          path={result.path}
+          onShow={() => popToRoot({ clearSearchBar: true })}
+        />
+        <Action
+          title="Send Finder selection to Folder"
+          icon={Icon.Folder}
+          shortcut={{ modifiers: ["cmd", "shift"], key: "s" }}
+          onAction={() => sendFinderSelectionToFolder(result.path)}
+        />
+        <Action.OpenWith
+          title="Open With..."
+          shortcut={{ modifiers: ["cmd"], key: "o" }}
+          path={result.path}
+          onOpen={() => popToRoot({ clearSearchBar: true })}
+        />
+        <Action
+          title="Show Info in Finder"
+          icon={Icon.Finder}
+          shortcut={{ modifiers: ["cmd"], key: "i" }}
+          onAction={() => showFolderInfoInFinder(result)}
+        />
+        <Action
+          title="Toggle Details"
+          icon={Icon.Sidebar}
+          shortcut={{ modifiers: ["cmd", "shift"], key: "d" }}
+          onAction={() => setIsShowingDetail(!isShowingDetail)}
+        />
+        <Action
+          title={!resultIsPinned(result) ? "Pin" : "Unpin"}
+          icon={!resultIsPinned(result) ? Icon.Star : Icon.StarDisabled}
+          shortcut={{ modifiers: ["cmd", "shift"], key: "p" }}
+          onAction={() => toggleResultPinnedStatus(result, resultIndex)}
+        />
+        {result.title === "Pinned" && (
+          <>
+            {resultIndex > 0 && (
+              <Action
+                title="Move Pin Up"
+                icon={Icon.ArrowUpCircle}
+                shortcut={Keyboard.Shortcut.Common.MoveUp}
+                onAction={() => movePinUp(result, resultIndex)}
               />
-            }
-            actions={
-              <ActionPanel title={folderName(result)}>
-                <Action.Open
-                  title="Open"
-                  icon={Icon.Folder}
-                  target={result.path}
-                  onOpen={() => popToRoot({ clearSearchBar: true })}
-                />
-                <Action.ShowInFinder
-                  title="Show in Finder"
-                  path={result.path}
-                  onShow={() => popToRoot({ clearSearchBar: true })}
-                />
-                <Action
-                  title="Send Finder selection to Folder"
-                  icon={Icon.Folder}
-                  shortcut={{ modifiers: ["cmd", "shift"], key: "s" }}
-                  onAction={async () => {
-                    const selectedItems = await getSelectedFinderItems();
-
-                    if (selectedItems.length === 0) {
-                      await showHUD(`⚠️  No Finder selection to send.`);
-                    } else {
-                      for (const item of selectedItems) {
-                        // Source path = item
-                        // Source file name = path.basename(item)
-                        //
-                        // Destination folder = result.path
-                        // Destination file = result.path + '/' + path.basename(item)
-
-                        const sourceFileName = path.basename(item.path);
-                        const destinationFolder = result.path;
-                        const destinationFile = result.path + "/" + path.basename(item.path);
-
-                        try {
-                          const exists = await fse.pathExists(destinationFile);
-                          if (exists) {
-                            const overwrite = await confirmAlert({
-                              title: "Ooverwrite the existing file?",
-                              message: sourceFileName + " already exists in " + destinationFolder,
-                            });
-
-                            if (overwrite) {
-                              if (item.path == destinationFile) {
-                                await showHUD("The source and destination file are the same");
-                              }
-                              fse.moveSync(item.path, destinationFile, { overwrite: true });
-                              await showHUD("Moved file " + path.basename(item.path) + " to " + destinationFolder);
-                            } else {
-                              await showHUD("Cancelling move");
-                            }
-                          } else {
-                            fse.moveSync(item.path, destinationFile);
-                            await showHUD("Moved file " + sourceFileName + " to " + destinationFolder);
-                          }
-
-                          open(result.path);
-                        } catch (e) {
-                          console.error("ERROR " + String(e));
-                          await showToast(Toast.Style.Failure, "Error moving file " + String(e));
-                        }
-                      }
-                    }
-
-                    closeMainWindow();
-                    popToRoot({ clearSearchBar: true });
-                  }}
-                />
-                <Action.OpenWith
-                  title="Open With..."
-                  shortcut={{ modifiers: ["cmd"], key: "o" }}
-                  path={result.path}
-                  onOpen={() => popToRoot({ clearSearchBar: true })}
-                />
-                <Action
-                  title="Show Info in Finder"
-                  icon={Icon.Finder}
-                  shortcut={{ modifiers: ["cmd"], key: "i" }}
-                  onAction={() => showFolderInfoInFinder(result)}
-                />
-                <Action
-                  title="Toggle Details"
-                  icon={Icon.Sidebar}
-                  shortcut={{ modifiers: ["cmd", "shift"], key: "d" }}
-                  onAction={() => setIsShowingDetail(!isShowingDetail)}
-                />
-                <Action
-                  title={!resultIsPinned(result) ? "Pin" : "Unpin"}
-                  icon={!resultIsPinned(result) ? Icon.Star : Icon.StarDisabled}
-                  shortcut={{ modifiers: ["cmd", "shift"], key: "p" }}
-                  onAction={() => toggleResultPinnedStatus(result, resultIndex)}
-                />
-                {props.title === "Pinned" && (
-                  <>
-                    {resultIndex > 0 && (
-                      <Action
-                        title="Move Pin Up"
-                        icon={Icon.ArrowUpCircle}
-                        shortcut={Keyboard.Shortcut.Common.MoveUp}
-                        onAction={() => movePinUp(result, resultIndex)}
-                      />
-                    )}
-                    {resultIndex < props.results.length - 1 && (
-                      <Action
-                        title="Move Pin Down"
-                        icon={Icon.ArrowDownCircle}
-                        shortcut={Keyboard.Shortcut.Common.MoveDown}
-                        onAction={() => movePinDown(result, resultIndex)}
-                      />
-                    )}
-                  </>
-                )}
-                <ActionPanel.Section>
-                  <Action.CopyToClipboard
-                    title="Copy Folder"
-                    shortcut={{ modifiers: ["cmd"], key: "." }}
-                    content={``}
-                    onCopy={() => copyFolderToClipboard(result)}
-                  />
-                  <Action.CopyToClipboard
-                    title="Copy Name"
-                    shortcut={{ modifiers: ["cmd", "shift"], key: "." }}
-                    content={folderName(result)}
-                  />
-                  <Action.CopyToClipboard
-                    title="Copy Path"
-                    shortcut={{ modifiers: ["cmd", "shift"], key: "," }}
-                    content={result.path}
-                  />
-                </ActionPanel.Section>
-                <ActionPanel.Section>
-                  <Action.CreateQuicklink
-                    title="Create Quicklink"
-                    icon={Icon.Link}
-                    shortcut={{ modifiers: ["cmd", "shift"], key: "l" }}
-                    quicklink={{ link: result.path, name: folderName(result) }}
-                  />
-                </ActionPanel.Section>
-                <ActionPanel.Section>
-                  <Action
-                    title="Move to Trash"
-                    style={Action.Style.Destructive}
-                    icon={{ source: Icon.Trash, tintColor: Color.Red }}
-                    shortcut={{ modifiers: ["ctrl"], key: "x" }}
-                    onAction={() => maybeMoveResultToTrash(result, () => removeResultFromPinnedResults(result))}
-                  />
-                </ActionPanel.Section>
-                <ActionPanel.Section title="Plugins">
-                  {plugins.map((plugin: FolderSearchPlugin, pluginIndex) => (
-                    <Action
-                      key={pluginIndex}
-                      title={plugin.title}
-                      icon={IconDictionaried[plugin.icon]}
-                      shortcut={{ ...plugin.shortcut }}
-                      onAction={() => {
-                        popToRoot({ clearSearchBar: true });
-                        closeMainWindow({ clearRootSearch: true });
-                        runAppleScript(plugin.appleScript(result));
-                      }}
-                    />
-                  ))}
-                </ActionPanel.Section>
-              </ActionPanel>
-            }
+            )}
+            {resultIndex < pinnedResults.length - 1 && (
+              <Action
+                title="Move Pin Down"
+                icon={Icon.ArrowDownCircle}
+                shortcut={Keyboard.Shortcut.Common.MoveDown}
+                onAction={() => movePinDown(result, resultIndex)}
+              />
+            )}
+          </>
+        )}
+        <ActionPanel.Section>
+          <Action.CopyToClipboard
+            title="Copy Folder"
+            shortcut={{ modifiers: ["cmd"], key: "." }}
+            content={``}
+            onCopy={() => copyFolderToClipboard(result)}
+          />
+          <Action.CopyToClipboard
+            title="Copy Name"
+            shortcut={{ modifiers: ["cmd", "shift"], key: "." }}
+            content={folderName(result)}
+          />
+          <Action.CopyToClipboard
+            title="Copy Path"
+            shortcut={{ modifiers: ["cmd", "shift"], key: "," }}
+            content={result.path}
+          />
+        </ActionPanel.Section>
+        <ActionPanel.Section>
+          <Action.CreateQuicklink
+            title="Create Quicklink"
+            icon={Icon.Link}
+            shortcut={{ modifiers: ["cmd", "shift"], key: "l" }}
+            quicklink={{ link: result.path, name: folderName(result) }}
+          />
+        </ActionPanel.Section>
+        <ActionPanel.Section>
+          <Action
+            title="Move to Trash"
+            style={Action.Style.Destructive}
+            icon={{ source: Icon.Trash, tintColor: Color.Red }}
+            shortcut={{ modifiers: ["ctrl"], key: "x" }}
+            onAction={() => maybeMoveResultToTrash(result, () => removeResultFromPinnedResults(result))}
           />
-        ))}
-      </List.Section>
+        </ActionPanel.Section>
+        <ActionPanel.Section title="Plugins">
+          {plugins.map((plugin: any, pluginIndex: number) => (
path: extensions/folder-search/src/search.tsx
user: login: greptile-apps[bot]
body: style: plugin should be typed as FolderSearchPlugin instead of any

```suggestion
          {plugins.map((plugin: FolderSearchPlugin, pluginIndex: number) => (
```
created_at: 2025-03-13T04:52:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,412 +53,189 @@ interface IconDictionary {
 const IconDictionaried: IconDictionary = Icon as IconDictionary;
 
 export default function Command() {
-  const [searchText, setSearchText] = useState<string>("");
-  const [pinnedResults, setPinnedResults] = useState<SpotlightSearchResult[]>([]);
-  const [selectedItemId, setSelectedItemId] = useState<string>("");
-  const [searchScope, setSearchScope] = useState<string>("");
-  const [isShowingDetail, setIsShowingDetail] = useState<boolean>(true);
-  const [results, setResults] = useState<SpotlightSearchResult[]>([]);
-
-  // hack to fix annoying double text during fallback. Typing helloworld results in helloworldhelloworld
-  let fixedText = "";
-  useEffect(() => {
-    fixedText = fixDoubleConcat(searchText);
-
-    if (fixedText !== searchText) {
-      setSearchText(fixedText); // Update the state of searchText
-    }
-  }, [searchText]);
-
-  const [plugins, setPlugins] = useState<FolderSearchPlugin[]>([]);
-
-  const [isQuerying, setIsQuerying] = useState<boolean>(false);
-  const [canExecute, setCanExecute] = useState<boolean>(false);
-
-  const [hasCheckedPlugins, setHasCheckedPlugins] = useState<boolean>(false);
-  const [hasCheckedPreferences, setHasCheckedPreferences] = useState<boolean>(false);
-
-  const abortable = useRef<AbortController>();
-
-  // check plugins
-  usePromise(
-    async () => {
-      const plugins = await loadPlugins();
-      setPlugins(plugins);
-    },
-    [],
-    {
-      onData() {
-        setHasCheckedPlugins(true);
-      },
-      onError() {
-        showToast({
-          title: "An Error Occured",
-          message: "Could not read plugins",
-          style: Toast.Style.Failure,
-        });
-
-        setHasCheckedPlugins(true);
-      },
+  const {
+    searchText,
+    setSearchText,
+    results,
+    isQuerying,
+    isShowingDetail,
+    setIsShowingDetail,
+    searchScope,
+    setSearchScope,
+    selectedItemId,
+    pinnedResults,
+    plugins,
+    resultIsPinned,
+    toggleResultPinnedStatus,
+    removeResultFromPinnedResults,
+    movePinUp,
+    movePinDown,
+    hasCheckedPlugins,
+    hasCheckedPreferences,
+  } = useFolderSearch();
+
+  const sendFinderSelectionToFolder = async (destinationFolder: string) => {
+    const selectedItems = await getSelectedFinderItems();
+
+    if (selectedItems.length === 0) {
+      await showToast(Toast.Style.Failure, "No Finder selection to send");
+      return;
     }
-  );
 
-  // check prefs
-  usePromise(
-    async () => {
-      const maybePreferences = await LocalStorage.getItem(`${environment.extensionName}-preferences`);
-
-      if (maybePreferences) {
-        try {
-          return JSON.parse(maybePreferences as string);
-        } catch (_) {
-          // noop
-        }
-      }
-    },
-    [],
-    {
-      onData(preferences) {
-        setPinnedResults(preferences?.pinned || []);
-        setSearchScope(preferences?.searchScope || "");
-        setIsShowingDetail(preferences?.isShowingDetail);
-        setHasCheckedPreferences(true);
-      },
-      onError() {
-        showToast({
-          title: "An Error Occured",
-          message: "Could not read preferences",
-          style: Toast.Style.Failure,
-        });
+    for (const item of selectedItems) {
+      const sourceFileName = path.basename(item.path);
+      const destinationFile = path.join(destinationFolder, sourceFileName);
 
-        setHasCheckedPreferences(true);
-      },
-    }
-  );
-
-  // perform search
-  usePromise(
-    searchSpotlight,
-    [
-      searchText,
-      searchScope,
-      abortable,
-      (result: SpotlightSearchResult) => {
-        setResults((results) => [result, ...results].sort(lastUsedSort));
-      },
-    ],
-    {
-      onWillExecute: () => {
-        setIsQuerying(true);
-        setCanExecute(false);
-      },
-      onData: () => {
-        setIsQuerying(false);
-      },
-      onError: (e) => {
-        if (e.name !== "AbortError") {
-          showToast({
-            title: "An Error Occured",
-            message: "Something went wrong. Try again.",
-            style: Toast.Style.Failure,
+      try {
+        const exists = await fse.pathExists(destinationFile);
+        if (exists) {
+          const overwrite = await confirmAlert({
+            title: "Overwrite the existing file?",
+            message: sourceFileName + " already exists in " + destinationFolder,
           });
-        }
-
-        setIsQuerying(false);
-      },
-      execute: hasCheckedPlugins && hasCheckedPreferences && canExecute && !!searchText,
-      abortable,
-    }
-  );
 
-  // save preferences
-  useEffect(() => {
-    (async () => {
-      if (!(hasCheckedPlugins && hasCheckedPreferences)) {
-        return;
-      }
-
-      await LocalStorage.setItem(
-        `${environment.extensionName}-preferences`,
-        JSON.stringify({
-          pinned: pinnedResults,
-          searchScope,
-          isShowingDetail,
-        })
-      );
-    })();
-  }, [pinnedResults, searchScope, isShowingDetail]);
-
-  useEffect(() => {
-    (async () => {
-      abortable.current?.abort();
-
-      setResults([]);
-      setIsQuerying(false);
+          if (overwrite) {
+            if (item.path === destinationFile) {
+              await showToast(Toast.Style.Failure, "The source and destination file are the same");
+              continue;
+            }
+            fse.moveSync(item.path, destinationFile, { overwrite: true });
+            await showToast(Toast.Style.Success, "Moved file " + path.basename(item.path) + " to " + destinationFolder);
+          } else {
+            await showToast(Toast.Style.Failure, "Cancelling move");
+          }
+        } else {
+          fse.moveSync(item.path, destinationFile);
+          await showToast(Toast.Style.Success, "Moved file " + sourceFileName + " to " + destinationFolder);
+        }
 
-      // short-circuit for 'pinned'
-      if (searchScope === "pinned") {
-        setResults(
-          pinnedResults.filter((pin) =>
-            pin.kMDItemFSName.toLocaleLowerCase().includes(searchText.replace(/[[|\]]/gi, "").toLocaleLowerCase())
-          )
-        );
-        setCanExecute(false);
-      } else {
-        setCanExecute(true);
+        open(destinationFolder);
+      } catch (e) {
+        console.error("ERROR " + String(e));
+        await showToast(Toast.Style.Failure, "Error moving file " + String(e));
       }
-    })();
-  }, [searchText, searchScope]);
-
-  const resultIsPinned = (result: SpotlightSearchResult) => {
-    return pinnedResults.map((pin) => pin.path).includes(result.path);
-  };
-
-  const removeResultFromPinnedResults = (result: SpotlightSearchResult) => {
-    setPinnedResults((pinnedResults) => pinnedResults.filter((pinnedResult) => pinnedResult.path !== result.path));
-  };
-
-  const toggleResultPinnedStatus = (result: SpotlightSearchResult, resultIndex: number) => {
-    if (!resultIsPinned(result)) {
-      setPinnedResults((pinnedResults) => [result, ...pinnedResults]);
-    } else {
-      // extracted out so we can re-use for maybeMoveResultToTrash calls
-      removeResultFromPinnedResults(result);
     }
 
-    setSelectedItemId(`result-${resultIndex.toString()}`);
-  };
-
-  const movePinUp = (result: SpotlightSearchResult, resultIndex: number) => {
-    const newIndex = resultIndex - 1;
-
-    [pinnedResults[resultIndex], pinnedResults[newIndex]] = [pinnedResults[newIndex], pinnedResults[resultIndex]];
-
-    setPinnedResults([...pinnedResults]);
-  };
-
-  const movePinDown = (result: SpotlightSearchResult, resultIndex: number) => {
-    const newIndex = resultIndex + 1;
-
-    [pinnedResults[resultIndex], pinnedResults[newIndex]] = [pinnedResults[newIndex], pinnedResults[resultIndex]];
-
-    setPinnedResults([...pinnedResults]);
+    closeMainWindow();
+    popToRoot({ clearSearchBar: true });
   };
 
-  // re-usable for results and 'pinned/favourites'
-  const ListSection = (props: { title: string; results: SpotlightSearchResult[] }) => {
+  // Render actions for the folder list items
+  const renderFolderActions = (result: any, resultIndex: number) => {
path: extensions/folder-search/src/search.tsx
user: login: greptile-apps[bot]
body: style: result parameter should be typed as SpotlightSearchResult instead of any

```suggestion
  const renderFolderActions = (result: SpotlightSearchResult, resultIndex: number) => {
```
created_at: 2025-03-13T04:52:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,412 +53,189 @@ interface IconDictionary {
 const IconDictionaried: IconDictionary = Icon as IconDictionary;
 
 export default function Command() {
-  const [searchText, setSearchText] = useState<string>("");
-  const [pinnedResults, setPinnedResults] = useState<SpotlightSearchResult[]>([]);
-  const [selectedItemId, setSelectedItemId] = useState<string>("");
-  const [searchScope, setSearchScope] = useState<string>("");
-  const [isShowingDetail, setIsShowingDetail] = useState<boolean>(true);
-  const [results, setResults] = useState<SpotlightSearchResult[]>([]);
-
-  // hack to fix annoying double text during fallback. Typing helloworld results in helloworldhelloworld
-  let fixedText = "";
-  useEffect(() => {
-    fixedText = fixDoubleConcat(searchText);
-
-    if (fixedText !== searchText) {
-      setSearchText(fixedText); // Update the state of searchText
-    }
-  }, [searchText]);
-
-  const [plugins, setPlugins] = useState<FolderSearchPlugin[]>([]);
-
-  const [isQuerying, setIsQuerying] = useState<boolean>(false);
-  const [canExecute, setCanExecute] = useState<boolean>(false);
-
-  const [hasCheckedPlugins, setHasCheckedPlugins] = useState<boolean>(false);
-  const [hasCheckedPreferences, setHasCheckedPreferences] = useState<boolean>(false);
-
-  const abortable = useRef<AbortController>();
-
-  // check plugins
-  usePromise(
-    async () => {
-      const plugins = await loadPlugins();
-      setPlugins(plugins);
-    },
-    [],
-    {
-      onData() {
-        setHasCheckedPlugins(true);
-      },
-      onError() {
-        showToast({
-          title: "An Error Occured",
-          message: "Could not read plugins",
-          style: Toast.Style.Failure,
-        });
-
-        setHasCheckedPlugins(true);
-      },
+  const {
+    searchText,
+    setSearchText,
+    results,
+    isQuerying,
+    isShowingDetail,
+    setIsShowingDetail,
+    searchScope,
+    setSearchScope,
+    selectedItemId,
+    pinnedResults,
+    plugins,
+    resultIsPinned,
+    toggleResultPinnedStatus,
+    removeResultFromPinnedResults,
+    movePinUp,
+    movePinDown,
+    hasCheckedPlugins,
+    hasCheckedPreferences,
+  } = useFolderSearch();
+
+  const sendFinderSelectionToFolder = async (destinationFolder: string) => {
+    const selectedItems = await getSelectedFinderItems();
+
+    if (selectedItems.length === 0) {
+      await showToast(Toast.Style.Failure, "No Finder selection to send");
+      return;
     }
-  );
 
-  // check prefs
-  usePromise(
-    async () => {
-      const maybePreferences = await LocalStorage.getItem(`${environment.extensionName}-preferences`);
-
-      if (maybePreferences) {
-        try {
-          return JSON.parse(maybePreferences as string);
-        } catch (_) {
-          // noop
-        }
-      }
-    },
-    [],
-    {
-      onData(preferences) {
-        setPinnedResults(preferences?.pinned || []);
-        setSearchScope(preferences?.searchScope || "");
-        setIsShowingDetail(preferences?.isShowingDetail);
-        setHasCheckedPreferences(true);
-      },
-      onError() {
-        showToast({
-          title: "An Error Occured",
-          message: "Could not read preferences",
-          style: Toast.Style.Failure,
-        });
+    for (const item of selectedItems) {
+      const sourceFileName = path.basename(item.path);
+      const destinationFile = path.join(destinationFolder, sourceFileName);
 
-        setHasCheckedPreferences(true);
-      },
-    }
-  );
-
-  // perform search
-  usePromise(
-    searchSpotlight,
-    [
-      searchText,
-      searchScope,
-      abortable,
-      (result: SpotlightSearchResult) => {
-        setResults((results) => [result, ...results].sort(lastUsedSort));
-      },
-    ],
-    {
-      onWillExecute: () => {
-        setIsQuerying(true);
-        setCanExecute(false);
-      },
-      onData: () => {
-        setIsQuerying(false);
-      },
-      onError: (e) => {
-        if (e.name !== "AbortError") {
-          showToast({
-            title: "An Error Occured",
-            message: "Something went wrong. Try again.",
-            style: Toast.Style.Failure,
+      try {
+        const exists = await fse.pathExists(destinationFile);
+        if (exists) {
+          const overwrite = await confirmAlert({
+            title: "Overwrite the existing file?",
+            message: sourceFileName + " already exists in " + destinationFolder,
           });
-        }
-
-        setIsQuerying(false);
-      },
-      execute: hasCheckedPlugins && hasCheckedPreferences && canExecute && !!searchText,
-      abortable,
-    }
-  );
 
-  // save preferences
-  useEffect(() => {
-    (async () => {
-      if (!(hasCheckedPlugins && hasCheckedPreferences)) {
-        return;
-      }
-
-      await LocalStorage.setItem(
-        `${environment.extensionName}-preferences`,
-        JSON.stringify({
-          pinned: pinnedResults,
-          searchScope,
-          isShowingDetail,
-        })
-      );
-    })();
-  }, [pinnedResults, searchScope, isShowingDetail]);
-
-  useEffect(() => {
-    (async () => {
-      abortable.current?.abort();
-
-      setResults([]);
-      setIsQuerying(false);
+          if (overwrite) {
+            if (item.path === destinationFile) {
+              await showToast(Toast.Style.Failure, "The source and destination file are the same");
+              continue;
+            }
+            fse.moveSync(item.path, destinationFile, { overwrite: true });
+            await showToast(Toast.Style.Success, "Moved file " + path.basename(item.path) + " to " + destinationFolder);
+          } else {
+            await showToast(Toast.Style.Failure, "Cancelling move");
+          }
+        } else {
+          fse.moveSync(item.path, destinationFile);
+          await showToast(Toast.Style.Success, "Moved file " + sourceFileName + " to " + destinationFolder);
+        }
 
-      // short-circuit for 'pinned'
-      if (searchScope === "pinned") {
-        setResults(
-          pinnedResults.filter((pin) =>
-            pin.kMDItemFSName.toLocaleLowerCase().includes(searchText.replace(/[[|\]]/gi, "").toLocaleLowerCase())
-          )
-        );
-        setCanExecute(false);
-      } else {
-        setCanExecute(true);
+        open(destinationFolder);
+      } catch (e) {
+        console.error("ERROR " + String(e));
+        await showToast(Toast.Style.Failure, "Error moving file " + String(e));
       }
-    })();
-  }, [searchText, searchScope]);
-
-  const resultIsPinned = (result: SpotlightSearchResult) => {
-    return pinnedResults.map((pin) => pin.path).includes(result.path);
-  };
-
-  const removeResultFromPinnedResults = (result: SpotlightSearchResult) => {
-    setPinnedResults((pinnedResults) => pinnedResults.filter((pinnedResult) => pinnedResult.path !== result.path));
-  };
-
-  const toggleResultPinnedStatus = (result: SpotlightSearchResult, resultIndex: number) => {
-    if (!resultIsPinned(result)) {
-      setPinnedResults((pinnedResults) => [result, ...pinnedResults]);
-    } else {
-      // extracted out so we can re-use for maybeMoveResultToTrash calls
-      removeResultFromPinnedResults(result);
     }
 
-    setSelectedItemId(`result-${resultIndex.toString()}`);
-  };
-
-  const movePinUp = (result: SpotlightSearchResult, resultIndex: number) => {
-    const newIndex = resultIndex - 1;
-
-    [pinnedResults[resultIndex], pinnedResults[newIndex]] = [pinnedResults[newIndex], pinnedResults[resultIndex]];
-
-    setPinnedResults([...pinnedResults]);
-  };
-
-  const movePinDown = (result: SpotlightSearchResult, resultIndex: number) => {
-    const newIndex = resultIndex + 1;
-
-    [pinnedResults[resultIndex], pinnedResults[newIndex]] = [pinnedResults[newIndex], pinnedResults[resultIndex]];
-
-    setPinnedResults([...pinnedResults]);
+    closeMainWindow();
+    popToRoot({ clearSearchBar: true });
   };
 
-  // re-usable for results and 'pinned/favourites'
-  const ListSection = (props: { title: string; results: SpotlightSearchResult[] }) => {
+  // Render actions for the folder list items
+  const renderFolderActions = (result: any, resultIndex: number) => {
     return (
-      <List.Section title={props.title}>
-        {props.results.map((result: SpotlightSearchResult, resultIndex: number) => (
-          <List.Item
-            id={`result-${resultIndex.toString()}`}
-            key={resultIndex}
-            title={folderName(result)}
-            subtitle={!isShowingDetail ? enclosingFolderName(result) : ""}
-            icon={{ fileIcon: result.path }}
-            accessories={resultIsPinned(result) ? [{ icon: { source: Icon.Star, tintColor: Color.Yellow } }] : []}
-            detail={
-              <List.Item.Detail
-                metadata={
-                  <List.Item.Detail.Metadata>
-                    <List.Item.Detail.Metadata.Label title="Metadata" />
-                    <List.Item.Detail.Metadata.Label title="Name" text={result.kMDItemFSName} />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label title="Where" text={result.path} />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label title="Type" text={result.kMDItemKind} />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Created"
-                      text={result.kMDItemFSCreationDate?.toLocaleString()}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Modified"
-                      text={result.kMDItemContentModificationDate?.toLocaleString()}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Last used"
-                      text={result.kMDItemLastUsedDate?.toLocaleString() || "-"}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                    <List.Item.Detail.Metadata.Label
-                      title="Use count"
-                      text={result.kMDItemUseCount?.toLocaleString() || "-"}
-                    />
-                    <List.Item.Detail.Metadata.Separator />
-                  </List.Item.Detail.Metadata>
-                }
+      <ActionPanel title={folderName(result)}>
+        <Action.Open
+          title="Open"
+          icon={Icon.Folder}
+          target={result.path}
+          onOpen={() => popToRoot({ clearSearchBar: true })}
+        />
+        <Action.ShowInFinder
+          title="Show in Finder"
+          path={result.path}
+          onShow={() => popToRoot({ clearSearchBar: true })}
+        />
+        <Action
+          title="Send Finder selection to Folder"
+          icon={Icon.Folder}
+          shortcut={{ modifiers: ["cmd", "shift"], key: "s" }}
+          onAction={() => sendFinderSelectionToFolder(result.path)}
+        />
+        <Action.OpenWith
+          title="Open With..."
+          shortcut={{ modifiers: ["cmd"], key: "o" }}
+          path={result.path}
+          onOpen={() => popToRoot({ clearSearchBar: true })}
+        />
+        <Action
+          title="Show Info in Finder"
+          icon={Icon.Finder}
+          shortcut={{ modifiers: ["cmd"], key: "i" }}
+          onAction={() => showFolderInfoInFinder(result)}
+        />
+        <Action
+          title="Toggle Details"
+          icon={Icon.Sidebar}
+          shortcut={{ modifiers: ["cmd", "shift"], key: "d" }}
+          onAction={() => setIsShowingDetail(!isShowingDetail)}
+        />
+        <Action
+          title={!resultIsPinned(result) ? "Pin" : "Unpin"}
+          icon={!resultIsPinned(result) ? Icon.Star : Icon.StarDisabled}
+          shortcut={{ modifiers: ["cmd", "shift"], key: "p" }}
+          onAction={() => toggleResultPinnedStatus(result, resultIndex)}
+        />
+        {result.title === "Pinned" && (
+          <>
+            {resultIndex > 0 && (
+              <Action
+                title="Move Pin Up"
+                icon={Icon.ArrowUpCircle}
+                shortcut={Keyboard.Shortcut.Common.MoveUp}
+                onAction={() => movePinUp(result, resultIndex)}
               />
-            }
-            actions={
-              <ActionPanel title={folderName(result)}>
-                <Action.Open
-                  title="Open"
-                  icon={Icon.Folder}
-                  target={result.path}
-                  onOpen={() => popToRoot({ clearSearchBar: true })}
-                />
-                <Action.ShowInFinder
-                  title="Show in Finder"
-                  path={result.path}
-                  onShow={() => popToRoot({ clearSearchBar: true })}
-                />
-                <Action
-                  title="Send Finder selection to Folder"
-                  icon={Icon.Folder}
-                  shortcut={{ modifiers: ["cmd", "shift"], key: "s" }}
-                  onAction={async () => {
-                    const selectedItems = await getSelectedFinderItems();
-
-                    if (selectedItems.length === 0) {
-                      await showHUD(`⚠️  No Finder selection to send.`);
-                    } else {
-                      for (const item of selectedItems) {
-                        // Source path = item
-                        // Source file name = path.basename(item)
-                        //
-                        // Destination folder = result.path
-                        // Destination file = result.path + '/' + path.basename(item)
-
-                        const sourceFileName = path.basename(item.path);
-                        const destinationFolder = result.path;
-                        const destinationFile = result.path + "/" + path.basename(item.path);
-
-                        try {
-                          const exists = await fse.pathExists(destinationFile);
-                          if (exists) {
-                            const overwrite = await confirmAlert({
-                              title: "Ooverwrite the existing file?",
-                              message: sourceFileName + " already exists in " + destinationFolder,
-                            });
-
-                            if (overwrite) {
-                              if (item.path == destinationFile) {
-                                await showHUD("The source and destination file are the same");
-                              }
-                              fse.moveSync(item.path, destinationFile, { overwrite: true });
-                              await showHUD("Moved file " + path.basename(item.path) + " to " + destinationFolder);
-                            } else {
-                              await showHUD("Cancelling move");
-                            }
-                          } else {
-                            fse.moveSync(item.path, destinationFile);
-                            await showHUD("Moved file " + sourceFileName + " to " + destinationFolder);
-                          }
-
-                          open(result.path);
-                        } catch (e) {
-                          console.error("ERROR " + String(e));
-                          await showToast(Toast.Style.Failure, "Error moving file " + String(e));
-                        }
-                      }
-                    }
-
-                    closeMainWindow();
-                    popToRoot({ clearSearchBar: true });
-                  }}
-                />
-                <Action.OpenWith
-                  title="Open With..."
-                  shortcut={{ modifiers: ["cmd"], key: "o" }}
-                  path={result.path}
-                  onOpen={() => popToRoot({ clearSearchBar: true })}
-                />
-                <Action
-                  title="Show Info in Finder"
-                  icon={Icon.Finder}
-                  shortcut={{ modifiers: ["cmd"], key: "i" }}
-                  onAction={() => showFolderInfoInFinder(result)}
-                />
-                <Action
-                  title="Toggle Details"
-                  icon={Icon.Sidebar}
-                  shortcut={{ modifiers: ["cmd", "shift"], key: "d" }}
-                  onAction={() => setIsShowingDetail(!isShowingDetail)}
-                />
-                <Action
-                  title={!resultIsPinned(result) ? "Pin" : "Unpin"}
-                  icon={!resultIsPinned(result) ? Icon.Star : Icon.StarDisabled}
-                  shortcut={{ modifiers: ["cmd", "shift"], key: "p" }}
-                  onAction={() => toggleResultPinnedStatus(result, resultIndex)}
-                />
-                {props.title === "Pinned" && (
-                  <>
-                    {resultIndex > 0 && (
-                      <Action
-                        title="Move Pin Up"
-                        icon={Icon.ArrowUpCircle}
-                        shortcut={Keyboard.Shortcut.Common.MoveUp}
-                        onAction={() => movePinUp(result, resultIndex)}
-                      />
-                    )}
-                    {resultIndex < props.results.length - 1 && (
-                      <Action
-                        title="Move Pin Down"
-                        icon={Icon.ArrowDownCircle}
-                        shortcut={Keyboard.Shortcut.Common.MoveDown}
-                        onAction={() => movePinDown(result, resultIndex)}
-                      />
-                    )}
-                  </>
-                )}
-                <ActionPanel.Section>
-                  <Action.CopyToClipboard
-                    title="Copy Folder"
-                    shortcut={{ modifiers: ["cmd"], key: "." }}
-                    content={``}
-                    onCopy={() => copyFolderToClipboard(result)}
-                  />
-                  <Action.CopyToClipboard
-                    title="Copy Name"
-                    shortcut={{ modifiers: ["cmd", "shift"], key: "." }}
-                    content={folderName(result)}
-                  />
-                  <Action.CopyToClipboard
-                    title="Copy Path"
-                    shortcut={{ modifiers: ["cmd", "shift"], key: "," }}
-                    content={result.path}
-                  />
-                </ActionPanel.Section>
-                <ActionPanel.Section>
-                  <Action.CreateQuicklink
-                    title="Create Quicklink"
-                    icon={Icon.Link}
-                    shortcut={{ modifiers: ["cmd", "shift"], key: "l" }}
-                    quicklink={{ link: result.path, name: folderName(result) }}
-                  />
-                </ActionPanel.Section>
-                <ActionPanel.Section>
-                  <Action
-                    title="Move to Trash"
-                    style={Action.Style.Destructive}
-                    icon={{ source: Icon.Trash, tintColor: Color.Red }}
-                    shortcut={{ modifiers: ["ctrl"], key: "x" }}
-                    onAction={() => maybeMoveResultToTrash(result, () => removeResultFromPinnedResults(result))}
-                  />
-                </ActionPanel.Section>
-                <ActionPanel.Section title="Plugins">
-                  {plugins.map((plugin: FolderSearchPlugin, pluginIndex) => (
-                    <Action
-                      key={pluginIndex}
-                      title={plugin.title}
-                      icon={IconDictionaried[plugin.icon]}
-                      shortcut={{ ...plugin.shortcut }}
-                      onAction={() => {
-                        popToRoot({ clearSearchBar: true });
-                        closeMainWindow({ clearRootSearch: true });
-                        runAppleScript(plugin.appleScript(result));
-                      }}
-                    />
-                  ))}
-                </ActionPanel.Section>
-              </ActionPanel>
-            }
+            )}
+            {resultIndex < pinnedResults.length - 1 && (
+              <Action
+                title="Move Pin Down"
+                icon={Icon.ArrowDownCircle}
+                shortcut={Keyboard.Shortcut.Common.MoveDown}
+                onAction={() => movePinDown(result, resultIndex)}
+              />
+            )}
+          </>
+        )}
+        <ActionPanel.Section>
+          <Action.CopyToClipboard
+            title="Copy Folder"
+            shortcut={{ modifiers: ["cmd"], key: "." }}
+            content={``}
path: extensions/folder-search/src/search.tsx
user: login: greptile-apps[bot]
body: logic: empty string literal for content prop seems unintentional
created_at: 2025-03-13T04:52:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,185 @@
+import { userInfo } from "os";
+import {
+  Action,
+  ActionPanel,
+  Form,
+  Icon,
+  List,
+  Toast,
+  closeMainWindow,
+  popToRoot,
+  showToast,
+  showHUD,
+  confirmAlert,
+  open,
+  getSelectedFinderItems,
+} from "@raycast/api";
+
+import { useEffect } from "react";
+import path from "node:path";
+import fse from "fs-extra";
+
+import { useFolderSearch } from "./hooks/useFolderSearch";
+import { FolderListSection } from "./components/FolderListSection";
+import { folderName } from "./utils";
+
+export default function Command() {
+  const {
+    searchText,
+    setSearchText,
+    results,
+    isQuerying,
+    isShowingDetail,
+    setIsShowingDetail,
+    searchScope,
+    setSearchScope,
+    selectedItemId,
+    pinnedResults,
+    resultIsPinned,
+    hasCheckedPlugins,
+    hasCheckedPreferences,
+  } = useFolderSearch();
+
+  // Function to move selected Finder items to the selected folder
+  const moveSelectedFinderItemsToFolder = async (destinationFolder: string) => {
+    const selectedItems = await getSelectedFinderItems();
+
+    if (selectedItems.length === 0) {
+      await showHUD(`⚠️  No Finder selection to move.`);
+      return false;
+    }
+
+    let movedCount = 0;
+    let skippedCount = 0;
+
+    for (const item of selectedItems) {
+      const sourceFileName = path.basename(item.path);
+      const destinationFile = path.join(destinationFolder, sourceFileName);
+
+      try {
+        const exists = await fse.pathExists(destinationFile);
+        if (exists) {
+          const overwrite = await confirmAlert({
+            title: "Overwrite the existing file?",
+            message: sourceFileName + " already exists in " + destinationFolder,
+          });
+
+          if (overwrite) {
+            if (item.path === destinationFile) {
+              await showHUD("The source and destination file are the same");
+              skippedCount++;
+              continue;
+            }
+            await fse.move(item.path, destinationFile, { overwrite: true });
+            movedCount++;
+          } else {
+            await showHUD("Cancelling move for " + sourceFileName);
+            skippedCount++;
+            continue;
+          }
+        } else {
+          await fse.move(item.path, destinationFile);
+          movedCount++;
+        }
+      } catch (e) {
+        console.error("ERROR " + String(e));
+        await showToast(Toast.Style.Failure, "Error moving file " + String(e));
+        return false;
+      }
+    }
+
+    if (movedCount === 0) {
+      await showHUD(`No files were moved`);
+      return false;
+    } else if (skippedCount > 0) {
+      await showHUD(
+        `Moved ${movedCount} item(s) to ${path.basename(destinationFolder)}, skipped ${skippedCount} item(s)`
+      );
+    } else {
+      await showHUD(`Moved ${movedCount} item(s) to ${path.basename(destinationFolder)}`);
+    }
+
+    return movedCount > 0;
+  };
+
+  // Render actions for the folder list items
+  const renderFolderActions = (result: any, resultIndex: number) => {
path: extensions/folder-search/src/move.tsx
user: login: greptile-apps[bot]
body: style: result parameter should be typed as SpotlightSearchResult instead of any
created_at: 2025-03-13T04:52:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,185 @@
+import { userInfo } from "os";
+import {
+  Action,
+  ActionPanel,
+  Form,
+  Icon,
+  List,
+  Toast,
+  closeMainWindow,
+  popToRoot,
+  showToast,
+  showHUD,
+  confirmAlert,
+  open,
+  getSelectedFinderItems,
+} from "@raycast/api";
+
+import { useEffect } from "react";
+import path from "node:path";
+import fse from "fs-extra";
+
+import { useFolderSearch } from "./hooks/useFolderSearch";
+import { FolderListSection } from "./components/FolderListSection";
+import { folderName } from "./utils";
+
+export default function Command() {
+  const {
+    searchText,
+    setSearchText,
+    results,
+    isQuerying,
+    isShowingDetail,
+    setIsShowingDetail,
+    searchScope,
+    setSearchScope,
+    selectedItemId,
+    pinnedResults,
+    resultIsPinned,
+    hasCheckedPlugins,
+    hasCheckedPreferences,
+  } = useFolderSearch();
+
+  // Function to move selected Finder items to the selected folder
+  const moveSelectedFinderItemsToFolder = async (destinationFolder: string) => {
+    const selectedItems = await getSelectedFinderItems();
+
+    if (selectedItems.length === 0) {
+      await showHUD(`⚠️  No Finder selection to move.`);
+      return false;
+    }
+
+    let movedCount = 0;
+    let skippedCount = 0;
+
+    for (const item of selectedItems) {
+      const sourceFileName = path.basename(item.path);
+      const destinationFile = path.join(destinationFolder, sourceFileName);
+
+      try {
+        const exists = await fse.pathExists(destinationFile);
+        if (exists) {
+          const overwrite = await confirmAlert({
+            title: "Overwrite the existing file?",
+            message: sourceFileName + " already exists in " + destinationFolder,
+          });
+
+          if (overwrite) {
+            if (item.path === destinationFile) {
+              await showHUD("The source and destination file are the same");
+              skippedCount++;
+              continue;
+            }
+            await fse.move(item.path, destinationFile, { overwrite: true });
+            movedCount++;
+          } else {
+            await showHUD("Cancelling move for " + sourceFileName);
+            skippedCount++;
+            continue;
+          }
+        } else {
+          await fse.move(item.path, destinationFile);
+          movedCount++;
+        }
+      } catch (e) {
+        console.error("ERROR " + String(e));
+        await showToast(Toast.Style.Failure, "Error moving file " + String(e));
+        return false;
path: extensions/folder-search/src/move.tsx
user: login: greptile-apps[bot]
body: style: use showFailureToast from @raycast/utils instead of showToast for error handling

```suggestion
      } catch (e) {
        console.error("ERROR " + String(e));
        await showFailureToast("Error moving file " + String(e));
        return false;
```
created_at: 2025-03-13T04:52:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,185 @@
+import { userInfo } from "os";
+import {
+  Action,
+  ActionPanel,
+  Form,
+  Icon,
+  List,
+  Toast,
+  closeMainWindow,
+  popToRoot,
+  showToast,
+  showHUD,
+  confirmAlert,
+  open,
+  getSelectedFinderItems,
+} from "@raycast/api";
+
+import { useEffect } from "react";
+import path from "node:path";
+import fse from "fs-extra";
+
+import { useFolderSearch } from "./hooks/useFolderSearch";
+import { FolderListSection } from "./components/FolderListSection";
+import { folderName } from "./utils";
+
+export default function Command() {
+  const {
+    searchText,
+    setSearchText,
+    results,
+    isQuerying,
+    isShowingDetail,
+    setIsShowingDetail,
+    searchScope,
+    setSearchScope,
+    selectedItemId,
+    pinnedResults,
+    resultIsPinned,
+    hasCheckedPlugins,
+    hasCheckedPreferences,
+  } = useFolderSearch();
+
+  // Function to move selected Finder items to the selected folder
+  const moveSelectedFinderItemsToFolder = async (destinationFolder: string) => {
+    const selectedItems = await getSelectedFinderItems();
+
+    if (selectedItems.length === 0) {
+      await showHUD(`⚠️  No Finder selection to move.`);
+      return false;
+    }
+
+    let movedCount = 0;
+    let skippedCount = 0;
+
+    for (const item of selectedItems) {
+      const sourceFileName = path.basename(item.path);
+      const destinationFile = path.join(destinationFolder, sourceFileName);
+
+      try {
+        const exists = await fse.pathExists(destinationFile);
+        if (exists) {
+          const overwrite = await confirmAlert({
+            title: "Overwrite the existing file?",
+            message: sourceFileName + " already exists in " + destinationFolder,
path: extensions/folder-search/src/move.tsx
user: login: greptile-apps[bot]
body: style: use template literals for string concatenation: `${sourceFileName} already exists in ${destinationFolder}`
created_at: 2025-03-13T04:52:20Z
author_association: CONTRIBUTOR

PR comments:
user: login: raycastbot
created_at: 2025-03-13T04:48:48Z
body: Thank you for your  contribution! :tada:

🔔 @GastroGeek @fengtianze @ridemountainpig you might want to have a look.

You can use [this guide](https://developers.raycast.com/basics/review-pullrequest) to learn how to check out the Pull Request locally in order to test it.

Due to our current reduced availability, the initial review may take up to 10-15 business days
author_association: COLLABORATOR
user: login: github-actions[bot]
created_at: 2025-03-13T09:36:48Z
body: <!-- add-pr-comment:add-pr-comment -->

**Published to the Raycast Store:**
https://raycast.com/GastroGeek/folder-search
author_association: CONTRIBUTOR
user: login: raycastbot
created_at: 2025-03-13T09:36:50Z
body: :tada: :tada: :tada:

We've rewarded your Raycast account with some credits. You will soon be able to exchange them for some swag.
author_association: COLLABORATOR
user: login: omarshahine
created_at: 2025-03-13T14:24:19Z
body: > Hi 👋 
> 
> 
> 
> Looks good to me, approved 🔥

Thanks. The Original Author seems MIA. What do you do in this case. I'm fine if you want to change it to me. 
author_association: CONTRIBUTOR