From 38f5461b5458f8037fbb88fbb9d75ad259014152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20G=C3=B3mez?= Date: Thu, 11 Dec 2025 13:00:25 +0100 Subject: [PATCH] chore: setup eslint and prettier with code formatting --- .prettierignore | 5 + .prettierrc | 10 ++ eslint.config.js | 41 +++++++ package-lock.json | 105 +++++++++++++++++- package.json | 6 + src/components/EncodeViewer.tsx | 2 +- src/components/nostr/RichText.tsx | 17 ++- src/components/nostr/kinds/Kind0Renderer.tsx | 2 +- .../nostr/kinds/Kind1063Renderer.tsx | 2 +- src/components/nostr/kinds/Kind20Renderer.tsx | 2 +- src/components/nostr/kinds/Kind21Renderer.tsx | 2 +- src/components/nostr/kinds/Kind22Renderer.tsx | 2 +- .../nostr/kinds/Kind30023DetailRenderer.tsx | 20 ++-- .../nostr/kinds/Kind30023Renderer.tsx | 2 +- src/components/nostr/kinds/Kind3Renderer.tsx | 2 +- src/components/nostr/kinds/Kind6Renderer.tsx | 2 +- src/components/nostr/kinds/Kind7Renderer.tsx | 2 +- .../nostr/kinds/Kind9735Renderer.tsx | 18 +-- .../nostr/kinds/Kind9802Renderer.tsx | 2 +- src/components/ui/avatar.tsx | 24 ++-- src/components/ui/button.tsx | 29 ++--- src/components/ui/dialog.tsx | 52 ++++----- src/components/ui/input.tsx | 16 +-- src/components/ui/scroll-area.tsx | 18 +-- src/core/logic.ts | 2 +- src/hooks/useCopy.ts | 2 +- src/hooks/useLocale.ts | 3 +- src/hooks/useProfile.ts | 6 +- src/hooks/useRelayInfo.ts | 4 +- src/lib/nip-kinds.ts | 11 +- src/lib/nip05.ts | 7 +- src/lib/open-parser.ts | 6 +- src/lib/req-parser.ts | 10 +- vite.config.ts | 14 +-- 34 files changed, 317 insertions(+), 131 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 eslint.config.js diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..06accd0 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +dist +node_modules +.claude +*.md +package-lock.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..8daa855 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "trailingComma": "all", + "singleQuote": false, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "arrowParens": "always", + "endOfLine": "lf" +} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..866cec9 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,41 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import prettier from 'eslint-plugin-prettier' +import prettierConfig from 'eslint-config-prettier' + +export default tseslint.config( + { ignores: ['dist', 'node_modules', '.claude'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + 'prettier': prettier, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + 'prettier/prettier': 'error', + }, + }, + prettierConfig, +) diff --git a/package-lock.json b/package-lock.json index 0cde85e..4d5b3db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,10 +47,13 @@ "@vitejs/plugin-react": "^4.3.4", "autoprefixer": "^10.4.20", "eslint": "^9.17.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.16", "globals": "^15.14.0", "postcss": "^8.4.49", + "prettier": "^3.7.4", "tailwindcss": "^3.4.17", "typescript": "~5.6.2", "typescript-eslint": "^8.18.2", @@ -1549,6 +1552,19 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, "node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", @@ -4734,6 +4750,53 @@ } } }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react-hooks": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", @@ -4886,6 +4949,13 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -7255,9 +7325,9 @@ } }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", "bin": { @@ -7270,6 +7340,19 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/proc-log": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", @@ -8227,6 +8310,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, "node_modules/tailwind-merge": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", diff --git a/package.json b/package.json index aa22bcc..ab120e7 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,9 @@ "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css}\"", + "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css}\"", "preview": "vite preview" }, "dependencies": { @@ -49,10 +52,13 @@ "@vitejs/plugin-react": "^4.3.4", "autoprefixer": "^10.4.20", "eslint": "^9.17.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.16", "globals": "^15.14.0", "postcss": "^8.4.49", + "prettier": "^3.7.4", "tailwindcss": "^3.4.17", "typescript": "~5.6.2", "typescript-eslint": "^8.18.2", diff --git a/src/components/EncodeViewer.tsx b/src/components/EncodeViewer.tsx index 0b2d393..5af3a1c 100644 --- a/src/components/EncodeViewer.tsx +++ b/src/components/EncodeViewer.tsx @@ -40,7 +40,7 @@ export default function EncodeViewer({ args }: EncodeViewerProps) { ...parsed, relays: relays.length > 0 ? relays : undefined, }); - } catch (err) { + } catch { return null; } }, [parsed, relays]); diff --git a/src/components/nostr/RichText.tsx b/src/components/nostr/RichText.tsx index abe7c84..bc3076d 100644 --- a/src/components/nostr/RichText.tsx +++ b/src/components/nostr/RichText.tsx @@ -46,6 +46,18 @@ export function RichText({ className = "", depth = 1, }: RichTextProps) { + // Call hook unconditionally - it will handle undefined/null + const trimmedEvent = event + ? { + ...event, + content: event.content.trim(), + } + : undefined; + const renderedContent = useRenderedContent( + trimmedEvent as NostrEvent, + contentComponents, + ); + // If plain content is provided, just render it if (content && !event) { const lines = content.trim().split("\n"); @@ -62,11 +74,6 @@ export function RichText({ // Render event content with rich formatting if (event) { - const trimmedEvent = { - ...event, - content: event.content.trim(), - }; - const renderedContent = useRenderedContent(trimmedEvent, contentComponents); return (
diff --git a/src/components/nostr/kinds/Kind0Renderer.tsx b/src/components/nostr/kinds/Kind0Renderer.tsx index 779ffd5..c8a6c11 100644 --- a/src/components/nostr/kinds/Kind0Renderer.tsx +++ b/src/components/nostr/kinds/Kind0Renderer.tsx @@ -16,7 +16,7 @@ export function Kind0Renderer({ event }: BaseEventProps) { const website = profile?.website; return ( - +
{/* Profile Info */}
diff --git a/src/components/nostr/kinds/Kind1063Renderer.tsx b/src/components/nostr/kinds/Kind1063Renderer.tsx index 40f6e2d..463fdae 100644 --- a/src/components/nostr/kinds/Kind1063Renderer.tsx +++ b/src/components/nostr/kinds/Kind1063Renderer.tsx @@ -29,7 +29,7 @@ export function Kind1063Renderer({ event }: BaseEventProps) { event.tags.find((t) => t[0] === "summary")?.[1] || event.content; return ( - +
{/* File preview */} {metadata.url && (isImage || isVideo || isAudio) ? ( diff --git a/src/components/nostr/kinds/Kind20Renderer.tsx b/src/components/nostr/kinds/Kind20Renderer.tsx index ce1bdd7..746d632 100644 --- a/src/components/nostr/kinds/Kind20Renderer.tsx +++ b/src/components/nostr/kinds/Kind20Renderer.tsx @@ -15,7 +15,7 @@ export function Kind20Renderer({ event }: BaseEventProps) { const title = event.tags.find((t) => t[0] === "title")?.[1]; return ( - +
{/* Title if present */} {title && ( diff --git a/src/components/nostr/kinds/Kind21Renderer.tsx b/src/components/nostr/kinds/Kind21Renderer.tsx index 508293e..6a885e5 100644 --- a/src/components/nostr/kinds/Kind21Renderer.tsx +++ b/src/components/nostr/kinds/Kind21Renderer.tsx @@ -15,7 +15,7 @@ export function Kind21Renderer({ event }: BaseEventProps) { const title = event.tags.find((t) => t[0] === "title")?.[1]; return ( - +
{/* Title if present */} {title &&

{title}

} diff --git a/src/components/nostr/kinds/Kind22Renderer.tsx b/src/components/nostr/kinds/Kind22Renderer.tsx index f839e0c..7806562 100644 --- a/src/components/nostr/kinds/Kind22Renderer.tsx +++ b/src/components/nostr/kinds/Kind22Renderer.tsx @@ -15,7 +15,7 @@ export function Kind22Renderer({ event }: BaseEventProps) { const title = event.tags.find((t) => t[0] === "title")?.[1]; return ( - +
{/* Title if present */} {title &&

{title}

} diff --git a/src/components/nostr/kinds/Kind30023DetailRenderer.tsx b/src/components/nostr/kinds/Kind30023DetailRenderer.tsx index 9db891c..9c53386 100644 --- a/src/components/nostr/kinds/Kind30023DetailRenderer.tsx +++ b/src/components/nostr/kinds/Kind30023DetailRenderer.tsx @@ -182,7 +182,7 @@ export function Kind30023DetailRenderer({ event }: { event: NostrEvent }) { /> ) : null, // Handle nostr: links - a: ({ node, href, children, ...props }) => { + a: ({ href, children, ...props }) => { if (!href) return null; // Render nostr: mentions inline @@ -204,43 +204,43 @@ export function Kind30023DetailRenderer({ event }: { event: NostrEvent }) { ); }, // Make pre elements display inline - pre: ({ node, children, ...props }) => ( + pre: ({ children, ...props }) => ( {children} ), // Style adjustments for dark theme - h1: ({ node, ...props }) => ( + h1: ({ ...props }) => (

), - h2: ({ node, ...props }) => ( + h2: ({ ...props }) => (

), - h3: ({ node, ...props }) => ( + h3: ({ ...props }) => (

), - p: ({ node, ...props }) => ( + p: ({ ...props }) => (

), - code: ({ node, inline, ...props }: any) => ( + code: ({ ...props }: any) => ( ), - blockquote: ({ node, ...props }) => ( + blockquote: ({ ...props }) => (

), - ul: ({ node, ...props }) => ( + ul: ({ ...props }) => (
    ), - ol: ({ node, ...props }) => ( + ol: ({ ...props }) => (
      getArticleSummary(event), [event]); return ( - +
      {/* Title */} {title && ( diff --git a/src/components/nostr/kinds/Kind3Renderer.tsx b/src/components/nostr/kinds/Kind3Renderer.tsx index 0558c75..fbe1302 100644 --- a/src/components/nostr/kinds/Kind3Renderer.tsx +++ b/src/components/nostr/kinds/Kind3Renderer.tsx @@ -20,7 +20,7 @@ export function Kind3Renderer({ event }: BaseEventProps) { : false; return ( - +
      diff --git a/src/components/nostr/kinds/Kind6Renderer.tsx b/src/components/nostr/kinds/Kind6Renderer.tsx index 8d3d642..9c11a7e 100644 --- a/src/components/nostr/kinds/Kind6Renderer.tsx +++ b/src/components/nostr/kinds/Kind6Renderer.tsx @@ -15,7 +15,7 @@ export function Kind6Renderer({ event }: BaseEventProps) { const repostedEventId = eTag?.[1]; return ( - +
      diff --git a/src/components/nostr/kinds/Kind7Renderer.tsx b/src/components/nostr/kinds/Kind7Renderer.tsx index 252a7b7..fefda2b 100644 --- a/src/components/nostr/kinds/Kind7Renderer.tsx +++ b/src/components/nostr/kinds/Kind7Renderer.tsx @@ -108,7 +108,7 @@ export function Kind7Renderer({ event }: BaseEventProps) { }; return ( - +
      {/* Reaction indicator */}
      diff --git a/src/components/nostr/kinds/Kind9735Renderer.tsx b/src/components/nostr/kinds/Kind9735Renderer.tsx index 9542901..3df5a65 100644 --- a/src/components/nostr/kinds/Kind9735Renderer.tsx +++ b/src/components/nostr/kinds/Kind9735Renderer.tsx @@ -47,14 +47,6 @@ export function Kind9735Renderer({ event }: BaseEventProps) { return Math.floor(zapAmount / 1000); }, [zapAmount]); - if (!isValid) { - return ( - -
      Invalid zap receipt
      -
      - ); - } - // Override event.pubkey to show zap sender instead of receipt pubkey const displayEvent = useMemo( () => ({ @@ -64,8 +56,16 @@ export function Kind9735Renderer({ event }: BaseEventProps) { [event, zapSender], ); + if (!isValid) { + return ( + +
      Invalid zap receipt
      +
      + ); + } + return ( - +
      {/* Zap indicator */}
      diff --git a/src/components/nostr/kinds/Kind9802Renderer.tsx b/src/components/nostr/kinds/Kind9802Renderer.tsx index 96c361b..b054d8e 100644 --- a/src/components/nostr/kinds/Kind9802Renderer.tsx +++ b/src/components/nostr/kinds/Kind9802Renderer.tsx @@ -17,7 +17,7 @@ export function Kind9802Renderer({ event }: BaseEventProps) { const comment = useMemo(() => getHighlightComment(event), [event]); return ( - +
      {/* Comment */} {comment &&

      {comment}

      } diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx index 991f56e..444b1db 100644 --- a/src/components/ui/avatar.tsx +++ b/src/components/ui/avatar.tsx @@ -1,7 +1,7 @@ -import * as React from "react" -import * as AvatarPrimitive from "@radix-ui/react-avatar" +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const Avatar = React.forwardRef< React.ElementRef, @@ -11,12 +11,12 @@ const Avatar = React.forwardRef< ref={ref} className={cn( "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", - className + className, )} {...props} /> -)) -Avatar.displayName = AvatarPrimitive.Root.displayName +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; const AvatarImage = React.forwardRef< React.ElementRef, @@ -27,8 +27,8 @@ const AvatarImage = React.forwardRef< className={cn("aspect-square h-full w-full", className)} {...props} /> -)) -AvatarImage.displayName = AvatarPrimitive.Image.displayName +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; const AvatarFallback = React.forwardRef< React.ElementRef, @@ -38,11 +38,11 @@ const AvatarFallback = React.forwardRef< ref={ref} className={cn( "flex h-full w-full items-center justify-center rounded-full bg-muted", - className + className, )} {...props} /> -)) -AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; -export { Avatar, AvatarImage, AvatarFallback } +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 65d4fcd..775d380 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,8 +1,8 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const buttonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", @@ -31,27 +31,28 @@ const buttonVariants = cva( variant: "default", size: "default", }, - } -) + }, +); export interface ButtonProps - extends React.ButtonHTMLAttributes, + extends + React.ButtonHTMLAttributes, VariantProps { - asChild?: boolean + asChild?: boolean; } const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" + const Comp = asChild ? Slot : "button"; return ( - ) - } -) -Button.displayName = "Button" + ); + }, +); +Button.displayName = "Button"; -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index 56c381e..10d6900 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -1,16 +1,16 @@ -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { X } from "lucide-react" +import * as React from "react"; +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { X } from "lucide-react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; -const Dialog = DialogPrimitive.Root +const Dialog = DialogPrimitive.Root; -const DialogTrigger = DialogPrimitive.Trigger +const DialogTrigger = DialogPrimitive.Trigger; -const DialogPortal = DialogPrimitive.Portal +const DialogPortal = DialogPrimitive.Portal; -const DialogClose = DialogPrimitive.Close +const DialogClose = DialogPrimitive.Close; const DialogOverlay = React.forwardRef< React.ElementRef, @@ -20,12 +20,12 @@ const DialogOverlay = React.forwardRef< ref={ref} className={cn( "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", - className + className, )} {...props} /> -)) -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; const DialogContent = React.forwardRef< React.ElementRef, @@ -37,7 +37,7 @@ const DialogContent = React.forwardRef< ref={ref} className={cn( "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", - className + className, )} {...props} > @@ -48,8 +48,8 @@ const DialogContent = React.forwardRef< -)) -DialogContent.displayName = DialogPrimitive.Content.displayName +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; const DialogHeader = ({ className, @@ -58,12 +58,12 @@ const DialogHeader = ({
      -) -DialogHeader.displayName = "DialogHeader" +); +DialogHeader.displayName = "DialogHeader"; const DialogFooter = ({ className, @@ -72,12 +72,12 @@ const DialogFooter = ({
      -) -DialogFooter.displayName = "DialogFooter" +); +DialogFooter.displayName = "DialogFooter"; const DialogTitle = React.forwardRef< React.ElementRef, @@ -87,12 +87,12 @@ const DialogTitle = React.forwardRef< ref={ref} className={cn( "text-lg font-semibold leading-none tracking-tight", - className + className, )} {...props} /> -)) -DialogTitle.displayName = DialogPrimitive.Title.displayName +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; const DialogDescription = React.forwardRef< React.ElementRef, @@ -103,8 +103,8 @@ const DialogDescription = React.forwardRef< className={cn("text-sm text-muted-foreground", className)} {...props} /> -)) -DialogDescription.displayName = DialogPrimitive.Description.displayName +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; export { Dialog, @@ -117,4 +117,4 @@ export { DialogFooter, DialogTitle, DialogDescription, -} +}; diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index 69b64fb..7db5241 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -1,6 +1,6 @@ -import * as React from "react" +import * as React from "react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const Input = React.forwardRef>( ({ className, type, ...props }, ref) => { @@ -9,14 +9,14 @@ const Input = React.forwardRef>( type={type} className={cn( "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", - className + className, )} ref={ref} {...props} /> - ) - } -) -Input.displayName = "Input" + ); + }, +); +Input.displayName = "Input"; -export { Input } +export { Input }; diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx index cf253cf..ffc5a82 100644 --- a/src/components/ui/scroll-area.tsx +++ b/src/components/ui/scroll-area.tsx @@ -1,7 +1,7 @@ -import * as React from "react" -import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" +import * as React from "react"; +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const ScrollArea = React.forwardRef< React.ElementRef, @@ -18,8 +18,8 @@ const ScrollArea = React.forwardRef< -)) -ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName +)); +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; const ScrollBar = React.forwardRef< React.ElementRef, @@ -34,13 +34,13 @@ const ScrollBar = React.forwardRef< "h-full w-2.5 border-l border-l-transparent p-[1px]", orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]", - className + className, )} {...props} > -)) -ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName +)); +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName; -export { ScrollArea, ScrollBar } +export { ScrollArea, ScrollBar }; diff --git a/src/core/logic.ts b/src/core/logic.ts index e7c2e9e..51d08bd 100644 --- a/src/core/logic.ts +++ b/src/core/logic.ts @@ -128,7 +128,7 @@ export const removeWindow = ( const newWindowIds = ws.windowIds.filter((id) => id !== windowId); // Remove from global windows object - const { [windowId]: removedWindow, ...remainingWindows } = state.windows; + const { [windowId]: _removedWindow, ...remainingWindows } = state.windows; return { ...state, diff --git a/src/hooks/useCopy.ts b/src/hooks/useCopy.ts index 32a9ec0..9d079eb 100644 --- a/src/hooks/useCopy.ts +++ b/src/hooks/useCopy.ts @@ -38,7 +38,7 @@ export function useCopy(timeout = 3000) { setCopied(false); } }, - [timeout] + [timeout], ); return { copy, copied }; diff --git a/src/hooks/useLocale.ts b/src/hooks/useLocale.ts index f8dcf34..cdd0ce6 100644 --- a/src/hooks/useLocale.ts +++ b/src/hooks/useLocale.ts @@ -34,7 +34,8 @@ export function useLocale(): LocaleConfig { const formatted = testDate.toLocaleTimeString(browserLocale, { hour: "numeric", }); - const timeFormat = formatted.includes("PM") || formatted.includes("AM") ? "12h" : "24h"; + const timeFormat = + formatted.includes("PM") || formatted.includes("AM") ? "12h" : "24h"; return { locale: browserLocale, diff --git a/src/hooks/useProfile.ts b/src/hooks/useProfile.ts index f83785d..b694038 100644 --- a/src/hooks/useProfile.ts +++ b/src/hooks/useProfile.ts @@ -23,8 +23,10 @@ export function useProfile(pubkey: string): ProfileContent | undefined { if (!fetchedEvent || !fetchedEvent.content) return; try { - const profileData = JSON.parse(fetchedEvent.content) as ProfileContent; - + const profileData = JSON.parse( + fetchedEvent.content, + ) as ProfileContent; + // Save to IndexedDB await db.profiles.put({ ...profileData, diff --git a/src/hooks/useRelayInfo.ts b/src/hooks/useRelayInfo.ts index f760a4b..e803181 100644 --- a/src/hooks/useRelayInfo.ts +++ b/src/hooks/useRelayInfo.ts @@ -9,7 +9,9 @@ import db from "../services/db"; * @param wsUrl - WebSocket URL of the relay (ws:// or wss://) * @returns Relay information or undefined if not yet loaded */ -export function useRelayInfo(wsUrl: string | undefined): RelayInformation | undefined { +export function useRelayInfo( + wsUrl: string | undefined, +): RelayInformation | undefined { const cached = useLiveQuery( () => (wsUrl ? db.relayInfo.get(wsUrl) : undefined), [wsUrl], diff --git a/src/lib/nip-kinds.ts b/src/lib/nip-kinds.ts index 509bea4..6687e8b 100644 --- a/src/lib/nip-kinds.ts +++ b/src/lib/nip-kinds.ts @@ -1,17 +1,18 @@ -import { EVENT_KINDS } from '@/constants/kinds' +import { EVENT_KINDS } from "@/constants/kinds"; /** * Get all event kinds defined in a specific NIP */ export function getKindsForNip(nipId: string): number[] { - const kinds: number[] = [] + const kinds: number[] = []; for (const [kindKey, kindInfo] of Object.entries(EVENT_KINDS)) { if (kindInfo.nip === nipId) { - const kindNum = typeof kindInfo.kind === 'number' ? kindInfo.kind : parseInt(kindKey) - kinds.push(kindNum) + const kindNum = + typeof kindInfo.kind === "number" ? kindInfo.kind : parseInt(kindKey); + kinds.push(kindNum); } } - return kinds.sort((a, b) => a - b) + return kinds.sort((a, b) => a - b); } diff --git a/src/lib/nip05.ts b/src/lib/nip05.ts index 2509a2b..bba5870 100644 --- a/src/lib/nip05.ts +++ b/src/lib/nip05.ts @@ -17,7 +17,8 @@ export function isNip05(value: string): boolean { if (!value) return false; // Match user@domain format - const userAtDomain = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9][\w.-]+\.[a-zA-Z]{2,}$/.test(value); + const userAtDomain = + /^[a-zA-Z0-9._-]+@[a-zA-Z0-9][\w.-]+\.[a-zA-Z]{2,}$/.test(value); // Match bare domain format (domain.com -> _@domain.com) const bareDomain = /^[a-zA-Z0-9][\w.-]+\.[a-zA-Z]{2,}$/.test(value); @@ -66,7 +67,9 @@ export async function resolveNip05(nip05: string): Promise { return null; } - console.log(`NIP-05: Resolved ${nip05} → ${normalized} → ${profile.pubkey}`); + console.log( + `NIP-05: Resolved ${nip05} → ${normalized} → ${profile.pubkey}`, + ); return profile.pubkey.toLowerCase(); } catch (error) { console.warn(`NIP-05: Resolution failed for ${normalized}:`, error); diff --git a/src/lib/open-parser.ts b/src/lib/open-parser.ts index 35395ea..6c6fb20 100644 --- a/src/lib/open-parser.ts +++ b/src/lib/open-parser.ts @@ -1,5 +1,9 @@ import { nip19 } from "nostr-tools"; -import { isValidHexEventId, isValidHexPubkey, normalizeHex } from "./nostr-validation"; +import { + isValidHexEventId, + isValidHexPubkey, + normalizeHex, +} from "./nostr-validation"; // Define pointer types locally since they're not exported from nostr-tools export interface EventPointer { diff --git a/src/lib/req-parser.ts b/src/lib/req-parser.ts index f76061c..92c6622 100644 --- a/src/lib/req-parser.ts +++ b/src/lib/req-parser.ts @@ -1,7 +1,11 @@ import { nip19 } from "nostr-tools"; import type { NostrFilter } from "@/types/nostr"; import { isNip05 } from "./nip05"; -import { isValidHexPubkey, isValidHexEventId, normalizeHex } from "./nostr-validation"; +import { + isValidHexPubkey, + isValidHexEventId, + normalizeHex, +} from "./nostr-validation"; export interface ParsedReqCommand { filter: NostrFilter; @@ -264,7 +268,7 @@ function parseNpubOrHex(value: string): string | null { if (decoded.type === "npub") { return decoded.data; } - } catch (e) { + } catch { // Not valid npub, continue } } @@ -290,7 +294,7 @@ function parseNoteOrHex(value: string): string | null { if (decoded.type === "note") { return decoded.data; } - } catch (e) { + } catch { // Not valid note, continue } } diff --git a/vite.config.ts b/vite.config.ts index c099c84..28da367 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,16 +1,16 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import path from 'node:path' -import { fileURLToPath } from 'node:url' +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; -const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const __dirname = path.dirname(fileURLToPath(import.meta.url)); // https://vite.dev/config/ export default defineConfig({ plugins: [react()], resolve: { alias: { - '@': path.resolve(__dirname, './src'), + "@": path.resolve(__dirname, "./src"), }, }, -}) +});