From 1d61d095a89dfee02fc6f70b5955d6795c39f7b4 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Wed, 7 Jan 2026 11:09:59 +0100 Subject: [PATCH] Fix command launcher in event preview (#42) * fix(CommandLauncher): redirect to dashboard when executing commands from preview routes NIP-19 preview routes (/nevent..., /npub..., etc.) don't render the window system, so commands executed there appeared to do nothing. Now detects preview routes and navigates to the dashboard before creating the window, so the command actually takes effect. * fix(test): add WebSocket polyfill for Node.js test environment nostr-tools relay code requires WebSocket which isn't available in Node.js by default. Adding the ws package polyfill prevents "ReferenceError: WebSocket is not defined" errors in tests. * fix(test): add @types/ws and fix type cast for WebSocket polyfill --------- Co-authored-by: Claude --- package-lock.json | 11 +++++++++++ package.json | 1 + src/components/CommandLauncher.tsx | 22 ++++++++++++++++++++++ src/test/setup.ts | 9 +++++++-- 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1538447..1e345b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -73,6 +73,7 @@ "@types/react-dom": "^19.2.3", "@types/shell-quote": "^1.7.5", "@types/uuid": "^10.0.0", + "@types/ws": "^8.18.1", "@vitejs/plugin-react": "^4.3.4", "@vitest/ui": "^4.0.15", "autoprefixer": "^10.4.20", @@ -4389,6 +4390,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.48.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.0.tgz", diff --git a/package.json b/package.json index 46d704a..8320d18 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "@types/react-dom": "^19.2.3", "@types/shell-quote": "^1.7.5", "@types/uuid": "^10.0.0", + "@types/ws": "^8.18.1", "@vitejs/plugin-react": "^4.3.4", "@vitest/ui": "^4.0.15", "autoprefixer": "^10.4.20", diff --git a/src/components/CommandLauncher.tsx b/src/components/CommandLauncher.tsx index a4f487c..6e4e1f5 100644 --- a/src/components/CommandLauncher.tsx +++ b/src/components/CommandLauncher.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import { Command } from "cmdk"; import { useAtom } from "jotai"; import { useLiveQuery } from "dexie-react-hooks"; +import { useNavigate, useLocation } from "react-router"; import db from "@/services/db"; import { useGrimoire } from "@/core/state"; import { manPages } from "@/types/man"; @@ -11,6 +12,19 @@ import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; import { VisuallyHidden } from "@/components/ui/visually-hidden"; import "./command-launcher.css"; +/** Check if current path is a NIP-19 preview route (no window system) */ +function isNip19PreviewRoute(pathname: string): boolean { + // NIP-19 preview routes are single-segment paths starting with npub1, note1, nevent1, naddr1 + const segment = pathname.slice(1); // Remove leading / + if (segment.includes("/")) return false; // Multi-segment paths are not NIP-19 previews + return ( + segment.startsWith("npub1") || + segment.startsWith("note1") || + segment.startsWith("nevent1") || + segment.startsWith("naddr1") + ); +} + interface CommandLauncherProps { open: boolean; onOpenChange: (open: boolean) => void; @@ -23,6 +37,8 @@ export default function CommandLauncher({ const [input, setInput] = useState(""); const [editMode, setEditMode] = useAtom(commandLauncherEditModeAtom); const { state, addWindow, updateWindow } = useGrimoire(); + const navigate = useNavigate(); + const location = useLocation(); // Fetch spells with aliases const aliasedSpells = @@ -107,6 +123,12 @@ export default function CommandLauncher({ }); setEditMode(null); // Clear edit mode } else { + // If on a NIP-19 preview route (no window system), navigate to dashboard first + // The window will appear after navigation since state persists + if (isNip19PreviewRoute(location.pathname)) { + navigate("/"); + } + // Normal mode: create new window addWindow( recognizedCommand.appId, diff --git a/src/test/setup.ts b/src/test/setup.ts index eba54c2..cc8847a 100644 --- a/src/test/setup.ts +++ b/src/test/setup.ts @@ -1,7 +1,12 @@ /** * Vitest setup file * - * Polyfills IndexedDB for Node.js test environment. - * This allows Dexie to work in tests without a browser. + * Polyfills browser APIs for Node.js test environment. */ + +// Polyfill IndexedDB - allows Dexie to work in tests import "fake-indexeddb/auto"; + +// Polyfill WebSocket - required by nostr-tools relay code +import { WebSocket } from "ws"; +globalThis.WebSocket = WebSocket as unknown as typeof globalThis.WebSocket;