diff --git a/AGENTS.md b/AGENTS.md index e7d274f..13b9281 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,6 +17,14 @@ Always run `npm run lint` after making changes. No typecheck script is configure Do not commit changes unless explicitly asked to. +## Version bump + +When asked to bump the project version, update all three files: + +- `package.json` — `"version"` field +- `package-lock.json` — `"version"` field (both top-level and nested `packages[""]`) +- `README.md` — version badge at the top + All UI text is in English. ## Search API diff --git a/README.md b/README.md index 2ab6ceb..cdc7864 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ High-performance dashboard for exploring GitHub users, repositories, and search results. -**v0.3.0** +**v0.3.2** ## Features @@ -26,14 +26,14 @@ High-performance dashboard for exploring GitHub users, repositories, and search - **State:** Zustand v5 with local persistence - **Animations:** Framer Motion v12 - **Charts:** Recharts v3 (language donut chart) -- **Markdown:** react-markdown + remark-gfm +- **Markdown:** react-markdown + remark-gfm + rehype-raw - **PWA:** Web App Manifest + service worker via Next.js PWA plugin - **Icons:** Lucide React ## Prerequisites -- Node.js 20+ -- (Optional) `NEXT_PUBLIC_GITHUB_TOKEN` in `.env.local` to increase GitHub API rate limit +- Node.js >=22.0.0 +- (Optional) `GITHUB_TOKEN` in `.env.local` to increase GitHub API rate limit ## Commands diff --git a/next.config.ts b/next.config.ts index ec537f2..7954bdc 100644 --- a/next.config.ts +++ b/next.config.ts @@ -8,6 +8,10 @@ const nextConfig = { protocol: 'https', hostname: 'avatars.githubusercontent.com', }, + { + protocol: 'https', + hostname: 'raw.githubusercontent.com', + }, ], }, }; diff --git a/package-lock.json b/package-lock.json index 37ca172..31fcb63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "github-explorer-dashboard", - "version": "0.3.0", + "version": "0.3.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "github-explorer-dashboard", - "version": "0.3.0", + "version": "0.3.2", "dependencies": { "@base-ui/react": "^1.5.0", "class-variance-authority": "^0.7.1", @@ -18,6 +18,8 @@ "react-dom": "19.2.4", "react-markdown": "^10.1.0", "recharts": "^3.8.0", + "rehype-raw": "^7.0.0", + "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", "shadcn": "^4.7.0", "tailwind-merge": "^3.6.0", @@ -4620,6 +4622,18 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -6094,6 +6108,79 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-sanitize": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz", + "integrity": "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "unist-util-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", @@ -6121,6 +6208,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", @@ -6134,6 +6240,23 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/headers-polyfill": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-5.0.1.tgz", @@ -6180,6 +6303,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -9163,6 +9296,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -9643,6 +9788,35 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-sanitize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz", + "integrity": "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-sanitize": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-gfm": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", @@ -11394,6 +11568,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vfile-message": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", @@ -11430,6 +11618,16 @@ "d3-timer": "^3.0.1" } }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", @@ -11618,7 +11816,7 @@ "license": "ISC" }, "node_modules/wsl-utils": { - "version": "0.3.1", + "version": "0.3.2", "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", "license": "MIT", diff --git a/package.json b/package.json index 04a1ebb..6cb7a67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "github-explorer-dashboard", - "version": "0.3.1", + "version": "0.3.2", "private": true, "engines": { "node": ">=22.0.0", @@ -23,6 +23,8 @@ "react-dom": "19.2.4", "react-markdown": "^10.1.0", "recharts": "^3.8.0", + "rehype-raw": "^7.0.0", + "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", "shadcn": "^4.7.0", "tailwind-merge": "^3.6.0", diff --git a/src/components/ViewRepo.tsx b/src/components/ViewRepo.tsx index 5d8fb5c..a7486c7 100644 --- a/src/components/ViewRepo.tsx +++ b/src/components/ViewRepo.tsx @@ -18,6 +18,8 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Badge } from '@/components/ui/badge'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; +import rehypeRaw from 'rehype-raw'; +import rehypeSanitize from 'rehype-sanitize'; import { motion } from 'framer-motion'; // Common GitHub language colors mapping @@ -70,13 +72,13 @@ export const ViewRepo = () => { if (!activeRepo) return null; // Date Format Helpers - const createdDate = new Date(activeRepo.created_at).toLocaleDateString(undefined, { + const createdDate = new Date(activeRepo.created_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); - const pushedDate = new Date(activeRepo.pushed_at).toLocaleDateString(undefined, { + const pushedDate = new Date(activeRepo.pushed_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', @@ -236,7 +238,21 @@ export const ViewRepo = () => {
- + { + if (!src || typeof src !== 'string') return null; + const isAbsolute = src.startsWith('http://') || src.startsWith('https://'); + const resolved = isAbsolute + ? src + : `https://raw.githubusercontent.com/${activeRepo.owner.login}/${activeRepo.name}/${activeRepo.default_branch}/${src.replace(/^\.?\//, '')}`; + // eslint-disable-next-line @next/next/no-img-element + return {alt; + }, + }} + > {activeRepoReadme || 'No README available for this repository.'}
@@ -254,7 +270,7 @@ export const ViewRepo = () => { {activeRepoCommits.length > 0 ? (
{activeRepoCommits.map((item) => { - const commitDate = new Date(item.commit.author.date).toLocaleDateString(undefined, { + const commitDate = new Date(item.commit.author.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', diff --git a/src/components/ViewSearch.tsx b/src/components/ViewSearch.tsx index 87fcd74..0624433 100644 --- a/src/components/ViewSearch.tsx +++ b/src/components/ViewSearch.tsx @@ -106,18 +106,17 @@ export const ViewSearch = () => { Press Enter to search - +
@@ -224,7 +223,7 @@ export const ViewSearch = () => { - {new Date(item.timestamp).toLocaleDateString(undefined, { + {new Date(item.timestamp).toLocaleDateString('en-US', { month: 'short', day: 'numeric', })} diff --git a/src/components/ViewUser.tsx b/src/components/ViewUser.tsx index f79c037..e877af8 100644 --- a/src/components/ViewUser.tsx +++ b/src/components/ViewUser.tsx @@ -132,7 +132,7 @@ export const ViewUser = () => { if (!activeUser) return null; - const formattedDate = new Date(activeUser.created_at).toLocaleDateString(undefined, { + const formattedDate = new Date(activeUser.created_at).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', diff --git a/src/lib/github.ts b/src/lib/github.ts index 34719ca..4ec9f44 100644 --- a/src/lib/github.ts +++ b/src/lib/github.ts @@ -47,6 +47,7 @@ export interface GitHubRepository { url: string | null; } | null; topics: string[]; + default_branch: string; } export interface GitHubCommit {