mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-13 00:46:54 +02:00
111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import { useGrimoire } from "@/core/state";
|
|
import { useAccountSync } from "@/hooks/useAccountSync";
|
|
import { TabBar } from "./TabBar";
|
|
import { Mosaic, MosaicWindow, MosaicBranch } from "react-mosaic-component";
|
|
import CommandLauncher from "./CommandLauncher";
|
|
import { WindowToolbar } from "./WindowToolbar";
|
|
import { WindowTile } from "./WindowTitle";
|
|
import { Terminal } from "lucide-react";
|
|
import UserMenu from "./nostr/user-menu";
|
|
import { GrimoireWelcome } from "./GrimoireWelcome";
|
|
|
|
export default function Home() {
|
|
const { state, updateLayout, removeWindow } = useGrimoire();
|
|
const [commandLauncherOpen, setCommandLauncherOpen] = useState(false);
|
|
|
|
// Sync active account and fetch relay lists
|
|
useAccountSync();
|
|
|
|
// Keyboard shortcut: Cmd/Ctrl+K
|
|
useEffect(() => {
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
|
|
e.preventDefault();
|
|
setCommandLauncherOpen((open) => !open);
|
|
}
|
|
};
|
|
|
|
window.addEventListener("keydown", handleKeyDown);
|
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
}, []);
|
|
|
|
const handleRemoveWindow = (id: string) => {
|
|
// Remove from windows map
|
|
removeWindow(id);
|
|
};
|
|
|
|
const renderTile = (id: string, path: MosaicBranch[]) => {
|
|
const window = state.windows[id];
|
|
|
|
if (!window) {
|
|
return (
|
|
<MosaicWindow
|
|
path={path}
|
|
title="Unknown Window"
|
|
toolbarControls={<WindowToolbar />}
|
|
>
|
|
<div className="p-4 text-muted-foreground">
|
|
Window not found: {id}
|
|
</div>
|
|
</MosaicWindow>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<WindowTile
|
|
id={id}
|
|
window={window}
|
|
path={path}
|
|
onClose={handleRemoveWindow}
|
|
/>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<CommandLauncher
|
|
open={commandLauncherOpen}
|
|
onOpenChange={setCommandLauncherOpen}
|
|
/>
|
|
<main className="h-screen w-screen flex flex-col bg-background text-foreground">
|
|
<header className="flex flex-row items-center justify-between px-1 border-b border-border">
|
|
<button
|
|
onClick={() => setCommandLauncherOpen(true)}
|
|
className="p-1 text-muted-foreground hover:text-accent transition-colors cursor-crosshair"
|
|
title="Launch command (Cmd+K)"
|
|
>
|
|
<Terminal className="size-4" />
|
|
</button>
|
|
<UserMenu />
|
|
</header>
|
|
<section className="flex-1 relative overflow-hidden">
|
|
{state.workspaces[state.activeWorkspaceId] && (
|
|
<>
|
|
{state.workspaces[state.activeWorkspaceId].layout === null ? (
|
|
<GrimoireWelcome
|
|
onLaunchCommand={() => setCommandLauncherOpen(true)}
|
|
/>
|
|
) : (
|
|
<Mosaic
|
|
renderTile={renderTile}
|
|
value={state.workspaces[state.activeWorkspaceId].layout}
|
|
onChange={updateLayout}
|
|
onRelease={(node) => {
|
|
// When Mosaic removes a node from the layout, clean up the window
|
|
if (typeof node === "string") {
|
|
handleRemoveWindow(node);
|
|
}
|
|
}}
|
|
className="mosaic-blueprint-theme"
|
|
/>
|
|
)}
|
|
</>
|
|
)}
|
|
</section>
|
|
<TabBar />
|
|
</main>
|
|
</>
|
|
);
|
|
}
|