fluxy-bot 0.2.37 → 0.2.39

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.
@@ -0,0 +1 @@
1
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,: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-y-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-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-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-green-500:oklch(72.3% .219 149.579);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--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: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--animate-bounce:bounce 1s infinite;--blur-md:12px;--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);--color-background:#212121;--color-foreground:#f5f5f5}}@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,ui-sans-serif, system-ui, 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;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{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}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.-top-1\.5{top:calc(var(--spacing) * -1.5)}.top-1\/2{top:50%}.top-2{top:calc(var(--spacing) * 2)}.top-4{top:calc(var(--spacing) * 4)}.top-full{top:100%}.-right-1\.5{right:calc(var(--spacing) * -1.5)}.right-0{right:calc(var(--spacing) * 0)}.right-2{right:calc(var(--spacing) * 2)}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.left-0{left:calc(var(--spacing) * 0)}.left-1\/2{left:50%}.left-3{left:calc(var(--spacing) * 3)}.z-10{z-index:10}.z-50{z-index:50}.z-\[200\]{z-index:200}.my-1\.5{margin-block:calc(var(--spacing) * 1.5)}.my-2{margin-block:calc(var(--spacing) * 2)}.-mt-3{margin-top:calc(var(--spacing) * -3)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-px{margin-top:1px}.mr-1\.5{margin-right:calc(var(--spacing) * 1.5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.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)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-0\.5{margin-left:calc(var(--spacing) * .5)}.block{display:block}.flex{display:flex}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-1{height:calc(var(--spacing) * 1)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.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-11{height:calc(var(--spacing) * 11)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-28{height:calc(var(--spacing) * 28)}.h-\[18px\]{height:18px}.h-\[22px\]{height:22px}.h-\[180px\]{height:180px}.h-dvh{height:100dvh}.h-full{height:100%}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-\[85vh\]{max-height:85vh}.min-h-0{min-height:calc(var(--spacing) * 0)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-10{width:calc(var(--spacing) * 10)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-14{width:calc(var(--spacing) * 14)}.w-28{width:calc(var(--spacing) * 28)}.w-\[18px\]{width:18px}.w-auto{width:auto}.w-full{width:100%}.max-w-\[75\%\]{max-width:75%}.max-w-\[90vw\]{max-width:90vw}.max-w-\[92\%\]{max-width:92%}.max-w-\[280px\]{max-width:280px}.max-w-\[320px\]{max-width:320px}.max-w-\[480px\]{max-width:480px}.max-w-none{max-width:none}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[160px\]{min-width:160px}.min-w-\[180px\]{min-width:180px}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-\[18px\]{--tw-translate-x:18px;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-bounce{animation:var(--animate-bounce)}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize-none{resize:none}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-start{justify-content:flex-start}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.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-3\.5{gap:calc(var(--spacing) * 3.5)}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.75rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\[24px\]{border-radius:24px}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-\[1\.5px\]{border-style:var(--tw-border-style);border-width:1.5px}.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-\[\#AF27E3\]\/20{border-color:#af27e333}.border-\[\#AF27E3\]\/30{border-color:#af27e34d}.border-\[\#AF27E3\]\/40{border-color:#af27e366}.border-amber-500\/20{border-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/20{border-color:color-mix(in oklab,var(--color-amber-500) 20%,transparent)}}.border-border{border-color:#3a3a3a}.border-emerald-500\/15{border-color:#00bb7f26}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\/15{border-color:color-mix(in oklab,var(--color-emerald-500) 15%,transparent)}}.border-emerald-500\/20{border-color:#00bb7f33}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\/20{border-color:color-mix(in oklab,var(--color-emerald-500) 20%,transparent)}}.border-muted-foreground\/30{border-color:#9999994d}.border-red-500\/15{border-color:#fb2c3626}@supports (color:color-mix(in lab,red,red)){.border-red-500\/15{border-color:color-mix(in oklab,var(--color-red-500) 15%,transparent)}}.border-red-500\/20{border-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.border-red-500\/20{border-color:color-mix(in oklab,var(--color-red-500) 20%,transparent)}}.border-white\/10{border-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.border-white\/10{border-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.border-white\/20{border-color:#fff3}@supports (color:color-mix(in lab,red,red)){.border-white\/20{border-color:color-mix(in oklab,var(--color-white) 20%,transparent)}}.border-white\/\[0\.04\]{border-color:#ffffff0a}@supports (color:color-mix(in lab,red,red)){.border-white\/\[0\.04\]{border-color:color-mix(in oklab,var(--color-white) 4%,transparent)}}.border-white\/\[0\.06\]{border-color:#ffffff0f}@supports (color:color-mix(in lab,red,red)){.border-white\/\[0\.06\]{border-color:color-mix(in oklab,var(--color-white) 6%,transparent)}}.border-white\/\[0\.08\]{border-color:#ffffff14}@supports (color:color-mix(in lab,red,red)){.border-white\/\[0\.08\]{border-color:color-mix(in oklab,var(--color-white) 8%,transparent)}}.border-t-\[\#04D1FE\]{border-top-color:#04d1fe}.border-t-foreground{border-top-color:#f5f5f5}.border-t-primary{border-top-color:#3c8fff}.bg-\[\#222\]{background-color:#222}.bg-\[\#181818\]{background-color:#181818}.bg-\[\#AF27E3\]\/10{background-color:#af27e31a}.bg-\[\#AF27E3\]\/15{background-color:#af27e326}.bg-\[\#AF27E3\]\/\[0\.06\]{background-color:#af27e30f}.bg-amber-500\/8{background-color:#f99c0014}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/8{background-color:color-mix(in oklab,var(--color-amber-500) 8%,transparent)}}.bg-amber-500\/15{background-color:#f99c0026}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/15{background-color:color-mix(in oklab,var(--color-amber-500) 15%,transparent)}}.bg-background{background-color:#212121}.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-black\/85{background-color:#000000d9}@supports (color:color-mix(in lab,red,red)){.bg-black\/85{background-color:color-mix(in oklab,var(--color-black) 85%,transparent)}}.bg-black\/90{background-color:#000000e6}@supports (color:color-mix(in lab,red,red)){.bg-black\/90{background-color:color-mix(in oklab,var(--color-black) 90%,transparent)}}.bg-destructive{background-color:#fd486b}.bg-destructive\/10{background-color:#fd486b1a}.bg-emerald-500\/8{background-color:#00bb7f14}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/8{background-color:color-mix(in oklab,var(--color-emerald-500) 8%,transparent)}}.bg-emerald-500\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/10{background-color:color-mix(in oklab,var(--color-emerald-500) 10%,transparent)}}.bg-emerald-500\/15{background-color:#00bb7f26}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/15{background-color:color-mix(in oklab,var(--color-emerald-500) 15%,transparent)}}.bg-green-500{background-color:var(--color-green-500)}.bg-muted{background-color:#333}.bg-muted-foreground{background-color:#999}.bg-muted-foreground\/60{background-color:#9999}.bg-popover{background-color:#2a2a2a}.bg-primary{background-color:#3c8fff}.bg-red-500{background-color:var(--color-red-500)}.bg-red-500\/8{background-color:#fb2c3614}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/8{background-color:color-mix(in oklab,var(--color-red-500) 8%,transparent)}}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500) 10%,transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\/5{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.bg-white\/5{background-color:color-mix(in oklab,var(--color-white) 5%,transparent)}}.bg-white\/10{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.bg-white\/10{background-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.bg-white\/20{background-color:#fff3}@supports (color:color-mix(in lab,red,red)){.bg-white\/20{background-color:color-mix(in oklab,var(--color-white) 20%,transparent)}}.bg-white\/70{background-color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.bg-white\/70{background-color:color-mix(in oklab,var(--color-white) 70%,transparent)}}.bg-white\/\[0\.02\]{background-color:#ffffff05}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.02\]{background-color:color-mix(in oklab,var(--color-white) 2%,transparent)}}.bg-white\/\[0\.03\]{background-color:#ffffff08}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.03\]{background-color:color-mix(in oklab,var(--color-white) 3%,transparent)}}.bg-white\/\[0\.04\]{background-color:#ffffff0a}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.04\]{background-color:color-mix(in oklab,var(--color-white) 4%,transparent)}}.bg-white\/\[0\.05\]{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.05\]{background-color:color-mix(in oklab,var(--color-white) 5%,transparent)}}.bg-white\/\[0\.06\]{background-color:#ffffff0f}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.06\]{background-color:color-mix(in oklab,var(--color-white) 6%,transparent)}}.bg-white\/\[0\.08\]{background-color:#ffffff14}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.08\]{background-color:color-mix(in oklab,var(--color-white) 8%,transparent)}}.fill-current{fill:currentColor}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-0\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:calc(var(--spacing) * 1)}.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-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-7{padding-inline:calc(var(--spacing) * 7)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.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-8{padding-block:calc(var(--spacing) * 8)}.pt-2\.5{padding-top:calc(var(--spacing) * 2.5)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pr-0\.5{padding-right:calc(var(--spacing) * .5)}.pr-10{padding-right:calc(var(--spacing) * 10)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pb-8{padding-bottom:calc(var(--spacing) * 8)}.pl-1{padding-left:calc(var(--spacing) * 1)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.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-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.8rem\]{font-size:.8rem}.text-\[8px\]{font-size:8px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.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-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#AF27E3\]{color:#af27e3}.text-\[\#AF27E3\]\/60{color:#af27e399}.text-amber-400{color:var(--color-amber-400)}.text-amber-400\/60{color:#fcbb0099}@supports (color:color-mix(in lab,red,red)){.text-amber-400\/60{color:color-mix(in oklab,var(--color-amber-400) 60%,transparent)}}.text-amber-400\/70{color:#fcbb00b3}@supports (color:color-mix(in lab,red,red)){.text-amber-400\/70{color:color-mix(in oklab,var(--color-amber-400) 70%,transparent)}}.text-amber-400\/90{color:#fcbb00e6}@supports (color:color-mix(in lab,red,red)){.text-amber-400\/90{color:color-mix(in oklab,var(--color-amber-400) 90%,transparent)}}.text-destructive{color:#fd486b}.text-destructive-foreground{color:#fff}.text-emerald-400{color:var(--color-emerald-400)}.text-emerald-400\/60{color:#00d29499}@supports (color:color-mix(in lab,red,red)){.text-emerald-400\/60{color:color-mix(in oklab,var(--color-emerald-400) 60%,transparent)}}.text-emerald-400\/90{color:#00d294e6}@supports (color:color-mix(in lab,red,red)){.text-emerald-400\/90{color:color-mix(in oklab,var(--color-emerald-400) 90%,transparent)}}.text-foreground{color:#f5f5f5}.text-gray-400{color:var(--color-gray-400)}.text-gray-900{color:var(--color-gray-900)}.text-muted-foreground{color:#999}.text-muted-foreground\/50{color:#99999980}.text-primary-foreground{color:#fff}.text-primary-foreground\/60{color:#fff9}.text-red-400{color:var(--color-red-400)}.text-red-400\/70{color:#ff6568b3}@supports (color:color-mix(in lab,red,red)){.text-red-400\/70{color:color-mix(in oklab,var(--color-red-400) 70%,transparent)}}.text-red-400\/90{color:#ff6568e6}@supports (color:color-mix(in lab,red,red)){.text-red-400\/90{color:color-mix(in oklab,var(--color-red-400) 90%,transparent)}}.text-white{color:var(--color-white)}.text-white\/20{color:#fff3}@supports (color:color-mix(in lab,red,red)){.text-white\/20{color:color-mix(in oklab,var(--color-white) 20%,transparent)}}.text-white\/25{color:#ffffff40}@supports (color:color-mix(in lab,red,red)){.text-white\/25{color:color-mix(in oklab,var(--color-white) 25%,transparent)}}.text-white\/30{color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.text-white\/30{color:color-mix(in oklab,var(--color-white) 30%,transparent)}}.text-white\/35{color:#ffffff59}@supports (color:color-mix(in lab,red,red)){.text-white\/35{color:color-mix(in oklab,var(--color-white) 35%,transparent)}}.text-white\/40{color:#fff6}@supports (color:color-mix(in lab,red,red)){.text-white\/40{color:color-mix(in oklab,var(--color-white) 40%,transparent)}}.text-white\/50{color:#ffffff80}@supports (color:color-mix(in lab,red,red)){.text-white\/50{color:color-mix(in oklab,var(--color-white) 50%,transparent)}}.text-white\/60{color:#fff9}@supports (color:color-mix(in lab,red,red)){.text-white\/60{color:color-mix(in oklab,var(--color-white) 60%,transparent)}}.text-white\/70{color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.text-white\/70{color:color-mix(in oklab,var(--color-white) 70%,transparent)}}.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,)}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-75{opacity:.75}.opacity-100{opacity:1}.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-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-sm{--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-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)}.backdrop-blur-md{--tw-backdrop-blur:blur(var(--blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,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-\[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-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))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.will-change-transform{will-change:transform}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}@media(hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-gray-400::placeholder{color:var(--color-gray-400)}.placeholder\:text-muted-foreground::placeholder{color:#999}.placeholder\:text-white\/20::placeholder{color:#fff3}@supports (color:color-mix(in lab,red,red)){.placeholder\:text-white\/20::placeholder{color:color-mix(in oklab,var(--color-white) 20%,transparent)}}@media(hover:hover){.hover\:border-white\/10:hover{border-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.hover\:border-white\/10:hover{border-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.hover\:border-white\/15:hover{border-color:#ffffff26}@supports (color:color-mix(in lab,red,red)){.hover\:border-white\/15:hover{border-color:color-mix(in oklab,var(--color-white) 15%,transparent)}}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-white\/20:hover{background-color:#fff3}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/20:hover{background-color:color-mix(in oklab,var(--color-white) 20%,transparent)}}.hover\:bg-white\/30:hover{background-color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/30:hover{background-color:color-mix(in oklab,var(--color-white) 30%,transparent)}}.hover\:bg-white\/\[0\.1\]:hover{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/\[0\.1\]:hover{background-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.hover\:bg-white\/\[0\.02\]:hover{background-color:#ffffff05}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/\[0\.02\]:hover{background-color:color-mix(in oklab,var(--color-white) 2%,transparent)}}.hover\:bg-white\/\[0\.04\]:hover{background-color:#ffffff0a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/\[0\.04\]:hover{background-color:color-mix(in oklab,var(--color-white) 4%,transparent)}}.hover\:bg-white\/\[0\.06\]:hover{background-color:#ffffff0f}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/\[0\.06\]:hover{background-color:color-mix(in oklab,var(--color-white) 6%,transparent)}}.hover\:bg-white\/\[0\.08\]:hover{background-color:#ffffff14}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/\[0\.08\]:hover{background-color:color-mix(in oklab,var(--color-white) 8%,transparent)}}.hover\:bg-white\/\[0\.09\]:hover{background-color:#ffffff17}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/\[0\.09\]:hover{background-color:color-mix(in oklab,var(--color-white) 9%,transparent)}}.hover\:text-foreground:hover{color:#f5f5f5}.hover\:text-gray-600:hover{color:var(--color-gray-600)}.hover\:text-white:hover{color:var(--color-white)}.hover\:text-white\/40:hover{color:#fff6}@supports (color:color-mix(in lab,red,red)){.hover\:text-white\/40:hover{color:color-mix(in oklab,var(--color-white) 40%,transparent)}}.hover\:text-white\/50:hover{color:#ffffff80}@supports (color:color-mix(in lab,red,red)){.hover\:text-white\/50:hover{color:color-mix(in oklab,var(--color-white) 50%,transparent)}}.hover\:text-white\/60:hover{color:#fff9}@supports (color:color-mix(in lab,red,red)){.hover\:text-white\/60:hover{color:color-mix(in oklab,var(--color-white) 60%,transparent)}}.hover\:opacity-80:hover{opacity:.8}.hover\:opacity-90:hover{opacity:.9}}.focus\:border-\[\#AF27E3\]\/30:focus{border-color:#af27e34d}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.\[\&\>\*\:first-child\]\:mt-0>:first-child{margin-top:calc(var(--spacing) * 0)}.\[\&\>\*\:last-child\]\:mb-0>:last-child{margin-bottom:calc(var(--spacing) * 0)}}body{background-color:var(--color-background);color:var(--color-foreground);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}::selection{background-color:#af27e340}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:#3a3a3a;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#4a4a4a}.text-gradient{color:#0000;-webkit-text-fill-color:transparent;background-image:linear-gradient(135deg,#04d1fe,#af27e3,#fb4072);-webkit-background-clip:text;background-clip:text}.bg-gradient-brand{background-image:linear-gradient(135deg,#04d1fe,#af27e3,#fb4072)}.glow-border{box-shadow:0 0 0 1px #af27e31a,0 0 20px -5px #af27e326}.animated-border{position:relative;overflow:hidden}.animated-border:before{content:"";background:conic-gradient(#04d1fe,#af27e3,#fb4072,#04d1fe);animation:3s linear infinite border-spin;position:absolute;inset:-150%}.animated-border>*{z-index:1;position:relative}.animated-border-slow:before{animation-duration:5s}.input-glow:focus{border-color:#af27e366;box-shadow:0 0 0 1px #af27e326,0 0 20px -5px #af27e340,0 0 4px -1px #04d1fe1a}@keyframes border-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}
@@ -1 +1 @@
1
- import{a as o,j as e,R as n,O as r}from"./globals-CnbiE5KJ.js";function a(){const t=()=>{window.parent?.postMessage({type:"fluxy:onboard-complete"},"*")};return e.jsx(r,{onComplete:t})}o.createRoot(document.getElementById("root")).render(e.jsx(n.StrictMode,{children:e.jsx(a,{})}));
1
+ import{a as o,j as e,R as n,O as r}from"./globals--DVlL_N5.js";function a(){const t=()=>{window.parent?.postMessage({type:"fluxy:onboard-complete"},"*")};return e.jsx(r,{onComplete:t})}o.createRoot(document.getElementById("root")).render(e.jsx(n.StrictMode,{children:e.jsx(a,{})}));
@@ -4,9 +4,9 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content" />
6
6
  <title>Fluxy Chat</title>
