From 7241b3fb5aaece382f0d7a0b92ed7b26fd65c1b8 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Tue, 23 Dec 2025 00:00:52 +0100 Subject: [PATCH] feat: add copy button for NIP markdown (#26) * feat: add copy button for NIP markdown - Add copy button to WindowToolbar for regular NIPs (appId: "nip") - Button appears in window toolbar next to edit button - Uses Copy/CopyCheck icons from lucide-react - Fetches NIP content via useNip hook - Shows toast notification on successful copy - Add copy button to CommunityNIPDetailRenderer for community NIPs (kind 30817) - Button appears in header next to title - Copies event.content (markdown) to clipboard - Uses same Copy/CopyCheck icon pattern - Shows toast notification on successful copy Both implementations use the existing useCopy hook for state management and maintain consistent styling with other toolbar buttons. * refactor: use Button component and remove misleading shortcuts - Replace native button elements with Button component from shadcn/ui - Use variant="ghost" and size="icon" for consistent styling - Apply h-8 w-8 classes for uniform button sizing - Remove manual className styling in favor of component variants - Remove misleading keyboard shortcut hints from button titles - Changed "Edit command (Cmd+E)" to "Edit command" - Changed "Close window (Cmd+W)" to "Close window" - These shortcuts don't actually work, so they were misleading - Clean up imports and formatting - Format lucide-react imports across multiple lines - Add Button component import - Run prettier for consistent code style * refactor: use link variant and remove size class overrides - Change all Button components from variant="ghost" to variant="link" - Remove className="h-8 w-8" overrides to use default Button sizing - Maintains size="icon" for proper icon button behavior - Applies to WindowToolbar and CommunityNIPDetailRenderer * style: add muted color to window toolbar icon buttons - Add className="text-muted-foreground" to all Button components in WindowToolbar - Improves visual contrast for toolbar buttons - Applies to Edit, Copy NIP, More actions, and Close window buttons --------- Co-authored-by: Claude --- src/components/WindowToolbar.tsx | 73 +++++++++++++++---- .../kinds/CommunityNIPDetailRenderer.tsx | 26 ++++++- 2 files changed, 84 insertions(+), 15 deletions(-) diff --git a/src/components/WindowToolbar.tsx b/src/components/WindowToolbar.tsx index aee30ac..a20d832 100644 --- a/src/components/WindowToolbar.tsx +++ b/src/components/WindowToolbar.tsx @@ -1,4 +1,11 @@ -import { X, Pencil, MoreVertical, WandSparkles } from "lucide-react"; +import { + X, + Pencil, + MoreVertical, + WandSparkles, + Copy, + CopyCheck, +} from "lucide-react"; import { useSetAtom } from "jotai"; import { useState } from "react"; import { WindowInstance } from "@/types/app"; @@ -10,9 +17,12 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { Button } from "@/components/ui/button"; import { SpellDialog } from "@/components/nostr/SpellDialog"; import { reconstructCommand as reconstructReqCommand } from "@/lib/spell-conversion"; import { toast } from "sonner"; +import { useCopy } from "@/hooks/useCopy"; +import { useNip } from "@/hooks/useNip"; interface WindowToolbarProps { window?: WindowInstance; @@ -58,6 +68,22 @@ export function WindowToolbar({ setShowSpellDialog(true); }; + // Copy functionality for NIPs + const { copy, copied } = useCopy(); + const isNipWindow = window?.appId === "nip"; + + // Fetch NIP content for regular NIPs + const { content: nipContent } = useNip( + isNipWindow && window?.props?.number ? window.props.number : "", + ); + + const handleCopyNip = () => { + if (!window || !nipContent) return; + + copy(nipContent); + toast.success("NIP markdown copied to clipboard"); + }; + // Check if this is a REQ window for spell creation const isReqWindow = window?.appId === "req"; @@ -78,27 +104,46 @@ export function WindowToolbar({ <> {window && ( <> - {/* Edit button with keyboard shortcut hint */} - + + + {/* Copy button for NIPs */} + {isNipWindow && ( + + )} {/* More actions menu - only for REQ windows for now */} {isReqWindow && ( - + @@ -124,14 +169,16 @@ export function WindowToolbar({ )} {onClose && ( - + )} ); diff --git a/src/components/nostr/kinds/CommunityNIPDetailRenderer.tsx b/src/components/nostr/kinds/CommunityNIPDetailRenderer.tsx index 1a7c736..036ce81 100644 --- a/src/components/nostr/kinds/CommunityNIPDetailRenderer.tsx +++ b/src/components/nostr/kinds/CommunityNIPDetailRenderer.tsx @@ -1,7 +1,11 @@ import { useMemo } from "react"; +import { Copy, CopyCheck } from "lucide-react"; import { getTagValue } from "applesauce-core/helpers"; import { UserName } from "../UserName"; import { MarkdownContent } from "../MarkdownContent"; +import { Button } from "@/components/ui/button"; +import { useCopy } from "@/hooks/useCopy"; +import { toast } from "sonner"; import type { NostrEvent } from "@/types/nostr"; /** @@ -29,12 +33,30 @@ export function CommunityNIPDetailRenderer({ event }: { event: NostrEvent }) { }, ); + // Copy functionality + const { copy, copied } = useCopy(); + const handleCopy = () => { + copy(event.content); + toast.success("Community NIP markdown copied to clipboard"); + }; + return (
{/* NIP Header */}
- {/* Title */} -

{title}

+ {/* Title with Copy Button */} +
+

{title}

+ +
{/* Metadata */}