shashwatIDR commited on
Commit
78c7a3b
·
verified ·
1 Parent(s): e6934b5

Upload 23 files

Browse files
components.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "tailwind.config.ts",
8
+ "css": "client/src/index.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ }
20
+ }
dist/.DS_Store ADDED
Binary file (6.15 kB). View file
 
dist/public/assets/index.css ADDED
@@ -0,0 +1 @@
 
 
1
+ *,:before,:after{--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: rgb(59 130 246 / .5);--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: }::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: rgb(59 130 246 / .5);--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-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}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;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings: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:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-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{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,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{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}*{border-color:var(--border)}body{background-color:var(--background);font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";color:var(--foreground);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:Roboto,Arial,sans-serif}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-y-0{top:0;bottom:0}.-bottom-12{bottom:-3rem}.-left-12{left:-3rem}.-right-12{right:-3rem}.-top-12{top:-3rem}.bottom-0{bottom:0}.bottom-16{bottom:4rem}.bottom-2{bottom:.5rem}.bottom-4{bottom:1rem}.left-0{left:0}.left-1{left:.25rem}.left-1\/2{left:50%}.left-2{left:.5rem}.left-3{left:.75rem}.left-4{left:1rem}.left-\[50\%\]{left:50%}.right-0{right:0}.right-1{right:.25rem}.right-16{right:4rem}.right-2{right:.5rem}.right-3{right:.75rem}.right-4{right:1rem}.top-0{top:0}.top-1\.5{top:.375rem}.top-1\/2{top:50%}.top-16{top:4rem}.top-2{top:.5rem}.top-3\.5{top:.875rem}.top-4{top:1rem}.top-\[1px\]{top:1px}.top-\[50\%\]{top:50%}.top-\[60\%\]{top:60%}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.z-\[110\]{z-index:110}.z-\[1\]{z-index:1}.z-\[200\]{z-index:200}.z-\[210\]{z-index:210}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-3{margin-left:.75rem;margin-right:.75rem}.mx-3\.5{margin-left:.875rem;margin-right:.875rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-8{margin-left:2rem;margin-right:2rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0\.5{margin-top:.125rem;margin-bottom:.125rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.-ml-4{margin-left:-1rem}.-mt-4{margin-top:-1rem}.mb-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-16{margin-bottom:4rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-0\.5{margin-left:.125rem}.ml-1{margin-left:.25rem}.ml-12{margin-left:3rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-1\.5{margin-top:.375rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-24{margin-top:6rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.aspect-video{aspect-ratio:16 / 9}.size-4{width:1rem;height:1rem}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-20{height:5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.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-\[1px\]{height:1px}.h-\[calc\(100vh-4rem\)\]{height:calc(100vh - 4rem)}.h-\[var\(--radix-navigation-menu-viewport-height\)\]{height:var(--radix-navigation-menu-viewport-height)}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-svh{height:100svh}.max-h-\[--radix-context-menu-content-available-height\]{max-height:var(--radix-context-menu-content-available-height)}.max-h-\[--radix-select-content-available-height\]{max-height:var(--radix-select-content-available-height)}.max-h-\[300px\]{max-height:300px}.max-h-screen{max-height:100vh}.min-h-0{min-height:0px}.min-h-\[80px\]{min-height:80px}.min-h-\[calc\(100vh-4rem\)\]{min-height:calc(100vh - 4rem)}.min-h-screen{min-height:100vh}.min-h-svh{min-height:100svh}.w-0{width:0px}.w-1{width:.25rem}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-56{width:14rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[--sidebar-width\]{width:var(--sidebar-width)}.w-\[100px\]{width:100px}.w-\[1px\]{width:1px}.w-auto{width:auto}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-10{min-width:2.5rem}.min-w-11{min-width:2.75rem}.min-w-5{min-width:1.25rem}.min-w-9{min-width:2.25rem}.min-w-\[12rem\]{min-width:12rem}.min-w-\[35px\]{min-width:35px}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.max-w-\[--skeleton-width\]{max-width:var(--skeleton-width)}.max-w-\[1400px\]{max-width:1400px}.max-w-\[1600px\]{max-width:1600px}.max-w-lg{max-width:32rem}.max-w-max{max-width:-moz-max-content;max-width:max-content}.max-w-md{max-width:28rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}.grow-0{flex-grow:0}.basis-full{flex-basis:100%}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.origin-\[--radix-context-menu-content-transform-origin\]{transform-origin:var(--radix-context-menu-content-transform-origin)}.origin-\[--radix-hover-card-content-transform-origin\]{transform-origin:var(--radix-hover-card-content-transform-origin)}.origin-\[--radix-menubar-content-transform-origin\]{transform-origin:var(--radix-menubar-content-transform-origin)}.origin-\[--radix-popover-content-transform-origin\]{transform-origin:var(--radix-popover-content-transform-origin)}.origin-\[--radix-select-content-transform-origin\]{transform-origin:var(--radix-select-content-transform-origin)}.origin-\[--radix-tooltip-content-transform-origin\]{transform-origin:var(--radix-tooltip-content-transform-origin)}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-px{--tw-translate-x: -1px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\[-50\%\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-px{--tw-translate-x: 1px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\[-50\%\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-0{--tw-rotate: 0deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-45{--tw-rotate: 45deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-90{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-0{--tw-scale-x: 0;--tw-scale-y: 0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(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)) skew(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:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1.5rem * var(--tw-space-x-reverse));margin-left:calc(1.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(2rem * var(--tw-space-x-reverse));margin-left:calc(2rem * calc(1 - var(--tw-space-x-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-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-\[2px\]{border-radius:2px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:.75rem}.rounded-r-full{border-top-right-radius:9999px;border-bottom-right-radius:9999px}.rounded-t-\[10px\]{border-top-left-radius:10px;border-top-right-radius:10px}.rounded-tl-sm{border-top-left-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-2{border-width:2px}.border-\[1\.5px\]{border-width:1.5px}.border-y{border-top-width:1px;border-bottom-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-\[--color-border\]{border-color:var(--color-border)}.border-blue-200{--tw-border-opacity: 1;border-color:rgb(191 219 254 / var(--tw-border-opacity, 1))}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-border{border-color:var(--border)}.border-destructive{border-color:var(--destructive)}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-green-200{--tw-border-opacity: 1;border-color:rgb(187 247 208 / var(--tw-border-opacity, 1))}.border-green-500{--tw-border-opacity: 1;border-color:rgb(34 197 94 / var(--tw-border-opacity, 1))}.border-input{border-color:var(--input)}.border-orange-200{--tw-border-opacity: 1;border-color:rgb(254 215 170 / var(--tw-border-opacity, 1))}.border-primary{border-color:var(--primary)}.border-sidebar-border{border-color:var(--sidebar-border)}.border-transparent{border-color:transparent}.border-l-transparent{border-left-color:transparent}.border-t-transparent{border-top-color:transparent}.bg-\[--color-bg\]{background-color:var(--color-bg)}.bg-accent{background-color:var(--accent)}.bg-background{background-color:var(--background)}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\/80{background-color:#000c}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-border{background-color:var(--border)}.bg-card{background-color:var(--card)}.bg-destructive{background-color:var(--destructive)}.bg-foreground{background-color:var(--foreground)}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.bg-gray-300{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-input{background-color:var(--input)}.bg-muted{background-color:var(--muted)}.bg-orange-50{--tw-bg-opacity: 1;background-color:rgb(255 247 237 / var(--tw-bg-opacity, 1))}.bg-popover{background-color:var(--popover)}.bg-primary{background-color:var(--primary)}.bg-secondary{background-color:var(--secondary)}.bg-sidebar{background-color:var(--sidebar-background)}.bg-sidebar-border{background-color:var(--sidebar-border)}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-opacity-0{--tw-bg-opacity: 0}.bg-opacity-30{--tw-bg-opacity: .3}.bg-opacity-80{--tw-bg-opacity: .8}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-blue-600{--tw-gradient-from: #2563eb var(--tw-gradient-from-position);--tw-gradient-to: rgb(37 99 235 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-blue-600\/10{--tw-gradient-from: rgb(37 99 235 / .1) var(--tw-gradient-from-position);--tw-gradient-to: rgb(37 99 235 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-green-500{--tw-gradient-from: #22c55e var(--tw-gradient-from-position);--tw-gradient-to: rgb(34 197 94 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-blue-500{--tw-gradient-to: #3b82f6 var(--tw-gradient-to-position)}.to-purple-600{--tw-gradient-to: #9333ea var(--tw-gradient-to-position)}.to-purple-600\/10{--tw-gradient-to: rgb(147 51 234 / .1) var(--tw-gradient-to-position)}.fill-current{fill:currentColor}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.p-\[1px\]{padding:1px}.px-1{padding-left:.25rem;padding-right:.25rem}.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-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-8{padding-left:2rem;padding-right:2rem}.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-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.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-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-8{padding-bottom:2rem}.pl-10{padding-left:2.5rem}.pl-2\.5{padding-left:.625rem}.pl-4{padding-left:1rem}.pl-8{padding-left:2rem}.pr-12{padding-right:3rem}.pr-2{padding-right:.5rem}.pr-2\.5{padding-right:.625rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-16{padding-top:4rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-\[0\.8rem\]{font-size:.8rem}.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-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.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-5{line-height:1.25rem}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-widest{letter-spacing:.1em}.text-accent-foreground{color:var(--accent-foreground)}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / 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-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-card-foreground{color:var(--card-foreground)}.text-current{color:currentColor}.text-destructive{color:var(--destructive)}.text-destructive-foreground{color:var(--destructive-foreground)}.text-foreground{color:var(--foreground)}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-muted-foreground{color:var(--muted-foreground)}.text-orange-500{--tw-text-opacity: 1;color:rgb(249 115 22 / var(--tw-text-opacity, 1))}.text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity, 1))}.text-orange-800{--tw-text-opacity: 1;color:rgb(154 52 18 / var(--tw-text-opacity, 1))}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground{color:var(--sidebar-foreground)}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-90{opacity:.9}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--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-\[0_0_0_1px_hsl\(var\(--sidebar-border\)\)\]{--tw-shadow: 0 0 0 1px hsl(var(--sidebar-border));--tw-shadow-colored: 0 0 0 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-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--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-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--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)}.shadow-none{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;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 rgb(0 0 0 / .05);--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 rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--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:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-0{--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(0px + 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-ring{--tw-ring-color: var(--ring)}.ring-sidebar-ring{--tw-ring-color: var(--sidebar-ring)}.ring-offset-background{--tw-ring-offset-color: var(--background)}.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-lg{--tw-backdrop-blur: blur(16px);-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;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[left\,right\,width\]{transition-property:left,right,width;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[margin\,opacity\]{transition-property:margin,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[width\,height\,padding\]{transition-property:width,height,padding;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[width\]{transition-property:width;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-1000{transition-duration:1s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-linear{transition-timing-function:linear}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.animate-in{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.fade-in-0{--tw-enter-opacity: 0}.fade-in-80{--tw-enter-opacity: .8}.zoom-in-95{--tw-enter-scale: .95}.duration-1000{animation-duration:1s}.duration-200{animation-duration:.2s}.duration-300{animation-duration:.3s}.ease-in-out{animation-timing-function:cubic-bezier(.4,0,.2,1)}.ease-linear{animation-timing-function:linear}.paused{animation-play-state:paused}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}.line-clamp-2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.vertical-text{writing-mode:vertical-lr;text-orientation:mixed;transform:rotate(180deg);white-space:nowrap}.bg-youtube-red{background-color:var(--youtube-red)}.slider::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:12px;width:12px;border-radius:50%;background:#1db954;cursor:pointer;border:none;box-shadow:0 0 2px #555}.slider::-webkit-slider-thumb:hover{box-shadow:0 0 0 3px #1db95426}.slider::-moz-range-thumb{height:12px;width:12px;border-radius:50%;background:#1db954;cursor:pointer;border:none;box-shadow:0 0 2px #555}.slider::-moz-range-thumb:hover{box-shadow:0 0 0 3px #1db95426}:root{--background: #ffffff;--foreground: #000000;--muted: #f5f5f5;--muted-foreground: #333333;--popover: #ffffff;--popover-foreground: #000000;--card: #ffffff;--card-foreground: #000000;--border: #e0e0e0;--input: #f9f9f9;--primary: #007bff;--primary-foreground: #ffffff;--secondary: #e0e0e0;--secondary-foreground: #000000;--accent: #eaeaea;--accent-foreground: #000000;--destructive: #dc3545;--destructive-foreground: #ffffff;--ring: #cccccc;--radius: .5rem;--text-secondary: #333333;--text-placeholder: #999999;--text-link: #1a0dab;--text-muted: #cccccc;--input-border: #cccccc;--hover-bg: #eaeaea}.dark{--background: #121212;--foreground: #ffffff;--muted: #1e1e1e;--muted-foreground: #cccccc;--popover: #121212;--popover-foreground: #ffffff;--card: #1f1f1f;--card-foreground: #ffffff;--border: #2a2a2a;--input: #2c2c2c;--primary: #0d6efd;--primary-foreground: #ffffff;--secondary: #3a3a3a;--secondary-foreground: #ffffff;--accent: #333333;--accent-foreground: #ffffff;--destructive: #dc3545;--destructive-foreground: #ffffff;--ring: #444444;--radius: .5rem;--text-secondary: #cccccc;--text-placeholder: #777777;--text-link: #8ab4f8;--text-muted: #555555;--input-border: #444444;--hover-bg: #333333}.file\:border-0::file-selector-button{border-width:0px}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.file\:text-foreground::file-selector-button{color:var(--foreground)}.placeholder\:text-muted-foreground::-moz-placeholder{color:var(--muted-foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:-inset-2:after{content:var(--tw-content);top:-.5rem;right:-.5rem;bottom:-.5rem;left:-.5rem}.after\:inset-y-0:after{content:var(--tw-content);top:0;bottom:0}.after\:left-1\/2:after{content:var(--tw-content);left:50%}.after\:w-1:after{content:var(--tw-content);width:.25rem}.after\:w-\[2px\]:after{content:var(--tw-content);width:2px}.after\:-translate-x-1\/2:after{content:var(--tw-content);--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.first\:rounded-l-md:first-child{border-top-left-radius:calc(var(--radius) - 2px);border-bottom-left-radius:calc(var(--radius) - 2px)}.first\:border-l:first-child{border-left-width:1px}.last\:rounded-r-md:last-child{border-top-right-radius:calc(var(--radius) - 2px);border-bottom-right-radius:calc(var(--radius) - 2px)}.focus-within\:relative:focus-within{position:relative}.focus-within\:z-20:focus-within{z-index:20}.hover\:h-2:hover{height:.5rem}.hover\:scale-105:hover{--tw-scale-x: 1.05;--tw-scale-y: 1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:rounded-lg:hover{border-radius:var(--radius)}.hover\:bg-accent:hover{background-color:var(--accent)}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-green-400:hover{--tw-bg-opacity: 1;background-color:rgb(74 222 128 / var(--tw-bg-opacity, 1))}.hover\:bg-muted:hover{background-color:var(--muted)}.hover\:bg-primary:hover{background-color:var(--primary)}.hover\:bg-secondary:hover{background-color:var(--secondary)}.hover\:bg-sidebar-accent:hover{background-color:var(--sidebar-accent)}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-destructive:hover{color:var(--destructive)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-gray-800:hover{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.hover\:text-gray-900:hover{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.hover\:text-muted-foreground:hover{color:var(--muted-foreground)}.hover\:text-primary-foreground:hover{color:var(--primary-foreground)}.hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-\[0_0_0_1px_hsl\(var\(--sidebar-accent\)\)\]:hover{--tw-shadow: 0 0 0 1px hsl(var(--sidebar-accent));--tw-shadow-colored: 0 0 0 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)}.hover\:after\:bg-sidebar-border:hover:after{content:var(--tw-content);background-color:var(--sidebar-border)}.focus\:border-transparent:focus{border-color:transparent}.focus\:bg-accent:focus{background-color:var(--accent)}.focus\:bg-primary:focus{background-color:var(--primary)}.focus\:text-accent-foreground:focus{color:var(--accent-foreground)}.focus\:text-primary-foreground:focus{color:var(--primary-foreground)}.focus\:opacity-100:focus{opacity:1}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.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-primary:focus{--tw-ring-color: var(--primary)}.focus\:ring-ring:focus{--tw-ring-color: var(--ring)}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-1:focus-visible{--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-visible\:ring-2:focus-visible{--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-visible\:ring-ring:focus-visible{--tw-ring-color: var(--ring)}.focus-visible\:ring-sidebar-ring:focus-visible{--tw-ring-color: var(--sidebar-ring)}.focus-visible\:ring-offset-1:focus-visible{--tw-ring-offset-width: 1px}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color: var(--background)}.active\:bg-sidebar-accent:active{background-color:var(--sidebar-accent)}.active\:text-sidebar-accent-foreground:active{color:var(--sidebar-accent-foreground)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group\/menu-item:focus-within .group-focus-within\/menu-item\:opacity-100{opacity:1}.group:hover .group-hover\:bg-opacity-30{--tw-bg-opacity: .3}.group:hover .group-hover\:text-foreground{color:var(--foreground)}.group\/menu-item:hover .group-hover\/menu-item\:opacity-100,.group:hover .group-hover\:opacity-100{opacity:1}.group.destructive .group-\[\.destructive\]\:text-red-300{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.group.destructive .group-\[\.destructive\]\:hover\:bg-destructive:hover{background-color:var(--destructive)}.group.destructive .group-\[\.destructive\]\:hover\:text-destructive-foreground:hover{color:var(--destructive-foreground)}.group.destructive .group-\[\.destructive\]\:hover\:text-red-50:hover{--tw-text-opacity: 1;color:rgb(254 242 242 / var(--tw-text-opacity, 1))}.group.destructive .group-\[\.destructive\]\:focus\:ring-destructive:focus{--tw-ring-color: var(--destructive)}.group.destructive .group-\[\.destructive\]\:focus\:ring-red-400:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity, 1))}.group.destructive .group-\[\.destructive\]\:focus\:ring-offset-red-600:focus{--tw-ring-offset-color: #dc2626}.peer\/menu-button:hover~.peer-hover\/menu-button\:text-sidebar-accent-foreground{color:var(--sidebar-accent-foreground)}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.has-\[\[data-variant\=inset\]\]\:bg-sidebar:has([data-variant=inset]){background-color:var(--sidebar-background)}.has-\[\:disabled\]\:opacity-50:has(:disabled){opacity:.5}.group\/menu-item:has([data-sidebar=menu-action]) .group-has-\[\[data-sidebar\=menu-action\]\]\/menu-item\:pr-8{padding-right:2rem}.aria-disabled\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\:opacity-50[aria-disabled=true]{opacity:.5}.aria-selected\:bg-accent[aria-selected=true]{background-color:var(--accent)}.aria-selected\:text-accent-foreground[aria-selected=true]{color:var(--accent-foreground)}.aria-selected\:text-muted-foreground[aria-selected=true]{color:var(--muted-foreground)}.aria-selected\:opacity-100[aria-selected=true]{opacity:1}.data-\[disabled\=true\]\:pointer-events-none[data-disabled=true],.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[panel-group-direction\=vertical\]\:h-px[data-panel-group-direction=vertical]{height:1px}.data-\[panel-group-direction\=vertical\]\:w-full[data-panel-group-direction=vertical]{width:100%}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=checked\]\:translate-x-5[data-state=checked]{--tw-translate-x: 1.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked],.data-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel]{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end]{--tw-translate-x: var(--radix-toast-swipe-end-x);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\][data-swipe=move]{--tw-translate-x: var(--radix-toast-swipe-move-x);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes accordion-up{0%{height:var(--radix-accordion-content-height)}to{height:0}}.data-\[state\=closed\]\:animate-accordion-up[data-state=closed]{animation:accordion-up .2s ease-out}@keyframes accordion-down{0%{height:0}to{height:var(--radix-accordion-content-height)}}.data-\[state\=open\]\:animate-accordion-down[data-state=open]{animation:accordion-down .2s ease-out}.data-\[panel-group-direction\=vertical\]\:flex-col[data-panel-group-direction=vertical]{flex-direction:column}.data-\[active\=true\]\:bg-sidebar-accent[data-active=true]{background-color:var(--sidebar-accent)}.data-\[selected\=\'true\'\]\:bg-accent[data-selected=true]{background-color:var(--accent)}.data-\[state\=active\]\:bg-background[data-state=active]{background-color:var(--background)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--primary)}.data-\[state\=on\]\:bg-accent[data-state=on],.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:var(--accent)}.data-\[state\=open\]\:bg-secondary[data-state=open]{background-color:var(--secondary)}.data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:var(--muted)}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:var(--input)}.data-\[active\=true\]\:font-medium[data-active=true]{font-weight:500}.data-\[active\=true\]\:text-sidebar-accent-foreground[data-active=true]{color:var(--sidebar-accent-foreground)}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.data-\[selected\=true\]\:text-accent-foreground[data-selected=true]{color:var(--accent-foreground)}.data-\[state\=active\]\:text-foreground[data-state=active]{color:var(--foreground)}.data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:var(--primary-foreground)}.data-\[state\=on\]\:text-accent-foreground[data-state=on],.data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:var(--muted-foreground)}.data-\[disabled\=true\]\:opacity-50[data-disabled=true],.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[state\=open\]\:opacity-100[data-state=open]{opacity:1}.data-\[state\=active\]\:shadow-sm[data-state=active]{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--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)}.data-\[swipe\=move\]\:transition-none[data-swipe=move]{transition-property:none}.data-\[state\=closed\]\:duration-300[data-state=closed]{transition-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{transition-duration:.5s}.data-\[motion\^\=from-\]\:animate-in[data-motion^=from-],.data-\[state\=open\]\:animate-in[data-state=open],.data-\[state\=visible\]\:animate-in[data-state=visible]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.data-\[motion\^\=to-\]\:animate-out[data-motion^=to-],.data-\[state\=closed\]\:animate-out[data-state=closed],.data-\[state\=hidden\]\:animate-out[data-state=hidden],.data-\[swipe\=end\]\:animate-out[data-swipe=end]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity: initial;--tw-exit-scale: initial;--tw-exit-rotate: initial;--tw-exit-translate-x: initial;--tw-exit-translate-y: initial}.data-\[motion\^\=from-\]\:fade-in[data-motion^=from-]{--tw-enter-opacity: 0}.data-\[motion\^\=to-\]\:fade-out[data-motion^=to-],.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity: 0}.data-\[state\=closed\]\:fade-out-80[data-state=closed]{--tw-exit-opacity: .8}.data-\[state\=hidden\]\:fade-out[data-state=hidden]{--tw-exit-opacity: 0}.data-\[state\=open\]\:fade-in-0[data-state=open],.data-\[state\=visible\]\:fade-in[data-state=visible]{--tw-enter-opacity: 0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale: .95}.data-\[state\=open\]\:zoom-in-90[data-state=open]{--tw-enter-scale: .9}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale: .95}.data-\[motion\=from-end\]\:slide-in-from-right-52[data-motion=from-end]{--tw-enter-translate-x: 13rem}.data-\[motion\=from-start\]\:slide-in-from-left-52[data-motion=from-start]{--tw-enter-translate-x: -13rem}.data-\[motion\=to-end\]\:slide-out-to-right-52[data-motion=to-end]{--tw-exit-translate-x: 13rem}.data-\[motion\=to-start\]\:slide-out-to-left-52[data-motion=to-start]{--tw-exit-translate-x: -13rem}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y: -.5rem}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x: .5rem}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x: -.5rem}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y: .5rem}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y: 100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x: -100%}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x: -50%}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed],.data-\[state\=closed\]\:slide-out-to-right-full[data-state=closed]{--tw-exit-translate-x: 100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y: -100%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y: -48%}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y: 100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x: -100%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x: -50%}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x: 100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y: -100%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y: -48%}.data-\[state\=open\]\:slide-in-from-top-full[data-state=open]{--tw-enter-translate-y: -100%}.data-\[state\=closed\]\:duration-300[data-state=closed]{animation-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{animation-duration:.5s}.data-\[panel-group-direction\=vertical\]\:after\:left-0[data-panel-group-direction=vertical]:after{content:var(--tw-content);left:0}.data-\[panel-group-direction\=vertical\]\:after\:h-1[data-panel-group-direction=vertical]:after{content:var(--tw-content);height:.25rem}.data-\[panel-group-direction\=vertical\]\:after\:w-full[data-panel-group-direction=vertical]:after{content:var(--tw-content);width:100%}.data-\[panel-group-direction\=vertical\]\:after\:-translate-y-1\/2[data-panel-group-direction=vertical]:after{content:var(--tw-content);--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[panel-group-direction\=vertical\]\:after\:translate-x-0[data-panel-group-direction=vertical]:after{content:var(--tw-content);--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=open\]\:hover\:bg-accent:hover[data-state=open]{background-color:var(--accent)}.data-\[state\=open\]\:hover\:bg-sidebar-accent:hover[data-state=open]{background-color:var(--sidebar-accent)}.data-\[state\=open\]\:hover\:text-sidebar-accent-foreground:hover[data-state=open]{color:var(--sidebar-accent-foreground)}.data-\[state\=open\]\:focus\:bg-accent:focus[data-state=open]{background-color:var(--accent)}.group[data-collapsible=offcanvas] .group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]{left:calc(var(--sidebar-width) * -1)}.group[data-collapsible=offcanvas] .group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]{right:calc(var(--sidebar-width) * -1)}.group[data-side=left] .group-data-\[side\=left\]\:-right-4{right:-1rem}.group[data-side=right] .group-data-\[side\=right\]\:left-0{left:0}.group[data-collapsible=icon] .group-data-\[collapsible\=icon\]\:-mt-8{margin-top:-2rem}.group[data-collapsible=icon] .group-data-\[collapsible\=icon\]\:hidden{display:none}.group[data-collapsible=icon] .group-data-\[collapsible\=icon\]\:\!size-8{width:2rem!important;height:2rem!important}.group[data-collapsible=icon] .group-data-\[collapsible\=icon\]\:w-\[--sidebar-width-icon\]{width:var(--sidebar-width-icon)}.group[data-collapsible=icon] .group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)_\+_theme\(spacing\.4\)\)\]{width:calc(var(--sidebar-width-icon) + 1rem)}.group[data-collapsible=icon] .group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)_\+_theme\(spacing\.4\)_\+2px\)\]{width:calc(var(--sidebar-width-icon) + 1rem + 2px)}.group[data-collapsible=offcanvas] .group-data-\[collapsible\=offcanvas\]\:w-0{width:0px}.group[data-collapsible=offcanvas] .group-data-\[collapsible\=offcanvas\]\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group[data-side=right] .group-data-\[side\=right\]\:rotate-180,.group[data-state=open] .group-data-\[state\=open\]\:rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group[data-collapsible=icon] .group-data-\[collapsible\=icon\]\:overflow-hidden{overflow:hidden}.group[data-variant=floating] .group-data-\[variant\=floating\]\:rounded-lg{border-radius:var(--radius)}.group[data-variant=floating] .group-data-\[variant\=floating\]\:border{border-width:1px}.group[data-side=left] .group-data-\[side\=left\]\:border-r{border-right-width:1px}.group[data-side=right] .group-data-\[side\=right\]\:border-l{border-left-width:1px}.group[data-variant=floating] .group-data-\[variant\=floating\]\:border-sidebar-border{border-color:var(--sidebar-border)}.group[data-collapsible=icon] .group-data-\[collapsible\=icon\]\:\!p-0{padding:0!important}.group[data-collapsible=icon] .group-data-\[collapsible\=icon\]\:\!p-2{padding:.5rem!important}.group[data-collapsible=icon] .group-data-\[collapsible\=icon\]\:opacity-0{opacity:0}.group[data-variant=floating] .group-data-\[variant\=floating\]\:shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--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)}.group[data-collapsible=offcanvas] .group-data-\[collapsible\=offcanvas\]\:after\:left-full:after{content:var(--tw-content);left:100%}.group[data-collapsible=offcanvas] .group-data-\[collapsible\=offcanvas\]\:hover\:bg-sidebar:hover{background-color:var(--sidebar-background)}.peer\/menu-button[data-size=default]~.peer-data-\[size\=default\]\/menu-button\:top-1\.5{top:.375rem}.peer\/menu-button[data-size=lg]~.peer-data-\[size\=lg\]\/menu-button\:top-2\.5{top:.625rem}.peer\/menu-button[data-size=sm]~.peer-data-\[size\=sm\]\/menu-button\:top-1{top:.25rem}.peer\/menu-button[data-active=true]~.peer-data-\[active\=true\]\/menu-button\:text-sidebar-accent-foreground{color:var(--sidebar-accent-foreground)}.dark\:-rotate-90:is(.dark *){--tw-rotate: -90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:rotate-0:is(.dark *){--tw-rotate: 0deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:scale-0:is(.dark *){--tw-scale-x: 0;--tw-scale-y: 0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:scale-100:is(.dark *){--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:border-blue-800:is(.dark *){--tw-border-opacity: 1;border-color:rgb(30 64 175 / var(--tw-border-opacity, 1))}.dark\:border-destructive:is(.dark *){border-color:var(--destructive)}.dark\:border-gray-600:is(.dark *){--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity, 1))}.dark\:border-gray-800:is(.dark *){--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity, 1))}.dark\:border-green-800:is(.dark *){--tw-border-opacity: 1;border-color:rgb(22 101 52 / var(--tw-border-opacity, 1))}.dark\:border-orange-800:is(.dark *){--tw-border-opacity: 1;border-color:rgb(154 52 18 / var(--tw-border-opacity, 1))}.dark\:bg-blue-900\/20:is(.dark *){background-color:#1e3a8a33}.dark\:bg-card:is(.dark *){background-color:var(--card)}.dark\:bg-gray-600:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-700:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-800:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.dark\:bg-green-900\/20:is(.dark *){background-color:#14532d33}.dark\:bg-orange-950\/20:is(.dark *){background-color:#43140733}.dark\:text-blue-400:is(.dark *){--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.dark\:text-gray-300:is(.dark *){--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.dark\:text-gray-400:is(.dark *){--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.dark\:text-green-400:is(.dark *){--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.dark\:text-orange-200:is(.dark *){--tw-text-opacity: 1;color:rgb(254 215 170 / var(--tw-text-opacity, 1))}.dark\:text-orange-400:is(.dark *){--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.dark\:text-red-400:is(.dark *){--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.dark\:text-white:is(.dark *){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.dark\:hover\:bg-gray-800:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.dark\:hover\:text-gray-200:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.dark\:hover\:text-white:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}@media (min-width: 640px){.sm\:bottom-0{bottom:0}.sm\:right-0{right:0}.sm\:top-auto{top:auto}.sm\:mt-0{margin-top:0}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:max-w-md{max-width:28rem}.sm\:max-w-sm{max-width:24rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:flex-col{flex-direction:column}.sm\:justify-end{justify-content:flex-end}.sm\:gap-2\.5{gap:.625rem}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.sm\: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))}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}.data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open]{--tw-enter-translate-y: 100%}}@media (min-width: 768px){.md\:absolute{position:absolute}.md\:left-auto{left:auto}.md\:right-4{right:1rem}.md\:mb-8{margin-bottom:2rem}.md\:ml-64{margin-left:16rem}.md\:mt-8{margin-top:2rem}.md\:block{display:block}.md\:flex{display:flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:h-16{height:4rem}.md\:h-4{height:1rem}.md\:w-16{width:4rem}.md\:w-4{width:1rem}.md\:w-80{width:20rem}.md\:w-96{width:24rem}.md\:w-\[var\(--radix-navigation-menu-viewport-width\)\]{width:var(--radix-navigation-menu-viewport-width)}.md\:w-auto{width:auto}.md\:max-w-\[420px\]{max-width:420px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:gap-6{gap:1.5rem}.md\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.md\: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))}.md\:px-6{padding-left:1.5rem;padding-right:1.5rem}.md\:py-16{padding-top:4rem;padding-bottom:4rem}.md\:py-3{padding-top:.75rem;padding-bottom:.75rem}.md\:py-4{padding-top:1rem;padding-bottom:1rem}.md\:py-8{padding-top:2rem;padding-bottom:2rem}.md\:text-3xl{font-size:1.875rem;line-height:2.25rem}.md\:text-base{font-size:1rem;line-height:1.5rem}.md\:text-sm{font-size:.875rem;line-height:1.25rem}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}.md\:opacity-0{opacity:0}.after\:md\:hidden:after{content:var(--tw-content);display:none}.group:hover .md\:group-hover\:opacity-100{opacity:1}.peer[data-variant=inset]~.md\:peer-data-\[variant\=inset\]\:m-2{margin:.5rem}.peer[data-state=collapsed][data-variant=inset]~.md\:peer-data-\[state\=collapsed\]\:peer-data-\[variant\=inset\]\:ml-2{margin-left:.5rem}.peer[data-variant=inset]~.md\:peer-data-\[variant\=inset\]\:ml-0{margin-left:0}.peer[data-variant=inset]~.md\:peer-data-\[variant\=inset\]\:rounded-xl{border-radius:.75rem}.peer[data-variant=inset]~.md\:peer-data-\[variant\=inset\]\:shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--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)}}@media (min-width: 1024px){.lg\:col-span-1{grid-column:span 1 / span 1}.lg\:col-span-2{grid-column:span 2 / span 2}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width: 1280px){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.\[\&\:has\(\[aria-selected\]\)\]\:bg-accent:has([aria-selected]){background-color:var(--accent)}.first\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-l-md:has([aria-selected]):first-child{border-top-left-radius:calc(var(--radius) - 2px);border-bottom-left-radius:calc(var(--radius) - 2px)}.last\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-r-md:has([aria-selected]):last-child{border-top-right-radius:calc(var(--radius) - 2px);border-bottom-right-radius:calc(var(--radius) - 2px)}.\[\&\:has\(\[aria-selected\]\.day-range-end\)\]\:rounded-r-md:has([aria-selected].day-range-end){border-top-right-radius:calc(var(--radius) - 2px);border-bottom-right-radius:calc(var(--radius) - 2px)}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:0}.\[\&\>button\]\:hidden>button{display:none}.\[\&\>span\:last-child\]\:truncate>span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.\[\&\>span\]\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\[\&\>svg\+div\]\:translate-y-\[-3px\]>svg+div{--tw-translate-y: -3px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&\>svg\]\:absolute>svg{position:absolute}.\[\&\>svg\]\:left-4>svg{left:1rem}.\[\&\>svg\]\:top-4>svg{top:1rem}.\[\&\>svg\]\:size-4>svg{width:1rem;height:1rem}.\[\&\>svg\]\:h-2\.5>svg{height:.625rem}.\[\&\>svg\]\:h-3>svg{height:.75rem}.\[\&\>svg\]\:h-3\.5>svg{height:.875rem}.\[\&\>svg\]\:w-2\.5>svg{width:.625rem}.\[\&\>svg\]\:w-3>svg{width:.75rem}.\[\&\>svg\]\:w-3\.5>svg{width:.875rem}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&\>svg\]\:text-destructive>svg{color:var(--destructive)}.\[\&\>svg\]\:text-foreground>svg{color:var(--foreground)}.\[\&\>svg\]\:text-muted-foreground>svg{color:var(--muted-foreground)}.\[\&\>svg\]\:text-sidebar-accent-foreground>svg{color:var(--sidebar-accent-foreground)}.\[\&\>svg\~\*\]\:pl-7>svg~*{padding-left:1.75rem}.\[\&\>tr\]\:last\:border-b-0:last-child>tr{border-bottom-width:0px}.\[\&\[data-panel-group-direction\=vertical\]\>div\]\:rotate-90[data-panel-group-direction=vertical]>div{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&_\.recharts-cartesian-axis-tick_text\]\:fill-muted-foreground .recharts-cartesian-axis-tick text{fill:var(--muted-foreground)}.\[\&_\.recharts-curve\.recharts-tooltip-cursor\]\:stroke-border .recharts-curve.recharts-tooltip-cursor{stroke:var(--border)}.\[\&_\.recharts-dot\[stroke\=\'\#fff\'\]\]\:stroke-transparent .recharts-dot[stroke="#fff"]{stroke:transparent}.\[\&_\.recharts-layer\]\:outline-none .recharts-layer{outline:2px solid transparent;outline-offset:2px}.\[\&_\.recharts-polar-grid_\[stroke\=\'\#ccc\'\]\]\:stroke-border .recharts-polar-grid [stroke="#ccc"]{stroke:var(--border)}.\[\&_\.recharts-radial-bar-background-sector\]\:fill-muted .recharts-radial-bar-background-sector,.\[\&_\.recharts-rectangle\.recharts-tooltip-cursor\]\:fill-muted .recharts-rectangle.recharts-tooltip-cursor{fill:var(--muted)}.\[\&_\.recharts-reference-line_\[stroke\=\'\#ccc\'\]\]\:stroke-border .recharts-reference-line [stroke="#ccc"]{stroke:var(--border)}.\[\&_\.recharts-sector\[stroke\=\'\#fff\'\]\]\:stroke-transparent .recharts-sector[stroke="#fff"]{stroke:transparent}.\[\&_\.recharts-sector\]\:outline-none .recharts-sector,.\[\&_\.recharts-surface\]\:outline-none .recharts-surface{outline:2px solid transparent;outline-offset:2px}.\[\&_\[cmdk-group-heading\]\]\:px-2 [cmdk-group-heading]{padding-left:.5rem;padding-right:.5rem}.\[\&_\[cmdk-group-heading\]\]\:py-1\.5 [cmdk-group-heading]{padding-top:.375rem;padding-bottom:.375rem}.\[\&_\[cmdk-group-heading\]\]\:text-xs [cmdk-group-heading]{font-size:.75rem;line-height:1rem}.\[\&_\[cmdk-group-heading\]\]\:font-medium [cmdk-group-heading]{font-weight:500}.\[\&_\[cmdk-group-heading\]\]\:text-muted-foreground [cmdk-group-heading]{color:var(--muted-foreground)}.\[\&_\[cmdk-group\]\:not\(\[hidden\]\)_\~\[cmdk-group\]\]\:pt-0 [cmdk-group]:not([hidden])~[cmdk-group]{padding-top:0}.\[\&_\[cmdk-group\]\]\:px-2 [cmdk-group]{padding-left:.5rem;padding-right:.5rem}.\[\&_\[cmdk-input-wrapper\]_svg\]\:h-5 [cmdk-input-wrapper] svg{height:1.25rem}.\[\&_\[cmdk-input-wrapper\]_svg\]\:w-5 [cmdk-input-wrapper] svg{width:1.25rem}.\[\&_\[cmdk-input\]\]\:h-12 [cmdk-input]{height:3rem}.\[\&_\[cmdk-item\]\]\:px-2 [cmdk-item]{padding-left:.5rem;padding-right:.5rem}.\[\&_\[cmdk-item\]\]\:py-3 [cmdk-item]{padding-top:.75rem;padding-bottom:.75rem}.\[\&_\[cmdk-item\]_svg\]\:h-5 [cmdk-item] svg{height:1.25rem}.\[\&_\[cmdk-item\]_svg\]\:w-5 [cmdk-item] svg{width:1.25rem}.\[\&_p\]\:leading-relaxed p{line-height:1.625}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-width:0px}.\[\&_tr\]\:border-b tr{border-bottom-width:1px}[data-side=left][data-collapsible=offcanvas] .\[\[data-side\=left\]\[data-collapsible\=offcanvas\]_\&\]\:-right-2{right:-.5rem}[data-side=left][data-state=collapsed] .\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize{cursor:e-resize}[data-side=left] .\[\[data-side\=left\]_\&\]\:cursor-w-resize{cursor:w-resize}[data-side=right][data-collapsible=offcanvas] .\[\[data-side\=right\]\[data-collapsible\=offcanvas\]_\&\]\:-left-2{left:-.5rem}[data-side=right][data-state=collapsed] .\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize{cursor:w-resize}[data-side=right] .\[\[data-side\=right\]_\&\]\:cursor-e-resize{cursor:e-resize}
dist/public/assets/index.js ADDED
The diff for this file is too large to render. See raw diff
 
dist/public/create_icon.js ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Simple script to create PWA icons
2
+ const canvas = document.createElement('canvas');
3
+ const ctx = canvas.getContext('2d');
4
+
5
+ function createIcon(size) {
6
+ canvas.width = size;
7
+ canvas.height = size;
8
+
9
+ // Background
10
+ ctx.fillStyle = '#0d6efd';
11
+ ctx.fillRect(0, 0, size, size);
12
+
13
+ // Audio bars
14
+ ctx.fillStyle = 'white';
15
+ const barWidth = size * 0.06;
16
+ const spacing = size * 0.08;
17
+ const baseY = size * 0.35;
18
+
19
+ // Bar heights and positions
20
+ const bars = [
21
+ { height: size * 0.3, offset: 0 },
22
+ { height: size * 0.45, offset: -size * 0.075 },
23
+ { height: size * 0.38, offset: -size * 0.04 },
24
+ { height: size * 0.3, offset: 0 }
25
+ ];
26
+
27
+ let x = size * 0.25;
28
+ bars.forEach(bar => {
29
+ ctx.fillRect(x, baseY + bar.offset, barWidth, bar.height);
30
+ x += barWidth + spacing;
31
+ });
32
+
33
+ // Text
34
+ ctx.fillStyle = 'white';
35
+ ctx.font = `bold ${size * 0.09}px Arial`;
36
+ ctx.textAlign = 'center';
37
+ ctx.fillText('VELIN', size / 2, size * 0.85);
38
+
39
+ return canvas.toDataURL('image/png');
40
+ }
41
+
42
+ // Create icons
43
+ const icon192 = createIcon(192);
44
+ const icon512 = createIcon(512);
45
+
46
+ console.log('Icons created');
dist/public/generate_icons.html ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Generate PWA Icons</title>
5
+ </head>
6
+ <body>
7
+ <canvas id="canvas192" width="192" height="192" style="border: 1px solid #000; margin: 10px;"></canvas>
8
+ <canvas id="canvas512" width="512" height="512" style="border: 1px solid #000; margin: 10px;"></canvas>
9
+
10
+ <script>
11
+ function drawIcon(canvas, size) {
12
+ const ctx = canvas.getContext('2d');
13
+
14
+ // Background
15
+ ctx.fillStyle = '#0d6efd';
16
+ ctx.fillRect(0, 0, size, size);
17
+
18
+ // Border radius effect (simplified)
19
+ ctx.globalCompositeOperation = 'destination-in';
20
+ ctx.beginPath();
21
+ ctx.roundRect(0, 0, size, size, size * 0.125);
22
+ ctx.fill();
23
+ ctx.globalCompositeOperation = 'source-over';
24
+
25
+ // Audio bars
26
+ ctx.fillStyle = 'white';
27
+ const barWidth = size * 0.06;
28
+ const spacing = size * 0.08;
29
+ const baseY = size * 0.35;
30
+
31
+ // Bar 1
32
+ const x1 = size * 0.25;
33
+ const h1 = size * 0.3;
34
+ ctx.fillRect(x1, baseY, barWidth, h1);
35
+
36
+ // Bar 2
37
+ const x2 = x1 + barWidth + spacing;
38
+ const h2 = size * 0.45;
39
+ ctx.fillRect(x2, baseY - size * 0.075, barWidth, h2);
40
+
41
+ // Bar 3
42
+ const x3 = x2 + barWidth + spacing;
43
+ const h3 = size * 0.38;
44
+ ctx.fillRect(x3, baseY - size * 0.04, barWidth, h3);
45
+
46
+ // Bar 4
47
+ const x4 = x3 + barWidth + spacing;
48
+ const h4 = size * 0.3;
49
+ ctx.fillRect(x4, baseY, barWidth, h4);
50
+
51
+ // Text
52
+ ctx.fillStyle = 'white';
53
+ ctx.font = `bold ${size * 0.08}px Arial`;
54
+ ctx.textAlign = 'center';
55
+ ctx.fillText('VELIN', size / 2, size * 0.85);
56
+ }
57
+
58
+ drawIcon(document.getElementById('canvas192'), 192);
59
+ drawIcon(document.getElementById('canvas512'), 512);
60
+
61
+ // Convert to downloads
62
+ setTimeout(() => {
63
+ const canvas192 = document.getElementById('canvas192');
64
+ const canvas512 = document.getElementById('canvas512');
65
+
66
+ const link192 = document.createElement('a');
67
+ link192.download = 'icon-192.png';
68
+ link192.href = canvas192.toDataURL();
69
+ link192.click();
70
+
71
+ setTimeout(() => {
72
+ const link512 = document.createElement('a');
73
+ link512.download = 'icon-512.png';
74
+ link512.href = canvas512.toDataURL();
75
+ link512.click();
76
+ }, 1000);
77
+ }, 1000);
78
+ </script>
79
+ </body>
80
+ </html>
dist/public/icon-192.png ADDED
dist/public/icon-192.svg ADDED
dist/public/icon-512.png ADDED
dist/public/icon-512.svg ADDED
dist/public/icon.svg ADDED
dist/public/index.html ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
6
+ <title>VELIN - Podcast Player</title>
7
+
8
+ <!-- PWA Manifest -->
9
+ <link rel="manifest" href="/manifest.json" />
10
+
11
+ <!-- PWA Meta Tags -->
12
+ <meta name="theme-color" content="#0d6efd" />
13
+ <meta name="description" content="A modern podcast streaming application with offline playback capabilities" />
14
+
15
+ <!-- Apple Touch Icon -->
16
+ <link rel="apple-touch-icon" href="/icon-192.png" />
17
+
18
+ <!-- Apple Web App -->
19
+ <meta name="apple-mobile-web-app-capable" content="yes" />
20
+ <meta name="apple-mobile-web-app-status-bar-style" content="default" />
21
+ <meta name="apple-mobile-web-app-title" content="VELIN" />
22
+ <script type="module" crossorigin src="/assets/index.js"></script>
23
+ <link rel="stylesheet" crossorigin href="/assets/index.css">
24
+ </head>
25
+ <body>
26
+ <div id="root"></div>
27
+
28
+ <!-- Service Worker Registration -->
29
+ <script>
30
+ if ('serviceWorker' in navigator) {
31
+ window.addEventListener('load', () => {
32
+ navigator.serviceWorker.register('/sw.js')
33
+ .then((registration) => {
34
+ console.log('SW registered: ', registration);
35
+ })
36
+ .catch((registrationError) => {
37
+ console.log('SW registration failed: ', registrationError);
38
+ });
39
+ });
40
+ }
41
+ </script>
42
+ </body>
43
+ </html>
dist/public/manifest.json ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "VELIN - Podcast Player",
3
+ "short_name": "VELIN",
4
+ "description": "A modern podcast streaming application with offline playback capabilities",
5
+ "start_url": "/",
6
+ "scope": "/",
7
+ "display": "standalone",
8
+ "background_color": "#121212",
9
+ "theme_color": "#0d6efd",
10
+ "orientation": "portrait-primary",
11
+ "categories": ["entertainment", "music"],
12
+ "lang": "en",
13
+ "icons": [
14
+ {
15
+ "src": "/icon-192.svg",
16
+ "sizes": "192x192",
17
+ "type": "image/svg+xml",
18
+ "purpose": "any"
19
+ },
20
+ {
21
+ "src": "/icon-192.svg",
22
+ "sizes": "192x192",
23
+ "type": "image/svg+xml",
24
+ "purpose": "maskable"
25
+ },
26
+ {
27
+ "src": "/icon-512.svg",
28
+ "sizes": "512x512",
29
+ "type": "image/svg+xml",
30
+ "purpose": "any"
31
+ },
32
+ {
33
+ "src": "/icon-512.svg",
34
+ "sizes": "512x512",
35
+ "type": "image/svg+xml",
36
+ "purpose": "maskable"
37
+ }
38
+ ],
39
+ "shortcuts": [
40
+ {
41
+ "name": "Library",
42
+ "short_name": "Library",
43
+ "description": "View your podcast library",
44
+ "url": "/library"
45
+ },
46
+ {
47
+ "name": "History",
48
+ "short_name": "History",
49
+ "description": "View listening history",
50
+ "url": "/history"
51
+ }
52
+ ]
53
+ }
dist/public/offline.html ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>VELIN - Offline</title>
7
+ <style>
8
+ body {
9
+ font-family: 'Roboto', Arial, sans-serif;
10
+ background: #121212;
11
+ color: #ffffff;
12
+ margin: 0;
13
+ padding: 0;
14
+ display: flex;
15
+ flex-direction: column;
16
+ align-items: center;
17
+ justify-content: center;
18
+ min-height: 100vh;
19
+ text-align: center;
20
+ }
21
+ .container {
22
+ max-width: 400px;
23
+ padding: 2rem;
24
+ }
25
+ .logo {
26
+ font-size: 2rem;
27
+ font-weight: bold;
28
+ margin-bottom: 1rem;
29
+ color: #0d6efd;
30
+ }
31
+ .message {
32
+ font-size: 1.2rem;
33
+ margin-bottom: 1rem;
34
+ }
35
+ .description {
36
+ color: #cccccc;
37
+ margin-bottom: 2rem;
38
+ }
39
+ .button {
40
+ background: #0d6efd;
41
+ color: white;
42
+ border: none;
43
+ padding: 0.75rem 1.5rem;
44
+ border-radius: 0.5rem;
45
+ font-size: 1rem;
46
+ cursor: pointer;
47
+ text-decoration: none;
48
+ display: inline-block;
49
+ }
50
+ .button:hover {
51
+ background: #0b5ed7;
52
+ }
53
+ .offline-icon {
54
+ width: 64px;
55
+ height: 64px;
56
+ margin: 0 auto 2rem;
57
+ opacity: 0.5;
58
+ }
59
+ </style>
60
+ </head>
61
+ <body>
62
+ <div class="container">
63
+ <div class="offline-icon">
64
+ <svg viewBox="0 0 24 24" fill="currentColor">
65
+ <path d="M23 12l-2.44-2.78.34-3.68-3.61-.82-1.89-3.18L12 3 8.6 1.54 6.71 4.72l-3.61.81.34 3.68L1 12l2.44 2.78-.34 3.68 3.61.82 1.89 3.18L12 21l3.4 1.46 1.89-3.18 3.61-.81-.34-3.68L23 12zM13 17h-2v-6h2v6zm0-8h-2V7h2v2z"/>
66
+ </svg>
67
+ </div>
68
+
69
+ <div class="logo">VELIN</div>
70
+
71
+ <div class="message">You're offline</div>
72
+
73
+ <div class="description">
74
+ No internet connection detected. You can still listen to your downloaded podcasts in the Downloads section.
75
+ </div>
76
+
77
+ <a href="/" class="button" onclick="window.location.reload()">
78
+ Try Again
79
+ </a>
80
+ </div>
81
+
82
+ <script>
83
+ // Check for connection and reload when back online
84
+ window.addEventListener('online', () => {
85
+ window.location.reload();
86
+ });
87
+ </script>
88
+ </body>
89
+ </html>
dist/public/register-sw.js ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Service Worker Registration Script
2
+ // This ensures the service worker is only registered once
3
+
4
+ if ('serviceWorker' in navigator) {
5
+ window.addEventListener('load', () => {
6
+ navigator.serviceWorker.register('/sw.js', {
7
+ scope: '/'
8
+ })
9
+ .then(registration => {
10
+ console.log('SW registered: ', registration);
11
+
12
+ // Handle updates
13
+ registration.addEventListener('updatefound', () => {
14
+ const newWorker = registration.installing;
15
+ if (newWorker) {
16
+ newWorker.addEventListener('statechange', () => {
17
+ if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
18
+ // New service worker available
19
+ console.log('New service worker available');
20
+ }
21
+ });
22
+ }
23
+ });
24
+ })
25
+ .catch(registrationError => {
26
+ console.log('SW registration failed: ', registrationError);
27
+ });
28
+ });
29
+ }
dist/public/sw.js ADDED
@@ -0,0 +1,542 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Service Worker for VELIN Podcast Player
2
+ // Handles offline audio caching and playback
3
+
4
+ const CACHE_NAME = 'velin-audio-cache-v2';
5
+ const STATIC_CACHE_NAME = 'velin-static-v2';
6
+ const THUMBNAIL_CACHE_NAME = 'velin-thumbnails-v2';
7
+
8
+ // Static files to cache for offline functionality
9
+ const STATIC_FILES = [
10
+ '/',
11
+ '/manifest.json',
12
+ '/icon-192.png',
13
+ '/icon-512.png',
14
+ '/icon-192.svg',
15
+ '/icon-512.svg'
16
+ ];
17
+
18
+ // Install event - cache static files and discover dynamic assets
19
+ self.addEventListener('install', event => {
20
+ console.log('VELIN Service Worker installed');
21
+
22
+ event.waitUntil(
23
+ cacheStaticFiles()
24
+ .then(() => {
25
+ console.log('All static files cached successfully');
26
+ })
27
+ .catch(error => {
28
+ console.error('Failed to cache static files:', error);
29
+ })
30
+ );
31
+
32
+ self.skipWaiting();
33
+ });
34
+
35
+ // Cache static files and discover dynamic assets
36
+ async function cacheStaticFiles() {
37
+ try {
38
+ const cache = await caches.open(STATIC_CACHE_NAME);
39
+
40
+ // First, cache the main HTML page to discover dynamic assets
41
+ const indexResponse = await fetch('/');
42
+ if (indexResponse.ok) {
43
+ await cache.put('/', indexResponse.clone());
44
+
45
+ // Parse HTML to find JS and CSS files
46
+ const htmlText = await indexResponse.text();
47
+ const dynamicAssets = extractAssetsFromHTML(htmlText);
48
+
49
+ console.log('Found dynamic assets:', dynamicAssets);
50
+
51
+ // Cache all discovered assets along with static files
52
+ const allFilesToCache = [...STATIC_FILES.filter(file => file !== '/'), ...dynamicAssets];
53
+
54
+ // Cache each file individually to handle failures gracefully
55
+ await Promise.allSettled(
56
+ allFilesToCache.map(async (file) => {
57
+ try {
58
+ const response = await fetch(file);
59
+ if (response.ok) {
60
+ await cache.put(file, response);
61
+ console.log('Cached:', file);
62
+ } else {
63
+ console.warn('Failed to fetch:', file, response.status);
64
+ }
65
+ } catch (error) {
66
+ console.warn('Error caching:', file, error);
67
+ }
68
+ })
69
+ );
70
+ }
71
+ } catch (error) {
72
+ console.error('Error in cacheStaticFiles:', error);
73
+ // Fallback: try to cache just the static files
74
+ const cache = await caches.open(STATIC_CACHE_NAME);
75
+ await Promise.allSettled(
76
+ STATIC_FILES.map(async (file) => {
77
+ try {
78
+ const response = await fetch(file);
79
+ if (response.ok) {
80
+ await cache.put(file, response);
81
+ }
82
+ } catch (error) {
83
+ console.warn('Fallback cache error for:', file);
84
+ }
85
+ })
86
+ );
87
+ }
88
+ }
89
+
90
+ // Extract JS and CSS files from HTML
91
+ function extractAssetsFromHTML(html) {
92
+ const assets = [];
93
+
94
+ // Find script tags with src
95
+ const scriptMatches = html.matchAll(/<script[^>]+src="([^"]+)"/g);
96
+ for (const match of scriptMatches) {
97
+ if (match[1] && !match[1].startsWith('http') && !match[1].includes('replit')) {
98
+ assets.push(match[1]);
99
+ }
100
+ }
101
+
102
+ // Find link tags with stylesheet
103
+ const linkMatches = html.matchAll(/<link[^>]+href="([^"]+)"[^>]*rel="stylesheet"/g);
104
+ for (const match of linkMatches) {
105
+ if (match[1] && !match[1].startsWith('http')) {
106
+ assets.push(match[1]);
107
+ }
108
+ }
109
+
110
+ return assets;
111
+ }
112
+
113
+ // Activate event - claim all clients and clean old caches
114
+ self.addEventListener('activate', event => {
115
+ console.log('VELIN Service Worker activated');
116
+
117
+ event.waitUntil(
118
+ Promise.all([
119
+ self.clients.claim(),
120
+ caches.keys().then(cacheNames => {
121
+ return Promise.all(
122
+ cacheNames.map(cacheName => {
123
+ // Keep current caches, remove old versions
124
+ if (cacheName !== CACHE_NAME &&
125
+ cacheName !== STATIC_CACHE_NAME &&
126
+ cacheName !== THUMBNAIL_CACHE_NAME) {
127
+ console.log('Deleting old cache:', cacheName);
128
+ return caches.delete(cacheName);
129
+ }
130
+ })
131
+ );
132
+ })
133
+ ])
134
+ );
135
+ });
136
+
137
+ // Fetch event - handle caching and serving
138
+ self.addEventListener('fetch', event => {
139
+ const url = new URL(event.request.url);
140
+
141
+ // Handle audio files (.mp3, .m4a, .webm, .ogg)
142
+ if (isAudioFile(url.pathname) || isAudioStream(url.href)) {
143
+ event.respondWith(handleAudioRequest(event.request));
144
+ return;
145
+ }
146
+
147
+ // Handle API requests for audio downloads
148
+ if (url.pathname.startsWith('/api/download-audio/')) {
149
+ event.respondWith(handleAudioDownload(event.request));
150
+ return;
151
+ }
152
+
153
+ // Handle podcast stream requests
154
+ if (url.hostname === 'backendmix.vercel.app' && url.pathname.startsWith('/streams/')) {
155
+ event.respondWith(handleStreamRequest(event.request));
156
+ return;
157
+ }
158
+
159
+ // Handle thumbnail images
160
+ if (isThumbnailImage(url.href)) {
161
+ event.respondWith(handleThumbnailRequest(event.request));
162
+ return;
163
+ }
164
+
165
+ // Handle static files and navigation
166
+ if (event.request.method === 'GET') {
167
+ event.respondWith(handleStaticRequest(event.request));
168
+ return;
169
+ }
170
+
171
+ // Default handling for other requests
172
+ event.respondWith(fetch(event.request));
173
+ });
174
+
175
+ // Check if URL is an audio file
176
+ function isAudioFile(pathname) {
177
+ const audioExtensions = ['.mp3', '.m4a', '.webm', '.ogg', '.wav', '.aac'];
178
+ return audioExtensions.some(ext => pathname.endsWith(ext));
179
+ }
180
+
181
+ // Check if URL is an audio stream
182
+ function isAudioStream(url) {
183
+ // YouTube audio streams and other streaming services
184
+ return (
185
+ url.includes('googlevideo.com') ||
186
+ url.includes('youtube.com') ||
187
+ url.includes('ytimg.com') ||
188
+ (url.includes('mime=audio') || url.includes('type=audio'))
189
+ );
190
+ }
191
+
192
+ // Check if URL is a thumbnail image
193
+ function isThumbnailImage(url) {
194
+ return (
195
+ url.includes('ytimg.com') ||
196
+ url.includes('ggpht.com') ||
197
+ url.includes('youtube.com/vi/') ||
198
+ (url.includes('thumbnail') && (url.includes('.jpg') || url.includes('.webp') || url.includes('.png')))
199
+ );
200
+ }
201
+
202
+ // Handle thumbnail requests
203
+ async function handleThumbnailRequest(request) {
204
+ try {
205
+ const cache = await caches.open(THUMBNAIL_CACHE_NAME);
206
+ const cachedResponse = await cache.match(request);
207
+
208
+ if (cachedResponse) {
209
+ console.log('Serving thumbnail from cache:', request.url);
210
+ return cachedResponse;
211
+ }
212
+
213
+ // Fetch from network and cache
214
+ const networkResponse = await fetch(request);
215
+
216
+ if (networkResponse.ok) {
217
+ const responseClone = networkResponse.clone();
218
+ await cache.put(request, responseClone);
219
+ console.log('Thumbnail cached successfully:', request.url);
220
+ return networkResponse;
221
+ }
222
+
223
+ return networkResponse;
224
+ } catch (error) {
225
+ console.error('Error handling thumbnail request:', error);
226
+
227
+ // Return a fallback thumbnail when offline
228
+ const fallbackSvg = `
229
+ <svg viewBox="0 0 320 180" xmlns="http://www.w3.org/2000/svg">
230
+ <rect width="320" height="180" fill="#4B5563"/>
231
+ <g transform="translate(160, 90)">
232
+ <circle r="24" fill="#6366F1" opacity="0.2"/>
233
+ <path d="M-8 -6 L8 0 L-8 6 Z" fill="#6366F1"/>
234
+ </g>
235
+ <text x="160" y="135" text-anchor="middle" fill="#D1D5DB" font-family="sans-serif" font-size="12" font-weight="500">
236
+ VELIN
237
+ </text>
238
+ </svg>
239
+ `;
240
+
241
+ return new Response(fallbackSvg, {
242
+ headers: {
243
+ 'Content-Type': 'image/svg+xml',
244
+ 'Cache-Control': 'no-cache'
245
+ }
246
+ });
247
+ }
248
+ }
249
+
250
+ // Handle static file requests (for offline app functionality)
251
+ async function handleStaticRequest(request) {
252
+ const url = new URL(request.url);
253
+
254
+ try {
255
+ // For navigation requests (page loads), handle specially for SPA
256
+ if (request.mode === 'navigate' ||
257
+ (request.destination === 'document' && request.method === 'GET')) {
258
+
259
+ // Try network first for navigation
260
+ try {
261
+ const networkResponse = await fetch(request);
262
+
263
+ if (networkResponse.ok) {
264
+ // Cache successful page loads
265
+ const cache = await caches.open(STATIC_CACHE_NAME);
266
+ const responseClone = networkResponse.clone();
267
+ await cache.put(request, responseClone);
268
+ console.log('Cached page from network:', request.url);
269
+ return networkResponse;
270
+ }
271
+ } catch (networkError) {
272
+ console.log('Network failed for navigation, using cache fallback');
273
+ }
274
+
275
+ // Network failed or offline - serve from cache
276
+ const cache = await caches.open(STATIC_CACHE_NAME);
277
+
278
+ // For SPA routing, always serve the main index.html for any route
279
+ const cachedResponse = await cache.match('/');
280
+
281
+ if (cachedResponse) {
282
+ console.log('Serving SPA fallback from cache for:', request.url);
283
+ return cachedResponse;
284
+ }
285
+
286
+ // No cached page available
287
+ console.error('No cached page available for offline access');
288
+ return new Response(`
289
+ <!DOCTYPE html>
290
+ <html>
291
+ <head>
292
+ <title>VELIN - Offline</title>
293
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
294
+ <style>
295
+ body {
296
+ font-family: system-ui, sans-serif;
297
+ text-align: center;
298
+ padding: 2rem;
299
+ background: #121212;
300
+ color: #ffffff;
301
+ margin: 0;
302
+ }
303
+ .container { max-width: 400px; margin: 0 auto; padding-top: 20vh; }
304
+ .icon { font-size: 4rem; margin-bottom: 1rem; }
305
+ h1 { color: #6366f1; margin-bottom: 1rem; }
306
+ p { color: #9ca3af; line-height: 1.5; }
307
+ </style>
308
+ </head>
309
+ <body>
310
+ <div class="container">
311
+ <div class="icon">📱</div>
312
+ <h1>VELIN</h1>
313
+ <p>You're offline and the app isn't cached yet.<br>Please connect to the internet and reload the page.</p>
314
+ </div>
315
+ </body>
316
+ </html>
317
+ `, {
318
+ headers: { 'Content-Type': 'text/html' },
319
+ status: 200
320
+ });
321
+ }
322
+
323
+ // For static resources (JS, CSS, images, etc.)
324
+ try {
325
+ const networkResponse = await fetch(request);
326
+
327
+ // Cache successful responses for static resources
328
+ if (networkResponse.ok && (
329
+ request.destination === 'script' ||
330
+ request.destination === 'style' ||
331
+ request.destination === 'image' ||
332
+ url.pathname.includes('.js') ||
333
+ url.pathname.includes('.css') ||
334
+ url.pathname.includes('.png') ||
335
+ url.pathname.includes('.svg') ||
336
+ url.pathname.includes('/assets/')
337
+ )) {
338
+ const cache = await caches.open(STATIC_CACHE_NAME);
339
+ const responseClone = networkResponse.clone();
340
+ await cache.put(request, responseClone);
341
+ console.log('Cached static resource:', request.url);
342
+ }
343
+
344
+ return networkResponse;
345
+ } catch (networkError) {
346
+ console.log('Network failed for static resource, trying cache:', request.url);
347
+
348
+ // Network failed, try cache as fallback
349
+ const cache = await caches.open(STATIC_CACHE_NAME);
350
+ const cachedResponse = await cache.match(request);
351
+
352
+ if (cachedResponse) {
353
+ console.log('Serving static resource from cache:', request.url);
354
+ return cachedResponse;
355
+ }
356
+
357
+ // No cached version available
358
+ throw networkError;
359
+ }
360
+ } catch (error) {
361
+ console.error('Error handling static request:', error);
362
+ return new Response('Resource not available offline', {
363
+ status: 503,
364
+ statusText: 'Service Unavailable'
365
+ });
366
+ }
367
+ }
368
+
369
+ // Handle audio file requests
370
+ async function handleAudioRequest(request) {
371
+ try {
372
+ const cache = await caches.open(CACHE_NAME);
373
+ const cachedResponse = await cache.match(request);
374
+
375
+ if (cachedResponse) {
376
+ console.log('Serving audio from cache:', request.url);
377
+ return cachedResponse;
378
+ }
379
+
380
+ // Not in cache, fetch from network
381
+ console.log('Fetching audio from network:', request.url);
382
+ const networkResponse = await fetch(request);
383
+
384
+ if (networkResponse.ok) {
385
+ // Cache the response for future use
386
+ const responseClone = networkResponse.clone();
387
+ await cache.put(request, responseClone);
388
+ console.log('Audio cached successfully:', request.url);
389
+ }
390
+
391
+ return networkResponse;
392
+ } catch (error) {
393
+ console.error('Error handling audio request:', error);
394
+ // Return a fallback or error response
395
+ return new Response('Audio not available offline', {
396
+ status: 503,
397
+ statusText: 'Service Unavailable'
398
+ });
399
+ }
400
+ }
401
+
402
+ // Handle audio download API requests
403
+ async function handleAudioDownload(request) {
404
+ try {
405
+ const cache = await caches.open(CACHE_NAME);
406
+ const cachedResponse = await cache.match(request);
407
+
408
+ if (cachedResponse) {
409
+ console.log('Serving download from cache:', request.url);
410
+ return cachedResponse;
411
+ }
412
+
413
+ // Fetch from network and cache
414
+ const networkResponse = await fetch(request);
415
+
416
+ if (networkResponse.ok) {
417
+ const responseClone = networkResponse.clone();
418
+ await cache.put(request, responseClone);
419
+ console.log('Download cached successfully:', request.url);
420
+ }
421
+
422
+ return networkResponse;
423
+ } catch (error) {
424
+ console.error('Error handling download request:', error);
425
+ return new Response('Download not available offline', {
426
+ status: 503,
427
+ statusText: 'Service Unavailable'
428
+ });
429
+ }
430
+ }
431
+
432
+ // Handle stream info requests
433
+ async function handleStreamRequest(request) {
434
+ try {
435
+ const cache = await caches.open(STATIC_CACHE_NAME);
436
+ const cachedResponse = await cache.match(request);
437
+
438
+ // For stream info, we can cache for a short time
439
+ if (cachedResponse) {
440
+ const cacheDate = new Date(cachedResponse.headers.get('sw-cache-date') || 0);
441
+ const now = new Date();
442
+ const maxAge = 30 * 60 * 1000; // 30 minutes
443
+
444
+ if (now - cacheDate < maxAge) {
445
+ console.log('Serving stream info from cache:', request.url);
446
+ return cachedResponse;
447
+ }
448
+ }
449
+
450
+ // Fetch fresh data
451
+ const networkResponse = await fetch(request);
452
+
453
+ if (networkResponse.ok) {
454
+ const responseClone = networkResponse.clone();
455
+ const headers = new Headers(responseClone.headers);
456
+ headers.set('sw-cache-date', new Date().toISOString());
457
+
458
+ const cachedResponse = new Response(responseClone.body, {
459
+ status: responseClone.status,
460
+ statusText: responseClone.statusText,
461
+ headers: headers
462
+ });
463
+
464
+ await cache.put(request, cachedResponse);
465
+ }
466
+
467
+ return networkResponse;
468
+ } catch (error) {
469
+ console.error('Error handling stream request:', error);
470
+ return fetch(request);
471
+ }
472
+ }
473
+
474
+ // Message handling for manual cache operations
475
+ self.addEventListener('message', event => {
476
+ if (event.data && event.data.type === 'CACHE_AUDIO') {
477
+ event.waitUntil(cacheAudioFile(event.data.url));
478
+ }
479
+
480
+ if (event.data && event.data.type === 'CLEAR_CACHE') {
481
+ event.waitUntil(clearCache());
482
+ }
483
+
484
+ if (event.data && event.data.type === 'GET_CACHE_SIZE') {
485
+ event.waitUntil(getCacheSize().then(size => {
486
+ event.ports[0].postMessage({ size });
487
+ }));
488
+ }
489
+ });
490
+
491
+ // Manually cache an audio file
492
+ async function cacheAudioFile(url) {
493
+ try {
494
+ const cache = await caches.open(CACHE_NAME);
495
+ const response = await fetch(url);
496
+
497
+ if (response.ok) {
498
+ await cache.put(url, response);
499
+ console.log('Audio file cached manually:', url);
500
+ return true;
501
+ }
502
+ return false;
503
+ } catch (error) {
504
+ console.error('Error caching audio file:', error);
505
+ return false;
506
+ }
507
+ }
508
+
509
+ // Clear all caches
510
+ async function clearCache() {
511
+ try {
512
+ const cacheNames = await caches.keys();
513
+ await Promise.all(
514
+ cacheNames.map(cacheName => caches.delete(cacheName))
515
+ );
516
+ console.log('All caches cleared');
517
+ } catch (error) {
518
+ console.error('Error clearing cache:', error);
519
+ }
520
+ }
521
+
522
+ // Get cache size
523
+ async function getCacheSize() {
524
+ try {
525
+ const cache = await caches.open(CACHE_NAME);
526
+ const requests = await cache.keys();
527
+ let totalSize = 0;
528
+
529
+ for (const request of requests) {
530
+ const response = await cache.match(request);
531
+ if (response) {
532
+ const blob = await response.blob();
533
+ totalSize += blob.size;
534
+ }
535
+ }
536
+
537
+ return totalSize;
538
+ } catch (error) {
539
+ console.error('Error calculating cache size:', error);
540
+ return 0;
541
+ }
542
+ }
drizzle.config.ts ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from "drizzle-kit";
2
+
3
+ if (!process.env.DATABASE_URL) {
4
+ throw new Error("DATABASE_URL, ensure the database is provisioned");
5
+ }
6
+
7
+ export default defineConfig({
8
+ out: "./migrations",
9
+ schema: "./shared/schema.ts",
10
+ dialect: "postgresql",
11
+ dbCredentials: {
12
+ url: process.env.DATABASE_URL,
13
+ },
14
+ });
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "rest-express",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "scripts": {
7
+ "dev": "NODE_ENV=development tsx server/index.ts",
8
+ "build": "vite build && esbuild server/index.ts --platform=node --packages=external --bundle --format=esm --outdir=dist",
9
+ "start": "NODE_ENV=production node dist/index.js",
10
+ "check": "tsc",
11
+ "db:push": "drizzle-kit push"
12
+ },
13
+ "dependencies": {
14
+ "@hookform/resolvers": "^3.10.0",
15
+ "@jridgewell/trace-mapping": "^0.3.25",
16
+ "@neondatabase/serverless": "^0.10.4",
17
+ "@radix-ui/react-accordion": "^1.2.4",
18
+ "@radix-ui/react-alert-dialog": "^1.1.7",
19
+ "@radix-ui/react-aspect-ratio": "^1.1.3",
20
+ "@radix-ui/react-avatar": "^1.1.4",
21
+ "@radix-ui/react-checkbox": "^1.1.5",
22
+ "@radix-ui/react-collapsible": "^1.1.4",
23
+ "@radix-ui/react-context-menu": "^2.2.7",
24
+ "@radix-ui/react-dialog": "^1.1.7",
25
+ "@radix-ui/react-dropdown-menu": "^2.1.7",
26
+ "@radix-ui/react-hover-card": "^1.1.7",
27
+ "@radix-ui/react-label": "^2.1.3",
28
+ "@radix-ui/react-menubar": "^1.1.7",
29
+ "@radix-ui/react-navigation-menu": "^1.2.6",
30
+ "@radix-ui/react-popover": "^1.1.7",
31
+ "@radix-ui/react-progress": "^1.1.3",
32
+ "@radix-ui/react-radio-group": "^1.2.4",
33
+ "@radix-ui/react-scroll-area": "^1.2.4",
34
+ "@radix-ui/react-select": "^2.1.7",
35
+ "@radix-ui/react-separator": "^1.1.3",
36
+ "@radix-ui/react-slider": "^1.2.4",
37
+ "@radix-ui/react-slot": "^1.2.0",
38
+ "@radix-ui/react-switch": "^1.1.4",
39
+ "@radix-ui/react-tabs": "^1.1.4",
40
+ "@radix-ui/react-toast": "^1.2.7",
41
+ "@radix-ui/react-toggle": "^1.1.3",
42
+ "@radix-ui/react-toggle-group": "^1.1.3",
43
+ "@radix-ui/react-tooltip": "^1.2.0",
44
+ "@tanstack/react-query": "^5.60.5",
45
+ "class-variance-authority": "^0.7.1",
46
+ "clsx": "^2.1.1",
47
+ "cmdk": "^1.1.1",
48
+ "connect-pg-simple": "^10.0.0",
49
+ "date-fns": "^3.6.0",
50
+ "drizzle-orm": "^0.39.1",
51
+ "drizzle-zod": "^0.7.0",
52
+ "embla-carousel-react": "^8.6.0",
53
+ "express": "^4.21.2",
54
+ "express-session": "^1.18.1",
55
+ "framer-motion": "^11.13.1",
56
+ "input-otp": "^1.4.2",
57
+ "lucide-react": "^0.453.0",
58
+ "memorystore": "^1.6.7",
59
+ "next-themes": "^0.4.6",
60
+ "passport": "^0.7.0",
61
+ "passport-local": "^1.0.0",
62
+ "react": "^18.3.1",
63
+ "react-day-picker": "^8.10.1",
64
+ "react-dom": "^18.3.1",
65
+ "react-hook-form": "^7.55.0",
66
+ "react-icons": "^5.4.0",
67
+ "react-resizable-panels": "^2.1.7",
68
+ "recharts": "^2.15.2",
69
+ "tailwind-merge": "^2.6.0",
70
+ "tailwindcss-animate": "^1.0.7",
71
+ "tw-animate-css": "^1.2.5",
72
+ "vaul": "^1.1.2",
73
+ "wouter": "^3.3.5",
74
+ "ws": "^8.18.0",
75
+ "zod": "^3.24.2",
76
+ "zod-validation-error": "^3.4.0"
77
+ },
78
+ "devDependencies": {
79
+ "@replit/vite-plugin-cartographer": "^0.2.7",
80
+ "@replit/vite-plugin-runtime-error-modal": "^0.0.3",
81
+ "@tailwindcss/typography": "^0.5.15",
82
+ "@tailwindcss/vite": "^4.1.3",
83
+ "@types/connect-pg-simple": "^7.0.3",
84
+ "@types/express": "4.17.21",
85
+ "@types/express-session": "^1.18.0",
86
+ "@types/node": "20.16.11",
87
+ "@types/passport": "^1.0.16",
88
+ "@types/passport-local": "^1.0.38",
89
+ "@types/react": "^18.3.11",
90
+ "@types/react-dom": "^18.3.1",
91
+ "@types/ws": "^8.5.13",
92
+ "@vitejs/plugin-react": "^4.3.2",
93
+ "autoprefixer": "^10.4.20",
94
+ "drizzle-kit": "^0.30.4",
95
+ "esbuild": "^0.25.0",
96
+ "postcss": "^8.4.47",
97
+ "tailwindcss": "^3.4.17",
98
+ "tsx": "^4.19.1",
99
+ "typescript": "5.6.3",
100
+ "vite": "^5.4.19"
101
+ },
102
+ "optionalDependencies": {
103
+ "bufferutil": "^4.0.8"
104
+ }
105
+ }
postcss.config.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
tailwind.config.ts ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Config } from "tailwindcss";
2
+
3
+ export default {
4
+ darkMode: ["class"],
5
+ content: ["./client/index.html", "./client/src/**/*.{js,jsx,ts,tsx}"],
6
+ theme: {
7
+ extend: {
8
+ borderRadius: {
9
+ lg: "var(--radius)",
10
+ md: "calc(var(--radius) - 2px)",
11
+ sm: "calc(var(--radius) - 4px)",
12
+ },
13
+ colors: {
14
+ background: "var(--background)",
15
+ foreground: "var(--foreground)",
16
+ card: {
17
+ DEFAULT: "var(--card)",
18
+ foreground: "var(--card-foreground)",
19
+ },
20
+ popover: {
21
+ DEFAULT: "var(--popover)",
22
+ foreground: "var(--popover-foreground)",
23
+ },
24
+ primary: {
25
+ DEFAULT: "var(--primary)",
26
+ foreground: "var(--primary-foreground)",
27
+ },
28
+ secondary: {
29
+ DEFAULT: "var(--secondary)",
30
+ foreground: "var(--secondary-foreground)",
31
+ },
32
+ muted: {
33
+ DEFAULT: "var(--muted)",
34
+ foreground: "var(--muted-foreground)",
35
+ },
36
+ accent: {
37
+ DEFAULT: "var(--accent)",
38
+ foreground: "var(--accent-foreground)",
39
+ },
40
+ destructive: {
41
+ DEFAULT: "var(--destructive)",
42
+ foreground: "var(--destructive-foreground)",
43
+ },
44
+ border: "var(--border)",
45
+ input: "var(--input)",
46
+ ring: "var(--ring)",
47
+ chart: {
48
+ "1": "var(--chart-1)",
49
+ "2": "var(--chart-2)",
50
+ "3": "var(--chart-3)",
51
+ "4": "var(--chart-4)",
52
+ "5": "var(--chart-5)",
53
+ },
54
+ sidebar: {
55
+ DEFAULT: "var(--sidebar-background)",
56
+ foreground: "var(--sidebar-foreground)",
57
+ primary: "var(--sidebar-primary)",
58
+ "primary-foreground": "var(--sidebar-primary-foreground)",
59
+ accent: "var(--sidebar-accent)",
60
+ "accent-foreground": "var(--sidebar-accent-foreground)",
61
+ border: "var(--sidebar-border)",
62
+ ring: "var(--sidebar-ring)",
63
+ },
64
+ },
65
+ keyframes: {
66
+ "accordion-down": {
67
+ from: {
68
+ height: "0",
69
+ },
70
+ to: {
71
+ height: "var(--radix-accordion-content-height)",
72
+ },
73
+ },
74
+ "accordion-up": {
75
+ from: {
76
+ height: "var(--radix-accordion-content-height)",
77
+ },
78
+ to: {
79
+ height: "0",
80
+ },
81
+ },
82
+ },
83
+ animation: {
84
+ "accordion-down": "accordion-down 0.2s ease-out",
85
+ "accordion-up": "accordion-up 0.2s ease-out",
86
+ },
87
+ },
88
+ },
89
+ plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
90
+ } satisfies Config;
tsconfig.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "include": ["client/src/**/*", "shared/**/*", "server/**/*"],
3
+ "exclude": ["node_modules", "build", "dist", "**/*.test.ts"],
4
+ "compilerOptions": {
5
+ "incremental": true,
6
+ "tsBuildInfoFile": "./node_modules/typescript/tsbuildinfo",
7
+ "noEmit": true,
8
+ "module": "ESNext",
9
+ "strict": true,
10
+ "lib": ["esnext", "dom", "dom.iterable"],
11
+ "jsx": "preserve",
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "allowImportingTsExtensions": true,
15
+ "moduleResolution": "bundler",
16
+ "baseUrl": ".",
17
+ "types": ["node", "vite/client"],
18
+ "paths": {
19
+ "@/*": ["./client/src/*"],
20
+ "@shared/*": ["./shared/*"]
21
+ }
22
+ }
23
+ }
vite.config.ts ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import path from "path";
4
+ import runtimeErrorOverlay from "@replit/vite-plugin-runtime-error-modal";
5
+
6
+ export default defineConfig({
7
+ plugins: [
8
+ react(),
9
+ runtimeErrorOverlay(),
10
+ ...(process.env.NODE_ENV !== "production" &&
11
+ process.env.REPL_ID !== undefined
12
+ ? [
13
+ await import("@replit/vite-plugin-cartographer").then((m) =>
14
+ m.cartographer(),
15
+ ),
16
+ ]
17
+ : []),
18
+ ],
19
+ resolve: {
20
+ alias: {
21
+ "@": path.resolve(import.meta.dirname, "client", "src"),
22
+ "@shared": path.resolve(import.meta.dirname, "shared"),
23
+ "@assets": path.resolve(import.meta.dirname, "attached_assets"),
24
+ },
25
+ },
26
+ root: path.resolve(import.meta.dirname, "client"),
27
+ build: {
28
+ outDir: path.resolve(import.meta.dirname, "dist/public"),
29
+ emptyOutDir: true,
30
+ },
31
+ server: {
32
+ fs: {
33
+ strict: true,
34
+ deny: ["**/.*"],
35
+ },
36
+ },
37
+ });