@fastbackr/react 0.0.0-stage → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +46 -0
- package/README.md +216 -2
- package/dist/app-Cw3fHqqr.js +1615 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +132 -0
- package/package.json +66 -3
|
@@ -0,0 +1,1615 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { rememberVisible } from "@fastbackr/core/boot";
|
|
3
|
+
import { createContext, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { ApiClient, Layer, Selection, TokenSource, anchorBox, captureAnchor, captureScreenshot, consumeThreadLink, isCovered, isReflowed, pageContext, pathBreak, placeBox, plainKey, position, preloadScreenshot, regionBox, resolveAnchor, skeleton, threadLink, watchNavigation } from "@fastbackr/core";
|
|
6
|
+
import { createPortal } from "react-dom";
|
|
7
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
8
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
9
|
+
import { cva } from "class-variance-authority";
|
|
10
|
+
import { cn } from "cn";
|
|
11
|
+
import { LIMITS, THREAD_STATUSES } from "@fastbackr/core/contract";
|
|
12
|
+
import { Button } from "@base-ui/react/button";
|
|
13
|
+
import { Input } from "@base-ui/react/input";
|
|
14
|
+
import { RiAddLine, RiArrowDownSLine, RiChat3Line, RiCheckLine, RiCloseLine, RiPencilLine } from "@remixicon/react";
|
|
15
|
+
import { Checkbox } from "@base-ui/react/checkbox";
|
|
16
|
+
import { Separator } from "@base-ui/react/separator";
|
|
17
|
+
//#region \0virtual:fastbackr-styles
|
|
18
|
+
var _virtual_fastbackr_styles_default = "/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@layer properties{@supports (display:block){:host,:host{--shimmer-angle:20deg}*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-x-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:\"\";--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0;--scroll-fade-t:0px;--scroll-fade-b:0px;--scroll-fade-s:0px;--scroll-fade-e:0px;--scroll-fade-mask:initial;--shimmer-image:initial;--shimmer-text-fill:initial}}}@layer theme{:host,:host{--font-sans:var(--font-sans);--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-widest:.1em;--leading-snug:1.375;--leading-normal:1.5;--radius-xl:calc(var(--radius) * 1.4);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--blur-xs:4px;--perspective-normal:500px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){*{outline-color:color-mix(in oklab, var(--ring) 50%, transparent)}}body{background-color:var(--background);color:var(--foreground)}button:not(:disabled),[role=button]:not(:disabled){cursor:pointer}}@layer components;@layer utilities{.\\@container\\/card-header{container:card-header/inline-size}.\\@container\\/field-group{container:field-group/inline-size}.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{inset-inline:0}.inset-y-0{inset-block:0}.top-1\\.5{top:calc(var(--spacing) * 1.5)}.top-1\\/2{top:50%}.top-2\\.5{top:calc(var(--spacing) * 2.5)}.top-3{top:calc(var(--spacing) * 3)}.top-3\\.5{top:calc(var(--spacing) * 3.5)}.top-4{top:calc(var(--spacing) * 4)}.right-0{right:0}.right-1{right:var(--spacing)}.right-2{right:calc(var(--spacing) * 2)}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.right-full{right:100%}.bottom-0{bottom:0}.bottom-4{bottom:calc(var(--spacing) * 4)}.bottom-22{bottom:calc(var(--spacing) * 22)}.bottom-\\[calc\\(100\\%\\+6px\\)\\]{bottom:calc(100% + 6px)}.left-1\\/2{left:50%}.isolate{isolation:isolate}.-z-1{z-index:calc(1 * -1)}.-z-10{z-index:calc(10 * -1)}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.col-start-2{grid-column-start:2}.row-span-2{grid-row:span 2/span 2}.row-start-1{grid-row-start:1}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.mx-2{margin-inline:calc(var(--spacing) * 2)}.mx-3\\.5{margin-inline:calc(var(--spacing) * 3.5)}.mx-auto{margin-inline:auto}.-my-2{margin-block:calc(var(--spacing) * -2)}.my-1{margin-block:var(--spacing)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-auto{margin-top:auto}.-mr-1{margin-right:calc(var(--spacing) * -1)}.-mr-1\\.5{margin-right:calc(var(--spacing) * -1.5)}.mr-1\\.5{margin-right:calc(var(--spacing) * 1.5)}.mb-1\\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.no-scrollbar::-webkit-scrollbar{display:none}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-row{display:table-row}.field-sizing-content{field-sizing:content}.aspect-square{aspect-ratio:1}.size-2\\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.size-3\\!{width:calc(var(--spacing) * 3)!important;height:calc(var(--spacing) * 3)!important}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-6{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.size-10{width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.size-16{width:calc(var(--spacing) * 16);height:calc(var(--spacing) * 16)}.size-full{width:100%;height:100%}.h-\\(--accordion-panel-height\\){height:var(--accordion-panel-height)}.h-2\\.5{height:calc(var(--spacing) * 2.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-\\[calc\\(100\\%-1px\\)\\]{height:calc(100% - 1px)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-svh{height:100svh}.max-h-\\(--available-height\\){max-height:var(--available-height)}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\\[calc\\(100vh-110px\\)\\]{max-height:calc(100vh - 110px)}.min-h-0{min-height:0}.min-h-16{min-height:calc(var(--spacing) * 16)}.min-h-24{min-height:calc(var(--spacing) * 24)}.min-h-28{min-height:calc(var(--spacing) * 28)}.min-h-svh{min-height:100svh}.w-\\(--anchor-width\\){width:var(--anchor-width)}.w-\\(--sidebar-width\\){width:var(--sidebar-width)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-36{width:calc(var(--spacing) * 36)}.w-48{width:calc(var(--spacing) * 48)}.w-\\[350px\\]{width:350px}.w-\\[420px\\]{width:420px}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.max-w-\\(--skeleton-width\\){max-width:var(--skeleton-width)}.max-w-40{max-width:calc(var(--spacing) * 40)}.max-w-\\[440px\\]{max-width:440px}.max-w-\\[calc\\(100\\%-2rem\\)\\]{max-width:calc(100% - 2rem)}.max-w-\\[calc\\(100vw-2rem\\)\\]{max-width:calc(100vw - 2rem)}.max-w-\\[calc\\(100vw-24px\\)\\]{max-width:calc(100vw - 24px)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:0}.min-w-5{min-width:calc(var(--spacing) * 5)}.min-w-7{min-width:calc(var(--spacing) * 7)}.min-w-8{min-width:calc(var(--spacing) * 8)}.min-w-9{min-width:calc(var(--spacing) * 9)}.min-w-10{min-width:calc(var(--spacing) * 10)}.min-w-36{min-width:calc(var(--spacing) * 36)}.min-w-48{min-width:calc(var(--spacing) * 48)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.caption-bottom{caption-side:bottom}.origin-\\(--transform-origin\\){transform-origin:var(--transform-origin)}.-translate-x-1\\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-px{--tw-translate-x:-1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-px{--tw-translate-x:1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-\\[calc\\(-50\\%-2px\\)\\]{--tw-translate-y:calc(-50% - 2px);translate:var(--tw-translate-x) var(--tw-translate-y)}.rotate-45{rotate:45deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-fade-in{animation:.6s ease-in both fade-in}.animate-fade-in-left{animation:.6s ease-in-out both fade-in-left}.animate-fade-out{animation:.6s ease-out both fade-out}.animate-fade-out-right{animation:.6s ease-out both fade-out-right}.animate-flip-in-x{animation:.6s ease-out both flip-in-x}.animate-pulse{animation:2s cubic-bezier(.4,0,.6,1) infinite pulse}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.list-disc{list-style-type:disc}.auto-rows-min{grid-auto-rows:min-content}.grid-rows-\\[auto_1fr\\]{grid-template-rows:auto 1fr}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.place-content-center{place-content:center}.place-items-center{place-items:center}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-\\(--card-spacing\\){gap:var(--card-spacing)}.gap-0{gap:0}.gap-0\\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-7{gap:calc(var(--spacing) * 7)}.gap-\\[--spacing\\(var\\(--gap\\)\\)\\]{gap:calc(var(--spacing) * var(--gap))}:where(.-space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * -2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * -2) * calc(1 - var(--tw-space-x-reverse)))}.self-start{align-self:flex-start}.justify-self-end{justify-self:flex-end}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded-2xl{border-radius:calc(var(--radius) * 1.8)}.rounded-4xl{border-radius:calc(var(--radius) * 2.6)}.rounded-\\[2px\\]{border-radius:2px}.rounded-\\[6px\\]{border-radius:6px}.rounded-\\[14px_14px_14px_3px\\]{border-radius:14px 14px 14px 3px}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) * .8)}.rounded-xl{border-radius:calc(var(--radius) * 1.4)}.rounded-t-xl{border-top-left-radius:calc(var(--radius) * 1.4);border-top-right-radius:calc(var(--radius) * 1.4)}.rounded-b-xl{border-bottom-right-radius:calc(var(--radius) * 1.4);border-bottom-left-radius:calc(var(--radius) * 1.4)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-background{border-color:var(--background)}.border-border{border-color:var(--border)}.border-input{border-color:var(--input)}.border-sidebar-border{border-color:var(--sidebar-border)}.border-transparent{border-color:#0000}.bg-background{background-color:var(--background)}.bg-black\\/80{background-color:#000c}@supports (color:color-mix(in lab, red, red)){.bg-black\\/80{background-color:color-mix(in oklab, var(--color-black) 80%, transparent)}}.bg-border,.bg-border\\/50{background-color:var(--border)}@supports (color:color-mix(in lab, red, red)){.bg-border\\/50{background-color:color-mix(in oklab, var(--border) 50%, transparent)}}.bg-card{background-color:var(--card)}.bg-destructive,.bg-destructive\\/10{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.bg-destructive\\/10{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.bg-foreground{background-color:var(--foreground)}.bg-input\\/30{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.bg-input\\/30{background-color:color-mix(in oklab, var(--input) 30%, transparent)}}.bg-muted,.bg-muted\\/50{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.bg-muted\\/50{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.bg-popover{background-color:var(--popover)}.bg-primary,.bg-primary\\/15{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.bg-primary\\/15{background-color:color-mix(in oklab, var(--primary) 15%, transparent)}}.bg-secondary{background-color:var(--secondary)}.bg-sidebar{background-color:var(--sidebar)}.bg-sidebar-border{background-color:var(--sidebar-border)}.bg-transparent{background-color:#0000}.bg-clip-padding{background-clip:padding-box}.fill-foreground{fill:var(--foreground)}.object-cover{object-fit:cover}.object-top{object-position:top}.p-0{padding:0}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-\\[3px\\]{padding:3px}.px-\\(--card-spacing\\){padding-inline:var(--card-spacing)}.px-1{padding-inline:var(--spacing)}.px-1\\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.py-\\(--card-spacing\\){padding-block:var(--card-spacing)}.py-0{padding-block:0}.py-0\\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-8{padding-block:calc(var(--spacing) * 8)}.pt-0{padding-top:0}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-1\\.5{padding-left:calc(var(--spacing) * 1.5)}.pl-3{padding-left:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-left{text-align:left}.align-middle{vertical-align:middle}.font-heading,.font-sans{font-family:var(--font-sans)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.leading-none{--tw-leading:1;line-height:1}.leading-normal{--tw-leading:var(--leading-normal);line-height:var(--leading-normal)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-balance{text-wrap:balance}.break-words{overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-background{color:var(--background)}.text-card-foreground{color:var(--card-foreground)}.text-current{color:currentColor}.text-destructive{color:var(--destructive)}.text-foreground,.text-foreground\\/60{color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.text-foreground\\/60{color:color-mix(in oklab, var(--foreground) 60%, transparent)}}.text-link{color:var(--link)}.text-muted-foreground{color:var(--muted-foreground)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground,.text-sidebar-foreground\\/70{color:var(--sidebar-foreground)}@supports (color:color-mix(in lab, red, red)){.text-sidebar-foreground\\/70{color:color-mix(in oklab, var(--sidebar-foreground) 70%, transparent)}}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.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,)}.underline-offset-4{text-underline-offset:4px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.bg-blend-color{background-blend-mode:color}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\\[0_0_0_1px_var\\(--sidebar-border\\)\\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,var(--sidebar-border));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring,.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-background{--tw-ring-color:var(--background)}.ring-foreground\\/5{--tw-ring-color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.ring-foreground\\/5{--tw-ring-color:color-mix(in oklab, var(--foreground) 5%, transparent)}}.ring-foreground\\/10{--tw-ring-color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.ring-foreground\\/10{--tw-ring-color:color-mix(in oklab, var(--foreground) 10%, transparent)}}.ring-sidebar-ring{--tw-ring-color:var(--sidebar-ring)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.outline-2{outline-style:var(--tw-outline-style);outline-width:2px}.outline-offset-2{outline-offset:2px}.outline-primary{outline-color:var(--primary)}.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,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\\[left\\,right\\,width\\]{transition-property:left,right,width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\\[margin\\,opacity\\]{transition-property:margin,opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\\[width\\,height\\,padding\\]{transition-property:width,height,padding;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\\[width\\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-none{transition-property:none}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-linear{--tw-ease:linear;transition-timing-function:linear}.animate-delay-250{animation-delay:.25s}.animate-duration-150{animation-duration:.15s}.animate-duration-250{animation-duration:.25s}.animate-duration-300{animation-duration:.3s}.outline-dashed{--tw-outline-style:dashed;outline-style:dashed}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.\\[--card-spacing\\:--spacing\\(6\\)\\]{--card-spacing:calc(var(--spacing) * 6)}.\\[--radius\\:var\\(--radius-xl\\)\\]{--radius:var(--radius-xl)}.perspective-normal{perspective:var(--perspective-normal)}:is(.\\*\\:pointer-events-auto>*){pointer-events:auto}:is(.\\*\\:w-full>*){width:100%}.not-last\\:border-b:not(:last-child){border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.group-focus-within\\/menu-item\\:opacity-100:is(:where(.group\\/menu-item):focus-within *){opacity:1}@media (hover:hover){.group-hover\\/menu-item\\:opacity-100:is(:where(.group\\/menu-item):hover *){opacity:1}}.group-focus\\/dropdown-menu-item\\:text-accent-foreground:is(:where(.group\\/dropdown-menu-item):focus *){color:var(--accent-foreground)}.group-has-disabled\\/field\\:opacity-50:is(:where(.group\\/field):has(:disabled) *){opacity:.5}.group-has-data-\\[sidebar\\=menu-action\\]\\/menu-item\\:pr-8:is(:where(.group\\/menu-item):has([data-sidebar=menu-action]) *){padding-right:calc(var(--spacing) * 8)}.group-has-data-\\[size\\=lg\\]\\/avatar-group\\:size-10:is(:where(.group\\/avatar-group):has([data-size=lg]) *){width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.group-has-data-\\[size\\=sm\\]\\/avatar-group\\:size-6:is(:where(.group\\/avatar-group):has([data-size=sm]) *){width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.group-has-data-horizontal\\/field\\:text-balance:is(:where(.group\\/field):has(:where([data-orientation=horizontal])) *){text-wrap:balance}.group-has-\\[\\:focus-visible\\]\\/field-label\\:ring-0:is(:where(.group\\/field-label):has(:focus-visible) *){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.group-has-\\[\\:focus-visible\\]\\/field-label\\:not-data-checked\\:border-input:is(:where(.group\\/field-label):has(:focus-visible) *):not(:where([data-state=checked]),:where([data-checked]:not([data-checked=false]))){border-color:var(--input)}.group-has-\\[\\>svg\\]\\/alert\\:col-start-2:is(:where(.group\\/alert):has(>svg) *){grid-column-start:2}.group-aria-expanded\\/accordion-trigger\\:hidden:is(:where(.group\\/accordion-trigger)[aria-expanded=true] *){display:none}.group-aria-expanded\\/accordion-trigger\\:inline:is(:where(.group\\/accordion-trigger)[aria-expanded=true] *){display:inline}.group-data-\\[collapsible\\=icon\\]\\:-mt-8:is(:where(.group)[data-collapsible=icon] *){margin-top:calc(var(--spacing) * -8)}.group-data-\\[collapsible\\=icon\\]\\:hidden:is(:where(.group)[data-collapsible=icon] *){display:none}.group-data-\\[collapsible\\=icon\\]\\:size-8\\!:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--spacing) * 8)!important;height:calc(var(--spacing) * 8)!important}.group-data-\\[collapsible\\=icon\\]\\:w-\\(--sidebar-width-icon\\):is(:where(.group)[data-collapsible=icon] *){width:var(--sidebar-width-icon)}.group-data-\\[collapsible\\=icon\\]\\:w-\\[calc\\(var\\(--sidebar-width-icon\\)\\+\\(--spacing\\(4\\)\\)\\)\\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing) * 4)))}.group-data-\\[collapsible\\=icon\\]\\:w-\\[calc\\(var\\(--sidebar-width-icon\\)\\+\\(--spacing\\(4\\)\\)\\+2px\\)\\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing) * 4)) + 2px)}.group-data-\\[collapsible\\=icon\\]\\:overflow-hidden:is(:where(.group)[data-collapsible=icon] *){overflow:hidden}.group-data-\\[collapsible\\=icon\\]\\:p-0\\!:is(:where(.group)[data-collapsible=icon] *){padding:0!important}.group-data-\\[collapsible\\=icon\\]\\:p-2\\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing) * 2)!important}.group-data-\\[collapsible\\=icon\\]\\:opacity-0:is(:where(.group)[data-collapsible=icon] *){opacity:0}.group-data-\\[collapsible\\=offcanvas\\]\\:w-0:is(:where(.group)[data-collapsible=offcanvas] *){width:0}.group-data-\\[collapsible\\=offcanvas\\]\\:translate-x-0:is(:where(.group)[data-collapsible=offcanvas] *){--tw-translate-x:0px;translate:var(--tw-translate-x) var(--tw-translate-y)}.group-data-\\[disabled\\=true\\]\\:pointer-events-none:is(:where(.group)[data-disabled=true] *){pointer-events:none}.group-data-\\[disabled\\=true\\]\\:opacity-50:is(:where(.group)[data-disabled=true] *),.group-data-\\[disabled\\=true\\]\\/field\\:opacity-50:is(:where(.group\\/field)[data-disabled=true] *){opacity:.5}.group-data-\\[side\\=left\\]\\:-right-4:is(:where(.group)[data-side=left] *){right:calc(var(--spacing) * -4)}.group-data-\\[side\\=left\\]\\:border-r:is(:where(.group)[data-side=left] *){border-right-style:var(--tw-border-style);border-right-width:1px}.group-data-\\[side\\=right\\]\\:left-0:is(:where(.group)[data-side=right] *){left:0}.group-data-\\[side\\=right\\]\\:rotate-180:is(:where(.group)[data-side=right] *){rotate:180deg}.group-data-\\[side\\=right\\]\\:border-l:is(:where(.group)[data-side=right] *){border-left-style:var(--tw-border-style);border-left-width:1px}.group-data-\\[size\\=default\\]\\/avatar\\:size-2\\.5:is(:where(.group\\/avatar)[data-size=default] *){width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.group-data-\\[size\\=lg\\]\\/avatar\\:size-3:is(:where(.group\\/avatar)[data-size=lg] *){width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.group-data-\\[size\\=sm\\]\\/alert-dialog-content\\:grid:is(:where(.group\\/alert-dialog-content)[data-size=sm] *){display:grid}.group-data-\\[size\\=sm\\]\\/alert-dialog-content\\:grid-cols-2:is(:where(.group\\/alert-dialog-content)[data-size=sm] *){grid-template-columns:repeat(2,minmax(0,1fr))}.group-data-\\[size\\=sm\\]\\/avatar\\:size-2:is(:where(.group\\/avatar)[data-size=sm] *){width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.group-data-\\[size\\=sm\\]\\/avatar\\:text-xs:is(:where(.group\\/avatar)[data-size=sm] *){font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.group-data-\\[spacing\\=0\\]\\/toggle-group\\:rounded-none:is(:where(.group\\/toggle-group)[data-spacing=\"0\"] *){border-radius:0}.group-data-\\[spacing\\=0\\]\\/toggle-group\\:px-3:is(:where(.group\\/toggle-group)[data-spacing=\"0\"] *){padding-inline:calc(var(--spacing) * 3)}.group-data-\\[spacing\\=0\\]\\/toggle-group\\:shadow-none:is(:where(.group\\/toggle-group)[data-spacing=\"0\"] *){--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.group-data-\\[variant\\=floating\\]\\:rounded-lg:is(:where(.group)[data-variant=floating] *){border-radius:var(--radius)}.group-data-\\[variant\\=floating\\]\\:shadow-sm:is(:where(.group)[data-variant=floating] *){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.group-data-\\[variant\\=floating\\]\\:ring-1:is(:where(.group)[data-variant=floating] *){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.group-data-\\[variant\\=floating\\]\\:ring-sidebar-border:is(:where(.group)[data-variant=floating] *){--tw-ring-color:var(--sidebar-border)}.group-data-\\[variant\\=line\\]\\/tabs-list\\:bg-transparent:is(:where(.group\\/tabs-list)[data-variant=line] *){background-color:#0000}.group-data-\\[variant\\=outline\\]\\/field-group\\:-mb-2:is(:where(.group\\/field-group)[data-variant=outline] *){margin-bottom:calc(var(--spacing) * -2)}.group-data-horizontal\\/tabs\\:h-9:is(:where(.group\\/tabs):where([data-orientation=horizontal]) *){height:calc(var(--spacing) * 9)}.group-data-vertical\\/tabs\\:h-fit:is(:where(.group\\/tabs):where([data-orientation=vertical]) *){height:fit-content}.group-data-vertical\\/tabs\\:w-full:is(:where(.group\\/tabs):where([data-orientation=vertical]) *){width:100%}.group-data-vertical\\/tabs\\:flex-col:is(:where(.group\\/tabs):where([data-orientation=vertical]) *){flex-direction:column}.group-data-vertical\\/tabs\\:justify-start:is(:where(.group\\/tabs):where([data-orientation=vertical]) *){justify-content:flex-start}.group-data-vertical\\/tabs\\:rounded-2xl:is(:where(.group\\/tabs):where([data-orientation=vertical]) *){border-radius:calc(var(--radius) * 1.8)}.group-data-vertical\\/tabs\\:px-2\\.5:is(:where(.group\\/tabs):where([data-orientation=vertical]) *){padding-inline:calc(var(--spacing) * 2.5)}.group-data-vertical\\/tabs\\:py-1\\.5:is(:where(.group\\/tabs):where([data-orientation=vertical]) *){padding-block:calc(var(--spacing) * 1.5)}@media (hover:hover){.peer-hover\\/menu-button\\:text-sidebar-accent-foreground:is(:where(.peer\\/menu-button):hover~*){color:var(--sidebar-accent-foreground)}}.peer-disabled\\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.peer-data-\\[size\\=default\\]\\/menu-button\\:top-1\\.5:is(:where(.peer\\/menu-button)[data-size=default]~*){top:calc(var(--spacing) * 1.5)}.peer-data-\\[size\\=default\\]\\/menu-button\\:top-2:is(:where(.peer\\/menu-button)[data-size=default]~*){top:calc(var(--spacing) * 2)}.peer-data-\\[size\\=lg\\]\\/menu-button\\:top-2\\.5:is(:where(.peer\\/menu-button)[data-size=lg]~*){top:calc(var(--spacing) * 2.5)}.peer-data-\\[size\\=sm\\]\\/menu-button\\:top-1:is(:where(.peer\\/menu-button)[data-size=sm]~*){top:var(--spacing)}.peer-data-active\\/menu-button\\:text-sidebar-accent-foreground:is(:is(:where(.peer\\/menu-button):where([data-state=active]),:where(.peer\\/menu-button):where([data-active]:not([data-active=false])))~*){color:var(--sidebar-accent-foreground)}.file\\:inline-flex::file-selector-button{display:inline-flex}.file\\:h-7::file-selector-button{height:calc(var(--spacing) * 7)}.file\\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\\:bg-transparent::file-selector-button{background-color:#0000}.file\\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\\:text-foreground::file-selector-button{color:var(--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);inset:calc(var(--spacing) * -2)}.after\\:inset-0:after{content:var(--tw-content);inset:0}.after\\:-inset-x-3:after{content:var(--tw-content);inset-inline:calc(var(--spacing) * -3)}.after\\:-inset-y-2:after{content:var(--tw-content);inset-block:calc(var(--spacing) * -2)}.after\\:inset-y-0:after{content:var(--tw-content);inset-block:0}.after\\:start-1\\/2:after{content:var(--tw-content);inset-inline-start:50%}.after\\:w-\\[2px\\]:after{content:var(--tw-content);width:2px}.after\\:rounded-full:after{content:var(--tw-content);border-radius:3.40282e38px}.after\\:border:after{content:var(--tw-content);border-style:var(--tw-border-style);border-width:1px}.after\\:border-border:after{content:var(--tw-content);border-color:var(--border)}.after\\:bg-foreground:after{content:var(--tw-content);background-color:var(--foreground)}.after\\:opacity-0:after{content:var(--tw-content);opacity:0}.after\\:mix-blend-darken:after{content:var(--tw-content);mix-blend-mode:darken}.after\\:transition-opacity:after{content:var(--tw-content);transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.group-data-\\[collapsible\\=offcanvas\\]\\:after\\:left-full:is(:where(.group)[data-collapsible=offcanvas] *):after{content:var(--tw-content);left:100%}.group-data-horizontal\\/tabs\\:after\\:inset-x-0:is(:where(.group\\/tabs):where([data-orientation=horizontal]) *):after{content:var(--tw-content);inset-inline:0}.group-data-horizontal\\/tabs\\:after\\:bottom-\\[-5px\\]:is(:where(.group\\/tabs):where([data-orientation=horizontal]) *):after{content:var(--tw-content);bottom:-5px}.group-data-horizontal\\/tabs\\:after\\:h-0\\.5:is(:where(.group\\/tabs):where([data-orientation=horizontal]) *):after{content:var(--tw-content);height:calc(var(--spacing) * .5)}.group-data-vertical\\/tabs\\:after\\:inset-y-0:is(:where(.group\\/tabs):where([data-orientation=vertical]) *):after{content:var(--tw-content);inset-block:0}.group-data-vertical\\/tabs\\:after\\:-right-1:is(:where(.group\\/tabs):where([data-orientation=vertical]) *):after{content:var(--tw-content);right:calc(var(--spacing) * -1)}.group-data-vertical\\/tabs\\:after\\:w-0\\.5:is(:where(.group\\/tabs):where([data-orientation=vertical]) *):after{content:var(--tw-content);width:calc(var(--spacing) * .5)}.last\\:mt-0:last-child{margin-top:0}@media (hover:hover){.hover\\:bg-\\[color-mix\\(in_oklch\\,var\\(--secondary\\)\\,var\\(--foreground\\)_5\\%\\)\\]:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-\\[color-mix\\(in_oklch\\,var\\(--secondary\\)\\,var\\(--foreground\\)_5\\%\\)\\]:hover{background-color:color-mix(in oklch,var(--secondary),var(--foreground) 5%)}}.hover\\:bg-destructive\\/20:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-destructive\\/20:hover{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.hover\\:bg-input\\/50:hover{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-input\\/50:hover{background-color:color-mix(in oklab, var(--input) 50%, transparent)}}.hover\\:bg-muted:hover,.hover\\:bg-muted\\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-muted\\/50:hover{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.hover\\:bg-primary\\/80:hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-primary\\/80:hover{background-color:color-mix(in oklab, var(--primary) 80%, transparent)}}.hover\\:bg-sidebar-accent:hover{background-color:var(--sidebar-accent)}.hover\\:text-foreground:hover{color:var(--foreground)}.hover\\:text-muted-foreground:hover{color:var(--muted-foreground)}.hover\\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}.hover\\:underline:hover{text-decoration-line:underline}.hover\\:shadow-\\[0_0_0_1px_var\\(--sidebar-accent\\)\\]:hover{--tw-shadow:0 0 0 1px var(--tw-shadow-color,var(--sidebar-accent));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hover\\:group-data-\\[collapsible\\=offcanvas\\]\\:bg-sidebar:hover:is(:where(.group)[data-collapsible=offcanvas] *){background-color:var(--sidebar)}.hover\\:after\\:bg-sidebar-border:hover:after{content:var(--tw-content);background-color:var(--sidebar-border)}}.focus\\:z-10:focus{z-index:10}.focus\\:bg-accent:focus{background-color:var(--accent)}.focus\\:text-accent-foreground:focus,:is(.focus\\:\\*\\*\\:text-accent-foreground:focus *),:is(.not-data-\\[variant\\=destructive\\]\\:focus\\:\\*\\*\\:text-accent-foreground:not([data-variant=destructive]):focus *){color:var(--accent-foreground)}.focus-visible\\:z-10:focus-visible{z-index:10}.focus-visible\\:border-destructive\\/40:focus-visible{border-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.focus-visible\\:border-destructive\\/40:focus-visible{border-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.focus-visible\\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\\:ring-\\[3px\\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\\:ring-destructive\\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.focus-visible\\:ring-destructive\\/20:focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.focus-visible\\:ring-ring\\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.focus-visible\\:ring-ring\\/50:focus-visible{--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.focus-visible\\:outline-1:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\\:outline-ring:focus-visible{outline-color:var(--ring)}.active\\:bg-sidebar-accent:active{background-color:var(--sidebar-accent)}.active\\:text-sidebar-accent-foreground:active{color:var(--sidebar-accent-foreground)}.active\\:not-aria-\\[haspopup\\]\\:translate-y-px:active:not([aria-haspopup]){--tw-translate-y:1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.disabled\\:pointer-events-none:disabled{pointer-events:none}.disabled\\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\\:opacity-50:disabled{opacity:.5}:where([data-side=left]) .in-data-\\[side\\=left\\]\\:cursor-w-resize{cursor:w-resize}:where([data-side=right]) .in-data-\\[side\\=right\\]\\:cursor-e-resize{cursor:e-resize}.has-aria-expanded\\:bg-muted\\/50:has([aria-expanded=true]){background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.has-aria-expanded\\:bg-muted\\/50:has([aria-expanded=true]){background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.has-data-\\[icon\\=inline-end\\]\\:pr-1\\.5:has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 1.5)}.has-data-\\[icon\\=inline-end\\]\\:pr-2:has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 2)}.has-data-\\[icon\\=inline-end\\]\\:pr-2\\.5:has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 2.5)}.has-data-\\[icon\\=inline-end\\]\\:pr-3:has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 3)}.group-data-\\[spacing\\=0\\]\\/toggle-group\\:has-data-\\[icon\\=inline-end\\]\\:pr-2\\.5:is(:where(.group\\/toggle-group)[data-spacing=\"0\"] *):has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 2.5)}.has-data-\\[icon\\=inline-start\\]\\:pl-1\\.5:has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 1.5)}.has-data-\\[icon\\=inline-start\\]\\:pl-2:has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 2)}.has-data-\\[icon\\=inline-start\\]\\:pl-2\\.5:has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 2.5)}.has-data-\\[icon\\=inline-start\\]\\:pl-3:has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 3)}.group-data-\\[spacing\\=0\\]\\/toggle-group\\:has-data-\\[icon\\=inline-start\\]\\:pl-2\\.5:is(:where(.group\\/toggle-group)[data-spacing=\"0\"] *):has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 2.5)}.has-data-\\[slot\\=alert-action\\]\\:relative:has([data-slot=alert-action]){position:relative}.has-data-\\[slot\\=alert-action\\]\\:pr-18:has([data-slot=alert-action]){padding-right:calc(var(--spacing) * 18)}.has-data-\\[slot\\=alert-dialog-media\\]\\:grid-rows-\\[auto_auto_1fr\\]:has([data-slot=alert-dialog-media]){grid-template-rows:auto auto 1fr}.has-data-\\[slot\\=alert-dialog-media\\]\\:gap-x-6:has([data-slot=alert-dialog-media]){column-gap:calc(var(--spacing) * 6)}.has-data-\\[slot\\=card-action\\]\\:grid-cols-\\[1fr_auto\\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-data-\\[slot\\=card-description\\]\\:grid-rows-\\[auto_auto\\]:has([data-slot=card-description]){grid-template-rows:auto auto}.has-data-\\[slot\\=kbd\\]\\:pr-1\\.5:has([data-slot=kbd]){padding-right:calc(var(--spacing) * 1.5)}.has-data-\\[variant\\=inset\\]\\:bg-sidebar:has([data-variant=inset]){background-color:var(--sidebar)}.has-data-checked\\:border-primary\\/30:has(:where([data-state=checked]),:where([data-checked]:not([data-checked=false]))){border-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.has-data-checked\\:border-primary\\/30:has(:where([data-state=checked]),:where([data-checked]:not([data-checked=false]))){border-color:color-mix(in oklab, var(--primary) 30%, transparent)}}.has-data-checked\\:bg-primary\\/5:has(:where([data-state=checked]),:where([data-checked]:not([data-checked=false]))){background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.has-data-checked\\:bg-primary\\/5:has(:where([data-state=checked]),:where([data-checked]:not([data-checked=false]))){background-color:color-mix(in oklab, var(--primary) 5%, transparent)}}.has-\\[\\>\\[data-slot\\=checkbox-group\\]\\]\\:gap-3:has(>[data-slot=checkbox-group]){gap:calc(var(--spacing) * 3)}.has-\\[\\>\\[data-slot\\=field-content\\]\\]\\:items-start:has(>[data-slot=field-content]){align-items:flex-start}.has-\\[\\>\\[data-slot\\=field\\]\\]\\:w-full:has(>[data-slot=field]){width:100%}.has-\\[\\>\\[data-slot\\=field\\]\\]\\:flex-col:has(>[data-slot=field]){flex-direction:column}.has-\\[\\>\\[data-slot\\=field\\]\\]\\:rounded-xl:has(>[data-slot=field]){border-radius:calc(var(--radius) * 1.4)}.has-\\[\\>\\[data-slot\\=field\\]\\]\\:border:has(>[data-slot=field]){border-style:var(--tw-border-style);border-width:1px}@media (hover:hover){.has-\\[\\>\\[data-slot\\=field\\]\\]\\:not-has-\\[\\:disabled\\,\\[data-disabled\\]\\]\\:hover\\:bg-input\\/40:has(>[data-slot=field]):not(:has(:is(:disabled,[data-disabled]))):hover{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.has-\\[\\>\\[data-slot\\=field\\]\\]\\:not-has-\\[\\:disabled\\,\\[data-disabled\\]\\]\\:hover\\:bg-input\\/40:has(>[data-slot=field]):not(:has(:is(:disabled,[data-disabled]))):hover{background-color:color-mix(in oklab, var(--input) 40%, transparent)}}}.has-\\[\\>\\[data-slot\\=field\\]\\]\\:has-\\[\\:focus-visible\\]\\:border-ring:has(>[data-slot=field]):has(:focus-visible){border-color:var(--ring)}.has-\\[\\>\\[data-slot\\=field\\]\\]\\:has-\\[\\:focus-visible\\]\\:ring-\\[3px\\]:has(>[data-slot=field]):has(:focus-visible){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.has-\\[\\>\\[data-slot\\=field\\]\\]\\:has-\\[\\:focus-visible\\]\\:ring-ring\\/50:has(>[data-slot=field]):has(:focus-visible){--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.has-\\[\\>\\[data-slot\\=field\\]\\]\\:has-\\[\\:focus-visible\\]\\:ring-ring\\/50:has(>[data-slot=field]):has(:focus-visible){--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.has-\\[\\>\\[data-slot\\=radio-group\\]\\]\\:gap-3:has(>[data-slot=radio-group]){gap:calc(var(--spacing) * 3)}.has-\\[\\>img\\:first-child\\]\\:pt-0:has(>img:first-child){padding-top:0}.has-\\[\\>svg\\]\\:grid-cols-\\[auto_1fr\\]:has(>svg){grid-template-columns:auto 1fr}.has-\\[\\>svg\\]\\:gap-x-2\\.5:has(>svg){column-gap:calc(var(--spacing) * 2.5)}.aria-disabled\\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\\:opacity-50[aria-disabled=true]{opacity:.5}.aria-expanded\\:bg-muted[aria-expanded=true]{background-color:var(--muted)}.aria-expanded\\:bg-secondary[aria-expanded=true]{background-color:var(--secondary)}.aria-expanded\\:text-foreground[aria-expanded=true]{color:var(--foreground)}.aria-expanded\\:text-secondary-foreground[aria-expanded=true]{color:var(--secondary-foreground)}.aria-expanded\\:opacity-100[aria-expanded=true]{opacity:1}.aria-invalid\\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.aria-invalid\\:ring-\\[3px\\][aria-invalid=true]{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.aria-invalid\\:ring-destructive\\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.aria-invalid\\:ring-destructive\\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.aria-invalid\\:aria-checked\\:border-primary[aria-invalid=true][aria-checked=true]{border-color:var(--primary)}.aria-pressed\\:bg-muted[aria-pressed=true]{background-color:var(--muted)}.data-ending-style\\:h-0[data-ending-style]{height:0}.data-ending-style\\:opacity-0[data-ending-style]{opacity:0}.data-inset\\:pl-9\\.5[data-inset]{padding-left:calc(var(--spacing) * 9.5)}.data-popup-open\\:bg-accent[data-popup-open]{background-color:var(--accent)}.data-popup-open\\:text-accent-foreground[data-popup-open]{color:var(--accent-foreground)}.data-starting-style\\:h-0[data-starting-style]{height:0}.data-starting-style\\:opacity-0[data-starting-style]{opacity:0}.data-\\[invalid\\=true\\]\\:text-destructive[data-invalid=true]{color:var(--destructive)}.data-\\[side\\=bottom\\]\\:inset-x-0[data-side=bottom]{inset-inline:0}.data-\\[side\\=bottom\\]\\:top-1[data-side=bottom]{top:var(--spacing)}.data-\\[side\\=bottom\\]\\:bottom-0[data-side=bottom]{bottom:0}.data-\\[side\\=bottom\\]\\:h-auto[data-side=bottom]{height:auto}.data-\\[side\\=bottom\\]\\:border-t[data-side=bottom]{border-top-style:var(--tw-border-style);border-top-width:1px}.data-\\[side\\=bottom\\]\\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\\[side\\=bottom\\]\\:data-ending-style\\:translate-y-\\[2\\.5rem\\][data-side=bottom][data-ending-style],.data-\\[side\\=bottom\\]\\:data-starting-style\\:translate-y-\\[2\\.5rem\\][data-side=bottom][data-starting-style]{--tw-translate-y:2.5rem;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[side\\=inline-end\\]\\:top-1\\/2\\![data-side=inline-end]{top:50%!important}.data-\\[side\\=inline-end\\]\\:-left-1[data-side=inline-end]{left:calc(var(--spacing) * -1)}.data-\\[side\\=inline-end\\]\\:translate-x-\\[1\\.5px\\][data-side=inline-end]{--tw-translate-x:1.5px;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[side\\=inline-end\\]\\:-translate-y-1\\/2[data-side=inline-end]{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[side\\=inline-end\\]\\:slide-in-from-left-2[data-side=inline-end]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\\[side\\=inline-start\\]\\:top-1\\/2\\![data-side=inline-start]{top:50%!important}.data-\\[side\\=inline-start\\]\\:-right-1[data-side=inline-start]{right:calc(var(--spacing) * -1)}.data-\\[side\\=inline-start\\]\\:translate-x-\\[-1\\.5px\\][data-side=inline-start]{--tw-translate-x:-1.5px;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[side\\=inline-start\\]\\:-translate-y-1\\/2[data-side=inline-start]{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[side\\=inline-start\\]\\:slide-in-from-right-2[data-side=inline-start]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\\[side\\=left\\]\\:inset-y-0[data-side=left]{inset-block:0}.data-\\[side\\=left\\]\\:top-1\\/2\\![data-side=left]{top:50%!important}.data-\\[side\\=left\\]\\:-right-1[data-side=left]{right:calc(var(--spacing) * -1)}.data-\\[side\\=left\\]\\:left-0[data-side=left]{left:0}.data-\\[side\\=left\\]\\:h-full[data-side=left]{height:100%}.data-\\[side\\=left\\]\\:w-3\\/4[data-side=left]{width:75%}.data-\\[side\\=left\\]\\:translate-x-\\[-1\\.5px\\][data-side=left]{--tw-translate-x:-1.5px;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[side\\=left\\]\\:-translate-y-1\\/2[data-side=left]{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[side\\=left\\]\\:border-r[data-side=left]{border-right-style:var(--tw-border-style);border-right-width:1px}.data-\\[side\\=left\\]\\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\\[side\\=left\\]\\:group-data-\\[collapsible\\=offcanvas\\]\\:left-\\[calc\\(var\\(--sidebar-width\\)\\*-1\\)\\][data-side=left]:is(:where(.group)[data-collapsible=offcanvas] *){left:calc(var(--sidebar-width) * -1)}.data-\\[side\\=left\\]\\:data-ending-style\\:translate-x-\\[-2\\.5rem\\][data-side=left][data-ending-style],.data-\\[side\\=left\\]\\:data-starting-style\\:translate-x-\\[-2\\.5rem\\][data-side=left][data-starting-style]{--tw-translate-x:-2.5rem;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[side\\=right\\]\\:inset-y-0[data-side=right]{inset-block:0}.data-\\[side\\=right\\]\\:top-1\\/2\\![data-side=right]{top:50%!important}.data-\\[side\\=right\\]\\:right-0[data-side=right]{right:0}.data-\\[side\\=right\\]\\:-left-1[data-side=right]{left:calc(var(--spacing) * -1)}.data-\\[side\\=right\\]\\:h-full[data-side=right]{height:100%}.data-\\[side\\=right\\]\\:w-3\\/4[data-side=right]{width:75%}.data-\\[side\\=right\\]\\:translate-x-\\[1\\.5px\\][data-side=right]{--tw-translate-x:1.5px;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[side\\=right\\]\\:-translate-y-1\\/2[data-side=right]{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[side\\=right\\]\\:border-l[data-side=right]{border-left-style:var(--tw-border-style);border-left-width:1px}.data-\\[side\\=right\\]\\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\\[side\\=right\\]\\:group-data-\\[collapsible\\=offcanvas\\]\\:right-\\[calc\\(var\\(--sidebar-width\\)\\*-1\\)\\][data-side=right]:is(:where(.group)[data-collapsible=offcanvas] *){right:calc(var(--sidebar-width) * -1)}.data-\\[side\\=right\\]\\:data-ending-style\\:translate-x-\\[2\\.5rem\\][data-side=right][data-ending-style],.data-\\[side\\=right\\]\\:data-starting-style\\:translate-x-\\[2\\.5rem\\][data-side=right][data-starting-style]{--tw-translate-x:2.5rem;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[side\\=top\\]\\:inset-x-0[data-side=top]{inset-inline:0}.data-\\[side\\=top\\]\\:top-0[data-side=top]{top:0}.data-\\[side\\=top\\]\\:-bottom-2\\.5[data-side=top]{bottom:calc(var(--spacing) * -2.5)}.data-\\[side\\=top\\]\\:h-auto[data-side=top]{height:auto}.data-\\[side\\=top\\]\\:border-b[data-side=top]{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.data-\\[side\\=top\\]\\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\\[side\\=top\\]\\:data-ending-style\\:translate-y-\\[-2\\.5rem\\][data-side=top][data-ending-style],.data-\\[side\\=top\\]\\:data-starting-style\\:translate-y-\\[-2\\.5rem\\][data-side=top][data-starting-style]{--tw-translate-y:-2.5rem;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\\[size\\=default\\]\\:max-w-xs[data-size=default]{max-width:var(--container-xs)}.data-\\[size\\=lg\\]\\:size-10[data-size=lg]{width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.data-\\[size\\=md\\]\\:text-sm[data-size=md]{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.data-\\[size\\=sm\\]\\:size-6[data-size=sm]{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.data-\\[size\\=sm\\]\\:max-w-xs[data-size=sm]{max-width:var(--container-xs)}.data-\\[size\\=sm\\]\\:text-xs[data-size=sm]{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.data-\\[size\\=sm\\]\\:\\[--card-spacing\\:--spacing\\(4\\)\\][data-size=sm]{--card-spacing:calc(var(--spacing) * 4)}:is(.\\*\\*\\:data-\\[slot\\=accordion-trigger-icon\\]\\:ml-auto *)[data-slot=accordion-trigger-icon]{margin-left:auto}:is(.\\*\\*\\:data-\\[slot\\=accordion-trigger-icon\\]\\:size-4 *)[data-slot=accordion-trigger-icon]{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}:is(.\\*\\*\\:data-\\[slot\\=accordion-trigger-icon\\]\\:text-muted-foreground *)[data-slot=accordion-trigger-icon]{color:var(--muted-foreground)}:is(.\\*\\:data-\\[slot\\=alert-description\\]\\:text-destructive\\/90>*)[data-slot=alert-description]{color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){:is(.\\*\\:data-\\[slot\\=alert-description\\]\\:text-destructive\\/90>*)[data-slot=alert-description]{color:color-mix(in oklab, var(--destructive) 90%, transparent)}}:is(.\\*\\:data-\\[slot\\=avatar\\]\\:ring-2>*)[data-slot=avatar]{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}:is(.\\*\\:data-\\[slot\\=avatar\\]\\:ring-background>*)[data-slot=avatar]{--tw-ring-color:var(--background)}.data-\\[slot\\=checkbox-group\\]\\:gap-3[data-slot=checkbox-group]{gap:calc(var(--spacing) * 3)}:is(.\\*\\:data-\\[slot\\=field\\]\\:p-4>*)[data-slot=field]{padding:calc(var(--spacing) * 4)}:is(.\\*\\:data-\\[slot\\=field-group\\]\\:gap-4>*)[data-slot=field-group]{gap:calc(var(--spacing) * 4)}:is(.\\*\\:data-\\[slot\\=field-label\\]\\:flex-auto>*)[data-slot=field-label]{flex:auto}:is(.\\*\\*\\:data-\\[slot\\=kbd\\]\\:relative *)[data-slot=kbd]{position:relative}:is(.\\*\\*\\:data-\\[slot\\=kbd\\]\\:isolate *)[data-slot=kbd]{isolation:isolate}:is(.\\*\\*\\:data-\\[slot\\=kbd\\]\\:z-50 *)[data-slot=kbd]{z-index:50}:is(.\\*\\*\\:data-\\[slot\\=kbd\\]\\:rounded-4xl *)[data-slot=kbd]{border-radius:calc(var(--radius) * 2.6)}.group-data-horizontal\\/toggle-group\\:data-\\[spacing\\=0\\]\\:first\\:rounded-l-3xl:is(:where(.group\\/toggle-group):where([data-orientation=horizontal]) *)[data-spacing=\"0\"]:first-child{border-top-left-radius:calc(var(--radius) * 2.2);border-bottom-left-radius:calc(var(--radius) * 2.2)}.group-data-vertical\\/toggle-group\\:data-\\[spacing\\=0\\]\\:first\\:rounded-t-3xl:is(:where(.group\\/toggle-group):where([data-orientation=vertical]) *)[data-spacing=\"0\"]:first-child{border-top-left-radius:calc(var(--radius) * 2.2);border-top-right-radius:calc(var(--radius) * 2.2)}.group-data-horizontal\\/toggle-group\\:data-\\[spacing\\=0\\]\\:last\\:rounded-r-3xl:is(:where(.group\\/toggle-group):where([data-orientation=horizontal]) *)[data-spacing=\"0\"]:last-child{border-top-right-radius:calc(var(--radius) * 2.2);border-bottom-right-radius:calc(var(--radius) * 2.2)}.group-data-vertical\\/toggle-group\\:data-\\[spacing\\=0\\]\\:last\\:rounded-b-3xl:is(:where(.group\\/toggle-group):where([data-orientation=vertical]) *)[data-spacing=\"0\"]:last-child{border-bottom-right-radius:calc(var(--radius) * 2.2);border-bottom-left-radius:calc(var(--radius) * 2.2)}.data-\\[state\\=delayed-open\\]\\:animate-in[data-state=delayed-open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\\[state\\=delayed-open\\]\\:fade-in-0[data-state=delayed-open]{--tw-enter-opacity:0}.data-\\[state\\=delayed-open\\]\\:zoom-in-95[data-state=delayed-open]{--tw-enter-scale:.95}.data-\\[state\\=on\\]\\:bg-muted[data-state=on],.data-\\[state\\=selected\\]\\:bg-muted[data-state=selected]{background-color:var(--muted)}.data-\\[variant\\=destructive\\]\\:text-destructive[data-variant=destructive]{color:var(--destructive)}.data-\\[variant\\=destructive\\]\\:focus\\:bg-destructive\\/10[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.data-\\[variant\\=destructive\\]\\:focus\\:bg-destructive\\/10[data-variant=destructive]:focus{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.data-\\[variant\\=destructive\\]\\:focus\\:text-destructive[data-variant=destructive]:focus{color:var(--destructive)}.data-\\[variant\\=label\\]\\:text-sm[data-variant=label]{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.data-\\[variant\\=legend\\]\\:text-base[data-variant=legend]{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.data-\\[variant\\=line\\]\\:rounded-none[data-variant=line]{border-radius:0}.data-\\[spacing\\=0\\]\\:data-\\[variant\\=outline\\]\\:rounded-4xl[data-spacing=\"0\"][data-variant=outline]{border-radius:calc(var(--radius) * 2.6)}.group-data-horizontal\\/toggle-group\\:data-\\[spacing\\=0\\]\\:data-\\[variant\\=outline\\]\\:border-l-0:is(:where(.group\\/toggle-group):where([data-orientation=horizontal]) *)[data-spacing=\"0\"][data-variant=outline]{border-left-style:var(--tw-border-style);border-left-width:0}.group-data-vertical\\/toggle-group\\:data-\\[spacing\\=0\\]\\:data-\\[variant\\=outline\\]\\:border-t-0:is(:where(.group\\/toggle-group):where([data-orientation=vertical]) *)[data-spacing=\"0\"][data-variant=outline]{border-top-style:var(--tw-border-style);border-top-width:0}.group-data-horizontal\\/toggle-group\\:data-\\[spacing\\=0\\]\\:data-\\[variant\\=outline\\]\\:first\\:border-l:is(:where(.group\\/toggle-group):where([data-orientation=horizontal]) *)[data-spacing=\"0\"][data-variant=outline]:first-child{border-left-style:var(--tw-border-style);border-left-width:1px}.group-data-vertical\\/toggle-group\\:data-\\[spacing\\=0\\]\\:data-\\[variant\\=outline\\]\\:first\\:border-t:is(:where(.group\\/toggle-group):where([data-orientation=vertical]) *)[data-spacing=\"0\"][data-variant=outline]:first-child{border-top-style:var(--tw-border-style);border-top-width:1px}.nth-last-2\\:-mt-1:nth-last-child(2){margin-top:calc(var(--spacing) * -1)}@supports ((-webkit-backdrop-filter:var(--tw)) or (backdrop-filter:var(--tw))){.supports-backdrop-filter\\:backdrop-blur-xs{--tw-backdrop-blur:blur(var(--blur-xs));-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,)}}@media (prefers-reduced-motion:reduce){.motion-reduce\\:animate-none{animation:none}}@media (min-width:40rem){.sm\\:right-\\[22px\\]{right:22px}.sm\\:bottom-\\[22px\\]{bottom:22px}.sm\\:flex{display:flex}.sm\\:w-52{width:calc(var(--spacing) * 52)}.sm\\:max-w-md{max-width:var(--container-md)}.sm\\:flex-row{flex-direction:row}.sm\\:justify-end{justify-content:flex-end}.sm\\:group-data-\\[size\\=default\\]\\/alert-dialog-content\\:row-span-2:is(:where(.group\\/alert-dialog-content)[data-size=default] *){grid-row:span 2/span 2}.sm\\:group-data-\\[size\\=default\\]\\/alert-dialog-content\\:place-items-start:is(:where(.group\\/alert-dialog-content)[data-size=default] *){place-items:start}.sm\\:group-data-\\[size\\=default\\]\\/alert-dialog-content\\:text-left:is(:where(.group\\/alert-dialog-content)[data-size=default] *){text-align:left}.sm\\:group-data-\\[size\\=default\\]\\/alert-dialog-content\\:group-has-data-\\[slot\\=alert-dialog-media\\]\\/alert-dialog-content\\:col-start-2:is(:where(.group\\/alert-dialog-content)[data-size=default] *):is(:where(.group\\/alert-dialog-content):has([data-slot=alert-dialog-media]) *){grid-column-start:2}.sm\\:group-data-\\[size\\=default\\]\\/alert-dialog-content\\:has-data-\\[slot\\=alert-dialog-media\\]\\:grid-rows-\\[auto_1fr\\]:is(:where(.group\\/alert-dialog-content)[data-size=default] *):has([data-slot=alert-dialog-media]){grid-template-rows:auto 1fr}.data-\\[side\\=left\\]\\:sm\\:max-w-sm[data-side=left],.data-\\[side\\=right\\]\\:sm\\:max-w-sm[data-side=right]{max-width:var(--container-sm)}.data-\\[size\\=default\\]\\:sm\\:max-w-md[data-size=default]{max-width:var(--container-md)}}@media (min-width:48rem){.md\\:block{display:block}.md\\:flex{display:flex}.md\\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.md\\:text-pretty{text-wrap:pretty}.md\\:opacity-0{opacity:0}.md\\:peer-data-\\[variant\\=inset\\]\\:m-2:is(:where(.peer)[data-variant=inset]~*){margin:calc(var(--spacing) * 2)}.md\\:peer-data-\\[variant\\=inset\\]\\:ml-0:is(:where(.peer)[data-variant=inset]~*){margin-left:0}.md\\:peer-data-\\[variant\\=inset\\]\\:rounded-xl:is(:where(.peer)[data-variant=inset]~*){border-radius:calc(var(--radius) * 1.4)}.md\\:peer-data-\\[variant\\=inset\\]\\:shadow-sm:is(:where(.peer)[data-variant=inset]~*){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.md\\:peer-data-\\[variant\\=inset\\]\\:peer-data-\\[state\\=collapsed\\]\\:ml-2:is(:where(.peer)[data-variant=inset]~*):is(:where(.peer)[data-state=collapsed]~*){margin-left:calc(var(--spacing) * 2)}.md\\:after\\:hidden:after{content:var(--tw-content);display:none}}@container field-group (min-width:28rem){.\\@md\\/field-group\\:flex-row{flex-direction:row}.\\@md\\/field-group\\:items-center{align-items:center}:is(.\\@md\\/field-group\\:\\*\\:w-auto>*){width:auto}.\\@md\\/field-group\\:has-\\[\\>\\[data-slot\\=field-content\\]\\]\\:items-start:has(>[data-slot=field-content]){align-items:flex-start}:is(.\\@md\\/field-group\\:\\*\\:data-\\[slot\\=field-label\\]\\:flex-auto>*)[data-slot=field-label]{flex:auto}}.ltr\\:-translate-x-1\\/2:where(:dir(ltr),[dir=ltr],[dir=ltr] *),.rtl\\:-translate-x-1\\/2:where(:dir(rtl),[dir=rtl],[dir=rtl] *){--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.dark\\:bg-destructive\\/20:is(.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\\:bg-destructive\\/20:is(.dark *){background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.dark\\:bg-input\\/30:is(.dark *){background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.dark\\:bg-input\\/30:is(.dark *){background-color:color-mix(in oklab, var(--input) 30%, transparent)}}.dark\\:text-muted-foreground:is(.dark *){color:var(--muted-foreground)}.dark\\:ring-foreground\\/10:is(.dark *){--tw-ring-color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.dark\\:ring-foreground\\/10:is(.dark *){--tw-ring-color:color-mix(in oklab, var(--foreground) 10%, transparent)}}.dark\\:after\\:mix-blend-lighten:is(.dark *):after{content:var(--tw-content);mix-blend-mode:lighten}@media (hover:hover){.dark\\:hover\\:bg-destructive\\/30:is(.dark *):hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\\:hover\\:bg-destructive\\/30:is(.dark *):hover{background-color:color-mix(in oklab, var(--destructive) 30%, transparent)}}.dark\\:hover\\:bg-muted\\/50:is(.dark *):hover{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.dark\\:hover\\:bg-muted\\/50:is(.dark *):hover{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.dark\\:hover\\:text-foreground:is(.dark *):hover{color:var(--foreground)}}.dark\\:focus-visible\\:ring-destructive\\/40:is(.dark *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\\:focus-visible\\:ring-destructive\\/40:is(.dark *):focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\\:has-data-checked\\:border-primary\\/20:is(.dark *):has(:where([data-state=checked]),:where([data-checked]:not([data-checked=false]))){border-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.dark\\:has-data-checked\\:border-primary\\/20:is(.dark *):has(:where([data-state=checked]),:where([data-checked]:not([data-checked=false]))){border-color:color-mix(in oklab, var(--primary) 20%, transparent)}}.dark\\:has-data-checked\\:bg-primary\\/10:is(.dark *):has(:where([data-state=checked]),:where([data-checked]:not([data-checked=false]))){background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.dark\\:has-data-checked\\:bg-primary\\/10:is(.dark *):has(:where([data-state=checked]),:where([data-checked]:not([data-checked=false]))){background-color:color-mix(in oklab, var(--primary) 10%, transparent)}}.dark\\:aria-invalid\\:border-destructive\\/50:is(.dark *)[aria-invalid=true]{border-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\\:aria-invalid\\:border-destructive\\/50:is(.dark *)[aria-invalid=true]{border-color:color-mix(in oklab, var(--destructive) 50%, transparent)}}.dark\\:aria-invalid\\:ring-destructive\\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\\:aria-invalid\\:ring-destructive\\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\\:data-\\[variant\\=destructive\\]\\:focus\\:bg-destructive\\/20:is(.dark *)[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\\:data-\\[variant\\=destructive\\]\\:focus\\:bg-destructive\\/20:is(.dark *)[data-variant=destructive]:focus{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.data-open\\:animate-accordion-down:where([data-state=open]),.data-open\\:animate-accordion-down:where([data-open]:not([data-open=false])){animation:accordion-down var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-open\\:animate-in:where([data-state=open]),.data-open\\:animate-in:where([data-open]:not([data-open=false])){animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-open\\:bg-accent:where([data-state=open]),.data-open\\:bg-accent:where([data-open]:not([data-open=false])){background-color:var(--accent)}.data-open\\:bg-muted\\/50:where([data-state=open]),.data-open\\:bg-muted\\/50:where([data-open]:not([data-open=false])){background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.data-open\\:bg-muted\\/50:where([data-state=open]),.data-open\\:bg-muted\\/50:where([data-open]:not([data-open=false])){background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.data-open\\:text-accent-foreground:where([data-state=open]),.data-open\\:text-accent-foreground:where([data-open]:not([data-open=false])){color:var(--accent-foreground)}.data-open\\:fade-in-0:where([data-state=open]),.data-open\\:fade-in-0:where([data-open]:not([data-open=false])){--tw-enter-opacity:0}.data-open\\:zoom-in-95:where([data-state=open]),.data-open\\:zoom-in-95:where([data-open]:not([data-open=false])){--tw-enter-scale:.95}@media (hover:hover){:is(.data-open\\:hover\\:bg-sidebar-accent:where([data-state=open]),.data-open\\:hover\\:bg-sidebar-accent:where([data-open]:not([data-open=false]))):hover{background-color:var(--sidebar-accent)}:is(.data-open\\:hover\\:text-sidebar-accent-foreground:where([data-state=open]),.data-open\\:hover\\:text-sidebar-accent-foreground:where([data-open]:not([data-open=false]))):hover{color:var(--sidebar-accent-foreground)}}.data-closed\\:animate-accordion-up:where([data-state=closed]),.data-closed\\:animate-accordion-up:where([data-closed]:not([data-closed=false])){animation:accordion-up var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-closed\\:animate-out:where([data-state=closed]),.data-closed\\:animate-out:where([data-closed]:not([data-closed=false])){animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-closed\\:overflow-hidden:where([data-state=closed]),.data-closed\\:overflow-hidden:where([data-closed]:not([data-closed=false])){overflow:hidden}.data-closed\\:fade-out-0:where([data-state=closed]),.data-closed\\:fade-out-0:where([data-closed]:not([data-closed=false])){--tw-exit-opacity:0}.data-closed\\:zoom-out-95:where([data-state=closed]),.data-closed\\:zoom-out-95:where([data-closed]:not([data-closed=false])){--tw-exit-scale:.95}.data-checked\\:border-primary:where([data-state=checked]),.data-checked\\:border-primary:where([data-checked]:not([data-checked=false])){border-color:var(--primary)}.data-checked\\:bg-primary:where([data-state=checked]),.data-checked\\:bg-primary:where([data-checked]:not([data-checked=false])){background-color:var(--primary)}.data-checked\\:text-primary-foreground:where([data-state=checked]),.data-checked\\:text-primary-foreground:where([data-checked]:not([data-checked=false])){color:var(--primary-foreground)}.group-has-\\[\\:focus-visible\\]\\/field-label\\:data-checked\\:border-primary:is(:where(.group\\/field-label):has(:focus-visible) *):where([data-state=checked]),.group-has-\\[\\:focus-visible\\]\\/field-label\\:data-checked\\:border-primary:is(:where(.group\\/field-label):has(:focus-visible) *):where([data-checked]:not([data-checked=false])){border-color:var(--primary)}.dark\\:data-checked\\:bg-primary:is(.dark *):where([data-state=checked]),.dark\\:data-checked\\:bg-primary:is(.dark *):where([data-checked]:not([data-checked=false])){background-color:var(--primary)}.data-disabled\\:pointer-events-none:where([data-disabled=true]),.data-disabled\\:pointer-events-none:where([data-disabled]:not([data-disabled=false])){pointer-events:none}.data-disabled\\:opacity-50:where([data-disabled=true]),.data-disabled\\:opacity-50:where([data-disabled]:not([data-disabled=false])){opacity:.5}.data-active\\:bg-background:where([data-state=active]),.data-active\\:bg-background:where([data-active]:not([data-active=false])){background-color:var(--background)}.data-active\\:bg-sidebar-accent:where([data-state=active]),.data-active\\:bg-sidebar-accent:where([data-active]:not([data-active=false])){background-color:var(--sidebar-accent)}.data-active\\:font-medium:where([data-state=active]),.data-active\\:font-medium:where([data-active]:not([data-active=false])){--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.data-active\\:text-foreground:where([data-state=active]),.data-active\\:text-foreground:where([data-active]:not([data-active=false])){color:var(--foreground)}.data-active\\:text-sidebar-accent-foreground:where([data-state=active]),.data-active\\:text-sidebar-accent-foreground:where([data-active]:not([data-active=false])){color:var(--sidebar-accent-foreground)}.group-data-\\[variant\\=line\\]\\/tabs-list\\:data-active\\:bg-transparent:is(:where(.group\\/tabs-list)[data-variant=line] *):where([data-state=active]),.group-data-\\[variant\\=line\\]\\/tabs-list\\:data-active\\:bg-transparent:is(:where(.group\\/tabs-list)[data-variant=line] *):where([data-active]:not([data-active=false])){background-color:#0000}:is(.group-data-\\[variant\\=line\\]\\/tabs-list\\:data-active\\:after\\:opacity-100:is(:where(.group\\/tabs-list)[data-variant=line] *):where([data-state=active]),.group-data-\\[variant\\=line\\]\\/tabs-list\\:data-active\\:after\\:opacity-100:is(:where(.group\\/tabs-list)[data-variant=line] *):where([data-active]:not([data-active=false]))):after{content:var(--tw-content);opacity:1}.dark\\:data-active\\:border-input:is(.dark *):where([data-state=active]),.dark\\:data-active\\:border-input:is(.dark *):where([data-active]:not([data-active=false])){border-color:var(--input)}.dark\\:data-active\\:bg-input\\/30:is(.dark *):where([data-state=active]),.dark\\:data-active\\:bg-input\\/30:is(.dark *):where([data-active]:not([data-active=false])){background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.dark\\:data-active\\:bg-input\\/30:is(.dark *):where([data-state=active]),.dark\\:data-active\\:bg-input\\/30:is(.dark *):where([data-active]:not([data-active=false])){background-color:color-mix(in oklab, var(--input) 30%, transparent)}}.dark\\:data-active\\:text-foreground:is(.dark *):where([data-state=active]),.dark\\:data-active\\:text-foreground:is(.dark *):where([data-active]:not([data-active=false])){color:var(--foreground)}.dark\\:group-data-\\[variant\\=line\\]\\/tabs-list\\:data-active\\:border-transparent:is(.dark *):is(:where(.group\\/tabs-list)[data-variant=line] *):where([data-state=active]),.dark\\:group-data-\\[variant\\=line\\]\\/tabs-list\\:data-active\\:border-transparent:is(.dark *):is(:where(.group\\/tabs-list)[data-variant=line] *):where([data-active]:not([data-active=false])){border-color:#0000}.dark\\:group-data-\\[variant\\=line\\]\\/tabs-list\\:data-active\\:bg-transparent:is(.dark *):is(:where(.group\\/tabs-list)[data-variant=line] *):where([data-state=active]),.dark\\:group-data-\\[variant\\=line\\]\\/tabs-list\\:data-active\\:bg-transparent:is(.dark *):is(:where(.group\\/tabs-list)[data-variant=line] *):where([data-active]:not([data-active=false])){background-color:#0000}.data-horizontal\\:h-px:where([data-orientation=horizontal]){height:1px}.data-horizontal\\:w-full:where([data-orientation=horizontal]){width:100%}.data-horizontal\\:flex-col:where([data-orientation=horizontal]){flex-direction:column}.data-vertical\\:w-px:where([data-orientation=vertical]){width:1px}.data-vertical\\:flex-col:where([data-orientation=vertical]){flex-direction:column}.data-vertical\\:items-stretch:where([data-orientation=vertical]){align-items:stretch}.data-vertical\\:self-stretch:where([data-orientation=vertical]){align-self:stretch}.\\[\\&_a\\]\\:underline a{text-decoration-line:underline}.\\[\\&_a\\]\\:underline-offset-3 a{text-underline-offset:3px}@media (hover:hover){.\\[\\&_a\\]\\:hover\\:text-foreground a:hover{color:var(--foreground)}}.\\[\\&_p\\:not\\(\\:last-child\\)\\]\\:mb-4 p:not(:last-child){margin-bottom:calc(var(--spacing) * 4)}.\\[\\&_svg\\]\\:pointer-events-none svg{pointer-events:none}.\\[\\&_svg\\]\\:size-4 svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\\[\\&_svg\\]\\:shrink-0 svg{flex-shrink:0}.\\[\\&_svg\\:not\\(\\[class\\*\\=\\'size-\\'\\]\\)\\]\\:size-3 svg:not([class*=size-]){width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.\\[\\&_svg\\:not\\(\\[class\\*\\=\\'size-\\'\\]\\)\\]\\:size-4 svg:not([class*=size-]){width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\\[\\&_tr\\]\\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.\\[\\&_tr\\:last-child\\]\\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.\\[\\&\\:has\\(\\[role\\=checkbox\\]\\)\\]\\:pr-0:has([role=checkbox]){padding-right:0}.\\[\\.border-b\\]\\:pb-\\(--card-spacing\\).border-b{padding-bottom:var(--card-spacing)}.\\[\\.border-t\\]\\:pt-\\(--card-spacing\\).border-t{padding-top:var(--card-spacing)}:is(.\\*\\:\\[a\\]\\:underline>*):is(a){text-decoration-line:underline}:is(.\\*\\:\\[a\\]\\:underline-offset-3>*):is(a){text-underline-offset:3px}@media (hover:hover){.\\[a\\]\\:hover\\:bg-destructive\\/20:is(a):hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.\\[a\\]\\:hover\\:bg-destructive\\/20:is(a):hover{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.\\[a\\]\\:hover\\:bg-muted:is(a):hover{background-color:var(--muted)}.\\[a\\]\\:hover\\:bg-primary\\/80:is(a):hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.\\[a\\]\\:hover\\:bg-primary\\/80:is(a):hover{background-color:color-mix(in oklab, var(--primary) 80%, transparent)}}.\\[a\\]\\:hover\\:bg-secondary\\/80:is(a):hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){.\\[a\\]\\:hover\\:bg-secondary\\/80:is(a):hover{background-color:color-mix(in oklab, var(--secondary) 80%, transparent)}}.\\[a\\]\\:hover\\:text-muted-foreground:is(a):hover{color:var(--muted-foreground)}:is(.\\*\\:\\[a\\]\\:hover\\:text-foreground>*):is(a):hover{color:var(--foreground)}}:is(.\\*\\:\\[img\\:first-child\\]\\:rounded-t-xl>*):is(img:first-child){border-top-left-radius:calc(var(--radius) * 1.4);border-top-right-radius:calc(var(--radius) * 1.4)}:is(.\\*\\:\\[img\\:last-child\\]\\:rounded-b-xl>*):is(img:last-child){border-bottom-right-radius:calc(var(--radius) * 1.4);border-bottom-left-radius:calc(var(--radius) * 1.4)}:is(.\\*\\:\\[svg\\]\\:row-span-2>*):is(svg){grid-row:span 2/span 2}:is(.\\*\\:\\[svg\\]\\:translate-y-0\\.5>*):is(svg){--tw-translate-y:calc(var(--spacing) * .5);translate:var(--tw-translate-x) var(--tw-translate-y)}:is(.\\*\\:\\[svg\\]\\:text-current>*):is(svg){color:currentColor}:is(.data-\\[variant\\=destructive\\]\\:\\*\\:\\[svg\\]\\:text-destructive[data-variant=destructive]>*):is(svg){color:var(--destructive)}:is(.\\*\\:\\[svg\\:not\\(\\[class\\*\\=\\'size-\\'\\]\\)\\]\\:size-4>*):is(svg:not([class*=size-])){width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}:is(.\\*\\:\\[svg\\:not\\(\\[class\\*\\=\\'size-\\'\\]\\)\\]\\:size-8>*):is(svg:not([class*=size-])){width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.\\[\\&\\>\\.sr-only\\]\\:w-auto>.sr-only{width:auto}.has-\\[\\>\\[data-slot\\=field-content\\]\\]\\:\\[\\&\\>\\[role\\=checkbox\\]\\,\\[role\\=radio\\]\\]\\:mt-px:has(>[data-slot=field-content])>[role=checkbox],.has-\\[\\>\\[data-slot\\=field-content\\]\\]\\:\\[\\&\\>\\[role\\=checkbox\\]\\,\\[role\\=radio\\]\\]\\:mt-px:has(>[data-slot=field-content]) [role=radio]{margin-top:1px}@container field-group (min-width:28rem){.\\@md\\/field-group\\:has-\\[\\>\\[data-slot\\=field-content\\]\\]\\:\\[\\&\\>\\[role\\=checkbox\\]\\,\\[role\\=radio\\]\\]\\:mt-px:has(>[data-slot=field-content])>[role=checkbox],.\\@md\\/field-group\\:has-\\[\\>\\[data-slot\\=field-content\\]\\]\\:\\[\\&\\>\\[role\\=checkbox\\]\\,\\[role\\=radio\\]\\]\\:mt-px:has(>[data-slot=field-content]) [role=radio]{margin-top:1px}}.\\[\\&\\>a\\]\\:underline>a{text-decoration-line:underline}.\\[\\&\\>a\\]\\:underline-offset-4>a{text-underline-offset:4px}.\\[\\&\\>a\\:hover\\]\\:text-primary>a:hover{color:var(--primary)}.\\[\\&\\>button\\]\\:hidden>button{display:none}.\\[\\&\\>span\\:last-child\\]\\:truncate>span:last-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.\\[\\&\\>svg\\]\\:pointer-events-none>svg{pointer-events:none}.\\[\\&\\>svg\\]\\:size-3\\!>svg{width:calc(var(--spacing) * 3)!important;height:calc(var(--spacing) * 3)!important}.\\[\\&\\>svg\\]\\:size-3\\.5>svg{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.\\[\\&\\>svg\\]\\:size-4>svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\\[\\&\\>svg\\]\\:shrink-0>svg{flex-shrink:0}.\\[\\&\\>svg\\]\\:text-sidebar-accent-foreground>svg{color:var(--sidebar-accent-foreground)}.group-has-data-\\[size\\=lg\\]\\/avatar-group\\:\\[\\&\\>svg\\]\\:size-5:is(:where(.group\\/avatar-group):has([data-size=lg]) *)>svg{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.group-has-data-\\[size\\=sm\\]\\/avatar-group\\:\\[\\&\\>svg\\]\\:size-3:is(:where(.group\\/avatar-group):has([data-size=sm]) *)>svg{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.group-data-\\[size\\=default\\]\\/avatar\\:\\[\\&\\>svg\\]\\:size-2:is(:where(.group\\/avatar)[data-size=default] *)>svg,.group-data-\\[size\\=lg\\]\\/avatar\\:\\[\\&\\>svg\\]\\:size-2:is(:where(.group\\/avatar)[data-size=lg] *)>svg{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.group-data-\\[size\\=sm\\]\\/avatar\\:\\[\\&\\>svg\\]\\:hidden:is(:where(.group\\/avatar)[data-size=sm] *)>svg{display:none}.\\[\\&\\>tr\\]\\:last\\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}[data-side=left][data-collapsible=offcanvas] .\\[\\[data-side\\=left\\]\\[data-collapsible\\=offcanvas\\]_\\&\\]\\:-right-2{right:calc(var(--spacing) * -2)}[data-side=left][data-state=collapsed] .\\[\\[data-side\\=left\\]\\[data-state\\=collapsed\\]_\\&\\]\\:cursor-e-resize{cursor:e-resize}[data-side=right][data-collapsible=offcanvas] .\\[\\[data-side\\=right\\]\\[data-collapsible\\=offcanvas\\]_\\&\\]\\:-left-2{left:calc(var(--spacing) * -2)}[data-side=right][data-state=collapsed] .\\[\\[data-side\\=right\\]\\[data-state\\=collapsed\\]_\\&\\]\\:cursor-w-resize{cursor:w-resize}[data-variant=legend]+.\\[\\[data-variant\\=legend\\]\\+\\&\\]\\:-mt-1\\.5{margin-top:calc(var(--spacing) * -1.5)}.animate-dialog{opacity:var(--tw-anim-dialog-start-opacity,0);translate:var(--tw-anim-dialog-start-translate,0 1.5rem);scale:var(--tw-anim-dialog-start-scale,.96);will-change:opacity, translate, scale;transition:opacity var(--tw-anim-dialog-duration,.35s) ease, translate var(--tw-anim-dialog-duration,.35s) var(--tw-anim-dialog-easing,cubic-bezier(.22, 1, .36, 1)), scale var(--tw-anim-dialog-duration,.35s) var(--tw-anim-dialog-easing,cubic-bezier(.22, 1, .36, 1)), overlay var(--tw-anim-dialog-duration,.35s) ease allow-discrete, display var(--tw-anim-dialog-duration,.35s) ease allow-discrete}.animate-dialog[open]{opacity:1;translate:0;scale:1}.animate-dialog[open].is-closing,.animate-dialog[open].is-opening{opacity:var(--tw-anim-dialog-start-opacity,0);translate:var(--tw-anim-dialog-start-translate,0 1.5rem);scale:var(--tw-anim-dialog-start-scale,.96)}@starting-style{.animate-dialog[open]{opacity:var(--tw-anim-dialog-start-opacity,0);translate:var(--tw-anim-dialog-start-translate,0 1.5rem);scale:var(--tw-anim-dialog-start-scale,.96)}}.animate-dialog::backdrop{opacity:0;background:var(--tw-anim-dialog-backdrop-background,#0f172a94);-webkit-backdrop-filter:var(--tw-anim-dialog-backdrop-filter,blur(8px));backdrop-filter:var(--tw-anim-dialog-backdrop-filter,blur(8px));transition:opacity var(--tw-anim-dialog-duration,.35s) ease, overlay var(--tw-anim-dialog-duration,.35s) ease allow-discrete, display var(--tw-anim-dialog-duration,.35s) ease allow-discrete}.animate-dialog[open]::backdrop{opacity:1}.animate-dialog[open].is-closing::backdrop,.animate-dialog[open].is-opening::backdrop{opacity:0}@starting-style{.animate-dialog[open]::backdrop{opacity:0}}}@media (prefers-reduced-motion:reduce){.shimmer{-webkit-text-fill-color:currentColor;background-image:none;animation:none}}:host{--background:oklch(100% 0 0);--foreground:oklch(14.8% .004 228.8);--card:oklch(100% 0 0);--card-foreground:oklch(14.8% .004 228.8);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.8% .004 228.8);--primary:oklch(84.1% .238 128.85);--link:oklch(45.3% .124 130.933);--primary-foreground:oklch(40.5% .101 131.063);--secondary:oklch(96.7% .001 286.375);--secondary-foreground:oklch(21% .006 285.885);--muted:oklch(96.3% .002 197.1);--muted-foreground:oklch(56% .021 213.5);--accent:oklch(96.3% .002 197.1);--accent-foreground:oklch(21.8% .008 223.9);--destructive:oklch(57.7% .245 27.325);--border:oklch(92.5% .005 214.3);--input:oklch(92.5% .005 214.3);--ring:oklch(72.3% .014 214.4);--chart-1:oklch(89.7% .196 126.665);--chart-2:oklch(76.8% .233 130.85);--chart-3:oklch(64.8% .2 131.684);--chart-4:oklch(53.2% .157 131.589);--chart-5:oklch(45.3% .124 130.933);--radius:.625rem;--sidebar:oklch(98.7% .002 197.1);--sidebar-foreground:oklch(14.8% .004 228.8);--sidebar-primary:oklch(64.8% .2 131.684);--sidebar-primary-foreground:oklch(98.6% .031 120.757);--sidebar-accent:oklch(96.3% .002 197.1);--sidebar-accent-foreground:oklch(21.8% .008 223.9);--sidebar-border:oklch(92.5% .005 214.3);--sidebar-ring:oklch(72.3% .014 214.4)}.dark{--background:oklch(14.8% .004 228.8);--foreground:oklch(98.7% .002 197.1);--card:oklch(21.8% .008 223.9);--card-foreground:oklch(98.7% .002 197.1);--popover:oklch(21.8% .008 223.9);--popover-foreground:oklch(98.7% .002 197.1);--primary:oklch(76.8% .233 130.85);--link:var(--primary);--primary-foreground:oklch(40.5% .101 131.063);--secondary:oklch(27.4% .006 286.033);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(27.5% .011 216.9);--muted-foreground:oklch(72.3% .014 214.4);--accent:oklch(27.5% .011 216.9);--accent-foreground:oklch(98.7% .002 197.1);--destructive:oklch(70.4% .191 22.216);--border:oklch(100% 0 0/.1);--input:oklch(100% 0 0/.15);--ring:oklch(56% .021 213.5);--chart-1:oklch(89.7% .196 126.665);--chart-2:oklch(76.8% .233 130.85);--chart-3:oklch(64.8% .2 131.684);--chart-4:oklch(53.2% .157 131.589);--chart-5:oklch(45.3% .124 130.933);--sidebar:oklch(21.8% .008 223.9);--sidebar-foreground:oklch(98.7% .002 197.1);--sidebar-primary:oklch(76.8% .233 130.85);--sidebar-primary-foreground:oklch(27.4% .072 132.109);--sidebar-accent:oklch(27.5% .011 216.9);--sidebar-accent-foreground:oklch(98.7% .002 197.1);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(56% .021 213.5)}:host{--font-sans:ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;font-family:var(--font-sans);color:var(--foreground);-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5}.fb-or{transition:opacity .3s}[data-named] .fb-or{opacity:0}.fb-glow,.fb-halo{border-radius:inherit;z-index:-1;pointer-events:none;opacity:0;transition:opacity .3s;position:absolute;inset:-2px;overflow:hidden}.fb-halo{filter:blur(14px);inset:-3px}[data-glow] .fb-glow{opacity:1}[data-glow] .fb-halo{opacity:.75}.fb-glow:before,.fb-halo:before{content:\"\";aspect-ratio:1;background:conic-gradient(#0000,#ff3b30 4%,#ff9500 7%,#fc0 10%,#34c759 13%,#007aff 16%,#af52de 19%,#0000 23%);width:max(200%,480px);animation:4s linear infinite paused fb-spin;position:absolute;top:50%;left:50%;translate:-50% -50%}[data-glow] .fb-glow:before,[data-glow] .fb-halo:before{animation-play-state:running}.fb-glow:after{content:\"\";border-radius:inherit;background:var(--background);position:absolute;inset:2px}@keyframes fb-spin{to{rotate:360deg}}.fb-surface{animation:.25s ease-out backwards fb-expand}[data-leaving] .fb-surface{animation:.25s ease-in .15s both fb-collapse}@keyframes fb-expand{0%{clip-path:inset(0 0 0 calc(100% - 52px) round 9999px)}to{clip-path:inset(0 round 9999px)}}@keyframes fb-collapse{0%{clip-path:inset(0 round 9999px)}20%{opacity:1}to{clip-path:inset(0 0 0 calc(100% - 52px) round 9999px);opacity:0}}.selection-outline{z-index:-1;border:2px solid var(--chart-3);background:var(--primary);position:fixed}@supports (color:color-mix(in lab, red, red)){.selection-outline{background:color-mix(in oklch, var(--primary) 12%, transparent)}}.selection-outline{pointer-events:none;border-radius:4px}@media (prefers-reduced-motion:reduce){.fb-or,.fb-glow,.fb-halo{transition:none}.fb-glow:before,.fb-halo:before,.fb-surface,[data-leaving] .fb-surface{animation:none}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@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));filter:blur(var(--tw-enter-blur,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));filter:blur(var(--tw-exit-blur,0))}}@keyframes accordion-down{0%{height:0}to{height:var(--radix-accordion-content-height,var(--accordion-panel-height,auto))}}@keyframes accordion-up{0%{height:var(--radix-accordion-content-height,var(--accordion-panel-height,auto))}to{height:0}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-out{0%{opacity:1}to{opacity:0}}@keyframes fade-in-left{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}@keyframes fade-out-right{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(20px)}}@keyframes flip-in-x{0%{opacity:0;transform:rotateY(90deg)}to{opacity:1;transform:rotateY(0)}}\n";
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region ../ui/src/components/badge.tsx
|
|
21
|
+
const badgeVariants = cva("group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", {
|
|
22
|
+
variants: { variant: {
|
|
23
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
24
|
+
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
25
|
+
destructive: "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
|
26
|
+
outline: "border-border bg-input/30 text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
27
|
+
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
28
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
29
|
+
} },
|
|
30
|
+
defaultVariants: { variant: "default" }
|
|
31
|
+
});
|
|
32
|
+
function Badge({ className, variant = "default", render, ...props }) {
|
|
33
|
+
return useRender({
|
|
34
|
+
defaultTagName: "span",
|
|
35
|
+
props: mergeProps({ className: cn(badgeVariants({ variant }), className) }, props),
|
|
36
|
+
render,
|
|
37
|
+
state: {
|
|
38
|
+
slot: "badge",
|
|
39
|
+
variant
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/frame.ts
|
|
45
|
+
const PAINT_INTERVAL_MS = 32;
|
|
46
|
+
function createFrameLoop(signal) {
|
|
47
|
+
const callbacks = /* @__PURE__ */ new Set();
|
|
48
|
+
let last = 0;
|
|
49
|
+
let frame = requestAnimationFrame(function paint(time) {
|
|
50
|
+
if (time - last > PAINT_INTERVAL_MS) {
|
|
51
|
+
last = time;
|
|
52
|
+
for (const callback of callbacks) callback();
|
|
53
|
+
}
|
|
54
|
+
frame = requestAnimationFrame(paint);
|
|
55
|
+
});
|
|
56
|
+
signal.addEventListener("abort", () => cancelAnimationFrame(frame));
|
|
57
|
+
return { add(callback) {
|
|
58
|
+
callbacks.add(callback);
|
|
59
|
+
return () => callbacks.delete(callback);
|
|
60
|
+
} };
|
|
61
|
+
}
|
|
62
|
+
const FrameContext = createContext(null);
|
|
63
|
+
function useFrame(callback) {
|
|
64
|
+
const loop = useContext(FrameContext);
|
|
65
|
+
const latest = useRef(callback);
|
|
66
|
+
useLayoutEffect(() => {
|
|
67
|
+
latest.current = callback;
|
|
68
|
+
callback();
|
|
69
|
+
});
|
|
70
|
+
useEffect(() => loop?.add(() => latest.current()), [loop]);
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/badges.tsx
|
|
74
|
+
const shownAnchor = (thread) => thread.suggestion?.anchor ?? thread.anchor;
|
|
75
|
+
function Badges({ groups, activeId, open }) {
|
|
76
|
+
const nodes = useRef(/* @__PURE__ */ new Map());
|
|
77
|
+
const outline = useRef(null);
|
|
78
|
+
const [hovered, setHovered] = useState(null);
|
|
79
|
+
useFrame(() => {
|
|
80
|
+
const boxes = new Map(groups.map((group) => {
|
|
81
|
+
const box = anchorBox(group.anchor, group.target);
|
|
82
|
+
return [group.key, box && !isCovered(group.target, box) ? box : null];
|
|
83
|
+
}));
|
|
84
|
+
for (const [key, box] of boxes) {
|
|
85
|
+
const node = nodes.current.get(key);
|
|
86
|
+
if (!node) continue;
|
|
87
|
+
node.hidden = !box;
|
|
88
|
+
if (!box) continue;
|
|
89
|
+
const x = Math.max(0, Math.min(innerWidth - 30, box.x + box.width - 8));
|
|
90
|
+
node.style.left = `${x}px`;
|
|
91
|
+
node.style.top = `${Math.max(0, box.y - 12)}px`;
|
|
92
|
+
}
|
|
93
|
+
const lit = groups.find((group) => group.key === hovered) ?? groups.find((group) => group.threads.some((thread) => thread.id === activeId));
|
|
94
|
+
if (outline.current) placeBox(outline.current, (lit && boxes.get(lit.key)) ?? null);
|
|
95
|
+
});
|
|
96
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
97
|
+
ref: outline,
|
|
98
|
+
hidden: true,
|
|
99
|
+
className: "selection-outline"
|
|
100
|
+
}), groups.map((group) => {
|
|
101
|
+
const [first] = group.threads;
|
|
102
|
+
if (!first) return null;
|
|
103
|
+
const many = group.threads.length > 1;
|
|
104
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
105
|
+
type: "button",
|
|
106
|
+
ref: (node) => {
|
|
107
|
+
if (node) nodes.current.set(group.key, node);
|
|
108
|
+
else nodes.current.delete(group.key);
|
|
109
|
+
},
|
|
110
|
+
hidden: true,
|
|
111
|
+
"aria-label": (many ? `${group.threads.length} comment threads` : `Comment ${first.number}`) + (group.moved ? ", moved" : ""),
|
|
112
|
+
onClick: () => open(group.threads),
|
|
113
|
+
onPointerEnter: () => setHovered(group.key),
|
|
114
|
+
onPointerLeave: () => setHovered((key) => key === group.key ? null : key),
|
|
115
|
+
className: cn("pointer-events-auto fixed grid h-7 min-w-7 cursor-pointer place-items-center rounded-[14px_14px_14px_3px] border-2 border-background bg-primary px-1.5 text-xs font-bold text-primary-foreground shadow-md hover:bg-primary/80", group.moved && "outline-2 outline-offset-2 outline-primary outline-dashed"),
|
|
116
|
+
children: [many ? group.threads.length : first.number, group.moved && /* @__PURE__ */ jsx(Badge, {
|
|
117
|
+
variant: "secondary",
|
|
118
|
+
className: "absolute top-1/2 right-full mr-1.5 -translate-y-1/2 shadow-md",
|
|
119
|
+
children: "Moved"
|
|
120
|
+
})]
|
|
121
|
+
}, group.key);
|
|
122
|
+
})] });
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region ../ui/src/components/button.tsx
|
|
126
|
+
const buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-4xl border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
|
|
127
|
+
variants: {
|
|
128
|
+
variant: {
|
|
129
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
130
|
+
outline: "border-border bg-input/30 hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
131
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
132
|
+
ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
133
|
+
destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
134
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
135
|
+
},
|
|
136
|
+
size: {
|
|
137
|
+
default: "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
|
|
138
|
+
xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
|
|
139
|
+
sm: "h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
140
|
+
lg: "h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
|
|
141
|
+
icon: "size-9",
|
|
142
|
+
"icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3",
|
|
143
|
+
"icon-sm": "size-8",
|
|
144
|
+
"icon-lg": "size-10"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
defaultVariants: {
|
|
148
|
+
variant: "default",
|
|
149
|
+
size: "default"
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
function Button$1({ className, variant = "default", size = "default", ...props }) {
|
|
153
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
154
|
+
"data-slot": "button",
|
|
155
|
+
className: cn(buttonVariants({
|
|
156
|
+
variant,
|
|
157
|
+
size,
|
|
158
|
+
className
|
|
159
|
+
})),
|
|
160
|
+
...props
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region ../ui/src/components/input.tsx
|
|
165
|
+
function Input$1({ className, type, ...props }) {
|
|
166
|
+
return /* @__PURE__ */ jsx(Input, {
|
|
167
|
+
type,
|
|
168
|
+
"data-slot": "input",
|
|
169
|
+
className: cn("h-9 w-full min-w-0 rounded-4xl border border-input bg-input/30 px-3 py-1 text-base transition-colors outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 md:text-sm dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className),
|
|
170
|
+
...props
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region src/logo.tsx
|
|
175
|
+
const WORDMARK = "M7 0V-71H56V-58H14L23 -67V-34L14 -41H55V-28H14L23 -35V0ZM78 1Q69 1 64 -3Q59 -7 59 -13Q59 -20 63 -24Q68 -28 77 -30L94 -33Q94 -39 92 -41Q89 -44 85 -44Q81 -44 78 -42Q76 -40 75 -36L60 -37Q61 -46 68 -50Q74 -55 85 -55Q97 -55 103 -49Q109 -43 109 -32V-14Q109 -12 110 -11Q110 -10 112 -10H113V0Q113 0 111 0Q110 1 108 1Q105 1 102 -1Q99 -2 98 -4Q96 -7 96 -12L97 -11Q96 -7 94 -5Q91 -2 87 0Q83 1 78 1ZM81 -9Q85 -9 88 -11Q91 -12 92 -15Q94 -18 94 -21V-24L82 -21Q78 -21 76 -19Q74 -17 74 -15Q74 -12 76 -11Q78 -9 81 -9ZM141 1Q132 1 127 -1Q121 -3 118 -8Q115 -12 115 -17L130 -17Q131 -14 133 -12Q136 -9 141 -9Q145 -9 147 -11Q149 -12 149 -15Q149 -16 148 -18Q148 -19 145 -20Q143 -20 138 -21Q130 -23 125 -25Q120 -27 118 -30Q116 -33 116 -38Q116 -45 122 -50Q128 -55 140 -55Q148 -55 153 -52Q158 -50 161 -46Q164 -42 164 -36L149 -36Q149 -38 148 -40Q146 -42 144 -43Q143 -44 140 -44Q136 -44 134 -43Q132 -41 132 -38Q132 -36 133 -35Q134 -34 136 -33Q138 -32 142 -32Q150 -30 155 -28Q160 -26 162 -23Q165 -20 165 -15Q165 -10 162 -6Q159 -3 153 -1Q148 1 141 1ZM192 0Q184 0 180 -4Q176 -8 176 -16V-66H191V-18Q191 -14 193 -13Q194 -11 198 -11H205V0ZM167 -42V-54H205V-42ZM242 1Q236 1 232 -1Q228 -4 226 -8L226 0H211V-71H226V-46Q228 -50 233 -52Q237 -55 242 -55Q249 -55 254 -51Q259 -48 262 -42Q264 -35 264 -27Q264 -18 262 -12Q259 -6 254 -2Q249 1 242 1ZM238 -10Q243 -10 246 -15Q249 -19 249 -27Q249 -35 246 -39Q243 -43 238 -43Q234 -43 232 -41Q229 -39 228 -36Q226 -32 226 -27Q226 -22 228 -18Q229 -14 232 -12Q234 -10 238 -10ZM288 1Q279 1 274 -3Q269 -7 269 -13Q269 -20 273 -24Q278 -28 287 -30L304 -33Q304 -39 301 -41Q299 -44 295 -44Q290 -44 288 -42Q286 -40 285 -36L270 -37Q271 -46 278 -50Q284 -55 295 -55Q307 -55 313 -49Q319 -43 319 -32V-14Q319 -12 320 -11Q320 -10 322 -10H323V0Q323 0 321 0Q319 1 318 1Q315 1 312 -1Q309 -2 308 -4Q306 -7 306 -12L307 -11Q306 -7 304 -5Q301 -2 297 0Q293 1 288 1ZM291 -9Q295 -9 298 -11Q301 -12 302 -15Q304 -18 304 -21V-24L292 -21Q288 -21 286 -19Q284 -17 284 -15Q284 -12 286 -11Q288 -9 291 -9ZM352 1Q344 1 338 -2Q332 -6 328 -12Q325 -18 325 -27Q325 -35 328 -42Q332 -48 338 -51Q344 -55 352 -55Q359 -55 365 -52Q370 -50 374 -45Q377 -40 378 -34L363 -33Q362 -38 359 -41Q356 -43 352 -43Q346 -43 343 -39Q340 -35 340 -27Q340 -19 343 -15Q346 -10 352 -10Q356 -10 359 -13Q362 -16 363 -21L378 -20Q377 -14 374 -9Q371 -4 365 -1Q359 1 352 1ZM384 0V-71H399V-32L419 -54H437L416 -32L438 0H421L406 -23L399 -15V0ZM442 0V-54H456L457 -38L456 -38Q457 -46 460 -50Q463 -54 469 -54H474V-41H469Q465 -41 462 -40Q460 -39 458 -37Q457 -34 457 -30V0Z";
|
|
176
|
+
function Logo({ className }) {
|
|
177
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
178
|
+
viewBox: "0 -71 567 72",
|
|
179
|
+
role: "img",
|
|
180
|
+
"aria-label": "Fastbackr",
|
|
181
|
+
className,
|
|
182
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
183
|
+
cx: "28",
|
|
184
|
+
cy: "-35.5",
|
|
185
|
+
r: "28",
|
|
186
|
+
fill: "var(--primary)"
|
|
187
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
188
|
+
transform: "translate(93 0)",
|
|
189
|
+
d: WORDMARK,
|
|
190
|
+
fill: "currentColor"
|
|
191
|
+
})]
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/bar.tsx
|
|
196
|
+
const GLOW_MS = 10250;
|
|
197
|
+
const FLASH_MS = 2e3;
|
|
198
|
+
const ANONYMOUS_NAME = "Anonymous";
|
|
199
|
+
const CLOSE_TITLE = "Close Fastbackr in this browser. Press f three times to bring it back.";
|
|
200
|
+
function Bar(props) {
|
|
201
|
+
const [leaving, setLeaving] = useState(false);
|
|
202
|
+
const [flashing, setFlashing] = useState(false);
|
|
203
|
+
const seenFlash = useRef(props.flashRequest);
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (props.flashRequest === seenFlash.current) return;
|
|
206
|
+
seenFlash.current = props.flashRequest;
|
|
207
|
+
setFlashing(true);
|
|
208
|
+
const timer = setTimeout(() => setFlashing(false), FLASH_MS);
|
|
209
|
+
return () => clearTimeout(timer);
|
|
210
|
+
}, [props.flashRequest]);
|
|
211
|
+
const minimize = () => {
|
|
212
|
+
if (matchMedia("(prefers-reduced-motion: reduce)").matches) props.minimize();
|
|
213
|
+
else {
|
|
214
|
+
setLeaving(true);
|
|
215
|
+
props.showFab();
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
const shared = {
|
|
219
|
+
...props,
|
|
220
|
+
minimize,
|
|
221
|
+
leaving,
|
|
222
|
+
flashing,
|
|
223
|
+
collapsed: props.minimize
|
|
224
|
+
};
|
|
225
|
+
return props.reviewer ? /* @__PURE__ */ jsx(SignedIn, {
|
|
226
|
+
...shared,
|
|
227
|
+
reviewer: props.reviewer
|
|
228
|
+
}) : /* @__PURE__ */ jsx(SignedOut, { ...shared });
|
|
229
|
+
}
|
|
230
|
+
const BAR = "pointer-events-auto fixed right-4 bottom-4 flex max-w-[calc(100vw-2rem)] items-center gap-2 rounded-full p-2 sm:right-[22px] sm:bottom-[22px]";
|
|
231
|
+
const ENTER = "animate-fade-in-left animate-duration-250 animate-delay-250 motion-reduce:animate-none";
|
|
232
|
+
const LEAVE = "animate-fade-out-right animate-duration-150 motion-reduce:animate-none";
|
|
233
|
+
function navProps({ leaving, flashing }, glow = false) {
|
|
234
|
+
return {
|
|
235
|
+
"aria-label": "Fastbackr bar",
|
|
236
|
+
"data-leaving": leaving || void 0,
|
|
237
|
+
"data-glow": glow || flashing || void 0,
|
|
238
|
+
className: cn(BAR, leaving && "pointer-events-none")
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function Glow({ leaving }) {
|
|
242
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
243
|
+
"aria-hidden": "true",
|
|
244
|
+
className: cn("absolute inset-0 -z-1 rounded-full motion-reduce:animate-none", leaving ? "animate-fade-out animate-duration-150" : "animate-fade-in animate-duration-250 animate-delay-250"),
|
|
245
|
+
children: [/* @__PURE__ */ jsx("div", { className: "fb-halo" }), /* @__PURE__ */ jsx("div", { className: "fb-glow" })]
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
function Surface({ leaving, collapsed }) {
|
|
249
|
+
return /* @__PURE__ */ jsx("div", {
|
|
250
|
+
"aria-hidden": "true",
|
|
251
|
+
className: "fb-surface absolute inset-0 -z-10 rounded-full bg-background shadow-xl ring-1 ring-foreground/10",
|
|
252
|
+
onAnimationEnd: (event) => {
|
|
253
|
+
if (leaving && event.animationName === "fb-collapse" && event.target === event.currentTarget) collapsed();
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function Author({ name, verified, strong = false }) {
|
|
258
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
259
|
+
className: "inline-flex min-w-0 items-center gap-1.5",
|
|
260
|
+
children: [strong ? /* @__PURE__ */ jsx("strong", {
|
|
261
|
+
className: "truncate font-semibold",
|
|
262
|
+
children: name
|
|
263
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
264
|
+
className: "truncate",
|
|
265
|
+
children: name
|
|
266
|
+
}), !verified && /* @__PURE__ */ jsx(Badge, {
|
|
267
|
+
variant: "outline",
|
|
268
|
+
children: "guest"
|
|
269
|
+
})]
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
function WindowButtons({ minimize, close, size, className, iconClassName }) {
|
|
273
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button$1, {
|
|
274
|
+
variant: "ghost",
|
|
275
|
+
size,
|
|
276
|
+
className,
|
|
277
|
+
"aria-label": "Minimize",
|
|
278
|
+
title: "Minimize",
|
|
279
|
+
onClick: minimize,
|
|
280
|
+
children: /* @__PURE__ */ jsx(RiArrowDownSLine, {
|
|
281
|
+
"aria-hidden": "true",
|
|
282
|
+
className: iconClassName
|
|
283
|
+
})
|
|
284
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
285
|
+
variant: "ghost",
|
|
286
|
+
size,
|
|
287
|
+
className,
|
|
288
|
+
"aria-label": "Close",
|
|
289
|
+
title: CLOSE_TITLE,
|
|
290
|
+
onClick: close,
|
|
291
|
+
children: /* @__PURE__ */ jsx(RiCloseLine, {
|
|
292
|
+
"aria-hidden": "true",
|
|
293
|
+
className: iconClassName
|
|
294
|
+
})
|
|
295
|
+
})] });
|
|
296
|
+
}
|
|
297
|
+
const PILL = "h-4 rounded-full bg-background shadow-md ring-1 ring-foreground/10";
|
|
298
|
+
const TAB = `${PILL} hover:bg-muted`;
|
|
299
|
+
function TopRow(props) {
|
|
300
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
301
|
+
className: cn("pointer-events-none absolute inset-x-0 bottom-[calc(100%+6px)] flex items-center justify-between gap-1 *:pointer-events-auto", props.leaving ? LEAVE : ENTER),
|
|
302
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
303
|
+
className: `${PILL} flex items-center px-1.5`,
|
|
304
|
+
children: /* @__PURE__ */ jsx(Logo, { className: "h-2.5 w-auto" })
|
|
305
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
306
|
+
className: "flex gap-1",
|
|
307
|
+
children: [props.signIn && /* @__PURE__ */ jsx(Button$1, {
|
|
308
|
+
variant: "ghost",
|
|
309
|
+
className: `${TAB} px-1.5 text-[10px]`,
|
|
310
|
+
onClick: props.signIn,
|
|
311
|
+
children: "Sign in"
|
|
312
|
+
}), /* @__PURE__ */ jsx(WindowButtons, {
|
|
313
|
+
...props,
|
|
314
|
+
size: "icon-xs",
|
|
315
|
+
className: `${TAB} size-4`,
|
|
316
|
+
iconClassName: "size-3!"
|
|
317
|
+
})]
|
|
318
|
+
})]
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
function Identity({ reviewer, changeName }) {
|
|
322
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
323
|
+
"aria-hidden": "true",
|
|
324
|
+
className: "grid size-6 shrink-0 place-items-center rounded-full bg-muted text-xs font-semibold text-muted-foreground uppercase",
|
|
325
|
+
children: reviewer.name.trim().charAt(0) || "?"
|
|
326
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
327
|
+
className: "truncate",
|
|
328
|
+
children: reviewer.name
|
|
329
|
+
})] });
|
|
330
|
+
const className = "max-w-40 gap-2 pr-3 pl-1.5";
|
|
331
|
+
return reviewer.verified ? /* @__PURE__ */ jsx("span", {
|
|
332
|
+
className: cn(className, "inline-flex h-9 min-w-0 items-center text-sm font-medium"),
|
|
333
|
+
title: `Signed in as ${reviewer.name}`,
|
|
334
|
+
children: content
|
|
335
|
+
}) : /* @__PURE__ */ jsxs(Button$1, {
|
|
336
|
+
variant: "ghost",
|
|
337
|
+
className: cn(className, "min-w-0"),
|
|
338
|
+
title: "Change name",
|
|
339
|
+
"aria-label": `${reviewer.name}, change name`,
|
|
340
|
+
onClick: changeName,
|
|
341
|
+
children: [content, /* @__PURE__ */ jsx(RiPencilLine, {
|
|
342
|
+
"aria-hidden": "true",
|
|
343
|
+
className: "text-muted-foreground"
|
|
344
|
+
})]
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
function SignedIn(props) {
|
|
348
|
+
const { reviewer, count, leaving } = props;
|
|
349
|
+
return /* @__PURE__ */ jsxs("nav", {
|
|
350
|
+
...navProps(props),
|
|
351
|
+
children: [
|
|
352
|
+
/* @__PURE__ */ jsx(Surface, { ...props }),
|
|
353
|
+
/* @__PURE__ */ jsx(Glow, { leaving }),
|
|
354
|
+
/* @__PURE__ */ jsx(TopRow, {
|
|
355
|
+
leaving,
|
|
356
|
+
minimize: props.minimize,
|
|
357
|
+
close: props.close
|
|
358
|
+
}),
|
|
359
|
+
/* @__PURE__ */ jsxs("div", {
|
|
360
|
+
className: cn("flex min-w-0 items-center gap-2", leaving ? LEAVE : ENTER),
|
|
361
|
+
children: [
|
|
362
|
+
/* @__PURE__ */ jsx(Identity, {
|
|
363
|
+
reviewer,
|
|
364
|
+
changeName: props.changeName
|
|
365
|
+
}),
|
|
366
|
+
/* @__PURE__ */ jsxs(Button$1, {
|
|
367
|
+
variant: "outline",
|
|
368
|
+
onClick: props.toggleDrawer,
|
|
369
|
+
"aria-label": `Feedback, ${count} open on this page`,
|
|
370
|
+
children: [
|
|
371
|
+
/* @__PURE__ */ jsx(RiChat3Line, { "aria-hidden": "true" }),
|
|
372
|
+
"Feedback",
|
|
373
|
+
count > 0 && /* @__PURE__ */ jsx(Badge, {
|
|
374
|
+
variant: "outline",
|
|
375
|
+
className: "-mr-1 bg-background tabular-nums",
|
|
376
|
+
children: count
|
|
377
|
+
})
|
|
378
|
+
]
|
|
379
|
+
}),
|
|
380
|
+
/* @__PURE__ */ jsx(Button$1, {
|
|
381
|
+
size: "icon",
|
|
382
|
+
"aria-label": "New comment",
|
|
383
|
+
"aria-keyshortcuts": "c",
|
|
384
|
+
title: "New comment (c)",
|
|
385
|
+
onClick: props.comment,
|
|
386
|
+
children: /* @__PURE__ */ jsx(RiAddLine, {
|
|
387
|
+
"aria-hidden": "true",
|
|
388
|
+
className: "size-6"
|
|
389
|
+
})
|
|
390
|
+
})
|
|
391
|
+
]
|
|
392
|
+
})
|
|
393
|
+
]
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
function SignedOut(props) {
|
|
397
|
+
const { leaving } = props;
|
|
398
|
+
const [name, setName] = useState(props.guestName === ANONYMOUS_NAME ? "" : props.guestName);
|
|
399
|
+
const [busy, setBusy] = useState(false);
|
|
400
|
+
const [typed, setTyped] = useState(false);
|
|
401
|
+
const input = useRef(null);
|
|
402
|
+
const named = Boolean(name.trim());
|
|
403
|
+
const [glowed, setGlowed] = useState(false);
|
|
404
|
+
useEffect(() => {
|
|
405
|
+
const timer = setTimeout(() => setGlowed(true), GLOW_MS);
|
|
406
|
+
return () => clearTimeout(timer);
|
|
407
|
+
}, []);
|
|
408
|
+
const go = (as) => {
|
|
409
|
+
setBusy(true);
|
|
410
|
+
props.continueAsGuest(as).finally(() => setBusy(false));
|
|
411
|
+
};
|
|
412
|
+
return /* @__PURE__ */ jsxs("nav", {
|
|
413
|
+
...navProps(props, !named && !glowed),
|
|
414
|
+
"data-named": named || void 0,
|
|
415
|
+
children: [
|
|
416
|
+
/* @__PURE__ */ jsx(Surface, { ...props }),
|
|
417
|
+
/* @__PURE__ */ jsx(Glow, { leaving }),
|
|
418
|
+
/* @__PURE__ */ jsx(TopRow, {
|
|
419
|
+
leaving,
|
|
420
|
+
minimize: props.minimize,
|
|
421
|
+
close: props.close,
|
|
422
|
+
signIn: props.canSignIn ? props.signIn : void 0
|
|
423
|
+
}),
|
|
424
|
+
/* @__PURE__ */ jsxs("form", {
|
|
425
|
+
className: cn("flex items-center gap-2", leaving ? LEAVE : ENTER),
|
|
426
|
+
onSubmit: (event) => {
|
|
427
|
+
event.preventDefault();
|
|
428
|
+
if (named) go(name);
|
|
429
|
+
},
|
|
430
|
+
children: [
|
|
431
|
+
/* @__PURE__ */ jsx(Input$1, {
|
|
432
|
+
name: "name",
|
|
433
|
+
"aria-label": "Your name",
|
|
434
|
+
autoComplete: "name",
|
|
435
|
+
placeholder: "Comment with your name",
|
|
436
|
+
maxLength: LIMITS.reviewerNameChars,
|
|
437
|
+
ref: input,
|
|
438
|
+
value: name,
|
|
439
|
+
onChange: (event) => {
|
|
440
|
+
setName(event.target.value);
|
|
441
|
+
setTyped(true);
|
|
442
|
+
},
|
|
443
|
+
className: "w-48 flex-1 sm:w-52"
|
|
444
|
+
}),
|
|
445
|
+
/* @__PURE__ */ jsx("span", {
|
|
446
|
+
className: "fb-or text-xs text-muted-foreground",
|
|
447
|
+
children: "or"
|
|
448
|
+
}),
|
|
449
|
+
/* @__PURE__ */ jsx("div", {
|
|
450
|
+
className: "grid w-36 perspective-normal",
|
|
451
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
452
|
+
className: cn("flex gap-1.5", typed && "animate-flip-in-x animate-duration-300 motion-reduce:animate-none"),
|
|
453
|
+
children: named ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button$1, {
|
|
454
|
+
type: "submit",
|
|
455
|
+
className: "flex-1 font-semibold",
|
|
456
|
+
disabled: busy,
|
|
457
|
+
children: "Save"
|
|
458
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
459
|
+
type: "button",
|
|
460
|
+
variant: "destructive",
|
|
461
|
+
size: "icon",
|
|
462
|
+
"aria-label": "Clear name",
|
|
463
|
+
title: "Clear name",
|
|
464
|
+
disabled: busy,
|
|
465
|
+
onClick: () => {
|
|
466
|
+
setName("");
|
|
467
|
+
input.current?.focus();
|
|
468
|
+
},
|
|
469
|
+
children: /* @__PURE__ */ jsx(RiCloseLine, { "aria-hidden": "true" })
|
|
470
|
+
})] }) : /* @__PURE__ */ jsx(Button$1, {
|
|
471
|
+
type: "button",
|
|
472
|
+
className: "flex-1 font-semibold",
|
|
473
|
+
disabled: busy,
|
|
474
|
+
onClick: () => go(ANONYMOUS_NAME),
|
|
475
|
+
children: "Anonymously"
|
|
476
|
+
})
|
|
477
|
+
}, named ? "named" : "anonymous")
|
|
478
|
+
})
|
|
479
|
+
]
|
|
480
|
+
})
|
|
481
|
+
]
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
//#endregion
|
|
485
|
+
//#region ../ui/src/components/card.tsx
|
|
486
|
+
function Card({ className, size = "default", ...props }) {
|
|
487
|
+
return /* @__PURE__ */ jsx("div", {
|
|
488
|
+
"data-slot": "card",
|
|
489
|
+
"data-size": size,
|
|
490
|
+
className: cn("group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-2xl bg-card py-(--card-spacing) text-sm text-card-foreground ring-1 ring-foreground/10 [--card-spacing:--spacing(6)] has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(4)] *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", className),
|
|
491
|
+
...props
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
//#endregion
|
|
495
|
+
//#region ../ui/src/components/checkbox.tsx
|
|
496
|
+
function Checkbox$1({ className, ...props }) {
|
|
497
|
+
return /* @__PURE__ */ jsx(Checkbox.Root, {
|
|
498
|
+
"data-slot": "checkbox",
|
|
499
|
+
className: cn("peer relative flex size-4 shrink-0 items-center justify-center rounded-[6px] border border-input transition-shadow outline-none group-has-disabled/field:opacity-50 group-has-[:focus-visible]/field-label:ring-0 group-has-[:focus-visible]/field-label:not-data-checked:border-input after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground group-has-[:focus-visible]/field-label:data-checked:border-primary dark:data-checked:bg-primary", className),
|
|
500
|
+
...props,
|
|
501
|
+
children: /* @__PURE__ */ jsx(Checkbox.Indicator, {
|
|
502
|
+
"data-slot": "checkbox-indicator",
|
|
503
|
+
className: "grid place-content-center text-current transition-none [&>svg]:size-3.5",
|
|
504
|
+
children: /* @__PURE__ */ jsx(RiCheckLine, {})
|
|
505
|
+
})
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
//#endregion
|
|
509
|
+
//#region ../ui/src/components/label.tsx
|
|
510
|
+
function Label({ className, ...props }) {
|
|
511
|
+
return /* @__PURE__ */ jsx("label", {
|
|
512
|
+
"data-slot": "label",
|
|
513
|
+
className: cn("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className),
|
|
514
|
+
...props
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
//#endregion
|
|
518
|
+
//#region ../ui/src/components/textarea.tsx
|
|
519
|
+
function Textarea({ className, ...props }) {
|
|
520
|
+
return /* @__PURE__ */ jsx("textarea", {
|
|
521
|
+
"data-slot": "textarea",
|
|
522
|
+
className: cn("flex field-sizing-content min-h-16 w-full resize-none rounded-xl border border-input bg-input/30 px-3 py-3 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 md:text-sm dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className),
|
|
523
|
+
...props
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
//#endregion
|
|
527
|
+
//#region src/panel.tsx
|
|
528
|
+
const PANEL = "pointer-events-auto fixed w-[350px] max-w-[calc(100vw-24px)] px-4 shadow-xl";
|
|
529
|
+
function PanelHeading({ eyebrow, title, children }) {
|
|
530
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
531
|
+
/* @__PURE__ */ jsx("div", {
|
|
532
|
+
className: "text-[10px] tracking-widest text-muted-foreground uppercase",
|
|
533
|
+
children: eyebrow
|
|
534
|
+
}),
|
|
535
|
+
/* @__PURE__ */ jsx("h2", {
|
|
536
|
+
className: "text-base font-semibold",
|
|
537
|
+
children: title
|
|
538
|
+
}),
|
|
539
|
+
children
|
|
540
|
+
] });
|
|
541
|
+
}
|
|
542
|
+
const sendOnModEnter = (send) => (event) => {
|
|
543
|
+
if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
|
|
544
|
+
event.preventDefault();
|
|
545
|
+
send();
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
//#endregion
|
|
549
|
+
//#region src/composer.tsx
|
|
550
|
+
function draftKey(pageKey, target) {
|
|
551
|
+
const anchor = captureAnchor(target);
|
|
552
|
+
return `new:${pageKey}:${anchor.stableId ?? anchor.elementId ?? anchor.selectorCandidates[0]}`;
|
|
553
|
+
}
|
|
554
|
+
function Composer(props) {
|
|
555
|
+
const { selection, drafts, pageKey } = props;
|
|
556
|
+
const { target, region } = selection;
|
|
557
|
+
const key = useMemo(() => draftKey(pageKey, target), [pageKey, target]);
|
|
558
|
+
const [body, setBody] = useState(() => drafts.get(key) ?? "");
|
|
559
|
+
const [saving, setSaving] = useState(false);
|
|
560
|
+
const [screenshot, setScreenshot] = useState(true);
|
|
561
|
+
const panel = useRef(null);
|
|
562
|
+
const textarea = useRef(null);
|
|
563
|
+
useEffect(() => {
|
|
564
|
+
setBody(drafts.get(key) ?? "");
|
|
565
|
+
textarea.current?.focus();
|
|
566
|
+
}, [drafts, key]);
|
|
567
|
+
useFrame(() => {
|
|
568
|
+
if (panel.current) position(panel.current, regionBox(target, region), Boolean(region));
|
|
569
|
+
});
|
|
570
|
+
const write = (value) => {
|
|
571
|
+
setBody(value);
|
|
572
|
+
drafts.set(key, value);
|
|
573
|
+
};
|
|
574
|
+
const submit = async () => {
|
|
575
|
+
const text = body.trim();
|
|
576
|
+
if (!text) {
|
|
577
|
+
textarea.current?.focus();
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
setSaving(true);
|
|
581
|
+
try {
|
|
582
|
+
if (await props.save(selection, text, screenshot)) {
|
|
583
|
+
drafts.delete(key);
|
|
584
|
+
props.close();
|
|
585
|
+
}
|
|
586
|
+
} finally {
|
|
587
|
+
setSaving(false);
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
const [first] = props.existing;
|
|
591
|
+
const plural = props.existing.length === 1 ? "" : "s";
|
|
592
|
+
return /* @__PURE__ */ jsxs(Card, {
|
|
593
|
+
ref: panel,
|
|
594
|
+
size: "sm",
|
|
595
|
+
"aria-label": "New comment",
|
|
596
|
+
className: PANEL,
|
|
597
|
+
children: [
|
|
598
|
+
/* @__PURE__ */ jsxs("div", {
|
|
599
|
+
className: "flex items-center justify-between gap-2",
|
|
600
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
601
|
+
className: "text-base font-semibold",
|
|
602
|
+
children: "New comment"
|
|
603
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
604
|
+
variant: "ghost",
|
|
605
|
+
size: "icon-xs",
|
|
606
|
+
className: "-mr-1.5",
|
|
607
|
+
"aria-label": "Close",
|
|
608
|
+
title: "Close",
|
|
609
|
+
onClick: props.close,
|
|
610
|
+
children: /* @__PURE__ */ jsx(RiCloseLine, { "aria-hidden": "true" })
|
|
611
|
+
})]
|
|
612
|
+
}),
|
|
613
|
+
first && /* @__PURE__ */ jsxs(Button$1, {
|
|
614
|
+
variant: "ghost",
|
|
615
|
+
className: "self-start",
|
|
616
|
+
onClick: () => {
|
|
617
|
+
props.openThread(first.id);
|
|
618
|
+
props.close();
|
|
619
|
+
},
|
|
620
|
+
children: [
|
|
621
|
+
"Read ",
|
|
622
|
+
props.existing.length,
|
|
623
|
+
" existing comment",
|
|
624
|
+
plural
|
|
625
|
+
]
|
|
626
|
+
}),
|
|
627
|
+
/* @__PURE__ */ jsx(Textarea, {
|
|
628
|
+
ref: textarea,
|
|
629
|
+
"aria-label": "Your comment",
|
|
630
|
+
placeholder: "What could be better?",
|
|
631
|
+
maxLength: LIMITS.bodyChars,
|
|
632
|
+
value: body,
|
|
633
|
+
onChange: (event) => write(event.target.value),
|
|
634
|
+
onKeyDown: sendOnModEnter(() => void submit()),
|
|
635
|
+
className: "min-h-28"
|
|
636
|
+
}),
|
|
637
|
+
/* @__PURE__ */ jsxs("div", {
|
|
638
|
+
className: "flex items-center justify-between gap-2",
|
|
639
|
+
children: [props.canScreenshot && /* @__PURE__ */ jsxs(Label, {
|
|
640
|
+
className: "text-xs font-normal text-muted-foreground",
|
|
641
|
+
children: [/* @__PURE__ */ jsx(Checkbox$1, {
|
|
642
|
+
checked: screenshot,
|
|
643
|
+
onCheckedChange: (checked) => setScreenshot(checked)
|
|
644
|
+
}), "Take screenshot"]
|
|
645
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
646
|
+
className: "ml-auto",
|
|
647
|
+
disabled: saving,
|
|
648
|
+
onClick: () => void submit(),
|
|
649
|
+
children: "Save"
|
|
650
|
+
})]
|
|
651
|
+
})
|
|
652
|
+
]
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
//#endregion
|
|
656
|
+
//#region src/controller.ts
|
|
657
|
+
const ERROR_MS = 6e3;
|
|
658
|
+
const RELOCATE_IDLE_MS = 2e3;
|
|
659
|
+
var FeedbackController = class {
|
|
660
|
+
state;
|
|
661
|
+
options;
|
|
662
|
+
signal;
|
|
663
|
+
tokens;
|
|
664
|
+
api;
|
|
665
|
+
picker;
|
|
666
|
+
listeners = /* @__PURE__ */ new Set();
|
|
667
|
+
resolutions = /* @__PURE__ */ new Map();
|
|
668
|
+
pendingComment = false;
|
|
669
|
+
pageRequest = 0;
|
|
670
|
+
listRequest = 0;
|
|
671
|
+
detailRequest = 0;
|
|
672
|
+
errorTimer = 0;
|
|
673
|
+
relocated = /* @__PURE__ */ new Set();
|
|
674
|
+
relocateTimer = 0;
|
|
675
|
+
constructor(options, layer, signal) {
|
|
676
|
+
this.options = options;
|
|
677
|
+
this.signal = signal;
|
|
678
|
+
this.state = {
|
|
679
|
+
reviewer: null,
|
|
680
|
+
guestName: "",
|
|
681
|
+
page: pageContext(options),
|
|
682
|
+
threads: [],
|
|
683
|
+
groups: [],
|
|
684
|
+
composer: null,
|
|
685
|
+
conversation: null,
|
|
686
|
+
drawer: {
|
|
687
|
+
open: false,
|
|
688
|
+
status: "open",
|
|
689
|
+
page: "",
|
|
690
|
+
list: { items: [] }
|
|
691
|
+
},
|
|
692
|
+
error: null
|
|
693
|
+
};
|
|
694
|
+
this.tokens = new TokenSource({
|
|
695
|
+
getToken: options.getToken,
|
|
696
|
+
onChange: (reviewer) => this.reviewerChanged(reviewer)
|
|
697
|
+
});
|
|
698
|
+
this.api = new ApiClient(options.endpoint, this.tokens);
|
|
699
|
+
this.picker = new Selection(layer, (target, point, region) => this.select(target, point, region), () => {});
|
|
700
|
+
layer.follow(() => this.updateBadges(this.state.threads), signal);
|
|
701
|
+
const restoreHistory = watchNavigation(() => this.navigate(), signal);
|
|
702
|
+
signal.addEventListener("abort", () => {
|
|
703
|
+
restoreHistory();
|
|
704
|
+
this.picker.destroy();
|
|
705
|
+
clearTimeout(this.errorTimer);
|
|
706
|
+
clearTimeout(this.relocateTimer);
|
|
707
|
+
});
|
|
708
|
+
this.set({ guestName: this.tokens.guest()?.name ?? "" });
|
|
709
|
+
this.connect();
|
|
710
|
+
}
|
|
711
|
+
subscribe = (listener) => {
|
|
712
|
+
this.listeners.add(listener);
|
|
713
|
+
return () => this.listeners.delete(listener);
|
|
714
|
+
};
|
|
715
|
+
getSnapshot = () => this.state;
|
|
716
|
+
set(patch) {
|
|
717
|
+
if (this.signal.aborted) return;
|
|
718
|
+
this.state = {
|
|
719
|
+
...this.state,
|
|
720
|
+
...patch
|
|
721
|
+
};
|
|
722
|
+
for (const listener of this.listeners) listener();
|
|
723
|
+
}
|
|
724
|
+
setDrawer(patch) {
|
|
725
|
+
this.set({ drawer: {
|
|
726
|
+
...this.state.drawer,
|
|
727
|
+
...patch
|
|
728
|
+
} });
|
|
729
|
+
}
|
|
730
|
+
closedDrawer() {
|
|
731
|
+
return {
|
|
732
|
+
...this.state.drawer,
|
|
733
|
+
open: false
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
showError = (message) => {
|
|
737
|
+
clearTimeout(this.errorTimer);
|
|
738
|
+
this.set({ error: {
|
|
739
|
+
message,
|
|
740
|
+
id: (this.state.error?.id ?? 0) + 1
|
|
741
|
+
} });
|
|
742
|
+
this.errorTimer = window.setTimeout(() => this.set({ error: null }), ERROR_MS);
|
|
743
|
+
};
|
|
744
|
+
failed(error) {
|
|
745
|
+
if (this.signal.aborted) return;
|
|
746
|
+
if (error instanceof DOMException && error.name === "AbortError") return;
|
|
747
|
+
this.showError(error instanceof Error ? error.message : "Feedback is unavailable. Try again.");
|
|
748
|
+
}
|
|
749
|
+
async connect() {
|
|
750
|
+
try {
|
|
751
|
+
await this.tokens.token();
|
|
752
|
+
} catch (error) {
|
|
753
|
+
this.failed(error);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
reviewerChanged(reviewer) {
|
|
757
|
+
if (this.signal.aborted) return;
|
|
758
|
+
const before = this.state.reviewer;
|
|
759
|
+
this.set({
|
|
760
|
+
reviewer,
|
|
761
|
+
guestName: this.tokens.guest()?.name ?? ""
|
|
762
|
+
});
|
|
763
|
+
if (!reviewer) {
|
|
764
|
+
this.signedOut();
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
if (this.pendingComment) this.startSelection();
|
|
768
|
+
if (before?.id === reviewer.id && before.verified === reviewer.verified) return;
|
|
769
|
+
this.signedIn(!before);
|
|
770
|
+
}
|
|
771
|
+
async signedIn(arriving) {
|
|
772
|
+
await this.refreshAnchors();
|
|
773
|
+
if (this.state.drawer.open) this.loadDrawer();
|
|
774
|
+
if (arriving) {
|
|
775
|
+
const id = consumeThreadLink();
|
|
776
|
+
if (id) {
|
|
777
|
+
this.setDrawer({ open: true });
|
|
778
|
+
await this.openThread(id, true);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
signedOut() {
|
|
783
|
+
this.picker.clear();
|
|
784
|
+
this.resolutions.clear();
|
|
785
|
+
this.forgetRelocations();
|
|
786
|
+
this.set({
|
|
787
|
+
threads: [],
|
|
788
|
+
groups: [],
|
|
789
|
+
composer: null,
|
|
790
|
+
conversation: null,
|
|
791
|
+
drawer: this.closedDrawer()
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
continueAsGuest = async (name) => {
|
|
795
|
+
try {
|
|
796
|
+
if (!await this.tokens.continueAsGuest(name)) this.showError(this.options.signIn ? "This site does not take guest comments. Sign in to comment." : "This site does not take guest comments.");
|
|
797
|
+
} catch (error) {
|
|
798
|
+
this.failed(error);
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
changeName = () => this.tokens.forgetGuestName();
|
|
802
|
+
signIn = async () => {
|
|
803
|
+
rememberVisible(true);
|
|
804
|
+
try {
|
|
805
|
+
await this.options.signIn?.();
|
|
806
|
+
} catch (error) {
|
|
807
|
+
this.failed(error);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
if (this.signal.aborted) return;
|
|
811
|
+
this.tokens.invalidate();
|
|
812
|
+
await this.connect();
|
|
813
|
+
};
|
|
814
|
+
comment = () => {
|
|
815
|
+
this.pendingComment = true;
|
|
816
|
+
if (this.state.reviewer) this.startSelection();
|
|
817
|
+
};
|
|
818
|
+
startSelection = () => {
|
|
819
|
+
if (!this.state.reviewer) return;
|
|
820
|
+
this.pendingComment = false;
|
|
821
|
+
this.closePanels();
|
|
822
|
+
this.picker.start();
|
|
823
|
+
};
|
|
824
|
+
select(target, point, region) {
|
|
825
|
+
if (this.tokens.features.screenshots) preloadScreenshot();
|
|
826
|
+
this.set({ composer: region ? {
|
|
827
|
+
target,
|
|
828
|
+
point,
|
|
829
|
+
region
|
|
830
|
+
} : {
|
|
831
|
+
target,
|
|
832
|
+
point
|
|
833
|
+
} });
|
|
834
|
+
}
|
|
835
|
+
closeComposer = () => {
|
|
836
|
+
this.picker.clear();
|
|
837
|
+
this.set({ composer: null });
|
|
838
|
+
};
|
|
839
|
+
closePanels = () => {
|
|
840
|
+
this.picker.clear();
|
|
841
|
+
this.detailRequest++;
|
|
842
|
+
this.set({
|
|
843
|
+
composer: null,
|
|
844
|
+
conversation: null,
|
|
845
|
+
drawer: this.closedDrawer()
|
|
846
|
+
});
|
|
847
|
+
};
|
|
848
|
+
existing(target) {
|
|
849
|
+
return this.state.threads.filter((thread) => this.resolutions.get(thread.id)?.element === target);
|
|
850
|
+
}
|
|
851
|
+
get canScreenshot() {
|
|
852
|
+
return this.tokens.features.screenshots;
|
|
853
|
+
}
|
|
854
|
+
save = async (selection, body, screenshot) => {
|
|
855
|
+
const { target, point, region } = selection;
|
|
856
|
+
const anchor = captureAnchor(target, point, region);
|
|
857
|
+
const image = screenshot && this.canScreenshot ? captureScreenshot(region ? {
|
|
858
|
+
target,
|
|
859
|
+
region
|
|
860
|
+
} : { target }) : null;
|
|
861
|
+
let thread;
|
|
862
|
+
try {
|
|
863
|
+
thread = await this.api.createThread({
|
|
864
|
+
page: this.state.page,
|
|
865
|
+
anchor,
|
|
866
|
+
body
|
|
867
|
+
}, this.signal);
|
|
868
|
+
} catch (error) {
|
|
869
|
+
this.failed(error);
|
|
870
|
+
return false;
|
|
871
|
+
}
|
|
872
|
+
this.refreshAnchors();
|
|
873
|
+
if (image) this.uploadScreenshot(thread.id, image);
|
|
874
|
+
return true;
|
|
875
|
+
};
|
|
876
|
+
async uploadScreenshot(id, screenshot) {
|
|
877
|
+
try {
|
|
878
|
+
const image = await screenshot;
|
|
879
|
+
if (!image || this.signal.aborted) return;
|
|
880
|
+
await this.api.uploadScreenshot(id, image, this.signal);
|
|
881
|
+
} catch {}
|
|
882
|
+
}
|
|
883
|
+
screenshot = (id, signal) => this.api.getScreenshot(id, signal);
|
|
884
|
+
navigate() {
|
|
885
|
+
const next = pageContext(this.options);
|
|
886
|
+
if (next.key === this.state.page.key) return;
|
|
887
|
+
this.picker.clear();
|
|
888
|
+
this.detailRequest++;
|
|
889
|
+
this.resolutions.clear();
|
|
890
|
+
this.forgetRelocations();
|
|
891
|
+
this.set({
|
|
892
|
+
page: next,
|
|
893
|
+
composer: null,
|
|
894
|
+
conversation: null,
|
|
895
|
+
threads: [],
|
|
896
|
+
groups: []
|
|
897
|
+
});
|
|
898
|
+
if (this.state.reviewer) {
|
|
899
|
+
this.refreshAnchors();
|
|
900
|
+
if (this.state.drawer.open) this.loadDrawer();
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
async refreshAnchors() {
|
|
904
|
+
if (!this.state.reviewer) return;
|
|
905
|
+
const generation = ++this.pageRequest;
|
|
906
|
+
try {
|
|
907
|
+
const result = await this.api.listThreads({
|
|
908
|
+
status: "open",
|
|
909
|
+
page: this.state.page.key
|
|
910
|
+
}, this.signal);
|
|
911
|
+
if (generation !== this.pageRequest || this.signal.aborted) return;
|
|
912
|
+
this.updateBadges(result.items);
|
|
913
|
+
} catch (error) {
|
|
914
|
+
this.failed(error);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
updateBadges(threads) {
|
|
918
|
+
this.resolutions = /* @__PURE__ */ new Map();
|
|
919
|
+
const groups = /* @__PURE__ */ new Map();
|
|
920
|
+
for (const thread of threads) {
|
|
921
|
+
const anchor = shownAnchor(thread);
|
|
922
|
+
const found = resolveAnchor(anchor);
|
|
923
|
+
this.resolutions.set(thread.id, found);
|
|
924
|
+
if (!found.element) continue;
|
|
925
|
+
const areas = groups.get(found.element) ?? /* @__PURE__ */ new Map();
|
|
926
|
+
const area = JSON.stringify([anchor.region ?? null, Boolean(thread.suggestion)]);
|
|
927
|
+
areas.set(area, [...areas.get(area) ?? [], thread]);
|
|
928
|
+
groups.set(found.element, areas);
|
|
929
|
+
}
|
|
930
|
+
const badges = [];
|
|
931
|
+
for (const [target, areas] of groups) for (const grouped of areas.values()) {
|
|
932
|
+
const first = grouped[0];
|
|
933
|
+
if (first) badges.push({
|
|
934
|
+
key: grouped.map((thread) => thread.id).join(","),
|
|
935
|
+
target,
|
|
936
|
+
anchor: shownAnchor(first),
|
|
937
|
+
threads: grouped,
|
|
938
|
+
moved: Boolean(first.suggestion)
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
this.set({
|
|
942
|
+
threads,
|
|
943
|
+
groups: badges
|
|
944
|
+
});
|
|
945
|
+
this.scheduleRelocation();
|
|
946
|
+
}
|
|
947
|
+
forgetRelocations() {
|
|
948
|
+
clearTimeout(this.relocateTimer);
|
|
949
|
+
this.relocated.clear();
|
|
950
|
+
}
|
|
951
|
+
scheduleRelocation() {
|
|
952
|
+
clearTimeout(this.relocateTimer);
|
|
953
|
+
if (!this.state.reviewer?.verified) return;
|
|
954
|
+
this.relocateTimer = window.setTimeout(() => void this.relocate(), RELOCATE_IDLE_MS);
|
|
955
|
+
}
|
|
956
|
+
async relocate() {
|
|
957
|
+
const { reviewer, page, threads } = this.state;
|
|
958
|
+
if (!reviewer?.verified) return;
|
|
959
|
+
for (const thread of threads) {
|
|
960
|
+
if (this.signal.aborted || this.state.page.key !== page.key) return;
|
|
961
|
+
const state = this.resolutions.get(thread.id)?.state;
|
|
962
|
+
if (state !== "orphan" && state !== "ambiguous" || !thread.anchor.path || thread.suggestion || thread.pageKey !== page.key || this.relocated.has(thread.id)) continue;
|
|
963
|
+
this.relocated.add(thread.id);
|
|
964
|
+
const request = relocationRequest(thread);
|
|
965
|
+
if (!request) continue;
|
|
966
|
+
try {
|
|
967
|
+
const { suggestion } = await this.api.relocate(thread.id, request, this.signal);
|
|
968
|
+
if (!suggestion || this.state.page.key !== page.key) continue;
|
|
969
|
+
this.patchThread(thread.id, (current) => ({
|
|
970
|
+
...current,
|
|
971
|
+
suggestion
|
|
972
|
+
}));
|
|
973
|
+
} catch {}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
patchThread(id, change) {
|
|
977
|
+
const open = this.state.conversation;
|
|
978
|
+
if (open?.detail.thread.id === id) this.set({ conversation: {
|
|
979
|
+
...open,
|
|
980
|
+
detail: {
|
|
981
|
+
...open.detail,
|
|
982
|
+
thread: change(open.detail.thread)
|
|
983
|
+
}
|
|
984
|
+
} });
|
|
985
|
+
if (this.state.threads.some((thread) => thread.id === id)) this.updateBadges(this.state.threads.map((thread) => thread.id === id ? change(thread) : thread));
|
|
986
|
+
}
|
|
987
|
+
decideSuggestion = async (thread, decision) => {
|
|
988
|
+
const { suggestion } = thread;
|
|
989
|
+
if (!suggestion) return false;
|
|
990
|
+
let next;
|
|
991
|
+
try {
|
|
992
|
+
next = await this.api.decideSuggestion(thread.id, {
|
|
993
|
+
suggestionId: suggestion.id,
|
|
994
|
+
decision
|
|
995
|
+
}, this.signal);
|
|
996
|
+
} catch (error) {
|
|
997
|
+
this.failed(error);
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
this.patchThread(thread.id, () => next);
|
|
1001
|
+
const open = this.state.conversation;
|
|
1002
|
+
if (open?.detail.thread.id === thread.id && !open.inDrawer && !resolveAnchor(shownAnchor(next)).element) this.set({
|
|
1003
|
+
conversation: {
|
|
1004
|
+
...open,
|
|
1005
|
+
inDrawer: true
|
|
1006
|
+
},
|
|
1007
|
+
drawer: {
|
|
1008
|
+
...this.state.drawer,
|
|
1009
|
+
open: true
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
return true;
|
|
1013
|
+
};
|
|
1014
|
+
openBadge = (threads) => {
|
|
1015
|
+
const [first] = threads;
|
|
1016
|
+
if (threads.length === 1 && first) {
|
|
1017
|
+
this.openThread(first.id, false);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
this.setDrawer({
|
|
1021
|
+
open: true,
|
|
1022
|
+
page: this.state.page.key
|
|
1023
|
+
});
|
|
1024
|
+
this.loadDrawer();
|
|
1025
|
+
};
|
|
1026
|
+
toggleDrawer = () => {
|
|
1027
|
+
if (this.state.drawer.open) {
|
|
1028
|
+
this.closeDrawer();
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
this.setDrawer({ open: true });
|
|
1032
|
+
this.loadDrawer();
|
|
1033
|
+
};
|
|
1034
|
+
closeDrawer = () => {
|
|
1035
|
+
this.detailRequest++;
|
|
1036
|
+
const inDrawer = this.state.conversation?.inDrawer;
|
|
1037
|
+
this.set({
|
|
1038
|
+
drawer: this.closedDrawer(),
|
|
1039
|
+
conversation: inDrawer ? null : this.state.conversation
|
|
1040
|
+
});
|
|
1041
|
+
};
|
|
1042
|
+
setDrawerFilter = (patch) => {
|
|
1043
|
+
this.setDrawer(patch);
|
|
1044
|
+
this.loadDrawer();
|
|
1045
|
+
};
|
|
1046
|
+
refresh = () => {
|
|
1047
|
+
Promise.all([this.loadDrawer(), this.refreshAnchors()]);
|
|
1048
|
+
};
|
|
1049
|
+
loadDrawer = async () => {
|
|
1050
|
+
if (!this.state.reviewer) return;
|
|
1051
|
+
const generation = ++this.listRequest;
|
|
1052
|
+
this.detailRequest++;
|
|
1053
|
+
const { status, page } = this.state.drawer;
|
|
1054
|
+
this.set({
|
|
1055
|
+
conversation: null,
|
|
1056
|
+
drawer: {
|
|
1057
|
+
...this.state.drawer,
|
|
1058
|
+
list: {
|
|
1059
|
+
items: [],
|
|
1060
|
+
message: "Loading feedback…"
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
try {
|
|
1065
|
+
const query = page ? {
|
|
1066
|
+
status,
|
|
1067
|
+
page
|
|
1068
|
+
} : { status };
|
|
1069
|
+
const result = await this.api.listThreads(query, this.signal);
|
|
1070
|
+
if (generation !== this.listRequest || this.signal.aborted) return;
|
|
1071
|
+
this.setDrawer({ list: { items: result.items } });
|
|
1072
|
+
} catch (error) {
|
|
1073
|
+
this.setDrawer({ list: {
|
|
1074
|
+
items: [],
|
|
1075
|
+
message: "Could not load feedback. Use Refresh to try again."
|
|
1076
|
+
} });
|
|
1077
|
+
this.failed(error);
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
openThread = async (id, inDrawer) => {
|
|
1081
|
+
if (!this.state.reviewer) return;
|
|
1082
|
+
this.picker.clear();
|
|
1083
|
+
this.set({ composer: null });
|
|
1084
|
+
const generation = ++this.detailRequest;
|
|
1085
|
+
try {
|
|
1086
|
+
const detail = await this.api.getThread(id, this.signal);
|
|
1087
|
+
if (generation !== this.detailRequest || this.signal.aborted) return;
|
|
1088
|
+
const { thread } = detail;
|
|
1089
|
+
const visible = thread.pageKey === this.state.page.key && Boolean(resolveAnchor(shownAnchor(thread)).element);
|
|
1090
|
+
const shownInDrawer = inDrawer || !visible;
|
|
1091
|
+
this.set({
|
|
1092
|
+
conversation: {
|
|
1093
|
+
detail,
|
|
1094
|
+
inDrawer: shownInDrawer
|
|
1095
|
+
},
|
|
1096
|
+
drawer: shownInDrawer ? {
|
|
1097
|
+
...this.state.drawer,
|
|
1098
|
+
open: true
|
|
1099
|
+
} : this.state.drawer
|
|
1100
|
+
});
|
|
1101
|
+
} catch (error) {
|
|
1102
|
+
this.failed(error);
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
closeConversation = () => {
|
|
1106
|
+
this.detailRequest++;
|
|
1107
|
+
this.set({ conversation: null });
|
|
1108
|
+
};
|
|
1109
|
+
async reloadThread() {
|
|
1110
|
+
const open = this.state.conversation;
|
|
1111
|
+
await Promise.all([open ? this.openThread(open.detail.thread.id, open.inDrawer) : null, this.refreshAnchors()]);
|
|
1112
|
+
}
|
|
1113
|
+
reply = async (thread, body) => {
|
|
1114
|
+
try {
|
|
1115
|
+
await this.api.createMessage(thread.id, { body }, this.signal);
|
|
1116
|
+
} catch (error) {
|
|
1117
|
+
this.failed(error);
|
|
1118
|
+
return false;
|
|
1119
|
+
}
|
|
1120
|
+
await this.reloadThread();
|
|
1121
|
+
return true;
|
|
1122
|
+
};
|
|
1123
|
+
toggleStatus = async (thread) => {
|
|
1124
|
+
try {
|
|
1125
|
+
await this.api.updateThread(thread.id, { status: thread.status === "open" ? "done" : "open" }, this.signal);
|
|
1126
|
+
await this.reloadThread();
|
|
1127
|
+
} catch (error) {
|
|
1128
|
+
this.failed(error);
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
};
|
|
1132
|
+
function relocationRequest(thread) {
|
|
1133
|
+
const found = pathBreak(thread.anchor);
|
|
1134
|
+
if (!found || found.breakAt === thread.anchor.path?.length) return null;
|
|
1135
|
+
const request = {
|
|
1136
|
+
breakAt: found.breakAt,
|
|
1137
|
+
skeleton: skeleton(found.root)
|
|
1138
|
+
};
|
|
1139
|
+
return new TextEncoder().encode(JSON.stringify(request)).length > LIMITS.relocateBytes ? null : request;
|
|
1140
|
+
}
|
|
1141
|
+
//#endregion
|
|
1142
|
+
//#region ../ui/src/components/separator.tsx
|
|
1143
|
+
function Separator$1({ className, orientation = "horizontal", ...props }) {
|
|
1144
|
+
return /* @__PURE__ */ jsx(Separator, {
|
|
1145
|
+
"data-slot": "separator",
|
|
1146
|
+
orientation,
|
|
1147
|
+
className: cn("shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch", className),
|
|
1148
|
+
...props
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
//#endregion
|
|
1152
|
+
//#region src/conversation.tsx
|
|
1153
|
+
function Screenshot({ threadId, load }) {
|
|
1154
|
+
const [url, setUrl] = useState(null);
|
|
1155
|
+
useEffect(() => {
|
|
1156
|
+
const request = new AbortController();
|
|
1157
|
+
let objectUrl = null;
|
|
1158
|
+
setUrl(null);
|
|
1159
|
+
load(threadId, request.signal).then((image) => {
|
|
1160
|
+
if (request.signal.aborted) return;
|
|
1161
|
+
objectUrl = URL.createObjectURL(image);
|
|
1162
|
+
setUrl(objectUrl);
|
|
1163
|
+
}).catch(() => {});
|
|
1164
|
+
return () => {
|
|
1165
|
+
request.abort();
|
|
1166
|
+
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
1167
|
+
};
|
|
1168
|
+
}, [threadId, load]);
|
|
1169
|
+
if (!url) return null;
|
|
1170
|
+
return /* @__PURE__ */ jsx("a", {
|
|
1171
|
+
href: url,
|
|
1172
|
+
target: "_blank",
|
|
1173
|
+
rel: "noopener",
|
|
1174
|
+
title: "Open full size",
|
|
1175
|
+
className: "block overflow-hidden rounded-xl border",
|
|
1176
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
1177
|
+
src: url,
|
|
1178
|
+
alt: "Screenshot of the page when this comment was made",
|
|
1179
|
+
className: "block h-auto max-h-64 w-full object-cover object-top",
|
|
1180
|
+
onError: () => setUrl(null)
|
|
1181
|
+
})
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
function notice(thread, pageKey, resolution) {
|
|
1185
|
+
if (thread.pageKey !== pageKey) return null;
|
|
1186
|
+
const width = thread.anchor.viewport?.width;
|
|
1187
|
+
const leftAt = width ? ` It was left at ${Math.round(width)}px wide.` : "";
|
|
1188
|
+
if (resolution.state === "orphan" || resolution.state === "ambiguous") return `Orphan — element not found in this view. It may be in a closed dialog or have changed.${leftAt}`;
|
|
1189
|
+
if (resolution.state === "hidden") return `This element is hidden in the current view.${leftAt}`;
|
|
1190
|
+
if (resolution.element && isReflowed(thread.anchor, resolution.element)) return `The layout has changed since this area was drawn, so the whole element is outlined.${leftAt}`;
|
|
1191
|
+
return null;
|
|
1192
|
+
}
|
|
1193
|
+
function Conversation(props) {
|
|
1194
|
+
const { detail, inDrawer, drafts, showError } = props;
|
|
1195
|
+
const { thread, messages } = detail;
|
|
1196
|
+
const draftKey = `reply:${thread.id}`;
|
|
1197
|
+
const [body, setBody] = useState(() => drafts.get(draftKey) ?? "");
|
|
1198
|
+
const [sending, setSending] = useState(false);
|
|
1199
|
+
const [deciding, setDeciding] = useState(false);
|
|
1200
|
+
const panel = useRef(null);
|
|
1201
|
+
const anchor = shownAnchor(thread);
|
|
1202
|
+
const resolution = useMemo(() => resolveAnchor(anchor), [anchor, props.layout]);
|
|
1203
|
+
useFrame(() => {
|
|
1204
|
+
if (inDrawer || !panel.current) return;
|
|
1205
|
+
const target = resolution.element;
|
|
1206
|
+
position(panel.current, target ? anchorBox(anchor, target) : null);
|
|
1207
|
+
});
|
|
1208
|
+
const decide = async (decision) => {
|
|
1209
|
+
setDeciding(true);
|
|
1210
|
+
try {
|
|
1211
|
+
await props.decideSuggestion(thread, decision);
|
|
1212
|
+
} finally {
|
|
1213
|
+
setDeciding(false);
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
const send = async () => {
|
|
1217
|
+
const text = body.trim();
|
|
1218
|
+
if (!text) return;
|
|
1219
|
+
setSending(true);
|
|
1220
|
+
try {
|
|
1221
|
+
if (await props.reply(thread, text)) {
|
|
1222
|
+
drafts.delete(draftKey);
|
|
1223
|
+
setBody("");
|
|
1224
|
+
}
|
|
1225
|
+
} finally {
|
|
1226
|
+
setSending(false);
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
const locate = () => {
|
|
1230
|
+
if (thread.pageKey === props.pageKey) {
|
|
1231
|
+
const target = resolution.element;
|
|
1232
|
+
if (!target) {
|
|
1233
|
+
showError("This element is not on the page right now.");
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1237
|
+
target.scrollIntoView({
|
|
1238
|
+
block: "center",
|
|
1239
|
+
behavior: reduced ? "instant" : "smooth"
|
|
1240
|
+
});
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
const href = threadLink(location.href, thread.path, thread.id);
|
|
1244
|
+
if (href) location.assign(href);
|
|
1245
|
+
else showError("This comment's page is not on this site.");
|
|
1246
|
+
};
|
|
1247
|
+
const copyLink = async () => {
|
|
1248
|
+
const href = threadLink(location.href, thread.path, thread.id);
|
|
1249
|
+
if (!href) {
|
|
1250
|
+
showError("This comment's page is not on this site.");
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
try {
|
|
1254
|
+
await navigator.clipboard.writeText(href);
|
|
1255
|
+
} catch {
|
|
1256
|
+
showError(`Could not copy the link: ${href}`);
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
const suggestion = thread.pageKey === props.pageKey ? thread.suggestion : void 0;
|
|
1260
|
+
const warning = suggestion ? null : notice(thread, props.pageKey, resolution);
|
|
1261
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1262
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1263
|
+
className: "flex items-center justify-between gap-2",
|
|
1264
|
+
children: [/* @__PURE__ */ jsx(Button$1, {
|
|
1265
|
+
variant: "ghost",
|
|
1266
|
+
size: "xs",
|
|
1267
|
+
onClick: inDrawer ? props.backToList : props.close,
|
|
1268
|
+
children: inDrawer ? "← All feedback" : "Close"
|
|
1269
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
1270
|
+
size: "xs",
|
|
1271
|
+
onClick: () => props.toggleStatus(thread),
|
|
1272
|
+
children: thread.status === "open" ? "Mark done" : "Reopen"
|
|
1273
|
+
})]
|
|
1274
|
+
}),
|
|
1275
|
+
/* @__PURE__ */ jsx(PanelHeading, {
|
|
1276
|
+
eyebrow: thread.status,
|
|
1277
|
+
title: `Comment #${thread.number}`,
|
|
1278
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1279
|
+
className: "text-xs text-muted-foreground",
|
|
1280
|
+
children: thread.path
|
|
1281
|
+
})
|
|
1282
|
+
}),
|
|
1283
|
+
warning && /* @__PURE__ */ jsx("div", {
|
|
1284
|
+
className: "rounded-xl bg-primary/15 px-3 py-2 text-xs text-primary-foreground",
|
|
1285
|
+
children: warning
|
|
1286
|
+
}),
|
|
1287
|
+
suggestion && /* @__PURE__ */ jsxs("div", {
|
|
1288
|
+
className: "flex flex-col gap-2 rounded-xl bg-primary/15 px-3 py-2 text-xs text-primary-foreground",
|
|
1289
|
+
children: [/* @__PURE__ */ jsxs("p", { children: [
|
|
1290
|
+
"This part of the page changed. We think the comment belongs here (",
|
|
1291
|
+
Math.round(suggestion.confidence * 100),
|
|
1292
|
+
"% sure)."
|
|
1293
|
+
] }), props.canDecide && /* @__PURE__ */ jsxs("div", {
|
|
1294
|
+
className: "flex gap-2",
|
|
1295
|
+
children: [/* @__PURE__ */ jsx(Button$1, {
|
|
1296
|
+
size: "xs",
|
|
1297
|
+
disabled: deciding,
|
|
1298
|
+
onClick: () => void decide("accept"),
|
|
1299
|
+
children: "Yes, it's here"
|
|
1300
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
1301
|
+
variant: "outline",
|
|
1302
|
+
size: "xs",
|
|
1303
|
+
disabled: deciding,
|
|
1304
|
+
onClick: () => void decide("reject"),
|
|
1305
|
+
children: "No"
|
|
1306
|
+
})]
|
|
1307
|
+
})]
|
|
1308
|
+
}),
|
|
1309
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1310
|
+
className: "flex items-center justify-between gap-2",
|
|
1311
|
+
children: [/* @__PURE__ */ jsx(Button$1, {
|
|
1312
|
+
variant: "ghost",
|
|
1313
|
+
size: "xs",
|
|
1314
|
+
onClick: locate,
|
|
1315
|
+
children: "Locate on page"
|
|
1316
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
1317
|
+
variant: "ghost",
|
|
1318
|
+
size: "xs",
|
|
1319
|
+
onClick: () => void copyLink(),
|
|
1320
|
+
children: "Copy link"
|
|
1321
|
+
})]
|
|
1322
|
+
}),
|
|
1323
|
+
thread.hasScreenshot && /* @__PURE__ */ jsx(Screenshot, {
|
|
1324
|
+
threadId: thread.id,
|
|
1325
|
+
load: props.loadScreenshot
|
|
1326
|
+
}),
|
|
1327
|
+
/* @__PURE__ */ jsx("div", {
|
|
1328
|
+
className: "flex flex-col",
|
|
1329
|
+
children: messages.map((message) => /* @__PURE__ */ jsxs("article", {
|
|
1330
|
+
className: "flex flex-col gap-2 py-3",
|
|
1331
|
+
children: [
|
|
1332
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1333
|
+
className: "flex items-center justify-between gap-2 text-xs text-muted-foreground",
|
|
1334
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1335
|
+
className: "text-foreground",
|
|
1336
|
+
children: /* @__PURE__ */ jsx(Author, {
|
|
1337
|
+
name: message.authorName,
|
|
1338
|
+
verified: message.authorVerified,
|
|
1339
|
+
strong: true
|
|
1340
|
+
})
|
|
1341
|
+
}), /* @__PURE__ */ jsx("span", { children: new Date(message.createdAt).toLocaleString() })]
|
|
1342
|
+
}),
|
|
1343
|
+
/* @__PURE__ */ jsx("p", {
|
|
1344
|
+
className: "break-words whitespace-pre-wrap",
|
|
1345
|
+
children: message.body
|
|
1346
|
+
}),
|
|
1347
|
+
/* @__PURE__ */ jsx(Separator$1, { className: "mt-1" })
|
|
1348
|
+
]
|
|
1349
|
+
}, message.id))
|
|
1350
|
+
}),
|
|
1351
|
+
/* @__PURE__ */ jsx(Textarea, {
|
|
1352
|
+
"aria-label": "Reply",
|
|
1353
|
+
placeholder: "Write a reply…",
|
|
1354
|
+
maxLength: LIMITS.bodyChars,
|
|
1355
|
+
value: body,
|
|
1356
|
+
onChange: (event) => {
|
|
1357
|
+
setBody(event.target.value);
|
|
1358
|
+
drafts.set(draftKey, event.target.value);
|
|
1359
|
+
},
|
|
1360
|
+
onKeyDown: sendOnModEnter(() => void send()),
|
|
1361
|
+
className: "min-h-24"
|
|
1362
|
+
}),
|
|
1363
|
+
/* @__PURE__ */ jsx(Button$1, {
|
|
1364
|
+
className: "self-start",
|
|
1365
|
+
disabled: sending,
|
|
1366
|
+
onClick: () => void send(),
|
|
1367
|
+
children: "Reply"
|
|
1368
|
+
})
|
|
1369
|
+
] });
|
|
1370
|
+
if (inDrawer) return /* @__PURE__ */ jsx("div", {
|
|
1371
|
+
className: "flex flex-col gap-3 p-4",
|
|
1372
|
+
children: content
|
|
1373
|
+
});
|
|
1374
|
+
return /* @__PURE__ */ jsx(Card, {
|
|
1375
|
+
ref: panel,
|
|
1376
|
+
size: "sm",
|
|
1377
|
+
"aria-label": `Comment #${thread.number}`,
|
|
1378
|
+
className: `${PANEL} max-h-[calc(100vh-110px)] overflow-auto`,
|
|
1379
|
+
children: content
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
//#endregion
|
|
1383
|
+
//#region src/drawer.tsx
|
|
1384
|
+
const STATUS_FILTERS = [...THREAD_STATUSES, "all"];
|
|
1385
|
+
function Drawer(props) {
|
|
1386
|
+
const [page, setPage] = useState(props.page);
|
|
1387
|
+
const { items, message } = props.list;
|
|
1388
|
+
return /* @__PURE__ */ jsxs(Card, {
|
|
1389
|
+
size: "sm",
|
|
1390
|
+
"aria-label": "Feedback",
|
|
1391
|
+
className: "pointer-events-auto fixed top-3 right-3 bottom-22 w-[420px] max-w-[calc(100vw-24px)] gap-0 py-0 shadow-xl",
|
|
1392
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1393
|
+
className: "flex items-center justify-between gap-3 border-b px-5 py-4",
|
|
1394
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
1395
|
+
className: "text-lg font-semibold",
|
|
1396
|
+
children: props.detail?.title ?? "Feedback"
|
|
1397
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
1398
|
+
variant: "ghost",
|
|
1399
|
+
size: "xs",
|
|
1400
|
+
onClick: props.close,
|
|
1401
|
+
children: "Close"
|
|
1402
|
+
})]
|
|
1403
|
+
}), props.detail ? /* @__PURE__ */ jsx("div", {
|
|
1404
|
+
className: "flex-1 overflow-auto",
|
|
1405
|
+
children: props.detail.content
|
|
1406
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
1407
|
+
className: "flex flex-wrap items-center gap-2 border-b px-4 py-3",
|
|
1408
|
+
children: [
|
|
1409
|
+
/* @__PURE__ */ jsx("select", {
|
|
1410
|
+
"aria-label": "Status",
|
|
1411
|
+
value: props.status,
|
|
1412
|
+
onChange: (event) => props.setStatus(STATUS_FILTERS.find((value) => value === event.target.value) ?? "open"),
|
|
1413
|
+
className: "h-8 rounded-4xl border border-input bg-input/30 px-3 text-sm",
|
|
1414
|
+
children: STATUS_FILTERS.map((value) => /* @__PURE__ */ jsx("option", {
|
|
1415
|
+
value,
|
|
1416
|
+
children: value.charAt(0).toUpperCase() + value.slice(1)
|
|
1417
|
+
}, value))
|
|
1418
|
+
}),
|
|
1419
|
+
/* @__PURE__ */ jsx(Button$1, {
|
|
1420
|
+
variant: "outline",
|
|
1421
|
+
size: "sm",
|
|
1422
|
+
onClick: props.refresh,
|
|
1423
|
+
children: "Refresh"
|
|
1424
|
+
}),
|
|
1425
|
+
/* @__PURE__ */ jsx(Input$1, {
|
|
1426
|
+
"aria-label": "Page filter",
|
|
1427
|
+
placeholder: "Filter by page path",
|
|
1428
|
+
value: page,
|
|
1429
|
+
onChange: (event) => setPage(event.target.value),
|
|
1430
|
+
onBlur: () => props.setPage(page.trim()),
|
|
1431
|
+
onKeyDown: (event) => {
|
|
1432
|
+
if (event.key === "Enter") props.setPage(page.trim());
|
|
1433
|
+
},
|
|
1434
|
+
className: "h-8 text-xs"
|
|
1435
|
+
})
|
|
1436
|
+
]
|
|
1437
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1438
|
+
className: "flex-1 overflow-auto px-2 pb-3",
|
|
1439
|
+
children: [(message || !items.length) && /* @__PURE__ */ jsx("p", {
|
|
1440
|
+
className: "px-4 py-8 text-center text-muted-foreground",
|
|
1441
|
+
children: message ?? "No feedback here yet. Click a place on the page to comment."
|
|
1442
|
+
}), items.map((thread) => {
|
|
1443
|
+
const replies = thread.messageCount - 1;
|
|
1444
|
+
const plural = replies === 1 ? "y" : "ies";
|
|
1445
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
1446
|
+
type: "button",
|
|
1447
|
+
onClick: () => props.openThread(thread.id),
|
|
1448
|
+
className: "block w-full cursor-pointer border-b px-3 py-4 text-left hover:bg-muted",
|
|
1449
|
+
children: [
|
|
1450
|
+
/* @__PURE__ */ jsxs("span", {
|
|
1451
|
+
className: "mb-1.5 flex justify-between gap-2 text-[11px] text-muted-foreground",
|
|
1452
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
1453
|
+
"#",
|
|
1454
|
+
thread.number,
|
|
1455
|
+
" · ",
|
|
1456
|
+
thread.status
|
|
1457
|
+
] }), /* @__PURE__ */ jsx(Author, {
|
|
1458
|
+
name: thread.authorName,
|
|
1459
|
+
verified: thread.authorVerified
|
|
1460
|
+
})]
|
|
1461
|
+
}),
|
|
1462
|
+
/* @__PURE__ */ jsx("span", {
|
|
1463
|
+
className: "block break-words whitespace-pre-wrap",
|
|
1464
|
+
children: thread.excerpt
|
|
1465
|
+
}),
|
|
1466
|
+
/* @__PURE__ */ jsxs("span", {
|
|
1467
|
+
className: "mt-2 block text-[11px] text-muted-foreground",
|
|
1468
|
+
children: [
|
|
1469
|
+
thread.path,
|
|
1470
|
+
" ·",
|
|
1471
|
+
" ",
|
|
1472
|
+
replies ? `${replies} repl${plural}` : "No replies"
|
|
1473
|
+
]
|
|
1474
|
+
})
|
|
1475
|
+
]
|
|
1476
|
+
}, thread.id);
|
|
1477
|
+
})]
|
|
1478
|
+
})] })]
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
//#endregion
|
|
1482
|
+
//#region src/app.tsx
|
|
1483
|
+
function App(props) {
|
|
1484
|
+
const [shell, setShell] = useState(null);
|
|
1485
|
+
const { options } = props;
|
|
1486
|
+
useEffect(() => {
|
|
1487
|
+
const abort = new AbortController();
|
|
1488
|
+
const layer = new Layer(_virtual_fastbackr_styles_default);
|
|
1489
|
+
const container = document.createElement("div");
|
|
1490
|
+
container.className = "contents";
|
|
1491
|
+
layer.root.append(container);
|
|
1492
|
+
const loop = createFrameLoop(abort.signal);
|
|
1493
|
+
loop.add(() => layer.sync());
|
|
1494
|
+
const controller = new FeedbackController(options, layer, abort.signal);
|
|
1495
|
+
loop.add(() => controller.picker.paint());
|
|
1496
|
+
setShell({
|
|
1497
|
+
container,
|
|
1498
|
+
loop,
|
|
1499
|
+
controller
|
|
1500
|
+
});
|
|
1501
|
+
return () => {
|
|
1502
|
+
abort.abort();
|
|
1503
|
+
layer.destroy();
|
|
1504
|
+
};
|
|
1505
|
+
}, [options]);
|
|
1506
|
+
if (!shell) return null;
|
|
1507
|
+
return createPortal(/* @__PURE__ */ jsx(FrameContext, {
|
|
1508
|
+
value: shell.loop,
|
|
1509
|
+
children: /* @__PURE__ */ jsx(Feedback, {
|
|
1510
|
+
...props,
|
|
1511
|
+
controller: shell.controller
|
|
1512
|
+
})
|
|
1513
|
+
}), shell.container);
|
|
1514
|
+
}
|
|
1515
|
+
function Feedback(props) {
|
|
1516
|
+
const { controller, drafts, commentRequest, hideFab } = props;
|
|
1517
|
+
const state = useSyncExternalStore(controller.subscribe, controller.getSnapshot);
|
|
1518
|
+
const { reviewer, conversation, drawer, composer, page, error } = state;
|
|
1519
|
+
useLayoutEffect(() => {
|
|
1520
|
+
hideFab();
|
|
1521
|
+
}, [hideFab]);
|
|
1522
|
+
useEffect(() => {
|
|
1523
|
+
if (commentRequest) controller.comment();
|
|
1524
|
+
}, [controller, commentRequest]);
|
|
1525
|
+
useEffect(() => {
|
|
1526
|
+
const abort = new AbortController();
|
|
1527
|
+
document.addEventListener("keydown", (event) => {
|
|
1528
|
+
if (!plainKey(event)) return;
|
|
1529
|
+
if (event.key.toLowerCase() === "c" && controller.state.reviewer) {
|
|
1530
|
+
event.preventDefault();
|
|
1531
|
+
controller.startSelection();
|
|
1532
|
+
}
|
|
1533
|
+
const { composer, conversation, drawer } = controller.state;
|
|
1534
|
+
const panelOpen = Boolean(composer || conversation || drawer.open);
|
|
1535
|
+
if (event.key === "Escape" && !controller.picker.active && panelOpen) {
|
|
1536
|
+
event.preventDefault();
|
|
1537
|
+
event.stopPropagation();
|
|
1538
|
+
controller.closePanels();
|
|
1539
|
+
}
|
|
1540
|
+
}, { signal: abort.signal });
|
|
1541
|
+
return () => abort.abort();
|
|
1542
|
+
}, [controller]);
|
|
1543
|
+
const thread = conversation && /* @__PURE__ */ jsx(Conversation, {
|
|
1544
|
+
detail: conversation.detail,
|
|
1545
|
+
inDrawer: conversation.inDrawer,
|
|
1546
|
+
pageKey: page.key,
|
|
1547
|
+
drafts,
|
|
1548
|
+
showError: controller.showError,
|
|
1549
|
+
layout: state.groups,
|
|
1550
|
+
close: controller.closeConversation,
|
|
1551
|
+
backToList: () => void controller.loadDrawer(),
|
|
1552
|
+
toggleStatus: (thread) => void controller.toggleStatus(thread),
|
|
1553
|
+
reply: controller.reply,
|
|
1554
|
+
canDecide: reviewer?.verified === true,
|
|
1555
|
+
decideSuggestion: controller.decideSuggestion,
|
|
1556
|
+
loadScreenshot: controller.screenshot
|
|
1557
|
+
}, conversation.detail.thread.id);
|
|
1558
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1559
|
+
className: "font-sans text-sm text-foreground antialiased",
|
|
1560
|
+
children: [
|
|
1561
|
+
/* @__PURE__ */ jsx(Badges, {
|
|
1562
|
+
groups: state.groups,
|
|
1563
|
+
activeId: conversation ? conversation.detail.thread.id : null,
|
|
1564
|
+
open: controller.openBadge
|
|
1565
|
+
}),
|
|
1566
|
+
drawer.open && /* @__PURE__ */ jsx(Drawer, {
|
|
1567
|
+
status: drawer.status,
|
|
1568
|
+
page: drawer.page,
|
|
1569
|
+
list: drawer.list,
|
|
1570
|
+
detail: conversation?.inDrawer ? {
|
|
1571
|
+
title: `Comment #${conversation.detail.thread.number}`,
|
|
1572
|
+
content: thread
|
|
1573
|
+
} : null,
|
|
1574
|
+
setStatus: (status) => controller.setDrawerFilter({ status }),
|
|
1575
|
+
setPage: (page) => controller.setDrawerFilter({ page }),
|
|
1576
|
+
refresh: controller.refresh,
|
|
1577
|
+
openThread: (id) => void controller.openThread(id, true),
|
|
1578
|
+
close: controller.closeDrawer
|
|
1579
|
+
}),
|
|
1580
|
+
conversation && !conversation.inDrawer && thread,
|
|
1581
|
+
composer && reviewer && /* @__PURE__ */ jsx(Composer, {
|
|
1582
|
+
selection: composer,
|
|
1583
|
+
drafts,
|
|
1584
|
+
pageKey: page.key,
|
|
1585
|
+
canScreenshot: controller.canScreenshot,
|
|
1586
|
+
existing: controller.existing(composer.target),
|
|
1587
|
+
openThread: (id) => void controller.openThread(id, false),
|
|
1588
|
+
close: controller.closeComposer,
|
|
1589
|
+
save: controller.save
|
|
1590
|
+
}),
|
|
1591
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
1592
|
+
reviewer,
|
|
1593
|
+
count: state.threads.length,
|
|
1594
|
+
guestName: state.guestName,
|
|
1595
|
+
canSignIn: Boolean(props.options.signIn),
|
|
1596
|
+
comment: controller.startSelection,
|
|
1597
|
+
toggleDrawer: controller.toggleDrawer,
|
|
1598
|
+
minimize: props.minimize,
|
|
1599
|
+
showFab: props.showFab,
|
|
1600
|
+
flashRequest: props.flashRequest,
|
|
1601
|
+
close: props.close,
|
|
1602
|
+
continueAsGuest: controller.continueAsGuest,
|
|
1603
|
+
signIn: () => void controller.signIn(),
|
|
1604
|
+
changeName: controller.changeName
|
|
1605
|
+
}, reviewer?.id ?? "signed-out"),
|
|
1606
|
+
error && /* @__PURE__ */ jsx("div", {
|
|
1607
|
+
role: "alert",
|
|
1608
|
+
className: "pointer-events-auto fixed top-4 left-1/2 max-w-[440px] -translate-x-1/2 rounded-2xl bg-destructive px-4 py-2.5 text-white shadow-lg",
|
|
1609
|
+
children: error.message
|
|
1610
|
+
}, error.id)
|
|
1611
|
+
]
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
//#endregion
|
|
1615
|
+
export { App as default };
|