fix: improved balanced window algo

This commit is contained in:
Alejandro Gómez
2025-12-18 15:45:34 +01:00
parent 3fba62b316
commit 5cc97484b5
8 changed files with 341 additions and 181 deletions

View File

@@ -9,6 +9,7 @@ import {
parseTagStructure,
getContentTypeDescription,
} from "@/lib/nostr-schema";
import { CenteredContent } from "./ui/CenteredContent";
// NIP-01 Kind ranges
const REPLACEABLE_START = 10000;
@@ -44,7 +45,7 @@ export default function KindRenderer({ kind }: { kind: number }) {
}
return (
<div className="h-full w-full overflow-y-auto p-6 space-y-6">
<CenteredContent>
{/* Header */}
<div className="flex items-center gap-4">
{Icon && (
@@ -180,7 +181,7 @@ export default function KindRenderer({ kind }: { kind: number }) {
</div>
</>
)}
</div>
</CenteredContent>
);
}

View File

@@ -1,6 +1,7 @@
import { getKindInfo } from "@/constants/kinds";
import { kindRenderers } from "./nostr/kinds";
import { NIPBadge } from "./NIPBadge";
import { CenteredContent } from "./ui/CenteredContent";
// Dynamically derive supported kinds from renderer registry
const SUPPORTED_KINDS = Object.keys(kindRenderers).map(Number);
@@ -14,10 +15,9 @@ export default function KindsViewer() {
const sortedKinds = [...SUPPORTED_KINDS].sort((a, b) => a - b);
return (
<div className="h-full w-full overflow-y-auto p-6">
<div className="max-w-3xl mx-auto space-y-6">
{/* Header */}
<div>
<CenteredContent>
{/* Header */}
<div>
<h1 className="text-2xl font-bold mb-2">
Supported Event Kinds ({sortedKinds.length})
</h1>
@@ -70,7 +70,6 @@ export default function KindsViewer() {
);
})}
</div>
</div>
</div>
</CenteredContent>
);
}

View File

