fluxy-bot 0.2.44 → 0.3.0

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-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)}.-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-\[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-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-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-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-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--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,{})}));
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,{})}));
@@ -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-B20BJakh.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">
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">
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-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">
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">
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.44",
3
+ "version": "0.3.0",
4
4
  "description": "Self-hosted AI bot — run your own AI assistant from anywhere",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -55,14 +55,12 @@
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",
59
58
  "lucide-react": "^0.575.0",
60
59
  "postcss": "^8.5.6",
61
60
  "radix-ui": "^1.4.3",
62
61
  "react": "^19.2.4",
63
62
  "react-dom": "^19.2.4",
64
63
  "react-markdown": "^10.1.0",
65
- "react-router-dom": "^7.13.1",
66
64
  "react-syntax-highlighter": "^16.1.0",
67
65
  "recharts": "^3.7.0",
68
66
  "remark-gfm": "^4.0.1",
package/shared/config.ts CHANGED
@@ -17,7 +17,6 @@ export interface BotConfig {
17
17
  url: string;
18
18
  };
19
19
  tunnelUrl?: string;
20
- jwtSecret?: string;
21
20
  }
22
21
 
23
22
  const DEFAULTS: BotConfig = {
@@ -8,88 +8,10 @@ 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 contentType = res.headers.get('content-type') || '';
30
- if (!contentType.includes('application/json')) {
31
- console.error(`[login] Non-JSON response: ${res.status} ${contentType}`);
32
- setError(`Server error (${res.status})`);
33
- return;
34
- }
35
- const data = await res.json();
36
- if (data.ok) {
37
- onSuccess();
38
- } else {
39
- setError(data.error || 'Login failed');
40
- }
41
- } catch (err) {
42
- console.error('[login] Fetch error:', err);
43
- setError('Connection error — check if server is running');
44
- } finally {
45
- setLoading(false);
46
- }
47
- };
48
-
49
- return (
50
- <div className="flex-1 flex flex-col items-center justify-center px-6 py-8">
51
- <img src="/fluxy.png" alt={botName} className="h-10 w-auto mb-4" />
52
- <h2 className="text-lg font-semibold mb-1">Sign in to chat</h2>
53
- <p className="text-sm text-muted-foreground mb-6">Enter your portal credentials</p>
54
-
55
- <form onSubmit={handleSubmit} className="w-full max-w-[280px] flex flex-col gap-3">
56
- <input
57
- type="text"
58
- placeholder="Username"
59
- value={username}
60
- onChange={(e) => setUsername(e.target.value)}
61
- autoComplete="username"
62
- 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"
63
- />
64
- <input
65
- type="password"
66
- placeholder="Password"
67
- value={password}
68
- onChange={(e) => setPassword(e.target.value)}
69
- autoComplete="current-password"
70
- 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"
71
- />
72
- {error && (
73
- <div className="bg-red-500/8 border border-red-500/15 rounded-lg px-3 py-2 text-xs text-red-400">
74
- {error}
75
- </div>
76
- )}
77
- <button
78
- type="submit"
79
- disabled={loading || !username || !password}
80
- className="w-full bg-gradient-brand text-white font-medium rounded-full py-3 text-sm hover:opacity-90 transition-opacity disabled:opacity-50"
81
- >
82
- {loading ? 'Signing in...' : 'Sign In'}
83
- </button>
84
- </form>
85
- </div>
86
- );
87
- }
88
-
89
- /** Chat view — only mounted when authenticated or first-run */
90
- function ChatView({ botName }: { botName: string }) {
11
+ function FluxyApp() {
91
12
  const clientRef = useRef<WsClient | null>(null);
92
13
  const [connected, setConnected] = useState(false);
14
+ const [botName, setBotName] = useState('Fluxy');
93
15
  const [whisperEnabled, setWhisperEnabled] = useState(false);
94
16
  const [menuOpen, setMenuOpen] = useState(false);
95
17
  const [showWizard, setShowWizard] = useState(false);
@@ -105,12 +27,14 @@ function ChatView({ botName }: { botName: string }) {
105
27
 
106
28
  const unsub = client.onStatus((isConnected) => {
107
29
  setConnected(isConnected);
30
+ // On reconnect, trigger a reload from DB to catch missed messages
108
31
  if (isConnected && wasConnected.current) {
109
32
  setReloadTrigger((n) => n + 1);
110
33
  }
111
34
  wasConnected.current = isConnected;
112
35
  });
113
36
 
37
+ // Forward rebuild/HMR events to parent (dashboard) via postMessage
114
38
  const unsubRebuilding = client.on('app:rebuilding', () => {
115
39
  console.log('[fluxy] app:rebuilding received');
116
40
  window.parent?.postMessage({ type: 'fluxy:rebuilding' }, '*');
@@ -137,19 +61,21 @@ function ChatView({ botName }: { botName: string }) {
137
61
  unsubBuildError();
138
62
  unsubHmr();
139
63
  client.disconnect();
140
- clientRef.current = null;
141
64
  };
142
65
  }, []);
143
66
 
67
+ // Try to load settings (will work when worker is up, fail silently when down)
144
68
  useEffect(() => {
145
69
  fetch('/api/settings')
146
70
  .then((r) => r.json())
147
71
  .then((s) => {
72
+ if (s.agent_name) setBotName(s.agent_name);
148
73
  if (s.whisper_enabled === 'true') setWhisperEnabled(true);
149
74
  })
150
75
  .catch(() => {});
151
76
  }, []);
152
77
 
78
+ // Close menu on outside click
153
79
  useEffect(() => {
154
80
  if (!menuOpen) return;
155
81
  const handler = (e: MouseEvent) => {
@@ -163,10 +89,20 @@ function ChatView({ botName }: { botName: string }) {
163
89
  useFluxyChat(clientRef.current, reloadTrigger);
164
90
 
165
91
  return (
166
- <>
167
- {/* Status + menu in header area */}
168
- <div className="flex items-center gap-2 shrink-0" style={{ position: 'absolute', right: 12, top: 10 }}>
92
+ <div className="flex flex-col h-dvh overflow-hidden">
93
+ {/* Header */}
94
+ <div className="flex items-center gap-3 px-4 py-3 border-b border-border shrink-0">
95
+ <button
96
+ onClick={() => window.parent?.postMessage({ type: 'fluxy:close' }, '*')}
97
+ className="flex items-center justify-center h-7 w-7 -ml-1 rounded-full text-muted-foreground hover:text-foreground hover:bg-white/[0.06] transition-colors"
98
+ aria-label="Close chat"
99
+ >
100
+ <ArrowLeft className="h-5 w-5" />
101
+ </button>
102
+ <img src="/fluxy.png" alt={botName} className="h-5 w-auto" />
103
+ <span className="text-sm font-semibold">{botName}</span>
169
104
  <div className={`h-2 w-2 rounded-full ${connected ? 'bg-green-500' : 'bg-red-500'}`} />
105
+ <div className="flex-1" />
170
106
  <div className="relative" ref={menuRef}>
171
107
  <button
172
108
  onClick={() => setMenuOpen((v) => !v)}
@@ -195,91 +131,30 @@ function ChatView({ botName }: { botName: string }) {
195
131
  </div>
196
132
  </div>
197
133
 
134
+ {/* Chat body */}
198
135
  <div className="flex-1 min-h-0 flex flex-col overflow-hidden">
199
136
  <MessageList messages={messages} streaming={streaming} streamBuffer={streamBuffer} tools={tools} />
200
137
  <InputBar onSend={sendMessage} onStop={stopStreaming} streaming={streaming} whisperEnabled={whisperEnabled} />
201
138
  </div>
202
139
 
140
+ {/* Setup Wizard overlay */}
203
141
  {showWizard && (
204
142
  <OnboardWizard
205
143
  onComplete={() => {
206
144
  setShowWizard(false);
145
+ // Reload settings (bot name, whisper, etc.)
207
146
  fetch('/api/settings')
208
147
  .then((r) => r.json())
209
148
  .then((s) => {
210
- if (s.whisper_enabled === 'true') setWhisperEnabled(true);
149
+ if (s.agent_name) setBotName(s.agent_name);
150
+ setWhisperEnabled(s.whisper_enabled === 'true');
211
151
  })
212
152
  .catch(() => {});
153
+ // Notify dashboard so it can refresh
213
154
  window.parent?.postMessage({ type: 'fluxy:onboard-complete' }, '*');
214
155
  }}
215
156
  />
216
157
  )}
217
- </>
218
- );
219
- }
220
-
221
- function FluxyApp() {
222
- const [botName, setBotName] = useState('Fluxy');
223
- const [authState, setAuthState] = useState<AuthState>('checking');
224
-
225
- // Check auth on mount
226
- useEffect(() => {
227
- (async () => {
228
- try {
229
- const meRes = await fetch('/api/auth/me');
230
- if (meRes.ok) {
231
- const me = await meRes.json();
232
- if (me.authenticated) { setAuthState('authenticated'); return; }
233
- }
234
-
235
- const cfgRes = await fetch('/api/auth/configured');
236
- const cfg = await cfgRes.json();
237
- setAuthState(cfg.configured ? 'unauthenticated' : 'first-run');
238
- } catch {
239
- setAuthState('first-run');
240
- }
241
- })();
242
- }, []);
243
-
244
- // Load bot name
245
- useEffect(() => {
246
- fetch('/api/settings')
247
- .then((r) => r.json())
248
- .then((s) => { if (s.agent_name) setBotName(s.agent_name); })
249
- .catch(() => {});
250
- }, []);
251
-
252
- const showChat = authState === 'authenticated' || authState === 'first-run';
253
-
254
- return (
255
- <div className="flex flex-col h-dvh overflow-hidden relative">
256
- {/* Header */}
257
- <div className="flex items-center gap-3 px-4 py-3 border-b border-border shrink-0">
258
- <button
259
- onClick={() => window.parent?.postMessage({ type: 'fluxy:close' }, '*')}
260
- className="flex items-center justify-center h-7 w-7 -ml-1 rounded-full text-muted-foreground hover:text-foreground hover:bg-white/[0.06] transition-colors"
261
- aria-label="Close chat"
262
- >
263
- <ArrowLeft className="h-5 w-5" />
264
- </button>
265
- <img src="/fluxy.png" alt={botName} className="h-5 w-auto" />
266
- <span className="text-sm font-semibold">{botName}</span>
267
- <div className="flex-1" />
268
- </div>
269
-
270
- {/* Auth gate */}
271
- {authState === 'checking' && (
272
- <div className="flex-1 flex items-center justify-center">
273
- <div className="h-6 w-6 border-2 border-muted-foreground/30 border-t-foreground rounded-full animate-spin" />
274
- </div>
275
- )}
276
-
277
- {authState === 'unauthenticated' && (
278
- <LoginForm botName={botName} onSuccess={() => setAuthState('authenticated')} />
279
- )}
280
-
281
- {/* Chat — only mounted when auth is resolved, so useFluxyChat won't fire 401s */}
282
- {showChat && <ChatView botName={botName} />}
283
158
  </div>
284
159
  );
285
160
  }
@@ -15,7 +15,6 @@ 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';
19
18
 
20
19
  const DIST_FLUXY = path.join(PKG_DIR, 'dist-fluxy');
21
20
 
@@ -92,11 +91,6 @@ export async function startSupervisor() {
92
91
 
93
92
  // HTTP server — proxies to Vite dev servers + worker API
94
93
  const server = http.createServer((req, res) => {
95
- // Log every request (POST/PUT/DELETE only to reduce noise)
96
- if (req.method !== 'GET') {
97
- console.log(`[supervisor] ★ ${req.method} ${req.url} | content-type: ${req.headers['content-type']} | content-length: ${req.headers['content-length']}`);
98
- }
99
-
100
94
  // Fluxy widget — served directly (not part of Vite build)
101
95
  if (req.url === '/fluxy/widget.js') {
102
96
  console.log('[supervisor] Serving /fluxy/widget.js directly');
@@ -134,10 +128,7 @@ export async function startSupervisor() {
134
128
 
135
129
  // API routes → proxy to worker
136
130
  if (req.url?.startsWith('/api')) {
137
- const isAuthReq = req.url.startsWith('/api/auth/');
138
- if (isAuthReq) console.log(`[supervisor] AUTH → worker :${workerPort} | ${req.method} ${req.url} | content-type: ${req.headers['content-type']} | content-length: ${req.headers['content-length']}`);
139
- else console.log(`[supervisor] → worker :${workerPort} | ${req.method} ${req.url}`);
140
-
131
+ console.log(`[supervisor] worker :${workerPort} | ${req.method} ${req.url}`);
141
132
  if (!isWorkerAlive()) {
142
133
  console.log('[supervisor] Worker down — returning 503');
143
134
  res.writeHead(503, { 'Content-Type': 'text/html' });
@@ -148,7 +139,6 @@ export async function startSupervisor() {
148
139
  const proxy = http.request(
149
140
  { host: '127.0.0.1', port: workerPort, path: req.url, method: req.method, headers: req.headers },
150
141
  (proxyRes) => {
151
- if (isAuthReq) console.log(`[supervisor] AUTH ← worker ${proxyRes.statusCode} | set-cookie: ${proxyRes.headers['set-cookie'] ? 'yes' : 'no'}`);
152
142
  res.writeHead(proxyRes.statusCode!, proxyRes.headers);
153
143
  proxyRes.pipe(res);
154
144
  },
@@ -394,42 +384,12 @@ export async function startSupervisor() {
394
384
  });
395
385
  });
396
386
 
397
- server.on('upgrade', async (req, socket: net.Socket, head) => {
387
+ server.on('upgrade', (req, socket: net.Socket, head) => {
398
388
  console.log(`[supervisor] WebSocket upgrade: ${req.url}`);
399
389
 
400
390
  if (req.url === '/fluxy/ws') {
401
- // Auth gate for Fluxy WebSocket
402
- const token = parseCookie(req.headers.cookie, 'fluxy_session');
403
- const payload = token ? await verifyToken(token) : null;
404
-
405
- if (payload) {
406
- // Authenticated — allow
407
- console.log('[supervisor] → Fluxy chat WebSocket (authenticated)');
408
- fluxyWss.handleUpgrade(req, socket, head, (ws) => fluxyWss.emit('connection', ws, req));
409
- return;
410
- }
411
-
412
- // Check if credentials are configured (first-run bypass)
413
- try {
414
- const res = await fetch(`http://127.0.0.1:${workerPort}/api/auth/configured`);
415
- const data = await res.json() as { configured: boolean };
416
- if (!data.configured) {
417
- // First run — allow without auth
418
- console.log('[supervisor] → Fluxy chat WebSocket (first-run, no credentials configured)');
419
- fluxyWss.handleUpgrade(req, socket, head, (ws) => fluxyWss.emit('connection', ws, req));
420
- return;
421
- }
422
- } catch {
423
- // Worker not ready yet — allow (will be gated by chat UI)
424
- console.log('[supervisor] → Fluxy chat WebSocket (worker not ready, allowing)');
425
- fluxyWss.handleUpgrade(req, socket, head, (ws) => fluxyWss.emit('connection', ws, req));
426
- return;
427
- }
428
-
429
- // Credentials configured but no valid token — reject
430
- console.log('[supervisor] WebSocket rejected: 401 Unauthorized');
431
- socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
432
- socket.destroy();
391
+ console.log('[supervisor] Fluxy chat WebSocket');
392
+ fluxyWss.handleUpgrade(req, socket, head, (ws) => fluxyWss.emit('connection', ws, req));
433
393
  return;
434
394
  }
435
395
 
package/worker/index.ts CHANGED
@@ -8,7 +8,6 @@ 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';
12
11
 
13
12
  // ── Password hashing (scrypt) ──
14
13
 
@@ -37,99 +36,6 @@ ensureFileDirs();
37
36
  const app = express();
38
37
  app.use(express.json());
39
38
 
40
- // ── Auth endpoints (public) ──
41
-
42
- app.post('/api/auth/login', async (req, res) => {
43
- log.ok(`[auth/login] POST received — body keys: ${Object.keys(req.body || {}).join(', ') || '(empty)'}`);
44
- const { username, password } = req.body;
45
- if (!username || !password) {
46
- log.warn(`[auth/login] Missing credentials — username: ${!!username}, password: ${!!password}`);
47
- res.status(400).json({ error: 'Missing credentials' }); return;
48
- }
49
-
50
- const storedUser = getSetting('portal_user');
51
- const storedPass = getSetting('portal_pass');
52
- log.ok(`[auth/login] Stored portal_user: ${storedUser || '(none)'}, portal_pass: ${storedPass ? 'set' : '(none)'}`);
53
- if (!storedUser || !storedPass) { res.status(401).json({ error: 'Portal not configured' }); return; }
54
-
55
- const usernameMatch = username.trim().toLowerCase() === storedUser.toLowerCase();
56
- const passwordMatch = verifyPassword(password, storedPass);
57
- log.ok(`[auth/login] username match: ${usernameMatch}, password match: ${passwordMatch}`);
58
-
59
- if (!usernameMatch || !passwordMatch) {
60
- res.status(401).json({ error: 'Invalid username or password' });
61
- return;
62
- }
63
-
64
- try {
65
- const token = await signToken({ sub: storedUser, role: 'owner' });
66
- log.ok(`[auth/login] JWT signed OK (length: ${token.length})`);
67
- const secure = req.headers['x-forwarded-proto'] === 'https' || req.protocol === 'https';
68
- log.ok(`[auth/login] secure=${secure}, x-forwarded-proto=${req.headers['x-forwarded-proto']}`);
69
- res.setHeader('Set-Cookie', buildSessionCookie(token, secure));
70
- res.json({ ok: true, username: storedUser });
71
- log.ok(`[auth/login] Success — response sent`);
72
- } catch (err: any) {
73
- log.warn(`[auth/login] JWT sign error: ${err.message}`);
74
- res.status(500).json({ error: 'Internal error' });
75
- }
76
- });
77
-
78
- app.get('/api/auth/me', async (req, res) => {
79
- const token = parseCookie(req.headers.cookie, 'fluxy_session');
80
- if (!token) { res.status(401).json({ authenticated: false }); return; }
81
- const payload = await verifyToken(token);
82
- if (!payload) { res.status(401).json({ authenticated: false }); return; }
83
- res.json({ authenticated: true, username: payload.sub });
84
- });
85
-
86
- app.post('/api/auth/logout', (req, res) => {
87
- const secure = req.headers['x-forwarded-proto'] === 'https' || req.protocol === 'https';
88
- res.setHeader('Set-Cookie', buildClearCookie(secure));
89
- res.json({ ok: true });
90
- });
91
-
92
- app.get('/api/auth/configured', (_, res) => {
93
- const portalUser = getSetting('portal_user');
94
- const portalPass = getSetting('portal_pass');
95
- const onboardComplete = getSetting('onboard_complete');
96
- res.json({ configured: !!(portalUser && portalPass), onboardComplete: onboardComplete === 'true' });
97
- });
98
-
99
- // ── Auth middleware ──
100
-
101
- const PUBLIC_PREFIXES = [
102
- '/api/health',
103
- '/api/auth/',
104
- '/api/onboard/',
105
- '/api/handle/',
106
- '/api/portal/',
107
- ];
108
-
109
- app.use('/api', (req, res, next) => {
110
- const url = req.originalUrl || req.url;
111
-
112
- // Allow public routes
113
- if (PUBLIC_PREFIXES.some((p) => url.startsWith(p))) { next(); return; }
114
-
115
- // Allow GET /api/settings (needed by landing page for bot name)
116
- if (url === '/api/settings' && req.method === 'GET') { next(); return; }
117
-
118
- // First-run bypass: if portal credentials don't exist, skip auth
119
- const portalUser = getSetting('portal_user');
120
- const portalPass = getSetting('portal_pass');
121
- if (!portalUser || !portalPass) { next(); return; }
122
-
123
- // Verify JWT
124
- const token = parseCookie(req.headers.cookie, 'fluxy_session');
125
- if (!token) { res.status(401).json({ error: 'Authentication required' }); return; }
126
-
127
- verifyToken(token).then((payload) => {
128
- if (!payload) { res.status(401).json({ error: 'Invalid or expired session' }); return; }
129
- next();
130
- });
131
- });
132
-
133
39
  app.get('/api/health', (_, res) => res.json({ status: 'ok' }));
134
40
  app.get('/api/conversations', (_, res) => res.json(listConversations()));
135
41
  app.get('/api/conversations/:id', (req, res) => {
@@ -345,7 +251,7 @@ app.post('/api/portal/verify-password', (req, res) => {
345
251
  res.json({ valid: verifyPassword(password, stored) });
346
252
  });
347
253
 
348
- app.post('/api/onboard', async (req, res) => {
254
+ app.post('/api/onboard', (req, res) => {
349
255
  const { userName, agentName, provider, model, apiKey, baseUrl, portalUser, portalPass, whisperEnabled, whisperKey } = req.body;
350
256
  setSetting('user_name', userName || '');
351
257
  setSetting('agent_name', agentName || 'Fluxy');
@@ -384,13 +290,6 @@ app.post('/api/onboard', async (req, res) => {
384
290
 
385
291
  saveConfig(currentCfg);
386
292
 
387
- // Auto-login: issue JWT cookie so the user is immediately authenticated after onboarding
388
- if (portalUser) {
389
- const token = await signToken({ sub: portalUser.trim().toLowerCase(), role: 'owner' });
390
- const secure = req.headers['x-forwarded-proto'] === 'https' || req.protocol === 'https';
391
- res.setHeader('Set-Cookie', buildSessionCookie(token, secure));
392
- }
393
-
394
293
  res.json({ ok: true });
395
294
  });
396
295