heartbeads 0.6.2 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +2 -2
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_global-error.html +2 -2
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/docs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/docs.html +2 -2
- package/.next/server/app/api/docs.rsc +2 -2
- package/.next/server/app/api/docs.segments/_full.segment.rsc +2 -2
- package/.next/server/app/api/docs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/api/docs.segments/_index.segment.rsc +2 -2
- package/.next/server/app/api/docs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/docs.segments/api/docs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/api/docs.segments/api/docs.segment.rsc +1 -1
- package/.next/server/app/api/docs.segments/api.segment.rsc +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +3 -3
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +1 -1
- package/.next/server/app/login.rsc +2 -2
- package/.next/server/app/login.segments/_full.segment.rsc +2 -2
- package/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/.next/server/app/login.segments/_index.segment.rsc +2 -2
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__04e6e565._.js +3 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__04e6e565._.js.map +1 -1
- package/.next/server/middleware/middleware-manifest.json +1 -1
- package/.next/server/middleware-manifest.json +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +2 -2
- package/.next/static/chunks/{7e4fcecd4ca504d5.js → 2394a2e7d0fff196.js} +3 -3
- package/.next/static/chunks/295ab055d1c2269f.css +1 -0
- package/app/page.tsx +163 -0
- package/components/AllCommentsPanel.tsx +177 -128
- package/components/BeadsGraph.tsx +8 -2
- package/components/DescriptionModal.tsx +51 -0
- package/components/ProfilePanel.tsx +107 -86
- package/package.json +1 -1
- package/.next/static/chunks/5d4996401af7e2d1.css +0 -1
- /package/.next/static/{zq8Ew2hci2YIJ61-i832n → 9kusRFLCVoWHASQ-qfwqV}/_buildManifest.js +0 -0
- /package/.next/static/{zq8Ew2hci2YIJ61-i832n → 9kusRFLCVoWHASQ-qfwqV}/_clientMiddlewareManifest.json +0 -0
- /package/.next/static/{zq8Ew2hci2YIJ61-i832n → 9kusRFLCVoWHASQ-qfwqV}/_ssgManifest.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border:0 solid #e5e7eb}:before,:after{--tw-content:""}html,:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:JetBrains Mono,Fira Code,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:#0000;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder{opacity:1;color:#9ca3af}textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html,body{height:100%;overflow:hidden}body{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body:is(.dark *){--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}body{font-feature-settings:"rlig" 1,"calt" 1}.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.status-badge{border-radius:9999px;align-items:center;padding:.125rem .5rem;font-size:.75rem;font-weight:500;line-height:1rem;display:inline-flex}.custom-scrollbar::-webkit-scrollbar{width:4px}.custom-scrollbar::-webkit-scrollbar-track{background-color:#0000}.custom-scrollbar::-webkit-scrollbar-thumb{--tw-bg-opacity:1;background-color:rgb(228 228 231/var(--tw-bg-opacity,1));border-radius:9999px}.custom-scrollbar:is(.dark *)::-webkit-scrollbar-thumb{--tw-bg-opacity:1;background-color:rgb(63 63 70/var(--tw-bg-opacity,1))}.custom-scrollbar::-webkit-scrollbar-thumb:hover{--tw-bg-opacity:1;background-color:rgb(212 212 216/var(--tw-bg-opacity,1))}.custom-scrollbar:is(.dark *)::-webkit-scrollbar-thumb:hover{--tw-bg-opacity:1;background-color:rgb(82 82 91/var(--tw-bg-opacity,1))}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{left:0;right:0}.bottom-0{bottom:0}.bottom-2{bottom:.5rem}.bottom-4{bottom:1rem}.left-0{left:0}.left-2{left:.5rem}.left-3{left:.75rem}.left-4{left:1rem}.right-0{right:0}.right-2{right:.5rem}.right-3{right:.75rem}.top-0{top:0}.top-1\/2{top:50%}.top-2{top:.5rem}.top-3{top:.75rem}.top-full{top:100%}.isolate{isolation:isolate}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-50{z-index:50}.z-\[100\]{z-index:100}.z-\[110\]{z-index:110}.z-\[1\]{z-index:1}.z-\[55\]{z-index:55}.z-\[56\]{z-index:56}.z-\[60\]{z-index:60}.z-\[99\]{z-index:99}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.-mr-0\.5{margin-right:-.125rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-10{margin-bottom:2.5rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-2\.5{margin-bottom:.625rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-1\.5{margin-right:.375rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mt-\[6px\]{margin-top:6px}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[env\(safe-area-inset-bottom\)\]{height:env(safe-area-inset-bottom)}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-40{max-height:10rem}.max-h-72{max-height:18rem}.max-h-\[220px\]{max-height:220px}.max-h-\[260px\]{max-height:260px}.max-h-\[60vh\]{max-height:60vh}.max-h-\[70vh\]{max-height:70vh}.max-h-\[80vh\]{max-height:80vh}.min-h-\[300px\]{min-height:300px}.min-h-screen{min-height:100vh}.w-0\.5{width:.125rem}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-44{width:11rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-60{width:15rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[360px\]{width:360px}.w-\[90vw\]{width:90vw}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0}.min-w-\[18px\]{min-width:18px}.min-w-\[280px\]{min-width:280px}.min-w-\[3ch\]{min-width:3ch}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-\[100px\]{max-width:100px}.max-w-\[180px\]{max-width:180px}.max-w-\[480px\]{max-width:480px}.max-w-\[calc\(100vw-2rem\)\]{max-width:calc(100vw - 2rem)}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.translate-x-full{--tw-translate-x:100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.translate-y-full{--tw-translate-y:100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:2s cubic-bezier(.4,0,.6,1) infinite pulse}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:1s linear infinite spin}.cursor-crosshair{cursor:crosshair}.cursor-e-resize{cursor:e-resize}.cursor-n-resize{cursor:n-resize}.cursor-ne-resize{cursor:ne-resize}.cursor-pointer{cursor:pointer}.cursor-wait{cursor:wait}.select-none{-webkit-user-select:none;user-select:none}.resize-none{resize:none}.resize{resize:both}.scroll-mt-8{scroll-margin-top:2rem}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-y-1{row-gap:.25rem}.gap-y-1\.5{row-gap:.375rem}.space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-zinc-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(250 250 250/var(--tw-divide-opacity,1))}.self-stretch{align-self:stretch}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.rounded-r-lg{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem}.rounded-t-2xl{border-top-left-radius:1rem;border-top-right-radius:1rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.rounded-tr-lg{border-top-right-radius:.5rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-emerald-200{--tw-border-opacity:1;border-color:rgb(167 243 208/var(--tw-border-opacity,1))}.border-red-100{--tw-border-opacity:1;border-color:rgb(254 226 226/var(--tw-border-opacity,1))}.border-transparent{border-color:#0000}.border-white\/30{border-color:#ffffff4d}.border-zinc-100{--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity,1))}.border-zinc-100\/60{border-color:#f4f4f599}.border-zinc-100\/80{border-color:#f4f4f5cc}.border-zinc-200{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.border-zinc-200\/60{border-color:#e4e4e799}.border-zinc-200\/80{border-color:#e4e4e7cc}.border-zinc-50{--tw-border-opacity:1;border-color:rgb(250 250 250/var(--tw-border-opacity,1))}.border-t-white{--tw-border-opacity:1;border-top-color:rgb(255 255 255/var(--tw-border-opacity,1))}.border-t-zinc-400{--tw-border-opacity:1;border-top-color:rgb(161 161 170/var(--tw-border-opacity,1))}.bg-amber-400{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity,1))}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity,1))}.bg-black\/20{background-color:#0003}.bg-black\/30{background-color:#0000004d}.bg-black\/40{background-color:#0006}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity,1))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.bg-emerald-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.bg-emerald-400{--tw-bg-opacity:1;background-color:rgb(52 211 153/var(--tw-bg-opacity,1))}.bg-emerald-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.bg-emerald-50\/30{background-color:#ecfdf54d}.bg-emerald-50\/90{background-color:#ecfdf5e6}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-emerald-600{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.bg-red-300{--tw-bg-opacity:1;background-color:rgb(252 165 165/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-rose-300{--tw-bg-opacity:1;background-color:rgb(253 164 175/var(--tw-bg-opacity,1))}.bg-teal-100{--tw-bg-opacity:1;background-color:rgb(204 251 241/var(--tw-bg-opacity,1))}.bg-teal-500{--tw-bg-opacity:1;background-color:rgb(20 184 166/var(--tw-bg-opacity,1))}.bg-transparent{background-color:#0000}.bg-violet-400{--tw-bg-opacity:1;background-color:rgb(167 139 250/var(--tw-bg-opacity,1))}.bg-violet-500{--tw-bg-opacity:1;background-color:rgb(139 92 246/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/90{background-color:#ffffffe6}.bg-white\/95{background-color:#fffffff2}.bg-zinc-100{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.bg-zinc-200{--tw-bg-opacity:1;background-color:rgb(228 228 231/var(--tw-bg-opacity,1))}.bg-zinc-200\/80{background-color:#e4e4e7cc}.bg-zinc-300{--tw-bg-opacity:1;background-color:rgb(212 212 216/var(--tw-bg-opacity,1))}.bg-zinc-400{--tw-bg-opacity:1;background-color:rgb(161 161 170/var(--tw-bg-opacity,1))}.bg-zinc-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.bg-zinc-50\/30{background-color:#fafafa4d}.bg-zinc-50\/50{background-color:#fafafa80}.bg-zinc-50\/80{background-color:#fafafacc}.bg-zinc-50\/95{background-color:#fafafaf2}.bg-zinc-800{--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.bg-zinc-950{--tw-bg-opacity:1;background-color:rgb(9 9 11/var(--tw-bg-opacity,1))}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-\[18px_20px\]{padding:18px 20px}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pb-6{padding-bottom:1.5rem}.pl-3{padding-left:.75rem}.pl-7{padding-left:1.75rem}.pr-1{padding-right:.25rem}.pr-10{padding-right:2.5rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pr-7{padding-right:1.75rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.pt-\[20vh\]{padding-top:20vh}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:JetBrains Mono,Fira Code,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-7xl{font-size:4.5rem;line-height:1}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[15px\]{font-size:15px}.text-\[7px\]{font-size:7px}.text-\[8px\]{font-size:8px}.text-\[9px\]{font-size:9px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-extralight{font-weight:200}.font-light{font-weight:300}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal)var(--tw-slashed-zero)var(--tw-numeric-figure)var(--tw-numeric-spacing)var(--tw-numeric-fraction)}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-amber-500{--tw-text-opacity:1;color:rgb(245 158 11/var(--tw-text-opacity,1))}.text-amber-700{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-emerald-500{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity,1))}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.text-emerald-700{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-rose-400{--tw-text-opacity:1;color:rgb(251 113 133/var(--tw-text-opacity,1))}.text-rose-500{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.text-teal-500{--tw-text-opacity:1;color:rgb(20 184 166/var(--tw-text-opacity,1))}.text-teal-600{--tw-text-opacity:1;color:rgb(13 148 136/var(--tw-text-opacity,1))}.text-violet-500{--tw-text-opacity:1;color:rgb(139 92 246/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-zinc-200{--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.text-zinc-300{--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.text-zinc-400{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.text-zinc-500{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.text-zinc-600{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.text-zinc-700{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.text-zinc-800{--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.text-zinc-900{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.decoration-emerald-300{text-decoration-color:#6ee7b7}.underline-offset-2{text-underline-offset:2px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-zinc-400::placeholder{--tw-placeholder-opacity:1;color:rgb(161 161 170/var(--tw-placeholder-opacity,1))}.opacity-0{opacity:0}.opacity-25{opacity:.25}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.opacity-\[0\.03\]{opacity:.03}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px #00000040;--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline-offset:2px;outline:2px solid #0000}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(3px + var(--tw-ring-offset-width))var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-emerald-400\/60{--tw-ring-color:#34d39999}.ring-zinc-100{--tw-ring-opacity:1;--tw-ring-color:rgb(244 244 245/var(--tw-ring-opacity,1))}.ring-zinc-300{--tw-ring-opacity:1;--tw-ring-color:rgb(212 212 216/var(--tw-ring-opacity,1))}.blur{--tw-blur:blur(8px);filter:var(--tw-blur)var(--tw-brightness)var(--tw-contrast)var(--tw-grayscale)var(--tw-hue-rotate)var(--tw-invert)var(--tw-saturate)var(--tw-sepia)var(--tw-drop-shadow)}.\!filter{filter:var(--tw-blur)var(--tw-brightness)var(--tw-contrast)var(--tw-grayscale)var(--tw-hue-rotate)var(--tw-invert)var(--tw-saturate)var(--tw-sepia)var(--tw-drop-shadow)!important}.filter{filter:var(--tw-blur)var(--tw-brightness)var(--tw-contrast)var(--tw-grayscale)var(--tw-hue-rotate)var(--tw-invert)var(--tw-saturate)var(--tw-sepia)var(--tw-drop-shadow)}.backdrop-blur-\[2px\]{--tw-backdrop-blur:blur(2px);-webkit-backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-\[right\]{transition-property:right;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-property:all;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-property:transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@keyframes fadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.animate-fade-in{animation:.3s ease-out forwards fadeIn}@keyframes pulseSoft{0%,to{opacity:1}50%{opacity:.6}}.animate-pulse-soft{animation:2s ease-in-out infinite pulseSoft}@keyframes beadTooltipFade{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes slideInRight{0%{transform:translate(100%)}to{transform:translate(0)}}.animate-slide-in-right{animation:.25s ease-out slideInRight}@keyframes slideUpSheet{0%{transform:translateY(100%)}to{transform:translateY(0)}}.animate-slide-up-sheet{animation:.2s ease-out slideUpSheet}.description-markdown h1,.description-markdown h2,.description-markdown h3,.description-markdown h4{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1));margin-top:.5rem;margin-bottom:.25rem;font-weight:600}.description-markdown h1:is(.dark *),.description-markdown h2:is(.dark *),.description-markdown h3:is(.dark *),.description-markdown h4:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.description-markdown h1{font-size:.85rem}.description-markdown h2{font-size:.8rem}.description-markdown h3{font-size:.75rem}.description-markdown h4{font-size:.7rem}.description-markdown p{margin-bottom:.375rem}.description-markdown p:last-child{margin-bottom:0}.description-markdown ul,.description-markdown ol{margin-bottom:.375rem}.description-markdown ul>:not([hidden])~:not([hidden]),.description-markdown ol>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem*var(--tw-space-y-reverse))}.description-markdown ul,.description-markdown ol{padding-left:1rem}.description-markdown ul{list-style-type:disc}.description-markdown ol{list-style-type:decimal}.description-markdown code{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1));background-color:#e4e4e799;border-radius:.25rem;padding:.125rem .25rem;font-family:JetBrains Mono,Fira Code,monospace;font-size:10px}.description-markdown code:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1));background-color:#3f3f4699}.description-markdown pre{background-color:#e4e4e799;border-radius:.375rem;margin-bottom:.375rem;padding:.5rem;overflow-x:auto}.description-markdown pre:is(.dark *){background-color:#27272a99}.description-markdown pre code{background-color:#0000;padding:0;font-size:10px}.description-markdown a{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1));text-underline-offset:2px;text-decoration-line:underline}.description-markdown a:hover{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity,1))}.description-markdown a:is(.dark *){--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.description-markdown a:hover:is(.dark *){--tw-text-opacity:1;color:rgb(110 231 183/var(--tw-text-opacity,1))}.description-markdown blockquote{--tw-border-opacity:1;border-left-width:2px;border-color:rgb(212 212 216/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1));margin-top:.375rem;margin-bottom:.375rem;padding-left:.5rem;font-style:italic}.description-markdown blockquote:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.description-markdown table{border-collapse:collapse;width:100%;margin-bottom:.375rem;font-size:10px}.description-markdown th{--tw-border-opacity:1;border-bottom-width:1px;border-color:rgb(228 228 231/var(--tw-border-opacity,1));text-align:left;--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1));padding-bottom:.125rem;padding-right:.5rem;font-weight:600}.description-markdown th:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1));--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.description-markdown td{--tw-border-opacity:1;border-bottom-width:1px;border-color:rgb(244 244 245/var(--tw-border-opacity,1));padding-top:.125rem;padding-bottom:.125rem;padding-right:.5rem}.description-markdown td:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.description-markdown hr{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1));margin-top:.5rem;margin-bottom:.5rem}.description-markdown hr:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1))}.description-markdown strong{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1));font-weight:600}.description-markdown strong:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.description-markdown img{border-radius:.25rem;max-width:100%}.timeline-slider{appearance:none;cursor:pointer;background:0 0}.timeline-slider::-webkit-slider-runnable-track{background:#e4e4e7;border-radius:2px;height:4px}.dark .timeline-slider::-webkit-slider-runnable-track{background:#3f3f46}.timeline-slider::-webkit-slider-thumb{-webkit-appearance:none;cursor:pointer;background:#10b981;border-radius:50%;width:12px;height:12px;margin-top:-4px;transition:transform .1s}.timeline-slider::-webkit-slider-thumb:hover{transform:scale(1.2)}.timeline-slider::-moz-range-track{background:#e4e4e7;border:none;border-radius:2px;height:4px}.dark .timeline-slider::-moz-range-track{background:#3f3f46}.timeline-slider::-moz-range-thumb{cursor:pointer;background:#10b981;border:none;border-radius:50%;width:12px;height:12px}.force-graph-container canvas{touch-action:none;border-radius:.5rem}.placeholder\:text-zinc-300::placeholder{--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.placeholder\:text-zinc-400::placeholder{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.first\:mt-0:first-child{margin-top:0}.hover\:border-zinc-200:hover{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.hover\:border-zinc-300:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.hover\:bg-amber-50:hover{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.hover\:bg-emerald-100:hover{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.hover\:bg-emerald-200\/50:hover{background-color:#a7f3d080}.hover\:bg-emerald-50:hover{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.hover\:bg-emerald-600:hover{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.hover\:bg-emerald-700:hover{--tw-bg-opacity:1;background-color:rgb(4 120 87/var(--tw-bg-opacity,1))}.hover\:bg-red-50:hover{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.hover\:bg-zinc-100:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.hover\:bg-zinc-100\/50:hover{background-color:#f4f4f580}.hover\:bg-zinc-200:hover{--tw-bg-opacity:1;background-color:rgb(228 228 231/var(--tw-bg-opacity,1))}.hover\:bg-zinc-200\/60:hover{background-color:#e4e4e799}.hover\:bg-zinc-300\/40:hover{background-color:#d4d4d866}.hover\:bg-zinc-50:hover{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.hover\:bg-zinc-50\/50:hover{background-color:#fafafa80}.hover\:bg-zinc-50\/60:hover{background-color:#fafafa99}.hover\:text-amber-500:hover{--tw-text-opacity:1;color:rgb(245 158 11/var(--tw-text-opacity,1))}.hover\:text-emerald-300:hover{--tw-text-opacity:1;color:rgb(110 231 183/var(--tw-text-opacity,1))}.hover\:text-emerald-500:hover{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity,1))}.hover\:text-emerald-600:hover{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.hover\:text-emerald-700:hover{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity,1))}.hover\:text-red-400:hover{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.hover\:text-red-500:hover{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.hover\:text-red-600:hover{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.hover\:text-rose-500:hover{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.hover\:text-teal-600:hover{--tw-text-opacity:1;color:rgb(13 148 136/var(--tw-text-opacity,1))}.hover\:text-teal-700:hover{--tw-text-opacity:1;color:rgb(15 118 110/var(--tw-text-opacity,1))}.hover\:text-zinc-500:hover{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.hover\:text-zinc-600:hover{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.hover\:text-zinc-700:hover{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.hover\:text-zinc-900:hover{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:decoration-emerald-500:hover{text-decoration-color:#10b981}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-80:hover{opacity:.8}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:border-emerald-400:focus{--tw-border-opacity:1;border-color:rgb(52 211 153/var(--tw-border-opacity,1))}.focus\:border-emerald-500:focus{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.focus\:outline-none:focus{outline-offset:2px;outline:2px solid #0000}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-emerald-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(16 185 129/var(--tw-ring-opacity,1))}.focus\:ring-emerald-500\/30:focus{--tw-ring-color:#10b9814d}.active\:bg-emerald-50:active{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.active\:bg-red-50:active{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.active\:bg-zinc-50:active{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:hover\:bg-emerald-500:hover:disabled{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.disabled\:hover\:shadow-sm:hover:disabled{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.group:hover .group-hover\:text-emerald-500{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity,1))}.group:hover .group-hover\:text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.group:hover .group-hover\:text-emerald-700{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity,1))}.group:hover .group-hover\:text-zinc-700{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.group:hover .group-hover\:opacity-100{opacity:1}.dark\:divide-zinc-800:is(.dark *)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(39 39 42/var(--tw-divide-opacity,1))}.dark\:border-emerald-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(6 95 70/var(--tw-border-opacity,1))}.dark\:border-red-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(153 27 27/var(--tw-border-opacity,1))}.dark\:border-zinc-600:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.dark\:border-zinc-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1))}.dark\:border-zinc-700\/60:is(.dark *){border-color:#3f3f4699}.dark\:border-zinc-700\/80:is(.dark *){border-color:#3f3f46cc}.dark\:border-zinc-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.dark\:border-zinc-800\/80:is(.dark *){border-color:#27272acc}.dark\:bg-black:is(.dark *){--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.dark\:bg-black\/95:is(.dark *){background-color:#000000f2}.dark\:bg-blue-950:is(.dark *){--tw-bg-opacity:1;background-color:rgb(23 37 84/var(--tw-bg-opacity,1))}.dark\:bg-emerald-950:is(.dark *){--tw-bg-opacity:1;background-color:rgb(2 44 34/var(--tw-bg-opacity,1))}.dark\:bg-emerald-950\/30:is(.dark *){background-color:#022c224d}.dark\:bg-emerald-950\/90:is(.dark *){background-color:#022c22e6}.dark\:bg-red-950:is(.dark *){--tw-bg-opacity:1;background-color:rgb(69 10 10/var(--tw-bg-opacity,1))}.dark\:bg-teal-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(19 78 74/var(--tw-bg-opacity,1))}.dark\:bg-zinc-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(82 82 91/var(--tw-bg-opacity,1))}.dark\:bg-zinc-700:is(.dark *){--tw-bg-opacity:1;background-color:rgb(63 63 70/var(--tw-bg-opacity,1))}.dark\:bg-zinc-700\/80:is(.dark *){background-color:#3f3f46cc}.dark\:bg-zinc-800:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\:bg-zinc-800\/30:is(.dark *){background-color:#27272a4d}.dark\:bg-zinc-800\/50:is(.dark *){background-color:#27272a80}.dark\:bg-zinc-800\/80:is(.dark *){background-color:#27272acc}.dark\:bg-zinc-800\/95:is(.dark *){background-color:#27272af2}.dark\:bg-zinc-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.dark\:bg-zinc-900\/90:is(.dark *){background-color:#18181be6}.dark\:bg-zinc-900\/95:is(.dark *){background-color:#18181bf2}.dark\:text-blue-400:is(.dark *){--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.dark\:text-emerald-300:is(.dark *){--tw-text-opacity:1;color:rgb(110 231 183/var(--tw-text-opacity,1))}.dark\:text-emerald-400:is(.dark *){--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.dark\:text-teal-400:is(.dark *){--tw-text-opacity:1;color:rgb(45 212 191/var(--tw-text-opacity,1))}.dark\:text-zinc-100:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.dark\:text-zinc-200:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.dark\:text-zinc-300:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dark\:text-zinc-400:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.dark\:text-zinc-500:is(.dark *){--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.dark\:text-zinc-600:is(.dark *){--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.dark\:text-zinc-700:is(.dark *){--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.dark\:placeholder-zinc-500:is(.dark *)::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(113 113 122/var(--tw-placeholder-opacity,1))}.dark\:placeholder-zinc-500:is(.dark *)::placeholder{--tw-placeholder-opacity:1;color:rgb(113 113 122/var(--tw-placeholder-opacity,1))}.dark\:opacity-\[0\.06\]:is(.dark *){opacity:.06}.dark\:shadow-2xl:is(.dark *){--tw-shadow:0 25px 50px -12px #00000040;--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.dark\:shadow-black\/20:is(.dark *){--tw-shadow-color:#0003;--tw-shadow:var(--tw-shadow-colored)}.dark\:ring-emerald-500\/60:is(.dark *){--tw-ring-color:#10b98199}.dark\:ring-zinc-600:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(82 82 91/var(--tw-ring-opacity,1))}.dark\:ring-zinc-700:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(63 63 70/var(--tw-ring-opacity,1))}.dark\:placeholder\:text-zinc-500:is(.dark *)::-moz-placeholder{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.dark\:placeholder\:text-zinc-500:is(.dark *)::placeholder{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.dark\:hover\:border-zinc-600:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity,1))}.dark\:hover\:border-zinc-700:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity,1))}.dark\:hover\:bg-emerald-900:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(6 78 59/var(--tw-bg-opacity,1))}.dark\:hover\:bg-emerald-900\/50:hover:is(.dark *){background-color:#064e3b80}.dark\:hover\:bg-emerald-950:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(2 44 34/var(--tw-bg-opacity,1))}.dark\:hover\:bg-red-950:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(69 10 10/var(--tw-bg-opacity,1))}.dark\:hover\:bg-zinc-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(63 63 70/var(--tw-bg-opacity,1))}.dark\:hover\:bg-zinc-700\/50:hover:is(.dark *){background-color:#3f3f4680}.dark\:hover\:bg-zinc-700\/60:hover:is(.dark *){background-color:#3f3f4699}.dark\:hover\:bg-zinc-800:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\:hover\:bg-zinc-800\/50:hover:is(.dark *){background-color:#27272a80}.dark\:hover\:bg-zinc-800\/60:hover:is(.dark *){background-color:#27272a99}.dark\:hover\:bg-zinc-900:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.dark\:hover\:text-emerald-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(110 231 183/var(--tw-text-opacity,1))}.dark\:hover\:text-emerald-400:hover:is(.dark *){--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.dark\:hover\:text-teal-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(94 234 212/var(--tw-text-opacity,1))}.dark\:hover\:text-zinc-100:hover:is(.dark *){--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity,1))}.dark\:hover\:text-zinc-200:hover:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.dark\:hover\:text-zinc-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity,1))}.dark\:hover\:text-zinc-400:hover:is(.dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.dark\:active\:bg-emerald-950:active:is(.dark *){--tw-bg-opacity:1;background-color:rgb(2 44 34/var(--tw-bg-opacity,1))}.dark\:active\:bg-red-950:active:is(.dark *){--tw-bg-opacity:1;background-color:rgb(69 10 10/var(--tw-bg-opacity,1))}.dark\:active\:bg-zinc-800:active:is(.dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.group:hover .dark\:group-hover\:text-emerald-300:is(.dark *){--tw-text-opacity:1;color:rgb(110 231 183/var(--tw-text-opacity,1))}.group:hover .dark\:group-hover\:text-emerald-400:is(.dark *){--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.group:hover .dark\:group-hover\:text-zinc-200:is(.dark *){--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}@media (min-width:640px){.sm\:bottom-4{bottom:1rem}.sm\:left-4{left:1rem}.sm\:right-4{right:1rem}.sm\:top-4{top:1rem}.sm\:block{display:block}.sm\:inline-block{display:inline-block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:gap-2{gap:.5rem}.sm\:px-3{padding-left:.75rem;padding-right:.75rem}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-2{padding-top:.5rem;padding-bottom:.5rem}}@media (min-width:768px){.md\:flex{display:flex}.md\:hidden{display:none}}
|
package/app/page.tsx
CHANGED
|
@@ -653,6 +653,159 @@ export default function Home() {
|
|
|
653
653
|
const [descriptionModalNode, setDescriptionModalNode] =
|
|
654
654
|
useState<GraphNode | null>(null);
|
|
655
655
|
|
|
656
|
+
// Description modal navigation: topological sort of connected component
|
|
657
|
+
const descriptionModalFamily = useMemo(() => {
|
|
658
|
+
if (!descriptionModalNode || !data) return [];
|
|
659
|
+
const nodeId = descriptionModalNode.id;
|
|
660
|
+
const allNodes = data.graphData.nodes;
|
|
661
|
+
const allLinks = data.graphData.links;
|
|
662
|
+
|
|
663
|
+
// Helper: force-graph mutates link source/target to objects after simulation
|
|
664
|
+
const getId = (x: any): string | undefined =>
|
|
665
|
+
typeof x === "string" ? x : x?.id;
|
|
666
|
+
|
|
667
|
+
// Build undirected adjacency (for finding connected component)
|
|
668
|
+
// and directed dependency map (for topological sort)
|
|
669
|
+
const neighbors = new Map<string, Set<string>>();
|
|
670
|
+
const dependsOn = new Map<string, Set<string>>(); // node → nodes it depends on (upstream)
|
|
671
|
+
|
|
672
|
+
for (const link of allLinks) {
|
|
673
|
+
if ((link as any)._removeTime) continue; // Skip links being removed (fade-out animation)
|
|
674
|
+
const src = getId(link.source);
|
|
675
|
+
const tgt = getId(link.target);
|
|
676
|
+
if (!src || !tgt) continue;
|
|
677
|
+
|
|
678
|
+
// Undirected for BFS
|
|
679
|
+
if (!neighbors.has(src)) neighbors.set(src, new Set());
|
|
680
|
+
if (!neighbors.has(tgt)) neighbors.set(tgt, new Set());
|
|
681
|
+
neighbors.get(src)!.add(tgt);
|
|
682
|
+
neighbors.get(tgt)!.add(src);
|
|
683
|
+
|
|
684
|
+
// Directed: target depends on source (source is upstream)
|
|
685
|
+
if (!dependsOn.has(tgt)) dependsOn.set(tgt, new Set());
|
|
686
|
+
dependsOn.get(tgt)!.add(src);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// BFS to find connected component containing current node
|
|
690
|
+
const component = new Set<string>();
|
|
691
|
+
const bfsQueue = [nodeId];
|
|
692
|
+
component.add(nodeId);
|
|
693
|
+
while (bfsQueue.length > 0) {
|
|
694
|
+
const current = bfsQueue.shift()!;
|
|
695
|
+
for (const neighbor of neighbors.get(current) || []) {
|
|
696
|
+
if (!component.has(neighbor)) {
|
|
697
|
+
component.add(neighbor);
|
|
698
|
+
bfsQueue.push(neighbor);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Single node — no navigation
|
|
704
|
+
if (component.size <= 1) return [];
|
|
705
|
+
|
|
706
|
+
// Tiebreaker: sort by ID with numeric suffix awareness
|
|
707
|
+
// e.g., "xxx.2" < "xxx.10" (numeric, not lexicographic)
|
|
708
|
+
const compareIds = (a: string, b: string): number => {
|
|
709
|
+
const aParts = a.split(".");
|
|
710
|
+
const bParts = b.split(".");
|
|
711
|
+
// Compare base part first
|
|
712
|
+
if (aParts[0] !== bParts[0]) return a.localeCompare(b);
|
|
713
|
+
// Same base: compare numeric suffixes depth by depth
|
|
714
|
+
for (let i = 1; i < Math.max(aParts.length, bParts.length); i++) {
|
|
715
|
+
const aNum = i < aParts.length ? parseInt(aParts[i], 10) : -1;
|
|
716
|
+
const bNum = i < bParts.length ? parseInt(bParts[i], 10) : -1;
|
|
717
|
+
if (aNum !== bNum) return aNum - bNum;
|
|
718
|
+
}
|
|
719
|
+
return 0;
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
// Kahn's topological sort with sorted ready queue (stable, deterministic)
|
|
723
|
+
const inDegree = new Map<string, number>();
|
|
724
|
+
for (const id of component) {
|
|
725
|
+
inDegree.set(id, 0);
|
|
726
|
+
}
|
|
727
|
+
for (const id of component) {
|
|
728
|
+
for (const dep of dependsOn.get(id) || []) {
|
|
729
|
+
if (component.has(dep)) {
|
|
730
|
+
inDegree.set(id, (inDegree.get(id) || 0) + 1);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// Build reverse adjacency: node -> set of nodes that depend on it
|
|
736
|
+
const dependents = new Map<string, Set<string>>();
|
|
737
|
+
for (const id of component) {
|
|
738
|
+
for (const dep of dependsOn.get(id) || []) {
|
|
739
|
+
if (component.has(dep)) {
|
|
740
|
+
if (!dependents.has(dep)) dependents.set(dep, new Set());
|
|
741
|
+
dependents.get(dep)!.add(id);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const sorted: string[] = [];
|
|
747
|
+
// Start with nodes that have no dependencies (in-degree 0)
|
|
748
|
+
const ready = Array.from(component)
|
|
749
|
+
.filter(id => (inDegree.get(id) || 0) === 0)
|
|
750
|
+
.sort(compareIds);
|
|
751
|
+
|
|
752
|
+
while (ready.length > 0) {
|
|
753
|
+
const node = ready.shift()!;
|
|
754
|
+
sorted.push(node);
|
|
755
|
+
|
|
756
|
+
// Reduce in-degree for nodes that depend on this one
|
|
757
|
+
for (const dependent of dependents.get(node) || []) {
|
|
758
|
+
const newDeg = (inDegree.get(dependent) || 1) - 1;
|
|
759
|
+
inDegree.set(dependent, newDeg);
|
|
760
|
+
if (newDeg === 0) {
|
|
761
|
+
// Insert in sorted position (maintain order in ready queue)
|
|
762
|
+
const insertIdx = ready.findIndex(r => compareIds(r, dependent) > 0);
|
|
763
|
+
if (insertIdx === -1) ready.push(dependent);
|
|
764
|
+
else ready.splice(insertIdx, 0, dependent);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Handle cycles: append remaining nodes in sorted ID order
|
|
770
|
+
if (sorted.length < component.size) {
|
|
771
|
+
const sortedSet = new Set(sorted);
|
|
772
|
+
const remaining = Array.from(component)
|
|
773
|
+
.filter(id => !sortedSet.has(id))
|
|
774
|
+
.sort(compareIds);
|
|
775
|
+
sorted.push(...remaining);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Map to GraphNode objects, filter to nodes with descriptions only
|
|
779
|
+
const nodeMap = new Map(allNodes.map(n => [n.id, n]));
|
|
780
|
+
return sorted
|
|
781
|
+
.map(id => nodeMap.get(id))
|
|
782
|
+
.filter((n): n is GraphNode => n != null && !!n.description);
|
|
783
|
+
}, [descriptionModalNode, data]);
|
|
784
|
+
|
|
785
|
+
// Description modal navigation handler
|
|
786
|
+
const handleDescriptionNavigate = useCallback((direction: "prev" | "next") => {
|
|
787
|
+
if (!descriptionModalNode || descriptionModalFamily.length <= 1) return;
|
|
788
|
+
const currentIndex = descriptionModalFamily.findIndex(n => n.id === descriptionModalNode.id);
|
|
789
|
+
if (currentIndex === -1) return;
|
|
790
|
+
|
|
791
|
+
let nextIndex: number;
|
|
792
|
+
if (direction === "next") {
|
|
793
|
+
nextIndex = (currentIndex + 1) % descriptionModalFamily.length;
|
|
794
|
+
} else {
|
|
795
|
+
nextIndex = (currentIndex - 1 + descriptionModalFamily.length) % descriptionModalFamily.length;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
setDescriptionModalNode(descriptionModalFamily[nextIndex]);
|
|
799
|
+
}, [descriptionModalNode, descriptionModalFamily]);
|
|
800
|
+
|
|
801
|
+
// Description modal navigation label
|
|
802
|
+
const descriptionNavLabel = useMemo(() => {
|
|
803
|
+
if (!descriptionModalNode || descriptionModalFamily.length <= 1) return undefined;
|
|
804
|
+
const idx = descriptionModalFamily.findIndex(n => n.id === descriptionModalNode.id);
|
|
805
|
+
if (idx === -1) return undefined;
|
|
806
|
+
return `${idx + 1}/${descriptionModalFamily.length}`;
|
|
807
|
+
}, [descriptionModalNode, descriptionModalFamily]);
|
|
808
|
+
|
|
656
809
|
// Settings modal state
|
|
657
810
|
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
|
|
658
811
|
|
|
@@ -1302,6 +1455,14 @@ export default function Home() {
|
|
|
1302
1455
|
// --- End timeline replay logic ---
|
|
1303
1456
|
|
|
1304
1457
|
const handleNodeClick = useCallback((node: GraphNode, event?: MouseEvent) => {
|
|
1458
|
+
// Cmd+Click (Mac) or Ctrl+Click (Windows/Linux): open description modal
|
|
1459
|
+
if ((event?.metaKey || event?.ctrlKey) && node.description) {
|
|
1460
|
+
setDescriptionModalNode(node);
|
|
1461
|
+
setAllCommentsPanelOpen(false);
|
|
1462
|
+
setProfilePanelHandle(null);
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1305
1466
|
if (event?.shiftKey) {
|
|
1306
1467
|
// Shift+click: toggle node in multi-selection
|
|
1307
1468
|
setSelectedNodeIds((prev) => {
|
|
@@ -2710,6 +2871,8 @@ export default function Home() {
|
|
|
2710
2871
|
onClose={() => setDescriptionModalNode(null)}
|
|
2711
2872
|
repoUrl={repoUrls[descriptionModalNode.prefix]}
|
|
2712
2873
|
onOpenSettings={() => setSettingsModalOpen(true)}
|
|
2874
|
+
onNavigate={descriptionModalFamily.length > 1 ? handleDescriptionNavigate : undefined}
|
|
2875
|
+
navigationLabel={descriptionNavLabel}
|
|
2713
2876
|
/>
|
|
2714
2877
|
)}
|
|
2715
2878
|
|
|
@@ -83,166 +83,215 @@ export default function AllCommentsPanel({
|
|
|
83
83
|
const regex = globToRegex(filter.trim());
|
|
84
84
|
return allComments.filter(c => regex.test(c.nodeId));
|
|
85
85
|
}, [allComments, filter]);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
86
|
+
|
|
87
|
+
// Extract shared content to avoid duplication between desktop and mobile
|
|
88
|
+
const filterBarContent = (
|
|
89
|
+
<div className="shrink-0 px-5 py-2 border-b border-zinc-100 dark:border-zinc-800">
|
|
90
|
+
<div className="relative">
|
|
91
|
+
<input
|
|
92
|
+
type="text"
|
|
93
|
+
value={filter}
|
|
94
|
+
onChange={(e) => setFilter(e.target.value)}
|
|
95
|
+
placeholder="Filter by target (e.g. beads-map-*)"
|
|
96
|
+
className="w-full pl-7 pr-7 py-1.5 text-xs border border-zinc-200 dark:border-zinc-700 rounded-md bg-zinc-50 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 placeholder-zinc-400 dark:placeholder-zinc-500 focus:outline-none focus:ring-1 focus:ring-emerald-500 focus:border-emerald-500"
|
|
97
|
+
/>
|
|
98
|
+
{/* Search icon */}
|
|
99
|
+
<svg className="absolute left-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-zinc-400" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
|
|
100
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 01-.659 1.591l-5.432 5.432a2.25 2.25 0 00-.659 1.591v2.927a2.25 2.25 0 01-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 00-.659-1.591L3.659 7.409A2.25 2.25 0 013 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0112 3z" />
|
|
101
|
+
</svg>
|
|
102
|
+
{/* Clear button */}
|
|
103
|
+
{filter && (
|
|
104
|
+
<button
|
|
105
|
+
onClick={() => setFilter("")}
|
|
106
|
+
className="absolute right-2 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"
|
|
107
|
+
>
|
|
108
|
+
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
|
109
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
110
|
+
</svg>
|
|
111
|
+
</button>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
{/* Quick filter chips */}
|
|
115
|
+
{prefixes && prefixes.length > 1 && !filter && (
|
|
116
|
+
<div className="flex flex-wrap gap-1 mt-1.5">
|
|
117
|
+
{prefixes.map(p => (
|
|
118
|
+
<button
|
|
119
|
+
key={p}
|
|
120
|
+
onClick={() => setFilter(p + '-*')}
|
|
121
|
+
className="px-1.5 py-0.5 text-[10px] font-mono bg-zinc-100 dark:bg-zinc-800 text-zinc-500 dark:text-zinc-400 rounded hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors"
|
|
122
|
+
>
|
|
123
|
+
{p}-*
|
|
124
|
+
</button>
|
|
125
|
+
))}
|
|
103
126
|
</div>
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
127
|
+
)}
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const commentListContent = (
|
|
132
|
+
<div className="space-y-0">
|
|
133
|
+
{filteredComments.length === 0 && filter ? (
|
|
134
|
+
<div className="flex flex-col items-center justify-center py-12 text-center">
|
|
135
|
+
<p className="text-xs text-zinc-400 dark:text-zinc-500">No comments matching “{filter}”</p>
|
|
136
|
+
<button onClick={() => setFilter("")} className="text-[10px] text-emerald-600 dark:text-emerald-400 hover:underline mt-1">
|
|
137
|
+
Clear filter
|
|
138
|
+
</button>
|
|
139
|
+
</div>
|
|
140
|
+
) : filteredComments.length === 0 ? (
|
|
141
|
+
<div className="flex flex-col items-center justify-center py-12 text-center">
|
|
108
142
|
<svg
|
|
109
|
-
className="w-
|
|
143
|
+
className="w-8 h-8 text-zinc-200 dark:text-zinc-700 mb-3"
|
|
110
144
|
fill="none"
|
|
111
|
-
stroke="currentColor"
|
|
112
145
|
viewBox="0 0 24 24"
|
|
113
146
|
strokeWidth={1.5}
|
|
147
|
+
stroke="currentColor"
|
|
114
148
|
>
|
|
115
149
|
<path
|
|
116
150
|
strokeLinecap="round"
|
|
117
151
|
strokeLinejoin="round"
|
|
118
|
-
d="
|
|
152
|
+
d="M2.25 12.76c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.076-4.076a1.526 1.526 0 011.037-.443 48.282 48.282 0 005.68-.494c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z"
|
|
119
153
|
/>
|
|
120
154
|
</svg>
|
|
121
|
-
|
|
122
|
-
|
|
155
|
+
<p className="text-xs text-zinc-400 dark:text-zinc-500">No comments yet</p>
|
|
156
|
+
<p className="text-[10px] text-zinc-300 dark:text-zinc-600 mt-1">
|
|
157
|
+
Right-click a node to leave a comment
|
|
158
|
+
</p>
|
|
159
|
+
</div>
|
|
160
|
+
) : (
|
|
161
|
+
<>
|
|
162
|
+
{/* Only show root comments (without replyTo) — replies are nested */}
|
|
163
|
+
{filteredComments.filter(c => !c.replyTo).map((comment) => (
|
|
164
|
+
<AllCommentCard
|
|
165
|
+
key={comment.uri}
|
|
166
|
+
comment={comment}
|
|
167
|
+
currentDid={currentDid}
|
|
168
|
+
isAuthenticated={isAuthenticated}
|
|
169
|
+
onNodeNavigate={onNodeNavigate}
|
|
170
|
+
onLike={onLikeComment}
|
|
171
|
+
onDelete={onDeleteComment}
|
|
172
|
+
onStartReply={(c) => setReplyingToUri(c.uri)}
|
|
173
|
+
replyingToUri={replyingToUri}
|
|
174
|
+
replyText={replyText}
|
|
175
|
+
onReplyTextChange={setReplyText}
|
|
176
|
+
onSubmitReply={handleSubmitReply}
|
|
177
|
+
onCancelReply={() => {
|
|
178
|
+
setReplyingToUri(null);
|
|
179
|
+
setReplyText("");
|
|
180
|
+
}}
|
|
181
|
+
isSubmittingReply={isSubmittingReply}
|
|
182
|
+
depth={0}
|
|
183
|
+
prefixes={prefixes}
|
|
184
|
+
/>
|
|
185
|
+
))}
|
|
186
|
+
</>
|
|
187
|
+
)}
|
|
188
|
+
</div>
|
|
189
|
+
);
|
|
123
190
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
{/* Search icon */}
|
|
135
|
-
<svg className="absolute left-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-zinc-400" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
|
|
136
|
-
<path strokeLinecap="round" strokeLinejoin="round" d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 01-.659 1.591l-5.432 5.432a2.25 2.25 0 00-.659 1.591v2.927a2.25 2.25 0 01-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 00-.659-1.591L3.659 7.409A2.25 2.25 0 013 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0112 3z" />
|
|
137
|
-
</svg>
|
|
138
|
-
{/* Clear button */}
|
|
139
|
-
{filter && (
|
|
140
|
-
<button
|
|
141
|
-
onClick={() => setFilter("")}
|
|
142
|
-
className="absolute right-2 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"
|
|
143
|
-
>
|
|
144
|
-
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
|
145
|
-
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
146
|
-
</svg>
|
|
147
|
-
</button>
|
|
148
|
-
)}
|
|
191
|
+
const composeContent = (
|
|
192
|
+
<div className="shrink-0 px-5 py-3 border-t border-zinc-100 dark:border-zinc-800 bg-zinc-50/50 dark:bg-zinc-800/50">
|
|
193
|
+
{isAuthenticated ? (
|
|
194
|
+
<GeneralCommentCompose
|
|
195
|
+
prefixes={prefixes || []}
|
|
196
|
+
onSubmit={onPostComment}
|
|
197
|
+
/>
|
|
198
|
+
) : (
|
|
199
|
+
<div className="text-[10px] text-zinc-400 dark:text-zinc-500">
|
|
200
|
+
Sign in to comment
|
|
149
201
|
</div>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
onClick={() => setFilter(p + '-*')}
|
|
157
|
-
className="px-1.5 py-0.5 text-[10px] font-mono bg-zinc-100 dark:bg-zinc-800 text-zinc-500 dark:text-zinc-400 rounded hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors"
|
|
158
|
-
>
|
|
159
|
-
{p}-*
|
|
160
|
-
</button>
|
|
161
|
-
))}
|
|
162
|
-
</div>
|
|
202
|
+
)}
|
|
203
|
+
<div className="text-[10px] text-zinc-400 dark:text-zinc-500 mt-2">
|
|
204
|
+
{filter ? (
|
|
205
|
+
<>{filteredComments.length} of {allComments.length} comments matching “{filter}”</>
|
|
206
|
+
) : (
|
|
207
|
+
<>{allComments.length} comment{allComments.length !== 1 ? 's' : ''} across all issues</>
|
|
163
208
|
)}
|
|
164
209
|
</div>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
165
212
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
213
|
+
return (
|
|
214
|
+
<>
|
|
215
|
+
{/* Desktop: right sidebar */}
|
|
216
|
+
<aside
|
|
217
|
+
className={`hidden md:flex absolute top-0 right-0 h-full w-[360px] bg-white dark:bg-zinc-900 border-l border-zinc-200 dark:border-zinc-700 flex-col shadow-xl z-30 transform transition-transform duration-300 ease-out ${
|
|
218
|
+
isOpen ? "translate-x-0" : "translate-x-full"
|
|
219
|
+
}`}
|
|
220
|
+
>
|
|
221
|
+
{/* Header */}
|
|
222
|
+
<div className="shrink-0 px-5 py-3 border-b border-zinc-100 dark:border-zinc-800 flex items-center justify-between">
|
|
223
|
+
<div className="flex items-center gap-2">
|
|
224
|
+
<h2 className="text-xs font-semibold text-zinc-400 uppercase tracking-wider">
|
|
225
|
+
All Comments
|
|
226
|
+
</h2>
|
|
227
|
+
{(filter ? filteredComments.length : allComments.length) > 0 && (
|
|
228
|
+
<span className="px-1.5 py-0.5 bg-emerald-50 dark:bg-emerald-950 text-emerald-600 dark:text-emerald-400 rounded-full text-[10px] font-medium">
|
|
229
|
+
{filter ? filteredComments.length : allComments.length}
|
|
230
|
+
</span>
|
|
231
|
+
)}
|
|
174
232
|
</div>
|
|
175
|
-
|
|
176
|
-
|
|
233
|
+
<button
|
|
234
|
+
onClick={onClose}
|
|
235
|
+
className="p-1 text-zinc-400 dark:text-zinc-500 hover:text-zinc-600 dark:hover:text-zinc-300 transition-colors rounded-full hover:bg-zinc-100 dark:hover:bg-zinc-800"
|
|
236
|
+
>
|
|
177
237
|
<svg
|
|
178
|
-
className="w-
|
|
238
|
+
className="w-4 h-4"
|
|
179
239
|
fill="none"
|
|
240
|
+
stroke="currentColor"
|
|
180
241
|
viewBox="0 0 24 24"
|
|
181
242
|
strokeWidth={1.5}
|
|
182
|
-
stroke="currentColor"
|
|
183
243
|
>
|
|
184
244
|
<path
|
|
185
245
|
strokeLinecap="round"
|
|
186
246
|
strokeLinejoin="round"
|
|
187
|
-
d="
|
|
247
|
+
d="M6 18L18 6M6 6l12 12"
|
|
188
248
|
/>
|
|
189
249
|
</svg>
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
))}
|
|
250
|
+
</button>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
{/* Filter bar */}
|
|
254
|
+
{filterBarContent}
|
|
255
|
+
|
|
256
|
+
{/* Comment list */}
|
|
257
|
+
<div className="flex-1 overflow-y-auto custom-scrollbar px-5 py-3">
|
|
258
|
+
{commentListContent}
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
{/* Compose area */}
|
|
262
|
+
{composeContent}
|
|
263
|
+
</aside>
|
|
264
|
+
|
|
265
|
+
{/* Mobile: bottom drawer */}
|
|
266
|
+
<div
|
|
267
|
+
className={`md:hidden fixed inset-x-0 bottom-0 transform transition-transform duration-300 ease-out z-20 ${
|
|
268
|
+
isOpen ? "translate-y-0" : "translate-y-full"
|
|
269
|
+
}`}
|
|
270
|
+
>
|
|
271
|
+
<div className="bg-white dark:bg-zinc-900 rounded-t-2xl shadow-2xl border-t border-zinc-200 dark:border-zinc-700 max-h-[70vh] flex flex-col">
|
|
272
|
+
{/* Header with title + close button */}
|
|
273
|
+
<div className="flex items-center justify-between px-5 py-3 border-b border-zinc-100 dark:border-zinc-800 shrink-0">
|
|
274
|
+
<h2 className="text-xs font-semibold text-zinc-400 dark:text-zinc-400 uppercase tracking-wider">All Comments</h2>
|
|
275
|
+
<button onClick={onClose} className="p-1 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 rounded-full hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors">
|
|
276
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
|
277
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
278
|
+
</svg>
|
|
279
|
+
</button>
|
|
221
280
|
</div>
|
|
222
|
-
)}
|
|
223
|
-
</div>
|
|
224
281
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
/>
|
|
232
|
-
) : (
|
|
233
|
-
<div className="text-[10px] text-zinc-400 dark:text-zinc-500">
|
|
234
|
-
Sign in to comment
|
|
282
|
+
{/* Filter bar */}
|
|
283
|
+
{filterBarContent}
|
|
284
|
+
|
|
285
|
+
{/* Scrollable content: same comment list as desktop */}
|
|
286
|
+
<div className="flex-1 overflow-y-auto custom-scrollbar px-5 py-3">
|
|
287
|
+
{commentListContent}
|
|
235
288
|
</div>
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
{
|
|
239
|
-
<>{filteredComments.length} of {allComments.length} comments matching “{filter}”</>
|
|
240
|
-
) : (
|
|
241
|
-
<>{allComments.length} comment{allComments.length !== 1 ? 's' : ''} across all issues</>
|
|
242
|
-
)}
|
|
289
|
+
|
|
290
|
+
{/* Compose area (same as desktop) */}
|
|
291
|
+
{composeContent}
|
|
243
292
|
</div>
|
|
244
293
|
</div>
|
|
245
|
-
|
|
294
|
+
</>
|
|
246
295
|
);
|
|
247
296
|
}
|
|
248
297
|
|