mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-12 00:17:02 +02:00
* feat: Add pop-out command route for standalone window rendering Add a new /run route that allows windows to be opened in separate browser windows/tabs without affecting the main workspace layout. Changes: - Add RunCommandPage component for /run?cmd=<command> route - Add Pop Out button to WindowToolbar (ExternalLink icon) - Parse command from URL query parameter and render result - Construct minimal WindowInstance for rendering - Display command string in header with clean minimal UI This enables users to pop out any window into a separate browser context while maintaining the main workspace layout, useful for multi-monitor setups or keeping reference windows visible. * refactor: Remove header from pop-out command page Simplify RunCommandPage to only show the window renderer without any additional UI chrome. This provides a cleaner, more focused experience for popped-out windows. * refactor: Move pop-out action to window menu dropdown Move the pop-out button from a standalone icon to the three-dot menu dropdown to reduce toolbar clutter. The menu now always appears since pop-out is always available. * feat: Add AppShell header to pop-out command page Wrap RunCommandPage with AppShell (hideBottomBar) to show the header with user menu and command launcher, matching the behavior of NIP-19 preview pages. When a command is launched from the /run page, it navigates to the main dashboard (/) where the window system exists. --------- Co-authored-by: Claude <noreply@anthropic.com>
72 lines
1.7 KiB
TypeScript
72 lines
1.7 KiB
TypeScript
import { createBrowserRouter, RouterProvider } from "react-router";
|
|
import { AppShell } from "./components/layouts/AppShell";
|
|
import DashboardPage from "./components/pages/DashboardPage";
|
|
import SpellbookPage from "./components/pages/SpellbookPage";
|
|
import Nip19PreviewRouter from "./components/pages/Nip19PreviewRouter";
|
|
import RunCommandPage from "./components/pages/RunCommandPage";
|
|
|
|
const router = createBrowserRouter([
|
|
{
|
|
path: "/",
|
|
element: (
|
|
<AppShell>
|
|
<DashboardPage />
|
|
</AppShell>
|
|
),
|
|
},
|
|
{
|
|
path: "/run",
|
|
element: (
|
|
<AppShell hideBottomBar>
|
|
<RunCommandPage />
|
|
</AppShell>
|
|
),
|
|
},
|
|
{
|
|
path: "/preview/:actor/:identifier",
|
|
element: (
|
|
<AppShell>
|
|
<SpellbookPage />
|
|
</AppShell>
|
|
),
|
|
},
|
|
// NIP-19 identifier preview route - must come before /:actor/:identifier catch-all
|
|
{
|
|
path: "/:identifier",
|
|
element: (
|
|
<AppShell hideBottomBar>
|
|
<Nip19PreviewRouter />
|
|
</AppShell>
|
|
),
|
|
// Only match single-segment paths that look like NIP-19 identifiers
|
|
loader: ({ params }) => {
|
|
const id = params.identifier;
|
|
if (
|
|
!id ||
|
|
!(
|
|
id.startsWith("npub1") ||
|
|
id.startsWith("note1") ||
|
|
id.startsWith("nevent1") ||
|
|
id.startsWith("naddr1")
|
|
)
|
|
) {
|
|
throw new Response("Not Found", { status: 404 });
|
|
}
|
|
return null;
|
|
},
|
|
},
|
|
// Catch-all for two-segment paths (spellbooks, etc.)
|
|
{
|
|
path: "/:actor/:identifier",
|
|
element: (
|
|
<AppShell>
|
|
<SpellbookPage />
|
|
</AppShell>
|
|
),
|
|
},
|
|
]);
|
|
|
|
export default function Root() {
|
|
return <RouterProvider router={router} />;
|
|
}
|