From 5baf39c2dd2299a78d5bf49b87a6b5719a2d612a Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Tue, 27 Jan 2026 10:08:46 +0100 Subject: [PATCH 01/31] first commit vulnerability-database.mdx created --- src/pages/_meta.ts | 5 ++--- src/pages/vulnerability-database.mdx | 13 +++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 src/pages/vulnerability-database.mdx diff --git a/src/pages/_meta.ts b/src/pages/_meta.ts index ce5c0af..a3311b0 100644 --- a/src/pages/_meta.ts +++ b/src/pages/_meta.ts @@ -24,9 +24,8 @@ export default { explanations: { title: 'Explanations' }, reference: { title: 'Reference' }, contributing: { title: 'Contributing' }, - other: { - title: 'Other', - }, + other: { title: 'Other', }, + 'vulnerability-database': { title: 'Vulnerability Database' }, 'header-pricing': { title: 'Pricing', type: 'page', diff --git a/src/pages/vulnerability-database.mdx b/src/pages/vulnerability-database.mdx new file mode 100644 index 0000000..f30c9f3 --- /dev/null +++ b/src/pages/vulnerability-database.mdx @@ -0,0 +1,13 @@ +--- +title: Vulnerability Database +--- + +import Image from 'next/image'; +import { Callout } from 'nextra/components'; +import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"; +import PageContentComingSoon from '@/components/PageContentComingSoon' + + +# Vulnerability Database + + \ No newline at end of file From 6332e820442e3e48a2fc4cded247b73d195b22b7 Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Thu, 29 Jan 2026 10:45:14 +0100 Subject: [PATCH 02/31] vulnerability-database terminal output, ui database --- package-lock.json | 273 +++++++++-- package.json | 3 + src/components/ui/card.tsx | 79 +++ src/components/ui/chart.tsx | 367 ++++++++++++++ src/components/ui/input.tsx | 20 + src/components/ui/table.tsx | 117 +++++ .../vulnerability-database/hero.tsx | 94 ++++ .../vulnerability-database/light-rays.tsx | 454 ++++++++++++++++++ .../vulnerability-database/radar-chart.tsx | 83 ++++ .../vulnerability-data-table/columns.tsx | 86 ++++ .../vulnerability-data-table/data-table.tsx | 123 +++++ src/pages/_meta.ts | 7 +- src/pages/vulnerability-database.mdx | 39 +- 13 files changed, 1701 insertions(+), 44 deletions(-) create mode 100644 src/components/ui/card.tsx create mode 100644 src/components/ui/chart.tsx create mode 100644 src/components/ui/input.tsx create mode 100644 src/components/ui/table.tsx create mode 100644 src/components/vulnerability-database/hero.tsx create mode 100644 src/components/vulnerability-database/light-rays.tsx create mode 100644 src/components/vulnerability-database/radar-chart.tsx create mode 100644 src/components/vulnerability-database/vulnerability-data-table/columns.tsx create mode 100644 src/components/vulnerability-database/vulnerability-data-table/data-table.tsx diff --git a/package-lock.json b/package-lock.json index dd190df..7b1d46e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", + "@tanstack/react-table": "^8.21.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dotenv": "^16.5.0", @@ -29,6 +30,7 @@ "next-sitemap": "^4.2.3", "nextra": "3.3.1", "nextra-theme-docs": "3.3.1", + "ogl": "^1.0.11", "p-limit": "^6.2.0", "postprocessing": "^6.38.2", "radix-ui": "^1.4.3", @@ -37,6 +39,7 @@ "react-hook-form": "^7.57.0", "react-markdown": "^9.0.3", "react-use-measure": "^2.1.7", + "recharts": "^2.15.4", "remark-gemoji": "^8.0.0", "remark-gfm": "^4.0.1", "remark-mdx-disable-explicit-jsx": "^0.1.0", @@ -120,7 +123,6 @@ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -708,6 +710,7 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "license": "MIT", + "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -731,6 +734,7 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -832,6 +836,7 @@ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "deprecated": "Use @eslint/config-array instead", "license": "Apache-2.0", + "peer": true, "dependencies": { "@humanwhocodes/object-schema": "^2.0.2", "debug": "^4.3.1", @@ -846,6 +851,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=12.22" }, @@ -859,7 +865,8 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@iconify/types": { "version": "2.0.0", @@ -5210,6 +5217,18 @@ "node": "^18 || ^20 || >= 21" } }, + "node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2/node_modules/tree-sitter": { + "version": "0.22.4", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.22.4.tgz", + "integrity": "sha512-usbHZP9/oxNsUY65MQUsduGRqDHQOou1cagUSwjhoSYAmSahjQDAVsh9s+SlZkn8X8+O1FULRGwHu7AFP3kjzg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + } + }, "node_modules/@swagger-api/apidom-reference": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-1.1.0.tgz", @@ -5308,6 +5327,26 @@ "tslib": "^2.8.0" } }, + "node_modules/@tanstack/react-table": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", + "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@tanstack/react-virtual": { "version": "3.13.10", "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.10.tgz", @@ -5325,6 +5364,19 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tanstack/virtual-core": { "version": "3.13.10", "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.10.tgz", @@ -5856,7 +5908,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -5998,7 +6049,6 @@ "integrity": "sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.14.0", "@typescript-eslint/types": "8.14.0", @@ -6213,7 +6263,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6272,6 +6321,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6930,7 +6980,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7743,7 +7792,6 @@ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.3.tgz", "integrity": "sha512-HncJ9gGJbVtw7YXtIs3+6YAFSSiKsom0amWc33Z7QbylbY2JGMrA0yz4EwrdTScZxnwclXeEZHzO5pxoy0ZE4g==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10" } @@ -8153,7 +8201,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -8354,6 +8401,12 @@ "dev": true, "license": "MIT" }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, "node_modules/decode-named-character-reference": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", @@ -8395,7 +8448,8 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/deepmerge": { "version": "4.3.1", @@ -8533,6 +8587,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "license": "Apache-2.0", + "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -8540,6 +8595,16 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/domexception": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -8898,6 +8963,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -9016,7 +9082,6 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -9106,7 +9171,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -9321,6 +9385,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -9358,6 +9423,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -9388,6 +9454,7 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "license": "BSD-3-Clause", + "peer": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -9400,6 +9467,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -9553,6 +9621,12 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -9632,7 +9706,8 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fast-diff": { "version": "1.3.0", @@ -9641,6 +9716,15 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/fast-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -9685,7 +9769,8 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fastq": { "version": "1.17.1", @@ -9737,6 +9822,7 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "license": "MIT", + "peer": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -9761,6 +9847,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "license": "MIT", + "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -9777,6 +9864,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "license": "MIT", + "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -9790,7 +9878,8 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/flexsearch": { "version": "0.7.43", @@ -10187,6 +10276,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "license": "MIT", + "peer": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -10850,14 +10940,14 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.2.tgz", "integrity": "sha512-1NU7hWZDkV7hJ4PJ9dur9gTNQ4ePNPN4k9/0YhwjzykTi/+3Q5pF93YU5QoVj8BuOnhLgaY8gs0U2pj4kSYVcw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "license": "MIT", + "peer": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -11348,6 +11438,7 @@ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -12826,7 +12917,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -12839,13 +12931,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/json5": { "version": "2.2.3", @@ -12897,6 +12991,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "license": "MIT", + "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -12988,6 +13083,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "license": "MIT", + "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -13019,6 +13115,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "license": "MIT", + "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -13051,7 +13148,8 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/logform": { "version": "2.7.0", @@ -14659,7 +14757,6 @@ "resolved": "https://registry.npmjs.org/next/-/next-15.4.10.tgz", "integrity": "sha512-itVlc79QjpKMFMRhP+kbGKaSG/gZM6RCvwhEbwmCNF06CdDiNaoHcbeg0PqkEa2GOcn8KJ0nnc7+yL7EjoYLHQ==", "license": "MIT", - "peer": true, "dependencies": { "@next/env": "15.4.10", "@swc/helpers": "0.5.15", @@ -15148,6 +15245,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ogl": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ogl/-/ogl-1.0.11.tgz", + "integrity": "sha512-kUpC154AFfxi16pmZUK4jk3J+8zxwTWGPo03EoYA8QPbzikHoaC82n6pNTbd+oEaJonaE8aPWBlX7ad9zrqLsA==", + "license": "Unlicense" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -15222,6 +15325,7 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "license": "MIT", + "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -15272,6 +15376,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "license": "MIT", + "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -15287,6 +15392,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "license": "MIT", + "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -15302,6 +15408,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -15336,6 +15443,7 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "license": "MIT", + "peer": true, "dependencies": { "callsites": "^3.0.0" }, @@ -15651,7 +15759,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -15790,6 +15897,7 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -16144,7 +16252,6 @@ "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.30.1.tgz", "integrity": "sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/ramda" @@ -16193,7 +16300,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -16232,7 +16338,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -16407,6 +16512,21 @@ } } }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-style-singleton": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", @@ -16449,6 +16569,22 @@ "react": ">= 0.14.0" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/react-use-measure": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", @@ -16505,6 +16641,44 @@ "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==", "license": "MIT" }, + "node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "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" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recharts/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, "node_modules/recma-build-jsx": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", @@ -16573,8 +16747,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", @@ -17163,6 +17336,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "license": "MIT", + "peer": true, "engines": { "node": ">=4" } @@ -17273,6 +17447,7 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "license": "ISC", + "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -17289,6 +17464,7 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "license": "ISC", + "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -17446,7 +17622,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz", "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -17699,7 +17874,6 @@ "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.23.0.tgz", "integrity": "sha512-xfdu9DqPkIpExH29cmiTlgo0/jBki5la1Tkfhsv+Wu5TT3APLNHslR1acxuKJOCWqVdSc+pIbs/2ozjVRGppdg==", "license": "MIT", - "peer": true, "dependencies": { "@shikijs/core": "1.23.0", "@shikijs/engine-javascript": "1.23.0", @@ -18405,7 +18579,6 @@ "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -18475,7 +18648,6 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz", "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -18574,7 +18746,8 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/thenify": { "version": "3.3.1", @@ -18601,8 +18774,13 @@ "version": "0.181.2", "resolved": "https://registry.npmjs.org/three/-/three-0.181.2.tgz", "integrity": "sha512-k/CjiZ80bYss6Qs7/ex1TBlPD11whT9oKfT8oTGiHa34W4JRd1NiH/Tr1DbHWQ2/vMUypxksLnF2CfmlmM5XFQ==", - "license": "MIT", - "peer": true + "license": "MIT" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" }, "node_modules/tinyexec": { "version": "1.0.2", @@ -18894,6 +19072,7 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "license": "MIT", + "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -19014,7 +19193,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -19395,6 +19573,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "punycode": "^2.1.0" } @@ -19555,6 +19734,28 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", @@ -19850,6 +20051,7 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -20141,7 +20343,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 3eb20a1..bbb728a 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", + "@tanstack/react-table": "^8.21.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dotenv": "^16.5.0", @@ -54,6 +55,7 @@ "next-sitemap": "^4.2.3", "nextra": "3.3.1", "nextra-theme-docs": "3.3.1", + "ogl": "^1.0.11", "p-limit": "^6.2.0", "postprocessing": "^6.38.2", "radix-ui": "^1.4.3", @@ -62,6 +64,7 @@ "react-hook-form": "^7.57.0", "react-markdown": "^9.0.3", "react-use-measure": "^2.1.7", + "recharts": "^2.15.4", "remark-gemoji": "^8.0.0", "remark-gfm": "^4.0.1", "remark-mdx-disable-explicit-jsx": "^0.1.0", diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..f62edea --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,79 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/src/components/ui/chart.tsx b/src/components/ui/chart.tsx new file mode 100644 index 0000000..23dc1c1 --- /dev/null +++ b/src/components/ui/chart.tsx @@ -0,0 +1,367 @@ +import * as React from "react" +import * as RechartsPrimitive from "recharts" + +import { cn } from "@/lib/utils" + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) +} + +type ChartContextProps = { + config: ChartConfig +} + +const ChartContext = React.createContext(null) + +function useChart() { + const context = React.useContext(ChartContext) + + if (!context) { + throw new Error("useChart must be used within a ") + } + + return context +} + +const ChartContainer = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"] + } +>(({ id, className, children, config, ...props }, ref) => { + const uniqueId = React.useId() + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` + + return ( + +
+ + + {children} + +
+
+ ) +}) +ChartContainer.displayName = "Chart" + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([, config]) => config.theme || config.color + ) + + if (!colorConfig.length) { + return null + } + + return ( + + + + ) } + + \ No newline at end of file From 196ce97290749b0a7c763ca5f45fd6204cf64aa4 Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Tue, 3 Feb 2026 14:53:56 +0100 Subject: [PATCH 09/31] started bento grid for sub-page --- .../vulnerability-database/cve-detail.tsx | 237 +++++++++++++++++- src/pages/vulnerability-database/[ID].mdx | 25 +- src/types/api.ts | 19 ++ 3 files changed, 271 insertions(+), 10 deletions(-) create mode 100644 src/types/api.ts diff --git a/src/components/vulnerability-database/cve-detail.tsx b/src/components/vulnerability-database/cve-detail.tsx index 2906521..6ac093d 100644 --- a/src/components/vulnerability-database/cve-detail.tsx +++ b/src/components/vulnerability-database/cve-detail.tsx @@ -1,12 +1,41 @@ import { useEffect, useState } from 'react' +import { Container } from '../top-level-pages/container' +import { affectedComponents } from '@/types/api' interface CVEData { cve: string + affectedComponents: affectedComponents + cvss: number + datePublished: Date + dateLastModified: Date + description: string + epss?: number + risk?: { + baseScore: number + withEnvironment?: number + withThreatIntelligence?: number + withEnvironmentAndThreatIntelligence?: number + } + // exploits?: } export default function CVEDetailComponent({ cveId }: { cveId?: string }) { const [data, setData] = useState(null) const [loading, setLoading] = useState(true) + const [isDescriptionOpen, setIsDescriptionOpen] = useState(true) + const [isComponentsOpen, setIsComponentsOpen] = useState(false) + + const options: Intl.DateTimeFormatOptions = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + } + + useEffect(() => { + const isMobile = window.innerWidth < 1024 + setIsDescriptionOpen(!isMobile) + }, []) useEffect(() => { if (!cveId) return @@ -16,11 +45,215 @@ export default function CVEDetailComponent({ cveId }: { cveId?: string }) { setData(result) setLoading(false) }) - console.log(data) }, [cveId]) if (loading) return
Loading...
if (!data) return
Not found
- return
{data.cve}
+ return ( + +
+
+
+
+
+
+

+ {data.cve} +

+

+ Published:{' '} + {data.datePublished instanceof Date + ? data.datePublished.toLocaleDateString() + : new Date( + data.datePublished, + ).toLocaleDateString( + 'en-EN', + options, + )} +

+

+ Last Modified:{' '} + {data.dateLastModified instanceof Date + ? data.dateLastModified.toLocaleDateString() + : new Date( + data.dateLastModified, + ).toLocaleDateString( + 'en-EN', + options, + )} +

+
+
+ + CVSS Score + + = 9 + ? 'text-red-600' + : data.cvss >= 7 + ? 'text-orange-600' + : data.cvss >= 4 + ? 'text-yellow-600' + : 'text-green-600' + }`} + > + {data.cvss.toFixed(1)} + +
+
+
+
+
+ +
+
+
+

+ Risk Scores +

+
+
+ + BaseScore: + + + {data.risk?.baseScore.toFixed(1) ?? 'N/A'} + +
+ {data.risk?.withEnvironment && ( +
+ + With Environment: + + + {data.risk.withEnvironment.toFixed(1)} + +
+ )} + {data.risk?.withThreatIntelligence && ( +
+ + With Threat Intelligence: + + + {data.risk.withThreatIntelligence.toFixed( + 1, + )} + +
+ )} +
+ + EPSS: + + + {data.epss + ? `${(data.epss * 100).toFixed(2)}%` + : 'N/A'} + +
+
+
+
+
+
+ +
+
+
+ + {isDescriptionOpen && ( +
+

+ {data.description} +

+
+ )} +
+
+
+ +
+
+
+ + {isComponentsOpen && ( +
+
+ {data.affectedComponents.map((ac) => ( +
+

+ {ac.name} +

+ {ac.version && ( +

+ {ac.version} +

+ )} +

+ Ecosystem: {ac.ecosystem} +

+

+ {} +
Fix:{' '} + {ac.versionFixed + ? `${ac.versionFixed}%` + : 'N/A'} +

+
+ ))} +
+
+ )} +
+
+
+ + ) } diff --git a/src/pages/vulnerability-database/[ID].mdx b/src/pages/vulnerability-database/[ID].mdx index e19d40f..38e10ac 100644 --- a/src/pages/vulnerability-database/[ID].mdx +++ b/src/pages/vulnerability-database/[ID].mdx @@ -8,14 +8,23 @@ export const CVEDetail = () => { return ( <> + aside.nextra-sidebar-container { display: none !important; } + .nextra-toc { display: none !important; } + aside[class*="nextra-toc"] { display: none !important; } + .nextra-content { + max-width: 100% !important; + padding: 0 !important; + } + .nextra-content > div, + [class*="_max-w-6xl"] { + max-width: 100% !important; + padding-left: 0 !important; + padding-right: 0 !important; + } + nav.nextra-nav-container { position: sticky !important; top: 0 !important; } + a.nextra-focus[class*="max-w-"] { display: none !important; } + nav[class*="border-t"] { display: none !important; } +`} ) diff --git a/src/types/api.ts b/src/types/api.ts new file mode 100644 index 0000000..063bea6 --- /dev/null +++ b/src/types/api.ts @@ -0,0 +1,19 @@ +export type affectedComponent = { + id: string + Source: string + purl: string + ecosystem: string + scheme: string + type: string + name: string + namespace: string + subpath?: string + version: string + semverStart?: string + semverEnd?: string + versionIntroduced?: string + versionFixed?: string + cves?: string +} + +export type affectedComponents = affectedComponent[] From 6a361665b4e1681eceecddd109c70355b68db8cd Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Tue, 3 Feb 2026 15:43:44 +0100 Subject: [PATCH 10/31] fix database query and skeleton load --- src/components/ui/pie-chart.tsx | 402 ++++++++++++++++++ .../vulnerability-database/cve-detail.tsx | 163 ++++++- .../vulnerability-database-page.tsx | 3 +- 3 files changed, 548 insertions(+), 20 deletions(-) create mode 100644 src/components/ui/pie-chart.tsx diff --git a/src/components/ui/pie-chart.tsx b/src/components/ui/pie-chart.tsx new file mode 100644 index 0000000..a0a268d --- /dev/null +++ b/src/components/ui/pie-chart.tsx @@ -0,0 +1,402 @@ +import * as React from 'react' +import * as RechartsPrimitive from 'recharts' + +import { cn } from '@/lib/utils' + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: '', dark: '.dark' } as const + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) +} + +type ChartContextProps = { + config: ChartConfig +} + +const ChartContext = React.createContext(null) + +function useChart() { + const context = React.useContext(ChartContext) + + if (!context) { + throw new Error('useChart must be used within a ') + } + + return context +} + +const ChartContainer = React.forwardRef< + HTMLDivElement, + React.ComponentProps<'div'> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >['children'] + } +>(({ id, className, children, config, ...props }, ref) => { + const uniqueId = React.useId() + const chartId = `chart-${id || uniqueId.replace(/:/g, '')}` + + return ( + +
+ + + {children} + +
+
+ ) +}) +ChartContainer.displayName = 'Chart' + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([_, config]) => config.theme || config.color, + ) + + if (!colorConfig.length) { + return null + } + + return ( + From 5089a561d05643d854ddd600f0a0a36a8f53c23c Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Thu, 5 Feb 2026 16:22:05 +0100 Subject: [PATCH 18/31] mock dynamic sitemap setup --- src/pages/api/cve/[page].ts | 44 ++++++++++++++++++++++++++++++++++++ src/pages/api/sitemap.xml.ts | 33 +++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/pages/api/cve/[page].ts create mode 100644 src/pages/api/sitemap.xml.ts diff --git a/src/pages/api/cve/[page].ts b/src/pages/api/cve/[page].ts new file mode 100644 index 0000000..8402f63 --- /dev/null +++ b/src/pages/api/cve/[page].ts @@ -0,0 +1,44 @@ +import type { NextApiRequest, NextApiResponse } from 'next' + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse, +) { + const { page } = req.query + const pageNum = parseInt((page as string).replace('.xml', ''), 10) + + if (isNaN(pageNum) || pageNum < 0) { + return res.status(400).send('Invalid page number') + } + + const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://devguard.org' + const CVES_PER_SITEMAP = 50000 + const offset = pageNum * CVES_PER_SITEMAP + + // TODO: Hier deine CVE-Daten laden (offset, limit: CVES_PER_SITEMAP) + // const cves = await fetchCVEs(offset, CVES_PER_SITEMAP) + + // Platzhalter – ersetze mit echten Daten + const cveUrls = Array.from( + { length: CVES_PER_SITEMAP }, + (_, i) => `CVE-2024-${String(offset + i).padStart(5, '0')}`, + ) + + const sitemap = ` + +${cveUrls + .map( + (cve) => ` + ${baseUrl}/cve/${cve} + `, + ) + .join('\n')} +` + + res.setHeader('Content-Type', 'text/xml') + res.setHeader( + 'Cache-Control', + 'public, s-maxage=43200, stale-while-revalidate', + ) + res.status(200).send(sitemap) +} diff --git a/src/pages/api/sitemap.xml.ts b/src/pages/api/sitemap.xml.ts new file mode 100644 index 0000000..9ee04ef --- /dev/null +++ b/src/pages/api/sitemap.xml.ts @@ -0,0 +1,33 @@ +import type { NextApiRequest, NextApiResponse } from 'next' + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse, +) { + const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://devguard.org' + const TOTAL_CVES = 200000 + const CVES_PER_SITEMAP = 50000 + const numberOfCveSitemaps = Math.ceil(TOTAL_CVES / CVES_PER_SITEMAP) + + const sitemap = ` + + + ${baseUrl}/sitemap-0.xml + ${new Date().toISOString()} + +${Array.from( + { length: numberOfCveSitemaps }, + (_, i) => ` + ${baseUrl}/api/cve/${i}.xml + ${new Date().toISOString()} + `, +).join('\n')} +` + + res.setHeader('Content-Type', 'text/xml') + res.setHeader( + 'Cache-Control', + 'public, s-maxage=43200, stale-while-revalidate', + ) + res.status(200).send(sitemap) +} \ No newline at end of file From cc6fe0b888e2d40d6d6f25265dc3ec885e0c19d4 Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Tue, 10 Feb 2026 09:41:03 +0100 Subject: [PATCH 19/31] sitemap new load --- package.json | 2 +- sitemap.xml | 5 + src/components/ui/card.tsx | 104 +-- src/components/ui/chart.tsx | 668 ++++++++++-------- src/components/ui/skeleton.tsx | 18 +- src/components/ui/table.tsx | 165 ++--- .../vulnerability-database/hero.tsx | 2 +- .../vulnerability-database/light-rays.tsx | 665 ++++++++--------- .../vulnerability-data-table/data-table.tsx | 1 - src/pages/api/sitemap.xml.ts | 6 +- 10 files changed, 855 insertions(+), 781 deletions(-) create mode 100644 sitemap.xml diff --git a/package.json b/package.json index 23eae19..5381cf4 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "dev": "next dev", "build": "next build", - "postbuild": "next-sitemap", + "postbuild": "next-sitemap && cp sitemap.xml public/sitemap.xml", "start": "next start", "lint": "next lint" }, diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..960cfaa --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,5 @@ + + +https://devguard.org/sitemap-0.xml +https://devguard.org/api/sitemap.xml + \ No newline at end of file diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index f62edea..983c971 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -1,79 +1,79 @@ -import * as React from "react" +import * as React from 'react' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)) -Card.displayName = "Card" +Card.displayName = 'Card' const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)) -CardHeader.displayName = "CardHeader" +CardHeader.displayName = 'CardHeader' const CardTitle = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)) -CardTitle.displayName = "CardTitle" +CardTitle.displayName = 'CardTitle' const CardDescription = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)) -CardDescription.displayName = "CardDescription" +CardDescription.displayName = 'CardDescription' const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)) -CardContent.displayName = "CardContent" +CardContent.displayName = 'CardContent' const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)) -CardFooter.displayName = "CardFooter" +CardFooter.displayName = 'CardFooter' export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/src/components/ui/chart.tsx b/src/components/ui/chart.tsx index 23dc1c1..2e0fe17 100644 --- a/src/components/ui/chart.tsx +++ b/src/components/ui/chart.tsx @@ -1,367 +1,407 @@ -import * as React from "react" -import * as RechartsPrimitive from "recharts" +import * as React from 'react' +import * as RechartsPrimitive from 'recharts' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' // Format: { THEME_NAME: CSS_SELECTOR } -const THEMES = { light: "", dark: ".dark" } as const +const THEMES = { light: '', dark: '.dark' } as const export type ChartConfig = { - [k in string]: { - label?: React.ReactNode - icon?: React.ComponentType - } & ( - | { color?: string; theme?: never } - | { color?: never; theme: Record } - ) + [k in string]: { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) } type ChartContextProps = { - config: ChartConfig + config: ChartConfig } const ChartContext = React.createContext(null) function useChart() { - const context = React.useContext(ChartContext) + const context = React.useContext(ChartContext) - if (!context) { - throw new Error("useChart must be used within a ") - } + if (!context) { + throw new Error('useChart must be used within a ') + } - return context + return context } const ChartContainer = React.forwardRef< - HTMLDivElement, - React.ComponentProps<"div"> & { - config: ChartConfig - children: React.ComponentProps< - typeof RechartsPrimitive.ResponsiveContainer - >["children"] - } + HTMLDivElement, + React.ComponentProps<'div'> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >['children'] + } >(({ id, className, children, config, ...props }, ref) => { - const uniqueId = React.useId() - const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` - - return ( - -
- - - {children} - -
-
- ) + const uniqueId = React.useId() + const chartId = `chart-${id || uniqueId.replace(/:/g, '')}` + + return ( + +
+ + + {children} + +
+
+ ) }) -ChartContainer.displayName = "Chart" +ChartContainer.displayName = 'Chart' const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { - const colorConfig = Object.entries(config).filter( - ([, config]) => config.theme || config.color - ) - - if (!colorConfig.length) { - return null - } - - return ( -