feat: use Activity for keeping workspaces alive

This commit is contained in:
Alejandro Gómez
2025-12-10 13:12:05 +01:00
parent 5b00e42ddf
commit 0e59c288ff
6 changed files with 622 additions and 170 deletions

View File

@@ -0,0 +1,62 @@
import { Button } from "./ui/button";
interface GrimoireWelcomeProps {
onLaunchCommand: () => void;
}
export function GrimoireWelcome({ onLaunchCommand }: GrimoireWelcomeProps) {
return (
<div className="h-full w-full flex items-center justify-center">
<div className="flex flex-col items-center gap-8">
{/* Desktop: ASCII art */}
<div className="hidden md:block">
<pre className="font-mono text-xs leading-tight text-grimoire-gradient">
{` ★ ✦
: ☽
t#, ,;
✦ .Gt j. t ;##W. t j. f#i
j#W: EW, Ej .. : :#L:WE Ej EW, .E#t
☆ ;K#f E##j E#, ,W, .Et .KG ,#D E#, E##j i#W,
.G#D. E###D. E#t t##, ,W#t EE ;#f E#t E###D. L#D. ✦
j#K; E#jG#W; E#t L###, j###t f#. t#iE#t E#jG#W; :K#Wfff;
,K#f ,GD; E#t t##f E#t .E#j##, G#fE#t :#G GK E#t E#t t##f i##WLLLLt
☽ j#Wi E#t E#t :K#E: E#t ;WW; ##,:K#i E#t ;#L LW. E#t E#t :K#E: .E#L
.G#D: E#t E#KDDDD###iE#t j#E. ##f#W, E#t t#f f#: E#t E#KDDDD###i f#E: ★
,K#fK#t E#f,t#Wi,,,E#t .D#L ###K: E#t f#D#; E#t E#f,t#Wi,,, ,WW;
✦ j###t E#t ;#W: E#t :K#t ##D. E#t G#t E#t E#t ;#W: .D#;
.G#t DWi ,KK: E#t ... #G .. t E#t DWi ,KK: tt
;; ☆ ,;. j ✦ ,;. ☆ `}
</pre>
<p className="text-center text-muted-foreground text-sm font-mono mt-4">
a nostr client for magicians
</p>
</div>
{/* Mobile: Simple text */}
<div className="md:hidden text-center">
<h1 className="text-4xl font-bold text-grimoire-gradient mb-2">
grimoire
</h1>
<p className="text-muted-foreground text-sm font-mono">
a nostr client for magicians
</p>
</div>
{/* Launch button */}
<div className="flex flex-col items-center gap-3">
<p className="text-muted-foreground text-sm font-mono mb-2">
Press{" "}
<kbd className="px-2 py-1 bg-muted border border-border text-xs">
Cmd+K
</kbd>{" "}
or
</p>
<Button onClick={onLaunchCommand} variant="outline">
<span></span>
<span>Launch Command</span>
</Button>
</div>
</div>
</div>
);
}

View File

@@ -1,5 +1,4 @@
import { useState, useEffect } from "react";
import UserMenu from "./nostr/user-menu";
import { useState, useEffect, Activity } from "react";
import { useGrimoire } from "@/core/state";
import { useAccountSync } from "@/hooks/useAccountSync";
import Feed from "./nostr/Feed";
@@ -17,10 +16,11 @@ import EncodeViewer from "./EncodeViewer";
import DecodeViewer from "./DecodeViewer";
import KindRenderer from "./KindRenderer";
import { Terminal } from "lucide-react";
import { Button } from "./ui/button";
import UserMenu from "./nostr/user-menu";
import { GrimoireWelcome } from "./GrimoireWelcome";
export default function Home() {
const { state, activeWorkspace, updateLayout, removeWindow } = useGrimoire();
const { state, updateLayout, removeWindow } = useGrimoire();
const [commandLauncherOpen, setCommandLauncherOpen] = useState(false);
// Sync active account and fetch relay lists
@@ -129,7 +129,6 @@ export default function Home() {
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
@@ -142,59 +141,33 @@ export default function Home() {
<UserMenu />
</header>
<section className="flex-1 relative overflow-hidden">
{activeWorkspace.layout === null ? (
<div className="h-full w-full flex items-center justify-center">
<div className="flex flex-col items-center gap-8">
<pre className="font-mono text-xs leading-tight text-grimoire-gradient">
{` ★ ✦
: ☽
t#, ,;
✦ .Gt j. t ;##W. t j. f#i
j#W: EW, Ej .. : :#L:WE Ej EW, .E#t
;K#f E##j E#, ,W, .Et .KG ,#D E#, E##j i#W,
.G#D. E###D. E#t t##, ,W#t EE ;#f E#t E###D. L#D. ✦
j#K; E#jG#W; E#t L###, j###t f#. t#iE#t E#jG#W; :K#Wfff;
,K#f ,GD; E#t t##f E#t .E#j##, G#fE#t :#G GK E#t E#t t##f i##WLLLLt
☽ j#Wi E#t E#t :K#E: E#t ;WW; ##,:K#i E#t ;#L LW. E#t E#t :K#E: .E#L
.G#D: E#t E#KDDDD###iE#t j#E. ##f#W, E#t t#f f#: E#t E#KDDDD###i f#E: ★
,K#fK#t E#f,t#Wi,,,E#t .D#L ###K: E#t f#D#; E#t E#f,t#Wi,,, ,WW;
j###t E#t ;#W: E#t :K#t ##D. E#t G#t E#t E#t ;#W: .D#;
.G#t DWi ,KK: E#t ... #G .. t E#t DWi ,KK: tt
;; ,;. j ✦ ,;. ☆ `}
</pre>
<div className="flex flex-col items-center gap-3">
<p className="text-muted-foreground text-sm font-mono mb-2">
Press{" "}
<kbd className="px-2 py-1 bg-muted border border-border text-xs">
Cmd+K
</kbd>{" "}
or
</p>
<Button
onClick={() => setCommandLauncherOpen(true)}
variant="outline"
>
<span></span>
<span>Launch Command</span>
</Button>
</div>
</div>
</div>
) : (
<Mosaic
renderTile={renderTile}
value={activeWorkspace.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"
/>
)}
{Object.values(state.workspaces).map((workspace) => (
<Activity
key={workspace.id}
mode={
workspace.id === state.activeWorkspaceId ? "visible" : "hidden"
}
>
{workspace.layout === null ? (
<GrimoireWelcome
onLaunchCommand={() => setCommandLauncherOpen(true)}
/>
) : (
<Mosaic
renderTile={renderTile}
value={workspace.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"
/>
)}
</Activity>
))}
</section>
<TabBar />
</main>

View File

@@ -1,5 +1,3 @@
import type { TextNode } from "applesauce-content";
interface TextNodeProps {
node: {
type: "text";