7
- <script type="module" crossorigin src="/fluxy/assets/fluxy-tSkoe-BG.js"></script>
8
- <link rel="modulepreload" crossorigin href="/fluxy/assets/globals-CnbiE5KJ.js">
9
- <link rel="stylesheet" crossorigin href="/fluxy/assets/globals-CZf7hjQW.css">
7
+ <script type="module" crossorigin src="/fluxy/assets/fluxy-CLjt1lU-.js"></script>
8
+ <link rel="modulepreload" crossorigin href="/fluxy/assets/globals--DVlL_N5.js">
9
+ <link rel="stylesheet" crossorigin href="/fluxy/assets/globals-DMXu7CFU.css">
10
10
  </head>
11
11
  <body class="bg-background text-foreground">
12
12
  <div id="root"></div>
@@ -4,9 +4,9 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content" />
6
6
  <title>Fluxy Setup</title>
7
- <script type="module" crossorigin src="/fluxy/assets/onboard-B_QyBFoO.js"></script>
8
- <link rel="modulepreload" crossorigin href="/fluxy/assets/globals-CnbiE5KJ.js">
9
- <link rel="stylesheet" crossorigin href="/fluxy/assets/globals-CZf7hjQW.css">
7
+ <script type="module" crossorigin src="/fluxy/assets/onboard-CdR_xRWS.js"></script>
8
+ <link rel="modulepreload" crossorigin href="/fluxy/assets/globals--DVlL_N5.js">
9
+ <link rel="stylesheet" crossorigin href="/fluxy/assets/globals-DMXu7CFU.css">
10
10
  </head>
