react-doctor-cli-dev 1.0.9 → 1.0.12
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/dashboard/index.html +253 -0
- package/dashboard/package-lock.json +913 -0
- package/dashboard/package.json +19 -0
- package/dashboard/public/favicon.svg +1 -0
- package/dashboard/public/icons.svg +24 -0
- package/dashboard/src/api.js +107 -0
- package/dashboard/src/assets/hero.png +0 -0
- package/dashboard/src/assets/javascript.svg +1 -0
- package/dashboard/src/assets/vite.svg +1 -0
- package/dashboard/src/css/main.css +441 -0
- package/dashboard/src/data.js +202 -0
- package/dashboard/src/main.js +53 -0
- package/dashboard/src/pages.js +797 -0
- package/dashboard/src/router.js +77 -0
- package/dashboard/src/utils.js +163 -0
- package/dashboard/vite.config.js +19 -0
- package/data/screenshots/5-docs-fullLoad.png +0 -0
- package/data/screenshots/6--fcp.png +0 -0
- package/data/screenshots/6--fullLoad.png +0 -0
- package/data/screenshots/6-white-fullLoad.png +0 -0
- package/package.json +4 -22
- package/shared/dist/index.d.ts +2 -0
- package/shared/dist/index.js +19 -0
- package/shared/dist/schemas.d.ts +91 -0
- package/shared/dist/schemas.js +82 -0
- package/shared/dist/types.d.ts +44 -0
- package/shared/dist/types.js +2 -0
- package/shared/package-lock.json +47 -0
- package/shared/package.json +21 -0
- package/shared/src/index.ts +4 -0
- package/shared/src/schemas.ts +136 -0
- package/shared/src/types.ts +137 -0
- package/shared/tsconfig.json +15 -0
- package/tsconfig.json +25 -0
- /package/{backend/data/screenshots/4--fcp.png → data/screenshots/5--fcp.png} +0 -0
- /package/{backend/data/screenshots/4--fullLoad.png → data/screenshots/5--fullLoad.png} +0 -0
- /package/{backend/data/screenshots/4-white-fullLoad.png → data/screenshots/5-white-fullLoad.png} +0 -0
- /package/{backend/data/screenshots/4-docs-fullLoad.png → data/screenshots/6-docs-fullLoad.png} +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dashboard",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build --outDir ../backend/public",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"vite": "^8.1.0"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@fontsource/jetbrains-mono": "^5.2.8",
|
|
16
|
+
"@fontsource/tajawal": "^5.2.7",
|
|
17
|
+
"chart.js": "^4.5.1"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-49.64" y="2.03" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-45.045" y="20.029" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-43.513" y="21.178" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="15.756" y="-17.901" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-27.636" y="-22.853" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="20.116" y="-38.415" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="24.641" y="-11.323" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="8.244" y="-2.416" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="18.713" y="10.588" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter></defs></svg>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<symbol id="bluesky-icon" viewBox="0 0 16 17">
|
|
3
|
+
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
|
|
4
|
+
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
|
|
5
|
+
</symbol>
|
|
6
|
+
<symbol id="discord-icon" viewBox="0 0 20 19">
|
|
7
|
+
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
|
|
8
|
+
</symbol>
|
|
9
|
+
<symbol id="documentation-icon" viewBox="0 0 21 20">
|
|
10
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
|
|
11
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
|
|
12
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
|
|
13
|
+
</symbol>
|
|
14
|
+
<symbol id="github-icon" viewBox="0 0 19 19">
|
|
15
|
+
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
|
|
16
|
+
</symbol>
|
|
17
|
+
<symbol id="social-icon" viewBox="0 0 20 20">
|
|
18
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
|
|
19
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
|
|
20
|
+
</symbol>
|
|
21
|
+
<symbol id="x-icon" viewBox="0 0 19 19">
|
|
22
|
+
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
|
|
23
|
+
</symbol>
|
|
24
|
+
</svg>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// js/api.js — API client for the React Doctor backend
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
const api = (() => {
|
|
6
|
+
|
|
7
|
+
const BASE_URL = "http://localhost:3000";
|
|
8
|
+
const API_KEY = "react-doctor-secret-key-change-this";
|
|
9
|
+
|
|
10
|
+
async function request(path, options = {}) {
|
|
11
|
+
const url = `${BASE_URL}${path}`;
|
|
12
|
+
const defaultHeaders = { "Content-Type": "application/json" };
|
|
13
|
+
|
|
14
|
+
const res = await fetch(url, {
|
|
15
|
+
...options,
|
|
16
|
+
headers: { ...defaultHeaders, ...(options.headers || {}) },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
let errBody = null;
|
|
21
|
+
try { errBody = await res.json(); } catch {}
|
|
22
|
+
const msg = errBody?.error || errBody?.message || res.statusText;
|
|
23
|
+
throw new Error(`API ${res.status}: ${msg}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return res.json();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function healthCheck() {
|
|
30
|
+
return request("/health");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function listReports() {
|
|
34
|
+
return request("/api/reports");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ── Get a single report with screenshots ──────────────────
|
|
38
|
+
async function getReport(id) {
|
|
39
|
+
// Fetch the main report
|
|
40
|
+
const data = await request(`/api/reports/${id}`);
|
|
41
|
+
|
|
42
|
+
// Fetch screenshots for this report
|
|
43
|
+
try {
|
|
44
|
+
const screenshotData = await request(`/api/reports/${id}/screenshots`);
|
|
45
|
+
if (screenshotData && screenshotData.screenshots && screenshotData.screenshots.length > 0) {
|
|
46
|
+
// Group screenshots by route
|
|
47
|
+
const screenshotsByRoute = {};
|
|
48
|
+
for (const screenshot of screenshotData.screenshots) {
|
|
49
|
+
const route = screenshot.route || '';
|
|
50
|
+
if (!screenshotsByRoute[route]) {
|
|
51
|
+
screenshotsByRoute[route] = [];
|
|
52
|
+
}
|
|
53
|
+
screenshotsByRoute[route].push({
|
|
54
|
+
label: screenshot.label,
|
|
55
|
+
takenAt: screenshot.taken_at || screenshot.takenAt,
|
|
56
|
+
dataUrl: screenshot.data_url || screenshot.dataUrl,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Replace existing screenshots with the ones from the API
|
|
61
|
+
for (const [route, screenshots] of Object.entries(screenshotsByRoute)) {
|
|
62
|
+
if (data.runtime && data.runtime[route]) {
|
|
63
|
+
data.runtime[route].screenshots = screenshots;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
console.log(`📸 Loaded ${screenshotData.screenshots.length} screenshots from API`);
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.warn('No screenshots found for this report');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return data;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function getReportsByProject(projectName) {
|
|
76
|
+
return request(`/api/reports/project/${encodeURIComponent(projectName)}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function uploadReport(finalReport) {
|
|
80
|
+
return request("/api/reports/upload", {
|
|
81
|
+
method: "POST",
|
|
82
|
+
headers: { "x-api-key": API_KEY },
|
|
83
|
+
body: JSON.stringify(finalReport),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function getLatestReport() {
|
|
88
|
+
const { reports } = await listReports();
|
|
89
|
+
if (!reports || reports.length === 0) {
|
|
90
|
+
throw new Error("No reports found in the database");
|
|
91
|
+
}
|
|
92
|
+
return getReport(reports[0].id);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
healthCheck,
|
|
97
|
+
listReports,
|
|
98
|
+
getReport,
|
|
99
|
+
getReportsByProject,
|
|
100
|
+
uploadReport,
|
|
101
|
+
getLatestReport,
|
|
102
|
+
BASE_URL,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
})();
|
|
106
|
+
|
|
107
|
+
export default api;
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="32" height="32" viewBox="0 0 256 256"><path fill="#F7DF1E" d="M0 0h256v256H0V0Z"/><path d="m67.312 213.932l19.59-11.856c3.78 6.701 7.218 12.371 15.465 12.371c7.905 0 12.89-3.092 12.89-15.12v-81.798h24.057v82.138c0 24.917-14.606 36.259-35.916 36.259c-19.245 0-30.416-9.967-36.087-21.996m85.07-2.576l19.588-11.341c5.157 8.421 11.859 14.607 23.715 14.607c9.969 0 16.325-4.984 16.325-11.858c0-8.248-6.53-11.17-17.528-15.98l-6.013-2.58c-17.357-7.387-28.87-16.667-28.87-36.257c0-18.044 13.747-31.792 35.228-31.792c15.294 0 26.292 5.328 34.196 19.247l-18.732 12.03c-4.125-7.389-8.591-10.31-15.465-10.31c-7.046 0-11.514 4.468-11.514 10.31c0 7.217 4.468 10.14 14.778 14.608l6.014 2.577c20.45 8.765 31.963 17.7 31.963 37.804c0 21.654-17.012 33.51-39.867 33.51c-22.339 0-36.774-10.654-43.819-24.574"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="77" height="47" fill="none" aria-labelledby="vite-logo-title" viewBox="0 0 77 47"><title id="vite-logo-title">Vite</title><style>.parenthesis{fill:#000}@media (prefers-color-scheme:dark){.parenthesis{fill:#fff}}</style><path fill="#9135ff" d="M40.151 45.71c-.663.844-2.02.374-2.02-.699V34.708a2.26 2.26 0 0 0-2.262-2.262H24.493c-.92 0-1.457-1.04-.92-1.788l7.479-10.471c1.07-1.498 0-3.578-1.842-3.578H15.443c-.92 0-1.456-1.04-.92-1.788l9.696-13.576c.213-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.472c-1.07 1.497 0 3.578 1.842 3.578h11.376c.944 0 1.474 1.087.89 1.83L40.153 45.712z"/><mask id="a" width="48" height="47" x="14" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M40.047 45.71c-.663.843-2.02.374-2.02-.699V34.708a2.26 2.26 0 0 0-2.262-2.262H24.389c-.92 0-1.457-1.04-.92-1.788l7.479-10.472c1.07-1.497 0-3.578-1.842-3.578H15.34c-.92 0-1.456-1.04-.92-1.788l9.696-13.575c.213-.297.556-.474.92-.474H53.93c.92 0 1.456 1.04.92 1.788L47.37 13.03c-1.07 1.498 0 3.578 1.842 3.578h11.376c.944 0 1.474 1.088.89 1.831L40.049 45.712z"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#eee6ff" rx="5.508" ry="14.704" transform="rotate(269.814 20.96 11.29)scale(-1 1)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#eee6ff" rx="10.399" ry="29.851" transform="rotate(89.814 -16.902 -8.275)scale(1 -1)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#8900ff" rx="5.508" ry="30.487" transform="rotate(89.814 -19.197 -7.127)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#8900ff" rx="5.508" ry="30.599" transform="rotate(89.814 -25.928 4.177)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#8900ff" rx="5.508" ry="30.599" transform="rotate(89.814 -25.738 5.52)scale(1 -1)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#eee6ff" rx="14.072" ry="22.078" transform="rotate(93.35 31.245 55.578)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#8900ff" rx="3.47" ry="21.501" transform="rotate(89.009 35.419 55.202)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#8900ff" rx="3.47" ry="21.501" transform="rotate(89.009 35.419 55.202)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx="14.592" cy="9.743" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(39.51 14.592 9.743)"/></g><g filter="url(#k)"><ellipse cx="61.728" cy="-5.321" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 61.728 -5.32)"/></g><g filter="url(#l)"><ellipse cx="55.618" cy="7.104" fill="#00c2ff" rx="5.971" ry="9.665" transform="rotate(37.892 55.618 7.104)"/></g><g filter="url(#m)"><ellipse cx="12.326" cy="39.103" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 12.326 39.103)"/></g><g filter="url(#n)"><ellipse cx="12.326" cy="39.103" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 12.326 39.103)"/></g><g filter="url(#o)"><ellipse cx="49.857" cy="30.678" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 49.857 30.678)"/></g><g filter="url(#p)"><ellipse cx="52.623" cy="33.171" fill="#00c2ff" rx="5.971" ry="15.297" transform="rotate(37.892 52.623 33.17)"/></g></g><path d="M6.919 0c-9.198 13.166-9.252 33.575 0 46.789h6.215c-9.25-13.214-9.196-33.623 0-46.789zm62.424 0h-6.215c9.198 13.166 9.252 33.575 0 46.789h6.215c9.25-13.214 9.196-33.623 0-46.789" class="parenthesis"/><defs><filter id="b" width="60.045" height="41.654" x="-5.564" y="16.92" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-40.407" y="-6.762" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-35.435" y="2.801" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-30.84" y="20.8" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-29.307" y="21.949" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="29.961" y="-17.13" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="37.754" y="3.055" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="37.754" y="3.055" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-13.43" y="-22.082" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="34.321" y="-37.644" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="38.847" y="-10.552" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-15.081" y="6.78" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-15.081" y="6.78" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="22.45" y="-1.645" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="32.919" y="11.36" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter></defs></svg>
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/* ─────────────────────────────────────────────────────────────
|
|
2
|
+
main.css — variables, reset, layout, shared components
|
|
3
|
+
Theme: #0B0C10 deep dark · #C778DD purple accent · #00FFC2 teal
|
|
4
|
+
───────────────────────────────────────────────────────────── */
|
|
5
|
+
/* Fonts are no longer loaded from a CDN.
|
|
6
|
+
See VITE_SETUP.md — install with:
|
|
7
|
+
npm install @fontsource/tajawal @fontsource/jetbrains-mono
|
|
8
|
+
and import them in src/main.js:
|
|
9
|
+
import '@fontsource/tajawal/300.css';
|
|
10
|
+
import '@fontsource/tajawal/400.css';
|
|
11
|
+
import '@fontsource/tajawal/500.css';
|
|
12
|
+
import '@fontsource/tajawal/700.css';
|
|
13
|
+
import '@fontsource/jetbrains-mono/400.css';
|
|
14
|
+
import '@fontsource/jetbrains-mono/500.css'; */
|
|
15
|
+
|
|
16
|
+
:root {
|
|
17
|
+
--bg: #0B0C10;
|
|
18
|
+
--bg2: #0F1117;
|
|
19
|
+
--bg3: #161B22;
|
|
20
|
+
--bg4: #1C2333;
|
|
21
|
+
--border: #21262D;
|
|
22
|
+
--border2: #30363D;
|
|
23
|
+
--text: #E6EDF3;
|
|
24
|
+
--text2: #8B949E;
|
|
25
|
+
--text3: #6E7681;
|
|
26
|
+
--accent: #C778DD;
|
|
27
|
+
--accent2: #B05FC9;
|
|
28
|
+
--accent-rgb: 199, 120, 221;
|
|
29
|
+
--teal: #00FFC2;
|
|
30
|
+
--teal-rgb: 0, 255, 194;
|
|
31
|
+
--purple: #C778DD;
|
|
32
|
+
--blue: #58A6FF;
|
|
33
|
+
--orange: #F0883E;
|
|
34
|
+
--red: #FF7B72;
|
|
35
|
+
--green: #3FB950;
|
|
36
|
+
--yellow: #D29922;
|
|
37
|
+
--sidebar-w: 240px;
|
|
38
|
+
--radius: 10px;
|
|
39
|
+
--radius-sm: 6px;
|
|
40
|
+
--shadow: 0 4px 24px rgba(0,0,0,0.4);
|
|
41
|
+
--transition: 0.18s ease;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
45
|
+
html { font-size: 15px; scroll-behavior: smooth; }
|
|
46
|
+
|
|
47
|
+
body {
|
|
48
|
+
font-family: 'Tajawal', system-ui, sans-serif;
|
|
49
|
+
background: var(--bg);
|
|
50
|
+
color: var(--text);
|
|
51
|
+
min-height: 100vh;
|
|
52
|
+
display: flex;
|
|
53
|
+
overflow-x: hidden;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ── Sidebar ─────────────────────────────────────────────── */
|
|
57
|
+
#sidebar {
|
|
58
|
+
width: var(--sidebar-w);
|
|
59
|
+
min-height: 100vh;
|
|
60
|
+
background: var(--bg2);
|
|
61
|
+
border-right: 1px solid var(--border);
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
position: fixed;
|
|
65
|
+
top: 0; left: 0; bottom: 0;
|
|
66
|
+
z-index: 100;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.sidebar-logo {
|
|
70
|
+
padding: 22px 20px 18px;
|
|
71
|
+
border-bottom: 1px solid var(--border);
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
gap: 10px;
|
|
75
|
+
}
|
|
76
|
+
.logo-icon {
|
|
77
|
+
width: 34px; height: 34px;
|
|
78
|
+
background: linear-gradient(135deg, var(--accent), var(--teal));
|
|
79
|
+
border-radius: 8px;
|
|
80
|
+
display: flex; align-items: center; justify-content: center;
|
|
81
|
+
font-size: 18px;
|
|
82
|
+
flex-shrink: 0;
|
|
83
|
+
}
|
|
84
|
+
.logo-text { font-size: 0.95rem; font-weight: 700; letter-spacing: -0.01em; line-height: 1.2; }
|
|
85
|
+
.logo-sub { font-size: 0.65rem; color: var(--text3); font-weight: 400; letter-spacing: 0.05em; text-transform: uppercase; }
|
|
86
|
+
|
|
87
|
+
.sidebar-section {
|
|
88
|
+
padding: 18px 16px 6px;
|
|
89
|
+
font-size: 0.6rem;
|
|
90
|
+
font-weight: 700;
|
|
91
|
+
letter-spacing: 0.12em;
|
|
92
|
+
text-transform: uppercase;
|
|
93
|
+
color: var(--text3);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.nav-item {
|
|
97
|
+
display: flex;
|
|
98
|
+
align-items: center;
|
|
99
|
+
gap: 10px;
|
|
100
|
+
padding: 9px 14px;
|
|
101
|
+
margin: 1px 8px;
|
|
102
|
+
border-radius: var(--radius-sm);
|
|
103
|
+
cursor: pointer;
|
|
104
|
+
font-size: 0.85rem;
|
|
105
|
+
font-weight: 500;
|
|
106
|
+
color: var(--text2);
|
|
107
|
+
transition: background var(--transition), color var(--transition);
|
|
108
|
+
border: none;
|
|
109
|
+
background: none;
|
|
110
|
+
width: calc(100% - 16px);
|
|
111
|
+
text-align: left;
|
|
112
|
+
}
|
|
113
|
+
.nav-item:hover { background: var(--bg4); color: var(--text); }
|
|
114
|
+
.nav-item.active { background: rgba(var(--accent-rgb),0.1); color: var(--accent); }
|
|
115
|
+
.nav-icon { font-size: 1rem; width: 20px; text-align: center; flex-shrink: 0; }
|
|
116
|
+
.nav-badge {
|
|
117
|
+
margin-left: auto;
|
|
118
|
+
font-size: 0.64rem;
|
|
119
|
+
font-weight: 700;
|
|
120
|
+
padding: 2px 7px;
|
|
121
|
+
border-radius: 20px;
|
|
122
|
+
background: var(--bg4);
|
|
123
|
+
color: var(--text3);
|
|
124
|
+
}
|
|
125
|
+
.nav-item.active .nav-badge { background: rgba(var(--accent-rgb),0.15); color: var(--accent); }
|
|
126
|
+
|
|
127
|
+
.sidebar-footer {
|
|
128
|
+
margin-top: auto;
|
|
129
|
+
padding: 16px;
|
|
130
|
+
border-top: 1px solid var(--border);
|
|
131
|
+
}
|
|
132
|
+
.project-pill {
|
|
133
|
+
display: flex;
|
|
134
|
+
align-items: center;
|
|
135
|
+
gap: 8px;
|
|
136
|
+
background: var(--bg3);
|
|
137
|
+
border: 1px solid var(--border);
|
|
138
|
+
border-radius: var(--radius-sm);
|
|
139
|
+
padding: 9px 12px;
|
|
140
|
+
}
|
|
141
|
+
.status-dot {
|
|
142
|
+
width: 7px; height: 7px;
|
|
143
|
+
border-radius: 50%;
|
|
144
|
+
background: var(--accent);
|
|
145
|
+
box-shadow: 0 0 6px var(--accent);
|
|
146
|
+
flex-shrink: 0;
|
|
147
|
+
}
|
|
148
|
+
.proj-name { font-weight: 600; color: var(--text); font-size: 0.8rem; }
|
|
149
|
+
.proj-sub { font-size: 0.68rem; color: var(--text3); margin-top: 1px; }
|
|
150
|
+
|
|
151
|
+
/* ── Main wrapper ────────────────────────────────────────── */
|
|
152
|
+
#main { margin-left: var(--sidebar-w); flex: 1; min-height: 100vh; display: flex; flex-direction: column; }
|
|
153
|
+
|
|
154
|
+
.topbar {
|
|
155
|
+
height: 54px;
|
|
156
|
+
border-bottom: 1px solid var(--border);
|
|
157
|
+
display: flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
padding: 0 28px;
|
|
160
|
+
gap: 12px;
|
|
161
|
+
background: var(--bg2);
|
|
162
|
+
position: sticky;
|
|
163
|
+
top: 0;
|
|
164
|
+
z-index: 50;
|
|
165
|
+
}
|
|
166
|
+
.topbar-title { font-size: 0.95rem; font-weight: 600; }
|
|
167
|
+
.topbar-sub { font-size: 0.75rem; color: var(--text3); }
|
|
168
|
+
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }
|
|
169
|
+
.topbar-time { font-size: 0.7rem; color: var(--text3); font-family: 'JetBrains Mono', monospace; }
|
|
170
|
+
|
|
171
|
+
.page-content { padding: 26px 28px; flex: 1; display: none; }
|
|
172
|
+
.page-content.active { display: block; }
|
|
173
|
+
|
|
174
|
+
.section-gap { margin-bottom: 20px; }
|
|
175
|
+
|
|
176
|
+
/* ── Card ────────────────────────────────────────────────── */
|
|
177
|
+
.card {
|
|
178
|
+
background: var(--bg2);
|
|
179
|
+
border: 1px solid var(--border);
|
|
180
|
+
border-radius: var(--radius);
|
|
181
|
+
padding: 20px;
|
|
182
|
+
}
|
|
183
|
+
.card-title {
|
|
184
|
+
font-size: 0.72rem;
|
|
185
|
+
font-weight: 700;
|
|
186
|
+
text-transform: uppercase;
|
|
187
|
+
letter-spacing: 0.1em;
|
|
188
|
+
color: var(--text3);
|
|
189
|
+
margin-bottom: 16px;
|
|
190
|
+
display: flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
gap: 8px;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* ── Grid ────────────────────────────────────────────────── */
|
|
196
|
+
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
|
197
|
+
.grid-3 { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; }
|
|
198
|
+
.grid-4 { display: grid; grid-template-columns: repeat(4,1fr); gap: 16px; }
|
|
199
|
+
.grid-5 { display: grid; grid-template-columns: repeat(5,1fr); gap: 16px; }
|
|
200
|
+
|
|
201
|
+
/* ── Stat tile ───────────────────────────────────────────── */
|
|
202
|
+
.stat-tile {
|
|
203
|
+
background: var(--bg2);
|
|
204
|
+
border: 1px solid var(--border);
|
|
205
|
+
border-radius: var(--radius);
|
|
206
|
+
padding: 18px 20px;
|
|
207
|
+
position: relative;
|
|
208
|
+
overflow: hidden;
|
|
209
|
+
}
|
|
210
|
+
.stat-tile::before {
|
|
211
|
+
content: '';
|
|
212
|
+
position: absolute;
|
|
213
|
+
top: 0; left: 0; right: 0;
|
|
214
|
+
height: 2px;
|
|
215
|
+
}
|
|
216
|
+
.stat-tile.good::before { background: var(--green); }
|
|
217
|
+
.stat-tile.warn::before { background: var(--orange); }
|
|
218
|
+
.stat-tile.bad::before { background: var(--red); }
|
|
219
|
+
.stat-tile.blue::before { background: var(--blue); }
|
|
220
|
+
.stat-tile.purple::before { background: var(--teal); }
|
|
221
|
+
|
|
222
|
+
.stat-label {
|
|
223
|
+
font-size: 0.68rem;
|
|
224
|
+
font-weight: 700;
|
|
225
|
+
text-transform: uppercase;
|
|
226
|
+
letter-spacing: 0.1em;
|
|
227
|
+
color: var(--text3);
|
|
228
|
+
margin-bottom: 8px;
|
|
229
|
+
}
|
|
230
|
+
.stat-value {
|
|
231
|
+
font-size: 1.85rem;
|
|
232
|
+
font-weight: 700;
|
|
233
|
+
font-family: 'JetBrains Mono', monospace;
|
|
234
|
+
line-height: 1;
|
|
235
|
+
letter-spacing: -0.02em;
|
|
236
|
+
}
|
|
237
|
+
.stat-value.good { color: var(--green); }
|
|
238
|
+
.stat-value.warn { color: var(--orange); }
|
|
239
|
+
.stat-value.bad { color: var(--red); }
|
|
240
|
+
.stat-value.blue { color: var(--blue); }
|
|
241
|
+
.stat-value.purple { color: var(--teal); }
|
|
242
|
+
.stat-meta { font-size: 0.7rem; color: var(--text3); margin-top: 6px; }
|
|
243
|
+
|
|
244
|
+
/* ── Score ring ──────────────────────────────────────────── */
|
|
245
|
+
.score-ring-wrap { display: flex; align-items: center; gap: 32px; }
|
|
246
|
+
.score-ring { position: relative; width: 140px; height: 140px; flex-shrink: 0; }
|
|
247
|
+
.score-ring svg { transform: rotate(-90deg); }
|
|
248
|
+
.ring-bg { fill: none; stroke: var(--bg4); stroke-width: 10; }
|
|
249
|
+
.ring-fg { fill: none; stroke-width: 10; stroke-linecap: round; transition: stroke-dashoffset 1.4s cubic-bezier(.4,0,.2,1); }
|
|
250
|
+
.ring-label { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); text-align: center; }
|
|
251
|
+
.ring-num { font-size: 2rem; font-weight: 700; font-family: 'JetBrains Mono', monospace; line-height: 1; }
|
|
252
|
+
.ring-grade { font-size: 0.75rem; font-weight: 600; color: var(--text3); margin-top: 3px; }
|
|
253
|
+
.score-details h2 { font-size: 1.05rem; font-weight: 700; margin-bottom: 6px; }
|
|
254
|
+
.score-details p { font-size: 0.8rem; color: var(--text2); line-height: 1.6; }
|
|
255
|
+
|
|
256
|
+
/* ── Badge ───────────────────────────────────────────────── */
|
|
257
|
+
.badge {
|
|
258
|
+
display: inline-flex; align-items: center; gap: 4px;
|
|
259
|
+
font-size: 0.68rem; font-weight: 700;
|
|
260
|
+
padding: 2px 8px; border-radius: 20px;
|
|
261
|
+
letter-spacing: 0.04em; text-transform: uppercase;
|
|
262
|
+
}
|
|
263
|
+
.badge.critical { background: rgba(255,123,114,0.15); color: var(--red); border: 1px solid rgba(255,123,114,0.3); }
|
|
264
|
+
.badge.warning { background: rgba(240,136, 62,0.15); color: var(--orange); border: 1px solid rgba(240,136, 62,0.3); }
|
|
265
|
+
.badge.info { background: rgba( 88,166,255,0.12); color: var(--blue); border: 1px solid rgba( 88,166,255,0.25); }
|
|
266
|
+
.badge.good { background: rgba( 63,185, 80,0.12); color: var(--green); border: 1px solid rgba( 63,185, 80,0.25); }
|
|
267
|
+
.badge.grade-a { background: rgba(var(--accent-rgb),0.12); color: var(--accent); border: 1px solid rgba(var(--accent-rgb),0.25); font-size: 0.78rem; padding: 3px 12px; }
|
|
268
|
+
.badge.grade-b { background: rgba( 88,166,255,0.12); color: var(--blue); border: 1px solid rgba( 88,166,255,0.25); font-size: 0.78rem; padding: 3px 12px; }
|
|
269
|
+
.badge.grade-c { background: rgba(240,136, 62,0.12); color: var(--orange); border: 1px solid rgba(240,136, 62,0.3); font-size: 0.78rem; padding: 3px 12px; }
|
|
270
|
+
.badge.grade-d { background: rgba(255,123,114,0.12); color: var(--red); border: 1px solid rgba(255,123,114,0.3); font-size: 0.78rem; padding: 3px 12px; }
|
|
271
|
+
|
|
272
|
+
/* ── Table ───────────────────────────────────────────────── */
|
|
273
|
+
.rd-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
|
|
274
|
+
.rd-table th {
|
|
275
|
+
text-align: left;
|
|
276
|
+
font-size: 0.64rem; font-weight: 700;
|
|
277
|
+
text-transform: uppercase; letter-spacing: 0.1em;
|
|
278
|
+
color: var(--text3);
|
|
279
|
+
padding: 10px 14px;
|
|
280
|
+
border-bottom: 1px solid var(--border);
|
|
281
|
+
white-space: nowrap;
|
|
282
|
+
}
|
|
283
|
+
.rd-table td {
|
|
284
|
+
padding: 11px 14px;
|
|
285
|
+
border-bottom: 1px solid var(--border);
|
|
286
|
+
color: var(--text2);
|
|
287
|
+
vertical-align: middle;
|
|
288
|
+
}
|
|
289
|
+
.rd-table tr:last-child td { border-bottom: none; }
|
|
290
|
+
.rd-table tbody tr:hover td { background: var(--bg3); }
|
|
291
|
+
.rd-table .mono { font-family: 'JetBrains Mono', monospace; font-size: 0.78rem; color: var(--blue); }
|
|
292
|
+
.rd-table .score-cell { font-family: 'JetBrains Mono', monospace; font-weight: 700; }
|
|
293
|
+
|
|
294
|
+
/* ── Suggestion card ─────────────────────────────────────── */
|
|
295
|
+
.suggestion-card {
|
|
296
|
+
border: 1px solid var(--border);
|
|
297
|
+
border-radius: var(--radius);
|
|
298
|
+
padding: 16px 18px;
|
|
299
|
+
background: var(--bg2);
|
|
300
|
+
display: flex;
|
|
301
|
+
gap: 14px;
|
|
302
|
+
align-items: flex-start;
|
|
303
|
+
}
|
|
304
|
+
.suggestion-card + .suggestion-card { margin-top: 10px; }
|
|
305
|
+
.sug-icon {
|
|
306
|
+
width: 36px; height: 36px;
|
|
307
|
+
border-radius: 8px;
|
|
308
|
+
display: flex; align-items: center; justify-content: center;
|
|
309
|
+
font-size: 1rem; flex-shrink: 0;
|
|
310
|
+
}
|
|
311
|
+
.sug-icon.critical { background: rgba(255,123,114,0.12); }
|
|
312
|
+
.sug-icon.warning { background: rgba(240,136, 62,0.12); }
|
|
313
|
+
.sug-icon.info { background: rgba( 88,166,255,0.12); }
|
|
314
|
+
.sug-body { flex: 1; min-width: 0; }
|
|
315
|
+
.sug-title { font-weight: 600; font-size: 0.88rem; margin-bottom: 4px; display: flex; align-items: center; gap: 8px; }
|
|
316
|
+
.sug-desc { font-size: 0.78rem; color: var(--text2); line-height: 1.55; margin-bottom: 8px; }
|
|
317
|
+
.sug-fix {
|
|
318
|
+
font-size: 0.76rem;
|
|
319
|
+
font-family: 'JetBrains Mono', monospace;
|
|
320
|
+
background: var(--bg3);
|
|
321
|
+
border: 1px solid var(--border);
|
|
322
|
+
border-radius: var(--radius-sm);
|
|
323
|
+
padding: 8px 12px;
|
|
324
|
+
color: var(--accent);
|
|
325
|
+
line-height: 1.5;
|
|
326
|
+
word-break: break-word;
|
|
327
|
+
}
|
|
328
|
+
.sug-component { font-size: 0.7rem; color: var(--text3); margin-top: 6px; font-family: 'JetBrains Mono', monospace; }
|
|
329
|
+
|
|
330
|
+
/* ── Screenshot filmstrip ────────────────────────────────── */
|
|
331
|
+
.filmstrip { display: flex; gap: 14px; overflow-x: auto; padding-bottom: 4px; }
|
|
332
|
+
.filmstrip::-webkit-scrollbar { height: 4px; }
|
|
333
|
+
.filmstrip::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 2px; }
|
|
334
|
+
.film-frame { flex-shrink: 0; text-align: center; }
|
|
335
|
+
.film-img {
|
|
336
|
+
width: 210px; height: 128px;
|
|
337
|
+
object-fit: cover;
|
|
338
|
+
border-radius: var(--radius-sm);
|
|
339
|
+
border: 2px solid var(--border);
|
|
340
|
+
display: block;
|
|
341
|
+
background: var(--bg3);
|
|
342
|
+
cursor: pointer;
|
|
343
|
+
transition: border-color var(--transition), transform var(--transition);
|
|
344
|
+
}
|
|
345
|
+
.film-img:hover { border-color: var(--accent); transform: scale(1.02); }
|
|
346
|
+
.film-label {
|
|
347
|
+
font-size: 0.66rem; font-weight: 700;
|
|
348
|
+
text-transform: uppercase; letter-spacing: 0.08em;
|
|
349
|
+
color: var(--text3); margin-top: 6px;
|
|
350
|
+
}
|
|
351
|
+
.film-time { font-size: 0.7rem; color: var(--accent); font-family: 'JetBrains Mono', monospace; }
|
|
352
|
+
.film-placeholder {
|
|
353
|
+
width: 210px; height: 128px;
|
|
354
|
+
border-radius: var(--radius-sm);
|
|
355
|
+
border: 2px dashed var(--border2);
|
|
356
|
+
background: var(--bg3);
|
|
357
|
+
display: flex; flex-direction: column;
|
|
358
|
+
align-items: center; justify-content: center;
|
|
359
|
+
gap: 6px;
|
|
360
|
+
color: var(--text3);
|
|
361
|
+
font-size: 0.72rem;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* ── Lightbox ────────────────────────────────────────────── */
|
|
365
|
+
#lightbox {
|
|
366
|
+
display: none; position: fixed; inset: 0;
|
|
367
|
+
background: rgba(0,0,0,0.88);
|
|
368
|
+
z-index: 9999;
|
|
369
|
+
align-items: center; justify-content: center;
|
|
370
|
+
backdrop-filter: blur(6px);
|
|
371
|
+
}
|
|
372
|
+
#lightbox.open { display: flex; }
|
|
373
|
+
#lightbox img { max-width: 90vw; max-height: 86vh; border-radius: var(--radius); border: 1px solid var(--border2); box-shadow: var(--shadow); }
|
|
374
|
+
#lb-close {
|
|
375
|
+
position: fixed; top: 20px; right: 24px;
|
|
376
|
+
background: var(--bg3); border: 1px solid var(--border2);
|
|
377
|
+
color: var(--text); border-radius: 50%;
|
|
378
|
+
width: 36px; height: 36px;
|
|
379
|
+
display: flex; align-items: center; justify-content: center;
|
|
380
|
+
cursor: pointer; font-size: 1.1rem;
|
|
381
|
+
}
|
|
382
|
+
#lb-caption { position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); font-size: 0.8rem; color: var(--text2); background: var(--bg3); border: 1px solid var(--border); padding: 6px 16px; border-radius: 20px; }
|
|
383
|
+
|
|
384
|
+
/* ── Vitals bar ──────────────────────────────────────────── */
|
|
385
|
+
.vital-row { margin-bottom: 14px; }
|
|
386
|
+
.vital-row-head { display: flex; justify-content: space-between; font-size: 0.75rem; font-weight: 600; margin-bottom: 5px; }
|
|
387
|
+
.vital-row-head .vname { color: var(--text2); }
|
|
388
|
+
.vital-row-head .vval { font-family: 'JetBrains Mono', monospace; font-size: 0.77rem; }
|
|
389
|
+
.vital-track { height: 6px; background: var(--bg4); border-radius: 3px; overflow: hidden; }
|
|
390
|
+
.vital-fill { height: 100%; border-radius: 3px; transition: width 1s cubic-bezier(.4,0,.2,1); }
|
|
391
|
+
|
|
392
|
+
/* ── Pagination ──────────────────────────────────────────── */
|
|
393
|
+
.pagination { display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center; gap: 6px; justify-content: center; margin-top: 20px; overflow-x: auto; }
|
|
394
|
+
.page-btn {
|
|
395
|
+
min-width: 32px; height: 32px; padding: 0 8px;
|
|
396
|
+
border: 1px solid var(--border);
|
|
397
|
+
border-radius: var(--radius-sm);
|
|
398
|
+
background: var(--bg2);
|
|
399
|
+
color: var(--text2);
|
|
400
|
+
font-size: 0.78rem;
|
|
401
|
+
cursor: pointer;
|
|
402
|
+
display: flex; align-items: center; justify-content: center;
|
|
403
|
+
transition: all var(--transition);
|
|
404
|
+
}
|
|
405
|
+
.page-btn:hover:not(:disabled) { background: var(--bg4); color: var(--text); border-color: var(--border2); }
|
|
406
|
+
.page-btn.active { background: var(--accent); color: var(--bg); border-color: var(--accent); font-weight: 700; }
|
|
407
|
+
.page-btn:disabled { opacity: 0.35; cursor: not-allowed; }
|
|
408
|
+
.page-info { font-size: 0.73rem; color: var(--text3); padding: 0 6px; }
|
|
409
|
+
|
|
410
|
+
/* ── Filter bar ──────────────────────────────────────────── */
|
|
411
|
+
.filter-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
|
|
412
|
+
.filter-btn {
|
|
413
|
+
padding: 5px 14px; border-radius: 20px;
|
|
414
|
+
border: 1px solid var(--border);
|
|
415
|
+
background: var(--bg3); color: var(--text2);
|
|
416
|
+
font-size: 0.73rem; font-weight: 600;
|
|
417
|
+
cursor: pointer; transition: all var(--transition);
|
|
418
|
+
}
|
|
419
|
+
.filter-btn:hover { border-color: var(--border2); color: var(--text); }
|
|
420
|
+
.filter-btn.active { background: rgba(var(--accent-rgb),0.1); border-color: var(--accent); color: var(--accent); }
|
|
421
|
+
.filter-btn.fc.active { background: rgba(255,123,114,0.1); border-color: var(--red); color: var(--red); }
|
|
422
|
+
.filter-btn.fw.active { background: rgba(240,136,62,0.1); border-color: var(--orange); color: var(--orange); }
|
|
423
|
+
.filter-btn.fi.active { background: rgba(88,166,255,0.1); border-color: var(--blue); color: var(--blue); }
|
|
424
|
+
|
|
425
|
+
/* ── Route tabs ──────────────────────────────────────────── */
|
|
426
|
+
.route-tabs { display: flex; gap: 6px; margin-bottom: 20px; flex-wrap: wrap; }
|
|
427
|
+
.route-tab {
|
|
428
|
+
padding: 6px 14px; border-radius: var(--radius-sm);
|
|
429
|
+
border: 1px solid var(--border);
|
|
430
|
+
background: var(--bg3); color: var(--text2);
|
|
431
|
+
font-size: 0.76rem; font-weight: 500;
|
|
432
|
+
cursor: pointer; font-family: 'JetBrains Mono', monospace;
|
|
433
|
+
transition: all var(--transition);
|
|
434
|
+
}
|
|
435
|
+
.route-tab:hover { border-color: var(--border2); color: var(--text); }
|
|
436
|
+
.route-tab.active { background: rgba(var(--accent-rgb),0.08); border-color: var(--accent); color: var(--accent); }
|
|
437
|
+
|
|
438
|
+
/* ── Scrollbar ───────────────────────────────────────────── */
|
|
439
|
+
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
440
|
+
::-webkit-scrollbar-track { background: var(--bg); }
|
|
441
|
+
::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 3px; }
|