diff --git a/web/package.json b/web/package.json index 4629d0ff..d84c6ce2 100644 --- a/web/package.json +++ b/web/package.json @@ -34,6 +34,7 @@ "mermaid": "^11.2.1", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-force-graph-2d": "^1.25.6", "react-hot-toast": "^2.4.1", "react-i18next": "^15.0.2", "react-leaflet": "^4.2.1", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index f820677c..c0cdeeb8 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -83,6 +83,9 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + react-force-graph-2d: + specifier: ^1.25.6 + version: 1.25.6(react@18.3.1) react-hot-toast: specifier: ^2.4.1 version: 2.4.1(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1170,6 +1173,9 @@ packages: '@vue/compiler-sfc': optional: true + '@tweenjs/tween.js@25.0.0': + resolution: {integrity: sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1402,6 +1408,10 @@ packages: abort-controller-x@0.4.3: resolution: {integrity: sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==} + accessor-fn@1.5.1: + resolution: {integrity: sha512-zZpFYBqIL1Aqg+f2qmYHJ8+yIZF7/tP6PUGx2/QM0uGPSO5UegpinmkNwDohxWtOj586BpMPVRUjce2HI6xB3A==} + engines: {node: '>=12'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1502,6 +1512,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + bezier-js@6.1.4: + resolution: {integrity: sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -1536,6 +1549,10 @@ packages: caniuse-lite@1.0.30001662: resolution: {integrity: sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA==} + canvas-color-tracker@1.3.1: + resolution: {integrity: sha512-eNycxGS7oQ3IS/9QQY41f/aQjiO9Y/MtedhCgSdsbLSxC9EyUD8L3ehl/Q3Kfmvt8um79S45PBV+5Rxm5ztdSw==} + engines: {node: '>=12'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1664,6 +1681,9 @@ packages: resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} engines: {node: '>=12'} + d3-binarytree@1.0.2: + resolution: {integrity: sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==} + d3-brush@3.0.0: resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} engines: {node: '>=12'} @@ -1705,6 +1725,10 @@ packages: resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} engines: {node: '>=12'} + d3-force-3d@3.0.5: + resolution: {integrity: sha512-tdwhAhoTYZY/a6eo9nR7HP3xSW/C6XvJTbeRpR92nlPzH6OiE+4MliN9feuSFd0tPtEUo+191qOhCTWx3NYifg==} + engines: {node: '>=12'} + d3-force@3.0.0: resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} engines: {node: '>=12'} @@ -1725,6 +1749,9 @@ packages: resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} engines: {node: '>=12'} + d3-octree@1.0.2: + resolution: {integrity: sha512-Qxg4oirJrNXauiuC94uKMbgxwnhdda9xRLl9ihq45srlJ4Ga3CSgqGcAL8iW7N5CIv4Oz8x3E734ulxyvHPvwA==} + d3-path@1.0.9: resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} @@ -2036,6 +2063,10 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + force-graph@1.44.2: + resolution: {integrity: sha512-SthMFx+CdvMM4kwN/KoqmNx1//H5smwukOgQi7VsyqxDQU72vFE6/1/TDqb5NG8UnZjjaKEMcusOnAkpV+4zNA==} + engines: {node: '>=12'} + foreground-child@3.3.0: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} @@ -2043,6 +2074,9 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fromentries@1.3.2: + resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -2200,6 +2234,10 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + index-array-by@1.4.2: + resolution: {integrity: sha512-SP23P27OUKzXWEC/TOyWlwLviofQkCSCKONnc62eItjp69yCZZPqDQtr3Pw5gJDnPeUMqExmKydNZaJO0FU9pw==} + engines: {node: '>=12'} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -2359,6 +2397,10 @@ packages: javascript-natural-sort@0.7.1: resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} + jerrypick@1.1.1: + resolution: {integrity: sha512-XTtedPYEyVp4t6hJrXuRKr/jHj8SC4z+4K0b396PMkov6muL+i8IIamJIvZWe3jUspgIJak0P+BaWKawMYNBLg==} + engines: {node: '>=12'} + jiti@1.21.6: resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true @@ -2402,6 +2444,10 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + kapsule@1.14.6: + resolution: {integrity: sha512-wSi6tHNOfXrIK2Pvv6BhZ9ukzhbp+XZlOOPWSVGUbqfFsnnli4Eq8FN6TaWJv2e17sY5+fKYVxa4DP2oPGlKhg==} + engines: {node: '>=12'} + katex@0.16.11: resolution: {integrity: sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==} hasBin: true @@ -2769,6 +2815,12 @@ packages: peerDependencies: react: ^18.3.1 + react-force-graph-2d@1.25.6: + resolution: {integrity: sha512-qFarXF1pazVtGUoJul7cecqVGdfLoMQiX/98mewULM9YXDXesDfVExBaLkoz9KHN/guWdoSfH2sTUHQA9hpmDw==} + engines: {node: '>=12'} + peerDependencies: + react: '*' + react-hot-toast@2.4.1: resolution: {integrity: sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==} engines: {node: '>=10'} @@ -2795,6 +2847,12 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-kapsule@2.4.1: + resolution: {integrity: sha512-dAPhdUHZWbCH46lF2wHSqtJ3bdmNCYiV10v7QBgzYGAVMup/SZt6WHgP0PzURQvinVRKpq6PmsO4af1W37mQdg==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16.13.1' + react-leaflet@4.2.1: resolution: {integrity: sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==} peerDependencies: @@ -3139,6 +3197,9 @@ packages: resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} engines: {node: '>=10'} + tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + tinyexec@0.3.0: resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} @@ -4290,6 +4351,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@tweenjs/tween.js@25.0.0': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.25.6 @@ -4575,6 +4638,8 @@ snapshots: abort-controller-x@0.4.3: {} + accessor-fn@1.5.1: {} + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: acorn: 8.12.1 @@ -4697,6 +4762,8 @@ snapshots: balanced-match@1.0.2: {} + bezier-js@6.1.4: {} + binary-extensions@2.3.0: {} brace-expansion@1.1.11: @@ -4733,6 +4800,10 @@ snapshots: caniuse-lite@1.0.30001662: {} + canvas-color-tracker@1.3.1: + dependencies: + tinycolor2: 1.6.0 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -4869,6 +4940,8 @@ snapshots: d3-axis@3.0.0: {} + d3-binarytree@1.0.2: {} + d3-brush@3.0.0: dependencies: d3-dispatch: 3.0.1 @@ -4910,6 +4983,14 @@ snapshots: dependencies: d3-dsv: 3.0.1 + d3-force-3d@3.0.5: + dependencies: + d3-binarytree: 1.0.2 + d3-dispatch: 3.0.1 + d3-octree: 1.0.2 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + d3-force@3.0.0: dependencies: d3-dispatch: 3.0.1 @@ -4928,6 +5009,8 @@ snapshots: dependencies: d3-color: 3.1.0 + d3-octree@1.0.2: {} + d3-path@1.0.9: {} d3-path@3.1.0: {} @@ -5392,6 +5475,23 @@ snapshots: dependencies: is-callable: 1.2.7 + force-graph@1.44.2: + dependencies: + '@tweenjs/tween.js': 25.0.0 + accessor-fn: 1.5.1 + bezier-js: 6.1.4 + canvas-color-tracker: 1.3.1 + d3-array: 3.2.4 + d3-drag: 3.0.0 + d3-force-3d: 3.0.5 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + index-array-by: 1.4.2 + kapsule: 1.14.6 + lodash-es: 4.17.21 + foreground-child@3.3.0: dependencies: cross-spawn: 7.0.3 @@ -5399,6 +5499,8 @@ snapshots: fraction.js@4.3.7: {} + fromentries@1.3.2: {} + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -5552,6 +5654,8 @@ snapshots: imurmurhash@0.1.4: {} + index-array-by@1.4.2: {} + inflight@1.0.6: dependencies: once: 1.4.0 @@ -5703,6 +5807,8 @@ snapshots: javascript-natural-sort@0.7.1: {} + jerrypick@1.1.1: {} + jiti@1.21.6: {} js-base64@3.7.7: {} @@ -5734,6 +5840,10 @@ snapshots: object.assign: 4.1.5 object.values: 1.2.0 + kapsule@1.14.6: + dependencies: + lodash-es: 4.17.21 + katex@0.16.11: dependencies: commander: 8.3.0 @@ -6123,6 +6233,13 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-force-graph-2d@1.25.6(react@18.3.1): + dependencies: + force-graph: 1.44.2 + prop-types: 15.8.1 + react: 18.3.1 + react-kapsule: 2.4.1(react@18.3.1) + react-hot-toast@2.4.1(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: goober: 2.1.14(csstype@3.1.3) @@ -6144,6 +6261,12 @@ snapshots: react-is@18.3.1: {} + react-kapsule@2.4.1(react@18.3.1): + dependencies: + fromentries: 1.3.2 + jerrypick: 1.1.1 + react: 18.3.1 + react-leaflet@4.2.1(leaflet@1.9.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@react-leaflet/core': 2.1.0(leaflet@1.9.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -6556,6 +6679,8 @@ snapshots: throttle-debounce@3.0.1: {} + tinycolor2@1.6.0: {} + tinyexec@0.3.0: {} to-fast-properties@2.0.0: {} diff --git a/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx b/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx index 950496dd..3c0dc86e 100644 --- a/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx +++ b/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx @@ -3,6 +3,7 @@ import { isEqual } from "lodash-es"; import { CheckCircleIcon, Code2Icon, HashIcon, LinkIcon } from "lucide-react"; import { Memo, MemoProperty } from "@/types/proto/api/v1/memo_service"; import { useTranslate } from "@/utils/i18n"; +import MemoRelationForceGraph from "../MemoRelationForceGraph"; interface Props { memo: Memo; @@ -21,7 +22,13 @@ const MemoDetailSidebar = ({ memo, className }: Props) => { className, )} > -
Created at
diff --git a/web/src/components/MemoEditor/ActionButton/AddMemoRelationPopover.tsx b/web/src/components/MemoEditor/ActionButton/AddMemoRelationPopover.tsx
index 0ce91612..df8dfc55 100644
--- a/web/src/components/MemoEditor/ActionButton/AddMemoRelationPopover.tsx
+++ b/web/src/components/MemoEditor/ActionButton/AddMemoRelationPopover.tsx
@@ -27,7 +27,7 @@ const AddMemoRelationPopover = (props: Props) => {
const [isFetching, setIsFetching] = useState