11
11
  <body class="bg-background text-foreground">
12
12
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxy-bot",
3
- "version": "0.2.37",
3
+ "version": "0.2.39",
4
4
  "description": "Self-hosted AI bot — run your own AI assistant from anywhere",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -55,12 +55,14 @@
55
55
  "date-fns": "^4.1.0",
56
56
  "express": "^5.2.1",
57
57
  "framer-motion": "^12.34.3",
58
+ "jose": "^6.1.3",
58
59
  "lucide-react": "^0.575.0",
59
60
  "postcss": "^8.5.6",
60
61
  "radix-ui": "^1.4.3",
61
62
  "react": "^19.2.4",
62
63
  "react-dom": "^19.2.4",
63
64
  "react-markdown": "^10.1.0",
65
+ "react-router-dom": "^7.13.1",
64
66
  "react-syntax-highlighter": "^16.1.0",
65
67
  "recharts": "^3.7.0",
66
68
  "remark-gfm": "^4.0.1",
package/shared/auth.ts ADDED
@@ -0,0 +1,55 @@
1
+ import crypto from 'crypto';
2
+ import { loadConfig, saveConfig } from './config.js';
3
+ import { SignJWT, jwtVerify } from 'jose';
4
+
5
+ /** Returns the JWT secret as Uint8Array, generating one if missing */
6
+ export function ensureJwtSecret(): Uint8Array {
7
+ const config = loadConfig();
8
+ if (!config.jwtSecret) {
9
+ config.jwtSecret = crypto.randomBytes(32).toString('hex');
10
+ saveConfig(config);
11
+ }
12
+ return new TextEncoder().encode(config.jwtSecret);
13
+ }
14
+
15
+ /** Sign a JWT with HS256, 30-day expiry */
16
+ export async function signToken(payload: Record<string, unknown>): Promise<string> {
17
+ const secret = ensureJwtSecret();
18
+ return new SignJWT(payload)
19
+ .setProtectedHeader({ alg: 'HS256' })
20
+ .setIssuedAt()
21
+ .setExpirationTime('30d')
22
+ .sign(secret);
23
+ }
24
+
25
+ /** Verify a JWT, returns payload or null */
26
+ export async function verifyToken(token: string): Promise<Record<string, unknown> | null> {
27
+ try {
28
+ const secret = ensureJwtSecret();
29
+ const { payload } = await jwtVerify(token, secret);
30
+ return payload as Record<string, unknown>;
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+
36
+ /** Extract a named cookie from a raw Cookie header */
37
+ export function parseCookie(header: string | undefined, name: string): string | null {
38
+ if (!header) return null;
39
+ const match = header.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
40
+ return match ? match[1] : null;
41
+ }
42
+
43
+ /** Build a Set-Cookie header to create a session */
44
+ export function buildSessionCookie(token: string, secure: boolean): string {
45
+ let cookie = `fluxy_session=${token}; Path=/; HttpOnly; SameSite=Lax; Max-Age=2592000`;
46
+ if (secure) cookie += '; Secure';
47
+ return cookie;
48
+ }
49
+
50
+ /** Build a Set-Cookie header to clear the session */
51
+ export function buildClearCookie(secure: boolean): string {
52
+ let cookie = `fluxy_session=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`;
53
+ if (secure) cookie += '; Secure';
54
+ return cookie;
55
+ }
package/shared/config.ts CHANGED
@@ -17,6 +17,7 @@ export interface BotConfig {
17
17
  url: string;
18
18
  };
19
19
  tunnelUrl?: string;
20
+ jwtSecret?: string;
20
21
  }
21
22
 
22
23
  const DEFAULTS: BotConfig = {
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
2
  import ReactDOM from 'react-dom/client';
3
- import { ArrowLeft, MoreVertical, Trash2, Wand2 } from 'lucide-react';
3
+ import { ArrowLeft, MoreVertical, Trash2, Wand2, LogIn } from 'lucide-react';
4
4
  import { WsClient } from './src/lib/ws-client';
5
5
  import { useFluxyChat } from './src/hooks/useFluxyChat';
6
6
  import OnboardWizard from './OnboardWizard';
@@ -8,6 +8,77 @@ import MessageList from './src/components/Chat/MessageList';
8
8
  import InputBar from './src/components/Chat/InputBar';
9
9
  import './src/styles/globals.css';
10
10
 
11
+ type AuthState = 'checking' | 'authenticated' | 'unauthenticated' | 'first-run';
12
+
13
+ function LoginForm({ botName, onSuccess }: { botName: string; onSuccess: () => void }) {
14
+ const [username, setUsername] = useState('');
15
+ const [password, setPassword] = useState('');
16
+ const [error, setError] = useState('');
17
+ const [loading, setLoading] = useState(false);
18
+
19
+ const handleSubmit = async (e: React.FormEvent) => {
20
+ e.preventDefault();
21
+ setError('');
22
+ setLoading(true);
23
+ try {
24
+ const res = await fetch('/api/auth/login', {
25
+ method: 'POST',
26
+ headers: { 'Content-Type': 'application/json' },
27
+ body: JSON.stringify({ username, password }),
28
+ });
29
+ const data = await res.json();
30
+ if (data.ok) {
31
+ onSuccess();
32
+ } else {
33
+ setError(data.error || 'Login failed');
34
+ }
35
+ } catch {
36
+ setError('Connection error');
37
+ } finally {
38
+ setLoading(false);
39
+ }
40
+ };
41
+
42
+ return (
43
+ <div className="flex-1 flex flex-col items-center justify-center px-6 py-8">
44
+ <img src="/fluxy.png" alt={botName} className="h-10 w-auto mb-4" />
45
+ <h2 className="text-lg font-semibold mb-1">Sign in to chat</h2>
46
+ <p className="text-sm text-muted-foreground mb-6">Enter your portal credentials</p>
47
+
48
+ <form onSubmit={handleSubmit} className="w-full max-w-[280px] flex flex-col gap-3">
49
+ <input
50
+ type="text"
51
+ placeholder="Username"
52
+ value={username}
53
+ onChange={(e) => setUsername(e.target.value)}
54
+ autoComplete="username"
55
+ className="w-full bg-white/[0.05] border border-white/[0.08] rounded-xl px-4 py-3 text-sm text-foreground placeholder:text-muted-foreground outline-none input-glow"
56
+ />
57
+ <input
58
+ type="password"
59
+ placeholder="Password"
60
+ value={password}
61
+ onChange={(e) => setPassword(e.target.value)}
62
+ autoComplete="current-password"
63
+ className="w-full bg-white/[0.05] border border-white/[0.08] rounded-xl px-4 py-3 text-sm text-foreground placeholder:text-muted-foreground outline-none input-glow"
64
+ />
65
+ {error && (
66
+ <div className="bg-red-500/8 border border-red-500/15 rounded-lg px-3 py-2 text-xs text-red-400">
67
+ {error}
68
+ </div>
69
+ )}
70
+ <button
71
+ type="submit"
72
+ disabled={loading || !username || !password}
73
+ className="w-full bg-gradient-brand text-white font-medium rounded-full py-3 text-sm hover:opacity-90 transition-opacity disabled:opacity-50"
74
+ >
75
+ {loading ? 'Signing in...' : 'Sign In'}
76
+ </button>
77
+ </form>
78
+ </div>
79
+ );
80
+ }
81
+
11
82
  function FluxyApp() {
12
83
  const clientRef = useRef<WsClient | null>(null);
13
84
  const [connected, setConnected] = useState(false);
@@ -16,10 +87,40 @@ function FluxyApp() {
16
87
  const [menuOpen, setMenuOpen] = useState(false);
17
88
  const [showWizard, setShowWizard] = useState(false);
18
89
  const [reloadTrigger, setReloadTrigger] = useState(0);
90
+ const [authState, setAuthState] = useState<AuthState>('checking');
19
91
  const menuRef = useRef<HTMLDivElement>(null);
20
92
  const wasConnected = useRef(false);
21
93
 
94
+ // Check auth on mount
95
+ useEffect(() => {
96
+ (async () => {
97
+ try {
98
+ // Check if authenticated
99
+ const meRes = await fetch('/api/auth/me');
100
+ if (meRes.ok) {
101
+ const me = await meRes.json();
102
+ if (me.authenticated) { setAuthState('authenticated'); return; }
103
+ }
104
+
105
+ // Not authenticated — check if credentials are configured
106
+ const cfgRes = await fetch('/api/auth/configured');
107
+ const cfg = await cfgRes.json();
108
+ if (!cfg.configured) {
109
+ setAuthState('first-run'); // No credentials yet — allow chat for onboarding
110
+ } else {
111
+ setAuthState('unauthenticated');
112
+ }
113
+ } catch {
114
+ // Worker not ready — allow (onboarding scenario)
115
+ setAuthState('first-run');
116
+ }
117
+ })();
118
+ }, []);
119
+
120
+ // Connect WebSocket only when authenticated or first-run
22
121
  useEffect(() => {
122
+ if (authState !== 'authenticated' && authState !== 'first-run') return;
123
+
23
124
  const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
24
125
  const host = location.host;
25
126
  const client = new WsClient(`${proto}//${host}/fluxy/ws`);
@@ -27,14 +128,12 @@ function FluxyApp() {
27
128
 
28
129
  const unsub = client.onStatus((isConnected) => {
29
130
  setConnected(isConnected);
30
- // On reconnect, trigger a reload from DB to catch missed messages
31
131
  if (isConnected && wasConnected.current) {
32
132
  setReloadTrigger((n) => n + 1);
33
133
  }
34
134
  wasConnected.current = isConnected;
35
135
  });
36
136
 
37
- // Forward rebuild/HMR events to parent (dashboard) via postMessage
38
137
  const unsubRebuilding = client.on('app:rebuilding', () => {
39
138
  console.log('[fluxy] app:rebuilding received');
40
139
  window.parent?.postMessage({ type: 'fluxy:rebuilding' }, '*');
@@ -61,10 +160,11 @@ function FluxyApp() {
61
160
  unsubBuildError();
62
161
  unsubHmr();
63
162
  client.disconnect();
163
+ clientRef.current = null;
64
164
  };
65
- }, []);
165
+ }, [authState]);
66
166
 
67
- // Try to load settings (will work when worker is up, fail silently when down)
167
+ // Load settings
68
168
  useEffect(() => {
69
169
  fetch('/api/settings')
70
170
  .then((r) => r.json())
@@ -88,6 +188,8 @@ function FluxyApp() {
88
188
  const { messages, streaming, streamBuffer, tools, sendMessage, stopStreaming, clearContext } =
89
189
  useFluxyChat(clientRef.current, reloadTrigger);
90
190
 
191
+ const showChat = authState === 'authenticated' || authState === 'first-run';
192
+
91
193
  return (
92
194
  <div className="flex flex-col h-dvh overflow-hidden">
93
195
  {/* Header */}
@@ -101,60 +203,76 @@ function FluxyApp() {
101
203
  </button>
102
204
  <img src="/fluxy.png" alt={botName} className="h-5 w-auto" />
103
205
  <span className="text-sm font-semibold">{botName}</span>
104
- <div className={`h-2 w-2 rounded-full ${connected ? 'bg-green-500' : 'bg-red-500'}`} />
206
+ {showChat && <div className={`h-2 w-2 rounded-full ${connected ? 'bg-green-500' : 'bg-red-500'}`} />}
105
207
  <div className="flex-1" />
106
- <div className="relative" ref={menuRef}>
107
- <button
108
- onClick={() => setMenuOpen((v) => !v)}
109
- className="flex items-center justify-center h-7 w-7 rounded-full text-muted-foreground hover:text-foreground hover:bg-white/[0.06] transition-colors"
110
- >
111
- <MoreVertical className="h-4 w-4" />
112
- </button>
113
- {menuOpen && (
114
- <div className="absolute right-0 top-full mt-1 min-w-[160px] rounded-md border border-border bg-popover py-1 shadow-lg z-50">
115
- <button
116
- onClick={() => { setShowWizard(true); setMenuOpen(false); }}
117
- className="flex w-full items-center gap-2 px-3 py-2 text-sm text-muted-foreground hover:text-foreground hover:bg-white/[0.06] transition-colors"
118
- >
119
- <Wand2 className="h-4 w-4" />
120
- Setup Wizard
121
- </button>
122
- <button
123
- onClick={() => { clearContext(); setMenuOpen(false); }}
124
- className="flex w-full items-center gap-2 px-3 py-2 text-sm text-muted-foreground hover:text-foreground hover:bg-white/[0.06] transition-colors"
125
- >
126
- <Trash2 className="h-4 w-4" />
127
- Clear context
128
- </button>
129
- </div>
130
- )}
131
- </div>
208
+ {showChat && (
209
+ <div className="relative" ref={menuRef}>
210
+ <button
211
+ onClick={() => setMenuOpen((v) => !v)}
212
+ className="flex items-center justify-center h-7 w-7 rounded-full text-muted-foreground hover:text-foreground hover:bg-white/[0.06] transition-colors"
213
+ >
214
+ <MoreVertical className="h-4 w-4" />
215
+ </button>
216
+ {menuOpen && (
217
+ <div className="absolute right-0 top-full mt-1 min-w-[160px] rounded-md border border-border bg-popover py-1 shadow-lg z-50">
218
+ <button
219
+ onClick={() => { setShowWizard(true); setMenuOpen(false); }}
220
+ className="flex w-full items-center gap-2 px-3 py-2 text-sm text-muted-foreground hover:text-foreground hover:bg-white/[0.06] transition-colors"
221
+ >
222
+ <Wand2 className="h-4 w-4" />
223
+ Setup Wizard
224
+ </button>
225
+ <button
226
+ onClick={() => { clearContext(); setMenuOpen(false); }}
227
+ className="flex w-full items-center gap-2 px-3 py-2 text-sm text-muted-foreground hover:text-foreground hover:bg-white/[0.06] transition-colors"
228
+ >
229
+ <Trash2 className="h-4 w-4" />
230
+ Clear context
231
+ </button>
232
+ </div>
233
+ )}
234
+ </div>
235
+ )}
132
236
  </div>
133
237
 
134
- {/* Chat body */}
135
- <div className="flex-1 min-h-0 flex flex-col overflow-hidden">
136
- <MessageList messages={messages} streaming={streaming} streamBuffer={streamBuffer} tools={tools} />
137
- <InputBar onSend={sendMessage} onStop={stopStreaming} streaming={streaming} whisperEnabled={whisperEnabled} />
138
- </div>
238
+ {/* Auth gate or chat */}
239
+ {authState === 'checking' && (
240
+ <div className="flex-1 flex items-center justify-center">
241
+ <div className="h-6 w-6 border-2 border-muted-foreground/30 border-t-foreground rounded-full animate-spin" />
242
+ </div>
243
+ )}
139
244
 
140
- {/* Setup Wizard overlay */}
141
- {showWizard && (
142
- <OnboardWizard
143
- onComplete={() => {
144
- setShowWizard(false);
145
- // Reload settings (bot name, whisper, etc.)
146
- fetch('/api/settings')
147
- .then((r) => r.json())
148
- .then((s) => {
149
- if (s.agent_name) setBotName(s.agent_name);
150
- setWhisperEnabled(s.whisper_enabled === 'true');
151
- })
152
- .catch(() => {});
153
- // Notify dashboard so it can refresh
154
- window.parent?.postMessage({ type: 'fluxy:onboard-complete' }, '*');
155
- }}
245
+ {authState === 'unauthenticated' && (
246
+ <LoginForm
247
+ botName={botName}
248
+ onSuccess={() => setAuthState('authenticated')}
156
249
  />
157
250
  )}
251
+
252
+ {showChat && (
253
+ <>
254
+ <div className="flex-1 min-h-0 flex flex-col overflow-hidden">
255
+ <MessageList messages={messages} streaming={streaming} streamBuffer={streamBuffer} tools={tools} />
256
+ <InputBar onSend={sendMessage} onStop={stopStreaming} streaming={streaming} whisperEnabled={whisperEnabled} />
257
+ </div>
258
+
259
+ {showWizard && (
260
+ <OnboardWizard
261
+ onComplete={() => {
262
+ setShowWizard(false);
263
+ fetch('/api/settings')
264
+ .then((r) => r.json())
265
+ .then((s) => {
266
+ if (s.agent_name) setBotName(s.agent_name);
267
+ setWhisperEnabled(s.whisper_enabled === 'true');
268
+ })
269
+ .catch(() => {});
270
+ window.parent?.postMessage({ type: 'fluxy:onboard-complete' }, '*');
271
+ }}
272
+ />
273
+ )}
274
+ </>
275
+ )}
158
276
  </div>
159
277
  );
160
278
  }
@@ -15,6 +15,7 @@ import { updateTunnelUrl, startHeartbeat, stopHeartbeat, disconnect } from '../s
15
15
  import { startFluxyAgentQuery, stopFluxyAgentQuery, clearFluxySession } from './fluxy-agent.js';
16
16
  import { startViteDevServers, stopViteDevServers } from './vite-dev.js';
17
17
  import { execSync } from 'child_process';
18
+ import { verifyToken, parseCookie } from '../shared/auth.js';
18
19
 
19
20
  const DIST_FLUXY = path.join(PKG_DIR, 'dist-fluxy');
20
21
 
@@ -384,12 +385,42 @@ export async function startSupervisor() {
384
385
  });
385
386
  });
386
387
 
387
- server.on('upgrade', (req, socket: net.Socket, head) => {
388
+ server.on('upgrade', async (req, socket: net.Socket, head) => {
388
389
  console.log(`[supervisor] WebSocket upgrade: ${req.url}`);
389
390
 
390
391
  if (req.url === '/fluxy/ws') {
391
- console.log('[supervisor] Fluxy chat WebSocket');
392
- fluxyWss.handleUpgrade(req, socket, head, (ws) => fluxyWss.emit('connection', ws, req));
392
+ // Auth gate for Fluxy WebSocket
393
+ const token = parseCookie(req.headers.cookie, 'fluxy_session');
394
+ const payload = token ? await verifyToken(token) : null;
395
+
396
+ if (payload) {
397
+ // Authenticated — allow
398
+ console.log('[supervisor] → Fluxy chat WebSocket (authenticated)');
399
+ fluxyWss.handleUpgrade(req, socket, head, (ws) => fluxyWss.emit('connection', ws, req));
400
+ return;
401
+ }
402
+
403
+ // Check if credentials are configured (first-run bypass)
404
+ try {
405
+ const res = await fetch(`http://127.0.0.1:${workerPort}/api/auth/configured`);
406
+ const data = await res.json() as { configured: boolean };
407
+ if (!data.configured) {
408
+ // First run — allow without auth
409
+ console.log('[supervisor] → Fluxy chat WebSocket (first-run, no credentials configured)');
410
+ fluxyWss.handleUpgrade(req, socket, head, (ws) => fluxyWss.emit('connection', ws, req));
411
+ return;
412
+ }
413
+ } catch {
414
+ // Worker not ready yet — allow (will be gated by chat UI)
415
+ console.log('[supervisor] → Fluxy chat WebSocket (worker not ready, allowing)');
416
+ fluxyWss.handleUpgrade(req, socket, head, (ws) => fluxyWss.emit('connection', ws, req));
417
+ return;
418
+ }
419
+
420
+ // Credentials configured but no valid token — reject
421
+ console.log('[supervisor] WebSocket rejected: 401 Unauthorized');
422
+ socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
423
+ socket.destroy();
393
424
  return;
394
425
  }
395
426
 
package/worker/index.ts CHANGED
@@ -8,6 +8,7 @@ import { startCodexOAuth, cancelCodexOAuth, getCodexAuthStatus, readCodexAccessT
8
8
  import { startClaudeOAuth, exchangeClaudeCode, getClaudeAuthStatus, readClaudeAccessToken } from './claude-auth.js';
9
9
  import { checkAvailability, registerHandle, releaseHandle, updateTunnelUrl, startHeartbeat, stopHeartbeat } from '../shared/relay.js';
10
10
  import { ensureFileDirs } from './file-storage.js';
11
+ import { signToken, verifyToken, parseCookie, buildSessionCookie, buildClearCookie } from '../shared/auth.js';
11
12
 
12
13
  // ── Password hashing (scrypt) ──
13
14
 
@@ -36,6 +37,82 @@ ensureFileDirs();
36
37
  const app = express();
37
38
  app.use(express.json());
38
39
 
40
+ // ── Auth endpoints (public) ──
41
+
42
+ app.post('/api/auth/login', async (req, res) => {
43
+ const { username, password } = req.body;
44
+ if (!username || !password) { res.status(400).json({ error: 'Missing credentials' }); return; }
45
+
46
+ const storedUser = getSetting('portal_user');
47
+ const storedPass = getSetting('portal_pass');
48
+ if (!storedUser || !storedPass) { res.status(401).json({ error: 'Portal not configured' }); return; }
49
+
50
+ if (username.trim().toLowerCase() !== storedUser.toLowerCase() || !verifyPassword(password, storedPass)) {
51
+ res.status(401).json({ error: 'Invalid username or password' });
52
+ return;
53
+ }
54
+
55
+ const token = await signToken({ sub: storedUser, role: 'owner' });
56
+ const secure = req.headers['x-forwarded-proto'] === 'https' || req.protocol === 'https';
57
+ res.setHeader('Set-Cookie', buildSessionCookie(token, secure));
58
+ res.json({ ok: true, username: storedUser });
59
+ });
60
+
61
+ app.get('/api/auth/me', async (req, res) => {
62
+ const token = parseCookie(req.headers.cookie, 'fluxy_session');
63
+ if (!token) { res.status(401).json({ authenticated: false }); return; }
64
+ const payload = await verifyToken(token);
65
+ if (!payload) { res.status(401).json({ authenticated: false }); return; }
66
+ res.json({ authenticated: true, username: payload.sub });
67
+ });
68
+
69
+ app.post('/api/auth/logout', (req, res) => {
70
+ const secure = req.headers['x-forwarded-proto'] === 'https' || req.protocol === 'https';
71
+ res.setHeader('Set-Cookie', buildClearCookie(secure));
72
+ res.json({ ok: true });
73
+ });
74
+
75
+ app.get('/api/auth/configured', (_, res) => {
76
+ const portalUser = getSetting('portal_user');
77
+ const portalPass = getSetting('portal_pass');
78
+ const onboardComplete = getSetting('onboard_complete');
79
+ res.json({ configured: !!(portalUser && portalPass), onboardComplete: onboardComplete === 'true' });
80
+ });
81
+
82
+ // ── Auth middleware ──
83
+
84
+ const PUBLIC_PREFIXES = [
85
+ '/api/health',
86
+ '/api/auth/',
87
+ '/api/onboard/',
88
+ '/api/handle/',
89
+ '/api/portal/',
90
+ ];
91
+
92
+ app.use('/api', (req, res, next) => {
93
+ const url = req.originalUrl || req.url;
94
+
95
+ // Allow public routes
96
+ if (PUBLIC_PREFIXES.some((p) => url.startsWith(p))) { next(); return; }
97
+
98
+ // Allow GET /api/settings (needed by landing page for bot name)
99
+ if (url === '/api/settings' && req.method === 'GET') { next(); return; }
100
+
101
+ // First-run bypass: if portal credentials don't exist, skip auth
102
+ const portalUser = getSetting('portal_user');
103
+ const portalPass = getSetting('portal_pass');
104
+ if (!portalUser || !portalPass) { next(); return; }
105
+
106
+ // Verify JWT
107
+ const token = parseCookie(req.headers.cookie, 'fluxy_session');
108
+ if (!token) { res.status(401).json({ error: 'Authentication required' }); return; }
109
+
110
+ verifyToken(token).then((payload) => {
111
+ if (!payload) { res.status(401).json({ error: 'Invalid or expired session' }); return; }
112
+ next();
113
+ });
114
+ });
115
+
39
116
  app.get('/api/health', (_, res) => res.json({ status: 'ok' }));
40
117
  app.get('/api/conversations', (_, res) => res.json(listConversations()));
41
118
  app.get('/api/conversations/:id', (req, res) => {