mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-10 23:47:12 +02:00
feat: clickable spell and spellbook links
This commit is contained in:
@@ -63,6 +63,7 @@ function SpellbookCard({
|
||||
showAuthor = false,
|
||||
isOwner = true,
|
||||
}: SpellbookCardProps) {
|
||||
const { addWindow } = useGrimoire();
|
||||
const [isPublishing, setIsPublishing] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const displayName = spellbook.title || "Untitled Spellbook";
|
||||
@@ -108,6 +109,26 @@ function SpellbookCard({
|
||||
onApply(parsed);
|
||||
};
|
||||
|
||||
const handleOpenEvent = () => {
|
||||
const id = spellbook.eventId || (spellbook.event?.id as string);
|
||||
if (id && id.length === 64) {
|
||||
addWindow("open", { pointer: { id } }, `open ${id}`);
|
||||
} else if (spellbook.isPublished && spellbook.slug && authorPubkey) {
|
||||
// For addressable events (kind 30003)
|
||||
addWindow(
|
||||
"open",
|
||||
{
|
||||
pointer: {
|
||||
kind: SPELLBOOK_KIND,
|
||||
pubkey: authorPubkey,
|
||||
identifier: spellbook.slug,
|
||||
},
|
||||
},
|
||||
`open ${SPELLBOOK_KIND}:${authorPubkey}:${spellbook.slug}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
@@ -119,7 +140,19 @@ function SpellbookCard({
|
||||
<div className="flex items-center flex-wrap justify-between gap-2">
|
||||
<div className="flex items-center gap-2 flex-1 overflow-hidden">
|
||||
<BookHeart className="size-4 flex-shrink-0 text-muted-foreground mt-0.5" />
|
||||
<CardTitle className="text-xl truncate" title={displayName}>
|
||||
<CardTitle
|
||||
className={cn(
|
||||
"text-xl truncate",
|
||||
(spellbook.eventId || spellbook.isPublished) &&
|
||||
"cursor-pointer hover:underline text-primary",
|
||||
)}
|
||||
title={displayName}
|
||||
onClick={
|
||||
spellbook.eventId || spellbook.isPublished
|
||||
? handleOpenEvent
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{displayName}
|
||||
</CardTitle>
|
||||
</div>
|
||||
|
||||
@@ -47,6 +47,7 @@ interface SpellCardProps {
|
||||
}
|
||||
|
||||
function SpellCard({ spell, onDelete, onPublish }: SpellCardProps) {
|
||||
const { addWindow } = useGrimoire();
|
||||
const [isPublishing, setIsPublishing] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const displayName = spell.name || spell.alias || "Untitled Spell";
|
||||
@@ -80,6 +81,13 @@ function SpellCard({ spell, onDelete, onPublish }: SpellCardProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpenEvent = () => {
|
||||
const id = spell.eventId || (spell.event?.id as string);
|
||||
if (id && id.length === 64) {
|
||||
addWindow("open", { pointer: { id } }, `open ${id}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
@@ -91,7 +99,17 @@ function SpellCard({ spell, onDelete, onPublish }: SpellCardProps) {
|
||||
<div className="flex items-center flex-wrap justify-between gap-2">
|
||||
<div className="flex items-center gap-2 flex-1 overflow-hidden">
|
||||
<WandSparkles className="size-4 flex-shrink-0 text-muted-foreground mt-0.5" />
|
||||
<CardTitle className="text-xl truncate" title={displayName}>
|
||||
<CardTitle
|
||||
className={cn(
|
||||
"text-xl truncate",
|
||||
(spell.eventId || spell.event) &&
|
||||
"cursor-pointer hover:underline text-primary",
|
||||
)}
|
||||
title={displayName}
|
||||
onClick={
|
||||
spell.eventId || spell.event ? handleOpenEvent : undefined
|
||||
}
|
||||
>
|
||||
{displayName}
|
||||
</CardTitle>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, ReactNode } from "react";
|
||||
import { Terminal } from "lucide-react";
|
||||
import { useAccountSync } from "@/hooks/useAccountSync";
|
||||
import { useRelayListCacheSync } from "@/hooks/useRelayListCacheSync";
|
||||
import { useRelayState } from "@/hooks/useRelayState";
|
||||
@@ -59,10 +60,12 @@ export function AppShell({ children }: AppShellProps) {
|
||||
<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"
|
||||
className="p-1.5 text-muted-foreground hover:text-accent transition-colors cursor-crosshair flex items-center gap-2"
|
||||
title="Launch command (Cmd+K)"
|
||||
aria-label="Launch command palette"
|
||||
></button>
|
||||
>
|
||||
<Terminal className="size-4" />
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<SpellbookDropdown />
|
||||
|
||||
Reference in New Issue
Block a user