From d70071a555c88e0411b118d54a2de7a95b97f5e2 Mon Sep 17 00:00:00 2001 From: Andrew Huynh Date: Tue, 19 Nov 2024 17:13:45 -0800 Subject: [PATCH 1/2] ui: expandable search window (#552) * adding tauri api deps to desktop-client package.json * wip: adding minimize + resize icons to custom title bar * adding maximize button for windows/linux * moving invoke enum to its own module & adding an "open_big_mode" cmd * open big mode window when the user hits the "maximize" button * cleaning up window location constants and adding bigmode path * adding placeholder for big mode page * cargo clippy + fmt * updating search page import to be more explicit * fmt * wip: moving search input into it's own component * Pulling SearchInput as its own component so we can use it other places * Moving result view into its own reusable component * fix showing/hiding searchbar on macOS when other windows are open --- apps/desktop-client/package-lock.json | 300 ++++++++++++++++++ apps/desktop-client/package.json | 11 + .../src/assets/icons/macos-maximize.svg | 22 ++ .../src/bindings/ClientInvoke.ts | 6 +- .../src/components/CustomTitleBar.tsx | 61 +++- apps/desktop-client/src/main.tsx | 7 +- .../src/pages/bigmode/BigMode.tsx | 143 +++++++++ .../src/pages/search/ResultListView.tsx | 62 ++++ .../src/pages/search/SearchInput.tsx | 80 +++++ .../search/{index.tsx => SearchPage.tsx} | 252 +++++---------- .../src/pages/search/constants.tsx | 10 + apps/tauri/src/cmd.rs | 27 +- apps/tauri/src/cmd/window.rs | 30 ++ apps/tauri/src/constants.rs | 24 +- apps/tauri/src/lib.rs | 7 +- apps/tauri/src/menu.rs | 8 +- apps/tauri/src/platform/mac.rs | 8 +- apps/tauri/src/window.rs | 30 +- .../entities/src/models/indexed_document.rs | 7 +- crates/entities/src/models/lens.rs | 2 +- crates/shared/src/event.rs | 67 ---- crates/shared/src/invoke.rs | 74 +++++ crates/shared/src/lib.rs | 1 + crates/spyglass-processor/src/parser/audio.rs | 6 +- .../src/parser/pdf_parser.rs | 4 +- crates/spyglass-processor/src/utils/mime.rs | 4 +- crates/spyglass-searcher/src/lib.rs | 8 +- crates/spyglass-searcher/src/utils.rs | 2 +- crates/spyglass/src/crawler/mod.rs | 4 +- crates/spyglass/src/crawler/robots.rs | 6 +- crates/spyglass/src/task/worker.rs | 14 +- package-lock.json | 171 ---------- package.json | 5 - 33 files changed, 964 insertions(+), 499 deletions(-) create mode 100644 apps/desktop-client/src/assets/icons/macos-maximize.svg create mode 100644 apps/desktop-client/src/pages/bigmode/BigMode.tsx create mode 100644 apps/desktop-client/src/pages/search/ResultListView.tsx create mode 100644 apps/desktop-client/src/pages/search/SearchInput.tsx rename apps/desktop-client/src/pages/search/{index.tsx => SearchPage.tsx} (56%) create mode 100644 apps/tauri/src/cmd/window.rs create mode 100644 crates/shared/src/invoke.rs delete mode 100644 package-lock.json delete mode 100644 package.json diff --git a/apps/desktop-client/package-lock.json b/apps/desktop-client/package-lock.json index eb99139f0..949450b39 100644 --- a/apps/desktop-client/package-lock.json +++ b/apps/desktop-client/package-lock.json @@ -11,6 +11,16 @@ "@heroicons/react": "^2.1.5", "@icons-pack/react-simple-icons": "^10.1.0", "@tauri-apps/api": "^2.0.3", + "@tauri-apps/plugin-clipboard-manager": "^2.0.0", + "@tauri-apps/plugin-dialog": "^2.0.1", + "@tauri-apps/plugin-fs": "^2.0.1", + "@tauri-apps/plugin-global-shortcut": "^2.0.0", + "@tauri-apps/plugin-http": "^2.0.1", + "@tauri-apps/plugin-notification": "^2.0.0", + "@tauri-apps/plugin-os": "^2.0.0", + "@tauri-apps/plugin-process": "^2.0.0", + "@tauri-apps/plugin-shell": "^2.0.1", + "@tauri-apps/plugin-updater": "^2.0.0", "classnames": "^2.5.1", "handlebars": "^4.7.8", "react": "^18.3.1", @@ -20,6 +30,7 @@ "devDependencies": { "@eslint/js": "^9.13.0", "@tailwindcss/forms": "^0.5.9", + "@tauri-apps/cli": "^2.1.0", "@types/node": "^22.8.6", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", @@ -1270,6 +1281,295 @@ "url": "https://opencollective.com/tauri" } }, + "node_modules/@tauri-apps/cli": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.1.0.tgz", + "integrity": "sha512-K2VhcKqBhAeS5pNOVdnR/xQRU6jwpgmkSL2ejHXcl0m+kaTggT0WRDQnFtPq6NljA7aE03cvwsbCAoFG7vtkJw==", + "dev": true, + "license": "Apache-2.0 OR MIT", + "bin": { + "tauri": "tauri.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + }, + "optionalDependencies": { + "@tauri-apps/cli-darwin-arm64": "2.1.0", + "@tauri-apps/cli-darwin-x64": "2.1.0", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.1.0", + "@tauri-apps/cli-linux-arm64-gnu": "2.1.0", + "@tauri-apps/cli-linux-arm64-musl": "2.1.0", + "@tauri-apps/cli-linux-x64-gnu": "2.1.0", + "@tauri-apps/cli-linux-x64-musl": "2.1.0", + "@tauri-apps/cli-win32-arm64-msvc": "2.1.0", + "@tauri-apps/cli-win32-ia32-msvc": "2.1.0", + "@tauri-apps/cli-win32-x64-msvc": "2.1.0" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.1.0.tgz", + "integrity": "sha512-ESc6J6CE8hl1yKH2vJ+ALF+thq4Be+DM1mvmTyUCQObvezNCNhzfS6abIUd3ou4x5RGH51ouiANeT3wekU6dCw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.1.0.tgz", + "integrity": "sha512-TasHS442DFs8cSH2eUQzuDBXUST4ECjCd0yyP+zZzvAruiB0Bg+c8A+I/EnqCvBQ2G2yvWLYG8q/LI7c87A5UA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.1.0.tgz", + "integrity": "sha512-aP7ZBGNL4ny07Cbb6kKpUOSrmhcIK2KhjviTzYlh+pPhAptxnC78xQGD3zKQkTi2WliJLPmBYbOHWWQa57lQ9w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.1.0.tgz", + "integrity": "sha512-ZTdgD5gLeMCzndMT2f358EkoYkZ5T+Qy6zPzU+l5vv5M7dHVN9ZmblNAYYXmoOuw7y+BY4X/rZvHV9pcGrcanQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.1.0.tgz", + "integrity": "sha512-NzwqjUCilhnhJzusz3d/0i0F1GFrwCQbkwR6yAHUxItESbsGYkZRJk0yMEWkg3PzFnyK4cWTlQJMEU52TjhEzA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.1.0.tgz", + "integrity": "sha512-TyiIpMEtZxNOQmuFyfJwaaYbg3movSthpBJLIdPlKxSAB2BW0VWLY3/ZfIxm/G2YGHyREkjJvimzYE0i37PnMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.1.0.tgz", + "integrity": "sha512-/dQd0TlaxBdJACrR72DhynWftzHDaX32eBtS5WBrNJ+nnNb+znM3gON6nJ9tSE9jgDa6n1v2BkI/oIDtypfUXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.1.0.tgz", + "integrity": "sha512-NdQJO7SmdYqOcE+JPU7bwg7+odfZMWO6g8xF9SXYCMdUzvM2Gv/AQfikNXz5yS7ralRhNFuW32i5dcHlxh4pDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.1.0.tgz", + "integrity": "sha512-f5h8gKT/cB8s1ticFRUpNmHqkmaLutT62oFDB7N//2YTXnxst7EpMIn1w+QimxTvTk2gcx6EcW6bEk/y2hZGzg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.1.0.tgz", + "integrity": "sha512-P/+LrdSSb5Xbho1LRP4haBjFHdyPdjWvGgeopL96OVtrFpYnfC+RctB45z2V2XxqFk3HweDDxk266btjttfjGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/plugin-clipboard-manager": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.0.0.tgz", + "integrity": "sha512-V1sXmbjnwfXt/r48RJMwfUmDMSaP/8/YbH4CLNxt+/sf1eHlIP8PRFdFDQwLN0cNQKu2rqQVbG/Wc/Ps6cDUhw==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-dialog": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.0.1.tgz", + "integrity": "sha512-fnUrNr6EfvTqdls/ufusU7h6UbNFzLKvHk/zTuOiBq01R3dTODqwctZlzakdbfSp/7pNwTKvgKTAgl/NAP/Z0Q==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-fs": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-fs/-/plugin-fs-2.0.2.tgz", + "integrity": "sha512-4YZaX2j7ta81M5/DL8aN10kTnpUkEpkPo1FTYPT8Dd0ImHe3azM8i8MrtjrDGoyBYLPO3zFv7df/mSCYF8oA0Q==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-global-shortcut": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-global-shortcut/-/plugin-global-shortcut-2.0.0.tgz", + "integrity": "sha512-pnB4CUwFVjg4twtBSxoLJ4uLFTYxsvOdC1zIbG581pYzhYatOl6mjB+ijD5SSXgiS/jNoqMcfkOF9PWAisurew==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-http": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-http/-/plugin-http-2.0.1.tgz", + "integrity": "sha512-j6IA3pVBybSCwPpsihpX4z8bs6PluuGtr06ahL/xy4D8HunNBTmRmadJrFOQi0gOAbaig4MkQ15nzNLBLy8R1A==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-notification": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-notification/-/plugin-notification-2.0.0.tgz", + "integrity": "sha512-6qEDYJS7mgXZWLXA0EFL+DVCJh8sJlzSoyw6B50pxhLPVFjc5Vr5DVzl5W3mUHaYhod5wsC984eQnlCCGqxYDA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-os": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.0.0.tgz", + "integrity": "sha512-M7hG/nNyQYTJxVG/UhTKhp9mpXriwWzrs9mqDreB8mIgqA3ek5nHLdwRZJWhkKjZrnDT4v9CpA9BhYeplTlAiA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-process": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-process/-/plugin-process-2.0.0.tgz", + "integrity": "sha512-OYzi0GnkrF4NAnsHZU7U3tjSoP0PbeAlO7T1Z+vJoBUH9sFQ1NSLqWYWQyf8hcb3gVWe7P1JggjiskO+LST1ug==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-shell": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.0.1.tgz", + "integrity": "sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-updater": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.0.0.tgz", + "integrity": "sha512-N0cl71g7RPr7zK2Fe5aoIwzw14NcdLcz7XMGFWZVjprsqgDRWoxbnUkknyCQMZthjhGkppCd/wN2MIsUz+eAhQ==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", diff --git a/apps/desktop-client/package.json b/apps/desktop-client/package.json index 99560e213..b1313a81f 100644 --- a/apps/desktop-client/package.json +++ b/apps/desktop-client/package.json @@ -14,6 +14,16 @@ "@heroicons/react": "^2.1.5", "@icons-pack/react-simple-icons": "^10.1.0", "@tauri-apps/api": "^2.0.3", + "@tauri-apps/plugin-clipboard-manager": "^2.0.0", + "@tauri-apps/plugin-dialog": "^2.0.1", + "@tauri-apps/plugin-fs": "^2.0.1", + "@tauri-apps/plugin-global-shortcut": "^2.0.0", + "@tauri-apps/plugin-http": "^2.0.1", + "@tauri-apps/plugin-notification": "^2.0.0", + "@tauri-apps/plugin-os": "^2.0.0", + "@tauri-apps/plugin-process": "^2.0.0", + "@tauri-apps/plugin-shell": "^2.0.1", + "@tauri-apps/plugin-updater": "^2.0.0", "classnames": "^2.5.1", "handlebars": "^4.7.8", "react": "^18.3.1", @@ -23,6 +33,7 @@ "devDependencies": { "@eslint/js": "^9.13.0", "@tailwindcss/forms": "^0.5.9", + "@tauri-apps/cli": "^2.1.0", "@types/node": "^22.8.6", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", diff --git a/apps/desktop-client/src/assets/icons/macos-maximize.svg b/apps/desktop-client/src/assets/icons/macos-maximize.svg new file mode 100644 index 000000000..85b6c8624 --- /dev/null +++ b/apps/desktop-client/src/assets/icons/macos-maximize.svg @@ -0,0 +1,22 @@ + + + + + + + diff --git a/apps/desktop-client/src/bindings/ClientInvoke.ts b/apps/desktop-client/src/bindings/ClientInvoke.ts index 470978448..9994b83dd 100644 --- a/apps/desktop-client/src/bindings/ClientInvoke.ts +++ b/apps/desktop-client/src/bindings/ClientInvoke.ts @@ -1,3 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type ClientInvoke = "authorize_connection" | "choose_folder" | "default_indices" | "escape" | "open_plugins_folder" | "get_library_stats" | "get_shortcut" | "get_startup_progress" | "install_lens" | "list_connections" | "list_installed_lenses" | "list_installable_lenses" | "list_plugins" | "load_user_settings" | "load_action_settings" | "resize_window" | "resync_connection" | "revoke_connection" | "run_lens_updater" | "save_user_settings" | "search_docs" | "search_lenses" | "open_folder_path" | "open_lens_folder" | "open_result" | "copy_to_clipboard" | "open_settings_folder" | "uninstall_lens" | "update_and_restart" | "wizard_finished" | "navigate"; +/** + * NOTE: When adding a new invoke command, + * the label should match up to the tauri generated command names. + */ +export type ClientInvoke = "authorize_connection" | "choose_folder" | "copy_to_clipboard" | "default_indices" | "escape" | "open_plugins_folder" | "get_library_stats" | "get_shortcut" | "get_startup_progress" | "install_lens" | "list_connections" | "list_installed_lenses" | "list_installable_lenses" | "list_plugins" | "load_user_settings" | "load_action_settings" | "open_big_mode" | "open_folder_path" | "open_lens_folder" | "open_result" | "open_settings_folder" | "resize_window" | "resync_connection" | "revoke_connection" | "run_lens_updater" | "save_user_settings" | "search_docs" | "search_lenses" | "uninstall_lens" | "update_and_restart" | "wizard_finished" | "navigate"; diff --git a/apps/desktop-client/src/components/CustomTitleBar.tsx b/apps/desktop-client/src/components/CustomTitleBar.tsx index 9081f8dab..51bb9acc5 100644 --- a/apps/desktop-client/src/components/CustomTitleBar.tsx +++ b/apps/desktop-client/src/components/CustomTitleBar.tsx @@ -1,7 +1,8 @@ import classNames from "classnames"; import { getOperatingSystem, OperatingSystem } from "../utils"; -import { XMarkIcon } from "@heroicons/react/16/solid"; +import { MinusIcon, XMarkIcon } from "@heroicons/react/16/solid"; import { invoke } from "../glue"; +import macMaximize from "../assets/icons/macos-maximize.svg"; interface Props { osStyle?: OperatingSystem; @@ -12,41 +13,75 @@ export function CustomTitleBar({ osStyle = getOperatingSystem() }: Props) { await invoke("escape"); }; + const handleMaximize = async () => { + await invoke("open_big_mode"); + }; + const renderButton = () => { - const baseStyles = [ - "flex", - "flex-row", - "justify-center", - "items-center", - "group-hover:bg-red-500", - ]; + const baseStyles = ["flex", "flex-row", "justify-center", "items-center"]; if (osStyle === OperatingSystem.MacOS) { return ( -
+
+ +
); } else { return ( -
+
+
+ + + +
); } diff --git a/apps/desktop-client/src/main.tsx b/apps/desktop-client/src/main.tsx index 928a6bf4a..902792ee0 100644 --- a/apps/desktop-client/src/main.tsx +++ b/apps/desktop-client/src/main.tsx @@ -1,12 +1,13 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import "./index.css"; -import { SearchPage } from "./pages/search/index.tsx"; +import { SearchPage } from "./pages/search/SearchPage.tsx"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; import ErrorPage from "./error-page.tsx"; import { WizardPage } from "./pages/wizard/WizardPage.tsx"; import { SettingsPage } from "./pages/settings/SettingsPage.tsx"; import { ProgressPopup } from "./pages/ProgressPopup.tsx"; +import { BigMode } from "./pages/bigmode/BigMode.tsx"; const router = createBrowserRouter([ { @@ -14,6 +15,10 @@ const router = createBrowserRouter([ element: , errorElement: , }, + { + path: "/bigmode", + element: , + }, { path: "/progress", element: , diff --git a/apps/desktop-client/src/pages/bigmode/BigMode.tsx b/apps/desktop-client/src/pages/bigmode/BigMode.tsx new file mode 100644 index 000000000..fd3f5fca5 --- /dev/null +++ b/apps/desktop-client/src/pages/bigmode/BigMode.tsx @@ -0,0 +1,143 @@ +import { KeyboardEvent, useCallback, useEffect, useState } from "react"; +import { SearchInput } from "../search/SearchInput"; +import { ResultListView } from "../search/ResultListView"; +import { + LENS_SEARCH_PREFIX, + QUERY_DEBOUNCE_MS, + ResultDisplayMode, + SEARCH_MIN_CHARS, +} from "../search/constants"; +import { SearchResult } from "../../bindings/SearchResult"; +import { LensResult } from "../../bindings/LensResult"; +import { invoke } from "../../glue"; +import { SearchResults } from "../../bindings/SearchResults"; + +export function BigMode() { + const [selectedLenses, setSelectedLenses] = useState([]); + const [query, setQuery] = useState(""); + const [docResults, setDocResults] = useState([]); + const [lensResults, setLensResults] = useState([]); + const [selectedIdx, setSelectedIdx] = useState(0); + + const [isThinking, setIsThinking] = useState(false); + + const [resultMode, setResultMode] = useState( + ResultDisplayMode.None, + ); + + // Clear search queries & results + const clearQuery = useCallback(async () => { + setQuery(""); + setResultMode(ResultDisplayMode.None); + setSelectedIdx(0); + setDocResults([]); + setLensResults([]); + }, []); + + const moveSelectionUp = () => { + // notihng to do + if (resultMode === ResultDisplayMode.None) { + return; + } + setSelectedIdx((idx) => (idx > 0 ? idx - 1 : idx)); + }; + + const moveSelectionDown = () => { + let max = 0; + if (resultMode === ResultDisplayMode.Documents) { + max = docResults.length - 1; + } else if (resultMode === ResultDisplayMode.Lenses) { + max = lensResults.length - 1; + } + setSelectedIdx((idx) => (idx < max ? idx + 1 : max)); + }; + + const handleEnter = async () => { + // do action or handle selection + if (resultMode === ResultDisplayMode.Documents) { + const selected = docResults[selectedIdx]; + await invoke("open_result", { url: selected.url }); + clearQuery(); + await invoke("escape"); + } else if (resultMode === ResultDisplayMode.Lenses) { + const selected = lensResults[selectedIdx]; + setSelectedLenses((lenses) => [...lenses, selected.label]); + clearQuery(); + } + }; + + const handleKeyEvent = async (event: KeyboardEvent) => { + switch (event.key) { + case "ArrowUp": + moveSelectionUp(); + break; + case "ArrowDown": + moveSelectionDown(); + break; + case "Tab": + // Handle tab completion for len search/results + if (resultMode === ResultDisplayMode.Lenses) { + const selected = lensResults[selectedIdx]; + setSelectedLenses((lenses) => [...lenses, selected.label]); + clearQuery(); + } + break; + } + }; + + // when the query changes shoot it over to the server. + useEffect(() => { + if (query.length === 0) { + clearQuery(); + } + + const timer = setTimeout(async () => { + if (query.startsWith(LENS_SEARCH_PREFIX)) { + setIsThinking(true); + // search lenses. + const trimmedQuery = query.substring( + LENS_SEARCH_PREFIX.length, + query.length, + ); + const results = await invoke("search_lenses", { + query: trimmedQuery, + }); + setResultMode(ResultDisplayMode.Lenses); + setLensResults(results); + setIsThinking(false); + } else if (query.length >= SEARCH_MIN_CHARS) { + setIsThinking(true); + // search docs + const resp = await invoke("search_docs", { + query, + lenses: selectedLenses, + }); + setResultMode(ResultDisplayMode.Documents); + setDocResults(resp.results); + // setSearchMeta(resp.meta); + setIsThinking(false); + } + }, QUERY_DEBOUNCE_MS); + return () => clearTimeout(timer); + }, [query, selectedLenses, clearQuery]); + + return ( +
+ + + {isThinking ? : null} +
+ ); +} diff --git a/apps/desktop-client/src/pages/search/ResultListView.tsx b/apps/desktop-client/src/pages/search/ResultListView.tsx new file mode 100644 index 000000000..f6819eb17 --- /dev/null +++ b/apps/desktop-client/src/pages/search/ResultListView.tsx @@ -0,0 +1,62 @@ +import { useEffect } from "react"; +import { LensResult } from "../../bindings/LensResult"; +import { SearchResult } from "../../bindings/SearchResult"; +import { ResultDisplayMode } from "./constants"; +import { DocumentResultItem } from "./DocumentResultItem"; +import { LensResultItem } from "./LensResultItem"; + +interface Props { + docResults: SearchResult[]; + lensResults: LensResult[]; + displayMode: ResultDisplayMode; + selectedIdx: number; +} + +export function ResultListView({ + docResults, + lensResults, + displayMode, + selectedIdx, +}: Props) { + // Scroll to the current selected result whenever the selectedIdx changes. + useEffect(() => { + const element = document.getElementById(`result-${selectedIdx}`); + if (element) { + element.scrollIntoView(true); + } + }, [selectedIdx]); + + return ( +
+ {displayMode === ResultDisplayMode.Documents ? ( +
+
+ {docResults.map((doc, idx) => ( + {}} + result={doc} + isSelected={selectedIdx === idx} + /> + ))} +
+
+ ) : null} + {displayMode === ResultDisplayMode.Lenses ? ( +
+
+ {lensResults.map((lens, idx) => ( + + ))} +
+
+ ) : null} +
+ ); +} diff --git a/apps/desktop-client/src/pages/search/SearchInput.tsx b/apps/desktop-client/src/pages/search/SearchInput.tsx new file mode 100644 index 000000000..f28ca640c --- /dev/null +++ b/apps/desktop-client/src/pages/search/SearchInput.tsx @@ -0,0 +1,80 @@ +import React, { KeyboardEvent, useEffect, useRef } from "react"; +import { SelectedLenses } from "./SelectedLens"; +import { listen } from "@tauri-apps/api/event"; + +interface Props { + selectedLenses: string[]; + setSelectedLenses: React.Dispatch>; + query: string; + setQuery: React.Dispatch>; + // Event handlers. + onEnter?: (event: KeyboardEvent) => void; + onKeyEvent?: (event: KeyboardEvent) => void; +} + +export function SearchInput({ + selectedLenses, + setSelectedLenses, + query, + setQuery, + onEnter = () => {}, + onKeyEvent = () => {}, +}: Props) { + const searchInput = useRef(null); + const handleUpdateQuery = () => { + if (searchInput.current) { + setQuery(searchInput.current.value); + } + }; + + const handleKeyEvent = async (event: KeyboardEvent) => { + const key = event.key; + // ArrowXX: Prevent cursor from moving around + // Tab: Prevent search box from losing focus + if (["ArrowUp", "ArrowDown", "Tab"].includes(key)) { + event.preventDefault(); + } + + switch (event.key) { + case "Backspace": + // handle clearing lenses + if (query.length === 0 && selectedLenses.length > 0) { + setSelectedLenses([]); + } + break; + case "Enter": + return onEnter(event); + default: + return onKeyEvent(event); + } + }; + + useEffect(() => { + const initialize = async () => { + await listen("FocusWindow", () => { + searchInput.current?.focus(); + }); + }; + + initialize().catch(console.error); + }, []); + + return ( +
+ + searchInput.current?.focus()} + value={query} + spellCheck={false} + tabIndex={-1} + /> +
+ ); +} diff --git a/apps/desktop-client/src/pages/search/index.tsx b/apps/desktop-client/src/pages/search/SearchPage.tsx similarity index 56% rename from apps/desktop-client/src/pages/search/index.tsx rename to apps/desktop-client/src/pages/search/SearchPage.tsx index 74282da2d..127682fb9 100644 --- a/apps/desktop-client/src/pages/search/index.tsx +++ b/apps/desktop-client/src/pages/search/SearchPage.tsx @@ -5,38 +5,24 @@ import { LensResult } from "../../bindings/LensResult"; import { SearchResults } from "../../bindings/SearchResults"; import { SearchMeta } from "../../bindings/SearchMeta"; import { SearchResult } from "../../bindings/SearchResult"; -import { SelectedLenses } from "./SelectedLens"; import { SearchStatus } from "./SearchStatus"; -import { DocumentResultItem } from "./DocumentResultItem"; -import { LensResultItem } from "./LensResultItem"; import { UserActionSettings } from "../../bindings/UserActionSettings"; import { ActionListButton, ActionsList } from "./ActionsList"; -import { DEFAULT_ACTION } from "./constants"; +import { + DEFAULT_ACTION, + LENS_SEARCH_PREFIX, + QUERY_DEBOUNCE_MS, + ResultDisplayMode, + SEARCH_MIN_CHARS, +} from "./constants"; import Handlebars from "handlebars"; import { ContextActions } from "../../bindings/ContextActions"; import { includeAction, resultToTemplate } from "./utils"; import { CustomTitleBar } from "../../components/CustomTitleBar"; - -const LENS_SEARCH_PREFIX: string = "/"; -const QUERY_DEBOUNCE_MS: number = 256; -const SEARCH_MIN_CHARS: number = 2; - -enum ResultDisplay { - None, - Documents, - Lenses, -} - -// pressed_key: None, -// executed_key: None, -// executed_action: None, -// modifier: ModifiersState::empty(), -// show_actions: false, -// selected_action_idx: 0, -// action_menu_button_selected: false, +import { SearchInput } from "./SearchInput"; +import { ResultListView } from "./ResultListView"; export function SearchPage() { - const searchInput = useRef(null); const searchWrapperRef = useRef(null); const [selectedIdx, setSelectedIdx] = useState(0); @@ -44,8 +30,8 @@ export function SearchPage() { const [docResults, setDocResults] = useState([]); const [lensResults, setLensResults] = useState([]); - const [resultMode, setResultMode] = useState( - ResultDisplay.None, + const [resultMode, setResultMode] = useState( + ResultDisplayMode.None, ); const [isThinking, setIsThinking] = useState(false); @@ -71,7 +57,7 @@ export function SearchPage() { // Clear search results const clearResults = useCallback(async () => { - setResultMode(ResultDisplay.None); + setResultMode(ResultDisplayMode.None); setSelectedIdx(0); setDocResults([]); setLensResults([]); @@ -85,11 +71,7 @@ export function SearchPage() { const clearQuery = useCallback(async () => { setQuery(""); await clearResults(); - - if (searchInput.current) { - searchInput.current.value = ""; - } - }, [clearResults, searchInput]); + }, [clearResults]); const moveSelectionUp = () => { if (showActions) { @@ -97,7 +79,7 @@ export function SearchPage() { setSelectedActionIdx((idx) => (idx > 0 ? idx - 1 : idx)); } else { // notihng to do - if (resultMode === ResultDisplay.None) { + if (resultMode === ResultDisplayMode.None) { return; } setSelectedIdx((idx) => (idx > 0 ? idx - 1 : idx)); @@ -111,87 +93,67 @@ export function SearchPage() { setSelectedActionIdx((idx) => (idx < max ? idx + 1 : max)); } else { let max = 0; - if (resultMode === ResultDisplay.Documents) { + if (resultMode === ResultDisplayMode.Documents) { max = docResults.length - 1; - } else if (resultMode === ResultDisplay.Lenses) { + } else if (resultMode === ResultDisplayMode.Lenses) { max = lensResults.length - 1; } setSelectedIdx((idx) => (idx < max ? idx + 1 : max)); } }; - const handleKeyEvent = async (event: KeyboardEvent) => { - if (event.type === "keydown") { - const key = event.key; - if ( - // ArrowXX: Prevent cursor from moving around - key === "ArrowUp" || - key === "ArrowDown" || - // Tab: Prevent search box from losing focus - key === "Tab" - ) { - event.preventDefault(); + const handleEnter = async () => { + // do action or handle selection + if (showActions) { + // handle whichever action is selected. + const action = + selectedActionIdx === 0 + ? DEFAULT_ACTION + : currentContextActions[selectedActionIdx - 1]; + handleSelectedAction(action); + } else { + if (resultMode === ResultDisplayMode.Documents) { + const selected = docResults[selectedIdx]; + await invoke("open_result", { url: selected.url }); + clearQuery(); + await invoke("escape"); + } else if (resultMode === ResultDisplayMode.Lenses) { + const selected = lensResults[selectedIdx]; + setSelectedLenses((lenses) => [...lenses, selected.label]); + clearQuery(); } + } + }; - switch (event.key) { - case "ArrowUp": - moveSelectionUp(); - break; - case "ArrowDown": - moveSelectionDown(); - break; - case "Enter": - // do action or handle selection - if (showActions) { - // handle whichever action is selected. - const action = - selectedActionIdx === 0 - ? DEFAULT_ACTION - : currentContextActions[selectedActionIdx - 1]; - handleSelectedAction(action); - } else { - if (resultMode === ResultDisplay.Documents) { - const selected = docResults[selectedIdx]; - await invoke("open_result", { url: selected.url }); - clearQuery(); - await invoke("escape"); - } else if (resultMode === ResultDisplay.Lenses) { - const selected = lensResults[selectedIdx]; - setSelectedLenses((lenses) => [...lenses, selected.label]); - clearQuery(); - } - } - break; - case "Escape": - // Close action menu if we're in it. - if (showActions) { - setShowActions(false); - // otherwise close the window. - } else { - clearQuery(); - await invoke("escape"); - } - break; - case "Backspace": - // handle clearing lenses - if (query.length === 0 && selectedLenses.length > 0) { - setSelectedLenses([]); - } - break; - case "Tab": - // Handle tab completion for len search/results - if (resultMode === ResultDisplay.Lenses) { - const selected = lensResults[selectedIdx]; - setSelectedLenses((lenses) => [...lenses, selected.label]); - clearQuery(); - // Jump to action menu - } else if (resultMode === ResultDisplay.Documents) { - setShowActions(true); - } - break; - } - } else if (event.type === "keyup") { - // handle keyup events. + const handleKeyEvent = async (event: KeyboardEvent) => { + switch (event.key) { + case "ArrowUp": + moveSelectionUp(); + break; + case "ArrowDown": + moveSelectionDown(); + break; + case "Escape": + // Close action menu if we're in it. + if (showActions) { + setShowActions(false); + // otherwise close the window. + } else { + clearQuery(); + await invoke("escape"); + } + break; + case "Tab": + // Handle tab completion for len search/results + if (resultMode === ResultDisplayMode.Lenses) { + const selected = lensResults[selectedIdx]; + setSelectedLenses((lenses) => [...lenses, selected.label]); + clearQuery(); + // Jump to action menu + } else if (resultMode === ResultDisplayMode.Documents) { + setShowActions(true); + } + break; } }; @@ -225,12 +187,6 @@ export function SearchPage() { setShowActions(false); }; - const handleUpdateQuery = () => { - if (searchInput.current) { - setQuery(searchInput.current.value); - } - }; - // when the query changes shoot it over to the server. useEffect(() => { if (query.length === 0) { @@ -248,7 +204,7 @@ export function SearchPage() { const results = await invoke("search_lenses", { query: trimmedQuery, }); - setResultMode(ResultDisplay.Lenses); + setResultMode(ResultDisplayMode.Lenses); setLensResults(results); setIsThinking(false); } else if (query.length >= SEARCH_MIN_CHARS) { @@ -258,7 +214,7 @@ export function SearchPage() { query, lenses: selectedLenses, }); - setResultMode(ResultDisplay.Documents); + setResultMode(ResultDisplayMode.Documents); setDocResults(resp.results); setSearchMeta(resp.meta); setIsThinking(false); @@ -287,14 +243,6 @@ export function SearchPage() { requestResize(); }, [docResults, lensResults]); - // Scroll to the current selected result. - useEffect(() => { - const element = document.getElementById(`result-${selectedIdx}`); - if (element) { - element.scrollIntoView(true); - } - }, [selectedIdx]); - useEffect(() => { // get_action_list const fetchUserActions = async () => { @@ -313,10 +261,6 @@ export function SearchPage() { await listen("ClearSearch", () => { clearResults(); }); - await listen("FocusWindow", () => { - searchInput.current?.focus(); - }); - await fetchUserActions(); }; @@ -327,54 +271,22 @@ export function SearchPage() {
searchInput.current?.focus()} > -
- - searchInput.current?.focus()} - spellCheck={false} - tabIndex={-1} - /> -
- {resultMode === ResultDisplay.Documents ? ( -
-
- {docResults.map((doc, idx) => ( - {}} - result={doc} - isSelected={selectedIdx === idx} - /> - ))} -
-
- ) : null} - {resultMode === ResultDisplay.Lenses ? ( -
-
- {lensResults.map((lens, idx) => ( - - ))} -
-
- ) : null} + +
Result, String> Ok(folder_path) } -#[tauri::command] -pub async fn escape(window: tauri::WebviewWindow) -> Result<(), String> { - window::hide_search_bar(&window); - Ok(()) -} - #[tauri::command] pub async fn open_folder_path(_: tauri::Window, path: &str) -> Result<(), String> { open_folder(Path::new(path).to_path_buf()); @@ -142,11 +135,6 @@ pub async fn copy_to_clipboard(win: tauri::Window, txt: &str) -> Result<(), Stri Ok(()) } -#[tauri::command] -pub async fn resize_window(window: tauri::WebviewWindow, height: f64) { - window::resize_window(&window, height).await; -} - #[tauri::command] pub async fn search_docs<'r>( win: tauri::Window, @@ -383,7 +371,7 @@ pub async fn wizard_finished( let _ = window.close(); navigate_to_tab( window.app_handle(), - &crate::constants::TabLocation::Discover, + &crate::constants::WindowLocation::Discover, ); } @@ -440,12 +428,3 @@ pub async fn user_settings(win: tauri::Window) -> Result { Err(String::from("Unable to access user settings")) } - -#[tauri::command] -pub async fn navigate(win: tauri::Window, page: String) -> Result<(), String> { - if let Ok(tab_loc) = TabLocation::from_str(&page) { - super::window::navigate_to_tab(win.app_handle(), &tab_loc); - } - - Ok(()) -} diff --git a/apps/tauri/src/cmd/window.rs b/apps/tauri/src/cmd/window.rs new file mode 100644 index 000000000..77c4c9827 --- /dev/null +++ b/apps/tauri/src/cmd/window.rs @@ -0,0 +1,30 @@ +use std::str::FromStr; +use tauri::Manager; + +use crate::constants::WindowLocation; + +#[tauri::command] +pub async fn escape(window: tauri::WebviewWindow) -> Result<(), String> { + crate::window::hide_search_bar(&window); + Ok(()) +} + +#[tauri::command] +pub async fn resize_window(window: tauri::WebviewWindow, height: f64) { + crate::window::resize_window(&window, height).await; +} + +#[tauri::command] +pub async fn navigate(win: tauri::Window, page: String) -> Result<(), String> { + if let Ok(tab_loc) = WindowLocation::from_str(&page) { + crate::window::navigate_to_tab(win.app_handle(), &tab_loc); + } + + Ok(()) +} + +#[tauri::command] +pub async fn open_big_mode(win: tauri::WebviewWindow) -> Result<(), String> { + crate::window::show_bigmode(win.app_handle()); + Ok(()) +} diff --git a/apps/tauri/src/constants.rs b/apps/tauri/src/constants.rs index f980e0905..b921565d1 100644 --- a/apps/tauri/src/constants.rs +++ b/apps/tauri/src/constants.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use strum_macros::{AsRefStr, Display, EnumString}; pub const INPUT_WIDTH: f64 = 640.0; @@ -12,6 +14,7 @@ pub const VERSION_CHECK_INTERVAL_S: u64 = 60 * 60 * 6; // Check on start & every day for new lenses pub const LENS_UPDATE_CHECK_INTERVAL_S: u64 = 60 * 60 * 24; +pub const BIG_MODE_WIN_NAME: &str = "big_mode"; pub const SEARCH_WIN_NAME: &str = "main"; pub const SETTINGS_WIN_NAME: &str = "settings_window"; pub const STARTUP_WIN_NAME: &str = "startup_window"; @@ -20,7 +23,20 @@ pub const WIZARD_WIN_NAME: &str = "wizard_window"; pub const PROGRESS_WIN_NAME: &str = "progress_window"; #[derive(Display, EnumString, AsRefStr)] -pub enum TabLocation { +pub enum WindowLocation { + #[strum(serialize = "/bigmode")] + BigMode, + #[strum(serialize = "/progress")] + DownloadProgress, + #[strum(serialize = "/")] + SearchBar, + #[strum(serialize = "/startup")] + Startup, + #[strum(serialize = "/updater")] + Updater, + #[strum(serialize = "/wizard")] + Wizard, + // Settings tabs #[strum(serialize = "/settings/connections")] Connections, #[strum(serialize = "/settings/discover")] @@ -30,3 +46,9 @@ pub enum TabLocation { #[strum(serialize = "/settings/user")] UserSettings, } + +impl From for PathBuf { + fn from(value: WindowLocation) -> Self { + value.to_string().into() + } +} diff --git a/apps/tauri/src/lib.rs b/apps/tauri/src/lib.rs index a3b663fc2..ae6a4485b 100644 --- a/apps/tauri/src/lib.rs +++ b/apps/tauri/src/lib.rs @@ -140,26 +140,27 @@ pub fn run() -> Result<(), Box> { cmd::copy_to_clipboard, cmd::default_indices, cmd::delete_doc, - cmd::escape, cmd::get_library_stats, cmd::get_shortcut, cmd::list_connections, cmd::load_action_settings, cmd::load_user_settings, - cmd::navigate, cmd::network_change, cmd::open_folder_path, cmd::open_lens_folder, cmd::open_result, cmd::open_settings_folder, cmd::recrawl_domain, - cmd::resize_window, cmd::resync_connection, cmd::revoke_connection, cmd::save_user_settings, cmd::search_docs, cmd::search_lenses, cmd::update_and_restart, + cmd::window::escape, + cmd::window::navigate, + cmd::window::open_big_mode, + cmd::window::resize_window, cmd::wizard_finished, plugins::lens_updater::install_lens, plugins::lens_updater::list_installable_lenses, diff --git a/apps/tauri/src/menu.rs b/apps/tauri/src/menu.rs index 7d9b31297..9f723b1a7 100644 --- a/apps/tauri/src/menu.rs +++ b/apps/tauri/src/menu.rs @@ -237,13 +237,13 @@ pub fn handle_tray_menu_events(app: &AppHandle, event: MenuEvent) { } } MenuID::DISCOVER => { - window::navigate_to_tab(app, &crate::constants::TabLocation::Discover); + window::navigate_to_tab(app, &crate::constants::WindowLocation::Discover); } MenuID::OPEN_CONNECTION_MANAGER => { - window::navigate_to_tab(app, &crate::constants::TabLocation::Connections); + window::navigate_to_tab(app, &crate::constants::WindowLocation::Connections); } MenuID::OPEN_LENS_MANAGER => { - window::navigate_to_tab(app, &crate::constants::TabLocation::Library); + window::navigate_to_tab(app, &crate::constants::WindowLocation::Library); } MenuID::OPEN_LOGS_FOLDER => { if let Some(config) = app.try_state::() { @@ -251,7 +251,7 @@ pub fn handle_tray_menu_events(app: &AppHandle, event: MenuEvent) { } } MenuID::OPEN_SETTINGS_MANAGER => { - window::navigate_to_tab(app, &crate::constants::TabLocation::UserSettings); + window::navigate_to_tab(app, &crate::constants::WindowLocation::UserSettings); } MenuID::OPEN_WIZARD => { window::show_wizard_window(app); diff --git a/apps/tauri/src/platform/mac.rs b/apps/tauri/src/platform/mac.rs index 3d59ea91c..756c017e8 100644 --- a/apps/tauri/src/platform/mac.rs +++ b/apps/tauri/src/platform/mac.rs @@ -1,5 +1,5 @@ use shared::event::ClientEvent; -use tauri::{Emitter, Manager, WebviewWindow}; +use tauri::{Emitter, WebviewWindow}; use url::Url; use crate::window; @@ -9,14 +9,14 @@ pub fn is_visible(window: &WebviewWindow) -> bool { } pub fn show_search_bar(window: &WebviewWindow) { - let _ = tauri::AppHandle::show(window.app_handle()); + let _ = window.show(); let _ = window.set_focus(); window::center_search_bar(window); } pub fn hide_search_bar(window: &WebviewWindow) { - let _ = tauri::AppHandle::hide(window.app_handle()); - let _ = window.emit(ClientEvent::ClearSearch.as_ref(), true); + let _ = window.hide(); + let _ = window.emit(ClientEvent::ClearSearch.as_ref(), ()); } pub fn os_open(url: &Url, application: Option) -> anyhow::Result<()> { diff --git a/apps/tauri/src/window.rs b/apps/tauri/src/window.rs index 95f87be70..6e794657c 100644 --- a/apps/tauri/src/window.rs +++ b/apps/tauri/src/window.rs @@ -1,4 +1,4 @@ -use crate::constants::{TabLocation, SETTINGS_WIN_NAME}; +use crate::constants::{WindowLocation, SETTINGS_WIN_NAME}; use crate::menu::get_app_menu; use crate::{constants, platform}; use shared::config::Config; @@ -150,7 +150,7 @@ fn show_window(window: &WebviewWindow) { let _ = window.center(); } -pub fn navigate_to_tab(app: &AppHandle, tab_url: &TabLocation) { +pub fn navigate_to_tab(app: &AppHandle, tab_url: &WindowLocation) { let tab_url = tab_url.to_string(); let window = if let Some(window) = app.get_webview_window(constants::SETTINGS_WIN_NAME) { @@ -173,6 +173,24 @@ pub fn navigate_to_tab(app: &AppHandle, tab_url: &TabLocation) { show_window(&window); } +pub fn show_bigmode(app: &AppHandle) { + let window = if let Some(window) = app.get_webview_window(constants::BIG_MODE_WIN_NAME) { + window + } else { + WebviewWindowBuilder::new( + app, + constants::BIG_MODE_WIN_NAME, + WebviewUrl::App(WindowLocation::BigMode.into()), + ) + .title("Spyglass - Big Mode") + .min_inner_size(constants::MIN_WINDOW_WIDTH, constants::MIN_WINDOW_HEIGHT) + .build() + .expect("Unable to build window for bigmode") + }; + + show_window(&window); +} + pub fn show_update_window(app: &AppHandle) { let window = if let Some(window) = app.get_webview_window(constants::UPDATE_WIN_NAME) { window @@ -180,7 +198,7 @@ pub fn show_update_window(app: &AppHandle) { WebviewWindowBuilder::new( app, constants::UPDATE_WIN_NAME, - WebviewUrl::App("/updater".into()), + WebviewUrl::App(WindowLocation::Updater.into()), ) .title("Spyglass - Update Available!") .min_inner_size(450.0, 375.0) @@ -199,7 +217,7 @@ pub fn show_startup_window(app: &AppHandle) -> WebviewWindow { WebviewWindowBuilder::new( app, constants::STARTUP_WIN_NAME, - WebviewUrl::App("/startup".into()), + WebviewUrl::App(WindowLocation::Startup.into()), ) .title("Spyglass - Starting up") .decorations(false) @@ -221,7 +239,7 @@ pub fn update_progress_window(app: &AppHandle, msg: &str, progress: u8) -> Webvi WebviewWindowBuilder::new( app, constants::PROGRESS_WIN_NAME, - WebviewUrl::App("/progress".into()), + WebviewUrl::App(WindowLocation::DownloadProgress.into()), ) .title("Download Progress") .menu(Menu::new(app).expect("unable to create empty menu")) @@ -250,7 +268,7 @@ pub fn show_wizard_window(app: &AppHandle) { let wizard_window = WebviewWindowBuilder::new( app, constants::WIZARD_WIN_NAME, - WebviewUrl::App("/wizard".into()), + WebviewUrl::App(WindowLocation::Wizard.into()), ) .title("Getting Started") .menu(Menu::new(app).expect("Unable to create empty menu")) diff --git a/crates/entities/src/models/indexed_document.rs b/crates/entities/src/models/indexed_document.rs index b4c2c6c7e..4a7ef1093 100644 --- a/crates/entities/src/models/indexed_document.rs +++ b/crates/entities/src/models/indexed_document.rs @@ -585,10 +585,7 @@ mod test { let res = document_tag::Entity::find().all(&db).await?; assert_eq!(res.len(), 2); - let doc_res = super::Entity::find_by_id(doc.id.clone()) - .one(&db) - .await? - .unwrap(); + let doc_res = super::Entity::find_by_id(doc.id).one(&db).await?.unwrap(); let doc_tags = doc_res.find_related(tag::Entity).all(&db).await?; assert_eq!(doc_res.id, doc.id); @@ -650,7 +647,7 @@ mod test { for (id, model) in tags_before.iter() { // The same tag should not have been changed in anyway - if let Some(model_after) = tags_after.get(&id) { + if let Some(model_after) = tags_after.get(id) { assert_eq!(model.id, model_after.id); } } diff --git a/crates/entities/src/models/lens.rs b/crates/entities/src/models/lens.rs index ff07a55eb..6cf136163 100644 --- a/crates/entities/src/models/lens.rs +++ b/crates/entities/src/models/lens.rs @@ -310,7 +310,7 @@ mod test { let (is_new, _model) = add_or_enable(&db, &lens, super::LensType::Simple) .await .unwrap(); - assert_eq!(is_new, false); + assert!(!is_new); let model = Entity::find().one(&db).await.unwrap().unwrap(); assert_eq!(model.name, "test_lens".to_owned()); diff --git a/crates/shared/src/event.rs b/crates/shared/src/event.rs index 9dbb03554..90c5a2117 100644 --- a/crates/shared/src/event.rs +++ b/crates/shared/src/event.rs @@ -27,73 +27,6 @@ pub enum ClientEvent { UpdateLensFinished, } -#[derive(AsRefStr, Display, Deserialize, Serialize, TS)] -#[ts(export)] -pub enum ClientInvoke { - #[serde(rename = "authorize_connection")] - AuthorizeConnection, - #[serde(rename = "choose_folder")] - ChooseFolder, - #[serde(rename = "default_indices")] - DefaultIndices, - #[serde(rename = "escape")] - Escape, - #[serde(rename = "open_plugins_folder")] - EditPluginSettings, - #[serde(rename = "get_library_stats")] - GetLibraryStats, - #[serde(rename = "get_shortcut")] - GetShortcut, - #[serde(rename = "get_startup_progress")] - GetStartupProgressText, - #[serde(rename = "install_lens")] - InstallLens, - #[serde(rename = "list_connections")] - ListConnections, - #[serde(rename = "list_installed_lenses")] - ListInstalledLenses, - #[serde(rename = "list_installable_lenses")] - ListInstallableLenses, - #[serde(rename = "list_plugins")] - ListPlugins, - #[serde(rename = "load_user_settings")] - LoadUserSettings, - #[serde(rename = "load_action_settings")] - LoadUserActions, - #[serde(rename = "resize_window")] - ResizeWindow, - #[serde(rename = "resync_connection")] - ResyncConnection, - #[serde(rename = "revoke_connection")] - RevokeConnection, - #[serde(rename = "run_lens_updater")] - RunLensUpdater, - #[serde(rename = "save_user_settings")] - SaveUserSettings, - #[serde(rename = "search_docs")] - SearchDocuments, - #[serde(rename = "search_lenses")] - SearchLenses, - #[serde(rename = "open_folder_path")] - OpenFolder, - #[serde(rename = "open_lens_folder")] - OpenLensFolder, - #[serde(rename = "open_result")] - OpenResult, - #[serde(rename = "copy_to_clipboard")] - CopyToClipboard, - #[serde(rename = "open_settings_folder")] - OpenSettingsFolder, - #[serde(rename = "uninstall_lens")] - UninstallLens, - #[serde(rename = "update_and_restart")] - UpdateAndRestart, - #[serde(rename = "wizard_finished")] - WizardFinished, - #[serde(rename = "navigate")] - Navigate, -} - #[derive(Deserialize, Serialize)] pub struct AuthorizeConnectionParams { pub id: String, diff --git a/crates/shared/src/invoke.rs b/crates/shared/src/invoke.rs new file mode 100644 index 000000000..5e8b45a56 --- /dev/null +++ b/crates/shared/src/invoke.rs @@ -0,0 +1,74 @@ +use serde::{Deserialize, Serialize}; +use strum_macros::{AsRefStr, Display}; +use ts_rs::TS; + +/// NOTE: When adding a new invoke command, +/// the label should match up to the tauri generated command names. +#[derive(AsRefStr, Display, Deserialize, Serialize, TS)] +#[ts(export)] +pub enum ClientInvoke { + #[serde(rename = "authorize_connection")] + AuthorizeConnection, + #[serde(rename = "choose_folder")] + ChooseFolder, + #[serde(rename = "copy_to_clipboard")] + CopyToClipboard, + #[serde(rename = "default_indices")] + DefaultIndices, + #[serde(rename = "escape")] + Escape, + #[serde(rename = "open_plugins_folder")] + EditPluginSettings, + #[serde(rename = "get_library_stats")] + GetLibraryStats, + #[serde(rename = "get_shortcut")] + GetShortcut, + #[serde(rename = "get_startup_progress")] + GetStartupProgressText, + #[serde(rename = "install_lens")] + InstallLens, + #[serde(rename = "list_connections")] + ListConnections, + #[serde(rename = "list_installed_lenses")] + ListInstalledLenses, + #[serde(rename = "list_installable_lenses")] + ListInstallableLenses, + #[serde(rename = "list_plugins")] + ListPlugins, + #[serde(rename = "load_user_settings")] + LoadUserSettings, + #[serde(rename = "load_action_settings")] + LoadUserActions, + #[serde(rename = "open_big_mode")] + OpenBigMode, + #[serde(rename = "open_folder_path")] + OpenFolder, + #[serde(rename = "open_lens_folder")] + OpenLensFolder, + #[serde(rename = "open_result")] + OpenResult, + #[serde(rename = "open_settings_folder")] + OpenSettingsFolder, + #[serde(rename = "resize_window")] + ResizeWindow, + #[serde(rename = "resync_connection")] + ResyncConnection, + #[serde(rename = "revoke_connection")] + RevokeConnection, + #[serde(rename = "run_lens_updater")] + RunLensUpdater, + #[serde(rename = "save_user_settings")] + SaveUserSettings, + #[serde(rename = "search_docs")] + SearchDocuments, + #[serde(rename = "search_lenses")] + SearchLenses, + #[serde(rename = "uninstall_lens")] + UninstallLens, + #[serde(rename = "update_and_restart")] + UpdateAndRestart, + #[serde(rename = "wizard_finished")] + WizardFinished, + #[serde(rename = "navigate")] + Navigate, +} diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index ccacf09d6..69c19d340 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -3,6 +3,7 @@ pub mod config; pub mod constants; pub mod event; pub mod form; +pub mod invoke; pub mod keyboard; #[cfg(feature = "metrics")] pub mod metrics; diff --git a/crates/spyglass-processor/src/parser/audio.rs b/crates/spyglass-processor/src/parser/audio.rs index d2a40f4f0..f21aaca7f 100644 --- a/crates/spyglass-processor/src/parser/audio.rs +++ b/crates/spyglass-processor/src/parser/audio.rs @@ -44,7 +44,7 @@ mod test { let path = "../../fixtures/audio/jfk.wav".into(); let res = transcribe_audio(path, MODEL_PATH.into()).expect("Unable to transcribe"); let segments = res.segments; - assert!(segments.len() > 0); + assert!(!segments.is_empty()); let combined = segments .iter() @@ -60,7 +60,7 @@ mod test { let path = "../../fixtures/audio/armstrong.ogg".into(); let res = transcribe_audio(path, MODEL_PATH.into()).expect("Unable to transcribe"); let segments = res.segments; - assert!(segments.len() > 0); + assert!(!segments.is_empty()); let combined = segments .iter() .map(|x| x.segment.to_string()) @@ -77,7 +77,7 @@ mod test { let path = "../../fixtures/audio/count_of_monte_cristo.mp3".into(); let res = transcribe_audio(path, MODEL_PATH.into()).expect("Unable to transcribe"); let segments = res.segments; - assert!(segments.len() > 0); + assert!(!segments.is_empty()); let combined = segments .iter() .map(|x| x.segment.to_string()) diff --git a/crates/spyglass-processor/src/parser/pdf_parser.rs b/crates/spyglass-processor/src/parser/pdf_parser.rs index 00f0a33cb..8a776c8b1 100644 --- a/crates/spyglass-processor/src/parser/pdf_parser.rs +++ b/crates/spyglass-processor/src/parser/pdf_parser.rs @@ -175,7 +175,7 @@ mod tests { #[test] fn test_pdf_metadata_extraction_from_pdf_with_metadata() { let path_with_metadata = Path::new("../../fixtures/pdf/pdf_with_metadata.pdf"); - let metadata = super::PdfMetadata::parse(&path_with_metadata); + let metadata = super::PdfMetadata::parse(path_with_metadata); assert_eq!(metadata.title, Some("PDF title".to_string())); assert_eq!(metadata.author, Some("PDF author".to_string())); } @@ -183,7 +183,7 @@ mod tests { #[test] fn test_pdf_metadata_extraction_from_pdf_with_missing_metadata() { let path_with_metadata = Path::new("../../fixtures/pdf/pdf_without_metadata.pdf"); - let metadata = super::PdfMetadata::parse(&path_with_metadata); + let metadata = super::PdfMetadata::parse(path_with_metadata); assert_eq!(metadata.title, None); assert_eq!(metadata.author, None); } diff --git a/crates/spyglass-processor/src/utils/mime.rs b/crates/spyglass-processor/src/utils/mime.rs index c89cef077..994260648 100644 --- a/crates/spyglass-processor/src/utils/mime.rs +++ b/crates/spyglass-processor/src/utils/mime.rs @@ -214,7 +214,7 @@ mod test { #[test] pub fn test_is_supported() { - assert_eq!(SupportedMime::is_supported("text/javascript"), true); - assert_eq!(SupportedMime::is_supported(GSLIDES), false); + assert!(SupportedMime::is_supported("text/javascript")); + assert!(!SupportedMime::is_supported(GSLIDES)); } } diff --git a/crates/spyglass-searcher/src/lib.rs b/crates/spyglass-searcher/src/lib.rs index 91ebccaa5..eac6f23ff 100644 --- a/crates/spyglass-searcher/src/lib.rs +++ b/crates/spyglass-searcher/src/lib.rs @@ -198,7 +198,7 @@ mod test { fresh and green with every spring, carrying in their lower leaf junctures the debris of the winter’s flooding; and sycamores with mottled, white, recumbent limbs and branches that arch over the pool", - tags: &vec![1_i64], + tags: &[1_i64], published_at: None, last_modified: None, }.to_document()) @@ -220,7 +220,7 @@ mod test { fresh and green with every spring, carrying in their lower leaf junctures the debris of the winter’s flooding; and sycamores with mottled, white, recumbent limbs and branches that arch over the pool", - tags: &vec![2_i64], + tags: &[2_i64], published_at: None, last_modified: None, }.to_document()) @@ -241,7 +241,7 @@ mod test { eros. Donec rhoncus mauris libero, et imperdiet neque sagittis sed. Nulla ac volutpat massa. Vivamus sed imperdiet est, id pretium ex. Praesent suscipit mattis ipsum, a lacinia nunc semper vitae.", - tags: &vec![2_i64], + tags: &[2_i64], published_at: None, last_modified: None, } @@ -260,7 +260,7 @@ mod test { enterprise which you have regarded with such evil forebodings. I arrived here yesterday, and my first task is to assure my dear sister of my welfare and increasing confidence in the success of my undertaking.", - tags: &vec![1_i64], + tags: &[1_i64], published_at: None, last_modified: None }.to_document()).await diff --git a/crates/spyglass-searcher/src/utils.rs b/crates/spyglass-searcher/src/utils.rs index 3ebd5bfee..b8d7c5b73 100644 --- a/crates/spyglass-searcher/src/utils.rs +++ b/crates/spyglass-searcher/src/utils.rs @@ -169,7 +169,7 @@ mod test { .index .tokenizer_for_field(fields.content) .expect("Unable to get tokenizer for content field"); - let desc = generate_highlight_preview(&tokenizer, "rust programming", &blurb); + let desc = generate_highlight_preview(&tokenizer, "rust programming", blurb); assert_eq!(desc, "Rust rust is a multi-paradigm, high-level, general-purpose programming ..."); } } diff --git a/crates/spyglass/src/crawler/mod.rs b/crates/spyglass/src/crawler/mod.rs index 21bb36ac8..3abec2ee2 100644 --- a/crates/spyglass/src/crawler/mod.rs +++ b/crates/spyglass/src/crawler/mod.rs @@ -907,9 +907,9 @@ mod test { // Add resource rule to stop the crawl above let res = crawler.fetch_by_job(&state, model.id, true).await; - if let Err(error) = res { + if let Err(error) = &res { eprintln!("Error processing crawl {:?}", error); - assert!(false); } + assert!(res.is_ok()); } } diff --git a/crates/spyglass/src/crawler/robots.rs b/crates/spyglass/src/crawler/robots.rs index e3ece3d51..137d255e2 100644 --- a/crates/spyglass/src/crawler/robots.rs +++ b/crates/spyglass/src/crawler/robots.rs @@ -278,8 +278,8 @@ mod test { let allow = filter_set(&matches, true); let disallow = filter_set(&matches, false); - assert_eq!(allow.is_match("/Belt_transport_system"), true); - assert_eq!(disallow.is_match("/Belt_transport_system"), false); + assert!(allow.is_match("/Belt_transport_system")); + assert!(!disallow.is_match("/Belt_transport_system")); } #[tokio::test] @@ -305,6 +305,6 @@ mod test { let res = check_resource_rules(&db, &crawler.client, &url).await; - assert_eq!(res, true); + assert!(res); } } diff --git a/crates/spyglass/src/task/worker.rs b/crates/spyglass/src/task/worker.rs index 155615edb..0ce48b2a7 100644 --- a/crates/spyglass/src/task/worker.rs +++ b/crates/spyglass/src/task/worker.rs @@ -330,8 +330,10 @@ mod test { #[tokio::test] async fn test_handle_cdx_collection() { - let mut lens = LensConfig::default(); - lens.name = "example_lens".to_string(); + let lens = LensConfig { + name: "example_lens".to_string(), + ..Default::default() + }; let db = setup_test_db().await; let state = AppState::builder() @@ -502,14 +504,14 @@ mod test { assert_eq!(docs.len(), 1); // Should have added the tag. - let new_doc = docs.get(0).expect("new_doc"); + let new_doc = docs.first().expect("new_doc"); let tags = new_doc .find_related(tag::Entity) .all(&db) .await .unwrap_or_default(); assert_eq!(tags.len(), 1); - let tag = tags.get(0).expect("tags.get(0)"); + let tag = tags.first().expect("tags.get(0)"); assert_eq!(tag.label, TagType::Source.to_string()); assert_eq!(tag.value, "web".to_string()); } @@ -559,7 +561,7 @@ mod test { content: Some("fake content".to_owned()), title: Some("Title".to_owned()), url: "https://example.com/test".to_owned(), - tags: vec![((TagType::MimeType, "application/pdf".to_owned()))], + tags: vec![(TagType::MimeType, "application/pdf".to_owned())], ..Default::default() }; @@ -577,7 +579,7 @@ mod test { assert_eq!(docs.len(), 1); // Should only add the new tag. - let doc = docs.get(0).expect("docs.get"); + let doc = docs.first().expect("docs.get"); let tags = doc .find_related(tag::Entity) .all(&db) diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 8402aed98..000000000 --- a/package-lock.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "name": "spyglass-app", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "devDependencies": { - "daisyui": "^4.12.14" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/css-selector-tokenizer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", - "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/culori": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/culori/-/culori-3.3.0.tgz", - "integrity": "sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/daisyui": { - "version": "4.12.14", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.12.14.tgz", - "integrity": "sha512-hA27cdBasdwd4/iEjn+aidoCrRroDuo3G5W9NDKaVCJI437Mm/3eSL/2u7MkZ0pt8a+TrYF3aT2pFVemTS3how==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-selector-tokenizer": "^0.8", - "culori": "^3", - "picocolors": "^1", - "postcss-js": "^4" - }, - "engines": { - "node": ">=16.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/daisyui" - } - }, - "node_modules/fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 9ca9ff741..000000000 --- a/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "devDependencies": { - "daisyui": "^4.12.14" - } -} From 145186349a76ac9e525952b9a981a20a61a341a3 Mon Sep 17 00:00:00 2001 From: Andrew Huynh Date: Tue, 19 Nov 2024 23:03:02 -0800 Subject: [PATCH 2/2] bugfix: add missing startup/updater popups (#554) * adding missing startup popup window * adding missing updater popup * style cleanup --- apps/desktop-client/src/main.tsx | 6 +- .../desktop-client/src/pages/StartupPopup.tsx | 44 +++++++++++++ .../desktop-client/src/pages/UpdaterPopup.tsx | 64 +++++++++++++++++++ 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 apps/desktop-client/src/pages/StartupPopup.tsx create mode 100644 apps/desktop-client/src/pages/UpdaterPopup.tsx diff --git a/apps/desktop-client/src/main.tsx b/apps/desktop-client/src/main.tsx index 902792ee0..5e63966fa 100644 --- a/apps/desktop-client/src/main.tsx +++ b/apps/desktop-client/src/main.tsx @@ -8,6 +8,8 @@ import { WizardPage } from "./pages/wizard/WizardPage.tsx"; import { SettingsPage } from "./pages/settings/SettingsPage.tsx"; import { ProgressPopup } from "./pages/ProgressPopup.tsx"; import { BigMode } from "./pages/bigmode/BigMode.tsx"; +import { StartupPopup } from "./pages/StartupPopup.tsx"; +import { UpdaterPopup } from "./pages/UpdaterPopup.tsx"; const router = createBrowserRouter([ { @@ -30,11 +32,11 @@ const router = createBrowserRouter([ }, { path: "/startup", - element:
startup
, + element: , }, { path: "/updater", - element:
updater
, + element: , }, { path: "/wizard", diff --git a/apps/desktop-client/src/pages/StartupPopup.tsx b/apps/desktop-client/src/pages/StartupPopup.tsx new file mode 100644 index 000000000..095c8a7fd --- /dev/null +++ b/apps/desktop-client/src/pages/StartupPopup.tsx @@ -0,0 +1,44 @@ +import { ArrowPathIcon } from "@heroicons/react/24/solid"; +import { useEffect, useRef, useState } from "react"; +import { invoke } from "../glue"; + +export function StartupPopup() { + const checkInterval = useRef(null); + const [caption, setCaption] = useState("Reticulating splines..."); + const [timeTaken, setTimeTaken] = useState(0); + + const checkStatus = async () => { + setTimeTaken((t) => t + 1); + const status = await invoke("get_startup_progress"); + if (status === "DONE" && checkInterval.current) { + window.clearInterval(checkInterval.current); + } else { + setCaption(status); + } + }; + + useEffect(() => { + if (!checkInterval.current) { + checkInterval.current = window.setInterval(checkStatus, 1000); + } + + return () => { + if (checkInterval.current) { + window.clearInterval(checkInterval.current); + } + }; + }, []); + + return ( +
+
+ +
{"Starting Spyglass"}
+
{caption}
+
+ {timeTaken > 0 ? `${timeTaken}s` : null} +
+
+
+ ); +} diff --git a/apps/desktop-client/src/pages/UpdaterPopup.tsx b/apps/desktop-client/src/pages/UpdaterPopup.tsx new file mode 100644 index 000000000..5d65afaf7 --- /dev/null +++ b/apps/desktop-client/src/pages/UpdaterPopup.tsx @@ -0,0 +1,64 @@ +import classNames from "classnames"; +import { Btn } from "../components/Btn"; +import { useState } from "react"; +import { invoke } from "../glue"; + +// Random gif for your viewing pleasure. +const UPDATE_GIFS: string[] = [ + // Adventure Time + "10bxTLrpJNS0PC", + "fm4WhPMzu9hRK", + "13p77tfexyLtx6", + "13FBIII8M4IDDi", + // Futurama + "gYZ7qO81g4dt6", +]; + +export function UpdaterPopup() { + const [isUpdating, setIsUpdating] = useState(false); + + const handleUpdate = async () => { + setIsUpdating(true); + await invoke("update_and_restart"); + }; + + const rando = Math.floor(Math.random() * UPDATE_GIFS.length); + return ( +
+

Update Available!

+
+
+