@@ -1,5 +1,6 @@
import { manPages } from "@/types/man";
import { useGrimoire } from "@/core/state";
import { CenteredContent } from "./ui/CenteredContent";
interface ManPageProps {
cmd: string;
@@ -48,17 +49,17 @@ export default function ManPage({ cmd }: ManPageProps) {
if (!page) {
return (
<div className="p-6 font-mono text-sm">
<CenteredContent maxWidth="4xl" spacing="4" className="font-mono text-sm">
<div className="text-destructive">No manual entry for {cmd}</div>
<div className="mt-4 text-muted-foreground">
Use 'help' to see available commands.
</div>
</div>
</CenteredContent>
);
}
return (
<div className="p-6 font-mono text-sm space-y-4 max-w-4xl">
<CenteredContent maxWidth="4xl" spacing="4" className="font-mono text-sm">
{/* Header */}
<div className="flex justify-between border-b border-border pb-2">
<span className="font-bold">{page.name.toUpperCase()}</span>
@@ -161,6 +162,6 @@ export default function ManPage({ cmd }: ManPageProps) {
<div className="border-t border-border pt-2 text-muted-foreground text-xs">
Grimoire 1.0.0 {new Date().getFullYear()}
</div>
</div>
</CenteredContent>
);
}

View File

@@ -2,6 +2,8 @@ import { useNip } from "@/hooks/useNip";
import { MarkdownContent } from "./nostr/MarkdownContent";
import { KindBadge } from "./KindBadge";
import { getKindsForNip } from "@/lib/nip-kinds";
import { CenteredContent } from "./ui/CenteredContent";
import { cn } from "@/lib/utils";
interface NipRendererProps {
nipId: string;
@@ -14,21 +16,17 @@ export function NipRenderer({ nipId, className = "" }: NipRendererProps) {
if (loading) {
return (
<div className={`p-4 ${className}`}>
<div className="text-muted-foreground text-sm">
Loading NIP-{nipId}...
</div>
</div>
<CenteredContent className={cn("text-muted-foreground text-sm", className)}>
Loading NIP-{nipId}...
</CenteredContent>
);
}
if (error) {
return (
<div className={`p-4 ${className}`}>
<div className="text-destructive text-sm">
Error loading NIP-{nipId}: {error.message}
</div>
</div>
<CenteredContent className={cn("text-destructive text-sm", className)}>
Error loading NIP-{nipId}: {error.message}
</CenteredContent>
);
}
@@ -37,7 +35,7 @@ export function NipRenderer({ nipId, className = "" }: NipRendererProps) {
}
return (
<div className={`p-4 overflow-x-hidden ${className}`}>
<CenteredContent className={cn("overflow-x-hidden", className)}>
<MarkdownContent content={content} />
{kinds.length > 0 && (
@@ -52,6 +50,6 @@ export function NipRenderer({ nipId, className = "" }: NipRendererProps) {
</div>
</div>
)}
</div>
</CenteredContent>
);
}

View File

@@ -5,6 +5,7 @@ import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { NIPBadge } from "./NIPBadge";
import { useGrimoire } from "@/core/state";
import { CenteredContent } from "./ui/CenteredContent";
/**
* NipsViewer - Documentation introspection command
@@ -64,10 +65,9 @@ export default function NipsViewer() {
};
return (
<div className="h-full w-full overflow-y-auto p-6">
<div className="max-w-3xl mx-auto space-y-6">
{/* Header */}
<div>
<CenteredContent>
{/* Header */}
<div>
<h1 className="text-2xl font-bold mb-2">
{search
? `Showing ${filteredNips.length} of ${sortedNips.length} NIPs`
@@ -122,7 +122,6 @@ export default function NipsViewer() {
<p className="text-sm">Try searching for a different term</p>
</div>
)}
</div>
</div>
</CenteredContent>
);
}

View File

@@ -0,0 +1,108 @@
import { ReactNode } from "react";
import { cn } from "@/lib/utils";
interface CenteredContentProps {
children: ReactNode;
/**
* Maximum width of the centered content
* @default '3xl' (48rem / 768px)
*/
maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl" | "6xl" | "full";
/**
* Vertical spacing between child elements
* @default '6' (1.5rem)
*/
spacing?: "0" | "1" | "2" | "3" | "4" | "5" | "6" | "8" | "10" | "12";
/**
* Padding around the content
* @default '6' (1.5rem)
*/
padding?: "0" | "2" | "4" | "6" | "8";
/**
* Additional CSS classes for the inner content container
*/
className?: string;
}
const maxWidthClasses = {
sm: "max-w-sm", // 24rem / 384px
md: "max-w-md", // 28rem / 448px
lg: "max-w-lg", // 32rem / 512px
xl: "max-w-xl", // 36rem / 576px
"2xl": "max-w-2xl", // 42rem / 672px
"3xl": "max-w-3xl", // 48rem / 768px - DEFAULT
"4xl": "max-w-4xl", // 56rem / 896px
"5xl": "max-w-5xl", // 64rem / 1024px
"6xl": "max-w-6xl", // 72rem / 1152px
full: "max-w-full", // No limit
} as const;
const spacingClasses = {
"0": "space-y-0",
"1": "space-y-1",
"2": "space-y-2",
"3": "space-y-3",
"4": "space-y-4",
"5": "space-y-5",
"6": "space-y-6", // DEFAULT
"8": "space-y-8",
"10": "space-y-10",
"12": "space-y-12",
} as const;
const paddingClasses = {
"0": "p-0",
"2": "p-2",
"4": "p-4",
"6": "p-6", // DEFAULT
"8": "p-8",
} as const;
/**
* CenteredContent - Reusable container for centered, max-width content
*
* Provides consistent layout pattern for documentation-style pages:
* - Centered content with configurable max-width
* - Consistent padding and spacing
* - Works with WindowRenderer's scroll container
*
* @example
* // Default (3xl width, 6 spacing, 6 padding)
* <CenteredContent>
* {content}
* </CenteredContent>
*
* @example
* // Man pages (wider, tighter spacing)
* <CenteredContent maxWidth="4xl" spacing="4" className="font-mono text-sm">
* {content}
* </CenteredContent>
*
* @example
* // No padding (rare)
* <CenteredContent padding="0">
* {content}
* </CenteredContent>
*/
export function CenteredContent({
children,
maxWidth = "3xl",
spacing = "6",
padding = "6",
className,
}: CenteredContentProps) {
return (
<div className={paddingClasses[padding]}>
<div
className={cn(
"mx-auto",
maxWidthClasses[maxWidth],
spacingClasses[spacing],
className
)}
>
{children}
</div>
</div>
);
}