mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-09 15:07:10 +02:00
feat: window title tooltips with raw cmd
This commit is contained in:
@@ -4,9 +4,13 @@ import { useProfile } from "@/hooks/useProfile";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { getKindName, getKindIcon } from "@/constants/kinds";
|
||||
import { getNipTitle } from "@/constants/nips";
|
||||
import { getCommandIcon, getCommandDescription } from "@/constants/command-icons";
|
||||
import {
|
||||
getCommandIcon,
|
||||
getCommandDescription,
|
||||
} from "@/constants/command-icons";
|
||||
import type { EventPointer, AddressPointer } from "nostr-tools/nip19";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import { nip19 } from "nostr-tools";
|
||||
|
||||
export interface WindowTitleData {
|
||||
title: string;
|
||||
@@ -14,6 +18,85 @@ export interface WindowTitleData {
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate raw command string from window appId and props
|
||||
*/
|
||||
function generateRawCommand(appId: string, props: any): string {
|
||||
switch (appId) {
|
||||
case "profile":
|
||||
if (props.pubkey) {
|
||||
try {
|
||||
const npub = nip19.npubEncode(props.pubkey);
|
||||
return `profile ${npub}`;
|
||||
} catch {
|
||||
return `profile ${props.pubkey.slice(0, 16)}...`;
|
||||
}
|
||||
}
|
||||
return "profile";
|
||||
|
||||
case "kind":
|
||||
return props.number ? `kind ${props.number}` : "kind";
|
||||
|
||||
case "nip":
|
||||
return props.number ? `nip ${props.number}` : "nip";
|
||||
|
||||
case "relay":
|
||||
return props.url ? `relay ${props.url}` : "relay";
|
||||
|
||||
case "open":
|
||||
if (props.pointer) {
|
||||
try {
|
||||
if ("id" in props.pointer) {
|
||||
const nevent = nip19.neventEncode({ id: props.pointer.id });
|
||||
return `open ${nevent}`;
|
||||
} else if ("kind" in props.pointer && "pubkey" in props.pointer) {
|
||||
const naddr = nip19.naddrEncode({
|
||||
kind: props.pointer.kind,
|
||||
pubkey: props.pointer.pubkey,
|
||||
identifier: props.pointer.identifier || "",
|
||||
});
|
||||
return `open ${naddr}`;
|
||||
}
|
||||
} catch {
|
||||
// Fallback to shortened ID
|
||||
}
|
||||
}
|
||||
return "open";
|
||||
|
||||
case "encode":
|
||||
if (props.args && props.args[0]) {
|
||||
return `encode ${props.args[0]}`;
|
||||
}
|
||||
return "encode";
|
||||
|
||||
case "decode":
|
||||
if (props.args && props.args[0]) {
|
||||
return `decode ${props.args[0]}`;
|
||||
}
|
||||
return "decode";
|
||||
|
||||
case "req":
|
||||
// REQ command can be complex, show simplified version
|
||||
if (props.filter) {
|
||||
const parts: string[] = ["req"];
|
||||
if (props.filter.kinds?.length) {
|
||||
parts.push(`-k ${props.filter.kinds.join(",")}`);
|
||||
}
|
||||
if (props.filter.authors?.length) {
|
||||
parts.push(`-a ${props.filter.authors.slice(0, 2).join(",")}`);
|
||||
}
|
||||
return parts.join(" ");
|
||||
}
|
||||
return "req";
|
||||
|
||||
case "man":
|
||||
return props.cmd ? `man ${props.cmd}` : "man";
|
||||
|
||||
default:
|
||||
return appId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* useDynamicWindowTitle - Hook to generate dynamic window titles based on loaded data
|
||||
* Similar to WindowRenderer but for titles instead of content
|
||||
@@ -60,9 +143,7 @@ function useDynamicTitle(window: WindowInstance): WindowTitleData {
|
||||
if (event.kind === 30023) {
|
||||
const titleTag = event.tags.find((t) => t[0] === "title")?.[1];
|
||||
if (titleTag) {
|
||||
return titleTag.length > 50
|
||||
? `${titleTag.slice(0, 50)}...`
|
||||
: titleTag;
|
||||
return titleTag.length > 50 ? `${titleTag.slice(0, 50)}...` : titleTag;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,12 +188,16 @@ function useDynamicTitle(window: WindowInstance): WindowTitleData {
|
||||
if (kindNames.length <= 3) {
|
||||
parts.push(kindNames.join(", "));
|
||||
} else {
|
||||
parts.push(`${kindNames.slice(0, 3).join(", ")}, +${kindNames.length - 3}`);
|
||||
parts.push(
|
||||
`${kindNames.slice(0, 3).join(", ")}, +${kindNames.length - 3}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.authors && filter.authors.length > 0) {
|
||||
parts.push(`${filter.authors.length} author${filter.authors.length > 1 ? "s" : ""}`);
|
||||
parts.push(
|
||||
`${filter.authors.length} author${filter.authors.length > 1 ? "s" : ""}`,
|
||||
);
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts.join(" • ") : "REQ";
|
||||
@@ -132,7 +217,9 @@ function useDynamicTitle(window: WindowInstance): WindowTitleData {
|
||||
if (appId !== "decode") return null;
|
||||
const { args } = props;
|
||||
if (args && args[0]) {
|
||||
const prefix = args[0].match(/^(npub|nprofile|note|nevent|naddr|nsec)/i)?.[1];
|
||||
const prefix = args[0].match(
|
||||
/^(npub|nprofile|note|nevent|naddr|nsec)/i,
|
||||
)?.[1];
|
||||
if (prefix) {
|
||||
return `DECODE ${prefix.toUpperCase()}`;
|
||||
}
|
||||
@@ -185,70 +272,72 @@ function useDynamicTitle(window: WindowInstance): WindowTitleData {
|
||||
let icon: LucideIcon | undefined;
|
||||
let tooltip: string | undefined;
|
||||
|
||||
// Generate raw command for tooltip
|
||||
const rawCommand = generateRawCommand(appId, props);
|
||||
|
||||
// Priority order for title selection
|
||||
if (profileTitle) {
|
||||
title = profileTitle;
|
||||
icon = getCommandIcon("profile");
|
||||
tooltip = `profile: ${getCommandDescription("profile")}`;
|
||||
tooltip = rawCommand;
|
||||
} else if (eventTitle && appId === "open") {
|
||||
title = eventTitle;
|
||||
// Use the event's kind icon if we have the event loaded
|
||||
if (event) {
|
||||
icon = getKindIcon(event.kind);
|
||||
const kindName = getKindName(event.kind);
|
||||
tooltip = `${kindName} (kind ${event.kind})`;
|
||||
} else {
|
||||
icon = getCommandIcon("open");
|
||||
tooltip = `open: ${getCommandDescription("open")}`;
|
||||
}
|
||||
tooltip = rawCommand;
|
||||
} else if (kindTitle && appId === "kind") {
|
||||
title = kindTitle;
|
||||
const kindNum = parseInt(props.number);
|
||||
icon = getKindIcon(kindNum);
|
||||
tooltip = `kind: ${getCommandDescription("kind")}`;
|
||||
tooltip = rawCommand;
|
||||
} else if (relayTitle) {
|
||||
title = relayTitle;
|
||||
icon = getCommandIcon("relay");
|
||||
tooltip = `relay: ${getCommandDescription("relay")}`;
|
||||
tooltip = rawCommand;
|
||||
} else if (reqTitle) {
|
||||
title = reqTitle;
|
||||
icon = getCommandIcon("req");
|
||||
tooltip = `req: ${getCommandDescription("req")}`;
|
||||
tooltip = rawCommand;
|
||||
} else if (encodeTitle) {
|
||||
title = encodeTitle;
|
||||
icon = getCommandIcon("encode");
|
||||
tooltip = `encode: ${getCommandDescription("encode")}`;
|
||||
tooltip = rawCommand;
|
||||
} else if (decodeTitle) {
|
||||
title = decodeTitle;
|
||||
icon = getCommandIcon("decode");
|
||||
tooltip = `decode: ${getCommandDescription("decode")}`;
|
||||
tooltip = rawCommand;
|
||||
} else if (nipTitle) {
|
||||
title = nipTitle;
|
||||
icon = getCommandIcon("nip");
|
||||
tooltip = `nip: ${getCommandDescription("nip")}`;
|
||||
tooltip = rawCommand;
|
||||
} else if (manTitle) {
|
||||
title = manTitle;
|
||||
// Use the specific command's icon, not the generic "man" icon
|
||||
icon = getCommandIcon(props.cmd);
|
||||
tooltip = `${props.cmd}: ${getCommandDescription(props.cmd)}`;
|
||||
tooltip = rawCommand;
|
||||
} else if (feedTitle) {
|
||||
title = feedTitle;
|
||||
icon = getCommandIcon("feed");
|
||||
tooltip = `feed: ${getCommandDescription("feed")}`;
|
||||
tooltip = rawCommand;
|
||||
} else if (winTitle) {
|
||||
title = winTitle;
|
||||
icon = getCommandIcon("win");
|
||||
tooltip = `win: ${getCommandDescription("win")}`;
|
||||
tooltip = rawCommand;
|
||||
} else if (kindsTitle) {
|
||||
title = kindsTitle;
|
||||
icon = getCommandIcon("kinds");
|
||||
tooltip = `kinds: ${getCommandDescription("kinds")}`;
|
||||
tooltip = rawCommand;
|
||||
} else if (debugTitle) {
|
||||
title = debugTitle;
|
||||
icon = getCommandIcon("debug");
|
||||
tooltip = `debug: ${getCommandDescription("debug")}`;
|
||||
tooltip = rawCommand;
|
||||
} else {
|
||||
title = staticTitle;
|
||||
tooltip = rawCommand;
|
||||
}
|
||||
|
||||
return { title, icon, tooltip };
|
||||
|
||||
@@ -25,7 +25,9 @@ export function WindowTile({ id, window, path, onClose }: WindowTileProps) {
|
||||
<Icon className="size-4 text-muted-foreground" />
|
||||
</span>
|
||||
)}
|
||||
<span className="truncate">{title}</span>
|
||||
<span className="truncate" title={tooltip}>
|
||||
{title}
|
||||
</span>
|
||||
</div>
|
||||
<WindowToolbar onClose={() => onClose(id)} />
|
||||
</div>
|
||||
@@ -33,11 +35,7 @@ export function WindowTile({ id, window, path, onClose }: WindowTileProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<MosaicWindow
|
||||
path={path}
|
||||
title={title}
|
||||
renderToolbar={renderToolbar}
|
||||
>
|
||||
<MosaicWindow path={path} title={title} renderToolbar={renderToolbar}>
|
||||
<WindowRenderer window={window} onClose={() => onClose(id)} />
|
||||
</MosaicWindow>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user