@turmind/halo 1.6.3 → 1.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -67
- package/admin-out/404.html +1 -1
- package/admin-out/_next/static/chunks/87-6a2716c98a819c4e.js +1 -0
- package/admin-out/_next/static/chunks/989-066a874961bd519f.js +1 -0
- package/admin-out/_next/static/chunks/app/page-812bcbb46db628b6.js +1 -0
- package/admin-out/_next/static/css/e890aa0d13e94290.css +1 -0
- package/admin-out/index.html +1 -1
- package/admin-out/index.txt +5 -5
- package/bin/halo.js +4 -0
- package/bundled-docs/design/architecture.md +1 -1
- package/bundled-docs/design/canvas-extensions.md +40 -9
- package/bundled-docs/design/cron.md +12 -4
- package/bundled-docs/design/halo-city.md +14 -149
- package/bundled-docs/design/observability.md +7 -5
- package/bundled-docs/design/storage.md +14 -7
- package/bundled-docs/design/stt-local.md +106 -0
- package/bundled-docs/design/web.md +1 -1
- package/bundled-docs/design/wechat.md +1 -1
- package/bundled-docs/dev/add-model-provider.md +2 -1
- package/bundled-docs/dev/api.md +13 -9
- package/bundled-docs/dev/deploy.md +114 -26
- package/bundled-docs/dev/desktop-packaging.md +50 -10
- package/bundled-docs/dev/env.md +4 -4
- package/bundled-docs/dev/logging.md +13 -2
- package/bundled-docs/dev/tools.md +2 -2
- package/bundled-docs/guide/agents.md +2 -2
- package/bundled-docs/guide/cli.md +20 -8
- package/bundled-docs/guide/getting-started.md +13 -5
- package/bundled-docs/guide/workspace.md +2 -0
- package/bundled-docs/requirements/agent.md +1 -1
- package/bundled-docs/requirements/chat.md +8 -0
- package/bundled-docs/requirements/explorer.md +14 -2
- package/bundled-docs/requirements/settings.md +2 -2
- package/bundled-docs/requirements/workspace.md +4 -0
- package/dist/index.js +3645 -1725
- package/package.json +4 -2
- package/templates/models/gemini.yaml +93 -0
- package/templates/skills/cron/SKILL.md +2 -1
- package/templates/skills/cron/manage-cron.py +2 -1
- package/admin-out/_next/static/chunks/147-d4200c70445aabb6.js +0 -1
- package/admin-out/_next/static/chunks/989-ddba0eb72566da23.js +0 -1
- package/admin-out/_next/static/chunks/app/page-7ffcbddb49dc56c4.js +0 -1
- package/admin-out/_next/static/css/3b4dcd0ac6b8eae8.css +0 -1
- /package/admin-out/_next/static/{k5zuJrivSdbaPifBatwdr → 8v1hCXbUqJhyMuK7LVeet}/_buildManifest.js +0 -0
- /package/admin-out/_next/static/{k5zuJrivSdbaPifBatwdr → 8v1hCXbUqJhyMuK7LVeet}/_ssgManifest.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,::backdrop,:after,:before{--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-divide-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-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-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;--tw-ease:initial;--tw-content:""}}}@layer theme{:host,:root{--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-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-300:oklch(83.7% .128 66.29);--color-orange-400:oklch(75% .183 55.934);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-700:oklch(55.3% .195 38.402);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-900:oklch(41.4% .112 45.904);--color-amber-950:oklch(27.9% .077 45.635);--color-yellow-300:oklch(90.5% .182 98.111);--color-yellow-400:oklch(85.2% .199 91.936);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-emerald-800:oklch(43.2% .095 166.913);--color-emerald-900:oklch(37.8% .077 168.94);--color-emerald-950:oklch(26.2% .051 172.552);--color-teal-300:oklch(85.5% .138 181.071);--color-teal-500:oklch(70.4% .14 182.503);--color-cyan-300:oklch(86.5% .127 207.078);--color-cyan-400:oklch(78.9% .154 211.53);--color-sky-50:oklch(97.7% .013 236.62);--color-sky-300:oklch(82.8% .111 230.318);--color-sky-600:oklch(58.8% .158 241.966);--color-sky-700:oklch(50% .134 242.749);--color-sky-950:oklch(29.3% .066 243.157);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-900:oklch(37.9% .146 265.522);--color-indigo-300:oklch(78.5% .115 274.713);--color-violet-400:oklch(70.2% .183 293.541);--color-purple-300:oklch(82.7% .119 306.383);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-900:oklch(38.1% .176 304.987);--color-purple-950:oklch(29.1% .149 302.717);--color-pink-300:oklch(82.3% .12 346.018);--color-pink-400:oklch(71.8% .202 349.761);--color-zinc-100:oklch(96.7% .001 286.375);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--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-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--tracking-wider:.05em;--leading-tight:1.25;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1) infinite;--blur-sm:8px;--aspect-video:16/9;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,::backdrop,:after,:before{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}:host,html{-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,pre,samp{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}menu,ol,ul{list-style:none}audio,canvas,embed,iframe,img,object,svg,video{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,optgroup,select,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,::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-second-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,::-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}.\!visible{visibility:visible!important}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.inset-x-1\.5{inset-inline:calc(var(--spacing) * 1.5)}.inset-x-\[20\%\]{inset-inline:20%}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.-top-0\.5{top:calc(var(--spacing) * -.5)}.-top-1{top:calc(var(--spacing) * -1)}.-top-2{top:calc(var(--spacing) * -2)}.top-0{top:calc(var(--spacing) * 0)}.top-1{top:calc(var(--spacing) * 1)}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-1\/2{top:50%}.top-2{top:calc(var(--spacing) * 2)}.top-3{top:calc(var(--spacing) * 3)}.top-4{top:calc(var(--spacing) * 4)}.top-14{top:calc(var(--spacing) * 14)}.top-\[calc\(100\%\+2px\)\]{top:calc(100% + 2px)}.top-full{top:100%}.-right-0\.5{right:calc(var(--spacing) * -.5)}.-right-1{right:calc(var(--spacing) * -1)}.right-0{right:calc(var(--spacing) * 0)}.right-1{right:calc(var(--spacing) * 1)}.right-1\.5{right:calc(var(--spacing) * 1.5)}.right-2{right:calc(var(--spacing) * 2)}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.right-14{right:calc(var(--spacing) * 14)}.-bottom-1{bottom:calc(var(--spacing) * -1)}.bottom-0{bottom:calc(var(--spacing) * 0)}.bottom-1{bottom:calc(var(--spacing) * 1)}.bottom-3{bottom:calc(var(--spacing) * 3)}.bottom-full{bottom:100%}.-left-0\.5{left:calc(var(--spacing) * -.5)}.-left-1{left:calc(var(--spacing) * -1)}.left-0{left:calc(var(--spacing) * 0)}.left-1\/2{left:50%}.left-2{left:calc(var(--spacing) * 2)}.left-3{left:calc(var(--spacing) * 3)}.left-full{left:100%}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[45\]{z-index:45}.z-\[55\]{z-index:55}.z-\[60\]{z-index:60}.z-\[70\]{z-index:70}.z-\[80\]{z-index:80}.z-\[90\]{z-index:90}.z-\[10000\]{z-index:10000}.col-span-2{grid-column:span 2/span 2}.float-right{float:right}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-0\.5{margin-inline:calc(var(--spacing) * .5)}.mx-1{margin-inline:calc(var(--spacing) * 1)}.mx-1\.5{margin-inline:calc(var(--spacing) * 1.5)}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mx-auto{margin-inline:auto}.my-0\.5{margin-block:calc(var(--spacing) * .5)}.my-1{margin-block:calc(var(--spacing) * 1)}.my-2{margin-block:calc(var(--spacing) * 2)}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);margin-top:1.2em;margin-bottom:1.2em;font-size:1.25em;line-height:1.6}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);font-weight:500;text-decoration:underline}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em;list-style-type:decimal}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em;list-style-type:disc}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-counters);font-weight:400}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.25em;font-weight:600}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em;font-style:italic;font-weight:500}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:0;margin-bottom:.888889em;font-size:2.25em;font-weight:800;line-height:1.11111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:900}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:2em;margin-bottom:1em;font-size:1.5em;font-weight:700;line-height:1.33333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:800}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.6em;margin-bottom:.6em;font-size:1.25em;font-weight:600;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.5em;margin-bottom:.5em;font-weight:600;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em;display:block}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-kbd);box-shadow:0 0 0 1px var(--tw-prose-kbd-shadows),0 3px 0 var(--tw-prose-kbd-shadows);padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;border-radius:.3125rem;padding-inline-start:.375em;font-family:inherit;font-size:.875em;font-weight:500}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-size:.875em;font-weight:600}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after,.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);padding-top:.857143em;padding-inline-end:1.14286em;padding-bottom:.857143em;border-radius:.375rem;margin-top:1.71429em;margin-bottom:1.71429em;padding-inline-start:1.14286em;font-size:.875em;font-weight:400;line-height:1.71429;overflow-x:auto}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit;background-color:#0000;border-width:0;border-radius:0;padding:0}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after,.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){table-layout:auto;width:100%;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.71429}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);vertical-align:bottom;padding-inline-end:.571429em;padding-bottom:.571429em;padding-inline-start:.571429em;font-weight:600}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);margin-top:.857143em;font-size:.875em;line-height:1.42857}.prose{--tw-prose-body:oklch(37.3% .034 259.733);--tw-prose-headings:oklch(21% .034 264.665);--tw-prose-lead:oklch(44.6% .03 256.802);--tw-prose-links:oklch(21% .034 264.665);--tw-prose-bold:oklch(21% .034 264.665);--tw-prose-counters:oklch(55.1% .027 264.364);--tw-prose-bullets:oklch(87.2% .01 258.338);--tw-prose-hr:oklch(92.8% .006 264.531);--tw-prose-quotes:oklch(21% .034 264.665);--tw-prose-quote-borders:oklch(92.8% .006 264.531);--tw-prose-captions:oklch(55.1% .027 264.364);--tw-prose-kbd:oklch(21% .034 264.665);--tw-prose-kbd-shadows:oklab(21% -.00316127 -.0338527/.1);--tw-prose-code:oklch(21% .034 264.665);--tw-prose-pre-code:oklch(92.8% .006 264.531);--tw-prose-pre-bg:oklch(27.8% .033 256.848);--tw-prose-th-borders:oklch(87.2% .01 258.338);--tw-prose-td-borders:oklch(92.8% .006 264.531);--tw-prose-invert-body:oklch(87.2% .01 258.338);--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:oklch(70.7% .022 261.325);--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:oklch(70.7% .022 261.325);--tw-prose-invert-bullets:oklch(44.6% .03 256.802);--tw-prose-invert-hr:oklch(37.3% .034 259.733);--tw-prose-invert-quotes:oklch(96.7% .003 264.542);--tw-prose-invert-quote-borders:oklch(37.3% .034 259.733);--tw-prose-invert-captions:oklch(70.7% .022 261.325);--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:#ffffff1a;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:oklch(87.2% .01 258.338);--tw-prose-invert-pre-bg:#00000080;--tw-prose-invert-th-borders:oklch(44.6% .03 256.802);--tw-prose-invert-td-borders:oklch(37.3% .034 259.733);font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.571429em;padding-inline-end:.571429em;padding-bottom:.571429em;padding-inline-start:.571429em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-sm{font-size:.875rem;line-height:1.71429}.prose-sm :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.14286em;margin-bottom:1.14286em}.prose-sm :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.888889em;margin-bottom:.888889em;font-size:1.28571em;line-height:1.55556}.prose-sm :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.33333em;margin-bottom:1.33333em;padding-inline-start:1.11111em}.prose-sm :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:.8em;font-size:2.14286em;line-height:1.2}.prose-sm :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.6em;margin-bottom:.8em;font-size:1.42857em;line-height:1.4}.prose-sm :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.55556em;margin-bottom:.444444em;font-size:1.28571em;line-height:1.55556}.prose-sm :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.42857em;margin-bottom:.571429em;line-height:1.42857}.prose-sm :where(img):not(:where([class~=not-prose],[class~=not-prose] *)),.prose-sm :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.71429em;margin-bottom:1.71429em}.prose-sm :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.71429em;margin-bottom:1.71429em}.prose-sm :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.142857em;padding-inline-end:.357143em;padding-bottom:.142857em;border-radius:.3125rem;padding-inline-start:.357143em;font-size:.857143em}.prose-sm :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.857143em}.prose-sm :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.prose-sm :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.888889em}.prose-sm :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.666667em;padding-inline-end:1em;padding-bottom:.666667em;border-radius:.25rem;margin-top:1.66667em;margin-bottom:1.66667em;padding-inline-start:1em;font-size:.857143em;line-height:1.66667}.prose-sm :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)),.prose-sm :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.14286em;margin-bottom:1.14286em;padding-inline-start:1.57143em}.prose-sm :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.285714em;margin-bottom:.285714em}.prose-sm :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)),.prose-sm :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.428571em}.prose-sm :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.571429em;margin-bottom:.571429em}.prose-sm :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.14286em}.prose-sm :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em}.prose-sm :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.14286em}.prose-sm :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em}.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.571429em;margin-bottom:.571429em}.prose-sm :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.14286em;margin-bottom:1.14286em}.prose-sm :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.14286em}.prose-sm :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.285714em;padding-inline-start:1.57143em}.prose-sm :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2.85714em;margin-bottom:2.85714em}.prose-sm :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose-sm :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose-sm :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose-sm :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.857143em;line-height:1.5}.prose-sm :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.666667em;padding-inline-start:1em}.prose-sm :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.666667em;padding-inline-end:1em;padding-bottom:.666667em;padding-inline-start:1em}.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.71429em;margin-bottom:1.71429em}.prose-sm :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.666667em;font-size:.857143em;line-height:1.33333}.prose-sm :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.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-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-px{margin-top:1px}.mr-1{margin-right:calc(var(--spacing) * 1)}.mr-1\.5{margin-right:calc(var(--spacing) * 1.5)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.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-0\.5{margin-left:calc(var(--spacing) * -.5)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-0\.5{margin-left:calc(var(--spacing) * .5)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-6{margin-left:calc(var(--spacing) * 6)}.ml-\[22px\]{margin-left:22px}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2}.line-clamp-2,.line-clamp-3{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-3{-webkit-line-clamp:3}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.aspect-\[4\/3\]{aspect-ratio:4/3}.aspect-video{aspect-ratio:var(--aspect-video)}.h-0\.5{height:calc(var(--spacing) * .5)}.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-4\.5{height:calc(var(--spacing) * 4.5)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-16{height:calc(var(--spacing) * 16)}.h-24{height:calc(var(--spacing) * 24)}.h-64{height:calc(var(--spacing) * 64)}.h-\[3px\]{height:3px}.h-\[35px\]{height:35px}.h-\[80vh\]{height:80vh}.h-\[92vh\]{height:92vh}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-40{max-height:calc(var(--spacing) * 40)}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-56{max-height:calc(var(--spacing) * 56)}.max-h-60{max-height:calc(var(--spacing) * 60)}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-72{max-height:calc(var(--spacing) * 72)}.max-h-\[40vh\]{max-height:40vh}.max-h-\[50vh\]{max-height:50vh}.max-h-\[60vh\]{max-height:60vh}.max-h-\[70vh\]{max-height:70vh}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-\[200px\]{max-height:200px}.max-h-\[400px\]{max-height:400px}.max-h-\[420px\]{max-height:420px}.max-h-full{max-height:100%}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-\[8rem\]{min-height:8rem}.min-h-full{min-height:100%}.w-0\.5{width:calc(var(--spacing) * .5)}.w-1{width:calc(var(--spacing) * 1)}.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-4\.5{width:calc(var(--spacing) * 4.5)}.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-10{width:calc(var(--spacing) * 10)}.w-12{width:calc(var(--spacing) * 12)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-32{width:calc(var(--spacing) * 32)}.w-40{width:calc(var(--spacing) * 40)}.w-44{width:calc(var(--spacing) * 44)}.w-48{width:calc(var(--spacing) * 48)}.w-56{width:calc(var(--spacing) * 56)}.w-60{width:calc(var(--spacing) * 60)}.w-64{width:calc(var(--spacing) * 64)}.w-\[30px\]{width:30px}.w-\[96vw\]{width:96vw}.w-\[220px\]{width:220px}.w-\[260px\]{width:260px}.w-\[400px\]{width:400px}.w-\[420px\]{width:420px}.w-\[460px\]{width:460px}.w-\[480px\]{width:480px}.w-\[560px\]{width:560px}.w-\[640px\]{width:640px}.w-\[800px\]{width:800px}.w-\[min\(80vw\,480px\)\]{width:min(80vw,480px)}.w-\[min\(90vw\,640px\)\]{width:min(90vw,640px)}.w-\[min\(92vw\,640px\)\]{width:min(92vw,640px)}.w-\[min\(520px\,80\%\)\]{width:min(520px,80%)}.w-fit{width:fit-content}.w-full{width:100%}.w-px{width:1px}.w-screen{width:100vw}.max-w-20{max-width:calc(var(--spacing) * 20)}.max-w-32{max-width:calc(var(--spacing) * 32)}.max-w-36{max-width:calc(var(--spacing) * 36)}.max-w-40{max-width:calc(var(--spacing) * 40)}.max-w-\[80px\]{max-width:80px}.max-w-\[90vw\]{max-width:90vw}.max-w-\[92vw\]{max-width:92vw}.max-w-\[94vw\]{max-width:94vw}.max-w-\[220px\]{max-width:220px}.max-w-\[240px\]{max-width:240px}.max-w-\[360px\]{max-width:360px}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[3ch\]{min-width:3ch}.min-w-\[14px\]{min-width:14px}.min-w-\[140px\]{min-width:140px}.min-w-\[160px\]{min-width:160px}.min-w-\[180px\]{min-width:180px}.min-w-\[280px\]{min-width:280px}.min-w-full{min-width:100%}.flex-1{flex:1}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1)}.-translate-x-1\/2,.-translate-y-1\/2{translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1)}.-rotate-90{rotate:-90deg}.rotate-90{rotate:90deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-col-resize{cursor:col-resize}.cursor-crosshair{cursor:crosshair}.cursor-default{cursor:default}.cursor-ew-resize{cursor:ew-resize}.cursor-grabbing{cursor:grabbing}.cursor-move{cursor:move}.cursor-nesw-resize{cursor:nesw-resize}.cursor-not-allowed{cursor:not-allowed}.cursor-ns-resize{cursor:ns-resize}.cursor-nwse-resize{cursor:nwse-resize}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize{resize:both}.resize-none{resize:none}.resize-y{resize:vertical}.appearance-none{appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-\[1fr_1fr_80px\]{grid-template-columns:1fr 1fr 80px}.grid-cols-\[repeat\(auto-fill\,minmax\(220px\,1fr\)\)\]{grid-template-columns:repeat(auto-fill,minmax(220px,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0{gap:calc(var(--spacing) * 0)}.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-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-\[2px\]{gap:2px}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * 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-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)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-\[var\(--border\)\]>:not(:last-child)){border-color:var(--border)}.self-center{align-self:center}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e+38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-lg{border-top-left-radius:var(--radius-lg);border-top-right-radius:var(--radius-lg)}.rounded-t-md{border-top-left-radius:var(--radius-md);border-top-right-radius:var(--radius-md)}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-b-lg{border-bottom-right-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.rounded-b-md{border-bottom-right-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-t-0{border-top-style:var(--tw-border-style);border-top-width:0}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-\[var\(--border\)\],.border-\[var\(--border\)\]\/10{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-\[var\(--border\)\]\/10{border-color:color-mix(in oklab,var(--border) 10%,transparent)}}.border-\[var\(--border\)\]\/30{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-\[var\(--border\)\]\/30{border-color:color-mix(in oklab,var(--border) 30%,transparent)}}.border-\[var\(--border\)\]\/40{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-\[var\(--border\)\]\/40{border-color:color-mix(in oklab,var(--border) 40%,transparent)}}.border-\[var\(--border\)\]\/50{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-\[var\(--border\)\]\/50{border-color:color-mix(in oklab,var(--border) 50%,transparent)}}.border-\[var\(--destructive\)\]{border-color:var(--destructive)}.border-\[var\(--primary\)\]{border-color:var(--primary)}.border-amber-500\/30{border-color:#f99c004d}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/30{border-color:color-mix(in oklab,var(--color-amber-500) 30%,transparent)}}.border-blue-500\/30{border-color:#3080ff4d}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/30{border-color:color-mix(in oklab,var(--color-blue-500) 30%,transparent)}}.border-emerald-500\/30{border-color:#00bb7f4d}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\/30{border-color:color-mix(in oklab,var(--color-emerald-500) 30%,transparent)}}.border-green-500\/20{border-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.border-green-500\/20{border-color:color-mix(in oklab,var(--color-green-500) 20%,transparent)}}.border-purple-900\/50{border-color:#59168b80}@supports (color:color-mix(in lab,red,red)){.border-purple-900\/50{border-color:color-mix(in oklab,var(--color-purple-900) 50%,transparent)}}.border-purple-900\/60{border-color:#59168b99}@supports (color:color-mix(in lab,red,red)){.border-purple-900\/60{border-color:color-mix(in oklab,var(--color-purple-900) 60%,transparent)}}.border-red-400\/60{border-color:#ff656899}@supports (color:color-mix(in lab,red,red)){.border-red-400\/60{border-color:color-mix(in oklab,var(--color-red-400) 60%,transparent)}}.border-red-500{border-color:var(--color-red-500)}.border-red-500\/30{border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.border-red-500\/30{border-color:color-mix(in oklab,var(--color-red-500) 30%,transparent)}}.border-red-500\/50{border-color:#fb2c3680}@supports (color:color-mix(in lab,red,red)){.border-red-500\/50{border-color:color-mix(in oklab,var(--color-red-500) 50%,transparent)}}.border-transparent{border-color:#0000}.border-l-blue-500{border-left-color:var(--color-blue-500)}.border-l-emerald-500\/70{border-left-color:#00bb7fb3}@supports (color:color-mix(in lab,red,red)){.border-l-emerald-500\/70{border-left-color:color-mix(in oklab,var(--color-emerald-500) 70%,transparent)}}.border-l-purple-500\/70{border-left-color:#ac4bffb3}@supports (color:color-mix(in lab,red,red)){.border-l-purple-500\/70{border-left-color:color-mix(in oklab,var(--color-purple-500) 70%,transparent)}}.border-l-red-500\/60{border-left-color:#fb2c3699}@supports (color:color-mix(in lab,red,red)){.border-l-red-500\/60{border-left-color:color-mix(in oklab,var(--color-red-500) 60%,transparent)}}.bg-\[\#1a1a1a\]{background-color:#1a1a1a}.bg-\[var\(--accent\)\],.bg-\[var\(--accent\)\]\/30{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--accent\)\]\/30{background-color:color-mix(in oklab,var(--accent) 30%,transparent)}}.bg-\[var\(--accent\)\]\/80{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--accent\)\]\/80{background-color:color-mix(in oklab,var(--accent) 80%,transparent)}}.bg-\[var\(--background\)\],.bg-\[var\(--background\)\]\/40{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--background\)\]\/40{background-color:color-mix(in oklab,var(--background) 40%,transparent)}}.bg-\[var\(--background\)\]\/50{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--background\)\]\/50{background-color:color-mix(in oklab,var(--background) 50%,transparent)}}.bg-\[var\(--background\)\]\/80{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--background\)\]\/80{background-color:color-mix(in oklab,var(--background) 80%,transparent)}}.bg-\[var\(--border\)\]{background-color:var(--border)}.bg-\[var\(--card\)\],.bg-\[var\(--card\)\]\/40{background-color:var(--card)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--card\)\]\/40{background-color:color-mix(in oklab,var(--card) 40%,transparent)}}.bg-\[var\(--card\)\]\/90{background-color:var(--card)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--card\)\]\/90{background-color:color-mix(in oklab,var(--card) 90%,transparent)}}.bg-\[var\(--destructive\)\]\/10{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--destructive\)\]\/10{background-color:color-mix(in oklab,var(--destructive) 10%,transparent)}}.bg-\[var\(--foreground\)\]{background-color:var(--foreground)}.bg-\[var\(--muted\)\]\/50{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--muted\)\]\/50{background-color:color-mix(in oklab,var(--muted) 50%,transparent)}}.bg-\[var\(--muted-foreground\)\]{background-color:var(--muted-foreground)}.bg-\[var\(--primary\)\],.bg-\[var\(--primary\)\]\/10{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--primary\)\]\/10{background-color:color-mix(in oklab,var(--primary) 10%,transparent)}}.bg-\[var\(--primary\)\]\/20{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--primary\)\]\/20{background-color:color-mix(in oklab,var(--primary) 20%,transparent)}}.bg-\[var\(--secondary\)\],.bg-\[var\(--secondary\)\]\/30{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--secondary\)\]\/30{background-color:color-mix(in oklab,var(--secondary) 30%,transparent)}}.bg-\[var\(--secondary\)\]\/40{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--secondary\)\]\/40{background-color:color-mix(in oklab,var(--secondary) 40%,transparent)}}.bg-\[var\(--secondary\)\]\/50{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--secondary\)\]\/50{background-color:color-mix(in oklab,var(--secondary) 50%,transparent)}}.bg-amber-400{background-color:var(--color-amber-400)}.bg-amber-400\/10{background-color:#fcbb001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-400\/10{background-color:color-mix(in oklab,var(--color-amber-400) 10%,transparent)}}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500) 10%,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-amber-600{background-color:var(--color-amber-600)}.bg-amber-700{background-color:var(--color-amber-700)}.bg-amber-900\/30{background-color:#7b33064d}@supports (color:color-mix(in lab,red,red)){.bg-amber-900\/30{background-color:color-mix(in oklab,var(--color-amber-900) 30%,transparent)}}.bg-amber-900\/50{background-color:#7b330680}@supports (color:color-mix(in lab,red,red)){.bg-amber-900\/50{background-color:color-mix(in oklab,var(--color-amber-900) 50%,transparent)}}.bg-amber-950\/20{background-color:#46190133}@supports (color:color-mix(in lab,red,red)){.bg-amber-950\/20{background-color:color-mix(in oklab,var(--color-amber-950) 20%,transparent)}}.bg-black{background-color:var(--color-black)}.bg-black\/20{background-color:#0003}@supports (color:color-mix(in lab,red,red)){.bg-black\/20{background-color:color-mix(in oklab,var(--color-black) 20%,transparent)}}.bg-black\/30{background-color:#0000004d}@supports (color:color-mix(in lab,red,red)){.bg-black\/30{background-color:color-mix(in oklab,var(--color-black) 30%,transparent)}}.bg-black\/40{background-color:#0006}@supports (color:color-mix(in lab,red,red)){.bg-black\/40{background-color:color-mix(in oklab,var(--color-black) 40%,transparent)}}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-black\/70{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.bg-black\/70{background-color:color-mix(in oklab,var(--color-black) 70%,transparent)}}.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\/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-blue-400{background-color:var(--color-blue-400)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/10{background-color:color-mix(in oklab,var(--color-blue-500) 10%,transparent)}}.bg-blue-500\/15{background-color:#3080ff26}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/15{background-color:color-mix(in oklab,var(--color-blue-500) 15%,transparent)}}.bg-blue-500\/20{background-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/20{background-color:color-mix(in oklab,var(--color-blue-500) 20%,transparent)}}.bg-blue-700{background-color:var(--color-blue-700)}.bg-blue-900\/30{background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.bg-blue-900\/30{background-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.bg-blue-900\/40{background-color:#1c398e66}@supports (color:color-mix(in lab,red,red)){.bg-blue-900\/40{background-color:color-mix(in oklab,var(--color-blue-900) 40%,transparent)}}.bg-blue-900\/50{background-color:#1c398e80}@supports (color:color-mix(in lab,red,red)){.bg-blue-900\/50{background-color:color-mix(in oklab,var(--color-blue-900) 50%,transparent)}}.bg-current{background-color:currentColor}.bg-emerald-400{background-color:var(--color-emerald-400)}.bg-emerald-500{background-color:var(--color-emerald-500)}.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-emerald-500\/20{background-color:#00bb7f33}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/20{background-color:color-mix(in oklab,var(--color-emerald-500) 20%,transparent)}}.bg-emerald-500\/40{background-color:#00bb7f66}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/40{background-color:color-mix(in oklab,var(--color-emerald-500) 40%,transparent)}}.bg-emerald-500\/70{background-color:#00bb7fb3}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/70{background-color:color-mix(in oklab,var(--color-emerald-500) 70%,transparent)}}.bg-emerald-600{background-color:var(--color-emerald-600)}.bg-emerald-700{background-color:var(--color-emerald-700)}.bg-emerald-800{background-color:var(--color-emerald-800)}.bg-emerald-900\/30{background-color:#004e3b4d}@supports (color:color-mix(in lab,red,red)){.bg-emerald-900\/30{background-color:color-mix(in oklab,var(--color-emerald-900) 30%,transparent)}}.bg-emerald-900\/40{background-color:#004e3b66}@supports (color:color-mix(in lab,red,red)){.bg-emerald-900\/40{background-color:color-mix(in oklab,var(--color-emerald-900) 40%,transparent)}}.bg-emerald-950\/20{background-color:#002c2233}@supports (color:color-mix(in lab,red,red)){.bg-emerald-950\/20{background-color:color-mix(in oklab,var(--color-emerald-950) 20%,transparent)}}.bg-emerald-950\/40{background-color:#002c2266}@supports (color:color-mix(in lab,red,red)){.bg-emerald-950\/40{background-color:color-mix(in oklab,var(--color-emerald-950) 40%,transparent)}}.bg-green-500\/10{background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/10{background-color:color-mix(in oklab,var(--color-green-500) 10%,transparent)}}.bg-orange-600{background-color:var(--color-orange-600)}.bg-orange-700{background-color:var(--color-orange-700)}.bg-purple-900\/30{background-color:#59168b4d}@supports (color:color-mix(in lab,red,red)){.bg-purple-900\/30{background-color:color-mix(in oklab,var(--color-purple-900) 30%,transparent)}}.bg-purple-900\/40{background-color:#59168b66}@supports (color:color-mix(in lab,red,red)){.bg-purple-900\/40{background-color:color-mix(in oklab,var(--color-purple-900) 40%,transparent)}}.bg-purple-900\/50{background-color:#59168b80}@supports (color:color-mix(in lab,red,red)){.bg-purple-900\/50{background-color:color-mix(in oklab,var(--color-purple-900) 50%,transparent)}}.bg-purple-950\/20{background-color:#3c036633}@supports (color:color-mix(in lab,red,red)){.bg-purple-950\/20{background-color:color-mix(in oklab,var(--color-purple-950) 20%,transparent)}}.bg-purple-950\/40{background-color:#3c036666}@supports (color:color-mix(in lab,red,red)){.bg-purple-950\/40{background-color:color-mix(in oklab,var(--color-purple-950) 40%,transparent)}}.bg-red-400{background-color:var(--color-red-400)}.bg-red-500{background-color:var(--color-red-500)}.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-red-500\/15{background-color:#fb2c3626}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/15{background-color:color-mix(in oklab,var(--color-red-500) 15%,transparent)}}.bg-red-600{background-color:var(--color-red-600)}.bg-red-700{background-color:var(--color-red-700)}.bg-red-700\/80{background-color:#bf000fcc}@supports (color:color-mix(in lab,red,red)){.bg-red-700\/80{background-color:color-mix(in oklab,var(--color-red-700) 80%,transparent)}}.bg-red-800{background-color:var(--color-red-800)}.bg-red-900\/30{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.bg-red-900\/30{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.bg-red-950\/40{background-color:#46080966}@supports (color:color-mix(in lab,red,red)){.bg-red-950\/40{background-color:color-mix(in oklab,var(--color-red-950) 40%,transparent)}}.bg-sky-700{background-color:var(--color-sky-700)}.bg-sky-950\/20{background-color:#052f4a33}@supports (color:color-mix(in lab,red,red)){.bg-sky-950\/20{background-color:color-mix(in oklab,var(--color-sky-950) 20%,transparent)}}.bg-teal-500\/15{background-color:#00baa726}@supports (color:color-mix(in lab,red,red)){.bg-teal-500\/15{background-color:color-mix(in oklab,var(--color-teal-500) 15%,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-yellow-400{background-color:var(--color-yellow-400)}.bg-zinc-100{background-color:var(--color-zinc-100)}.fill-\[var\(--muted-foreground\)\]{fill:var(--muted-foreground)}.fill-amber-400{fill:var(--color-amber-400)}.fill-current{fill:currentColor}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:calc(var(--spacing) * 1)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.p-10{padding:calc(var(--spacing) * 10)}.px-0\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0{padding-block:calc(var(--spacing) * 0)}.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-4{padding-block:calc(var(--spacing) * 4)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-px{padding-block:1px}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-2\.5{padding-top:calc(var(--spacing) * 2.5)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pt-\[15vh\]{padding-top:15vh}.pr-1{padding-right:calc(var(--spacing) * 1)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-6{padding-right:calc(var(--spacing) * 6)}.pr-7{padding-right:calc(var(--spacing) * 7)}.pr-14{padding-right:calc(var(--spacing) * 14)}.pr-16{padding-right:calc(var(--spacing) * 16)}.pr-32{padding-right:calc(var(--spacing) * 32)}.pb-1{padding-bottom:calc(var(--spacing) * 1)}.pb-1\.5{padding-bottom:calc(var(--spacing) * 1.5)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-1{padding-left:calc(var(--spacing) * 1)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-2\.5{padding-left:calc(var(--spacing) * 2.5)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-6{padding-left:calc(var(--spacing) * 6)}.pl-7{padding-left:calc(var(--spacing) * 7)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.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-\[8px\]{font-size:8px}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.leading-4{--tw-leading:calc(var(--spacing) * 4);line-height:calc(var(--spacing) * 4)}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.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-\[var\(--accent-foreground\)\]{color:var(--accent-foreground)}.text-\[var\(--background\)\]{color:var(--background)}.text-\[var\(--destructive\)\]{color:var(--destructive)}.text-\[var\(--foreground\)\],.text-\[var\(--foreground\)\]\/80{color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.text-\[var\(--foreground\)\]\/80{color:color-mix(in oklab,var(--foreground) 80%,transparent)}}.text-\[var\(--muted-foreground\)\],.text-\[var\(--muted-foreground\)\]\/40{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-\[var\(--muted-foreground\)\]\/40{color:color-mix(in oklab,var(--muted-foreground) 40%,transparent)}}.text-\[var\(--primary\)\]{color:var(--primary)}.text-\[var\(--primary-foreground\)\]{color:var(--primary-foreground)}.text-amber-50{color:var(--color-amber-50)}.text-amber-300{color:var(--color-amber-300)}.text-amber-400{color:var(--color-amber-400)}.text-amber-500{color:var(--color-amber-500)}.text-amber-600{color:var(--color-amber-600)}.text-black{color:var(--color-black)}.text-blue-100{color:var(--color-blue-100)}.text-blue-300{color:var(--color-blue-300)}.text-blue-400{color:var(--color-blue-400)}.text-blue-500{color:var(--color-blue-500)}.text-cyan-300{color:var(--color-cyan-300)}.text-cyan-400{color:var(--color-cyan-400)}.text-emerald-50{color:var(--color-emerald-50)}.text-emerald-100{color:var(--color-emerald-100)}.text-emerald-300{color:var(--color-emerald-300)}.text-emerald-300\/80{color:#5ee9b5cc}@supports (color:color-mix(in lab,red,red)){.text-emerald-300\/80{color:color-mix(in oklab,var(--color-emerald-300) 80%,transparent)}}.text-emerald-400{color:var(--color-emerald-400)}.text-emerald-600{color:var(--color-emerald-600)}.text-green-300{color:var(--color-green-300)}.text-green-400{color:var(--color-green-400)}.text-indigo-300{color:var(--color-indigo-300)}.text-orange-100{color:var(--color-orange-100)}.text-orange-300{color:var(--color-orange-300)}.text-orange-400{color:var(--color-orange-400)}.text-pink-300{color:var(--color-pink-300)}.text-pink-400{color:var(--color-pink-400)}.text-purple-300{color:var(--color-purple-300)}.text-purple-300\/80{color:#d9b3ffcc}@supports (color:color-mix(in lab,red,red)){.text-purple-300\/80{color:color-mix(in oklab,var(--color-purple-300) 80%,transparent)}}.text-purple-400{color:var(--color-purple-400)}.text-purple-400\/60{color:#c07eff99}@supports (color:color-mix(in lab,red,red)){.text-purple-400\/60{color:color-mix(in oklab,var(--color-purple-400) 60%,transparent)}}.text-red-50{color:var(--color-red-50)}.text-red-100{color:var(--color-red-100)}.text-red-200{color:var(--color-red-200)}.text-red-300{color:var(--color-red-300)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-sky-50{color:var(--color-sky-50)}.text-sky-300{color:var(--color-sky-300)}.text-teal-300{color:var(--color-teal-300)}.text-violet-400{color:var(--color-violet-400)}.text-white{color:var(--color-white)}.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\/80{color:#fffc}@supports (color:color-mix(in lab,red,red)){.text-white\/80{color:color-mix(in oklab,var(--color-white) 80%,transparent)}}.text-yellow-300{color:var(--color-yellow-300)}.text-yellow-400{color:var(--color-yellow-400)}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal}.ordinal,.tabular-nums{font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.tabular-nums{--tw-numeric-spacing:tabular-nums}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-\[var\(--muted-foreground\)\]::placeholder{color:var(--muted-foreground)}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a)}.shadow,.shadow-2xl{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040)}.shadow-\[-6px_0_16px_-6px_rgba\(0\,0\,0\,0\.35\)\]{--tw-shadow:-6px 0 16px -6px var(--tw-shadow-color,#00000059)}.shadow-\[-6px_0_16px_-6px_rgba\(0\,0\,0\,0\.35\)\],.shadow-\[0_0_0_9999px_rgba\(0\,0\,0\,0\.55\)\]{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_0_9999px_rgba\(0\,0\,0\,0\.55\)\]{--tw-shadow:0 0 0 9999px var(--tw-shadow-color,#0000008c)}.shadow-\[6px_0_16px_-6px_rgba\(0\,0\,0\,0\.35\)\]{--tw-shadow:6px 0 16px -6px var(--tw-shadow-color,#00000059)}.shadow-\[6px_0_16px_-6px_rgba\(0\,0\,0\,0\.35\)\],.shadow-\[inset_0_-1px_0_var\(--border\)\]{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[inset_0_-1px_0_var\(--border\)\]{--tw-shadow:inset 0 -1px 0 var(--tw-shadow-color,var(--border))}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a)}.shadow-lg,.shadow-sm{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)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a)}.ring,.ring-1,.shadow-xl{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring,.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-\[var\(--border\)\]{--tw-ring-color:var(--border)}.ring-\[var\(--foreground\)\]{--tw-ring-color:var(--foreground)}.ring-\[var\(--primary\)\],.ring-\[var\(--primary\)\]\/30{--tw-ring-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.ring-\[var\(--primary\)\]\/30{--tw-ring-color:color-mix(in oklab,var(--primary) 30%,transparent)}}.ring-amber-400\/30{--tw-ring-color:#fcbb004d}@supports (color:color-mix(in lab,red,red)){.ring-amber-400\/30{--tw-ring-color:color-mix(in oklab,var(--color-amber-400) 30%,transparent)}}.ring-offset-1{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-\[var\(--card\)\]{--tw-ring-offset-color:var(--card)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px)}.backdrop-blur,.backdrop-blur-sm{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm))}.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-\[translate\,opacity\]{transition-property:translate,opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\,box-shadow\]{transition-property:width,box-shadow;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-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.prose-invert{--tw-prose-body:var(--tw-prose-invert-body);--tw-prose-headings:var(--tw-prose-invert-headings);--tw-prose-lead:var(--tw-prose-invert-lead);--tw-prose-links:var(--tw-prose-invert-links);--tw-prose-bold:var(--tw-prose-invert-bold);--tw-prose-counters:var(--tw-prose-invert-counters);--tw-prose-bullets:var(--tw-prose-invert-bullets);--tw-prose-hr:var(--tw-prose-invert-hr);--tw-prose-quotes:var(--tw-prose-invert-quotes);--tw-prose-quote-borders:var(--tw-prose-invert-quote-borders);--tw-prose-captions:var(--tw-prose-invert-captions);--tw-prose-kbd:var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows:var(--tw-prose-invert-kbd-shadows);--tw-prose-code:var(--tw-prose-invert-code);--tw-prose-pre-code:var(--tw-prose-invert-pre-code);--tw-prose-pre-bg:var(--tw-prose-invert-pre-bg);--tw-prose-th-borders:var(--tw-prose-invert-th-borders);--tw-prose-td-borders:var(--tw-prose-invert-td-borders)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.\[scrollbar-width\:none\]{scrollbar-width:none}.ring-inset{--tw-ring-inset:inset}@media (hover:hover){.group-hover\:block:is(:where(.group):hover *){display:block}.group-hover\:bg-emerald-500\/70:is(:where(.group):hover *){background-color:#00bb7fb3}@supports (color:color-mix(in lab,red,red)){.group-hover\:bg-emerald-500\/70:is(:where(.group):hover *){background-color:color-mix(in oklab,var(--color-emerald-500) 70%,transparent)}}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.odd\:bg-\[var\(--card\)\]\/40:nth-child(odd){background-color:var(--card)}@supports (color:color-mix(in lab,red,red)){.odd\:bg-\[var\(--card\)\]\/40:nth-child(odd){background-color:color-mix(in oklab,var(--card) 40%,transparent)}}.focus-within\:border-\[var\(--primary\)\]:focus-within{border-color:var(--primary)}.focus-within\:ring-1:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-within\:ring-\[var\(--primary\)\]:focus-within{--tw-ring-color:var(--primary)}@media (hover:hover){.hover\:h-1:hover{height:calc(var(--spacing) * 1)}.hover\:w-1:hover{width:calc(var(--spacing) * 1)}.hover\:border-\[var\(--foreground\)\]:hover{border-color:var(--foreground)}.hover\:border-\[var\(--primary\)\]:hover{border-color:var(--primary)}.hover\:bg-\[var\(--accent\)\]:hover,.hover\:bg-\[var\(--accent\)\]\/80:hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--accent\)\]\/80:hover{background-color:color-mix(in oklab,var(--accent) 80%,transparent)}}.hover\:bg-\[var\(--background\)\]\/40:hover{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--background\)\]\/40:hover{background-color:color-mix(in oklab,var(--background) 40%,transparent)}}.hover\:bg-\[var\(--border\)\]:hover{background-color:var(--border)}.hover\:bg-\[var\(--destructive\)\]\/20:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--destructive\)\]\/20:hover{background-color:color-mix(in oklab,var(--destructive) 20%,transparent)}}.hover\:bg-\[var\(--muted\)\]\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--muted\)\]\/50:hover{background-color:color-mix(in oklab,var(--muted) 50%,transparent)}}.hover\:bg-\[var\(--primary\)\]:hover,.hover\:bg-\[var\(--primary\)\]\/40:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--primary\)\]\/40:hover{background-color:color-mix(in oklab,var(--primary) 40%,transparent)}}.hover\:bg-\[var\(--secondary\)\]:hover,.hover\:bg-\[var\(--secondary\)\]\/40:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--secondary\)\]\/40:hover{background-color:color-mix(in oklab,var(--secondary) 40%,transparent)}}.hover\:bg-\[var\(--secondary\)\]\/50:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--secondary\)\]\/50:hover{background-color:color-mix(in oklab,var(--secondary) 50%,transparent)}}.hover\:bg-\[var\(--secondary\)\]\/60:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--secondary\)\]\/60:hover{background-color:color-mix(in oklab,var(--secondary) 60%,transparent)}}.hover\:bg-\[var\(--secondary\)\]\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--secondary\)\]\/80:hover{background-color:color-mix(in oklab,var(--secondary) 80%,transparent)}}.hover\:bg-amber-500\/20:hover{background-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.hover\:bg-amber-500\/20:hover{background-color:color-mix(in oklab,var(--color-amber-500) 20%,transparent)}}.hover\:bg-blue-600:hover{background-color:var(--color-blue-600)}.hover\:bg-emerald-600:hover{background-color:var(--color-emerald-600)}.hover\:bg-green-500\/20:hover{background-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.hover\:bg-green-500\/20:hover{background-color:color-mix(in oklab,var(--color-green-500) 20%,transparent)}}.hover\:bg-orange-700:hover{background-color:var(--color-orange-700)}.hover\:bg-purple-900\/20:hover{background-color:#59168b33}@supports (color:color-mix(in lab,red,red)){.hover\:bg-purple-900\/20:hover{background-color:color-mix(in oklab,var(--color-purple-900) 20%,transparent)}}.hover\:bg-purple-900\/30:hover{background-color:#59168b4d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-purple-900\/30:hover{background-color:color-mix(in oklab,var(--color-purple-900) 30%,transparent)}}.hover\:bg-red-500\/10:hover{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/10:hover{background-color:color-mix(in oklab,var(--color-red-500) 10%,transparent)}}.hover\:bg-red-500\/20:hover{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/20:hover{background-color:color-mix(in oklab,var(--color-red-500) 20%,transparent)}}.hover\:bg-red-500\/25:hover{background-color:#fb2c3640}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/25:hover{background-color:color-mix(in oklab,var(--color-red-500) 25%,transparent)}}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:bg-red-700\/80:hover{background-color:#bf000fcc}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-700\/80:hover{background-color:color-mix(in oklab,var(--color-red-700) 80%,transparent)}}.hover\:bg-red-900\/30:hover{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-900\/30:hover{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.hover\:bg-sky-600:hover{background-color:var(--color-sky-600)}.hover\:bg-white\/10:hover{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/10:hover{background-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.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\:text-\[var\(--foreground\)\]:hover{color:var(--foreground)}.hover\:text-\[var\(--primary\)\]:hover{color:var(--primary)}.hover\:text-amber-200:hover{color:var(--color-amber-200)}.hover\:text-blue-300:hover{color:var(--color-blue-300)}.hover\:text-blue-400:hover{color:var(--color-blue-400)}.hover\:text-green-300:hover{color:var(--color-green-300)}.hover\:text-red-300:hover{color:var(--color-red-300)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:text-white:hover{color:var(--color-white)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}.hover\:opacity-100:hover{opacity:1}.hover\:ring-1:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-\[var\(--primary\)\]\/40:hover{--tw-ring-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:ring-\[var\(--primary\)\]\/40:hover{--tw-ring-color:color-mix(in oklab,var(--primary) 40%,transparent)}}}.focus\:border-\[var\(--primary\)\]:focus{border-color:var(--primary)}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:border-red-500:focus{border-color:var(--color-red-500)}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-\[var\(--primary\)\]:focus{--tw-ring-color:var(--primary)}@media (hover:hover){.enabled\:hover\:bg-\[var\(--secondary\)\]:enabled:hover{background-color:var(--secondary)}.enabled\:hover\:text-\[var\(--foreground\)\]:enabled:hover{color:var(--foreground)}.enabled\:hover\:text-emerald-300:enabled:hover{color:var(--color-emerald-300)}}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-default:disabled{cursor:default}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}@media (hover:hover){.disabled\:hover\:bg-transparent:disabled:hover{background-color:#0000}}@media (prefers-reduced-motion:reduce){.motion-reduce\:transition-none{transition-property:none}}@media (min-width:40rem){.sm\:py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.sm\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@media (min-width:48rem){.md\:block{display:block}.md\:flex{display:flex}}@starting-style{.starting\:translate-x-4{--tw-translate-x:calc(var(--spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.starting\:opacity-0{opacity:0}}.prose-headings\:my-2 :where(h1,h2,h3,h4,h5,h6,th):not(:where([class~=not-prose],[class~=not-prose] *)){margin-block:calc(var(--spacing) * 2)}.prose-headings\:scroll-mt-4 :where(h1,h2,h3,h4,h5,h6,th):not(:where([class~=not-prose],[class~=not-prose] *)){scroll-margin-top:calc(var(--spacing) * 4)}.prose-headings\:font-semibold :where(h1,h2,h3,h4,h5,h6,th):not(:where([class~=not-prose],[class~=not-prose] *)){--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.prose-headings\:text-\[var\(--foreground\)\] :where(h1,h2,h3,h4,h5,h6,th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--foreground)}.prose-h1\:border-b :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.prose-h1\:border-\[var\(--border\)\] :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--border)}.prose-h1\:pb-2 :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){padding-bottom:calc(var(--spacing) * 2)}.prose-h1\:text-2xl :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.prose-h2\:border-b :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.prose-h2\:border-\[var\(--border\)\] :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--border)}.prose-h2\:pb-1\.5 :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){padding-bottom:calc(var(--spacing) * 1.5)}.prose-h2\:text-xl :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.prose-h3\:text-lg :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.prose-p\:my-1\.5 :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-block:calc(var(--spacing) * 1.5)}.prose-p\:leading-relaxed :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.prose-p\:text-\[var\(--foreground\)\] :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--foreground)}.prose-a\:text-\[var\(--primary\)\] :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--primary)}.prose-a\:text-blue-400 :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--color-blue-400)}.prose-a\:no-underline :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}@media (hover:hover){.hover\:prose-a\:underline:hover :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:underline}}.prose-blockquote\:border-l-blue-500 :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){border-left-color:var(--color-blue-500)}.prose-blockquote\:text-\[var\(--muted-foreground\)\] :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--muted-foreground)}.prose-strong\:text-\[var\(--foreground\)\] :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--foreground)}.prose-code\:rounded :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){border-radius:.25rem}.prose-code\:bg-\[var\(--secondary\)\] :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:var(--secondary)}.prose-code\:px-1 :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline:calc(var(--spacing) * 1)}.prose-code\:px-1\.5 :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline:calc(var(--spacing) * 1.5)}.prose-code\:py-0\.5 :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){padding-block:calc(var(--spacing) * .5)}.prose-code\:text-xs :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.prose-code\:text-amber-300 :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--color-amber-300)}.prose-code\:after\:content-none :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after,.prose-code\:before\:content-none :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:var(--tw-content);--tw-content:none;content:none}.prose-pre\:my-2 :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){margin-block:calc(var(--spacing) * 2)}.prose-pre\:rounded-lg :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){border-radius:var(--radius-lg)}.prose-pre\:border :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){border-style:var(--tw-border-style);border-width:1px}.prose-pre\:border-\[var\(--border\)\] :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--border)}.prose-pre\:bg-\[var\(--secondary\)\] :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:var(--secondary)}.prose-ol\:my-1\.5 :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)),.prose-ul\:my-1\.5 :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-block:calc(var(--spacing) * 1.5)}.prose-li\:my-0\.5 :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-block:calc(var(--spacing) * .5)}.prose-li\:text-\[var\(--foreground\)\] :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--foreground)}.prose-table\:border-collapse :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){border-collapse:collapse}.prose-th\:border :where(th):not(:where([class~=not-prose],[class~=not-prose] *)){border-style:var(--tw-border-style);border-width:1px}.prose-th\:border-\[var\(--border\)\] :where(th):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--border)}.prose-th\:bg-\[var\(--secondary\)\] :where(th):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:var(--secondary)}.prose-th\:px-2 :where(th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline:calc(var(--spacing) * 2)}.prose-th\:px-3 :where(th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline:calc(var(--spacing) * 3)}.prose-th\:py-1 :where(th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-block:calc(var(--spacing) * 1)}.prose-th\:py-1\.5 :where(th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-block:calc(var(--spacing) * 1.5)}.prose-th\:text-\[var\(--foreground\)\] :where(th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--foreground)}.prose-td\:border :where(td):not(:where([class~=not-prose],[class~=not-prose] *)){border-style:var(--tw-border-style);border-width:1px}.prose-td\:border-\[var\(--border\)\] :where(td):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--border)}.prose-td\:px-2 :where(td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline:calc(var(--spacing) * 2)}.prose-td\:px-3 :where(td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline:calc(var(--spacing) * 3)}.prose-td\:py-1 :where(td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-block:calc(var(--spacing) * 1)}.prose-td\:py-1\.5 :where(td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-block:calc(var(--spacing) * 1.5)}.prose-td\:text-\[var\(--foreground\)\] :where(td):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--foreground)}.prose-img\:rounded-lg :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){border-radius:var(--radius-lg)}.prose-hr\:border-\[var\(--border\)\] :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--border)}.\[\&\:\:-webkit-scrollbar\]\:hidden::-webkit-scrollbar{display:none}}:root{--background:#0a0a0a;--foreground:#ededed;--card:#141414;--card-foreground:#ededed;--border:#2a2a2a;--input:#2a2a2a;--primary:#3b82f6;--primary-foreground:#fff;--secondary:#1e1e1e;--secondary-foreground:#a1a1a1;--muted:#1e1e1e;--muted-foreground:#737373;--accent:#1e293b;--accent-foreground:#e2e8f0;--destructive:#ef4444;--ring:#3b82f6;--radius:.5rem;--scrollbar-thumb:#333;--scrollbar-thumb-hover:#555}[data-theme=light]{--background:#fff;--foreground:#171717;--card:#fafafa;--card-foreground:#171717;--border:#e5e5e5;--input:#e5e5e5;--primary:#2563eb;--primary-foreground:#fff;--secondary:#f5f5f5;--secondary-foreground:#525252;--muted:#f5f5f5;--muted-foreground:#737373;--accent:#e2e8f0;--accent-foreground:#1e293b;--destructive:#dc2626;--ring:#2563eb;--scrollbar-thumb:#d4d4d4;--scrollbar-thumb-hover:#a3a3a3}[data-theme=midnight]{--background:#0b1220;--foreground:#dbe4f3;--card:#111a2e;--card-foreground:#dbe4f3;--border:#1e2a47;--input:#1e2a47;--primary:#38bdf8;--primary-foreground:#0b1220;--secondary:#16213a;--secondary-foreground:#93a5c8;--muted:#16213a;--muted-foreground:#64769b;--accent:#1c2b4d;--accent-foreground:#d6e2f5;--destructive:#f87171;--ring:#38bdf8;--scrollbar-thumb:#243252;--scrollbar-thumb-hover:#33456e}[data-theme=warm]{--background:#f6f1e7;--foreground:#3d3427;--card:#fdfaf3;--card-foreground:#3d3427;--border:#ddd2bd;--input:#ddd2bd;--primary:#b45309;--primary-foreground:#fff;--secondary:#efe7d8;--secondary-foreground:#6b5d48;--muted:#efe7d8;--muted-foreground:#7d6e55;--accent:#e9dfc9;--accent-foreground:#4a3f2e;--destructive:#b91c1c;--ring:#b45309;--scrollbar-thumb:#d3c5a9;--scrollbar-thumb-hover:#b7a67f}[data-theme=light],[data-theme=warm]{--color-red-200:oklch(44.4% .177 26.899);--color-amber-200:oklch(47.3% .137 46.201);--color-red-300:oklch(50.5% .213 27.518);--color-orange-300:oklch(55.3% .195 38.402);--color-amber-300:oklch(55.5% .163 48.998);--color-yellow-300:oklch(55.4% .135 66.442);--color-green-300:oklch(52.7% .154 150.069);--color-emerald-300:oklch(50.8% .118 165.612);--color-teal-300:oklch(51.1% .096 186.391);--color-cyan-300:oklch(52% .105 223.128);--color-sky-300:oklch(50% .134 242.749);--color-blue-300:oklch(48.8% .243 264.376);--color-indigo-300:oklch(45.7% .24 277.023);--color-purple-300:oklch(49.6% .265 301.924);--color-pink-300:oklch(52.5% .223 3.958);--color-red-400:oklch(57.7% .245 27.325);--color-orange-400:oklch(64.6% .222 41.116);--color-amber-400:oklch(55.5% .163 48.998);--color-yellow-400:oklch(55.4% .135 66.442);--color-green-400:oklch(62.7% .194 149.214);--color-emerald-400:oklch(59.6% .145 163.225);--color-cyan-400:oklch(60.9% .126 221.723);--color-blue-400:oklch(54.6% .245 262.881);--color-purple-400:oklch(55.8% .288 302.321);--color-pink-400:oklch(59.2% .249 .584);--color-red-900:oklch(88.5% .062 18.334);--color-amber-900:oklch(92.4% .12 95.746);--color-emerald-900:oklch(90.5% .093 164.15);--color-blue-900:oklch(88.2% .059 254.128);--color-purple-900:oklch(90.2% .063 306.703);--color-red-950:oklch(93.6% .032 17.717);--color-amber-950:oklch(96.2% .059 95.617);--color-emerald-950:oklch(95% .052 163.051);--color-purple-950:oklch(94.6% .033 307.174);--color-sky-950:oklch(95.1% .026 236.824)}[data-theme=light] .prose,[data-theme=warm] .prose{--tw-prose-invert-body:var(--foreground);--tw-prose-invert-headings:var(--foreground);--tw-prose-invert-lead:var(--muted-foreground);--tw-prose-invert-links:var(--primary);--tw-prose-invert-bold:var(--foreground);--tw-prose-invert-counters:var(--muted-foreground);--tw-prose-invert-bullets:var(--muted-foreground);--tw-prose-invert-hr:var(--border);--tw-prose-invert-quotes:var(--foreground);--tw-prose-invert-quote-borders:var(--border);--tw-prose-invert-captions:var(--muted-foreground);--tw-prose-invert-kbd:var(--foreground);--tw-prose-invert-kbd-shadows:#0000001a;--tw-prose-invert-code:var(--foreground);--tw-prose-invert-pre-code:var(--foreground);--tw-prose-invert-pre-bg:var(--secondary);--tw-prose-invert-th-borders:var(--border);--tw-prose-invert-td-borders:var(--border)}*{border-color:var(--border)}body{background:var(--background);color:var(--foreground)}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}[data-panel-resize-handle-id]{background:var(--border);transition:background .15s}[data-panel-resize-handle-id]:hover,[data-panel-resize-handle-id][data-resize-handle-active]{background:var(--primary)}@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-divide-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-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@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}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(1turn)}}@keyframes pulse{50%{opacity:.5}}
|
package/admin-out/index.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><!--
|
|
1
|
+
<!DOCTYPE html><!--8v1hCXbUqJhyMuK7LVeet--><html lang="en" class="dark"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/e4af272ccee01ff0-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/7b736202f0b1dbfd.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/e890aa0d13e94290.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/659eccb5db697b76.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-74f577c1b094f942.js"/><script src="/_next/static/chunks/1b86ba38-c29611e086b2ce25.js" async=""></script><script src="/_next/static/chunks/698-19872069fed27164.js" async=""></script><script src="/_next/static/chunks/main-app-340a5b58ed229c4d.js" async=""></script><script src="/_next/static/chunks/989-066a874961bd519f.js" async=""></script><script src="/_next/static/chunks/app/layout-ea161287d934ba96.js" async=""></script><script src="/_next/static/chunks/1d2d5650-d8d130c85727fdbb.js" async=""></script><script src="/_next/static/chunks/87-6a2716c98a819c4e.js" async=""></script><script src="/_next/static/chunks/app/page-812bcbb46db628b6.js" async=""></script><meta name="next-size-adjust" content=""/><title>Halo - Multi-Agent Workspace</title><meta name="description" content="Multi-agent collaboration workspace for complex project delivery"/><link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16"/><link rel="icon" href="/icon.svg?3a6863548daca964" type="image/svg+xml" sizes="any"/><script>try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}</script><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="__variable_d8ebd1 font-sans antialiased"><div hidden=""><!--$--><!--/$--></div><div class="flex h-screen w-screen items-center justify-center bg-[var(--background)]"><div class="text-sm text-[var(--muted-foreground)]">Loading...</div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-74f577c1b094f942.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[58321,[\"989\",\"static/chunks/989-066a874961bd519f.js\",\"177\",\"static/chunks/app/layout-ea161287d934ba96.js\"],\"ThemeProvider\"]\n3:I[24554,[\"989\",\"static/chunks/989-066a874961bd519f.js\",\"177\",\"static/chunks/app/layout-ea161287d934ba96.js\"],\"AppI18nProvider\"]\n4:I[63693,[],\"\"]\n5:I[74713,[],\"\"]\n6:I[5444,[],\"ClientPageRoot\"]\n7:I[3124,[\"560\",\"static/chunks/1d2d5650-d8d130c85727fdbb.js\",\"87\",\"static/chunks/87-6a2716c98a819c4e.js\",\"989\",\"static/chunks/989-066a874961bd519f.js\",\"974\",\"static/chunks/app/page-812bcbb46db628b6.js\"],\"default\"]\na:I[23632,[],\"OutletBoundary\"]\nc:I[86889,[],\"AsyncMetadataOutlet\"]\ne:I[23632,[],\"ViewportBoundary\"]\n10:I[23632,[],\"MetadataBoundary\"]\n11:\"$Sreact.suspense\"\n13:I[1935,[],\"\"]\n:HL[\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/7b736202f0b1dbfd.css\",\"style\"]\n:HL[\"/_next/static/css/e890aa0d13e94290.css\",\"style\"]\n:HL[\"/_next/static/css/659eccb5db697b76.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"8v1hCXbUqJhyMuK7LVeet\",\"p\":\"\",\"c\":[\"\",\"\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/7b736202f0b1dbfd.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/e890aa0d13e94290.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"dark\",\"suppressHydrationWarning\":true,\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}\"}}]}],[\"$\",\"body\",null,{\"className\":\"__variable_d8ebd1 font-sans antialiased\",\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]}]}]]}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L6\",null,{\"Component\":\"$7\",\"searchParams\":{},\"params\":{},\"promises\":[\"$@8\",\"$@9\"]}],[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/659eccb5db697b76.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"$La\",null,{\"children\":[\"$Lb\",[\"$\",\"$Lc\",null,{\"promise\":\"$@d\"}]]}]]}],{},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[[\"$\",\"$Le\",null,{\"children\":\"$Lf\"}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]],[\"$\",\"$L10\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$11\",null,{\"fallback\":null,\"children\":\"$L12\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$13\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"8:{}\n9:\"$0:f:0:1:2:children:1:props:children:0:props:params\"\n"])</script><script>self.__next_f.push([1,"f:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\nb:null\n"])</script><script>self.__next_f.push([1,"14:I[88809,[],\"IconMark\"]\nd:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"Halo - Multi-Agent Workspace\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Multi-agent collaboration workspace for complex project delivery\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"16x16\"}],[\"$\",\"link\",\"3\",{\"rel\":\"icon\",\"href\":\"/icon.svg?3a6863548daca964\",\"type\":\"image/svg+xml\",\"sizes\":\"any\"}],[\"$\",\"$L14\",\"4\",{}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"12:\"$d:metadata\"\n"])</script></body></html>
|
package/admin-out/index.txt
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
1:"$Sreact.fragment"
|
|
2
|
-
2:I[58321,["989","static/chunks/989-
|
|
3
|
-
3:I[24554,["989","static/chunks/989-
|
|
2
|
+
2:I[58321,["989","static/chunks/989-066a874961bd519f.js","177","static/chunks/app/layout-ea161287d934ba96.js"],"ThemeProvider"]
|
|
3
|
+
3:I[24554,["989","static/chunks/989-066a874961bd519f.js","177","static/chunks/app/layout-ea161287d934ba96.js"],"AppI18nProvider"]
|
|
4
4
|
4:I[63693,[],""]
|
|
5
5
|
5:I[74713,[],""]
|
|
6
6
|
6:I[5444,[],"ClientPageRoot"]
|
|
7
|
-
7:I[
|
|
7
|
+
7:I[3124,["560","static/chunks/1d2d5650-d8d130c85727fdbb.js","87","static/chunks/87-6a2716c98a819c4e.js","989","static/chunks/989-066a874961bd519f.js","974","static/chunks/app/page-812bcbb46db628b6.js"],"default"]
|
|
8
8
|
a:I[23632,[],"OutletBoundary"]
|
|
9
9
|
c:I[86889,[],"AsyncMetadataOutlet"]
|
|
10
10
|
e:I[23632,[],"ViewportBoundary"]
|
|
@@ -13,9 +13,9 @@ e:I[23632,[],"ViewportBoundary"]
|
|
|
13
13
|
13:I[1935,[],""]
|
|
14
14
|
:HL["/_next/static/media/e4af272ccee01ff0-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
|
15
15
|
:HL["/_next/static/css/7b736202f0b1dbfd.css","style"]
|
|
16
|
-
:HL["/_next/static/css/
|
|
16
|
+
:HL["/_next/static/css/e890aa0d13e94290.css","style"]
|
|
17
17
|
:HL["/_next/static/css/659eccb5db697b76.css","style"]
|
|
18
|
-
0:{"P":null,"b":"
|
|
18
|
+
0:{"P":null,"b":"8v1hCXbUqJhyMuK7LVeet","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/7b736202f0b1dbfd.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/_next/static/css/e890aa0d13e94290.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"dark","suppressHydrationWarning":true,"children":[["$","head",null,{"children":["$","script",null,{"dangerouslySetInnerHTML":{"__html":"try{var t=localStorage.getItem('halo_theme');if(t==='light'||t==='midnight'||t==='warm')document.documentElement.dataset.theme=t}catch(e){}"}}]}],["$","body",null,{"className":"__variable_d8ebd1 font-sans antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L6",null,{"Component":"$7","searchParams":{},"params":{},"promises":["$@8","$@9"]}],[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/659eccb5db697b76.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","$La",null,{"children":["$Lb",["$","$Lc",null,{"promise":"$@d"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$Le",null,{"children":"$Lf"}],["$","meta",null,{"name":"next-size-adjust","content":""}]],["$","$L10",null,{"children":["$","div",null,{"hidden":true,"children":["$","$11",null,{"fallback":null,"children":"$L12"}]}]}]]}],false]],"m":"$undefined","G":["$13",[]],"s":false,"S":true}
|
|
19
19
|
8:{}
|
|
20
20
|
9:"$0:f:0:1:2:children:1:props:children:0:props:params"
|
|
21
21
|
f:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
|
package/bin/halo.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
if (Number(process.versions.node.split('.')[0]) < 22) {
|
|
3
|
+
process.stderr.write('halo requires Node.js 22 or newer (found ' + process.version + '). Upgrade: https://nodejs.org/ (or `nvm install 22`)\n')
|
|
4
|
+
process.exit(1)
|
|
5
|
+
}
|
|
2
6
|
const emit = process.emitWarning.bind(process)
|
|
3
7
|
process.emitWarning = (warning, ...args) => {
|
|
4
8
|
const o = args[0]
|
|
@@ -179,7 +179,7 @@ File: `tools/workspace-tools.ts`. 9 tools: file_read / view_image / file_write /
|
|
|
179
179
|
File: `ws/handler.ts` (`cleanupConnection`) + `bufferDetachedNotification` in `ws/event-processor.ts`. After WS disconnect, an inline handler takes over the session tree's events and buffers structural ones for replay on reconnect. `/session new` only opens a draft tab — the previous session stays subscribed — see [design/background-dispatch.md](background-dispatch.md).
|
|
180
180
|
|
|
181
181
|
### WorkspaceWatcher — file watching
|
|
182
|
-
File: `ws/file-watcher.ts`. `@parcel/watcher` recursive native subscription (one per workspace root, shared across connections via `ws/watcher-pool.ts`) → 300ms debounce + per-path Map dedup → callback. Ignores node_modules / .git / .next / dist etc. (`IGNORED_SEGMENTS`) — passed to parcel as the bare names (top-level `ignorePaths`, string compare) plus **one** `**/{a,b,…}/**` brace glob for nested occurrences. Not one glob per segment: parcel runs every ignore regex against every inode during the initial walk, and 52 separate regexes made subscribe on a ~26k-inode repo take ~13s (one brace regex: ~0.8s, same watch set). `.halo/sessions/` and `.halo/logs/` are **not** ignored — the dedup keeps volume low, and the front-end drops `change` events for files not open in the editor, so Explorer can reflect session deletions/creations while chat streaming stays cheap.
|
|
182
|
+
File: `ws/file-watcher.ts`. `@parcel/watcher` recursive native subscription (one per workspace root, shared across connections via `ws/watcher-pool.ts`) → 300ms debounce + per-path Map dedup → callback. Ignores node_modules / .git / .next / dist etc. (`IGNORED_SEGMENTS`, which also sets folder download's default exclusions — `routes/folder-zip.ts`) — passed to parcel as the bare names (top-level `ignorePaths`, string compare) plus **one** `**/{a,b,…}/**` brace glob for nested occurrences. Not one glob per segment: parcel runs every ignore regex against every inode during the initial walk, and 52 separate regexes made subscribe on a ~26k-inode repo take ~13s (one brace regex: ~0.8s, same watch set). `.halo/sessions/` and `.halo/logs/` are **not** ignored — the dedup keeps volume low, and the front-end drops `change` events for files not open in the editor, so Explorer can reflect session deletions/creations while chat streaming stays cheap.
|
|
183
183
|
|
|
184
184
|
### TerminalManager — PTY management
|
|
185
185
|
File: `ws/terminal-manager.ts`. Spawns a shell via node-pty. On disconnect, detaches with a 50KB ring buffer; replays on reconnect.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Canvas preview extensions — installable file viewers in sandboxed iframes
|
|
2
2
|
|
|
3
|
-
> API: [dev/api.md → Canvas Preview Extensions](../dev/api.md#canvas-preview-extensions). WS frame `extension:changed`: [ws.md](ws.md#other-server--client-messages). User-visible behavior: [requirements/editor.md](../requirements/editor.md). Code: `packages/core/src/protocol/extension-{types,frames}.ts`, `packages/server/src/extensions/{registry,watcher,install}.ts` + `routes/extensions.ts` + `middleware/auth.ts` (scoped token), `PUT /files/raw` in `routes/files.ts`, `routes/transcribe-ws.ts` (transcription proxy) + `settings-schema.ts` `extensionSections()`, `packages/admin/src/features/editor/previews/{registry,extension-host,extension-host-logic,extension-token}.ts(x)`, `packages/server/templates/skills/extension/`, `packages/server/src/agents/skill-sources.ts` (extension-shipped skills).
|
|
3
|
+
> API: [dev/api.md → Canvas Preview Extensions](../dev/api.md#canvas-preview-extensions). WS frame `extension:changed`: [ws.md](ws.md#other-server--client-messages). User-visible behavior: [requirements/editor.md](../requirements/editor.md). Code: `packages/core/src/protocol/extension-{types,frames}.ts`, `packages/server/src/extensions/{registry,watcher,install}.ts` + `routes/extensions.ts` + `middleware/auth.ts` (scoped token), `PUT /files/raw` in `routes/files.ts`, `routes/transcribe-ws.ts` (transcription proxy) + `settings-schema.ts` `extensionSections()`, `packages/admin/src/features/editor/previews/{registry,extension-host,extension-host-logic,extension-token}.ts(x)`, `packages/admin/src/features/workspace/extension-activity-{panel,logic}.ts(x)` (activity-bar pages + the shared file-less `ExtensionPageFrame`), `packages/admin/src/features/chat/extension-chatbar-{control,logic}.ts(x)` (chat-bar panels), `previews/extension-icon.tsx` (mask icon), `packages/server/templates/skills/extension/`, `packages/server/src/agents/skill-sources.ts` (extension-shipped skills).
|
|
4
4
|
|
|
5
5
|
## Problem
|
|
6
6
|
|
|
@@ -28,13 +28,15 @@ The admin editor's preview set was compile-time: eight plugins registered in `pr
|
|
|
28
28
|
| `id` | ✓ | `ID_RE`; **must equal the directory name** (`id mismatch` otherwise) |
|
|
29
29
|
| `name` | ✓ | ≤ 64 chars; shown in the open-with menu and Settings → Extensions |
|
|
30
30
|
| `version` | ✓ | semver `x.y.z[-pre]`; compared for **equality only**; part of the asset URL |
|
|
31
|
-
| `extensions` | ✓ |
|
|
31
|
+
| `extensions` | ✓ | array, each `^\.[a-z0-9]+$` (dot, lowercase); matched against the lowercased file ext. Non-empty (`extensions must be a non-empty array`) unless the manifest declares `activity` or `chatbar`, where `[]` = opens no files; the key stays required either way (`ext.sh`'s pre-check demands it) — with `activity` / `chatbar`, a non-array is `extensions must be an array (empty is fine with activity or chatbar)` |
|
|
32
32
|
| `entry` | ✓ | relative HTML path, no leading `/`, no `..`; file must exist |
|
|
33
33
|
| `priority` | – | `default` (default; opens the file directly) \| `option` (open-with menu only) |
|
|
34
|
-
| `capabilities` | – | `"save"` \| `"media"` (iframe gets `allow="microphone; display-capture; clipboard-write"`) \| `"transcribe"` (may open the server's [transcription proxy](#streaming-transcription-proxy)) \| `"fs-read"` (read-only `fs` over the workspace and the whole machine + the host file picker, bundle or not — see [Scoped file access and the picker](#scoped-file-access-and-the-picker-fs-read)); **unknown value = manifest error** (reject rather than let the host silently drop a declared capability) |
|
|
34
|
+
| `capabilities` | – | `"save"` \| `"media"` (iframe gets `allow="microphone; display-capture; clipboard-write"`) \| `"transcribe"` (may open the server's [transcription proxy](#streaming-transcription-proxy)) \| `"fs-read"` (read-only `fs` over the workspace and the whole machine + the host file picker, bundle or not — see [Scoped file access and the picker](#scoped-file-access-and-the-picker-fs-read)) \| `"stt-local"` (may open the server's `/api/stt/stream` WS for on-device speech-to-text — see [stt-local.md](stt-local.md)); **unknown value = manifest error** (reject rather than let the host silently drop a declared capability) |
|
|
35
35
|
| `bundle` | – | boolean, default `false`; `true` = every suffix names a **directory** (see [Bundle extensions](#bundle-extensions)). Non-boolean, or `true` together with `save` (`bundle extensions cannot declare save`) = manifest error |
|
|
36
36
|
| `platforms` | – | non-empty array of `web` \| `desktop-mac` \| `desktop-win` \| `desktop-linux` (deduped); omitted = everywhere. Empty / unknown value = manifest error |
|
|
37
37
|
| `settings` | – | `{ params?: [field…], secrets?: [field…] }`, field = `{ key, description?, description_zh?, default?, type?, options? }` (skill `config.yaml` format). Validated **strictly** (a skill's config.yaml is parsed leniently): `key` `^[a-z][a-z0-9_]{0,63}$`, unique across both lists, `type` ∈ string\|int\|float\|boolean\|enum (enum needs `options`), scalar `default` stringified; anything else = manifest error. `ExtensionInfo.settings` carries the declarations, never values. See [Extension settings](#extension-settings) |
|
|
38
|
+
| `activity` | – | `{ icon?, title?, title_zh?, description?, description_zh?, order? }` = one admin activity-bar button (see [Activity-bar entries](#activity-bar-entries)). Checked after `description`, before `extensions`: non-object → `activity must be an object`; `icon` relative `.svg` / `.png` (no leading `/`, no `..`, `/\.(svg\|png)$/i`) else `activity.icon must be a relative .svg or .png path inside the extension directory`, and must be a file (`activity icon not found: <icon>`); `title` / `title_zh` non-blank ≤ 64 (`activity.<k> must be a non-empty string (≤ 64 chars)`); `description` / `description_zh` ≤ 200 (`activity.<k> must be a string (≤ 200 chars)`); `order` integer 0–1000 (`activity.order must be an integer 0–1000`), default 100. Unknown keys inside are ignored; `ExtensionInfo.activity` carries only the fields present plus `order` |
|
|
39
|
+
| `chatbar` | – | `{ icon?, title?, title_zh?, height?, order? }` = one button on the chat input toolbar (see [Chat-bar entries](#chat-bar-entries)). Checked after `activity`, before `extensions`: non-object → `chatbar must be an object`; `icon` same rule as `activity.icon` (`chatbar.icon must be a relative .svg or .png path inside the extension directory`, `chatbar icon not found: <icon>`); `title` / `title_zh` non-blank ≤ 64 (`chatbar.<k> must be a non-empty string (≤ 64 chars)`); `height` integer 80–480 (`chatbar.height must be an integer 80–480`), default 160; `order` integer 0–1000 (`chatbar.order must be an integer 0–1000`), default 100. Unknown keys inside are ignored; `ExtensionInfo.chatbar` carries only the fields present plus `height` and `order` |
|
|
38
40
|
| `description` / `homepage` / `license` | – | ≤ 200 chars / http(s) URL / free string, display only |
|
|
39
41
|
|
|
40
42
|
No `keepAlive` / `readonly` field: both are derived from `capabilities` (see MRU). No `minHaloVersion`: compatibility is the protocol version carried in `init`.
|
|
@@ -114,7 +116,7 @@ Types in `packages/core/src/protocol/extension-frames.ts` (`EXTENSION_PROTOCOL_V
|
|
|
114
116
|
| Dir | `type` | Fields | Semantics |
|
|
115
117
|
|---|---|---|---|
|
|
116
118
|
| ext→host | `ready` | `protocol` | listener installed; host sends nothing before it. v1 host doesn't read `protocol` (field reserved for negotiation). A second `ready` (the extension reloaded itself) is not ignored: the host re-sends `init` (+ `load`, except to a bundle) and clears the tab's dirty flag, since the reloaded document holds nothing |
|
|
117
|
-
| host→ext | `init` | `protocol`, `file{name,path,size,ext}`, `capabilities`, `theme`, `themeVars`, `bundle`, `platform`, `lang`, `export`, `jupyter?` | right after each `ready`; `capabilities` = what the host grants; for a bundle `file` is the directory; `platform` = host platform, `lang` = `zh`\|`en` admin UI language; `theme` / `themeVars` see [Theme tokens](#theme-tokens); `export` = this host accepts `export` frames (see [Export](#export)) — older hosts omit it, so show export UI only when it is `true`; `jupyter` = `{ projectId }` when this host serves the [notebook kernel proxy](#notebook-kernels-jupyter-proxy) — sent to every non-bundle extension in a workspace panel (1.6.1+), omitted by older hosts → hide kernel UI |
|
|
119
|
+
| host→ext | `init` | `view`, `protocol`, `file{name,path,size,ext}`, `capabilities`, `theme`, `themeVars`, `bundle`, `platform`, `lang`, `export`, `jupyter?` | right after each `ready`; `view` = `'file'` from the editor host (older hosts omit it — absent means `'file'`); an [activity-bar page](#activity-bar-entries) gets `view: 'activity'` and a [chat-bar panel](#chat-bar-entries) `view: 'chatbar'`, both with only `protocol`, `capabilities`, `theme`, `themeVars`, `platform`, `lang`; `capabilities` = what the host grants; for a bundle `file` is the directory; `platform` = host platform, `lang` = `zh`\|`en` admin UI language; `theme` / `themeVars` see [Theme tokens](#theme-tokens); `export` = this host accepts `export` frames (see [Export](#export)) — older hosts omit it, so show export UI only when it is `true`; `jupyter` = `{ projectId }` when this host serves the [notebook kernel proxy](#notebook-kernels-jupyter-proxy) — sent to every non-bundle extension in a workspace panel (1.6.1+), omitted by older hosts → hide kernel UI |
|
|
118
120
|
| host→ext | `load` | `buffer`, `mtime` | file bytes (transferred); resent when the file changes on disk and the doc isn't dirty → "replace current document". **Never sent to a bundle** |
|
|
119
121
|
| ext→host | `dirty` | `dirty` | needs `save` (or `bundle`, where it means "busy"); otherwise ignored + `console.warn` |
|
|
120
122
|
| ext→host | `fs` | `id`, `op: read\|write\|append\|list\|stat`, `path`, `scope?: workspace\|system`, `buffer?` | no `scope`: bundle only (else `fs-result{denied}` + warn), `path` bundle-relative POSIX, all ops. With `scope` (needs `fs-read`, bundle or not; else `denied` + warn): `read` / `stat` / `list` only (`write` / `append` → `denied`), `workspace` = workspace-relative POSIX, `system` = absolute normalized path (POSIX `/…`, Windows `C:/…`, forward slashes, no `..`). Paths are validated before any request — see [Scoped file access](#scoped-file-access-and-the-picker-fs-read) |
|
|
@@ -131,6 +133,9 @@ Types in `packages/core/src/protocol/extension-frames.ts` (`EXTENSION_PROTOCOL_V
|
|
|
131
133
|
| ext→host | `export` | `name`, `buffer` | `init.export` hosts only; write `buffer` (transferred) next to the open file as `name` — see [Export](#export) |
|
|
132
134
|
| host→ext | `exported` | `name`, `path` | export written; `path` = workspace-relative path |
|
|
133
135
|
| host→ext | `export-error` | `reason: denied\|cancelled\|invalid\|io`, `message` | exactly one of `exported` / `export-error` per `export`; `cancelled` = the user declined to overwrite |
|
|
136
|
+
| ext→host | `insert-text` | `text` | [chat-bar panel](#chat-bar-entries) only (any other view: ignored + warn): insert `text` verbatim at the chat input's caret, replacing its selection; the host adds no spacing and **never sends**. Non-string, empty or over 10 000 chars → ignored + `console.warn('[ExtensionChatbar] …')`. No reply frame |
|
|
137
|
+
| ext→host | `busy` | `busy` (boolean) | [chat-bar panel](#chat-bar-entries) only (any other view: ignored + warn): closing the panel now would lose work in flight (recording, a result still pending). Initially false, and reset by every `ready`. Non-boolean → ignored + warn. No reply frame |
|
|
138
|
+
| host→ext | `close-request` | – | chat-bar panel only: the user closed it (its button / × / Esc) while it last said `busy: true`. The panel stays mounted, `insert-text` still lands; the extension winds down and reports `busy: false` → unmounted. 35 s without it, or a second user close → unmounted anyway |
|
|
134
139
|
|
|
135
140
|
```
|
|
136
141
|
host extension
|
|
@@ -209,6 +214,32 @@ A viewer extension (emulator, model viewer) otherwise sees only its own file or
|
|
|
209
214
|
|
|
210
215
|
`currentPlatform()` (`registry.ts`, computed once): UA contains `Electron/` → `desktop-mac|win|linux` by OS, else `web`. An extension whose `platforms` excludes it is skipped by every resolve (routes as if not installed; a bundle directory is a plain folder), the fallback page says `"<name>" only runs on: …`, and Settings → Extensions shows a platforms badge plus a gray "not supported here" tag.
|
|
211
216
|
|
|
217
|
+
### Activity-bar entries
|
|
218
|
+
|
|
219
|
+
Manifest `activity` gives an extension one button in the admin's leftmost activity bar; clicking it shows `entry` as a full-width page in the main area — no sidebar, like Agents / Settings. A page and a file viewer can live in one extension (`extensions` non-empty), or the extension opens no files (`extensions: []`).
|
|
220
|
+
|
|
221
|
+
- **Placement.** `activityEntries()` (`previews/registry.ts`) = valid extensions with `activity` that `runsHere`, sorted by `activity.order` (default 100) then id. `workspace-layout.tsx` renders them after the built-in top tabs (after Cron) and before the bottom group (Settings, quick toggles); built-in order never moves. It reads the list under `useRegistryVersion()`, so an install / uninstall arrives with the `extension:changed` push — no polling. Nothing installed = no button = no visible change.
|
|
222
|
+
- **Label.** Title: zh `title_zh` → `title` → `name`; en `title` → `name`. Hover-drawer description: zh `description_zh` → `description` → top-level `description` → `''`; en skips the `_zh` variants. The title is also the button's `aria-label`; the drawer reuses `ActivityBarLabel`.
|
|
223
|
+
- **Icon.** `activity.icon` renders as a 20×20 span with `background-color: currentColor` and `mask` / `-webkit-mask: url(/api/extensions/<id>/<version>/<token>/<icon>) center / contain no-repeat` (`ExtensionActivityIcon`), so it follows the active / hover colour and every theme, and the SVG never enters the DOM (the asset route serves `.svg` as `image/svg+xml`, which masks require). An empty box of the same size while the asset token is being fetched (so the generic icon never flashes first); lucide `Puzzle` when there is no `icon` or the token request fails.
|
|
224
|
+
- **Click / lifetime.** Through the same `handleTabClick` as built-ins (exits editor maximize). Tab id `ext:<id>`. The page (`ExtensionActivityPanel`, `features/workspace/extension-activity-panel.tsx`) mounts only while its tab is active: switching away unmounts the iframe, so its timers, polling and audio stop; nothing is kept across visits. Keyed by `id@version`, so an upgrade remounts. Same iframe grant as the file host (`sandbox="allow-scripts allow-same-origin"`, `allow` = `MEDIA_ALLOW` with `media` else `''`, `referrerpolicy="no-referrer"`).
|
|
225
|
+
- **Persistence.** `halo_sidebar_tab` may hold `ext:<id>`. After a reload the layout waits for `loadExtensions()` (3 s cap); if the id is then not in `activityEntries()` — uninstalled, broken, or excluded on this platform — it falls back to Explorer. The same rule fires when the extension disappears while its page is open. Never while the list is still loading (same three-state idea as the hidden Source Control tab).
|
|
226
|
+
- **Protocol.** `extension-activity-logic.ts` (pure, effects like the file host's): `ready` → `init` with `view: 'activity'` — `protocol`, `capabilities`, `theme`, `themeVars`, `platform`, `lang`; no `file` / `bundle` / `export` / `jupyter` — re-sent on every later `ready`; `error` → banner above the iframe; `theme` / `lang` frames on change, after `ready` only. `dirty` / `save` / `fs` / `pick` / `export` are ignored with a `console.warn('[ExtensionActivity] …')`. Granted `capabilities` = `media` / `transcribe` / `stt-local` from the manifest (the ones that work without a file); `save` / `fs-read` are not granted. No `ready` in 10 s → the `editor.extension.unresponsive` message with Retry (remounts the iframe).
|
|
227
|
+
- **Settings → Extensions** shows an "Activity bar" tag for such an extension, and drops the suffix line when `extensions` is empty.
|
|
228
|
+
- **Short viewports.** The top group (built-ins + extension buttons) is a scroll container (`min-h-0 flex-1 overflow-y-auto`, scrollbar hidden) in place of the old `flex-1` spacer, so Settings and the quick toggles never get pushed off-screen. The quick-toggles popover sits in the bottom group, outside the scroller, so it isn't clipped.
|
|
229
|
+
- **Data for the page.** A page served from the admin origin carries the admin cookie, so it can call cookie-authed routes; `/api/show/*` (the Halo City data) accepts that cookie as `full` — see [dev/api.md → Show](../dev/api.md#show-world-snapshot).
|
|
230
|
+
|
|
231
|
+
### Chat-bar entries
|
|
232
|
+
|
|
233
|
+
Manifest `chatbar` gives an extension one button on the chat input toolbar; clicking it opens `entry` in a small panel above the input box, and the extension may put text into the draft — the first user is the hub's voice-input extension (record → server-side local transcription → text at the caret). Like `activity`, it can sit beside a file viewer or stand alone (`extensions: []`); one extension may declare both.
|
|
234
|
+
|
|
235
|
+
- **Placement.** `chatbarEntries()` (`previews/registry.ts`) = valid extensions with `chatbar` that `runsHere`, sorted by `chatbar.order` (default 100) then id. `ExtensionChatbarControl` (`features/chat/extension-chatbar-control.tsx`) renders one 32×32 button each, right after the capture control in `message-input.tsx`, reading the list under `useRegistryVersion()` — installs / uninstalls arrive with `extension:changed`, no polling. Nothing installed = no button.
|
|
236
|
+
- **Label / icon.** Tooltip and `aria-label`: zh `title_zh` → `title` → `name`; en `title` → `name` (`chatbarTitle`). `chatbar.icon` is the same `currentColor` mask as the activity icon (shared `ExtensionMaskIcon`, `previews/extension-icon.tsx`), 16×16; blank while the token loads, lucide `Mic` when there is no `icon` or the token request fails.
|
|
237
|
+
- **Panel.** Portaled to `<body>` as `fixed z-[55]` over the input's wrapper — the chat dock's Panel clips its overflow, so an in-tree panel lost its title bar under the editor. `chatbarPanelBox` (`extension-chatbar-logic.ts`): width `min(420, viewport − 16)`, left = wrapper + 12 clamped 8 px inside the viewport, bottom 4 px above the wrapper; a thin title bar (title + ×, 28 px) over an iframe area of `chatbar.height` px, squeezed to what fits above the wrapper (floor 80 px — then the panel overlaps the input's top, title bar still 8 px inside the viewport). While open it re-measures on a ResizeObserver over the wrapper and every ancestor (a dock-divider drag moves without resizing it), window resize / scroll (capture) / pointermove (the floating chat drags by position), rAF-batched — only `style` changes, the iframe never remounts. A wrapper measuring 0×0 (chat tab on the terminal, dock collapsed — `hidden`, not unmounted) or a chat-tab switch (`activeTabId`; one MessageInput serves every tab) closes it. One panel at a time: another button switches, the same button / × / Esc on the host page closes it (Esc in the textarea closes it before the "Esc interrupts the turn" rule). A click outside does **not** close it — the user may edit the draft while dictating.
|
|
238
|
+
- **Lifetime.** The iframe exists only while the panel is open (`ExtensionPageFrame`, keyed `id@version`); closing unmounts it, so the microphone stops. A user close (its button / × / Esc) of a panel that last reported `busy: true` is graceful instead: `close-request` is posted and the panel stays — button still active, `insert-text` still landing — until `busy: false`, 35 s (`CHATBAR_CLOSE_TIMEOUT_MS`, above the extension's own 30 s), or a second user close. State: `chatbarReducer` (`{ openId, busy, closing }`) held by `MessageInput`. A chat-tab switch, a hidden input, uninstalling (or a platform exclusion) of the open extension, or opening another panel unmounts at once, busy or not. Same grant as the activity page: `sandbox="allow-scripts allow-same-origin"`, `allow` = `MEDIA_ALLOW` only with `media`, `referrerpolicy="no-referrer"`. No `ready` in 10 s → unresponsive + Retry inside the panel.
|
|
239
|
+
- **Protocol.** `extension-chatbar-logic.ts` (`onChatbarFrame`, pure; shares state / init / theme / lang with the activity logic): `ready` → `init` with `view: 'chatbar'` (same fields as `'activity'`), re-sent on every `ready`; `error` → banner; `theme` / `lang` after `ready`; `insert-text` → insert (rules in the protocol table); `busy` → the panel's close behaviour (see Lifetime); `dirty` / `save` / `fs` / `pick` / `export` ignored with `console.warn('[ExtensionChatbar] …')`. Granted `capabilities` = manifest ∩ `media` / `transcribe` / `stt-local`. There is deliberately **no send frame** (v1 product decision: an extension can fill the draft, only the user sends).
|
|
240
|
+
- **Insert.** `MessageInput` replaces the textarea's current `selectionStart`–`selectionEnd` with the text (`insertAtSelection`), through its own `setText` (so the draft, `@` detection and auto-grow behave as for typing), and puts the caret right after it. It does **not** focus the textarea — focus stays in the iframe, whose keyboard controls keep working. Works while a turn is running (it only edits the draft).
|
|
241
|
+
- **Settings → Extensions** shows a "Chat bar" tag for such an extension.
|
|
242
|
+
|
|
212
243
|
### Extension settings
|
|
213
244
|
|
|
214
245
|
An installed extension with manifest `settings` gets one Settings section (`settings-schema.ts` `extensionSections()`, built from the registry's cached snapshot): `source: 'extension'`, namespace `ext-<id>` (prefixed so an extension id can't collide with a skill / provider namespace), title = manifest `name`, every field `globalOnly`, secrets `secret: true` (masked). Values live in the global `~/.halo/secrets/settings.yaml` at `ext-<id>.params.<key>` / `ext-<id>.secrets.<key>` and go through the ordinary `/settings` routes (workspace scope refused by the global-only backstop). Admin: Settings nav group **Extension settings** under Extensions. Values never reach the manifest, the hub or the `init` frame — only server code reads them (`getServerParam` / `getServerSecret` in `config.ts`, read per use, so no restart).
|
|
@@ -229,7 +260,7 @@ The ipynb extension (2.0.0+) runs cells on **real kernels on the Halo machine**.
|
|
|
229
260
|
- **Trust level = the built-in terminal**: kernels run as the Halo service user, unsandboxed; admin cookie only (the HTTP routes sit behind `authMiddleware`, the WS handshake checks the cookie like `/ws`; web-channel / IM / A2A tokens can't reach it); not mounted in AgentCore mode; the child env is `cleanChildEnv()`. No manifest capability gates it — `init.jupyter` is the host's signal (a new capability value would make ipynb 2.0.0 uninstallable on a 1.6.0 registry, same reasoning as `init.export`). The iframe is same-origin with the login cookie, so the extension fetches / opens the WS to `/api/jupyter/*` itself.
|
|
230
261
|
- **Settings** (`general.jupyter.*`, globalOnly, read at each spawn — no restart): `enabled` (default `true`) and `path` (default empty = `jupyter` on PATH, else the full path of the executable).
|
|
231
262
|
- **Status** (`GET /api/jupyter/status`) never starts anything: disabled → `{enabled:false, available:false}`; executable missing → `available:false` + `error` + `hint: 'pip install jupyter_server ipykernel'`; else `<jupyter> server --version` is run once per resolved path and cached (a failing probe isn't cached, so a later `pip install` is picked up).
|
|
232
|
-
- **Server**: one per Halo process, started by the first `kernelspecs` / kernel start (concurrent callers share one start), restarted lazily after it exits. Args: `server --no-browser --ServerApp.ip=127.0.0.1 --ServerApp.port=<free port> --ServerApp.port_retries=0 --ServerApp.root_dir=<filesystem root> --ServerApp.terminals_enabled=False --ServerApp.shutdown_no_activity_timeout=600 --MappingKernelManager.cull_idle_timeout=3600 --MappingKernelManager.cull_interval=300 --MappingKernelManager.cull_connected=True` (+ `allow_root` when Halo itself runs as root). The random upstream token goes in `JUPYTER_TOKEN` (env, not argv — `ps` would show it) and every upstream call sends `Authorization: token …`. Ready = `GET /api/status` answers, within 30 s, else 503 with the stderr tail. When the process exits, the kernel Map is cleared and every client socket closes 1011.
|
|
263
|
+
- **Server**: one per Halo process, started by the first `kernelspecs` / kernel start (concurrent callers share one start), restarted lazily after it exits. Args: `server --no-browser --ServerApp.ip=127.0.0.1 --ServerApp.port=<free port> --ServerApp.port_retries=0 --ServerApp.root_dir=<filesystem root> --ServerApp.terminals_enabled=False --ServerApp.shutdown_no_activity_timeout=600 --MappingKernelManager.cull_idle_timeout=3600 --MappingKernelManager.cull_interval=300 --MappingKernelManager.cull_connected=True` (+ `allow_root` when Halo itself runs as root). Both spawns (the `--version` probe and the server) pass `cwd: os.homedir()` — inheriting the server's cwd left a live jupyter holding the desktop install dir open, blocking the installer; `root_dir` is unaffected. The random upstream token goes in `JUPYTER_TOKEN` (env, not argv — `ps` would show it) and every upstream call sends `Authorization: token …`. Ready = `GET /api/status` answers, within 30 s, else 503 with the stderr tail. When the process exits, the kernel Map is cleared and every client socket closes 1011.
|
|
233
264
|
- **Kernel start** (`POST /api/jupyter/kernels {projectId, path, name}`): `resolveProjectPath` + `validatePath` (symlinks followed; outside → 400), then the **real path of the notebook's directory** is sent as the upstream kernel `path`. jupyter_server 2.x `cwd_for_path` splits that on `/`, joins the parts onto `root_dir` and normalizes — with `root_dir` = `/` that is the absolute path itself; on Windows the native `C:\…` path is a single part and joining an absolute drive path onto the root replaces it, so any drive works (forward slashes would give a drive-relative `D:x`). jupyter silently walks up to `root_dir` when the directory doesn't exist, so Halo checks it first (400).
|
|
234
265
|
- **Kernel ops / channels** only for ids in Halo's in-process Map (unknown → 404, also when jupyter itself started it). Upstream 404 (culled) drops the id. A kernel jupyter shuts down on its own (idle cull) does **not** close its channels sockets — the only sign is an iopub `shutdown_reply` with `content.restart: false` (a restart sends the same frame with `restart: true`, and the socket keeps working across it); the proxy watches for that frame, drops the id and closes the client sockets 1000. A kernel that dies in a cell (`exit()`, a crash) is auto-restarted by jupyter: `status: restarting` → `starting`, same id, socket stays. Channels: WS `/api/jupyter/channels?kernel=<id>[&session_id=<s>]` → `ws://127.0.0.1:<port>/api/kernels/<id>/channels[?session_id=<s>]` (`session_id` `^[\w-]{1,128}$`, else 400; forwarded since 1.6.3 so jupyter's per-session buffering works, see Lifecycle), frames piped both ways as-is (text and binary), frames sent before the upstream handshake are queued. No subprotocol is offered upstream or accepted from the client, so jupyter speaks the **legacy JSON protocol** (every message a text frame carrying `channel`, `header`, `parent_header`, `metadata`, `content`, `buffers` go as binary frames). The proxy pings the client every 20 s (keepalive through reverse proxies; 2 missed pongs = dead).
|
|
235
266
|
- **Lifecycle**: a kernel with no client socket for **60 s** is DELETEd upstream (tab closed; the grace rides out a reload / reconnect, which cancels it; a kernel nobody ever connects to gets the same grace). **Reload**: since ipynb 2.0.1 the extension sends no `DELETE` on `pagehide` (it can't tell a reload from a close; 2.0.0's unload `DELETE` killed the kernel on every reload) — it closes the socket and stores `{id, name, session, run: {msgId, cellId, index, source, count, outputs}}` in `sessionStorage` (`halo-ipynb-kernel:<projectId>:<path>`, per browser tab, claimed once by the next page of that notebook). The next page reconnects with the same id and `session_id`; a handshake that fails (id gone after the grace, server restarted) falls back to a fresh kernel on the next run. jupyter_server (verified 2.21.1) buffers a disconnected session's messages and replays them to a socket that reconnects with the same `session_id` — output printed during the reload and the cell's `execute_reply` — and the proxy only reaches that buffer by forwarding `session_id` (1.6.2 doesn't: output in the gap is lost, live output still streams, and the shell `kernel_info_request` the extension sends on reattach, queued behind the running cell, still marks it done). jupyter sends no `status` to a new socket while the kernel is busy (its connect nudge skips busy kernels), so the extension sets the badge from what it stored (no route reads a kernel's state). The extension still `DELETE`s the old kernel when the user picks another kernelspec. jupyter culls kernels idle for 1 h even while connected, and exits itself after 10 min with no kernels. Halo's graceful shutdown POSTs upstream `/api/shutdown` (stops every kernel, then exits; works on Windows too) and SIGKILLs after 5 s; a `process.on('exit')` hook kills the child on other exits. If Halo dies hard, the orphaned server goes on its own: its kernels are culled after 1 h idle, then it exits 10 min later.
|
|
@@ -259,7 +290,7 @@ Built-in skill `templates/skills/extension/` (in `BUILTIN_SKILL_IDS`; `TEMPLATE_
|
|
|
259
290
|
- An API error or "no release" on an identified platform stops with a hint (set the token, or download the zip from the releases page and `install <path>`). **Never a git fallback**: a git checkout of a build-step extension lacks its build output and would install broken.
|
|
260
291
|
- Anything else (ssh, `ssh://`, `file://`, a local path, an https host that answered neither probe) → git mode: the highest `<id>-v<x.y.z>` tag by semver (prereleases skipped), shallow clone, `extensions/<id>/` minus the build inputs `pack.mjs` leaves out of a zip (`node_modules/`, `src/`, `package*.json`). Refused ("needs a build step") when the package has `fetch-deps.sh` / `build.sh` — a presence check, and all four current halo-hub extensions carry one, so a plain-git mirror of halo-hub installs none of them by id today.
|
|
261
292
|
|
|
262
|
-
The hub repo keeps one directory per extension (`glb` / `ipynb` commit their vendored deps, `fetch-deps.sh` only refreshes them; `drawio` / `excalidraw` gitignore their build output and commit only the inputs), `scripts/pack.mjs extensions/<id>` → `dist/<id>-<version>.zip` with contents at the zip root (maintenance scripts and build inputs excluded), and one release per tag `<id>-v<version>` carrying exactly one zip (a second one would be picked by accident). Skills and workspaces in the same repo are tagged `skill-<id>-v<ver>` / `ws-<id>-v<ver>`; nothing installs them yet. Catalog today (only `glb-v1.0.0` is released so far):
|
|
293
|
+
The hub repo keeps one directory per extension (`glb` / `ipynb` commit their vendored deps, `fetch-deps.sh` only refreshes them; `drawio` / `excalidraw` gitignore their build output and commit only the inputs), `scripts/pack.mjs extensions/<id>` → `dist/<id>-<version>.zip` with contents at the zip root (maintenance scripts and build inputs excluded; `pack.mjs` hand-mirrors the `registry.ts` manifest rules, and `packages/server/test/extension-manifest-rules-drift.test.ts` pins a hash of those rules and fails with a pointer to the hub copy when they change), and one release per tag `<id>-v<version>` carrying exactly one zip (a second one would be picked by accident). Skills and workspaces in the same repo are tagged `skill-<id>-v<ver>` / `ws-<id>-v<ver>`; nothing installs them yet. Catalog today (only `glb-v1.0.0` is released so far):
|
|
263
294
|
|
|
264
295
|
- `glb` 1.1.0 — `.glb .gltf .obj .stl`, `default`, read-only (`<model-viewer>` 4.3.1 offline bundle with Draco/Basis decoders; `.obj` / `.stl` converted in memory to GLB via three.js; `.gltf` must be self-contained — external `.bin` / textures → `error` frame; no `.mtl`).
|
|
265
296
|
- `ipynb` 2.0.0 — `.ipynb`, `default`, `save`: notebook editor (markdown, KaTeX math, highlighted code, outputs incl. images / HTML tables; no widgets / JS outputs) that runs cells through the Jupyter proxy above when the host sends `init.jupyter` (Halo ≥ 1.6.1); older hosts edit and save without run controls.
|
|
@@ -272,9 +303,9 @@ The hub repo keeps one directory per extension (`glb` / `ipynb` commit their ven
|
|
|
272
303
|
|
|
273
304
|
## Extension capability boundary
|
|
274
305
|
|
|
275
|
-
An extension **can**: receive one file's bytes and metadata; render in its own iframe; load static assets from its own directory via the path token; report `error`; receive `theme` and `lang`; with `save`, report `dirty` and hand back bytes for the host to write to **that same file**, and (on `init.export` hosts) hand back export bytes the host writes as a **new sibling file** after an overwrite confirm; with `bundle`, read / write / append / list / stat **inside its bundle directory** via `fs`; with `fs-read`, **read** (read / stat / list) any file in the workspace or on the machine outside the sandbox hidden lists, and ask the user to pick one; with `media`, use the microphone and screen capture; with `transcribe`, stream audio to the server's transcription proxy (results only — credentials stay server-side).
|
|
306
|
+
An extension **can**: receive one file's bytes and metadata; render in its own iframe; load static assets from its own directory via the path token; report `error`; receive `theme` and `lang`; with `save`, report `dirty` and hand back bytes for the host to write to **that same file**, and (on `init.export` hosts) hand back export bytes the host writes as a **new sibling file** after an overwrite confirm; with `bundle`, read / write / append / list / stat **inside its bundle directory** via `fs`; with `fs-read`, **read** (read / stat / list) any file in the workspace or on the machine outside the sandbox hidden lists, and ask the user to pick one; with `media`, use the microphone and screen capture; with `transcribe`, stream audio to the server's transcription proxy (results only — credentials stay server-side); with `stt-local`, stream audio to the server's on-device speech-to-text WS (`/api/stt/stream`); from a chat-bar panel, `insert-text` into the chat draft at the caret (never send) and, by reporting `busy`, finish its work before a user close unmounts it (35 s at most).
|
|
276
307
|
|
|
277
|
-
An extension **cannot** (sandbox + protocol): open popups, downloads, forms or navigate the top window; use camera / autoplay, or mic / screen capture without `media` (`allow=""`); add admin UI (toolbar, commands
|
|
308
|
+
An extension **cannot** (sandbox + protocol): open popups, downloads, forms or navigate the top window; use camera / autoplay, or mic / screen capture without `media` (`allow=""`); add admin UI beyond one activity-bar entry and one chat-bar button declared in its manifest (`activity` / `chatbar` — see [Activity-bar entries](#activity-bar-entries), [Chat-bar entries](#chat-bar-entries)): no other toolbar buttons, commands or sidebars; send a chat message; talk to other iframes. The **protocol** gives it no way to write any workspace file except the open one (or inside its bundle directory), and no way to read others without `fs-read`. Since the host grants `allow-same-origin` (see Host), the sandbox no longer isolates it from the admin's origin: a hostile extension *could* script `parent.document` or call the cookie-authed `/api/*` (measured: `fetch('/api/extensions')` from inside → 200). That is the accepted trust model — installing an extension is like installing a skill — not a gap to patch per route. **Outbound network is not blocked** — no CSP is injected, so `fetch('https://…')` works where the remote allows CORS; trust model is "code the user chose to install" (same as a skill), and hub policy is offline-capable extensions. A hard block would be one `Content-Security-Policy` header on the asset route, no protocol change.
|
|
278
309
|
|
|
279
310
|
Adding a capability: (1) extend `ExtensionCapability` + the `CAPABILITIES` whitelist in `registry.ts`; (2) add the frame type in `extension-frames.ts`; (3) gate it in `extension-host-logic.ts` (undeclared → ignore + warn, like `save`); (4) if it needs the server (e.g. reading a sibling file), the **host** calls `/api/files/*` — an extension never touches the API directly. Install, scan, watcher and registry stay untouched.
|
|
280
311
|
|
|
@@ -285,7 +316,7 @@ Adding a capability: (1) extend `ExtensionCapability` + the `CAPABILITIES` white
|
|
|
285
316
|
3. **No banner for an external change while dirty.** The host keeps the local edits silently and relies on the 409 (with the Overwrite confirm) on the next save.
|
|
286
317
|
4. **Conflict UI is chained `confirmAction`s**, not a dedicated overwrite / discard / cancel dialog (see Dirty, save, conflict).
|
|
287
318
|
5. **Uninstalling under a dirty tab** keeps the iframe alive with a banner — no confirm. A non-dirty tab shows the fallback page.
|
|
288
|
-
6. **Tests are not co-located**: `packages/admin/test/{extension-host-logic,extension-token,preview-registry}.test.ts`, `packages/server/test/{extensions-registry,extensions-install,extensions-routes,files-raw}.test.ts`.
|
|
319
|
+
6. **Tests are not co-located**: `packages/admin/test/{extension-host-logic,extension-token,preview-registry,extension-activity,extension-chatbar}.test.ts`, `packages/server/test/{extensions-registry,extensions-install,extensions-routes,files-raw,extension-manifest-rules-drift,show-cookie-auth}.test.ts`.
|
|
289
320
|
7. **`allow-same-origin` is granted** (design and first cut withheld it). Required for deployments behind a cookie-auth proxy; see Host and the capability boundary.
|
|
290
321
|
8. Smaller code-over-design points: the id regex is `ID_RE` (not `isSafeIdSegment`); staging dir is `.tmp-unpack-<rand>` (the id isn't known before unpacking); `snapshotKey` includes `installedAt`, so reinstalling the same version still broadcasts.
|
|
291
322
|
|