diff --git a/web/package.json b/web/package.json index 229c3885a..fdb5287c7 100644 --- a/web/package.json +++ b/web/package.json @@ -51,7 +51,6 @@ "mime": "^4.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-force-graph-2d": "^1.29.1", "react-hot-toast": "^2.6.0", "react-i18next": "^15.7.4", "react-leaflet": "^4.2.1", @@ -73,7 +72,6 @@ }, "devDependencies": { "@biomejs/biome": "^2.4.7", - "baseline-browser-mapping": "^2.10.8", "@bufbuild/protobuf": "^2.11.0", "@types/d3": "^7.4.3", "@types/hast": "^3.0.4", @@ -89,6 +87,7 @@ "@types/unist": "^3.0.3", "@types/uuid": "^10.0.0", "@vitejs/plugin-react": "^4.7.0", + "baseline-browser-mapping": "^2.10.8", "long": "^5.3.2", "terser": "^5.46.1", "tw-animate-css": "^1.4.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index aa478d5f0..cdab6ac8a 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -122,9 +122,6 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) - react-force-graph-2d: - specifier: ^1.29.1 - version: 1.29.1(react@18.3.1) react-hot-toast: specifier: ^2.6.0 version: 2.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1363,9 +1360,6 @@ packages: peerDependencies: react: ^18 || ^19 - '@tweenjs/tween.js@25.0.0': - resolution: {integrity: sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==} - '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1557,10 +1551,6 @@ packages: '@xobotyi/scrollbar-width@1.9.5': resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} - accessor-fn@1.5.3: - resolution: {integrity: sha512-rkAofCwe/FvYFUlMB0v0gWmhqtfAtV1IUkdPbfhTUyYniu5LrC0A0UJkTH0Jv3S8SvwkmfuAlY+mQIJATdocMA==} - engines: {node: '>=12'} - acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -1582,9 +1572,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - bezier-js@6.1.4: - resolution: {integrity: sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==} - browserslist@4.28.0: resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1600,10 +1587,6 @@ packages: caniuse-lite@1.0.30001757: resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} - canvas-color-tracker@1.3.2: - resolution: {integrity: sha512-ryQkDX26yJ3CXzb3hxUVNlg1NKE4REc5crLBq661Nxzr8TNd236SaEf2ffYLXyI5tSABSeguHLqcVq4vf9L3Zg==} - engines: {node: '>=12'} - ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -1712,9 +1695,6 @@ 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'} @@ -1756,10 +1736,6 @@ packages: resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} engines: {node: '>=12'} - d3-force-3d@3.0.6: - resolution: {integrity: sha512-4tsKHUPLOVkyfEffZo1v6sFHvGFwAIIjt/W8IThbp08DYAsXZck+2pSHEG5W1+gQgEvFLdZkYvmJAbRM2EzMnA==} - engines: {node: '>=12'} - d3-force@3.0.0: resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} engines: {node: '>=12'} @@ -1780,9 +1756,6 @@ packages: resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} engines: {node: '>=12'} - d3-octree@1.1.0: - resolution: {integrity: sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A==} - d3-path@1.0.9: resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} @@ -1952,14 +1925,6 @@ packages: find-root@1.1.0: resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - float-tooltip@1.7.5: - resolution: {integrity: sha512-/kXzuDnnBqyyWyhDMH7+PfP8J/oXiAavGzcRxASOMRHFuReDtofizLLJsf7nnDLAfEaMW4pVWaXrAjtnglpEkg==} - engines: {node: '>=12'} - - force-graph@1.51.0: - resolution: {integrity: sha512-aTnihCmiMA0ItLJLCbrQYS9mzriopW24goFPgUnKAAmAlPogTSmFWqoBPMXzIfPb7bs04Hur5zEI4WYgLW3Sig==} - engines: {node: '>=12'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2073,10 +2038,6 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - index-array-by@1.4.2: - resolution: {integrity: sha512-SP23P27OUKzXWEC/TOyWlwLviofQkCSCKONnc62eItjp69yCZZPqDQtr3Pw5gJDnPeUMqExmKydNZaJO0FU9pw==} - engines: {node: '>=12'} - inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -2113,10 +2074,6 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - jerrypick@1.1.2: - resolution: {integrity: sha512-YKnxXEekXKzhpf7CLYA0A+oDP8V0OhICNCr5lv96FvSsDEmrb0GKM776JgQvHTMjr7DTTPEVv/1Ciaw0uEWzBA==} - engines: {node: '>=12'} - jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -2140,10 +2097,6 @@ packages: engines: {node: '>=6'} hasBin: true - kapsule@1.16.3: - resolution: {integrity: sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg==} - engines: {node: '>=12'} - katex@0.16.38: resolution: {integrity: sha512-cjHooZUmIAUmDsHBN+1n8LaZdpmbj03LtYeYPyuYB7OuloiaeaV6N4LcfjcnHVzGWjVQmKrxxTrpDcmSzEZQwQ==} hasBin: true @@ -2454,10 +2407,6 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - package-manager-detector@1.5.0: resolution: {integrity: sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==} @@ -2511,12 +2460,6 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - preact@10.27.2: - resolution: {integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==} - - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - property-information@6.5.0: resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} @@ -2531,12 +2474,6 @@ packages: peerDependencies: react: ^18.3.1 - react-force-graph-2d@1.29.1: - resolution: {integrity: sha512-1Rl/1Z3xy2iTHKj6a0jRXGyiI86xUti81K+jBQZ+Oe46csaMikp47L5AjrzA9hY9fNGD63X8ffrqnvaORukCuQ==} - engines: {node: '>=12'} - peerDependencies: - react: '*' - react-hot-toast@2.6.0: resolution: {integrity: sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==} engines: {node: '>=10'} @@ -2563,12 +2500,6 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-kapsule@2.5.7: - resolution: {integrity: sha512-kifAF4ZPD77qZKc4CKLmozq6GY1sBzPEJTIJb0wWFK6HsePJatK3jXplZn2eeAt3x67CDozgi7/rO8fNQ/AL7A==} - engines: {node: '>=12'} - peerDependencies: - react: '>=16.13.1' - react-leaflet-cluster@2.1.0: resolution: {integrity: sha512-16X7XQpRThQFC4PH4OpXHimGg19ouWmjxjtpxOeBKpvERSvIRqTx7fvhTwkEPNMFTQ8zTfddz6fRTUmUEQul7g==} peerDependencies: @@ -2809,9 +2740,6 @@ packages: resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} engines: {node: '>=10'} - tinycolor2@1.6.0: - resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - tinyexec@1.0.2: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} @@ -3997,8 +3925,6 @@ snapshots: '@tanstack/query-core': 5.90.20 react: 18.3.1 - '@tweenjs/tween.js@25.0.0': {} - '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.5 @@ -4224,8 +4150,6 @@ snapshots: '@xobotyi/scrollbar-width@1.9.5': {} - accessor-fn@1.5.3: {} - acorn@8.15.0: {} aria-hidden@1.2.6: @@ -4242,8 +4166,6 @@ snapshots: baseline-browser-mapping@2.10.8: {} - bezier-js@6.1.4: {} - browserslist@4.28.0: dependencies: baseline-browser-mapping: 2.10.8 @@ -4258,10 +4180,6 @@ snapshots: caniuse-lite@1.0.30001757: {} - canvas-color-tracker@1.3.2: - dependencies: - tinycolor2: 1.6.0 - ccount@2.0.1: {} character-entities-html4@2.1.0: {} @@ -4363,8 +4281,6 @@ snapshots: d3-axis@3.0.0: {} - d3-binarytree@1.0.2: {} - d3-brush@3.0.0: dependencies: d3-dispatch: 3.0.1 @@ -4406,14 +4322,6 @@ snapshots: dependencies: d3-dsv: 3.0.1 - d3-force-3d@3.0.6: - dependencies: - d3-binarytree: 1.0.2 - d3-dispatch: 3.0.1 - d3-octree: 1.1.0 - d3-quadtree: 3.0.1 - d3-timer: 3.0.1 - d3-force@3.0.0: dependencies: d3-dispatch: 3.0.1 @@ -4432,8 +4340,6 @@ snapshots: dependencies: d3-color: 3.1.0 - d3-octree@1.1.0: {} - d3-path@1.0.9: {} d3-path@3.1.0: {} @@ -4635,30 +4541,6 @@ snapshots: find-root@1.1.0: {} - float-tooltip@1.7.5: - dependencies: - d3-selection: 3.0.0 - kapsule: 1.16.3 - preact: 10.27.2 - - force-graph@1.51.0: - dependencies: - '@tweenjs/tween.js': 25.0.0 - accessor-fn: 1.5.3 - bezier-js: 6.1.4 - canvas-color-tracker: 1.3.2 - d3-array: 3.2.4 - d3-drag: 3.0.0 - d3-force-3d: 3.0.6 - d3-scale: 4.0.2 - d3-scale-chromatic: 3.1.0 - d3-selection: 3.0.0 - d3-zoom: 3.0.0 - float-tooltip: 1.7.5 - index-array-by: 1.4.2 - kapsule: 1.16.3 - lodash-es: 4.17.23 - fsevents@2.3.3: optional: true @@ -4827,8 +4709,6 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - index-array-by@1.4.2: {} - inline-style-parser@0.2.7: {} inline-style-prefixer@7.0.1: @@ -4858,8 +4738,6 @@ snapshots: is-plain-obj@4.1.0: {} - jerrypick@1.1.2: {} - jiti@2.6.1: {} js-cookie@2.2.1: {} @@ -4872,10 +4750,6 @@ snapshots: json5@2.2.3: {} - kapsule@1.16.3: - dependencies: - lodash-es: 4.17.23 - katex@0.16.38: dependencies: commander: 8.3.0 @@ -5412,8 +5286,6 @@ snapshots: node-releases@2.0.27: {} - object-assign@4.1.1: {} - package-manager-detector@1.5.0: {} parent-module@1.0.1: @@ -5478,14 +5350,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - preact@10.27.2: {} - - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - property-information@6.5.0: {} property-information@7.1.0: {} @@ -5498,13 +5362,6 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 - react-force-graph-2d@1.29.1(react@18.3.1): - dependencies: - force-graph: 1.51.0 - prop-types: 15.8.1 - react: 18.3.1 - react-kapsule: 2.5.7(react@18.3.1) - react-hot-toast@2.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: csstype: 3.2.3 @@ -5524,11 +5381,6 @@ snapshots: react-is@16.13.1: {} - react-kapsule@2.5.7(react@18.3.1): - dependencies: - jerrypick: 1.1.2 - react: 18.3.1 - react-leaflet-cluster@2.1.0(leaflet@1.9.4)(react-dom@18.3.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))(react@18.3.1): dependencies: leaflet: 1.9.4 @@ -5838,8 +5690,6 @@ snapshots: throttle-debounce@3.0.1: {} - tinycolor2@1.6.0: {} - tinyexec@1.0.2: {} tinyglobby@0.2.15: diff --git a/web/src/components/MemoCommentSection.tsx b/web/src/components/MemoCommentSection.tsx new file mode 100644 index 000000000..e8f01b05e --- /dev/null +++ b/web/src/components/MemoCommentSection.tsx @@ -0,0 +1,79 @@ +import { MessageCircleIcon } from "lucide-react"; +import { useState } from "react"; +import MemoEditor from "@/components/MemoEditor"; +import MemoView from "@/components/MemoView"; +import { Button } from "@/components/ui/button"; +import { extractMemoIdFromName } from "@/helpers/resource-names"; +import useCurrentUser from "@/hooks/useCurrentUser"; +import type { Memo } from "@/types/proto/api/v1/memo_service_pb"; +import { useTranslate } from "@/utils/i18n"; + +interface Props { + memo: Memo; + comments: Memo[]; + parentPage?: string; +} + +const MemoCommentSection = ({ memo, comments, parentPage }: Props) => { + const t = useTranslate(); + const currentUser = useCurrentUser(); + const [showEditor, setShowEditor] = useState(false); + + const showCreateButton = currentUser && !showEditor; + + const handleCommentCreated = async (_memoCommentName: string) => { + setShowEditor(false); + }; + + return ( +
+

+ {t("memo.comment.self")} +

+
+ {comments.length === 0 ? ( + showCreateButton && ( +
+ +
+ ) + ) : ( +
+
+ + {t("memo.comment.self")} + ({comments.length}) +
+ {showCreateButton && ( + + )} +
+ )} + {showEditor && ( +
+ setShowEditor(false)} + /> +
+ )} + {comments.map((comment) => ( +
+ +
+ ))} +
+
+ ); +}; + +export default MemoCommentSection; diff --git a/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx b/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx index c96c46fcf..72093f641 100644 --- a/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx +++ b/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx @@ -1,116 +1,104 @@ import { create } from "@bufbuild/protobuf"; import { timestampDate } from "@bufbuild/protobuf/wkt"; import { isEqual } from "lodash-es"; -import { CheckCircleIcon, Code2Icon, HashIcon, LinkIcon, Share2Icon } from "lucide-react"; -import { useState } from "react"; -import MemoSharePanel from "@/components/MemoSharePanel"; +import { CheckCircleIcon, Code2Icon, HashIcon, LinkIcon, type LucideIcon, Share2Icon } from "lucide-react"; +import { useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import useCurrentUser from "@/hooks/useCurrentUser"; import { cn } from "@/lib/utils"; -import { Memo, Memo_PropertySchema, MemoRelation_Type } from "@/types/proto/api/v1/memo_service_pb"; -import { useTranslate } from "@/utils/i18n"; +import { Memo, Memo_PropertySchema } from "@/types/proto/api/v1/memo_service_pb"; +import { type Translations, useTranslate } from "@/utils/i18n"; import { isSuperUser } from "@/utils/user"; -import MemoRelationForceGraph from "../MemoRelationForceGraph"; +import MemoSharePanel from "./MemoSharePanel"; interface Props { memo: Memo; className?: string; - parentPage?: string; } -const SectionLabel = ({ children }: { children: React.ReactNode }) => ( -

{children}

+interface PropertyBadge { + icon: LucideIcon; + labelKey: Translations; +} + +const SidebarSection = ({ label, count, children }: { label: string; count?: number; children: React.ReactNode }) => ( +
+
+

{label}

+ {count != null && ({count})} +
+ {children} +
); -const MemoDetailSidebar = ({ memo, className, parentPage }: Props) => { +const PROPERTY_BADGE_CLASSES = + "inline-flex items-center gap-1.5 px-2 py-1 rounded-md border border-border/60 bg-muted/60 text-xs text-muted-foreground"; + +const TAG_BADGE_CLASSES = + "inline-flex items-center gap-1 px-1 rounded-md border border-border/60 bg-muted/60 text-sm text-muted-foreground hover:bg-muted hover:text-foreground/80 transition-colors cursor-pointer"; + +const MemoDetailSidebar = ({ memo, className }: Props) => { const t = useTranslate(); const currentUser = useCurrentUser(); const [sharePanelOpen, setSharePanelOpen] = useState(false); const property = create(Memo_PropertySchema, memo.property || {}); - const hasSpecialProperty = property.hasLink || property.hasTaskList || property.hasCode; - const hasReferenceRelations = memo.relations.some((r) => r.type === MemoRelation_Type.REFERENCE); const canManageShares = !memo.parent && (memo.creator === currentUser?.name || isSuperUser(currentUser)); + const hasUpdated = !isEqual(memo.createTime, memo.updateTime); + + const propertyBadges = useMemo(() => { + const badges: PropertyBadge[] = []; + if (property.hasLink) badges.push({ icon: LinkIcon, labelKey: "memo.links" }); + if (property.hasTaskList) badges.push({ icon: CheckCircleIcon, labelKey: "memo.to-do" }); + if (property.hasCode) badges.push({ icon: Code2Icon, labelKey: "memo.code" }); + return badges; + }, [property.hasLink, property.hasTaskList, property.hasCode]); return (