From 82090541edf98babf76abe7589f2813aab1f1c60 Mon Sep 17 00:00:00 2001 From: Luke Hagar Date: Thu, 18 Sep 2025 02:57:02 +0000 Subject: [PATCH] feat: add react-syntax-highlighter and update JSDoc cheatsheet layout Integrate react-syntax-highlighter for improved code example rendering and enhance the JSDoc cheatsheet layout with a new navigation structure and quick reference section. --- app/advanced/page.tsx | 103 +++ app/basic-tags/page.tsx | 104 +++ app/best-practices/page.tsx | 103 +++ app/documentation/page.tsx | 103 +++ app/examples/page.tsx | 103 +++ app/functions/page.tsx | 102 +++ app/modules/page.tsx | 103 +++ app/objects/page.tsx | 102 +++ app/page.tsx | 1031 ++++-------------------------- app/parameters/page.tsx | 103 +++ app/type-definitions/page.tsx | 102 +++ components/Content.tsx | 139 ++++ components/Header.tsx | 19 + components/Sidebar.tsx | 87 +++ components/SyntaxHighlighter.tsx | 46 ++ components/TableOfContents.tsx | 148 +++++ lib/data-loader.ts | 15 + lib/jsdoc-data.ts | 632 ++++++++++++++++++ original-page.tsx | 980 ++++++++++++++++++++++++++++ package.json | 2 + pnpm-lock.yaml | 181 ++++++ pnpm-workspace.yaml | 2 + 22 files changed, 3421 insertions(+), 889 deletions(-) create mode 100644 app/advanced/page.tsx create mode 100644 app/basic-tags/page.tsx create mode 100644 app/best-practices/page.tsx create mode 100644 app/documentation/page.tsx create mode 100644 app/examples/page.tsx create mode 100644 app/functions/page.tsx create mode 100644 app/modules/page.tsx create mode 100644 app/objects/page.tsx create mode 100644 app/parameters/page.tsx create mode 100644 app/type-definitions/page.tsx create mode 100644 components/Content.tsx create mode 100644 components/Header.tsx create mode 100644 components/Sidebar.tsx create mode 100644 components/SyntaxHighlighter.tsx create mode 100644 components/TableOfContents.tsx create mode 100644 lib/data-loader.ts create mode 100644 lib/jsdoc-data.ts create mode 100644 original-page.tsx create mode 100644 pnpm-workspace.yaml diff --git a/app/advanced/page.tsx b/app/advanced/page.tsx new file mode 100644 index 0000000..0e54b72 --- /dev/null +++ b/app/advanced/page.tsx @@ -0,0 +1,103 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import { BookOpen } from "lucide-react" +import CodeHighlighter from "@/components/SyntaxHighlighter" +import Sidebar from "@/components/Sidebar" +import TableOfContents from "@/components/TableOfContents" +import { jsdocData } from "@/lib/jsdoc-data" +import Header from "@/components/Header" + +const advancedData = jsdocData.advanced + +export default function AdvancedPage() { + // Generate TOC items from the data + const tocItems = advancedData.items.map((item, index) => ({ + id: item.tag.replace("@", "").replace(/[^a-zA-Z0-9]/g, "-"), + title: item.tag, + level: 1 + })) + + return ( +
+
+ + + +
+
+ {/* Section Header */} +
+

+ {advancedData.title} +

+

+ {advancedData.description} +

+
+ + {/* Content Cards */} +
+ {advancedData.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+
+
+ ))} +
+
+
+ + {/* Footer */} + +
+ ) +} diff --git a/app/basic-tags/page.tsx b/app/basic-tags/page.tsx new file mode 100644 index 0000000..b07b0cd --- /dev/null +++ b/app/basic-tags/page.tsx @@ -0,0 +1,104 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import { Tag } from "lucide-react" +import CodeHighlighter from "@/components/SyntaxHighlighter" +import Navigation from "@/components/Navigation" +import Header from "@/components/Header" +import Sidebar from "@/components/Sidebar" +import TableOfContents from "@/components/TableOfContents" +import { jsdocData } from "@/lib/jsdoc-data" + +const basicTagsData = jsdocData.basic + +export default function BasicTagsPage() { + // Generate TOC items from the data + const tocItems = basicTagsData.items.map((item, index) => ({ + id: item.tag.replace("@", "").replace(/[^a-zA-Z0-9]/g, "-"), + title: item.tag, + level: 1 + })) + + return ( +
+
+ + + +
+
+ {/* Section Header */} +
+

+ {basicTagsData.title} +

+

+ {basicTagsData.description} +

+
+ + {/* Content Cards */} +
+ {basicTagsData.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+
+
+ ))} +
+
+
+ + {/* Footer */} + +
+ ) +} diff --git a/app/best-practices/page.tsx b/app/best-practices/page.tsx new file mode 100644 index 0000000..829625f --- /dev/null +++ b/app/best-practices/page.tsx @@ -0,0 +1,103 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import { Users } from "lucide-react" +import CodeHighlighter from "@/components/SyntaxHighlighter" +import Header from "@/components/Header" +import Sidebar from "@/components/Sidebar" +import TableOfContents from "@/components/TableOfContents" +import { jsdocData } from "@/lib/jsdoc-data" + +const bestPracticesData = jsdocData["best-practices"] + +export default function BestPracticesPage() { + // Generate TOC items from the data + const tocItems = bestPracticesData.items.map((item, index) => ({ + id: item.tag.replace(/[^a-zA-Z0-9]/g, "-").toLowerCase(), + title: item.tag, + level: 1 + })) + + return ( +
+
+ + + +
+
+ {/* Section Header */} +
+

+ {bestPracticesData.title} +

+

+ {bestPracticesData.description} +

+
+ + {/* Content Cards */} +
+ {bestPracticesData.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+
+
+ ))} +
+
+
+ + {/* Footer */} + +
+ ) +} diff --git a/app/documentation/page.tsx b/app/documentation/page.tsx new file mode 100644 index 0000000..c903eed --- /dev/null +++ b/app/documentation/page.tsx @@ -0,0 +1,103 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import { Info } from "lucide-react" +import CodeHighlighter from "@/components/SyntaxHighlighter" +import Header from "@/components/Header" +import Sidebar from "@/components/Sidebar" +import TableOfContents from "@/components/TableOfContents" +import { jsdocData } from "@/lib/jsdoc-data" + +const documentationData = jsdocData.documentation + +export default function DocumentationPage() { + // Generate TOC items from the data + const tocItems = documentationData.items.map((item, index) => ({ + id: item.tag.replace("@", "").replace(/[^a-zA-Z0-9]/g, "-"), + title: item.tag, + level: 1 + })) + + return ( +
+
+ + + +
+
+ {/* Section Header */} +
+

+ {documentationData.title} +

+

+ {documentationData.description} +

+
+ + {/* Content Cards */} +
+ {documentationData.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+
+
+ ))} +
+
+
+ + {/* Footer */} + +
+ ) +} diff --git a/app/examples/page.tsx b/app/examples/page.tsx new file mode 100644 index 0000000..d71e500 --- /dev/null +++ b/app/examples/page.tsx @@ -0,0 +1,103 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import { Info } from "lucide-react" +import CodeHighlighter from "@/components/SyntaxHighlighter" +import Header from "@/components/Header" +import Sidebar from "@/components/Sidebar" +import TableOfContents from "@/components/TableOfContents" +import { jsdocData } from "@/lib/jsdoc-data" + +const examplesData = jsdocData.examples + +export default function ExamplesPage() { + // Generate TOC items from the data + const tocItems = examplesData.items.map((item, index) => ({ + id: item.tag.replace(/[^a-zA-Z0-9]/g, "-").toLowerCase(), + title: item.tag, + level: 1 + })) + + return ( +
+
+ + + +
+
+ {/* Section Header */} +
+

+ {examplesData.title} +

+

+ {examplesData.description} +

+
+ + {/* Content Cards */} +
+ {examplesData.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+
+
+ ))} +
+
+
+ + {/* Footer */} + +
+ ) +} diff --git a/app/functions/page.tsx b/app/functions/page.tsx new file mode 100644 index 0000000..0c17894 --- /dev/null +++ b/app/functions/page.tsx @@ -0,0 +1,102 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import CodeHighlighter from "@/components/SyntaxHighlighter" +import Header from "@/components/Header" +import Sidebar from "@/components/Sidebar" +import TableOfContents from "@/components/TableOfContents" +import { jsdocData } from "@/lib/jsdoc-data" + +const functionsData = jsdocData.functions + +export default function FunctionsPage() { + // Generate TOC items from the data + const tocItems = functionsData.items.map((item, index) => ({ + id: item.tag.replace("@", "").replace(/[^a-zA-Z0-9]/g, "-"), + title: item.tag, + level: 1 + })) + + return ( +
+
+ + + +
+
+ {/* Section Header */} +
+

+ {functionsData.title} +

+

+ {functionsData.description} +

+
+ + {/* Content Cards */} +
+ {functionsData.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+
+
+ ))} +
+
+
+ + {/* Footer */} + +
+ ) +} diff --git a/app/modules/page.tsx b/app/modules/page.tsx new file mode 100644 index 0000000..ebe4892 --- /dev/null +++ b/app/modules/page.tsx @@ -0,0 +1,103 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import { FileText } from "lucide-react" +import CodeHighlighter from "@/components/SyntaxHighlighter" +import Header from "@/components/Header" +import Sidebar from "@/components/Sidebar" +import TableOfContents from "@/components/TableOfContents" +import { jsdocData } from "@/lib/jsdoc-data" + +const modulesData = jsdocData.modules + +export default function ModulesPage() { + // Generate TOC items from the data + const tocItems = modulesData.items.map((item, index) => ({ + id: item.tag.replace("@", "").replace(/[^a-zA-Z0-9]/g, "-"), + title: item.tag, + level: 1 + })) + + return ( +
+
+ + + +
+
+ {/* Section Header */} +
+

+ {modulesData.title} +

+

+ {modulesData.description} +

+
+ + {/* Content Cards */} +
+ {modulesData.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+
+
+ ))} +
+
+
+ + {/* Footer */} + +
+ ) +} diff --git a/app/objects/page.tsx b/app/objects/page.tsx new file mode 100644 index 0000000..a28b4cd --- /dev/null +++ b/app/objects/page.tsx @@ -0,0 +1,102 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import CodeHighlighter from "@/components/SyntaxHighlighter" +import Header from "@/components/Header" +import Sidebar from "@/components/Sidebar" +import TableOfContents from "@/components/TableOfContents" +import { jsdocData } from "@/lib/jsdoc-data" + +const objectsData = jsdocData.objects + +export default function ObjectsPage() { + // Generate TOC items from the data + const tocItems = objectsData.items.map((item, index) => ({ + id: item.tag.replace("@", "").replace(/[^a-zA-Z0-9]/g, "-"), + title: item.tag, + level: 1 + })) + + return ( +
+
+ + + +
+
+ {/* Section Header */} +
+

+ {objectsData.title} +

+

+ {objectsData.description} +

+
+ + {/* Content Cards */} +
+ {objectsData.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+
+
+ ))} +
+
+
+ + {/* Footer */} + +
+ ) +} diff --git a/app/page.tsx b/app/page.tsx index 6ac031f..6843867 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,958 +1,211 @@ "use client" -import { useState, useEffect } from "react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" -import { ScrollArea } from "@/components/ui/scroll-area" -import { Separator } from "@/components/ui/separator" -import { BookOpen, Code, FileText, Hash, Info, Settings, Tag, Users, ChevronRight } from "lucide-react" +import { BookOpen, Code, FileText, Hash, Settings, Tag, ArrowRight, Info, Users } from "lucide-react" +import CodeHighlighter from "@/components/SyntaxHighlighter" +import Header from "@/components/Header" +import Sidebar from "@/components/Sidebar" +import TableOfContents from "@/components/TableOfContents" +import Link from "next/link" +import { jsdocData } from "@/lib/jsdoc-data" -const SyntaxHighlighter = ({ code, language = "javascript" }: { code: string; language?: string }) => { - const highlightCode = (text: string) => { - // Escape HTML first - let highlighted = text.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """) - - // Apply syntax highlighting with proper HTML structure - highlighted = highlighted - // JSDoc comments and tags - .replace(/(\/\*\*[\s\S]*?\*\/)/g, '$1') - .replace(/(@\w+)/g, '$1') - - // JavaScript keywords - .replace( - /\b(function|class|const|let|var|if|else|for|while|return|async|await|new|this|super|extends|implements|static|public|private|protected)\b/g, - '$1', - ) - - // Strings - .replace(/(["'`])((?:\\.|(?!\1)[^\\])*?)\1/g, '$1$2$1') - - // Numbers - .replace(/\b(\d+\.?\d*)\b/g, '$1') - - // Booleans and null/undefined - .replace(/\b(true|false|null|undefined)\b/g, '$1') - - // Types in curly braces - .replace(/\{([^}]+)\}/g, '{$1}') - - return highlighted - } - - return ( -
-      
-    
- ) -} - -const sections = [ +const navigationSections = [ { - id: "kitchen-sink", - title: "Kitchen Sink Example", - icon: BookOpen, - subsections: ["Complete Example"], - }, - { - id: "basic", + id: "basic-tags", title: "Basic Tags", + description: "Essential JSDoc tags for documenting your code", icon: Tag, - subsections: ["@description", "@author", "@version", "@since", "@deprecated"], + href: "/basic-tags", }, { id: "parameters", title: "Parameters & Returns", + description: "Document function parameters, return values, and exceptions", icon: Code, - subsections: ["@param", "@returns", "@throws", "@yields"], + href: "/parameters", }, { - id: "types", + id: "type-definitions", title: "Type Definitions", + description: "Define and document custom types and interfaces", icon: Hash, - subsections: ["@typedef", "@property", "@enum", "@type"], + href: "/type-definitions", }, { id: "functions", title: "Functions & Classes", + description: "Document functions, classes, constructors, and methods", icon: Settings, - subsections: ["@function", "@class", "@constructor", "@method", "@static", "@override"], + href: "/functions", }, { id: "modules", title: "Modules & Namespaces", + description: "Organize code with modules, namespaces, and membership", icon: FileText, - subsections: ["@module", "@namespace", "@memberof", "@exports", "@requires"], + href: "/modules", }, { id: "advanced", title: "Advanced Tags", + description: "Advanced JSDoc features for complex documentation needs", icon: BookOpen, - subsections: ["@callback", "@event", "@fires", "@listens", "@mixes", "@abstract"], + href: "/advanced", }, { id: "objects", title: "Objects & Interfaces", + description: "Document object structures, interfaces, and inheritance", icon: Hash, - subsections: ["@interface", "@implements", "@extends", "@mixin"], + href: "/objects", }, { id: "documentation", title: "Documentation Tags", + description: "Tags for linking, examples, and additional documentation", icon: Info, - subsections: ["@example", "@see", "@link", "@tutorial", "@todo"], + href: "/documentation", }, { id: "examples", title: "Complete Examples", + description: "Real-world JSDoc documentation examples", icon: Info, - subsections: ["Function Example", "Class Example", "Module Example"], + href: "/examples", }, { id: "best-practices", title: "Best Practices", + description: "Guidelines for writing effective JSDoc documentation", icon: Users, - subsections: ["Consistency", "Completeness", "Clarity", "Type Safety", "Examples"], + href: "/best-practices", }, ] -const jsdocData = { - kitchenSink: { - title: "Kitchen Sink Example", - description: "A comprehensive example showcasing multiple JSDoc features in one code block", - items: [ - { - tag: "Complete Example", - syntax: "Complete function documentation", - example: `/** - * Advanced user management service with comprehensive JSDoc documentation - * @module UserService - * @description Handles user creation, authentication, and profile management - * @author Jane Smith - * @version 2.1.0 - * @since 1.0.0 - * @requires Database - * @requires EmailService - * @exports UserService - * @example - * // Initialize the service - * const userService = new UserService(database, emailService); - * - * // Create a new user - * const user = await userService.createUser({ - * name: 'John Doe', - * email: 'john@example.com', - * role: 'admin' - * }); - */ - -/** - * @typedef {Object} UserData - * @property {string} name - Full name of the user - * @property {string} email - Email address (must be unique) - * @property {('admin'|'user'|'guest')} [role='user'] - User role in the system - * @property {number} [age] - User age (optional) - * @property {UserPreferences} [preferences] - User preferences object - */ - -/** - * @typedef {Object} UserPreferences - * @property {boolean} emailNotifications - Whether to send email notifications - * @property {string} theme - UI theme preference - * @property {string} language - Preferred language code - */ - -/** - * @callback ValidationCallback - * @param {Error|null} error - Validation error if any - * @param {boolean} isValid - Whether the data is valid - */ - -/** - * User management service class - * @class UserService - * @implements {EventEmitter} - * @fires UserService#userCreated - * @fires UserService#userDeleted - * @example - * const service = new UserService(db, emailService); - * service.on('userCreated', (user) => { - * console.log('New user created:', user.name); - * }); - */ -class UserService extends EventEmitter { - /** - * Creates a new UserService instance - * @constructor - * @param {Database} database - Database connection instance - * @param {EmailService} emailService - Email service for notifications - * @throws {TypeError} When database or emailService is not provided - * @since 1.0.0 - */ - constructor(database, emailService) { - super(); - if (!database || !emailService) { - throw new TypeError('Database and EmailService are required'); - } - this.db = database; - this.emailService = emailService; - } - - /** - * Creates a new user in the system - * @async - * @method createUser - * @memberof UserService - * @param {UserData} userData - User information object - * @param {Object} [options={}] - Additional options - * @param {boolean} [options.sendWelcomeEmail=true] - Send welcome email - * @param {boolean} [options.validateEmail=true] - Validate email format - * @returns {Promise} Promise resolving to created user object - * @throws {ValidationError} When user data is invalid - * @throws {DuplicateEmailError} When email already exists - * @throws {DatabaseError} When database operation fails - * @fires UserService#userCreated - * @example - * // Basic user creation - * const user = await userService.createUser({ - * name: 'Alice Johnson', - * email: 'alice@example.com' - * }); - * - * @example - * // Advanced user creation with options - * const adminUser = await userService.createUser({ - * name: 'Bob Admin', - * email: 'bob@example.com', - * role: 'admin', - * preferences: { - * emailNotifications: false, - * theme: 'dark', - * language: 'en' - * } - * }, { - * sendWelcomeEmail: false, - * validateEmail: true - * }); - * @since 1.0.0 - * @todo Add support for bulk user creation - * @todo Implement user avatar upload - */ - async createUser(userData, options = {}) { - // Implementation here... - - /** - * User created event - * @event UserService#userCreated - * @type {Object} - * @property {User} user - The created user object - * @property {Date} timestamp - When the user was created - */ - this.emit('userCreated', { user: newUser, timestamp: new Date() }); - - return newUser; - } - - /** - * Validates user email address - * @static - * @method validateEmail - * @param {string} email - Email address to validate - * @param {ValidationCallback} callback - Callback function for validation result - * @returns {boolean} True if email format is valid - * @example - * // Synchronous validation - * const isValid = UserService.validateEmail('test@example.com'); - * - * // Asynchronous validation with callback - * UserService.validateEmail('test@example.com', (error, isValid) => { - * if (error) { - * console.error('Validation error:', error); - * } else { - * console.log('Email is valid:', isValid); - * } - * }); - * @since 1.2.0 - */ - static validateEmail(email, callback) { - // Implementation here... - } - - /** - * @deprecated Since version 2.0.0. Use {@link UserService#createUser} instead. - * @method addUser - * @param {UserData} userData - User data - * @returns {Promise} Created user - * @see {@link UserService#createUser} - */ - async addUser(userData) { - console.warn('addUser is deprecated. Use createUser instead.'); - return this.createUser(userData); - } -} - -/** - * @namespace UserService.Utils - * @description Utility functions for user management - */ -UserService.Utils = { - /** - * Formats user display name - * @function formatDisplayName - * @memberof UserService.Utils - * @param {string} firstName - User's first name - * @param {string} lastName - User's last name - * @returns {string} Formatted display name - * @example - * const displayName = UserService.Utils.formatDisplayName('John', 'Doe'); - * console.log(displayName); // "John D." - */ - formatDisplayName(firstName, lastName) { - return \`\${firstName} \${lastName.charAt(0)}.\`; - } -}; - -module.exports = UserService;`, - description: "Complete function documentation with all common tags", - }, - ], - }, - basic: { - title: "Basic Tags", - description: "Essential JSDoc tags for documenting your code", - items: [ - { - tag: "@description", - syntax: "@description {string} Description text", - example: - "/**\n * @description Calculates the sum of two numbers\n * @param {number} a - First number\n * @param {number} b - Second number\n * @returns {number} The sum\n */\nfunction add(a, b) {\n return a + b;\n}", - description: "Provides a description of the function, class, or variable", - }, - { - tag: "@author", - syntax: "@author {string} Author name ", - example: "/**\n * @author John Doe \n * @author Jane Smith \n */", - description: "Specifies the author(s) of the code", - }, - { - tag: "@version", - syntax: "@version {string} Version number", - example: "/**\n * @version 1.2.0\n * @since 1.0.0\n */", - description: "Indicates the current version of the code", - }, - { - tag: "@since", - syntax: "@since {string} Version when added", - example: "/**\n * @since 1.0.0\n * @description Added in the initial release\n */", - description: "Specifies when the feature was first added", - }, - { - tag: "@deprecated", - syntax: "@deprecated {string} Deprecation message", - example: - "/**\n * @deprecated Since version 2.0.0. Use newFunction() instead.\n * @see {@link newFunction}\n */\nfunction oldFunction() {\n // deprecated implementation\n}", - description: "Marks code as deprecated with migration guidance", - }, - ], - }, - parameters: { - title: "Parameters & Returns", - description: "Document function parameters, return values, and exceptions", - items: [ - { - tag: "@param", - syntax: "@param {type} name Description", - example: - "/**\n * @param {number} x - The first number\n * @param {number} y - The second number\n * @param {Object} [options] - Optional configuration\n * @param {boolean} [options.strict=false] - Use strict mode\n */\nfunction calculate(x, y, options = {}) {\n // implementation\n}", - description: "Documents function parameters with types and descriptions", - }, - { - tag: "@returns", - syntax: "@returns {type} Description", - example: - "/**\n * @param {number[]} numbers - Array of numbers\n * @returns {number} The sum of all numbers\n * @returns {null} Returns null if array is empty\n */\nfunction sum(numbers) {\n if (numbers.length === 0) return null;\n return numbers.reduce((a, b) => a + b, 0);\n}", - description: "Documents the return value and its type", - }, - { - tag: "@throws", - syntax: "@throws {ErrorType} Description", - example: - "/**\n * @param {string} email - User email address\n * @throws {TypeError} When email is not a string\n * @throws {Error} When email format is invalid\n */\nfunction validateEmail(email) {\n if (typeof email !== 'string') {\n throw new TypeError('Email must be a string');\n }\n if (!email.includes('@')) {\n throw new Error('Invalid email format');\n }\n}", - description: "Documents exceptions that may be thrown", - }, - { - tag: "@yields", - syntax: "@yields {type} Description", - example: - "/**\n * @generator\n * @yields {number} The next number in the Fibonacci sequence\n * @returns {Generator}\n */\nfunction* fibonacci() {\n let a = 0, b = 1;\n while (true) {\n yield a;\n [a, b] = [b, a + b];\n }\n}", - description: "Documents what a generator function yields", - }, - ], - }, - types: { - title: "Type Definitions", - description: "Define and document custom types and interfaces", - items: [ - { - tag: "@typedef", - syntax: "@typedef {Object} TypeName", - example: "/**\n * @typedef {Object} User\n * @property {string} name\n * @property {number} age\n */", - description: "Defines a custom type", - }, - { - tag: "@property", - syntax: "@property {type} name Description", - example: "/**\n * @property {string} email - User email address\n */", - description: "Documents object properties", - }, - { - tag: "@enum", - syntax: "@enum {type}", - example: '/**\n * @enum {string}\n */\nconst Status = {\n PENDING: "pending",\n COMPLETE: "complete"\n}', - description: "Documents enumeration values", - }, - { - tag: "@type", - syntax: "@type {type}", - example: "/**\n * @type {string|number}\n */\nlet value;", - description: "Specifies the type of a variable", - }, - ], - }, - functions: { - title: "Functions & Classes", - description: "Document functions, classes, constructors, and methods", - items: [ - { - tag: "@function", - syntax: "@function", - example: "/**\n * @function calculateTotal\n * @description Calculates order total\n */", - description: "Explicitly marks something as a function", - }, - { - tag: "@class", - syntax: "@class", - example: "/**\n * @class\n * @description Represents a user account\n */", - description: "Documents a class", - }, - { - tag: "@constructor", - syntax: "@constructor", - example: "/**\n * @constructor\n * @param {string} name - User name\n */", - description: "Documents a constructor function", - }, - { - tag: "@method", - syntax: "@method", - example: "/**\n * @method getName\n * @returns {string} The user name\n */", - description: "Documents a method", - }, - { - tag: "@static", - syntax: "@static", - example: "/**\n * @static\n * @method createUser\n */", - description: "Indicates a static method or property", - }, - { - tag: "@override", - syntax: "@override", - example: "/**\n * @override\n * @method toString\n */", - description: "Indicates method overrides parent method", - }, - ], - }, - modules: { - title: "Modules & Namespaces", - description: "Organize code with modules, namespaces, and membership", - items: [ - { - tag: "@module", - syntax: "@module ModuleName", - example: "/**\n * @module UserUtils\n * @description Utilities for user management\n */", - description: "Documents a module", - }, - { - tag: "@namespace", - syntax: "@namespace NamespaceName", - example: "/**\n * @namespace MyApp.Utils\n */", - description: "Documents a namespace", - }, - { - tag: "@memberof", - syntax: "@memberof ParentName", - example: "/**\n * @memberof MyApp.Utils\n * @function formatName\n */", - description: "Indicates membership in a parent", - }, - { - tag: "@exports", - syntax: "@exports ModuleName", - example: "/**\n * @exports UserService\n */", - description: "Documents what a module exports", - }, - { - tag: "@requires", - syntax: "@requires ModuleName", - example: "/**\n * @requires lodash\n */", - description: "Documents module dependencies", - }, - ], - }, - advanced: { - title: "Advanced Tags", - description: "Advanced JSDoc features for complex documentation needs", - items: [ - { - tag: "@callback", - syntax: "@callback CallbackName", - example: "/**\n * @callback RequestCallback\n * @param {Error} error\n * @param {Object} response\n */", - description: "Documents a callback function type", - }, - { - tag: "@event", - syntax: "@event EventName", - example: "/**\n * @event MyClass#dataLoaded\n * @type {Object}\n */", - description: "Documents an event", - }, - { - tag: "@fires", - syntax: "@fires EventName", - example: "/**\n * @fires MyClass#dataLoaded\n */", - description: "Indicates function fires an event", - }, - { - tag: "@listens", - syntax: "@listens EventName", - example: "/**\n * @listens MyClass#dataLoaded\n */", - description: "Indicates function listens to an event", - }, - { - tag: "@mixes", - syntax: "@mixes MixinName", - example: "/**\n * @mixes EventEmitter\n */", - description: "Documents that class mixes in another", - }, - { - tag: "@abstract", - syntax: "@abstract", - example: "/**\n * @abstract\n * @method process\n */", - description: "Indicates abstract method or class", - }, - ], - }, - examples: { - title: "Complete Examples", - description: "Real-world JSDoc documentation examples", - items: [ - { - tag: "Function Example", - syntax: "Complete function documentation", - example: `/** - * Calculates the area of a rectangle - * @function calculateArea - * @param {number} width - The width of the rectangle - * @param {number} height - The height of the rectangle - * @returns {number} The area of the rectangle - * @throws {TypeError} When width or height is not a number - * @example - * // Calculate area of 5x3 rectangle - * const area = calculateArea(5, 3); - * console.log(area); // 15 - * @since 1.0.0 - * @author Jane Smith - */ -function calculateArea(width, height) { - if (typeof width !== 'number' || typeof height !== 'number') { - throw new TypeError('Width and height must be numbers'); - } - return width * height; -}`, - description: "Complete function documentation with all common tags", - }, - { - tag: "Class Example", - syntax: "Complete class documentation", - example: `/** - * Represents a user in the system - * @class User - * @param {string} name - The user's name - * @param {string} email - The user's email address - * @example - * const user = new User('John Doe', 'john@example.com'); - * console.log(user.getName()); // 'John Doe' - */ -class User { - /** - * Create a user - * @constructor - * @param {string} name - The user's name - * @param {string} email - The user's email address - */ - constructor(name, email) { - this.name = name; - this.email = email; - } - - /** - * Get the user's name - * @method getName - * @returns {string} The user's name - */ - getName() { - return this.name; - } -}`, - description: "Complete class documentation with constructor and methods", - }, - { - tag: "Module Example", - syntax: "Complete module documentation", - example: `/** - * User management module - * @module UserManagement - * @description Handles user creation and deletion - * @requires Database - * @requires EmailService - * @exports createUser - * @exports deleteUser - * @example - * // Create a user - * const user = createUser('John Doe', 'john@example.com'); - * console.log(user); - * - * // Delete a user - * deleteUser(user.id); - */ -const Database = require('./Database'); -const EmailService = require('./EmailService'); - -function createUser(name, email) { - // Implementation here -} - -function deleteUser(userId) { - // Implementation here -} - -module.exports = { - createUser, - deleteUser -};`, - description: "Complete module documentation with dependencies and exports", - }, - ], - }, - "best-practices": { - title: "Best Practices", - description: "Guidelines for writing effective JSDoc documentation", - items: [ - { - tag: "Consistency", - syntax: "Use consistent formatting and style", - example: - "// Always use the same format for similar tags\n// Good: @param {string} name - User name\n// Good: @param {number} age - User age", - description: "Maintain consistent formatting across your documentation", - }, - { - tag: "Completeness", - syntax: "Document all public APIs", - example: "// Document all parameters, return values, and exceptions\n// Include examples for complex functions", - description: "Ensure all public functions, classes, and modules are documented", - }, - { - tag: "Clarity", - syntax: "Write clear, concise descriptions", - example: "// Good: Calculates user age from birth date\n// Bad: Does age stuff", - description: "Use clear, descriptive language that explains purpose and behavior", - }, - { - tag: "Type Safety", - syntax: "Always specify types for parameters and returns", - example: - "// Always include types\n@param {string|null} name - User name or null\n@returns {Promise} Promise resolving to user object", - description: "Include detailed type information to improve code reliability", - }, - { - tag: "Examples", - syntax: "Include usage examples for complex functions", - example: - "/**\n * @example\n * // Basic usage\n * const result = myFunction('input');\n * \n * @example\n * // Advanced usage\n * const result = myFunction('input', { option: true });\n */", - description: "Provide practical examples showing how to use the code", - }, - ], - }, - objects: { - title: "Objects & Interfaces", - description: "Document object structures, interfaces, and inheritance", - items: [ - { - tag: "@interface", - syntax: "@interface InterfaceName", - example: "/**\n * @interface Drawable\n * @description Interface for drawable objects\n */", - description: "Documents an interface that classes can implement", - }, - { - tag: "@implements", - syntax: "@implements {InterfaceName}", - example: "/**\n * @class Circle\n * @implements {Drawable}\n */", - description: "Indicates that a class implements an interface", - }, - { - tag: "@extends", - syntax: "@extends ParentClass", - example: "/**\n * @class ColoredCircle\n * @extends Circle\n */", - description: "Documents class inheritance", - }, - { - tag: "@mixin", - syntax: "@mixin MixinName", - example: "/**\n * @mixin EventEmitter\n * @description Adds event handling capabilities\n */", - description: "Documents a mixin that can be mixed into classes", - }, - ], - }, - documentation: { - title: "Documentation Tags", - description: "Tags for linking, examples, and additional documentation", - items: [ - { - tag: "@example", - syntax: "@example\n// Example code here", - example: - "/**\n * @example\n * // Basic usage\n * const result = myFunction('test');\n * console.log(result);\n */", - description: "Provides usage examples", - }, - { - tag: "@see", - syntax: "@see {reference}", - example: "/**\n * @see {@link MyClass#method}\n * @see https://example.com/docs\n */", - description: "References related documentation or code", - }, - { - tag: "@link", - syntax: "{@link reference}", - example: "/**\n * Uses {@link MyClass} for processing\n */", - description: "Creates inline links to other documentation", - }, - { - tag: "@tutorial", - syntax: "@tutorial TutorialName", - example: "/**\n * @tutorial getting-started\n */", - description: "Links to a tutorial document", - }, - { - tag: "@todo", - syntax: "@todo Description of what needs to be done", - example: "/**\n * @todo Add input validation\n * @todo Optimize performance\n */", - description: "Documents future improvements or fixes needed", - }, - ], - }, -} - export default function JSDocCheatsheet() { - const [activeSection, setActiveSection] = useState("kitchen-sink") - const [expandedSections, setExpandedSections] = useState(["kitchen-sink"]) - const [isScrolling, setIsScrolling] = useState(false) - - const toggleSection = (sectionId: string) => { - setExpandedSections((prev) => - prev.includes(sectionId) ? prev.filter((id) => id !== sectionId) : [...prev, sectionId], - ) - } - - const scrollToTag = (tag: string) => { - const element = document.getElementById(tag.replace("@", "").replace(/[^a-zA-Z0-9]/g, "-")) - if (element) { - setIsScrolling(true) - element.scrollIntoView({ behavior: "smooth", block: "start" }) - - // Reset scrolling state after animation - setTimeout(() => setIsScrolling(false), 1000) - - // Highlight the element briefly - element.classList.add("ring-2", "ring-primary", "ring-opacity-50") - setTimeout(() => { - element.classList.remove("ring-2", "ring-primary", "ring-opacity-50") - }, 2000) - } - } - - useEffect(() => { - const handleScroll = () => { - if (isScrolling) return // Don't update during programmatic scrolling - - const sections = Object.keys(jsdocData) - const scrollPosition = window.scrollY + 200 - - for (const sectionId of sections) { - const element = document.getElementById(`section-${sectionId}`) - if (element) { - const { offsetTop, offsetHeight } = element - if (scrollPosition >= offsetTop && scrollPosition < offsetTop + offsetHeight) { - setActiveSection(sectionId) - break - } - } - } - } - - window.addEventListener("scroll", handleScroll) - return () => window.removeEventListener("scroll", handleScroll) - }, [isScrolling]) + // TOC items for the homepage + const tocItems = [ + { id: "kitchen-sink", title: "Kitchen Sink Example", level: 1 }, + { id: "navigation-grid", title: "Explore JSDoc Sections", level: 1 }, + { id: "quick-reference", title: "Quick Reference", level: 1 }, + ] return (
- {/* Header */} -
-
-
-

JSDoc Cheatsheet

-

Complete Reference for JavaScript Documentation

+
+ + + +
+
+ {/* Kitchen Sink Example */} + + + + + Kitchen Sink Example + + + + A comprehensive example showcasing multiple JSDoc features in one code block + + + + + + + + {/* Navigation Grid */} + -
-
-
-
- {/* Sidebar Navigation */} - - - {/* Main Content */} -
-
- {/* Kitchen Sink Example */} - - - - - Kitchen Sink Example - - - - A comprehensive example showcasing multiple JSDoc features in one code block - - - - - - - - {/* Section Header */} -
-

- {jsdocData[activeSection as keyof typeof jsdocData]?.title || "Section"} -

-

- {jsdocData[activeSection as keyof typeof jsdocData]?.description || "Documentation section"} -

-
- - {/* Content Cards */} -
- {jsdocData[activeSection as keyof typeof jsdocData]?.items.map((item, index) => ( - - -
- - - {item.tag} - - +
+ {navigationSections.map((section) => { + const Icon = section.icon + return ( + + + +
+
- {item.description} + + {section.title} + + + {section.description} +
- - {/* Syntax */} -
-

Syntax:

- - {item.syntax} - -
- - - - {/* Example with Syntax Highlighting */} -
-

Example:

- -
+ +
- )) || []} + + ) + })} +
+ + {/* Quick Reference */} + + + + + Quick Reference + + + Essential JSDoc tags you'll use most frequently + + + +
+
+

Basic Tags

+
+ @description + @author + @version + @since +
+
+
+

Parameters

+
+ @param + @returns + @throws + @example +
+
+
+

Types & Classes

+
+ @typedef + @class + @method + @static +
+
+
+

Advanced

+
+ @module + @callback + @interface + @link +
+
-
-
+ +
@@ -977,4 +230,4 @@ export default function JSDocCheatsheet() {
) -} +} \ No newline at end of file diff --git a/app/parameters/page.tsx b/app/parameters/page.tsx new file mode 100644 index 0000000..d7be16e --- /dev/null +++ b/app/parameters/page.tsx @@ -0,0 +1,103 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import { Code } from "lucide-react" +import CodeHighlighter from "@/components/SyntaxHighlighter" +import Sidebar from "@/components/Sidebar" +import TableOfContents from "@/components/TableOfContents" +import { jsdocData } from "@/lib/jsdoc-data" +import Header from "@/components/Header" + +const parametersData = jsdocData.parameters + +export default function ParametersPage() { + // Generate TOC items from the data + const tocItems = parametersData.items.map((item, index) => ({ + id: item.tag.replace("@", "").replace(/[^a-zA-Z0-9]/g, "-"), + title: item.tag, + level: 1 + })) + + return ( +
+
+ + + +
+
+ {/* Section Header */} +
+

+ {parametersData.title} +

+

+ {parametersData.description} +

+
+ + {/* Content Cards */} +
+ {parametersData.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+
+
+ ))} +
+
+
+ + {/* Footer */} + +
+ ) +} diff --git a/app/type-definitions/page.tsx b/app/type-definitions/page.tsx new file mode 100644 index 0000000..a38b9b6 --- /dev/null +++ b/app/type-definitions/page.tsx @@ -0,0 +1,102 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import CodeHighlighter from "@/components/SyntaxHighlighter" +import Sidebar from "@/components/Sidebar" +import TableOfContents from "@/components/TableOfContents" +import { jsdocData } from "@/lib/jsdoc-data" +import Header from "@/components/Header" + +const typeDefinitionsData = jsdocData.types + +export default function TypeDefinitionsPage() { + // Generate TOC items from the data + const tocItems = typeDefinitionsData.items.map((item, index) => ({ + id: item.tag.replace("@", "").replace(/[^a-zA-Z0-9]/g, "-"), + title: item.tag, + level: 1 + })) + + return ( +
+
+ + + +
+
+ {/* Section Header */} +
+

+ {typeDefinitionsData.title} +

+

+ {typeDefinitionsData.description} +

+
+ + {/* Content Cards */} +
+ {typeDefinitionsData.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+
+
+ ))} +
+
+
+ + {/* Footer */} + +
+ ) +} diff --git a/components/Content.tsx b/components/Content.tsx new file mode 100644 index 0000000..1a636ae --- /dev/null +++ b/components/Content.tsx @@ -0,0 +1,139 @@ +'use client' + +import React from 'react' +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import CustomSyntaxHighlighter from "@/components/SyntaxHighlighter" +import { ExternalLink } from "lucide-react" + +interface Source { + name: string + url: string + description: string +} + +interface JSDocItem { + tag: string + syntax: string + example: string + description: string + sources?: Source[] +} + +interface JSDocSection { + title: string + description: string + items: JSDocItem[] +} + +interface ContentProps { + activeSection: string + jsdocData: Record +} + +export default function Content({ activeSection, jsdocData }: ContentProps) { + const currentSection = jsdocData[activeSection] + + return ( +
+
+ {/* Kitchen Sink Example */} + {activeSection === 'kitchen-sink' && ( + + + + + Kitchen Sink Example + + + + A comprehensive example showcasing multiple JSDoc features in one code block + + + + + + + )} + + {/* Section Header */} +
+

+ {currentSection?.title || "Section"} +

+

+ {currentSection?.description || "Documentation section"} +

+
+ + {/* Content Cards */} +
+ {currentSection?.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+ + {/* Source Links */} + {item.sources && item.sources.length > 0 && ( + <> + +
+

Sources & References:

+
+ {item.sources.map((source, sourceIndex) => ( +
+ +
+ + {source.name} + +

{source.description}

+
+
+ ))} +
+
+ + )} +
+
+ )) || []} +
+
+
+ ) +} diff --git a/components/Header.tsx b/components/Header.tsx new file mode 100644 index 0000000..49172d7 --- /dev/null +++ b/components/Header.tsx @@ -0,0 +1,19 @@ +"use client" + +import { BookOpen } from "lucide-react" +import Link from "next/link" + +export default function Header() { + return ( +
+
+
+ + +

JSDoc Cheatsheet

+ +
+
+
+ ) +} \ No newline at end of file diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx new file mode 100644 index 0000000..afd8305 --- /dev/null +++ b/components/Sidebar.tsx @@ -0,0 +1,87 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { BookOpen, Code, FileText, Hash, Settings, Tag, Info, Users, Navigation, ChevronRight, Home } from "lucide-react" +import Link from "next/link" +import { usePathname } from "next/navigation" + +const navigationItems = [ + { name: "Kitchen Sink", href: "/", icon: Home }, + { name: "Basic Tags", href: "/basic-tags", icon: Tag }, + { name: "Parameters", href: "/parameters", icon: Code }, + { name: "Types", href: "/type-definitions", icon: Hash }, + { name: "Functions", href: "/functions", icon: Settings }, + { name: "Modules", href: "/modules", icon: FileText }, + { name: "Advanced", href: "/advanced", icon: BookOpen }, + { name: "Objects", href: "/objects", icon: Hash }, + { name: "Docs", href: "/documentation", icon: Info }, + { name: "Examples", href: "/examples", icon: Info }, + { name: "Best Practices", href: "/best-practices", icon: Users }, +] + +export default function Sidebar() { + const pathname = usePathname() + + return ( + + ) +} diff --git a/components/SyntaxHighlighter.tsx b/components/SyntaxHighlighter.tsx new file mode 100644 index 0000000..983d4f6 --- /dev/null +++ b/components/SyntaxHighlighter.tsx @@ -0,0 +1,46 @@ +"use client" + +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' +import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism' + +interface SyntaxHighlighterProps { + code: string + language?: string +} + +const CodeHighlighter = ({ code, language = "javascript" }: SyntaxHighlighterProps) => { + return ( +
+ ( +
+            {children}
+          
+ )} + > + {code} +
+
+ ) +} + +export default CodeHighlighter \ No newline at end of file diff --git a/components/TableOfContents.tsx b/components/TableOfContents.tsx new file mode 100644 index 0000000..24a5ee8 --- /dev/null +++ b/components/TableOfContents.tsx @@ -0,0 +1,148 @@ +"use client" + +import { useState, useEffect } from "react" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" +import { List, ChevronRight, Hash } from "lucide-react" + +interface TOCItem { + id: string + title: string + level: number +} + +interface TableOfContentsProps { + items: TOCItem[] + className?: string +} + +export default function TableOfContents({ items, className = "" }: TableOfContentsProps) { + const [activeId, setActiveId] = useState("") + const [isVisible, setIsVisible] = useState(false) + + useEffect(() => { + if (items.length === 0) return + + // Calculate header height dynamically + const header = document.querySelector('header') + const headerHeight = header ? header.offsetHeight + 20 : 100 // 20px buffer + const rootMargin = `-${headerHeight}px 0% -35% 0%` + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + setActiveId(entry.target.id) + } + }) + }, + { + rootMargin, + threshold: 0.1, + } + ) + + // Observe all TOC items + items.forEach((item) => { + const element = document.getElementById(item.id) + if (element) { + observer.observe(element) + } + }) + + return () => { + items.forEach((item) => { + const element = document.getElementById(item.id) + if (element) { + observer.unobserve(element) + } + }) + } + }, [items]) + + useEffect(() => { + // Show TOC if there are items + setIsVisible(items.length > 0) + }, [items]) + + const scrollToSection = (id: string) => { + const element = document.getElementById(id) + if (element) { + // Get the actual header height dynamically + const header = document.querySelector('header') + const headerHeight = header ? header.offsetHeight + 20 : 100 // 20px buffer + const elementPosition = element.offsetTop - headerHeight + + // Add a small delay to ensure smooth scrolling + setTimeout(() => { + window.scrollTo({ + top: Math.max(0, elementPosition), // Ensure we don't scroll to negative position + behavior: "smooth" + }) + }, 10) + } + } + + if (!isVisible) return null + + return ( + + ) +} diff --git a/lib/data-loader.ts b/lib/data-loader.ts new file mode 100644 index 0000000..32f5c84 --- /dev/null +++ b/lib/data-loader.ts @@ -0,0 +1,15 @@ +import { jsdocData } from '@/data/jsdoc-data' +import { restoredJSDocData } from '@/data/restored-jsdoc-data' + +// Lazy load data for better performance +export const loadSectionData = async (sectionId: string) => { + return jsdocData[sectionId] || jsdocData.basic +} + +// Preload critical data - use restored data for now +export const preloadCriticalData = async () => { + return { + ...jsdocData, + ...restoredJSDocData + } +} diff --git a/lib/jsdoc-data.ts b/lib/jsdoc-data.ts new file mode 100644 index 0000000..e572a53 --- /dev/null +++ b/lib/jsdoc-data.ts @@ -0,0 +1,632 @@ +export const jsdocData = { + kitchenSink: { + title: "Kitchen Sink Example", + description: "A comprehensive example showcasing multiple JSDoc features in one code block", + items: [ + { + tag: "Complete Example", + syntax: "Complete function documentation", + example: `/** + * Advanced user management service with comprehensive JSDoc documentation + * @module UserService + * @description Handles user creation, authentication, and profile management + * @author Jane Smith + * @version 2.1.0 + * @since 1.0.0 + * @requires Database + * @requires EmailService + * @exports UserService + * @example + * // Initialize the service + * const userService = new UserService(database, emailService); + * + * // Create a new user + * const user = await userService.createUser({ + * name: 'John Doe', + * email: 'john@example.com', + * role: 'admin' + * }); + */ + +/** + * @typedef {Object} UserData + * @property {string} name - Full name of the user + * @property {string} email - Email address (must be unique) + * @property {('admin'|'user'|'guest')} [role='user'] - User role in the system + * @property {number} [age] - User age (optional) + * @property {UserPreferences} [preferences] - User preferences object + */ + +/** + * @typedef {Object} UserPreferences + * @property {boolean} emailNotifications - Whether to send email notifications + * @property {string} theme - UI theme preference + * @property {string} language - Preferred language code + */ + +/** + * @callback ValidationCallback + * @param {Error|null} error - Validation error if any + * @param {boolean} isValid - Whether the data is valid + */ + +/** + * User management service class + * @class UserService + * @implements {EventEmitter} + * @fires UserService#userCreated + * @fires UserService#userDeleted + * @example + * const service = new UserService(db, emailService); + * service.on('userCreated', (user) => { + * console.log('New user created:', user.name); + * }); + */ +class UserService extends EventEmitter { + /** + * Creates a new UserService instance + * @constructor + * @param {Database} database - Database connection instance + * @param {EmailService} emailService - Email service for notifications + * @throws {TypeError} When database or emailService is not provided + * @since 1.0.0 + */ + constructor(database, emailService) { + super(); + if (!database || !emailService) { + throw new TypeError('Database and EmailService are required'); + } + this.db = database; + this.emailService = emailService; + } + + /** + * Creates a new user in the system + * @async + * @method createUser + * @memberof UserService + * @param {UserData} userData - User information object + * @param {Object} [options={}] - Additional options + * @param {boolean} [options.sendWelcomeEmail=true] - Send welcome email + * @param {boolean} [options.validateEmail=true] - Validate email format + * @returns {Promise} Promise resolving to created user object + * @throws {ValidationError} When user data is invalid + * @throws {DuplicateEmailError} When email already exists + * @throws {DatabaseError} When database operation fails + * @fires UserService#userCreated + * @example + * // Basic user creation + * const user = await userService.createUser({ + * name: 'Alice Johnson', + * email: 'alice@example.com' + * }); + * + * @example + * // Advanced user creation with options + * const adminUser = await userService.createUser({ + * name: 'Bob Admin', + * email: 'bob@example.com', + * role: 'admin', + * preferences: { + * emailNotifications: false, + * theme: 'dark', + * language: 'en' + * } + * }, { + * sendWelcomeEmail: false, + * validateEmail: true + * }); + * @since 1.0.0 + * @todo Add support for bulk user creation + * @todo Implement user avatar upload + */ + async createUser(userData, options = {}) { + // Implementation here... + + /** + * User created event + * @event UserService#userCreated + * @type {Object} + * @property {User} user - The created user object + * @property {Date} timestamp - When the user was created + */ + this.emit('userCreated', { user: newUser, timestamp: new Date() }); + + return newUser; + } + + /** + * Validates user email address + * @static + * @method validateEmail + * @param {string} email - Email address to validate + * @param {ValidationCallback} callback - Callback function for validation result + * @returns {boolean} True if email format is valid + * @example + * // Synchronous validation + * const isValid = UserService.validateEmail('test@example.com'); + * + * // Asynchronous validation with callback + * UserService.validateEmail('test@example.com', (error, isValid) => { + * if (error) { + * console.error('Validation error:', error); + * } else { + * console.log('Email is valid:', isValid); + * } + * }); + * @since 1.2.0 + */ + static validateEmail(email, callback) { + // Implementation here... + } + + /** + * @deprecated Since version 2.0.0. Use {@link UserService#createUser} instead. + * @method addUser + * @param {UserData} userData - User data + * @returns {Promise} Created user + * @see {@link UserService#createUser} + */ + async addUser(userData) { + console.warn('addUser is deprecated. Use createUser instead.'); + return this.createUser(userData); + } +} + +/** + * @namespace UserService.Utils + * @description Utility functions for user management + */ +UserService.Utils = { + /** + * Formats user display name + * @function formatDisplayName + * @memberof UserService.Utils + * @param {string} firstName - User's first name + * @param {string} lastName - User's last name + * @returns {string} Formatted display name + * @example + * const displayName = UserService.Utils.formatDisplayName('John', 'Doe'); + * console.log(displayName); // "John D." + */ + formatDisplayName(firstName, lastName) { + return \`\${firstName} \${lastName.charAt(0)}.\`; + } +}; + +module.exports = UserService;`, + description: "Complete function documentation with all common tags", + }, + ], + }, + basic: { + title: "Basic Tags", + description: "Essential JSDoc tags for documenting your code", + items: [ + { + tag: "@description", + syntax: "@description {string} Description text", + example: + "/**\n * @description Calculates the sum of two numbers\n * @param {number} a - First number\n * @param {number} b - Second number\n * @returns {number} The sum\n */\nfunction add(a, b) {\n return a + b;\n}", + description: "Provides a description of the function, class, or variable", + }, + { + tag: "@author", + syntax: "@author {string} Author name ", + example: "/**\n * @author John Doe \n * @author Jane Smith \n */", + description: "Specifies the author(s) of the code", + }, + { + tag: "@version", + syntax: "@version {string} Version number", + example: "/**\n * @version 1.2.0\n * @since 1.0.0\n */", + description: "Indicates the current version of the code", + }, + { + tag: "@since", + syntax: "@since {string} Version when added", + example: "/**\n * @since 1.0.0\n * @description Added in the initial release\n */", + description: "Specifies when the feature was first added", + }, + { + tag: "@deprecated", + syntax: "@deprecated {string} Deprecation message", + example: + "/**\n * @deprecated Since version 2.0.0. Use newFunction() instead.\n * @see {@link newFunction}\n */\nfunction oldFunction() {\n // deprecated implementation\n}", + description: "Marks code as deprecated with migration guidance", + }, + ], + }, + parameters: { + title: "Parameters & Returns", + description: "Document function parameters, return values, and exceptions", + items: [ + { + tag: "@param", + syntax: "@param {type} name Description", + example: + "/**\n * @param {number} x - The first number\n * @param {number} y - The second number\n * @param {Object} [options] - Optional configuration\n * @param {boolean} [options.strict=false] - Use strict mode\n */\nfunction calculate(x, y, options = {}) {\n // implementation\n}", + description: "Documents function parameters with types and descriptions", + }, + { + tag: "@returns", + syntax: "@returns {type} Description", + example: + "/**\n * @param {number[]} numbers - Array of numbers\n * @returns {number} The sum of all numbers\n * @returns {null} Returns null if array is empty\n */\nfunction sum(numbers) {\n if (numbers.length === 0) return null;\n return numbers.reduce((a, b) => a + b, 0);\n}", + description: "Documents the return value and its type", + }, + { + tag: "@throws", + syntax: "@throws {ErrorType} Description", + example: + "/**\n * @param {string} email - User email address\n * @throws {TypeError} When email is not a string\n * @throws {Error} When email format is invalid\n */\nfunction validateEmail(email) {\n if (typeof email !== 'string') {\n throw new TypeError('Email must be a string');\n }\n if (!email.includes('@')) {\n throw new Error('Invalid email format');\n }\n}", + description: "Documents exceptions that may be thrown", + }, + { + tag: "@yields", + syntax: "@yields {type} Description", + example: + "/**\n * @generator\n * @yields {number} The next number in the Fibonacci sequence\n * @returns {Generator}\n */\nfunction* fibonacci() {\n let a = 0, b = 1;\n while (true) {\n yield a;\n [a, b] = [b, a + b];\n }\n}", + description: "Documents what a generator function yields", + }, + ], + }, + types: { + title: "Type Definitions", + description: "Define and document custom types and interfaces", + items: [ + { + tag: "@typedef", + syntax: "@typedef {Object} TypeName", + example: "/**\n * @typedef {Object} User\n * @property {string} name\n * @property {number} age\n */", + description: "Defines a custom type", + }, + { + tag: "@property", + syntax: "@property {type} name Description", + example: "/**\n * @property {string} email - User email address\n */", + description: "Documents object properties", + }, + { + tag: "@enum", + syntax: "@enum {type}", + example: '/**\n * @enum {string}\n */\nconst Status = {\n PENDING: "pending",\n COMPLETE: "complete"\n}', + description: "Documents enumeration values", + }, + { + tag: "@type", + syntax: "@type {type}", + example: "/**\n * @type {string|number}\n */\nlet value;", + description: "Specifies the type of a variable", + }, + ], + }, + functions: { + title: "Functions & Classes", + description: "Document functions, classes, constructors, and methods", + items: [ + { + tag: "@function", + syntax: "@function", + example: "/**\n * @function calculateTotal\n * @description Calculates order total\n */", + description: "Explicitly marks something as a function", + }, + { + tag: "@class", + syntax: "@class", + example: "/**\n * @class\n * @description Represents a user account\n */", + description: "Documents a class", + }, + { + tag: "@constructor", + syntax: "@constructor", + example: "/**\n * @constructor\n * @param {string} name - User name\n */", + description: "Documents a constructor function", + }, + { + tag: "@method", + syntax: "@method", + example: "/**\n * @method getName\n * @returns {string} The user name\n */", + description: "Documents a method", + }, + { + tag: "@static", + syntax: "@static", + example: "/**\n * @static\n * @method createUser\n */", + description: "Indicates a static method or property", + }, + { + tag: "@override", + syntax: "@override", + example: "/**\n * @override\n * @method toString\n */", + description: "Indicates method overrides parent method", + }, + ], + }, + modules: { + title: "Modules & Namespaces", + description: "Organize code with modules, namespaces, and membership", + items: [ + { + tag: "@module", + syntax: "@module ModuleName", + example: "/**\n * @module UserUtils\n * @description Utilities for user management\n */", + description: "Documents a module", + }, + { + tag: "@namespace", + syntax: "@namespace NamespaceName", + example: "/**\n * @namespace MyApp.Utils\n */", + description: "Documents a namespace", + }, + { + tag: "@memberof", + syntax: "@memberof ParentName", + example: "/**\n * @memberof MyApp.Utils\n * @function formatName\n */", + description: "Indicates membership in a parent", + }, + { + tag: "@exports", + syntax: "@exports ModuleName", + example: "/**\n * @exports UserService\n */", + description: "Documents what a module exports", + }, + { + tag: "@requires", + syntax: "@requires ModuleName", + example: "/**\n * @requires lodash\n */", + description: "Documents module dependencies", + }, + ], + }, + advanced: { + title: "Advanced Tags", + description: "Advanced JSDoc features for complex documentation needs", + items: [ + { + tag: "@callback", + syntax: "@callback CallbackName", + example: "/**\n * @callback RequestCallback\n * @param {Error} error\n * @param {Object} response\n */", + description: "Documents a callback function type", + }, + { + tag: "@event", + syntax: "@event EventName", + example: "/**\n * @event MyClass#dataLoaded\n * @type {Object}\n */", + description: "Documents an event", + }, + { + tag: "@fires", + syntax: "@fires EventName", + example: "/**\n * @fires MyClass#dataLoaded\n */", + description: "Indicates function fires an event", + }, + { + tag: "@listens", + syntax: "@listens EventName", + example: "/**\n * @listens MyClass#dataLoaded\n */", + description: "Indicates function listens to an event", + }, + { + tag: "@mixes", + syntax: "@mixes MixinName", + example: "/**\n * @mixes EventEmitter\n */", + description: "Documents that class mixes in another", + }, + { + tag: "@abstract", + syntax: "@abstract", + example: "/**\n * @abstract\n * @method process\n */", + description: "Indicates abstract method or class", + }, + ], + }, + objects: { + title: "Objects & Interfaces", + description: "Document object structures, interfaces, and inheritance", + items: [ + { + tag: "@interface", + syntax: "@interface InterfaceName", + example: "/**\n * @interface Drawable\n * @description Interface for drawable objects\n */", + description: "Documents an interface that classes can implement", + }, + { + tag: "@implements", + syntax: "@implements {InterfaceName}", + example: "/**\n * @class Circle\n * @implements {Drawable}\n */", + description: "Indicates that a class implements an interface", + }, + { + tag: "@extends", + syntax: "@extends ParentClass", + example: "/**\n * @class ColoredCircle\n * @extends Circle\n */", + description: "Documents class inheritance", + }, + { + tag: "@mixin", + syntax: "@mixin MixinName", + example: "/**\n * @mixin EventEmitter\n * @description Adds event handling capabilities\n */", + description: "Documents a mixin that can be mixed into classes", + }, + ], + }, + documentation: { + title: "Documentation Tags", + description: "Tags for linking, examples, and additional documentation", + items: [ + { + tag: "@example", + syntax: "@example\n// Example code here", + example: + "/**\n * @example\n * // Basic usage\n * const result = myFunction('test');\n * console.log(result);\n */", + description: "Provides usage examples", + }, + { + tag: "@see", + syntax: "@see {reference}", + example: "/**\n * @see {@link MyClass#method}\n * @see https://example.com/docs\n */", + description: "References related documentation or code", + }, + { + tag: "@link", + syntax: "{@link reference}", + example: "/**\n * Uses {@link MyClass} for processing\n */", + description: "Creates inline links to other documentation", + }, + { + tag: "@tutorial", + syntax: "@tutorial TutorialName", + example: "/**\n * @tutorial getting-started\n */", + description: "Links to a tutorial document", + }, + { + tag: "@todo", + syntax: "@todo Description of what needs to be done", + example: "/**\n * @todo Add input validation\n * @todo Optimize performance\n */", + description: "Documents future improvements or fixes needed", + }, + ], + }, + examples: { + title: "Complete Examples", + description: "Real-world JSDoc documentation examples", + items: [ + { + tag: "Function Example", + syntax: "Complete function documentation", + example: `/** + * Calculates the area of a rectangle + * @function calculateArea + * @param {number} width - The width of the rectangle + * @param {number} height - The height of the rectangle + * @returns {number} The area of the rectangle + * @throws {TypeError} When width or height is not a number + * @example + * // Calculate area of 5x3 rectangle + * const area = calculateArea(5, 3); + * console.log(area); // 15 + * @since 1.0.0 + * @author Jane Smith + */ +function calculateArea(width, height) { + if (typeof width !== 'number' || typeof height !== 'number') { + throw new TypeError('Width and height must be numbers'); + } + return width * height; +}`, + description: "Complete function documentation with all common tags", + }, + { + tag: "Class Example", + syntax: "Complete class documentation", + example: `/** + * Represents a user in the system + * @class User + * @param {string} name - The user's name + * @param {string} email - The user's email address + * @example + * const user = new User('John Doe', 'john@example.com'); + * console.log(user.getName()); // 'John Doe' + */ +class User { + /** + * Create a user + * @constructor + * @param {string} name - The user's name + * @param {string} email - The user's email address + */ + constructor(name, email) { + this.name = name; + this.email = email; + } + + /** + * Get the user's name + * @method getName + * @returns {string} The user's name + */ + getName() { + return this.name; + } +}`, + description: "Complete class documentation with constructor and methods", + }, + { + tag: "Module Example", + syntax: "Complete module documentation", + example: `/** + * User management module + * @module UserManagement + * @description Handles user creation and deletion + * @requires Database + * @requires EmailService + * @exports createUser + * @exports deleteUser + * @example + * // Create a user + * const user = createUser('John Doe', 'john@example.com'); + * console.log(user); + * + * // Delete a user + * deleteUser(user.id); + */ +const Database = require('./Database'); +const EmailService = require('./EmailService'); + +function createUser(name, email) { + // Implementation here +} + +function deleteUser(userId) { + // Implementation here +} + +module.exports = { + createUser, + deleteUser +};`, + description: "Complete module documentation with dependencies and exports", + }, + ], + }, + "best-practices": { + title: "Best Practices", + description: "Guidelines for writing effective JSDoc documentation", + items: [ + { + tag: "Consistency", + syntax: "Use consistent formatting and style", + example: + "// Always use the same format for similar tags\n// Good: @param {string} name - User name\n// Good: @param {number} age - User age", + description: "Maintain consistent formatting across your documentation", + }, + { + tag: "Completeness", + syntax: "Document all public APIs", + example: "// Document all parameters, return values, and exceptions\n// Include examples for complex functions", + description: "Ensure all public functions, classes, and modules are documented", + }, + { + tag: "Clarity", + syntax: "Write clear, concise descriptions", + example: "// Good: Calculates user age from birth date\n// Bad: Does age stuff", + description: "Use clear, descriptive language that explains purpose and behavior", + }, + { + tag: "Type Safety", + syntax: "Always specify types for parameters and returns", + example: + "// Always include types\n@param {string|null} name - User name or null\n@returns {Promise} Promise resolving to user object", + description: "Include detailed type information to improve code reliability", + }, + { + tag: "Examples", + syntax: "Include usage examples for complex functions", + example: + "/**\n * @example\n * // Basic usage\n * const result = myFunction('input');\n * \n * @example\n * // Advanced usage\n * const result = myFunction('input', { option: true });\n */", + description: "Provide practical examples showing how to use the code", + }, + ], + }, +} diff --git a/original-page.tsx b/original-page.tsx new file mode 100644 index 0000000..f43db3d --- /dev/null +++ b/original-page.tsx @@ -0,0 +1,980 @@ +"use client" + +import { useState, useEffect } from "react" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { ScrollArea } from "@/components/ui/scroll-area" +import { Separator } from "@/components/ui/separator" +import { BookOpen, Code, FileText, Hash, Info, Settings, Tag, Users, ChevronRight } from "lucide-react" + +const SyntaxHighlighter = ({ code, language = "javascript" }: { code: string; language?: string }) => { + const highlightCode = (text: string) => { + // Escape HTML first + let highlighted = text.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """) + + // Apply syntax highlighting with proper HTML structure + highlighted = highlighted + // JSDoc comments and tags + .replace(/(\/\*\*[\s\S]*?\*\/)/g, '$1') + .replace(/(@\w+)/g, '$1') + + // JavaScript keywords + .replace( + /\b(function|class|const|let|var|if|else|for|while|return|async|await|new|this|super|extends|implements|static|public|private|protected)\b/g, + '$1', + ) + + // Strings + .replace(/(["'`])((?:\\.|(?!\1)[^\\])*?)\1/g, '$1$2$1') + + // Numbers + .replace(/\b(\d+\.?\d*)\b/g, '$1') + + // Booleans and null/undefined + .replace(/\b(true|false|null|undefined)\b/g, '$1') + + // Types in curly braces + .replace(/\{([^}]+)\}/g, '{$1}') + + return highlighted + } + + return ( +
+      
+    
+ ) +} + +const sections = [ + { + id: "kitchen-sink", + title: "Kitchen Sink Example", + icon: BookOpen, + subsections: ["Complete Example"], + }, + { + id: "basic", + title: "Basic Tags", + icon: Tag, + subsections: ["@description", "@author", "@version", "@since", "@deprecated"], + }, + { + id: "parameters", + title: "Parameters & Returns", + icon: Code, + subsections: ["@param", "@returns", "@throws", "@yields"], + }, + { + id: "types", + title: "Type Definitions", + icon: Hash, + subsections: ["@typedef", "@property", "@enum", "@type"], + }, + { + id: "functions", + title: "Functions & Classes", + icon: Settings, + subsections: ["@function", "@class", "@constructor", "@method", "@static", "@override"], + }, + { + id: "modules", + title: "Modules & Namespaces", + icon: FileText, + subsections: ["@module", "@namespace", "@memberof", "@exports", "@requires"], + }, + { + id: "advanced", + title: "Advanced Tags", + icon: BookOpen, + subsections: ["@callback", "@event", "@fires", "@listens", "@mixes", "@abstract"], + }, + { + id: "objects", + title: "Objects & Interfaces", + icon: Hash, + subsections: ["@interface", "@implements", "@extends", "@mixin"], + }, + { + id: "documentation", + title: "Documentation Tags", + icon: Info, + subsections: ["@example", "@see", "@link", "@tutorial", "@todo"], + }, + { + id: "examples", + title: "Complete Examples", + icon: Info, + subsections: ["Function Example", "Class Example", "Module Example"], + }, + { + id: "best-practices", + title: "Best Practices", + icon: Users, + subsections: ["Consistency", "Completeness", "Clarity", "Type Safety", "Examples"], + }, +] + +const jsdocData = { + kitchenSink: { + title: "Kitchen Sink Example", + description: "A comprehensive example showcasing multiple JSDoc features in one code block", + items: [ + { + tag: "Complete Example", + syntax: "Complete function documentation", + example: `/** + * Advanced user management service with comprehensive JSDoc documentation + * @module UserService + * @description Handles user creation, authentication, and profile management + * @author Jane Smith + * @version 2.1.0 + * @since 1.0.0 + * @requires Database + * @requires EmailService + * @exports UserService + * @example + * // Initialize the service + * const userService = new UserService(database, emailService); + * + * // Create a new user + * const user = await userService.createUser({ + * name: 'John Doe', + * email: 'john@example.com', + * role: 'admin' + * }); + */ + +/** + * @typedef {Object} UserData + * @property {string} name - Full name of the user + * @property {string} email - Email address (must be unique) + * @property {('admin'|'user'|'guest')} [role='user'] - User role in the system + * @property {number} [age] - User age (optional) + * @property {UserPreferences} [preferences] - User preferences object + */ + +/** + * @typedef {Object} UserPreferences + * @property {boolean} emailNotifications - Whether to send email notifications + * @property {string} theme - UI theme preference + * @property {string} language - Preferred language code + */ + +/** + * @callback ValidationCallback + * @param {Error|null} error - Validation error if any + * @param {boolean} isValid - Whether the data is valid + */ + +/** + * User management service class + * @class UserService + * @implements {EventEmitter} + * @fires UserService#userCreated + * @fires UserService#userDeleted + * @example + * const service = new UserService(db, emailService); + * service.on('userCreated', (user) => { + * console.log('New user created:', user.name); + * }); + */ +class UserService extends EventEmitter { + /** + * Creates a new UserService instance + * @constructor + * @param {Database} database - Database connection instance + * @param {EmailService} emailService - Email service for notifications + * @throws {TypeError} When database or emailService is not provided + * @since 1.0.0 + */ + constructor(database, emailService) { + super(); + if (!database || !emailService) { + throw new TypeError('Database and EmailService are required'); + } + this.db = database; + this.emailService = emailService; + } + + /** + * Creates a new user in the system + * @async + * @method createUser + * @memberof UserService + * @param {UserData} userData - User information object + * @param {Object} [options={}] - Additional options + * @param {boolean} [options.sendWelcomeEmail=true] - Send welcome email + * @param {boolean} [options.validateEmail=true] - Validate email format + * @returns {Promise} Promise resolving to created user object + * @throws {ValidationError} When user data is invalid + * @throws {DuplicateEmailError} When email already exists + * @throws {DatabaseError} When database operation fails + * @fires UserService#userCreated + * @example + * // Basic user creation + * const user = await userService.createUser({ + * name: 'Alice Johnson', + * email: 'alice@example.com' + * }); + * + * @example + * // Advanced user creation with options + * const adminUser = await userService.createUser({ + * name: 'Bob Admin', + * email: 'bob@example.com', + * role: 'admin', + * preferences: { + * emailNotifications: false, + * theme: 'dark', + * language: 'en' + * } + * }, { + * sendWelcomeEmail: false, + * validateEmail: true + * }); + * @since 1.0.0 + * @todo Add support for bulk user creation + * @todo Implement user avatar upload + */ + async createUser(userData, options = {}) { + // Implementation here... + + /** + * User created event + * @event UserService#userCreated + * @type {Object} + * @property {User} user - The created user object + * @property {Date} timestamp - When the user was created + */ + this.emit('userCreated', { user: newUser, timestamp: new Date() }); + + return newUser; + } + + /** + * Validates user email address + * @static + * @method validateEmail + * @param {string} email - Email address to validate + * @param {ValidationCallback} callback - Callback function for validation result + * @returns {boolean} True if email format is valid + * @example + * // Synchronous validation + * const isValid = UserService.validateEmail('test@example.com'); + * + * // Asynchronous validation with callback + * UserService.validateEmail('test@example.com', (error, isValid) => { + * if (error) { + * console.error('Validation error:', error); + * } else { + * console.log('Email is valid:', isValid); + * } + * }); + * @since 1.2.0 + */ + static validateEmail(email, callback) { + // Implementation here... + } + + /** + * @deprecated Since version 2.0.0. Use {@link UserService#createUser} instead. + * @method addUser + * @param {UserData} userData - User data + * @returns {Promise} Created user + * @see {@link UserService#createUser} + */ + async addUser(userData) { + console.warn('addUser is deprecated. Use createUser instead.'); + return this.createUser(userData); + } +} + +/** + * @namespace UserService.Utils + * @description Utility functions for user management + */ +UserService.Utils = { + /** + * Formats user display name + * @function formatDisplayName + * @memberof UserService.Utils + * @param {string} firstName - User's first name + * @param {string} lastName - User's last name + * @returns {string} Formatted display name + * @example + * const displayName = UserService.Utils.formatDisplayName('John', 'Doe'); + * console.log(displayName); // "John D." + */ + formatDisplayName(firstName, lastName) { + return \`\${firstName} \${lastName.charAt(0)}.\`; + } +}; + +module.exports = UserService;`, + description: "Complete function documentation with all common tags", + }, + ], + }, + basic: { + title: "Basic Tags", + description: "Essential JSDoc tags for documenting your code", + items: [ + { + tag: "@description", + syntax: "@description {string} Description text", + example: + "/**\n * @description Calculates the sum of two numbers\n * @param {number} a - First number\n * @param {number} b - Second number\n * @returns {number} The sum\n */\nfunction add(a, b) {\n return a + b;\n}", + description: "Provides a description of the function, class, or variable", + }, + { + tag: "@author", + syntax: "@author {string} Author name ", + example: "/**\n * @author John Doe \n * @author Jane Smith \n */", + description: "Specifies the author(s) of the code", + }, + { + tag: "@version", + syntax: "@version {string} Version number", + example: "/**\n * @version 1.2.0\n * @since 1.0.0\n */", + description: "Indicates the current version of the code", + }, + { + tag: "@since", + syntax: "@since {string} Version when added", + example: "/**\n * @since 1.0.0\n * @description Added in the initial release\n */", + description: "Specifies when the feature was first added", + }, + { + tag: "@deprecated", + syntax: "@deprecated {string} Deprecation message", + example: + "/**\n * @deprecated Since version 2.0.0. Use newFunction() instead.\n * @see {@link newFunction}\n */\nfunction oldFunction() {\n // deprecated implementation\n}", + description: "Marks code as deprecated with migration guidance", + }, + ], + }, + parameters: { + title: "Parameters & Returns", + description: "Document function parameters, return values, and exceptions", + items: [ + { + tag: "@param", + syntax: "@param {type} name Description", + example: + "/**\n * @param {number} x - The first number\n * @param {number} y - The second number\n * @param {Object} [options] - Optional configuration\n * @param {boolean} [options.strict=false] - Use strict mode\n */\nfunction calculate(x, y, options = {}) {\n // implementation\n}", + description: "Documents function parameters with types and descriptions", + }, + { + tag: "@returns", + syntax: "@returns {type} Description", + example: + "/**\n * @param {number[]} numbers - Array of numbers\n * @returns {number} The sum of all numbers\n * @returns {null} Returns null if array is empty\n */\nfunction sum(numbers) {\n if (numbers.length === 0) return null;\n return numbers.reduce((a, b) => a + b, 0);\n}", + description: "Documents the return value and its type", + }, + { + tag: "@throws", + syntax: "@throws {ErrorType} Description", + example: + "/**\n * @param {string} email - User email address\n * @throws {TypeError} When email is not a string\n * @throws {Error} When email format is invalid\n */\nfunction validateEmail(email) {\n if (typeof email !== 'string') {\n throw new TypeError('Email must be a string');\n }\n if (!email.includes('@')) {\n throw new Error('Invalid email format');\n }\n}", + description: "Documents exceptions that may be thrown", + }, + { + tag: "@yields", + syntax: "@yields {type} Description", + example: + "/**\n * @generator\n * @yields {number} The next number in the Fibonacci sequence\n * @returns {Generator}\n */\nfunction* fibonacci() {\n let a = 0, b = 1;\n while (true) {\n yield a;\n [a, b] = [b, a + b];\n }\n}", + description: "Documents what a generator function yields", + }, + ], + }, + types: { + title: "Type Definitions", + description: "Define and document custom types and interfaces", + items: [ + { + tag: "@typedef", + syntax: "@typedef {Object} TypeName", + example: "/**\n * @typedef {Object} User\n * @property {string} name\n * @property {number} age\n */", + description: "Defines a custom type", + }, + { + tag: "@property", + syntax: "@property {type} name Description", + example: "/**\n * @property {string} email - User email address\n */", + description: "Documents object properties", + }, + { + tag: "@enum", + syntax: "@enum {type}", + example: '/**\n * @enum {string}\n */\nconst Status = {\n PENDING: "pending",\n COMPLETE: "complete"\n}', + description: "Documents enumeration values", + }, + { + tag: "@type", + syntax: "@type {type}", + example: "/**\n * @type {string|number}\n */\nlet value;", + description: "Specifies the type of a variable", + }, + ], + }, + functions: { + title: "Functions & Classes", + description: "Document functions, classes, constructors, and methods", + items: [ + { + tag: "@function", + syntax: "@function", + example: "/**\n * @function calculateTotal\n * @description Calculates order total\n */", + description: "Explicitly marks something as a function", + }, + { + tag: "@class", + syntax: "@class", + example: "/**\n * @class\n * @description Represents a user account\n */", + description: "Documents a class", + }, + { + tag: "@constructor", + syntax: "@constructor", + example: "/**\n * @constructor\n * @param {string} name - User name\n */", + description: "Documents a constructor function", + }, + { + tag: "@method", + syntax: "@method", + example: "/**\n * @method getName\n * @returns {string} The user name\n */", + description: "Documents a method", + }, + { + tag: "@static", + syntax: "@static", + example: "/**\n * @static\n * @method createUser\n */", + description: "Indicates a static method or property", + }, + { + tag: "@override", + syntax: "@override", + example: "/**\n * @override\n * @method toString\n */", + description: "Indicates method overrides parent method", + }, + ], + }, + modules: { + title: "Modules & Namespaces", + description: "Organize code with modules, namespaces, and membership", + items: [ + { + tag: "@module", + syntax: "@module ModuleName", + example: "/**\n * @module UserUtils\n * @description Utilities for user management\n */", + description: "Documents a module", + }, + { + tag: "@namespace", + syntax: "@namespace NamespaceName", + example: "/**\n * @namespace MyApp.Utils\n */", + description: "Documents a namespace", + }, + { + tag: "@memberof", + syntax: "@memberof ParentName", + example: "/**\n * @memberof MyApp.Utils\n * @function formatName\n */", + description: "Indicates membership in a parent", + }, + { + tag: "@exports", + syntax: "@exports ModuleName", + example: "/**\n * @exports UserService\n */", + description: "Documents what a module exports", + }, + { + tag: "@requires", + syntax: "@requires ModuleName", + example: "/**\n * @requires lodash\n */", + description: "Documents module dependencies", + }, + ], + }, + advanced: { + title: "Advanced Tags", + description: "Advanced JSDoc features for complex documentation needs", + items: [ + { + tag: "@callback", + syntax: "@callback CallbackName", + example: "/**\n * @callback RequestCallback\n * @param {Error} error\n * @param {Object} response\n */", + description: "Documents a callback function type", + }, + { + tag: "@event", + syntax: "@event EventName", + example: "/**\n * @event MyClass#dataLoaded\n * @type {Object}\n */", + description: "Documents an event", + }, + { + tag: "@fires", + syntax: "@fires EventName", + example: "/**\n * @fires MyClass#dataLoaded\n */", + description: "Indicates function fires an event", + }, + { + tag: "@listens", + syntax: "@listens EventName", + example: "/**\n * @listens MyClass#dataLoaded\n */", + description: "Indicates function listens to an event", + }, + { + tag: "@mixes", + syntax: "@mixes MixinName", + example: "/**\n * @mixes EventEmitter\n */", + description: "Documents that class mixes in another", + }, + { + tag: "@abstract", + syntax: "@abstract", + example: "/**\n * @abstract\n * @method process\n */", + description: "Indicates abstract method or class", + }, + ], + }, + examples: { + title: "Complete Examples", + description: "Real-world JSDoc documentation examples", + items: [ + { + tag: "Function Example", + syntax: "Complete function documentation", + example: `/** + * Calculates the area of a rectangle + * @function calculateArea + * @param {number} width - The width of the rectangle + * @param {number} height - The height of the rectangle + * @returns {number} The area of the rectangle + * @throws {TypeError} When width or height is not a number + * @example + * // Calculate area of 5x3 rectangle + * const area = calculateArea(5, 3); + * console.log(area); // 15 + * @since 1.0.0 + * @author Jane Smith + */ +function calculateArea(width, height) { + if (typeof width !== 'number' || typeof height !== 'number') { + throw new TypeError('Width and height must be numbers'); + } + return width * height; +}`, + description: "Complete function documentation with all common tags", + }, + { + tag: "Class Example", + syntax: "Complete class documentation", + example: `/** + * Represents a user in the system + * @class User + * @param {string} name - The user's name + * @param {string} email - The user's email address + * @example + * const user = new User('John Doe', 'john@example.com'); + * console.log(user.getName()); // 'John Doe' + */ +class User { + /** + * Create a user + * @constructor + * @param {string} name - The user's name + * @param {string} email - The user's email address + */ + constructor(name, email) { + this.name = name; + this.email = email; + } + + /** + * Get the user's name + * @method getName + * @returns {string} The user's name + */ + getName() { + return this.name; + } +}`, + description: "Complete class documentation with constructor and methods", + }, + { + tag: "Module Example", + syntax: "Complete module documentation", + example: `/** + * User management module + * @module UserManagement + * @description Handles user creation and deletion + * @requires Database + * @requires EmailService + * @exports createUser + * @exports deleteUser + * @example + * // Create a user + * const user = createUser('John Doe', 'john@example.com'); + * console.log(user); + * + * // Delete a user + * deleteUser(user.id); + */ +const Database = require('./Database'); +const EmailService = require('./EmailService'); + +function createUser(name, email) { + // Implementation here +} + +function deleteUser(userId) { + // Implementation here +} + +module.exports = { + createUser, + deleteUser +};`, + description: "Complete module documentation with dependencies and exports", + }, + ], + }, + "best-practices": { + title: "Best Practices", + description: "Guidelines for writing effective JSDoc documentation", + items: [ + { + tag: "Consistency", + syntax: "Use consistent formatting and style", + example: + "// Always use the same format for similar tags\n// Good: @param {string} name - User name\n// Good: @param {number} age - User age", + description: "Maintain consistent formatting across your documentation", + }, + { + tag: "Completeness", + syntax: "Document all public APIs", + example: "// Document all parameters, return values, and exceptions\n// Include examples for complex functions", + description: "Ensure all public functions, classes, and modules are documented", + }, + { + tag: "Clarity", + syntax: "Write clear, concise descriptions", + example: "// Good: Calculates user age from birth date\n// Bad: Does age stuff", + description: "Use clear, descriptive language that explains purpose and behavior", + }, + { + tag: "Type Safety", + syntax: "Always specify types for parameters and returns", + example: + "// Always include types\n@param {string|null} name - User name or null\n@returns {Promise} Promise resolving to user object", + description: "Include detailed type information to improve code reliability", + }, + { + tag: "Examples", + syntax: "Include usage examples for complex functions", + example: + "/**\n * @example\n * // Basic usage\n * const result = myFunction('input');\n * \n * @example\n * // Advanced usage\n * const result = myFunction('input', { option: true });\n */", + description: "Provide practical examples showing how to use the code", + }, + ], + }, + objects: { + title: "Objects & Interfaces", + description: "Document object structures, interfaces, and inheritance", + items: [ + { + tag: "@interface", + syntax: "@interface InterfaceName", + example: "/**\n * @interface Drawable\n * @description Interface for drawable objects\n */", + description: "Documents an interface that classes can implement", + }, + { + tag: "@implements", + syntax: "@implements {InterfaceName}", + example: "/**\n * @class Circle\n * @implements {Drawable}\n */", + description: "Indicates that a class implements an interface", + }, + { + tag: "@extends", + syntax: "@extends ParentClass", + example: "/**\n * @class ColoredCircle\n * @extends Circle\n */", + description: "Documents class inheritance", + }, + { + tag: "@mixin", + syntax: "@mixin MixinName", + example: "/**\n * @mixin EventEmitter\n * @description Adds event handling capabilities\n */", + description: "Documents a mixin that can be mixed into classes", + }, + ], + }, + documentation: { + title: "Documentation Tags", + description: "Tags for linking, examples, and additional documentation", + items: [ + { + tag: "@example", + syntax: "@example\n// Example code here", + example: + "/**\n * @example\n * // Basic usage\n * const result = myFunction('test');\n * console.log(result);\n */", + description: "Provides usage examples", + }, + { + tag: "@see", + syntax: "@see {reference}", + example: "/**\n * @see {@link MyClass#method}\n * @see https://example.com/docs\n */", + description: "References related documentation or code", + }, + { + tag: "@link", + syntax: "{@link reference}", + example: "/**\n * Uses {@link MyClass} for processing\n */", + description: "Creates inline links to other documentation", + }, + { + tag: "@tutorial", + syntax: "@tutorial TutorialName", + example: "/**\n * @tutorial getting-started\n */", + description: "Links to a tutorial document", + }, + { + tag: "@todo", + syntax: "@todo Description of what needs to be done", + example: "/**\n * @todo Add input validation\n * @todo Optimize performance\n */", + description: "Documents future improvements or fixes needed", + }, + ], + }, +} + +export default function JSDocCheatsheet() { + const [activeSection, setActiveSection] = useState("kitchen-sink") + const [expandedSections, setExpandedSections] = useState(["kitchen-sink"]) + const [isScrolling, setIsScrolling] = useState(false) + + const toggleSection = (sectionId: string) => { + setExpandedSections((prev) => + prev.includes(sectionId) ? prev.filter((id) => id !== sectionId) : [...prev, sectionId], + ) + } + + const scrollToTag = (tag: string) => { + const element = document.getElementById(tag.replace("@", "").replace(/[^a-zA-Z0-9]/g, "-")) + if (element) { + setIsScrolling(true) + element.scrollIntoView({ behavior: "smooth", block: "start" }) + + // Reset scrolling state after animation + setTimeout(() => setIsScrolling(false), 1000) + + // Highlight the element briefly + element.classList.add("ring-2", "ring-primary", "ring-opacity-50") + setTimeout(() => { + element.classList.remove("ring-2", "ring-primary", "ring-opacity-50") + }, 2000) + } + } + + useEffect(() => { + const handleScroll = () => { + if (isScrolling) return // Don't update during programmatic scrolling + + const sections = Object.keys(jsdocData) + const scrollPosition = window.scrollY + 200 + + for (const sectionId of sections) { + const element = document.getElementById(`section-${sectionId}`) + if (element) { + const { offsetTop, offsetHeight } = element + if (scrollPosition >= offsetTop && scrollPosition < offsetTop + offsetHeight) { + setActiveSection(sectionId) + break + } + } + } + } + + window.addEventListener("scroll", handleScroll) + return () => window.removeEventListener("scroll", handleScroll) + }, [isScrolling]) + + return ( +
+ {/* Header */} +
+
+
+

JSDoc Cheatsheet

+

Complete Reference for JavaScript Documentation

+
+
+
+ +
+
+ {/* Sidebar Navigation */} + + + {/* Main Content */} +
+
+ {/* Kitchen Sink Example */} + + + + + Kitchen Sink Example + + + + A comprehensive example showcasing multiple JSDoc features in one code block + + + + + + + + {/* Section Header */} +
+

+ {jsdocData[activeSection as keyof typeof jsdocData]?.title || "Section"} +

+

+ {jsdocData[activeSection as keyof typeof jsdocData]?.description || "Documentation section"} +

+
+ + {/* Content Cards */} +
+ {jsdocData[activeSection as keyof typeof jsdocData]?.items.map((item, index) => ( + + +
+ + + {item.tag} + + +
+ {item.description} +
+ + {/* Syntax */} +
+

Syntax:

+ + {item.syntax} + +
+ + + + {/* Example with Syntax Highlighting */} +
+

Example:

+ +
+
+
+ )) || []} +
+
+
+
+
+ + {/* Footer */} + +
+ ) +} \ No newline at end of file diff --git a/package.json b/package.json index 097ad84..693f308 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "react-dom": "^18", "react-hook-form": "^7.60.0", "react-resizable-panels": "^2.1.7", + "react-syntax-highlighter": "^15.6.6", "recharts": "2.15.4", "sonner": "^1.7.4", "tailwind-merge": "^2.5.5", @@ -66,6 +67,7 @@ "@types/node": "^22", "@types/react": "^18", "@types/react-dom": "^18", + "@types/react-syntax-highlighter": "^15.5.13", "postcss": "^8.5", "tailwindcss": "^4.1.9", "tw-animate-css": "1.3.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2fc6f16..574a5e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,6 +143,9 @@ importers: react-resizable-panels: specifier: ^2.1.7 version: 2.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0) + react-syntax-highlighter: + specifier: ^15.6.6 + version: 15.6.6(react@18.0.0) recharts: specifier: 2.15.4 version: 2.15.4(react-dom@18.0.0(react@18.0.0))(react@18.0.0) @@ -174,6 +177,9 @@ importers: '@types/react-dom': specifier: ^18 version: 18.0.0 + '@types/react-syntax-highlighter': + specifier: ^15.5.13 + version: 15.5.13 postcss: specifier: ^8.5 version: 8.5.0 @@ -1121,6 +1127,9 @@ packages: '@types/d3-timer@3.0.2': resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + '@types/node@22.0.0': resolution: {integrity: sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==} @@ -1130,12 +1139,18 @@ packages: '@types/react-dom@18.0.0': resolution: {integrity: sha512-49897Y0UiCGmxZqpC8Blrf6meL8QUla6eb+BBhn69dTXlmuOlzkfr7HHY/O8J25e1lTUMs+YYxSlVDAaGHCOLg==} + '@types/react-syntax-highlighter@15.5.13': + resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} + '@types/react@18.0.0': resolution: {integrity: sha512-7+K7zEQYu7NzOwQGLR91KwWXXDzmTFODRVizJyIALf6RfLv2GDpqpknX64pvRVILXCpXi7O/pua8NGk44dLvJw==} '@types/scheduler@0.26.0': resolution: {integrity: sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + '@vercel/analytics@1.5.0': resolution: {integrity: sha512-MYsBzfPki4gthY5HnYN7jgInhAZ7Ac1cYDoRWFomwGHWEX7odTEzbtg9kf/QSo7XEsEAqlQugA6gJ2WS2DEa3g==} peerDependencies: @@ -1189,6 +1204,15 @@ packages: caniuse-lite@1.0.30001743: resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==} + character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + + character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + + character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + chownr@3.0.0: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} @@ -1209,6 +1233,9 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc + comma-separated-tokens@1.0.8: + resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -1306,6 +1333,13 @@ packages: resolution: {integrity: sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==} engines: {node: '>=6.0.0'} + fault@1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -1321,6 +1355,18 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + hast-util-parse-selector@2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + + hastscript@6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + highlightjs-vue@1.0.0: + resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} + input-otp@1.4.1: resolution: {integrity: sha512-+yvpmKYKHi9jIGngxagY9oWiiblPB7+nEO75F2l2o4vs+6vpPZZmUl4tBNYuTCvQjhvEIbdNeJu70bhfYP2nbw==} peerDependencies: @@ -1331,6 +1377,18 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} + is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + + is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + + is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + + is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + jiti@2.5.1: resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} hasBin: true @@ -1409,6 +1467,9 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lowlight@1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + lucide-react@0.454.0: resolution: {integrity: sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==} peerDependencies: @@ -1470,6 +1531,9 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1484,9 +1548,20 @@ packages: resolution: {integrity: sha512-27VKOqrYfPncKA2NrFOVhP5MGAfHKLYn/Q0mz9cNQyRAKYi3VNHwYU2qKKqPCqgBmeeJ0uAFB56NumXZ5ZReXg==} engines: {node: ^10 || ^12 || >=14} + prismjs@1.27.0: + resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} + engines: {node: '>=6'} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + property-information@5.6.0: + resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + react-day-picker@9.8.0: resolution: {integrity: sha512-E0yhhg7R+pdgbl/2toTb0xBhsEAtmAx1l7qjIWYfcxOy8w4rTSVfbtBoSzVVhPwKP/5E9iL38LivzoE3AQDhCQ==} engines: {node: '>=18'} @@ -1552,6 +1627,11 @@ packages: '@types/react': optional: true + react-syntax-highlighter@15.6.6: + resolution: {integrity: sha512-DgXrc+AZF47+HvAPEmn7Ua/1p10jNoVZVI/LoPiYdtY+OM+/nG5yefLHKJwdKqY1adMuHFbeyBaG9j64ML7vTw==} + peerDependencies: + react: '>= 0.14.0' + react-transition-group@4.4.5: resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: @@ -1572,6 +1652,9 @@ packages: react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + refractor@3.6.0: + resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + scheduler@0.21.0: resolution: {integrity: sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==} @@ -1585,6 +1668,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + space-separated-tokens@1.1.5: + resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -1678,6 +1764,10 @@ packages: victory-vendor@36.9.2: resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + yallist@5.0.0: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} @@ -2593,6 +2683,10 @@ snapshots: '@types/d3-timer@3.0.2': {} + '@types/hast@2.3.10': + dependencies: + '@types/unist': 2.0.11 + '@types/node@22.0.0': dependencies: undici-types: 6.11.1 @@ -2603,6 +2697,10 @@ snapshots: dependencies: '@types/react': 18.0.0 + '@types/react-syntax-highlighter@15.5.13': + dependencies: + '@types/react': 18.0.0 + '@types/react@18.0.0': dependencies: '@types/prop-types': 15.7.15 @@ -2611,6 +2709,8 @@ snapshots: '@types/scheduler@0.26.0': {} + '@types/unist@2.0.11': {} + '@vercel/analytics@1.5.0(next@14.2.16(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0)': optionalDependencies: next: 14.2.16(react-dom@18.0.0(react@18.0.0))(react@18.0.0) @@ -2646,6 +2746,12 @@ snapshots: caniuse-lite@1.0.30001743: {} + character-entities-legacy@1.1.4: {} + + character-entities@1.2.4: {} + + character-reference-invalid@1.1.4: {} + chownr@3.0.0: {} class-variance-authority@0.7.1: @@ -2668,6 +2774,8 @@ snapshots: - '@types/react' - '@types/react-dom' + comma-separated-tokens@1.0.8: {} + csstype@3.1.3: {} d3-array@3.2.4: @@ -2748,6 +2856,12 @@ snapshots: fast-equals@5.2.2: {} + fault@1.0.4: + dependencies: + format: 0.2.2 + + format@0.2.2: {} + fraction.js@4.3.7: {} geist@1.5.1(next@14.2.16(react-dom@18.0.0(react@18.0.0))(react@18.0.0)): @@ -2758,6 +2872,20 @@ snapshots: graceful-fs@4.2.11: {} + hast-util-parse-selector@2.2.5: {} + + hastscript@6.0.0: + dependencies: + '@types/hast': 2.3.10 + comma-separated-tokens: 1.0.8 + hast-util-parse-selector: 2.2.5 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + + highlight.js@10.7.3: {} + + highlightjs-vue@1.0.0: {} + input-otp@1.4.1(react-dom@18.0.0(react@18.0.0))(react@18.0.0): dependencies: react: 18.0.0 @@ -2765,6 +2893,17 @@ snapshots: internmap@2.0.3: {} + is-alphabetical@1.0.4: {} + + is-alphanumerical@1.0.4: + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + + is-decimal@1.0.4: {} + + is-hexadecimal@1.0.4: {} + jiti@2.5.1: {} js-tokens@4.0.0: {} @@ -2820,6 +2959,11 @@ snapshots: dependencies: js-tokens: 4.0.0 + lowlight@1.20.0: + dependencies: + fault: 1.0.4 + highlight.js: 10.7.3 + lucide-react@0.454.0(react@18.0.0): dependencies: react: 18.0.0 @@ -2874,6 +3018,15 @@ snapshots: object-assign@4.1.1: {} + parse-entities@2.0.0: + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + picocolors@1.1.1: {} postcss-value-parser@4.2.0: {} @@ -2890,12 +3043,20 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + prismjs@1.27.0: {} + + prismjs@1.30.0: {} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 + property-information@5.6.0: + dependencies: + xtend: 4.0.2 + react-day-picker@9.8.0(react@18.0.0): dependencies: '@date-fns/tz': 1.2.0 @@ -2957,6 +3118,16 @@ snapshots: optionalDependencies: '@types/react': 18.0.0 + react-syntax-highlighter@15.6.6(react@18.0.0): + dependencies: + '@babel/runtime': 7.28.4 + highlight.js: 10.7.3 + highlightjs-vue: 1.0.0 + lowlight: 1.20.0 + prismjs: 1.30.0 + react: 18.0.0 + refractor: 3.6.0 + react-transition-group@4.4.5(react-dom@18.0.0(react@18.0.0))(react@18.0.0): dependencies: '@babel/runtime': 7.28.4 @@ -2987,6 +3158,12 @@ snapshots: tiny-invariant: 1.3.3 victory-vendor: 36.9.2 + refractor@3.6.0: + dependencies: + hastscript: 6.0.0 + parse-entities: 2.0.0 + prismjs: 1.27.0 + scheduler@0.21.0: dependencies: loose-envify: 1.4.0 @@ -2998,6 +3175,8 @@ snapshots: source-map-js@1.2.1: {} + space-separated-tokens@1.1.5: {} + streamsearch@1.1.0: {} styled-jsx@5.1.1(react@18.0.0): @@ -3085,6 +3264,8 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 + xtend@4.0.2: {} + yallist@5.0.0: {} zod@3.25.67: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..fd050a4 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +onlyBuiltDependencies: + - '@tailwindcss/oxide'