From 17e3883bff5d4eec2c14c35104cd8678003f1f87 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Tue, 1 Jul 2025 18:23:02 -0400 Subject: [PATCH] fix(translation): correct syntax and logic in translation modal - Reverted the broken openTranslateModal function to a working state. - Re-implemented the logic to correctly inject the tag for relative paths and to ensure the translation notice is displayed correctly. - Ran biome to format the code and ensure there are no syntax errors. --- css/chunkfive/stylesheet.css | 16 +- css/pico.classless.min.css | 2611 +++++++++++++++++++++++++++++++- src/chat.js | 236 +-- src/config.js | 21 +- src/content.js | 66 +- src/file-upload.js | 194 +-- src/models.js | 389 ++--- src/page-reader.js | 122 +- src/storage.js | 71 +- src/translation.js | 462 +++--- src/tts.js | 288 ++-- src/ui.js | 2726 ++++++++++++++++++---------------- uncloseai.js | 97 +- 13 files changed, 5029 insertions(+), 2270 deletions(-) diff --git a/css/chunkfive/stylesheet.css b/css/chunkfive/stylesheet.css index 8a333f9..4421f01 100644 --- a/css/chunkfive/stylesheet.css +++ b/css/chunkfive/stylesheet.css @@ -1,12 +1,10 @@ /*! Generated by Font Squirrel (https://www.fontsquirrel.com) on October 4, 2021 */ - - @font-face { - font-family: 'chunkfiveregular'; - src: url('chunkfive-regular-webfont.woff2') format('woff2'), - url('chunkfive-regular-webfont.woff') format('woff'); - font-weight: normal; - font-style: normal; - -} \ No newline at end of file + font-family: "chunkfiveregular"; + src: + url("chunkfive-regular-webfont.woff2") format("woff2"), + url("chunkfive-regular-webfont.woff") format("woff"); + font-weight: normal; + font-style: normal; +} diff --git a/css/pico.classless.min.css b/css/pico.classless.min.css index 7d8d77b..24bc005 100644 --- a/css/pico.classless.min.css +++ b/css/pico.classless.min.css @@ -1,4 +1,2611 @@ -@charset "UTF-8";/*! +@charset "UTF-8"; /*! * Pico CSS ✨ v2.0.6 (https://picocss.com) * Copyright 2019-2024 - Licensed under MIT - */:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:0.1rem;--pico-border-radius:0.25rem;--pico-border-width:0.0625rem;--pico-outline-width:0.125rem;--pico-transition:0.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-form-element-spacing-vertical:0.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 0.0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(0.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:0.5rem;--pico-nav-link-spacing-vertical:0.5rem;--pico-nav-link-spacing-horizontal:0.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--pico-icon-close:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--pico-icon-loading:url("data:image/svg+xml,%3Csvg fill='none' height='24' width='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' %3E%3Cstyle%3E g %7B animation: rotate 2s linear infinite; transform-origin: center center; %7D circle %7B stroke-dasharray: 75,100; stroke-dashoffset: -5; animation: dash 1.5s ease-in-out infinite; stroke-linecap: round; %7D @keyframes rotate %7B 0%25 %7B transform: rotate(0deg); %7D 100%25 %7B transform: rotate(360deg); %7D %7D @keyframes dash %7B 0%25 %7B stroke-dasharray: 1,100; stroke-dashoffset: 0; %7D 50%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -17.5; %7D 100%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -62; %7D %7D %3C/style%3E%3Cg%3E%3Ccircle cx='12' cy='12' r='10' fill='none' stroke='rgb(136, 145, 164)' stroke-width='4' /%3E%3C/g%3E%3C/svg%3E")}@media (min-width:576px){:root{--pico-font-size:106.25%}}@media (min-width:768px){:root{--pico-font-size:112.5%}}@media (min-width:1024px){:root{--pico-font-size:118.75%}}@media (min-width:1280px){:root{--pico-font-size:125%}}@media (min-width:1536px){:root{--pico-font-size:131.25%}}a{--pico-text-decoration:underline}small{--pico-font-size:0.875em}h1,h2,h3,h4,h5,h6{--pico-font-weight:700}h1{--pico-font-size:2rem;--pico-line-height:1.125;--pico-typography-spacing-top:3rem}h2{--pico-font-size:1.75rem;--pico-line-height:1.15;--pico-typography-spacing-top:2.625rem}h3{--pico-font-size:1.5rem;--pico-line-height:1.175;--pico-typography-spacing-top:2.25rem}h4{--pico-font-size:1.25rem;--pico-line-height:1.2;--pico-typography-spacing-top:1.874rem}h5{--pico-font-size:1.125rem;--pico-line-height:1.225;--pico-typography-spacing-top:1.6875rem}h6{--pico-font-size:1rem;--pico-line-height:1.25;--pico-typography-spacing-top:1.5rem}tfoot td,tfoot th,thead td,thead th{--pico-font-weight:600;--pico-border-width:0.1875rem}code,kbd,pre,samp{--pico-font-family:var(--pico-font-family-monospace)}kbd{--pico-font-weight:bolder}:where(select,textarea),input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pico-outline-width:0.0625rem}[type=search]{--pico-border-radius:5rem}[type=checkbox],[type=radio]{--pico-border-width:0.125rem}[type=checkbox][role=switch]{--pico-border-width:0.1875rem}[role=search]{--pico-border-radius:5rem}[role=group] [role=button],[role=group] [type=button],[role=group] [type=submit],[role=group] button,[role=search] [role=button],[role=search] [type=button],[role=search] [type=submit],[role=search] button{--pico-form-element-spacing-horizontal:2rem}details summary[role=button]::after{filter:brightness(0) invert(1)}[aria-busy=true]:not(input,select,textarea):is(button,[type=submit],[type=button],[type=reset],[role=button])::before{filter:brightness(0) invert(1)}:root:not([data-theme=dark]),[data-theme=light]{--pico-background-color:#fff;--pico-color:#373c44;--pico-text-selection-color:rgba(2, 154, 232, 0.25);--pico-muted-color:#646b79;--pico-muted-border-color:#e7eaf0;--pico-primary:#0172ad;--pico-primary-background:#0172ad;--pico-primary-border:var(--pico-primary-background);--pico-primary-underline:rgba(1, 114, 173, 0.5);--pico-primary-hover:#015887;--pico-primary-hover-background:#02659a;--pico-primary-hover-border:var(--pico-primary-hover-background);--pico-primary-hover-underline:var(--pico-primary-hover);--pico-primary-focus:rgba(2, 154, 232, 0.5);--pico-primary-inverse:#fff;--pico-secondary:#5d6b89;--pico-secondary-background:#525f7a;--pico-secondary-border:var(--pico-secondary-background);--pico-secondary-underline:rgba(93, 107, 137, 0.5);--pico-secondary-hover:#48536b;--pico-secondary-hover-background:#48536b;--pico-secondary-hover-border:var(--pico-secondary-hover-background);--pico-secondary-hover-underline:var(--pico-secondary-hover);--pico-secondary-focus:rgba(93, 107, 137, 0.25);--pico-secondary-inverse:#fff;--pico-contrast:#181c25;--pico-contrast-background:#181c25;--pico-contrast-border:var(--pico-contrast-background);--pico-contrast-underline:rgba(24, 28, 37, 0.5);--pico-contrast-hover:#000;--pico-contrast-hover-background:#000;--pico-contrast-hover-border:var(--pico-contrast-hover-background);--pico-contrast-hover-underline:var(--pico-secondary-hover);--pico-contrast-focus:rgba(93, 107, 137, 0.25);--pico-contrast-inverse:#fff;--pico-box-shadow:0.0145rem 0.029rem 0.174rem rgba(129, 145, 181, 0.01698),0.0335rem 0.067rem 0.402rem rgba(129, 145, 181, 0.024),0.0625rem 0.125rem 0.75rem rgba(129, 145, 181, 0.03),0.1125rem 0.225rem 1.35rem rgba(129, 145, 181, 0.036),0.2085rem 0.417rem 2.502rem rgba(129, 145, 181, 0.04302),0.5rem 1rem 6rem rgba(129, 145, 181, 0.06),0 0 0 0.0625rem rgba(129, 145, 181, 0.015);--pico-h1-color:#2d3138;--pico-h2-color:#373c44;--pico-h3-color:#424751;--pico-h4-color:#4d535e;--pico-h5-color:#5c6370;--pico-h6-color:#646b79;--pico-mark-background-color:#fde7c0;--pico-mark-color:#0f1114;--pico-ins-color:#1d6a54;--pico-del-color:#883935;--pico-blockquote-border-color:var(--pico-muted-border-color);--pico-blockquote-footer-color:var(--pico-muted-color);--pico-button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-table-border-color:var(--pico-muted-border-color);--pico-table-row-stripped-background-color:rgba(111, 120, 135, 0.0375);--pico-code-background-color:#f3f5f7;--pico-code-color:#646b79;--pico-code-kbd-background-color:var(--pico-color);--pico-code-kbd-color:var(--pico-background-color);--pico-form-element-background-color:#fbfcfc;--pico-form-element-selected-background-color:#dfe3eb;--pico-form-element-border-color:#cfd5e2;--pico-form-element-color:#23262c;--pico-form-element-placeholder-color:var(--pico-muted-color);--pico-form-element-active-background-color:#fff;--pico-form-element-active-border-color:var(--pico-primary-border);--pico-form-element-focus-color:var(--pico-primary-border);--pico-form-element-disabled-opacity:0.5;--pico-form-element-invalid-border-color:#b86a6b;--pico-form-element-invalid-active-border-color:#c84f48;--pico-form-element-invalid-focus-color:var(--pico-form-element-invalid-active-border-color);--pico-form-element-valid-border-color:#4c9b8a;--pico-form-element-valid-active-border-color:#279977;--pico-form-element-valid-focus-color:var(--pico-form-element-valid-active-border-color);--pico-switch-background-color:#bfc7d9;--pico-switch-checked-background-color:var(--pico-primary-background);--pico-switch-color:#fff;--pico-switch-thumb-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-range-border-color:#dfe3eb;--pico-range-active-border-color:#bfc7d9;--pico-range-thumb-border-color:var(--pico-background-color);--pico-range-thumb-color:var(--pico-secondary-background);--pico-range-thumb-active-color:var(--pico-primary-background);--pico-accordion-border-color:var(--pico-muted-border-color);--pico-accordion-active-summary-color:var(--pico-primary-hover);--pico-accordion-close-summary-color:var(--pico-color);--pico-accordion-open-summary-color:var(--pico-muted-color);--pico-card-background-color:var(--pico-background-color);--pico-card-border-color:var(--pico-muted-border-color);--pico-card-box-shadow:var(--pico-box-shadow);--pico-card-sectioning-background-color:#fbfcfc;--pico-loading-spinner-opacity:0.5;--pico-modal-overlay-background-color:rgba(232, 234, 237, 0.75);--pico-progress-background-color:#dfe3eb;--pico-progress-color:var(--pico-primary-background);--pico-tooltip-background-color:var(--pico-contrast-background);--pico-tooltip-color:var(--pico-contrast-inverse);--pico-icon-valid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 155, 138)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-invalid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(200, 79, 72)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");color-scheme:light}:root:not([data-theme=dark]) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),[data-theme=light] input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pico-form-element-focus-color:var(--pico-primary-focus)}@media only screen and (prefers-color-scheme:dark){:root:not([data-theme]){--pico-background-color:#13171f;--pico-color:#c2c7d0;--pico-text-selection-color:rgba(1, 170, 255, 0.1875);--pico-muted-color:#7b8495;--pico-muted-border-color:#202632;--pico-primary:#01aaff;--pico-primary-background:#0172ad;--pico-primary-border:var(--pico-primary-background);--pico-primary-underline:rgba(1, 170, 255, 0.5);--pico-primary-hover:#79c0ff;--pico-primary-hover-background:#017fc0;--pico-primary-hover-border:var(--pico-primary-hover-background);--pico-primary-hover-underline:var(--pico-primary-hover);--pico-primary-focus:rgba(1, 170, 255, 0.375);--pico-primary-inverse:#fff;--pico-secondary:#969eaf;--pico-secondary-background:#525f7a;--pico-secondary-border:var(--pico-secondary-background);--pico-secondary-underline:rgba(150, 158, 175, 0.5);--pico-secondary-hover:#b3b9c5;--pico-secondary-hover-background:#5d6b89;--pico-secondary-hover-border:var(--pico-secondary-hover-background);--pico-secondary-hover-underline:var(--pico-secondary-hover);--pico-secondary-focus:rgba(144, 158, 190, 0.25);--pico-secondary-inverse:#fff;--pico-contrast:#dfe3eb;--pico-contrast-background:#eff1f4;--pico-contrast-border:var(--pico-contrast-background);--pico-contrast-underline:rgba(223, 227, 235, 0.5);--pico-contrast-hover:#fff;--pico-contrast-hover-background:#fff;--pico-contrast-hover-border:var(--pico-contrast-hover-background);--pico-contrast-hover-underline:var(--pico-contrast-hover);--pico-contrast-focus:rgba(207, 213, 226, 0.25);--pico-contrast-inverse:#000;--pico-box-shadow:0.0145rem 0.029rem 0.174rem rgba(7, 9, 12, 0.01698),0.0335rem 0.067rem 0.402rem rgba(7, 9, 12, 0.024),0.0625rem 0.125rem 0.75rem rgba(7, 9, 12, 0.03),0.1125rem 0.225rem 1.35rem rgba(7, 9, 12, 0.036),0.2085rem 0.417rem 2.502rem rgba(7, 9, 12, 0.04302),0.5rem 1rem 6rem rgba(7, 9, 12, 0.06),0 0 0 0.0625rem rgba(7, 9, 12, 0.015);--pico-h1-color:#f0f1f3;--pico-h2-color:#e0e3e7;--pico-h3-color:#c2c7d0;--pico-h4-color:#b3b9c5;--pico-h5-color:#a4acba;--pico-h6-color:#8891a4;--pico-mark-background-color:#014063;--pico-mark-color:#fff;--pico-ins-color:#62af9a;--pico-del-color:#ce7e7b;--pico-blockquote-border-color:var(--pico-muted-border-color);--pico-blockquote-footer-color:var(--pico-muted-color);--pico-button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-table-border-color:var(--pico-muted-border-color);--pico-table-row-stripped-background-color:rgba(111, 120, 135, 0.0375);--pico-code-background-color:#1a1f28;--pico-code-color:#8891a4;--pico-code-kbd-background-color:var(--pico-color);--pico-code-kbd-color:var(--pico-background-color);--pico-form-element-background-color:#1c212c;--pico-form-element-selected-background-color:#2a3140;--pico-form-element-border-color:#2a3140;--pico-form-element-color:#e0e3e7;--pico-form-element-placeholder-color:#8891a4;--pico-form-element-active-background-color:#1a1f28;--pico-form-element-active-border-color:var(--pico-primary-border);--pico-form-element-focus-color:var(--pico-primary-border);--pico-form-element-disabled-opacity:0.5;--pico-form-element-invalid-border-color:#964a50;--pico-form-element-invalid-active-border-color:#b7403b;--pico-form-element-invalid-focus-color:var(--pico-form-element-invalid-active-border-color);--pico-form-element-valid-border-color:#2a7b6f;--pico-form-element-valid-active-border-color:#16896a;--pico-form-element-valid-focus-color:var(--pico-form-element-valid-active-border-color);--pico-switch-background-color:#333c4e;--pico-switch-checked-background-color:var(--pico-primary-background);--pico-switch-color:#fff;--pico-switch-thumb-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-range-border-color:#202632;--pico-range-active-border-color:#2a3140;--pico-range-thumb-border-color:var(--pico-background-color);--pico-range-thumb-color:var(--pico-secondary-background);--pico-range-thumb-active-color:var(--pico-primary-background);--pico-accordion-border-color:var(--pico-muted-border-color);--pico-accordion-active-summary-color:var(--pico-primary-hover);--pico-accordion-close-summary-color:var(--pico-color);--pico-accordion-open-summary-color:var(--pico-muted-color);--pico-card-background-color:#181c25;--pico-card-border-color:var(--pico-card-background-color);--pico-card-box-shadow:var(--pico-box-shadow);--pico-card-sectioning-background-color:#1a1f28;--pico-loading-spinner-opacity:0.5;--pico-modal-overlay-background-color:rgba(8, 9, 10, 0.75);--pico-progress-background-color:#202632;--pico-progress-color:var(--pico-primary-background);--pico-tooltip-background-color:var(--pico-contrast-background);--pico-tooltip-color:var(--pico-contrast-inverse);--pico-icon-valid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-invalid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(150, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");color-scheme:dark}:root:not([data-theme]) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pico-form-element-focus-color:var(--pico-primary-focus)}}[data-theme=dark]{--pico-background-color:#13171f;--pico-color:#c2c7d0;--pico-text-selection-color:rgba(1, 170, 255, 0.1875);--pico-muted-color:#7b8495;--pico-muted-border-color:#202632;--pico-primary:#01aaff;--pico-primary-background:#0172ad;--pico-primary-border:var(--pico-primary-background);--pico-primary-underline:rgba(1, 170, 255, 0.5);--pico-primary-hover:#79c0ff;--pico-primary-hover-background:#017fc0;--pico-primary-hover-border:var(--pico-primary-hover-background);--pico-primary-hover-underline:var(--pico-primary-hover);--pico-primary-focus:rgba(1, 170, 255, 0.375);--pico-primary-inverse:#fff;--pico-secondary:#969eaf;--pico-secondary-background:#525f7a;--pico-secondary-border:var(--pico-secondary-background);--pico-secondary-underline:rgba(150, 158, 175, 0.5);--pico-secondary-hover:#b3b9c5;--pico-secondary-hover-background:#5d6b89;--pico-secondary-hover-border:var(--pico-secondary-hover-background);--pico-secondary-hover-underline:var(--pico-secondary-hover);--pico-secondary-focus:rgba(144, 158, 190, 0.25);--pico-secondary-inverse:#fff;--pico-contrast:#dfe3eb;--pico-contrast-background:#eff1f4;--pico-contrast-border:var(--pico-contrast-background);--pico-contrast-underline:rgba(223, 227, 235, 0.5);--pico-contrast-hover:#fff;--pico-contrast-hover-background:#fff;--pico-contrast-hover-border:var(--pico-contrast-hover-background);--pico-contrast-hover-underline:var(--pico-contrast-hover);--pico-contrast-focus:rgba(207, 213, 226, 0.25);--pico-contrast-inverse:#000;--pico-box-shadow:0.0145rem 0.029rem 0.174rem rgba(7, 9, 12, 0.01698),0.0335rem 0.067rem 0.402rem rgba(7, 9, 12, 0.024),0.0625rem 0.125rem 0.75rem rgba(7, 9, 12, 0.03),0.1125rem 0.225rem 1.35rem rgba(7, 9, 12, 0.036),0.2085rem 0.417rem 2.502rem rgba(7, 9, 12, 0.04302),0.5rem 1rem 6rem rgba(7, 9, 12, 0.06),0 0 0 0.0625rem rgba(7, 9, 12, 0.015);--pico-h1-color:#f0f1f3;--pico-h2-color:#e0e3e7;--pico-h3-color:#c2c7d0;--pico-h4-color:#b3b9c5;--pico-h5-color:#a4acba;--pico-h6-color:#8891a4;--pico-mark-background-color:#014063;--pico-mark-color:#fff;--pico-ins-color:#62af9a;--pico-del-color:#ce7e7b;--pico-blockquote-border-color:var(--pico-muted-border-color);--pico-blockquote-footer-color:var(--pico-muted-color);--pico-button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-table-border-color:var(--pico-muted-border-color);--pico-table-row-stripped-background-color:rgba(111, 120, 135, 0.0375);--pico-code-background-color:#1a1f28;--pico-code-color:#8891a4;--pico-code-kbd-background-color:var(--pico-color);--pico-code-kbd-color:var(--pico-background-color);--pico-form-element-background-color:#1c212c;--pico-form-element-selected-background-color:#2a3140;--pico-form-element-border-color:#2a3140;--pico-form-element-color:#e0e3e7;--pico-form-element-placeholder-color:#8891a4;--pico-form-element-active-background-color:#1a1f28;--pico-form-element-active-border-color:var(--pico-primary-border);--pico-form-element-focus-color:var(--pico-primary-border);--pico-form-element-disabled-opacity:0.5;--pico-form-element-invalid-border-color:#964a50;--pico-form-element-invalid-active-border-color:#b7403b;--pico-form-element-invalid-focus-color:var(--pico-form-element-invalid-active-border-color);--pico-form-element-valid-border-color:#2a7b6f;--pico-form-element-valid-active-border-color:#16896a;--pico-form-element-valid-focus-color:var(--pico-form-element-valid-active-border-color);--pico-switch-background-color:#333c4e;--pico-switch-checked-background-color:var(--pico-primary-background);--pico-switch-color:#fff;--pico-switch-thumb-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-range-border-color:#202632;--pico-range-active-border-color:#2a3140;--pico-range-thumb-border-color:var(--pico-background-color);--pico-range-thumb-color:var(--pico-secondary-background);--pico-range-thumb-active-color:var(--pico-primary-background);--pico-accordion-border-color:var(--pico-muted-border-color);--pico-accordion-active-summary-color:var(--pico-primary-hover);--pico-accordion-close-summary-color:var(--pico-color);--pico-accordion-open-summary-color:var(--pico-muted-color);--pico-card-background-color:#181c25;--pico-card-border-color:var(--pico-card-background-color);--pico-card-box-shadow:var(--pico-box-shadow);--pico-card-sectioning-background-color:#1a1f28;--pico-loading-spinner-opacity:0.5;--pico-modal-overlay-background-color:rgba(8, 9, 10, 0.75);--pico-progress-background-color:#202632;--pico-progress-color:var(--pico-primary-background);--pico-tooltip-background-color:var(--pico-contrast-background);--pico-tooltip-color:var(--pico-contrast-inverse);--pico-icon-valid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-invalid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(150, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");color-scheme:dark}[data-theme=dark] input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pico-form-element-focus-color:var(--pico-primary-focus)}[type=checkbox],[type=radio],[type=range],progress{accent-color:var(--pico-primary)}*,::after,::before{box-sizing:border-box;background-repeat:no-repeat}::after,::before{text-decoration:inherit;vertical-align:inherit}:where(:root){-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--pico-background-color);color:var(--pico-color);font-weight:var(--pico-font-weight);font-size:var(--pico-font-size);line-height:var(--pico-line-height);font-family:var(--pico-font-family);text-underline-offset:var(--pico-text-underline-offset);text-rendering:optimizeLegibility;overflow-wrap:break-word;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{width:100%;margin:0}main{display:block}body>footer,body>header,body>main{width:100%;margin-right:auto;margin-left:auto;padding:var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal)}@media (min-width:576px){body>footer,body>header,body>main{max-width:510px;padding-right:0;padding-left:0}}@media (min-width:768px){body>footer,body>header,body>main{max-width:700px}}@media (min-width:1024px){body>footer,body>header,body>main{max-width:950px}}@media (min-width:1280px){body>footer,body>header,body>main{max-width:1200px}}@media (min-width:1536px){body>footer,body>header,body>main{max-width:1450px}}section{margin-bottom:var(--pico-block-spacing-vertical)}b,strong{font-weight:bolder}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}address,blockquote,dl,ol,p,pre,table,ul{margin-top:0;margin-bottom:var(--pico-typography-spacing-vertical);color:var(--pico-color);font-style:normal;font-weight:var(--pico-font-weight)}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:var(--pico-typography-spacing-vertical);color:var(--pico-color);font-weight:var(--pico-font-weight);font-size:var(--pico-font-size);line-height:var(--pico-line-height);font-family:var(--pico-font-family)}h1{--pico-color:var(--pico-h1-color)}h2{--pico-color:var(--pico-h2-color)}h3{--pico-color:var(--pico-h3-color)}h4{--pico-color:var(--pico-h4-color)}h5{--pico-color:var(--pico-h5-color)}h6{--pico-color:var(--pico-h6-color)}:where(article,address,blockquote,dl,figure,form,ol,p,pre,table,ul)~:is(h1,h2,h3,h4,h5,h6){margin-top:var(--pico-typography-spacing-top)}p{margin-bottom:var(--pico-typography-spacing-vertical)}hgroup{margin-bottom:var(--pico-typography-spacing-vertical)}hgroup>*{margin-top:0;margin-bottom:0}hgroup>:not(:first-child):last-child{--pico-color:var(--pico-muted-color);--pico-font-weight:unset;font-size:1rem}:where(ol,ul) li{margin-bottom:calc(var(--pico-typography-spacing-vertical) * .25)}:where(dl,ol,ul) :where(dl,ol,ul){margin:0;margin-top:calc(var(--pico-typography-spacing-vertical) * .25)}ul li{list-style:square}mark{padding:.125rem .25rem;background-color:var(--pico-mark-background-color);color:var(--pico-mark-color);vertical-align:baseline}blockquote{display:block;margin:var(--pico-typography-spacing-vertical) 0;padding:var(--pico-spacing);border-right:none;border-left:.25rem solid var(--pico-blockquote-border-color);border-inline-start:0.25rem solid var(--pico-blockquote-border-color);border-inline-end:none}blockquote footer{margin-top:calc(var(--pico-typography-spacing-vertical) * .5);color:var(--pico-blockquote-footer-color)}abbr[title]{border-bottom:1px dotted;text-decoration:none;cursor:help}ins{color:var(--pico-ins-color);text-decoration:none}del{color:var(--pico-del-color)}::-moz-selection{background-color:var(--pico-text-selection-color)}::selection{background-color:var(--pico-text-selection-color)}:where(a:not([role=button])),[role=link]{--pico-color:var(--pico-primary);--pico-background-color:transparent;--pico-underline:var(--pico-primary-underline);outline:0;background-color:var(--pico-background-color);color:var(--pico-color);-webkit-text-decoration:var(--pico-text-decoration);text-decoration:var(--pico-text-decoration);text-decoration-color:var(--pico-underline);text-underline-offset:0.125em;transition:background-color var(--pico-transition),color var(--pico-transition),box-shadow var(--pico-transition),-webkit-text-decoration var(--pico-transition);transition:background-color var(--pico-transition),color var(--pico-transition),text-decoration var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),color var(--pico-transition),text-decoration var(--pico-transition),box-shadow var(--pico-transition),-webkit-text-decoration var(--pico-transition)}:where(a:not([role=button])):is([aria-current]:not([aria-current=false]),:hover,:active,:focus),[role=link]:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-color:var(--pico-primary-hover);--pico-underline:var(--pico-primary-hover-underline);--pico-text-decoration:underline}:where(a:not([role=button])):focus-visible,[role=link]:focus-visible{box-shadow:0 0 0 var(--pico-outline-width) var(--pico-primary-focus)}a[role=button]{display:inline-block}button{margin:0;overflow:visible;font-family:inherit;text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[role=button],[type=button],[type=file]::file-selector-button,[type=reset],[type=submit],button{--pico-background-color:var(--pico-primary-background);--pico-border-color:var(--pico-primary-border);--pico-color:var(--pico-primary-inverse);--pico-box-shadow:var(--pico-button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));padding:var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);border:var(--pico-border-width) solid var(--pico-border-color);border-radius:var(--pico-border-radius);outline:0;background-color:var(--pico-background-color);box-shadow:var(--pico-box-shadow);color:var(--pico-color);font-weight:var(--pico-font-weight);font-size:1rem;line-height:var(--pico-line-height);text-align:center;text-decoration:none;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;transition:background-color var(--pico-transition),border-color var(--pico-transition),color var(--pico-transition),box-shadow var(--pico-transition)}[role=button]:is(:hover,:active,:focus),[role=button]:is([aria-current]:not([aria-current=false])),[type=button]:is(:hover,:active,:focus),[type=button]:is([aria-current]:not([aria-current=false])),[type=file]::file-selector-button:is(:hover,:active,:focus),[type=file]::file-selector-button:is([aria-current]:not([aria-current=false])),[type=reset]:is(:hover,:active,:focus),[type=reset]:is([aria-current]:not([aria-current=false])),[type=submit]:is(:hover,:active,:focus),[type=submit]:is([aria-current]:not([aria-current=false])),button:is(:hover,:active,:focus),button:is([aria-current]:not([aria-current=false])){--pico-background-color:var(--pico-primary-hover-background);--pico-border-color:var(--pico-primary-hover-border);--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));--pico-color:var(--pico-primary-inverse)}[role=button]:focus,[role=button]:is([aria-current]:not([aria-current=false])):focus,[type=button]:focus,[type=button]:is([aria-current]:not([aria-current=false])):focus,[type=file]::file-selector-button:focus,[type=file]::file-selector-button:is([aria-current]:not([aria-current=false])):focus,[type=reset]:focus,[type=reset]:is([aria-current]:not([aria-current=false])):focus,[type=submit]:focus,[type=submit]:is([aria-current]:not([aria-current=false])):focus,button:focus,button:is([aria-current]:not([aria-current=false])):focus{--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--pico-outline-width) var(--pico-primary-focus)}[type=button],[type=reset],[type=submit]{margin-bottom:var(--pico-spacing)}[type=file]::file-selector-button,[type=reset]{--pico-background-color:var(--pico-secondary-background);--pico-border-color:var(--pico-secondary-border);--pico-color:var(--pico-secondary-inverse);cursor:pointer}[type=file]::file-selector-button:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),[type=reset]:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-background-color:var(--pico-secondary-hover-background);--pico-border-color:var(--pico-secondary-hover-border);--pico-color:var(--pico-secondary-inverse)}[type=file]::file-selector-button:focus,[type=reset]:focus{--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--pico-outline-width) var(--pico-secondary-focus)}:where(button,[type=submit],[type=reset],[type=button],[role=button])[disabled],:where(fieldset[disabled]) :is(button,[type=submit],[type=button],[type=reset],[role=button]){opacity:.5;pointer-events:none}:where(table){width:100%;border-collapse:collapse;border-spacing:0;text-indent:0}td,th{padding:calc(var(--pico-spacing)/ 2) var(--pico-spacing);border-bottom:var(--pico-border-width) solid var(--pico-table-border-color);background-color:var(--pico-background-color);color:var(--pico-color);font-weight:var(--pico-font-weight);text-align:left;text-align:start}tfoot td,tfoot th{border-top:var(--pico-border-width) solid var(--pico-table-border-color);border-bottom:0}table.striped tbody tr:nth-child(odd) td,table.striped tbody tr:nth-child(odd) th{background-color:var(--pico-table-row-stripped-background-color)}:where(audio,canvas,iframe,img,svg,video){vertical-align:middle}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}:where(iframe){border-style:none}img{max-width:100%;height:auto;border-style:none}:where(svg:not([fill])){fill:currentColor}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-size:.875em;font-family:var(--pico-font-family)}pre code{font-size:inherit;font-family:inherit}pre{-ms-overflow-style:scrollbar;overflow:auto}code,kbd,pre{border-radius:var(--pico-border-radius);background:var(--pico-code-background-color);color:var(--pico-code-color);font-weight:var(--pico-font-weight);line-height:initial}code,kbd{display:inline-block;padding:.375rem}pre{display:block;margin-bottom:var(--pico-spacing);overflow-x:auto}pre>code{display:block;padding:var(--pico-spacing);background:0 0;line-height:var(--pico-line-height)}kbd{background-color:var(--pico-code-kbd-background-color);color:var(--pico-code-kbd-color);vertical-align:baseline}figure{display:block;margin:0;padding:0}figure figcaption{padding:calc(var(--pico-spacing) * .5) 0;color:var(--pico-muted-color)}hr{height:0;margin:var(--pico-typography-spacing-vertical) 0;border:0;border-top:1px solid var(--pico-muted-border-color);color:inherit}[hidden],template{display:none!important}canvas{display:inline-block}input,optgroup,select,textarea{margin:0;font-size:1rem;line-height:var(--pico-line-height);font-family:inherit;letter-spacing:inherit}input{overflow:visible}select{text-transform:none}legend{max-width:100%;padding:0;color:inherit;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}::-moz-focus-inner{padding:0;border-style:none}:-moz-focusring{outline:0}:-moz-ui-invalid{box-shadow:none}::-ms-expand{display:none}[type=file],[type=range]{padding:0;border-width:0}input:not([type=checkbox],[type=radio],[type=range]){height:calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2)}fieldset{width:100%;margin:0;margin-bottom:var(--pico-spacing);padding:0;border:0}fieldset legend,label{display:block;margin-bottom:calc(var(--pico-spacing) * .375);color:var(--pico-color);font-weight:var(--pico-form-label-font-weight,var(--pico-font-weight))}fieldset legend{margin-bottom:calc(var(--pico-spacing) * .5)}button[type=submit],input:not([type=checkbox],[type=radio]),select,textarea{width:100%}input:not([type=checkbox],[type=radio],[type=range],[type=file]),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal)}input,select,textarea{--pico-background-color:var(--pico-form-element-background-color);--pico-border-color:var(--pico-form-element-border-color);--pico-color:var(--pico-form-element-color);--pico-box-shadow:none;border:var(--pico-border-width) solid var(--pico-border-color);border-radius:var(--pico-border-radius);outline:0;background-color:var(--pico-background-color);box-shadow:var(--pico-box-shadow);color:var(--pico-color);font-weight:var(--pico-font-weight);transition:background-color var(--pico-transition),border-color var(--pico-transition),color var(--pico-transition),box-shadow var(--pico-transition)}:where(select,textarea):not([readonly]):is(:active,:focus),input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[readonly]):is(:active,:focus){--pico-background-color:var(--pico-form-element-active-background-color)}:where(select,textarea):not([readonly]):is(:active,:focus),input:not([type=submit],[type=button],[type=reset],[role=switch],[readonly]):is(:active,:focus){--pico-border-color:var(--pico-form-element-active-border-color)}:where(select,textarea):not([readonly]):focus,input:not([type=submit],[type=button],[type=reset],[type=range],[type=file],[readonly]):focus{--pico-box-shadow:0 0 0 var(--pico-outline-width) var(--pico-form-element-focus-color)}:where(fieldset[disabled]) :is(input:not([type=submit],[type=button],[type=reset]),select,textarea),input:not([type=submit],[type=button],[type=reset])[disabled],label[aria-disabled=true],select[disabled],textarea[disabled]{opacity:var(--pico-form-element-disabled-opacity);pointer-events:none}label[aria-disabled=true] input[disabled]{opacity:1}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid]{padding-right:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem)!important;padding-left:var(--pico-form-element-spacing-horizontal);padding-inline-start:var(--pico-form-element-spacing-horizontal)!important;padding-inline-end:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem)!important;background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid=false]:not(select){background-image:var(--pico-icon-valid)}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid=true]:not(select){background-image:var(--pico-icon-invalid)}:where(input,select,textarea)[aria-invalid=false]{--pico-border-color:var(--pico-form-element-valid-border-color)}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus){--pico-border-color:var(--pico-form-element-valid-active-border-color)!important}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus):not([type=checkbox],[type=radio]){--pico-box-shadow:0 0 0 var(--pico-outline-width) var(--pico-form-element-valid-focus-color)!important}:where(input,select,textarea)[aria-invalid=true]{--pico-border-color:var(--pico-form-element-invalid-border-color)}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus){--pico-border-color:var(--pico-form-element-invalid-active-border-color)!important}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus):not([type=checkbox],[type=radio]){--pico-box-shadow:0 0 0 var(--pico-outline-width) var(--pico-form-element-invalid-focus-color)!important}[dir=rtl] :where(input,select,textarea):not([type=checkbox],[type=radio]):is([aria-invalid],[aria-invalid=true],[aria-invalid=false]){background-position:center left .75rem}input::-webkit-input-placeholder,input::placeholder,select:invalid,textarea::-webkit-input-placeholder,textarea::placeholder{color:var(--pico-form-element-placeholder-color);opacity:1}input:not([type=checkbox],[type=radio]),select,textarea{margin-bottom:var(--pico-spacing)}select::-ms-expand{border:0;background-color:transparent}select:not([multiple],[size]){padding-right:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem);padding-left:var(--pico-form-element-spacing-horizontal);padding-inline-start:var(--pico-form-element-spacing-horizontal);padding-inline-end:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem);background-image:var(--pico-icon-chevron);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}select[multiple] option:checked{background:var(--pico-form-element-selected-background-color);color:var(--pico-form-element-color)}[dir=rtl] select:not([multiple],[size]){background-position:center left .75rem}textarea{display:block;resize:vertical}textarea[aria-invalid]{--pico-icon-height:calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2);background-position:top right .75rem!important;background-size:1rem var(--pico-icon-height)!important}:where(input,select,textarea,fieldset)+small{display:block;width:100%;margin-top:calc(var(--pico-spacing) * -.75);margin-bottom:var(--pico-spacing);color:var(--pico-muted-color)}:where(input,select,textarea,fieldset)[aria-invalid=false]+small{color:var(--pico-ins-color)}:where(input,select,textarea,fieldset)[aria-invalid=true]+small{color:var(--pico-del-color)}label>:where(input,select,textarea){margin-top:calc(var(--pico-spacing) * .25)}label:has([type=checkbox],[type=radio]){width:-moz-fit-content;width:fit-content;cursor:pointer}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:1.25em;height:1.25em;margin-top:-.125em;margin-inline-end:.5em;border-width:var(--pico-border-width);vertical-align:middle;cursor:pointer}[type=checkbox]::-ms-check,[type=radio]::-ms-check{display:none}[type=checkbox]:checked,[type=checkbox]:checked:active,[type=checkbox]:checked:focus,[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--pico-background-color:var(--pico-primary-background);--pico-border-color:var(--pico-primary-border);background-image:var(--pico-icon-checkbox);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=checkbox]~label,[type=radio]~label{display:inline-block;margin-bottom:0;cursor:pointer}[type=checkbox]~label:not(:last-of-type),[type=radio]~label:not(:last-of-type){margin-inline-end:1em}[type=checkbox]:indeterminate{--pico-background-color:var(--pico-primary-background);--pico-border-color:var(--pico-primary-border);background-image:var(--pico-icon-minus);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=radio]{border-radius:50%}[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--pico-background-color:var(--pico-primary-inverse);border-width:.35em;background-image:none}[type=checkbox][role=switch]{--pico-background-color:var(--pico-switch-background-color);--pico-color:var(--pico-switch-color);width:2.25em;height:1.25em;border:var(--pico-border-width) solid var(--pico-border-color);border-radius:1.25em;background-color:var(--pico-background-color);line-height:1.25em}[type=checkbox][role=switch]:not([aria-invalid]){--pico-border-color:var(--pico-switch-background-color)}[type=checkbox][role=switch]:before{display:block;aspect-ratio:1;height:100%;border-radius:50%;background-color:var(--pico-color);box-shadow:var(--pico-switch-thumb-box-shadow);content:"";transition:margin .1s ease-in-out}[type=checkbox][role=switch]:focus{--pico-background-color:var(--pico-switch-background-color);--pico-border-color:var(--pico-switch-background-color)}[type=checkbox][role=switch]:checked{--pico-background-color:var(--pico-switch-checked-background-color);--pico-border-color:var(--pico-switch-checked-background-color);background-image:none}[type=checkbox][role=switch]:checked::before{margin-inline-start:calc(2.25em - 1.25em)}[type=checkbox][role=switch][disabled]{--pico-background-color:var(--pico-border-color)}[type=checkbox][aria-invalid=false]:checked,[type=checkbox][aria-invalid=false]:checked:active,[type=checkbox][aria-invalid=false]:checked:focus,[type=checkbox][role=switch][aria-invalid=false]:checked,[type=checkbox][role=switch][aria-invalid=false]:checked:active,[type=checkbox][role=switch][aria-invalid=false]:checked:focus{--pico-background-color:var(--pico-form-element-valid-border-color)}[type=checkbox]:checked:active[aria-invalid=true],[type=checkbox]:checked:focus[aria-invalid=true],[type=checkbox]:checked[aria-invalid=true],[type=checkbox][role=switch]:checked:active[aria-invalid=true],[type=checkbox][role=switch]:checked:focus[aria-invalid=true],[type=checkbox][role=switch]:checked[aria-invalid=true]{--pico-background-color:var(--pico-form-element-invalid-border-color)}[type=checkbox][aria-invalid=false]:checked,[type=checkbox][aria-invalid=false]:checked:active,[type=checkbox][aria-invalid=false]:checked:focus,[type=checkbox][role=switch][aria-invalid=false]:checked,[type=checkbox][role=switch][aria-invalid=false]:checked:active,[type=checkbox][role=switch][aria-invalid=false]:checked:focus,[type=radio][aria-invalid=false]:checked,[type=radio][aria-invalid=false]:checked:active,[type=radio][aria-invalid=false]:checked:focus{--pico-border-color:var(--pico-form-element-valid-border-color)}[type=checkbox]:checked:active[aria-invalid=true],[type=checkbox]:checked:focus[aria-invalid=true],[type=checkbox]:checked[aria-invalid=true],[type=checkbox][role=switch]:checked:active[aria-invalid=true],[type=checkbox][role=switch]:checked:focus[aria-invalid=true],[type=checkbox][role=switch]:checked[aria-invalid=true],[type=radio]:checked:active[aria-invalid=true],[type=radio]:checked:focus[aria-invalid=true],[type=radio]:checked[aria-invalid=true]{--pico-border-color:var(--pico-form-element-invalid-border-color)}[type=color]::-webkit-color-swatch-wrapper{padding:0}[type=color]::-moz-focus-inner{padding:0}[type=color]::-webkit-color-swatch{border:0;border-radius:calc(var(--pico-border-radius) * .5)}[type=color]::-moz-color-swatch{border:0;border-radius:calc(var(--pico-border-radius) * .5)}input:not([type=checkbox],[type=radio],[type=range],[type=file]):is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){--pico-icon-position:0.75rem;--pico-icon-width:1rem;padding-right:calc(var(--pico-icon-width) + var(--pico-icon-position));background-image:var(--pico-icon-date);background-position:center right var(--pico-icon-position);background-size:var(--pico-icon-width) auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=time]{background-image:var(--pico-icon-time)}[type=date]::-webkit-calendar-picker-indicator,[type=datetime-local]::-webkit-calendar-picker-indicator,[type=month]::-webkit-calendar-picker-indicator,[type=time]::-webkit-calendar-picker-indicator,[type=week]::-webkit-calendar-picker-indicator{width:var(--pico-icon-width);margin-right:calc(var(--pico-icon-width) * -1);margin-left:var(--pico-icon-position);opacity:0}@-moz-document url-prefix(){[type=date],[type=datetime-local],[type=month],[type=time],[type=week]{padding-right:var(--pico-form-element-spacing-horizontal)!important;background-image:none!important}}[dir=rtl] :is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){text-align:right}[type=file]{--pico-color:var(--pico-muted-color);margin-left:calc(var(--pico-outline-width) * -1);padding:calc(var(--pico-form-element-spacing-vertical) * .5) 0;padding-left:var(--pico-outline-width);border:0;border-radius:0;background:0 0}[type=file]::file-selector-button{margin-right:calc(var(--pico-spacing)/ 2);padding:calc(var(--pico-form-element-spacing-vertical) * .5) var(--pico-form-element-spacing-horizontal)}[type=file]:is(:hover,:active,:focus)::file-selector-button{--pico-background-color:var(--pico-secondary-hover-background);--pico-border-color:var(--pico-secondary-hover-border)}[type=file]:focus::file-selector-button{--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--pico-outline-width) var(--pico-secondary-focus)}[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:1.25rem;background:0 0}[type=range]::-webkit-slider-runnable-track{width:100%;height:.375rem;border-radius:var(--pico-border-radius);background-color:var(--pico-range-border-color);-webkit-transition:background-color var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),box-shadow var(--pico-transition)}[type=range]::-moz-range-track{width:100%;height:.375rem;border-radius:var(--pico-border-radius);background-color:var(--pico-range-border-color);-moz-transition:background-color var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),box-shadow var(--pico-transition)}[type=range]::-ms-track{width:100%;height:.375rem;border-radius:var(--pico-border-radius);background-color:var(--pico-range-border-color);-ms-transition:background-color var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),box-shadow var(--pico-transition)}[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pico-range-thumb-border-color);border-radius:50%;background-color:var(--pico-range-thumb-color);cursor:pointer;-webkit-transition:background-color var(--pico-transition),transform var(--pico-transition);transition:background-color var(--pico-transition),transform var(--pico-transition)}[type=range]::-moz-range-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pico-range-thumb-border-color);border-radius:50%;background-color:var(--pico-range-thumb-color);cursor:pointer;-moz-transition:background-color var(--pico-transition),transform var(--pico-transition);transition:background-color var(--pico-transition),transform var(--pico-transition)}[type=range]::-ms-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pico-range-thumb-border-color);border-radius:50%;background-color:var(--pico-range-thumb-color);cursor:pointer;-ms-transition:background-color var(--pico-transition),transform var(--pico-transition);transition:background-color var(--pico-transition),transform var(--pico-transition)}[type=range]:active,[type=range]:focus-within{--pico-range-border-color:var(--pico-range-active-border-color);--pico-range-thumb-color:var(--pico-range-thumb-active-color)}[type=range]:active::-webkit-slider-thumb{transform:scale(1.25)}[type=range]:active::-moz-range-thumb{transform:scale(1.25)}[type=range]:active::-ms-thumb{transform:scale(1.25)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{padding-inline-start:calc(var(--pico-form-element-spacing-horizontal) + 1.75rem);background-image:var(--pico-icon-search);background-position:center left calc(var(--pico-form-element-spacing-horizontal) + .125rem);background-size:1rem auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{padding-inline-start:calc(var(--pico-form-element-spacing-horizontal) + 1.75rem)!important;background-position:center left 1.125rem,center right .75rem}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=false]{background-image:var(--pico-icon-search),var(--pico-icon-valid)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=true]{background-image:var(--pico-icon-search),var(--pico-icon-invalid)}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{background-position:center right 1.125rem}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{background-position:center right 1.125rem,center left .75rem}details{display:block;margin-bottom:var(--pico-spacing)}details summary{line-height:1rem;list-style-type:none;cursor:pointer;transition:color var(--pico-transition)}details summary:not([role]){color:var(--pico-accordion-close-summary-color)}details summary::-webkit-details-marker{display:none}details summary::marker{display:none}details summary::-moz-list-bullet{list-style-type:none}details summary::after{display:block;width:1rem;height:1rem;margin-inline-start:calc(var(--pico-spacing,1rem) * .5);float:right;transform:rotate(-90deg);background-image:var(--pico-icon-chevron);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:"";transition:transform var(--pico-transition)}details summary:focus{outline:0}details summary:focus:not([role]){color:var(--pico-accordion-active-summary-color)}details summary:focus-visible:not([role]){outline:var(--pico-outline-width) solid var(--pico-primary-focus);outline-offset:calc(var(--pico-spacing,1rem) * 0.5);color:var(--pico-primary)}details summary[role=button]{width:100%;text-align:left}details summary[role=button]::after{height:calc(1rem * var(--pico-line-height,1.5))}details[open]>summary{margin-bottom:var(--pico-spacing)}details[open]>summary:not([role]):not(:focus){color:var(--pico-accordion-open-summary-color)}details[open]>summary::after{transform:rotate(0)}[dir=rtl] details summary{text-align:right}[dir=rtl] details summary::after{float:left;background-position:left center}article{margin-bottom:var(--pico-block-spacing-vertical);padding:var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal);border-radius:var(--pico-border-radius);background:var(--pico-card-background-color);box-shadow:var(--pico-card-box-shadow)}article>footer,article>header{margin-right:calc(var(--pico-block-spacing-horizontal) * -1);margin-left:calc(var(--pico-block-spacing-horizontal) * -1);padding:calc(var(--pico-block-spacing-vertical) * .66) var(--pico-block-spacing-horizontal);background-color:var(--pico-card-sectioning-background-color)}article>header{margin-top:calc(var(--pico-block-spacing-vertical) * -1);margin-bottom:var(--pico-block-spacing-vertical);border-bottom:var(--pico-border-width) solid var(--pico-card-border-color);border-top-right-radius:var(--pico-border-radius);border-top-left-radius:var(--pico-border-radius)}article>footer{margin-top:var(--pico-block-spacing-vertical);margin-bottom:calc(var(--pico-block-spacing-vertical) * -1);border-top:var(--pico-border-width) solid var(--pico-card-border-color);border-bottom-right-radius:var(--pico-border-radius);border-bottom-left-radius:var(--pico-border-radius)}[role=group],[role=search]{display:inline-flex;position:relative;width:100%;margin-bottom:var(--pico-spacing);border-radius:var(--pico-border-radius);box-shadow:var(--pico-group-box-shadow,0 0 0 transparent);vertical-align:middle;transition:box-shadow var(--pico-transition)}[role=group] input:not([type=checkbox],[type=radio]),[role=group] select,[role=group]>*,[role=search] input:not([type=checkbox],[type=radio]),[role=search] select,[role=search]>*{position:relative;flex:1 1 auto;margin-bottom:0}[role=group] input:not([type=checkbox],[type=radio]):not(:first-child),[role=group] select:not(:first-child),[role=group]>:not(:first-child),[role=search] input:not([type=checkbox],[type=radio]):not(:first-child),[role=search] select:not(:first-child),[role=search]>:not(:first-child){margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}[role=group] input:not([type=checkbox],[type=radio]):not(:last-child),[role=group] select:not(:last-child),[role=group]>:not(:last-child),[role=search] input:not([type=checkbox],[type=radio]):not(:last-child),[role=search] select:not(:last-child),[role=search]>:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}[role=group] input:not([type=checkbox],[type=radio]):focus,[role=group] select:focus,[role=group]>:focus,[role=search] input:not([type=checkbox],[type=radio]):focus,[role=search] select:focus,[role=search]>:focus{z-index:2}[role=group] [role=button]:not(:first-child),[role=group] [type=button]:not(:first-child),[role=group] [type=reset]:not(:first-child),[role=group] [type=submit]:not(:first-child),[role=group] button:not(:first-child),[role=group] input:not([type=checkbox],[type=radio]):not(:first-child),[role=group] select:not(:first-child),[role=search] [role=button]:not(:first-child),[role=search] [type=button]:not(:first-child),[role=search] [type=reset]:not(:first-child),[role=search] [type=submit]:not(:first-child),[role=search] button:not(:first-child),[role=search] input:not([type=checkbox],[type=radio]):not(:first-child),[role=search] select:not(:first-child){margin-left:calc(var(--pico-border-width) * -1)}[role=group] [role=button],[role=group] [type=button],[role=group] [type=reset],[role=group] [type=submit],[role=group] button,[role=search] [role=button],[role=search] [type=button],[role=search] [type=reset],[role=search] [type=submit],[role=search] button{width:auto}@supports selector(:has(*)){[role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus),[role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus){--pico-group-box-shadow:var(--pico-group-box-shadow-focus-with-button)}[role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) input:not([type=checkbox],[type=radio]),[role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) select,[role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) input:not([type=checkbox],[type=radio]),[role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) select{border-color:transparent}[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus),[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus){--pico-group-box-shadow:var(--pico-group-box-shadow-focus-with-input)}[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [role=button],[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=button],[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=submit],[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) button,[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [role=button],[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=button],[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=submit],[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) button{--pico-button-box-shadow:0 0 0 var(--pico-border-width) var(--pico-primary-border);--pico-button-hover-box-shadow:0 0 0 var(--pico-border-width) var(--pico-primary-hover-border)}[role=group] [role=button]:focus,[role=group] [type=button]:focus,[role=group] [type=reset]:focus,[role=group] [type=submit]:focus,[role=group] button:focus,[role=search] [role=button]:focus,[role=search] [type=button]:focus,[role=search] [type=reset]:focus,[role=search] [type=submit]:focus,[role=search] button:focus{box-shadow:none}}[role=search]>:first-child{border-top-left-radius:5rem;border-bottom-left-radius:5rem}[role=search]>:last-child{border-top-right-radius:5rem;border-bottom-right-radius:5rem}[aria-busy=true]:not(input,select,textarea,html){white-space:nowrap}[aria-busy=true]:not(input,select,textarea,html)::before{display:inline-block;width:1em;height:1em;background-image:var(--pico-icon-loading);background-size:1em auto;background-repeat:no-repeat;content:"";vertical-align:-.125em}[aria-busy=true]:not(input,select,textarea,html):not(:empty)::before{margin-inline-end:calc(var(--pico-spacing) * .5)}[aria-busy=true]:not(input,select,textarea,html):empty{text-align:center}[role=button][aria-busy=true],[type=button][aria-busy=true],[type=reset][aria-busy=true],[type=submit][aria-busy=true],a[aria-busy=true],button[aria-busy=true]{pointer-events:none}:root{--pico-scrollbar-width:0px}dialog{display:flex;z-index:999;position:fixed;top:0;right:0;bottom:0;left:0;align-items:center;justify-content:center;width:inherit;min-width:100%;height:inherit;min-height:100%;padding:0;border:0;-webkit-backdrop-filter:var(--pico-modal-overlay-backdrop-filter);backdrop-filter:var(--pico-modal-overlay-backdrop-filter);background-color:var(--pico-modal-overlay-background-color);color:var(--pico-color)}dialog article{width:100%;max-height:calc(100vh - var(--pico-spacing) * 2);margin:var(--pico-spacing);overflow:auto}@media (min-width:576px){dialog article{max-width:510px}}@media (min-width:768px){dialog article{max-width:700px}}dialog article>header>*{margin-bottom:0}dialog article>header :is(a,button)[rel=prev]{margin:0;margin-left:var(--pico-spacing);padding:0;float:right}dialog article>footer{text-align:right}dialog article>footer [role=button],dialog article>footer button{margin-bottom:0}dialog article>footer [role=button]:not(:first-of-type),dialog article>footer button:not(:first-of-type){margin-left:calc(var(--pico-spacing) * .5)}dialog article :is(a,button)[rel=prev]{display:block;width:1rem;height:1rem;margin-top:calc(var(--pico-spacing) * -1);margin-bottom:var(--pico-spacing);margin-left:auto;border:none;background-image:var(--pico-icon-close);background-position:center;background-size:auto 1rem;background-repeat:no-repeat;background-color:transparent;opacity:.5;transition:opacity var(--pico-transition)}dialog article :is(a,button)[rel=prev]:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){opacity:1}dialog:not([open]),dialog[open=false]{display:none}:where(nav li)::before{float:left;content:"​"}nav,nav ul{display:flex}nav{justify-content:space-between;overflow:visible}nav ol,nav ul{align-items:center;margin-bottom:0;padding:0;list-style:none}nav ol:first-of-type,nav ul:first-of-type{margin-left:calc(var(--pico-nav-element-spacing-horizontal) * -1)}nav ol:last-of-type,nav ul:last-of-type{margin-right:calc(var(--pico-nav-element-spacing-horizontal) * -1)}nav li{display:inline-block;margin:0;padding:var(--pico-nav-element-spacing-vertical) var(--pico-nav-element-spacing-horizontal)}nav li :where(a,[role=link]){display:inline-block;margin:calc(var(--pico-nav-link-spacing-vertical) * -1) calc(var(--pico-nav-link-spacing-horizontal) * -1);padding:var(--pico-nav-link-spacing-vertical) var(--pico-nav-link-spacing-horizontal);border-radius:var(--pico-border-radius)}nav li :where(a,[role=link]):not(:hover){text-decoration:none}nav li [role=button],nav li [type=button],nav li button,nav li input:not([type=checkbox],[type=radio],[type=range],[type=file]),nav li select{height:auto;margin-right:inherit;margin-bottom:0;margin-left:inherit;padding:calc(var(--pico-nav-link-spacing-vertical) - var(--pico-border-width) * 2) var(--pico-nav-link-spacing-horizontal)}nav[aria-label=breadcrumb]{align-items:center;justify-content:start}nav[aria-label=breadcrumb] ul li:not(:first-child){margin-inline-start:var(--pico-nav-link-spacing-horizontal)}nav[aria-label=breadcrumb] ul li a{margin:calc(var(--pico-nav-link-spacing-vertical) * -1) 0;margin-inline-start:calc(var(--pico-nav-link-spacing-horizontal) * -1)}nav[aria-label=breadcrumb] ul li:not(:last-child)::after{display:inline-block;position:absolute;width:calc(var(--pico-nav-link-spacing-horizontal) * 4);margin:0 calc(var(--pico-nav-link-spacing-horizontal) * -1);content:var(--pico-nav-breadcrumb-divider);color:var(--pico-muted-color);text-align:center;text-decoration:none;white-space:nowrap}nav[aria-label=breadcrumb] a[aria-current]:not([aria-current=false]){background-color:transparent;color:inherit;text-decoration:none;pointer-events:none}aside li,aside nav,aside ol,aside ul{display:block}aside li{padding:calc(var(--pico-nav-element-spacing-vertical) * .5) var(--pico-nav-element-spacing-horizontal)}aside li a{display:block}aside li [role=button]{margin:inherit}[dir=rtl] nav[aria-label=breadcrumb] ul li:not(:last-child) ::after{content:"\\"}progress{display:inline-block;vertical-align:baseline}progress{-webkit-appearance:none;-moz-appearance:none;display:inline-block;appearance:none;width:100%;height:.5rem;margin-bottom:calc(var(--pico-spacing) * .5);overflow:hidden;border:0;border-radius:var(--pico-border-radius);background-color:var(--pico-progress-background-color);color:var(--pico-progress-color)}progress::-webkit-progress-bar{border-radius:var(--pico-border-radius);background:0 0}progress[value]::-webkit-progress-value{background-color:var(--pico-progress-color);-webkit-transition:inline-size var(--pico-transition);transition:inline-size var(--pico-transition)}progress::-moz-progress-bar{background-color:var(--pico-progress-color)}@media (prefers-reduced-motion:no-preference){progress:indeterminate{background:var(--pico-progress-background-color) linear-gradient(to right,var(--pico-progress-color) 30%,var(--pico-progress-background-color) 30%) top left/150% 150% no-repeat;animation:progress-indeterminate 1s linear infinite}progress:indeterminate[value]::-webkit-progress-value{background-color:transparent}progress:indeterminate::-moz-progress-bar{background-color:transparent}}@media (prefers-reduced-motion:no-preference){[dir=rtl] progress:indeterminate{animation-direction:reverse}}@keyframes progress-indeterminate{0%{background-position:200% 0}100%{background-position:-200% 0}}[data-tooltip]{position:relative}[data-tooltip]:not(a,button,input){border-bottom:1px dotted;text-decoration:none;cursor:help}[data-tooltip]::after,[data-tooltip]::before,[data-tooltip][data-placement=top]::after,[data-tooltip][data-placement=top]::before{display:block;z-index:99;position:absolute;bottom:100%;left:50%;padding:.25rem .5rem;overflow:hidden;transform:translate(-50%,-.25rem);border-radius:var(--pico-border-radius);background:var(--pico-tooltip-background-color);content:attr(data-tooltip);color:var(--pico-tooltip-color);font-style:normal;font-weight:var(--pico-font-weight);font-size:.875rem;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;opacity:0;pointer-events:none}[data-tooltip]::after,[data-tooltip][data-placement=top]::after{padding:0;transform:translate(-50%,0);border-top:.3rem solid;border-right:.3rem solid transparent;border-left:.3rem solid transparent;border-radius:0;background-color:transparent;content:"";color:var(--pico-tooltip-background-color)}[data-tooltip][data-placement=bottom]::after,[data-tooltip][data-placement=bottom]::before{top:100%;bottom:auto;transform:translate(-50%,.25rem)}[data-tooltip][data-placement=bottom]:after{transform:translate(-50%,-.3rem);border:.3rem solid transparent;border-bottom:.3rem solid}[data-tooltip][data-placement=left]::after,[data-tooltip][data-placement=left]::before{top:50%;right:100%;bottom:auto;left:auto;transform:translate(-.25rem,-50%)}[data-tooltip][data-placement=left]:after{transform:translate(.3rem,-50%);border:.3rem solid transparent;border-left:.3rem solid}[data-tooltip][data-placement=right]::after,[data-tooltip][data-placement=right]::before{top:50%;right:auto;bottom:auto;left:100%;transform:translate(.25rem,-50%)}[data-tooltip][data-placement=right]:after{transform:translate(-.3rem,-50%);border:.3rem solid transparent;border-right:.3rem solid}[data-tooltip]:focus::after,[data-tooltip]:focus::before,[data-tooltip]:hover::after,[data-tooltip]:hover::before{opacity:1}@media (hover:hover) and (pointer:fine){[data-tooltip]:focus::after,[data-tooltip]:focus::before,[data-tooltip]:hover::after,[data-tooltip]:hover::before{--pico-tooltip-slide-to:translate(-50%, -0.25rem);transform:translate(-50%,.75rem);animation-duration:.2s;animation-fill-mode:forwards;animation-name:tooltip-slide;opacity:0}[data-tooltip]:focus::after,[data-tooltip]:hover::after{--pico-tooltip-caret-slide-to:translate(-50%, 0rem);transform:translate(-50%,-.25rem);animation-name:tooltip-caret-slide}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:focus::before,[data-tooltip][data-placement=bottom]:hover::after,[data-tooltip][data-placement=bottom]:hover::before{--pico-tooltip-slide-to:translate(-50%, 0.25rem);transform:translate(-50%,-.75rem);animation-name:tooltip-slide}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover::after{--pico-tooltip-caret-slide-to:translate(-50%, -0.3rem);transform:translate(-50%,-.5rem);animation-name:tooltip-caret-slide}[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:focus::before,[data-tooltip][data-placement=left]:hover::after,[data-tooltip][data-placement=left]:hover::before{--pico-tooltip-slide-to:translate(-0.25rem, -50%);transform:translate(.75rem,-50%);animation-name:tooltip-slide}[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:hover::after{--pico-tooltip-caret-slide-to:translate(0.3rem, -50%);transform:translate(.05rem,-50%);animation-name:tooltip-caret-slide}[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:focus::before,[data-tooltip][data-placement=right]:hover::after,[data-tooltip][data-placement=right]:hover::before{--pico-tooltip-slide-to:translate(0.25rem, -50%);transform:translate(-.75rem,-50%);animation-name:tooltip-slide}[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:hover::after{--pico-tooltip-caret-slide-to:translate(-0.3rem, -50%);transform:translate(-.05rem,-50%);animation-name:tooltip-caret-slide}}@keyframes tooltip-slide{to{transform:var(--pico-tooltip-slide-to);opacity:1}}@keyframes tooltip-caret-slide{50%{opacity:0}to{transform:var(--pico-tooltip-caret-slide-to);opacity:1}}[aria-controls]{cursor:pointer}[aria-disabled=true],[disabled]{cursor:not-allowed}[aria-hidden=false][hidden]{display:initial}[aria-hidden=false][hidden]:not(:focus){clip:rect(0,0,0,0);position:absolute}[tabindex],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation}[dir=rtl]{direction:rtl}@media (prefers-reduced-motion:reduce){:not([aria-busy=true]),:not([aria-busy=true])::after,:not([aria-busy=true])::before{background-attachment:initial!important;animation-duration:1ms!important;animation-delay:-1ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-delay:0s!important;transition-duration:0s!important}} \ No newline at end of file + */ +:root { + --pico-font-family-emoji: + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --pico-font-family-sans-serif: + system-ui, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, Helvetica, Arial, + "Helvetica Neue", sans-serif, var(--pico-font-family-emoji); + --pico-font-family-monospace: + ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", + monospace, var(--pico-font-family-emoji); + --pico-font-family: var(--pico-font-family-sans-serif); + --pico-line-height: 1.5; + --pico-font-weight: 400; + --pico-font-size: 100%; + --pico-text-underline-offset: 0.1rem; + --pico-border-radius: 0.25rem; + --pico-border-width: 0.0625rem; + --pico-outline-width: 0.125rem; + --pico-transition: 0.2s ease-in-out; + --pico-spacing: 1rem; + --pico-typography-spacing-vertical: 1rem; + --pico-block-spacing-vertical: var(--pico-spacing); + --pico-block-spacing-horizontal: var(--pico-spacing); + --pico-form-element-spacing-vertical: 0.75rem; + --pico-form-element-spacing-horizontal: 1rem; + --pico-group-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --pico-group-box-shadow-focus-with-button: 0 0 0 var(--pico-outline-width) + var(--pico-primary-focus); + --pico-group-box-shadow-focus-with-input: 0 0 0 0.0625rem + var(--pico-form-element-border-color); + --pico-modal-overlay-backdrop-filter: blur(0.375rem); + --pico-nav-element-spacing-vertical: 1rem; + --pico-nav-element-spacing-horizontal: 0.5rem; + --pico-nav-link-spacing-vertical: 0.5rem; + --pico-nav-link-spacing-horizontal: 0.5rem; + --pico-nav-breadcrumb-divider: ">"; + --pico-icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); + --pico-icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E"); + --pico-icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); + --pico-icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E"); + --pico-icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E"); + --pico-icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E"); + --pico-icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E"); + --pico-icon-loading: url("data:image/svg+xml,%3Csvg fill='none' height='24' width='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' %3E%3Cstyle%3E g %7B animation: rotate 2s linear infinite; transform-origin: center center; %7D circle %7B stroke-dasharray: 75,100; stroke-dashoffset: -5; animation: dash 1.5s ease-in-out infinite; stroke-linecap: round; %7D @keyframes rotate %7B 0%25 %7B transform: rotate(0deg); %7D 100%25 %7B transform: rotate(360deg); %7D %7D @keyframes dash %7B 0%25 %7B stroke-dasharray: 1,100; stroke-dashoffset: 0; %7D 50%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -17.5; %7D 100%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -62; %7D %7D %3C/style%3E%3Cg%3E%3Ccircle cx='12' cy='12' r='10' fill='none' stroke='rgb(136, 145, 164)' stroke-width='4' /%3E%3C/g%3E%3C/svg%3E"); +} +@media (min-width: 576px) { + :root { + --pico-font-size: 106.25%; + } +} +@media (min-width: 768px) { + :root { + --pico-font-size: 112.5%; + } +} +@media (min-width: 1024px) { + :root { + --pico-font-size: 118.75%; + } +} +@media (min-width: 1280px) { + :root { + --pico-font-size: 125%; + } +} +@media (min-width: 1536px) { + :root { + --pico-font-size: 131.25%; + } +} +a { + --pico-text-decoration: underline; +} +small { + --pico-font-size: 0.875em; +} +h1, +h2, +h3, +h4, +h5, +h6 { + --pico-font-weight: 700; +} +h1 { + --pico-font-size: 2rem; + --pico-line-height: 1.125; + --pico-typography-spacing-top: 3rem; +} +h2 { + --pico-font-size: 1.75rem; + --pico-line-height: 1.15; + --pico-typography-spacing-top: 2.625rem; +} +h3 { + --pico-font-size: 1.5rem; + --pico-line-height: 1.175; + --pico-typography-spacing-top: 2.25rem; +} +h4 { + --pico-font-size: 1.25rem; + --pico-line-height: 1.2; + --pico-typography-spacing-top: 1.874rem; +} +h5 { + --pico-font-size: 1.125rem; + --pico-line-height: 1.225; + --pico-typography-spacing-top: 1.6875rem; +} +h6 { + --pico-font-size: 1rem; + --pico-line-height: 1.25; + --pico-typography-spacing-top: 1.5rem; +} +tfoot td, +tfoot th, +thead td, +thead th { + --pico-font-weight: 600; + --pico-border-width: 0.1875rem; +} +code, +kbd, +pre, +samp { + --pico-font-family: var(--pico-font-family-monospace); +} +kbd { + --pico-font-weight: bolder; +} +:where(select, textarea), +input:not( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] + ) { + --pico-outline-width: 0.0625rem; +} +[type="search"] { + --pico-border-radius: 5rem; +} +[type="checkbox"], +[type="radio"] { + --pico-border-width: 0.125rem; +} +[type="checkbox"][role="switch"] { + --pico-border-width: 0.1875rem; +} +[role="search"] { + --pico-border-radius: 5rem; +} +[role="group"] [role="button"], +[role="group"] [type="button"], +[role="group"] [type="submit"], +[role="group"] button, +[role="search"] [role="button"], +[role="search"] [type="button"], +[role="search"] [type="submit"], +[role="search"] button { + --pico-form-element-spacing-horizontal: 2rem; +} +details summary[role="button"]::after { + filter: brightness(0) invert(1); +} +[aria-busy="true"]:not(input, select, textarea):is( + button, + [type="submit"], + [type="button"], + [type="reset"], + [role="button"] + )::before { + filter: brightness(0) invert(1); +} +:root:not([data-theme="dark"]), +[data-theme="light"] { + --pico-background-color: #fff; + --pico-color: #373c44; + --pico-text-selection-color: rgba(2, 154, 232, 0.25); + --pico-muted-color: #646b79; + --pico-muted-border-color: #e7eaf0; + --pico-primary: #0172ad; + --pico-primary-background: #0172ad; + --pico-primary-border: var(--pico-primary-background); + --pico-primary-underline: rgba(1, 114, 173, 0.5); + --pico-primary-hover: #015887; + --pico-primary-hover-background: #02659a; + --pico-primary-hover-border: var(--pico-primary-hover-background); + --pico-primary-hover-underline: var(--pico-primary-hover); + --pico-primary-focus: rgba(2, 154, 232, 0.5); + --pico-primary-inverse: #fff; + --pico-secondary: #5d6b89; + --pico-secondary-background: #525f7a; + --pico-secondary-border: var(--pico-secondary-background); + --pico-secondary-underline: rgba(93, 107, 137, 0.5); + --pico-secondary-hover: #48536b; + --pico-secondary-hover-background: #48536b; + --pico-secondary-hover-border: var(--pico-secondary-hover-background); + --pico-secondary-hover-underline: var(--pico-secondary-hover); + --pico-secondary-focus: rgba(93, 107, 137, 0.25); + --pico-secondary-inverse: #fff; + --pico-contrast: #181c25; + --pico-contrast-background: #181c25; + --pico-contrast-border: var(--pico-contrast-background); + --pico-contrast-underline: rgba(24, 28, 37, 0.5); + --pico-contrast-hover: #000; + --pico-contrast-hover-background: #000; + --pico-contrast-hover-border: var(--pico-contrast-hover-background); + --pico-contrast-hover-underline: var(--pico-secondary-hover); + --pico-contrast-focus: rgba(93, 107, 137, 0.25); + --pico-contrast-inverse: #fff; + --pico-box-shadow: + 0.0145rem 0.029rem 0.174rem rgba(129, 145, 181, 0.01698), 0.0335rem 0.067rem 0.402rem rgba( + 129, + 145, + 181, + 0.024 + ), 0.0625rem 0.125rem 0.75rem rgba(129, 145, 181, 0.03), 0.1125rem 0.225rem 1.35rem rgba( + 129, + 145, + 181, + 0.036 + ), 0.2085rem 0.417rem 2.502rem rgba(129, 145, 181, 0.04302), 0.5rem 1rem 6rem rgba( + 129, + 145, + 181, + 0.06 + ), 0 0 0 0.0625rem rgba(129, 145, 181, 0.015); + --pico-h1-color: #2d3138; + --pico-h2-color: #373c44; + --pico-h3-color: #424751; + --pico-h4-color: #4d535e; + --pico-h5-color: #5c6370; + --pico-h6-color: #646b79; + --pico-mark-background-color: #fde7c0; + --pico-mark-color: #0f1114; + --pico-ins-color: #1d6a54; + --pico-del-color: #883935; + --pico-blockquote-border-color: var(--pico-muted-border-color); + --pico-blockquote-footer-color: var(--pico-muted-color); + --pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --pico-table-border-color: var(--pico-muted-border-color); + --pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375); + --pico-code-background-color: #f3f5f7; + --pico-code-color: #646b79; + --pico-code-kbd-background-color: var(--pico-color); + --pico-code-kbd-color: var(--pico-background-color); + --pico-form-element-background-color: #fbfcfc; + --pico-form-element-selected-background-color: #dfe3eb; + --pico-form-element-border-color: #cfd5e2; + --pico-form-element-color: #23262c; + --pico-form-element-placeholder-color: var(--pico-muted-color); + --pico-form-element-active-background-color: #fff; + --pico-form-element-active-border-color: var(--pico-primary-border); + --pico-form-element-focus-color: var(--pico-primary-border); + --pico-form-element-disabled-opacity: 0.5; + --pico-form-element-invalid-border-color: #b86a6b; + --pico-form-element-invalid-active-border-color: #c84f48; + --pico-form-element-invalid-focus-color: var( + --pico-form-element-invalid-active-border-color + ); + --pico-form-element-valid-border-color: #4c9b8a; + --pico-form-element-valid-active-border-color: #279977; + --pico-form-element-valid-focus-color: var( + --pico-form-element-valid-active-border-color + ); + --pico-switch-background-color: #bfc7d9; + --pico-switch-checked-background-color: var(--pico-primary-background); + --pico-switch-color: #fff; + --pico-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --pico-range-border-color: #dfe3eb; + --pico-range-active-border-color: #bfc7d9; + --pico-range-thumb-border-color: var(--pico-background-color); + --pico-range-thumb-color: var(--pico-secondary-background); + --pico-range-thumb-active-color: var(--pico-primary-background); + --pico-accordion-border-color: var(--pico-muted-border-color); + --pico-accordion-active-summary-color: var(--pico-primary-hover); + --pico-accordion-close-summary-color: var(--pico-color); + --pico-accordion-open-summary-color: var(--pico-muted-color); + --pico-card-background-color: var(--pico-background-color); + --pico-card-border-color: var(--pico-muted-border-color); + --pico-card-box-shadow: var(--pico-box-shadow); + --pico-card-sectioning-background-color: #fbfcfc; + --pico-loading-spinner-opacity: 0.5; + --pico-modal-overlay-background-color: rgba(232, 234, 237, 0.75); + --pico-progress-background-color: #dfe3eb; + --pico-progress-color: var(--pico-primary-background); + --pico-tooltip-background-color: var(--pico-contrast-background); + --pico-tooltip-color: var(--pico-contrast-inverse); + --pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 155, 138)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); + --pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(200, 79, 72)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); + color-scheme: light; +} +:root:not([data-theme="dark"]) + input:is( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] + ), +[data-theme="light"] + input:is( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] + ) { + --pico-form-element-focus-color: var(--pico-primary-focus); +} +@media only screen and (prefers-color-scheme: dark) { + :root:not([data-theme]) { + --pico-background-color: #13171f; + --pico-color: #c2c7d0; + --pico-text-selection-color: rgba(1, 170, 255, 0.1875); + --pico-muted-color: #7b8495; + --pico-muted-border-color: #202632; + --pico-primary: #01aaff; + --pico-primary-background: #0172ad; + --pico-primary-border: var(--pico-primary-background); + --pico-primary-underline: rgba(1, 170, 255, 0.5); + --pico-primary-hover: #79c0ff; + --pico-primary-hover-background: #017fc0; + --pico-primary-hover-border: var(--pico-primary-hover-background); + --pico-primary-hover-underline: var(--pico-primary-hover); + --pico-primary-focus: rgba(1, 170, 255, 0.375); + --pico-primary-inverse: #fff; + --pico-secondary: #969eaf; + --pico-secondary-background: #525f7a; + --pico-secondary-border: var(--pico-secondary-background); + --pico-secondary-underline: rgba(150, 158, 175, 0.5); + --pico-secondary-hover: #b3b9c5; + --pico-secondary-hover-background: #5d6b89; + --pico-secondary-hover-border: var(--pico-secondary-hover-background); + --pico-secondary-hover-underline: var(--pico-secondary-hover); + --pico-secondary-focus: rgba(144, 158, 190, 0.25); + --pico-secondary-inverse: #fff; + --pico-contrast: #dfe3eb; + --pico-contrast-background: #eff1f4; + --pico-contrast-border: var(--pico-contrast-background); + --pico-contrast-underline: rgba(223, 227, 235, 0.5); + --pico-contrast-hover: #fff; + --pico-contrast-hover-background: #fff; + --pico-contrast-hover-border: var(--pico-contrast-hover-background); + --pico-contrast-hover-underline: var(--pico-contrast-hover); + --pico-contrast-focus: rgba(207, 213, 226, 0.25); + --pico-contrast-inverse: #000; + --pico-box-shadow: + 0.0145rem 0.029rem 0.174rem rgba(7, 9, 12, 0.01698), 0.0335rem 0.067rem 0.402rem rgba( + 7, + 9, + 12, + 0.024 + ), 0.0625rem 0.125rem 0.75rem rgba(7, 9, 12, 0.03), 0.1125rem 0.225rem 1.35rem rgba( + 7, + 9, + 12, + 0.036 + ), 0.2085rem 0.417rem 2.502rem rgba(7, 9, 12, 0.04302), 0.5rem 1rem 6rem rgba( + 7, + 9, + 12, + 0.06 + ), 0 0 0 0.0625rem rgba(7, 9, 12, 0.015); + --pico-h1-color: #f0f1f3; + --pico-h2-color: #e0e3e7; + --pico-h3-color: #c2c7d0; + --pico-h4-color: #b3b9c5; + --pico-h5-color: #a4acba; + --pico-h6-color: #8891a4; + --pico-mark-background-color: #014063; + --pico-mark-color: #fff; + --pico-ins-color: #62af9a; + --pico-del-color: #ce7e7b; + --pico-blockquote-border-color: var(--pico-muted-border-color); + --pico-blockquote-footer-color: var(--pico-muted-color); + --pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --pico-table-border-color: var(--pico-muted-border-color); + --pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375); + --pico-code-background-color: #1a1f28; + --pico-code-color: #8891a4; + --pico-code-kbd-background-color: var(--pico-color); + --pico-code-kbd-color: var(--pico-background-color); + --pico-form-element-background-color: #1c212c; + --pico-form-element-selected-background-color: #2a3140; + --pico-form-element-border-color: #2a3140; + --pico-form-element-color: #e0e3e7; + --pico-form-element-placeholder-color: #8891a4; + --pico-form-element-active-background-color: #1a1f28; + --pico-form-element-active-border-color: var(--pico-primary-border); + --pico-form-element-focus-color: var(--pico-primary-border); + --pico-form-element-disabled-opacity: 0.5; + --pico-form-element-invalid-border-color: #964a50; + --pico-form-element-invalid-active-border-color: #b7403b; + --pico-form-element-invalid-focus-color: var( + --pico-form-element-invalid-active-border-color + ); + --pico-form-element-valid-border-color: #2a7b6f; + --pico-form-element-valid-active-border-color: #16896a; + --pico-form-element-valid-focus-color: var( + --pico-form-element-valid-active-border-color + ); + --pico-switch-background-color: #333c4e; + --pico-switch-checked-background-color: var(--pico-primary-background); + --pico-switch-color: #fff; + --pico-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --pico-range-border-color: #202632; + --pico-range-active-border-color: #2a3140; + --pico-range-thumb-border-color: var(--pico-background-color); + --pico-range-thumb-color: var(--pico-secondary-background); + --pico-range-thumb-active-color: var(--pico-primary-background); + --pico-accordion-border-color: var(--pico-muted-border-color); + --pico-accordion-active-summary-color: var(--pico-primary-hover); + --pico-accordion-close-summary-color: var(--pico-color); + --pico-accordion-open-summary-color: var(--pico-muted-color); + --pico-card-background-color: #181c25; + --pico-card-border-color: var(--pico-card-background-color); + --pico-card-box-shadow: var(--pico-box-shadow); + --pico-card-sectioning-background-color: #1a1f28; + --pico-loading-spinner-opacity: 0.5; + --pico-modal-overlay-background-color: rgba(8, 9, 10, 0.75); + --pico-progress-background-color: #202632; + --pico-progress-color: var(--pico-primary-background); + --pico-tooltip-background-color: var(--pico-contrast-background); + --pico-tooltip-color: var(--pico-contrast-inverse); + --pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); + --pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(150, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); + color-scheme: dark; + } + :root:not([data-theme]) + input:is( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] + ) { + --pico-form-element-focus-color: var(--pico-primary-focus); + } +} +[data-theme="dark"] { + --pico-background-color: #13171f; + --pico-color: #c2c7d0; + --pico-text-selection-color: rgba(1, 170, 255, 0.1875); + --pico-muted-color: #7b8495; + --pico-muted-border-color: #202632; + --pico-primary: #01aaff; + --pico-primary-background: #0172ad; + --pico-primary-border: var(--pico-primary-background); + --pico-primary-underline: rgba(1, 170, 255, 0.5); + --pico-primary-hover: #79c0ff; + --pico-primary-hover-background: #017fc0; + --pico-primary-hover-border: var(--pico-primary-hover-background); + --pico-primary-hover-underline: var(--pico-primary-hover); + --pico-primary-focus: rgba(1, 170, 255, 0.375); + --pico-primary-inverse: #fff; + --pico-secondary: #969eaf; + --pico-secondary-background: #525f7a; + --pico-secondary-border: var(--pico-secondary-background); + --pico-secondary-underline: rgba(150, 158, 175, 0.5); + --pico-secondary-hover: #b3b9c5; + --pico-secondary-hover-background: #5d6b89; + --pico-secondary-hover-border: var(--pico-secondary-hover-background); + --pico-secondary-hover-underline: var(--pico-secondary-hover); + --pico-secondary-focus: rgba(144, 158, 190, 0.25); + --pico-secondary-inverse: #fff; + --pico-contrast: #dfe3eb; + --pico-contrast-background: #eff1f4; + --pico-contrast-border: var(--pico-contrast-background); + --pico-contrast-underline: rgba(223, 227, 235, 0.5); + --pico-contrast-hover: #fff; + --pico-contrast-hover-background: #fff; + --pico-contrast-hover-border: var(--pico-contrast-hover-background); + --pico-contrast-hover-underline: var(--pico-contrast-hover); + --pico-contrast-focus: rgba(207, 213, 226, 0.25); + --pico-contrast-inverse: #000; + --pico-box-shadow: + 0.0145rem 0.029rem 0.174rem rgba(7, 9, 12, 0.01698), 0.0335rem 0.067rem 0.402rem rgba( + 7, + 9, + 12, + 0.024 + ), 0.0625rem 0.125rem 0.75rem rgba(7, 9, 12, 0.03), 0.1125rem 0.225rem 1.35rem rgba( + 7, + 9, + 12, + 0.036 + ), 0.2085rem 0.417rem 2.502rem rgba(7, 9, 12, 0.04302), 0.5rem 1rem 6rem rgba( + 7, + 9, + 12, + 0.06 + ), 0 0 0 0.0625rem rgba(7, 9, 12, 0.015); + --pico-h1-color: #f0f1f3; + --pico-h2-color: #e0e3e7; + --pico-h3-color: #c2c7d0; + --pico-h4-color: #b3b9c5; + --pico-h5-color: #a4acba; + --pico-h6-color: #8891a4; + --pico-mark-background-color: #014063; + --pico-mark-color: #fff; + --pico-ins-color: #62af9a; + --pico-del-color: #ce7e7b; + --pico-blockquote-border-color: var(--pico-muted-border-color); + --pico-blockquote-footer-color: var(--pico-muted-color); + --pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --pico-table-border-color: var(--pico-muted-border-color); + --pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375); + --pico-code-background-color: #1a1f28; + --pico-code-color: #8891a4; + --pico-code-kbd-background-color: var(--pico-color); + --pico-code-kbd-color: var(--pico-background-color); + --pico-form-element-background-color: #1c212c; + --pico-form-element-selected-background-color: #2a3140; + --pico-form-element-border-color: #2a3140; + --pico-form-element-color: #e0e3e7; + --pico-form-element-placeholder-color: #8891a4; + --pico-form-element-active-background-color: #1a1f28; + --pico-form-element-active-border-color: var(--pico-primary-border); + --pico-form-element-focus-color: var(--pico-primary-border); + --pico-form-element-disabled-opacity: 0.5; + --pico-form-element-invalid-border-color: #964a50; + --pico-form-element-invalid-active-border-color: #b7403b; + --pico-form-element-invalid-focus-color: var( + --pico-form-element-invalid-active-border-color + ); + --pico-form-element-valid-border-color: #2a7b6f; + --pico-form-element-valid-active-border-color: #16896a; + --pico-form-element-valid-focus-color: var( + --pico-form-element-valid-active-border-color + ); + --pico-switch-background-color: #333c4e; + --pico-switch-checked-background-color: var(--pico-primary-background); + --pico-switch-color: #fff; + --pico-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0); + --pico-range-border-color: #202632; + --pico-range-active-border-color: #2a3140; + --pico-range-thumb-border-color: var(--pico-background-color); + --pico-range-thumb-color: var(--pico-secondary-background); + --pico-range-thumb-active-color: var(--pico-primary-background); + --pico-accordion-border-color: var(--pico-muted-border-color); + --pico-accordion-active-summary-color: var(--pico-primary-hover); + --pico-accordion-close-summary-color: var(--pico-color); + --pico-accordion-open-summary-color: var(--pico-muted-color); + --pico-card-background-color: #181c25; + --pico-card-border-color: var(--pico-card-background-color); + --pico-card-box-shadow: var(--pico-box-shadow); + --pico-card-sectioning-background-color: #1a1f28; + --pico-loading-spinner-opacity: 0.5; + --pico-modal-overlay-background-color: rgba(8, 9, 10, 0.75); + --pico-progress-background-color: #202632; + --pico-progress-color: var(--pico-primary-background); + --pico-tooltip-background-color: var(--pico-contrast-background); + --pico-tooltip-color: var(--pico-contrast-inverse); + --pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); + --pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(150, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); + color-scheme: dark; +} +[data-theme="dark"] + input:is( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [type="file"] + ) { + --pico-form-element-focus-color: var(--pico-primary-focus); +} +[type="checkbox"], +[type="radio"], +[type="range"], +progress { + accent-color: var(--pico-primary); +} +*, +::after, +::before { + box-sizing: border-box; + background-repeat: no-repeat; +} +::after, +::before { + text-decoration: inherit; + vertical-align: inherit; +} +:where(:root) { + -webkit-tap-highlight-color: transparent; + -webkit-text-size-adjust: 100%; + -moz-text-size-adjust: 100%; + text-size-adjust: 100%; + background-color: var(--pico-background-color); + color: var(--pico-color); + font-weight: var(--pico-font-weight); + font-size: var(--pico-font-size); + line-height: var(--pico-line-height); + font-family: var(--pico-font-family); + text-underline-offset: var(--pico-text-underline-offset); + text-rendering: optimizeLegibility; + overflow-wrap: break-word; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; +} +body { + width: 100%; + margin: 0; +} +main { + display: block; +} +body > footer, +body > header, +body > main { + width: 100%; + margin-right: auto; + margin-left: auto; + padding: var(--pico-block-spacing-vertical) + var(--pico-block-spacing-horizontal); +} +@media (min-width: 576px) { + body > footer, + body > header, + body > main { + max-width: 510px; + padding-right: 0; + padding-left: 0; + } +} +@media (min-width: 768px) { + body > footer, + body > header, + body > main { + max-width: 700px; + } +} +@media (min-width: 1024px) { + body > footer, + body > header, + body > main { + max-width: 950px; + } +} +@media (min-width: 1280px) { + body > footer, + body > header, + body > main { + max-width: 1200px; + } +} +@media (min-width: 1536px) { + body > footer, + body > header, + body > main { + max-width: 1450px; + } +} +section { + margin-bottom: var(--pico-block-spacing-vertical); +} +b, +strong { + font-weight: bolder; +} +sub, +sup { + position: relative; + font-size: 0.75em; + line-height: 0; + vertical-align: baseline; +} +sub { + bottom: -0.25em; +} +sup { + top: -0.5em; +} +address, +blockquote, +dl, +ol, +p, +pre, +table, +ul { + margin-top: 0; + margin-bottom: var(--pico-typography-spacing-vertical); + color: var(--pico-color); + font-style: normal; + font-weight: var(--pico-font-weight); +} +h1, +h2, +h3, +h4, +h5, +h6 { + margin-top: 0; + margin-bottom: var(--pico-typography-spacing-vertical); + color: var(--pico-color); + font-weight: var(--pico-font-weight); + font-size: var(--pico-font-size); + line-height: var(--pico-line-height); + font-family: var(--pico-font-family); +} +h1 { + --pico-color: var(--pico-h1-color); +} +h2 { + --pico-color: var(--pico-h2-color); +} +h3 { + --pico-color: var(--pico-h3-color); +} +h4 { + --pico-color: var(--pico-h4-color); +} +h5 { + --pico-color: var(--pico-h5-color); +} +h6 { + --pico-color: var(--pico-h6-color); +} +:where(article, address, blockquote, dl, figure, form, ol, p, pre, table, ul) + ~ :is(h1, h2, h3, h4, h5, h6) { + margin-top: var(--pico-typography-spacing-top); +} +p { + margin-bottom: var(--pico-typography-spacing-vertical); +} +hgroup { + margin-bottom: var(--pico-typography-spacing-vertical); +} +hgroup > * { + margin-top: 0; + margin-bottom: 0; +} +hgroup > :not(:first-child):last-child { + --pico-color: var(--pico-muted-color); + --pico-font-weight: unset; + font-size: 1rem; +} +:where(ol, ul) li { + margin-bottom: calc(var(--pico-typography-spacing-vertical) * 0.25); +} +:where(dl, ol, ul) :where(dl, ol, ul) { + margin: 0; + margin-top: calc(var(--pico-typography-spacing-vertical) * 0.25); +} +ul li { + list-style: square; +} +mark { + padding: 0.125rem 0.25rem; + background-color: var(--pico-mark-background-color); + color: var(--pico-mark-color); + vertical-align: baseline; +} +blockquote { + display: block; + margin: var(--pico-typography-spacing-vertical) 0; + padding: var(--pico-spacing); + border-right: none; + border-left: 0.25rem solid var(--pico-blockquote-border-color); + border-inline-start: 0.25rem solid var(--pico-blockquote-border-color); + border-inline-end: none; +} +blockquote footer { + margin-top: calc(var(--pico-typography-spacing-vertical) * 0.5); + color: var(--pico-blockquote-footer-color); +} +abbr[title] { + border-bottom: 1px dotted; + text-decoration: none; + cursor: help; +} +ins { + color: var(--pico-ins-color); + text-decoration: none; +} +del { + color: var(--pico-del-color); +} +::-moz-selection { + background-color: var(--pico-text-selection-color); +} +::selection { + background-color: var(--pico-text-selection-color); +} +:where(a:not([role="button"])), +[role="link"] { + --pico-color: var(--pico-primary); + --pico-background-color: transparent; + --pico-underline: var(--pico-primary-underline); + outline: 0; + background-color: var(--pico-background-color); + color: var(--pico-color); + -webkit-text-decoration: var(--pico-text-decoration); + text-decoration: var(--pico-text-decoration); + text-decoration-color: var(--pico-underline); + text-underline-offset: 0.125em; + transition: + background-color var(--pico-transition), + color var(--pico-transition), + box-shadow var(--pico-transition), + -webkit-text-decoration var(--pico-transition); + transition: + background-color var(--pico-transition), + color var(--pico-transition), + text-decoration var(--pico-transition), + box-shadow var(--pico-transition); + transition: + background-color var(--pico-transition), + color var(--pico-transition), + text-decoration var(--pico-transition), + box-shadow var(--pico-transition), + -webkit-text-decoration var(--pico-transition); +} +:where(a:not([role="button"])):is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ), +[role="link"]:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + --pico-color: var(--pico-primary-hover); + --pico-underline: var(--pico-primary-hover-underline); + --pico-text-decoration: underline; +} +:where(a:not([role="button"])):focus-visible, +[role="link"]:focus-visible { + box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-primary-focus); +} +a[role="button"] { + display: inline-block; +} +button { + margin: 0; + overflow: visible; + font-family: inherit; + text-transform: none; +} +[type="button"], +[type="reset"], +[type="submit"], +button { + -webkit-appearance: button; +} +[role="button"], +[type="button"], +[type="file"]::file-selector-button, +[type="reset"], +[type="submit"], +button { + --pico-background-color: var(--pico-primary-background); + --pico-border-color: var(--pico-primary-border); + --pico-color: var(--pico-primary-inverse); + --pico-box-shadow: var(--pico-button-box-shadow, 0 0 0 rgba(0, 0, 0, 0)); + padding: var(--pico-form-element-spacing-vertical) + var(--pico-form-element-spacing-horizontal); + border: var(--pico-border-width) solid var(--pico-border-color); + border-radius: var(--pico-border-radius); + outline: 0; + background-color: var(--pico-background-color); + box-shadow: var(--pico-box-shadow); + color: var(--pico-color); + font-weight: var(--pico-font-weight); + font-size: 1rem; + line-height: var(--pico-line-height); + text-align: center; + text-decoration: none; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + transition: + background-color var(--pico-transition), + border-color var(--pico-transition), + color var(--pico-transition), + box-shadow var(--pico-transition); +} +[role="button"]:is(:hover, :active, :focus), +[role="button"]:is([aria-current]:not([aria-current="false"])), +[type="button"]:is(:hover, :active, :focus), +[type="button"]:is([aria-current]:not([aria-current="false"])), +[type="file"]::file-selector-button:is(:hover, :active, :focus), +[type="file"]::file-selector-button:is( + [aria-current]:not([aria-current="false"]) + ), +[type="reset"]:is(:hover, :active, :focus), +[type="reset"]:is([aria-current]:not([aria-current="false"])), +[type="submit"]:is(:hover, :active, :focus), +[type="submit"]:is([aria-current]:not([aria-current="false"])), +button:is(:hover, :active, :focus), +button:is([aria-current]:not([aria-current="false"])) { + --pico-background-color: var(--pico-primary-hover-background); + --pico-border-color: var(--pico-primary-hover-border); + --pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)); + --pico-color: var(--pico-primary-inverse); +} +[role="button"]:focus, +[role="button"]:is([aria-current]:not([aria-current="false"])):focus, +[type="button"]:focus, +[type="button"]:is([aria-current]:not([aria-current="false"])):focus, +[type="file"]::file-selector-button:focus, +[type="file"]::file-selector-button:is( + [aria-current]:not([aria-current="false"]) + ):focus, +[type="reset"]:focus, +[type="reset"]:is([aria-current]:not([aria-current="false"])):focus, +[type="submit"]:focus, +[type="submit"]:is([aria-current]:not([aria-current="false"])):focus, +button:focus, +button:is([aria-current]:not([aria-current="false"])):focus { + --pico-box-shadow: + var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 + var(--pico-outline-width) var(--pico-primary-focus); +} +[type="button"], +[type="reset"], +[type="submit"] { + margin-bottom: var(--pico-spacing); +} +[type="file"]::file-selector-button, +[type="reset"] { + --pico-background-color: var(--pico-secondary-background); + --pico-border-color: var(--pico-secondary-border); + --pico-color: var(--pico-secondary-inverse); + cursor: pointer; +} +[type="file"]::file-selector-button:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ), +[type="reset"]:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + --pico-background-color: var(--pico-secondary-hover-background); + --pico-border-color: var(--pico-secondary-hover-border); + --pico-color: var(--pico-secondary-inverse); +} +[type="file"]::file-selector-button:focus, +[type="reset"]:focus { + --pico-box-shadow: + var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 + var(--pico-outline-width) var(--pico-secondary-focus); +} +:where( + button, + [type="submit"], + [type="reset"], + [type="button"], + [role="button"] + )[disabled], +:where(fieldset[disabled]) + :is( + button, + [type="submit"], + [type="button"], + [type="reset"], + [role="button"] + ) { + opacity: 0.5; + pointer-events: none; +} +:where(table) { + width: 100%; + border-collapse: collapse; + border-spacing: 0; + text-indent: 0; +} +td, +th { + padding: calc(var(--pico-spacing) / 2) var(--pico-spacing); + border-bottom: var(--pico-border-width) solid var(--pico-table-border-color); + background-color: var(--pico-background-color); + color: var(--pico-color); + font-weight: var(--pico-font-weight); + text-align: left; + text-align: start; +} +tfoot td, +tfoot th { + border-top: var(--pico-border-width) solid var(--pico-table-border-color); + border-bottom: 0; +} +table.striped tbody tr:nth-child(odd) td, +table.striped tbody tr:nth-child(odd) th { + background-color: var(--pico-table-row-stripped-background-color); +} +:where(audio, canvas, iframe, img, svg, video) { + vertical-align: middle; +} +audio, +video { + display: inline-block; +} +audio:not([controls]) { + display: none; + height: 0; +} +:where(iframe) { + border-style: none; +} +img { + max-width: 100%; + height: auto; + border-style: none; +} +:where(svg:not([fill])) { + fill: currentColor; +} +svg:not(:root) { + overflow: hidden; +} +code, +kbd, +pre, +samp { + font-size: 0.875em; + font-family: var(--pico-font-family); +} +pre code { + font-size: inherit; + font-family: inherit; +} +pre { + -ms-overflow-style: scrollbar; + overflow: auto; +} +code, +kbd, +pre { + border-radius: var(--pico-border-radius); + background: var(--pico-code-background-color); + color: var(--pico-code-color); + font-weight: var(--pico-font-weight); + line-height: initial; +} +code, +kbd { + display: inline-block; + padding: 0.375rem; +} +pre { + display: block; + margin-bottom: var(--pico-spacing); + overflow-x: auto; +} +pre > code { + display: block; + padding: var(--pico-spacing); + background: 0 0; + line-height: var(--pico-line-height); +} +kbd { + background-color: var(--pico-code-kbd-background-color); + color: var(--pico-code-kbd-color); + vertical-align: baseline; +} +figure { + display: block; + margin: 0; + padding: 0; +} +figure figcaption { + padding: calc(var(--pico-spacing) * 0.5) 0; + color: var(--pico-muted-color); +} +hr { + height: 0; + margin: var(--pico-typography-spacing-vertical) 0; + border: 0; + border-top: 1px solid var(--pico-muted-border-color); + color: inherit; +} +[hidden], +template { + display: none !important; +} +canvas { + display: inline-block; +} +input, +optgroup, +select, +textarea { + margin: 0; + font-size: 1rem; + line-height: var(--pico-line-height); + font-family: inherit; + letter-spacing: inherit; +} +input { + overflow: visible; +} +select { + text-transform: none; +} +legend { + max-width: 100%; + padding: 0; + color: inherit; + white-space: normal; +} +textarea { + overflow: auto; +} +[type="checkbox"], +[type="radio"] { + padding: 0; +} +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} +[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px; +} +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} +::-moz-focus-inner { + padding: 0; + border-style: none; +} +:-moz-focusring { + outline: 0; +} +:-moz-ui-invalid { + box-shadow: none; +} +::-ms-expand { + display: none; +} +[type="file"], +[type="range"] { + padding: 0; + border-width: 0; +} +input:not([type="checkbox"], [type="radio"], [type="range"]) { + height: calc( + 1rem * + var(--pico-line-height) + + var(--pico-form-element-spacing-vertical) * + 2 + + var(--pico-border-width) * + 2 + ); +} +fieldset { + width: 100%; + margin: 0; + margin-bottom: var(--pico-spacing); + padding: 0; + border: 0; +} +fieldset legend, +label { + display: block; + margin-bottom: calc(var(--pico-spacing) * 0.375); + color: var(--pico-color); + font-weight: var(--pico-form-label-font-weight, var(--pico-font-weight)); +} +fieldset legend { + margin-bottom: calc(var(--pico-spacing) * 0.5); +} +button[type="submit"], +input:not([type="checkbox"], [type="radio"]), +select, +textarea { + width: 100%; +} +input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"]), +select, +textarea { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: var(--pico-form-element-spacing-vertical) + var(--pico-form-element-spacing-horizontal); +} +input, +select, +textarea { + --pico-background-color: var(--pico-form-element-background-color); + --pico-border-color: var(--pico-form-element-border-color); + --pico-color: var(--pico-form-element-color); + --pico-box-shadow: none; + border: var(--pico-border-width) solid var(--pico-border-color); + border-radius: var(--pico-border-radius); + outline: 0; + background-color: var(--pico-background-color); + box-shadow: var(--pico-box-shadow); + color: var(--pico-color); + font-weight: var(--pico-font-weight); + transition: + background-color var(--pico-transition), + border-color var(--pico-transition), + color var(--pico-transition), + box-shadow var(--pico-transition); +} +:where(select, textarea):not([readonly]):is(:active, :focus), +input:not( + [type="submit"], + [type="button"], + [type="reset"], + [type="checkbox"], + [type="radio"], + [readonly] + ):is(:active, :focus) { + --pico-background-color: var(--pico-form-element-active-background-color); +} +:where(select, textarea):not([readonly]):is(:active, :focus), +input:not( + [type="submit"], + [type="button"], + [type="reset"], + [role="switch"], + [readonly] + ):is(:active, :focus) { + --pico-border-color: var(--pico-form-element-active-border-color); +} +:where(select, textarea):not([readonly]):focus, +input:not( + [type="submit"], + [type="button"], + [type="reset"], + [type="range"], + [type="file"], + [readonly] + ):focus { + --pico-box-shadow: 0 0 0 var(--pico-outline-width) + var(--pico-form-element-focus-color); +} +:where(fieldset[disabled]) + :is( + input:not([type="submit"], [type="button"], [type="reset"]), + select, + textarea + ), +input:not([type="submit"], [type="button"], [type="reset"])[disabled], +label[aria-disabled="true"], +select[disabled], +textarea[disabled] { + opacity: var(--pico-form-element-disabled-opacity); + pointer-events: none; +} +label[aria-disabled="true"] input[disabled] { + opacity: 1; +} +:where(input, select, textarea):not( + [type="checkbox"], + [type="radio"], + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"], + [type="range"] + )[aria-invalid] { + padding-right: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem) !important; + padding-left: var(--pico-form-element-spacing-horizontal); + padding-inline-start: var(--pico-form-element-spacing-horizontal) !important; + padding-inline-end: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem) !important; + background-position: center right 0.75rem; + background-size: 1rem auto; + background-repeat: no-repeat; +} +:where(input, select, textarea):not( + [type="checkbox"], + [type="radio"], + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"], + [type="range"] + )[aria-invalid="false"]:not(select) { + background-image: var(--pico-icon-valid); +} +:where(input, select, textarea):not( + [type="checkbox"], + [type="radio"], + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"], + [type="range"] + )[aria-invalid="true"]:not(select) { + background-image: var(--pico-icon-invalid); +} +:where(input, select, textarea)[aria-invalid="false"] { + --pico-border-color: var(--pico-form-element-valid-border-color); +} +:where(input, select, textarea)[aria-invalid="false"]:is(:active, :focus) { + --pico-border-color: var(--pico-form-element-valid-active-border-color) !important; +} +:where(input, select, textarea)[aria-invalid="false"]:is(:active, :focus):not( + [type="checkbox"], + [type="radio"] + ) { + --pico-box-shadow: 0 0 0 var(--pico-outline-width) + var(--pico-form-element-valid-focus-color) !important; +} +:where(input, select, textarea)[aria-invalid="true"] { + --pico-border-color: var(--pico-form-element-invalid-border-color); +} +:where(input, select, textarea)[aria-invalid="true"]:is(:active, :focus) { + --pico-border-color: var(--pico-form-element-invalid-active-border-color) !important; +} +:where(input, select, textarea)[aria-invalid="true"]:is(:active, :focus):not( + [type="checkbox"], + [type="radio"] + ) { + --pico-box-shadow: 0 0 0 var(--pico-outline-width) + var(--pico-form-element-invalid-focus-color) !important; +} +[dir="rtl"] + :where(input, select, textarea):not([type="checkbox"], [type="radio"]):is( + [aria-invalid], + [aria-invalid="true"], + [aria-invalid="false"] + ) { + background-position: center left 0.75rem; +} +input::-webkit-input-placeholder, +input::placeholder, +select:invalid, +textarea::-webkit-input-placeholder, +textarea::placeholder { + color: var(--pico-form-element-placeholder-color); + opacity: 1; +} +input:not([type="checkbox"], [type="radio"]), +select, +textarea { + margin-bottom: var(--pico-spacing); +} +select::-ms-expand { + border: 0; + background-color: transparent; +} +select:not([multiple], [size]) { + padding-right: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem); + padding-left: var(--pico-form-element-spacing-horizontal); + padding-inline-start: var(--pico-form-element-spacing-horizontal); + padding-inline-end: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem); + background-image: var(--pico-icon-chevron); + background-position: center right 0.75rem; + background-size: 1rem auto; + background-repeat: no-repeat; +} +select[multiple] option:checked { + background: var(--pico-form-element-selected-background-color); + color: var(--pico-form-element-color); +} +[dir="rtl"] select:not([multiple], [size]) { + background-position: center left 0.75rem; +} +textarea { + display: block; + resize: vertical; +} +textarea[aria-invalid] { + --pico-icon-height: calc( + 1rem * + var(--pico-line-height) + + var(--pico-form-element-spacing-vertical) * + 2 + + var(--pico-border-width) * + 2 + ); + background-position: top right 0.75rem !important; + background-size: 1rem var(--pico-icon-height) !important; +} +:where(input, select, textarea, fieldset) + small { + display: block; + width: 100%; + margin-top: calc(var(--pico-spacing) * -0.75); + margin-bottom: var(--pico-spacing); + color: var(--pico-muted-color); +} +:where(input, select, textarea, fieldset)[aria-invalid="false"] + small { + color: var(--pico-ins-color); +} +:where(input, select, textarea, fieldset)[aria-invalid="true"] + small { + color: var(--pico-del-color); +} +label > :where(input, select, textarea) { + margin-top: calc(var(--pico-spacing) * 0.25); +} +label:has([type="checkbox"], [type="radio"]) { + width: -moz-fit-content; + width: fit-content; + cursor: pointer; +} +[type="checkbox"], +[type="radio"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: 1.25em; + height: 1.25em; + margin-top: -0.125em; + margin-inline-end: 0.5em; + border-width: var(--pico-border-width); + vertical-align: middle; + cursor: pointer; +} +[type="checkbox"]::-ms-check, +[type="radio"]::-ms-check { + display: none; +} +[type="checkbox"]:checked, +[type="checkbox"]:checked:active, +[type="checkbox"]:checked:focus, +[type="radio"]:checked, +[type="radio"]:checked:active, +[type="radio"]:checked:focus { + --pico-background-color: var(--pico-primary-background); + --pico-border-color: var(--pico-primary-border); + background-image: var(--pico-icon-checkbox); + background-position: center; + background-size: 0.75em auto; + background-repeat: no-repeat; +} +[type="checkbox"] ~ label, +[type="radio"] ~ label { + display: inline-block; + margin-bottom: 0; + cursor: pointer; +} +[type="checkbox"] ~ label:not(:last-of-type), +[type="radio"] ~ label:not(:last-of-type) { + margin-inline-end: 1em; +} +[type="checkbox"]:indeterminate { + --pico-background-color: var(--pico-primary-background); + --pico-border-color: var(--pico-primary-border); + background-image: var(--pico-icon-minus); + background-position: center; + background-size: 0.75em auto; + background-repeat: no-repeat; +} +[type="radio"] { + border-radius: 50%; +} +[type="radio"]:checked, +[type="radio"]:checked:active, +[type="radio"]:checked:focus { + --pico-background-color: var(--pico-primary-inverse); + border-width: 0.35em; + background-image: none; +} +[type="checkbox"][role="switch"] { + --pico-background-color: var(--pico-switch-background-color); + --pico-color: var(--pico-switch-color); + width: 2.25em; + height: 1.25em; + border: var(--pico-border-width) solid var(--pico-border-color); + border-radius: 1.25em; + background-color: var(--pico-background-color); + line-height: 1.25em; +} +[type="checkbox"][role="switch"]:not([aria-invalid]) { + --pico-border-color: var(--pico-switch-background-color); +} +[type="checkbox"][role="switch"]:before { + display: block; + aspect-ratio: 1; + height: 100%; + border-radius: 50%; + background-color: var(--pico-color); + box-shadow: var(--pico-switch-thumb-box-shadow); + content: ""; + transition: margin 0.1s ease-in-out; +} +[type="checkbox"][role="switch"]:focus { + --pico-background-color: var(--pico-switch-background-color); + --pico-border-color: var(--pico-switch-background-color); +} +[type="checkbox"][role="switch"]:checked { + --pico-background-color: var(--pico-switch-checked-background-color); + --pico-border-color: var(--pico-switch-checked-background-color); + background-image: none; +} +[type="checkbox"][role="switch"]:checked::before { + margin-inline-start: calc(2.25em - 1.25em); +} +[type="checkbox"][role="switch"][disabled] { + --pico-background-color: var(--pico-border-color); +} +[type="checkbox"][aria-invalid="false"]:checked, +[type="checkbox"][aria-invalid="false"]:checked:active, +[type="checkbox"][aria-invalid="false"]:checked:focus, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked:active, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked:focus { + --pico-background-color: var(--pico-form-element-valid-border-color); +} +[type="checkbox"]:checked:active[aria-invalid="true"], +[type="checkbox"]:checked:focus[aria-invalid="true"], +[type="checkbox"]:checked[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked:active[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked:focus[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked[aria-invalid="true"] { + --pico-background-color: var(--pico-form-element-invalid-border-color); +} +[type="checkbox"][aria-invalid="false"]:checked, +[type="checkbox"][aria-invalid="false"]:checked:active, +[type="checkbox"][aria-invalid="false"]:checked:focus, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked:active, +[type="checkbox"][role="switch"][aria-invalid="false"]:checked:focus, +[type="radio"][aria-invalid="false"]:checked, +[type="radio"][aria-invalid="false"]:checked:active, +[type="radio"][aria-invalid="false"]:checked:focus { + --pico-border-color: var(--pico-form-element-valid-border-color); +} +[type="checkbox"]:checked:active[aria-invalid="true"], +[type="checkbox"]:checked:focus[aria-invalid="true"], +[type="checkbox"]:checked[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked:active[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked:focus[aria-invalid="true"], +[type="checkbox"][role="switch"]:checked[aria-invalid="true"], +[type="radio"]:checked:active[aria-invalid="true"], +[type="radio"]:checked:focus[aria-invalid="true"], +[type="radio"]:checked[aria-invalid="true"] { + --pico-border-color: var(--pico-form-element-invalid-border-color); +} +[type="color"]::-webkit-color-swatch-wrapper { + padding: 0; +} +[type="color"]::-moz-focus-inner { + padding: 0; +} +[type="color"]::-webkit-color-swatch { + border: 0; + border-radius: calc(var(--pico-border-radius) * 0.5); +} +[type="color"]::-moz-color-swatch { + border: 0; + border-radius: calc(var(--pico-border-radius) * 0.5); +} +input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"]):is( + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"] + ) { + --pico-icon-position: 0.75rem; + --pico-icon-width: 1rem; + padding-right: calc(var(--pico-icon-width) + var(--pico-icon-position)); + background-image: var(--pico-icon-date); + background-position: center right var(--pico-icon-position); + background-size: var(--pico-icon-width) auto; + background-repeat: no-repeat; +} +input:not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="time"] { + background-image: var(--pico-icon-time); +} +[type="date"]::-webkit-calendar-picker-indicator, +[type="datetime-local"]::-webkit-calendar-picker-indicator, +[type="month"]::-webkit-calendar-picker-indicator, +[type="time"]::-webkit-calendar-picker-indicator, +[type="week"]::-webkit-calendar-picker-indicator { + width: var(--pico-icon-width); + margin-right: calc(var(--pico-icon-width) * -1); + margin-left: var(--pico-icon-position); + opacity: 0; +} +@-moz-document url-prefix() { + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"] { + padding-right: var(--pico-form-element-spacing-horizontal) !important; + background-image: none !important; + } +} +[dir="rtl"] + :is( + [type="date"], + [type="datetime-local"], + [type="month"], + [type="time"], + [type="week"] + ) { + text-align: right; +} +[type="file"] { + --pico-color: var(--pico-muted-color); + margin-left: calc(var(--pico-outline-width) * -1); + padding: calc(var(--pico-form-element-spacing-vertical) * 0.5) 0; + padding-left: var(--pico-outline-width); + border: 0; + border-radius: 0; + background: 0 0; +} +[type="file"]::file-selector-button { + margin-right: calc(var(--pico-spacing) / 2); + padding: calc(var(--pico-form-element-spacing-vertical) * 0.5) + var(--pico-form-element-spacing-horizontal); +} +[type="file"]:is(:hover, :active, :focus)::file-selector-button { + --pico-background-color: var(--pico-secondary-hover-background); + --pico-border-color: var(--pico-secondary-hover-border); +} +[type="file"]:focus::file-selector-button { + --pico-box-shadow: + var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 + var(--pico-outline-width) var(--pico-secondary-focus); +} +[type="range"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: 100%; + height: 1.25rem; + background: 0 0; +} +[type="range"]::-webkit-slider-runnable-track { + width: 100%; + height: 0.375rem; + border-radius: var(--pico-border-radius); + background-color: var(--pico-range-border-color); + -webkit-transition: + background-color var(--pico-transition), + box-shadow var(--pico-transition); + transition: + background-color var(--pico-transition), + box-shadow var(--pico-transition); +} +[type="range"]::-moz-range-track { + width: 100%; + height: 0.375rem; + border-radius: var(--pico-border-radius); + background-color: var(--pico-range-border-color); + -moz-transition: + background-color var(--pico-transition), + box-shadow var(--pico-transition); + transition: + background-color var(--pico-transition), + box-shadow var(--pico-transition); +} +[type="range"]::-ms-track { + width: 100%; + height: 0.375rem; + border-radius: var(--pico-border-radius); + background-color: var(--pico-range-border-color); + -ms-transition: + background-color var(--pico-transition), + box-shadow var(--pico-transition); + transition: + background-color var(--pico-transition), + box-shadow var(--pico-transition); +} +[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + width: 1.25rem; + height: 1.25rem; + margin-top: -0.4375rem; + border: 2px solid var(--pico-range-thumb-border-color); + border-radius: 50%; + background-color: var(--pico-range-thumb-color); + cursor: pointer; + -webkit-transition: + background-color var(--pico-transition), + transform var(--pico-transition); + transition: + background-color var(--pico-transition), + transform var(--pico-transition); +} +[type="range"]::-moz-range-thumb { + -webkit-appearance: none; + width: 1.25rem; + height: 1.25rem; + margin-top: -0.4375rem; + border: 2px solid var(--pico-range-thumb-border-color); + border-radius: 50%; + background-color: var(--pico-range-thumb-color); + cursor: pointer; + -moz-transition: + background-color var(--pico-transition), + transform var(--pico-transition); + transition: + background-color var(--pico-transition), + transform var(--pico-transition); +} +[type="range"]::-ms-thumb { + -webkit-appearance: none; + width: 1.25rem; + height: 1.25rem; + margin-top: -0.4375rem; + border: 2px solid var(--pico-range-thumb-border-color); + border-radius: 50%; + background-color: var(--pico-range-thumb-color); + cursor: pointer; + -ms-transition: + background-color var(--pico-transition), + transform var(--pico-transition); + transition: + background-color var(--pico-transition), + transform var(--pico-transition); +} +[type="range"]:active, +[type="range"]:focus-within { + --pico-range-border-color: var(--pico-range-active-border-color); + --pico-range-thumb-color: var(--pico-range-thumb-active-color); +} +[type="range"]:active::-webkit-slider-thumb { + transform: scale(1.25); +} +[type="range"]:active::-moz-range-thumb { + transform: scale(1.25); +} +[type="range"]:active::-ms-thumb { + transform: scale(1.25); +} +input:not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"] { + padding-inline-start: calc( + var(--pico-form-element-spacing-horizontal) + + 1.75rem + ); + background-image: var(--pico-icon-search); + background-position: center left + calc(var(--pico-form-element-spacing-horizontal) + 0.125rem); + background-size: 1rem auto; + background-repeat: no-repeat; +} +input:not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"][aria-invalid] { + padding-inline-start: calc( + var(--pico-form-element-spacing-horizontal) + + 1.75rem + ) !important; + background-position: + center left 1.125rem, + center right 0.75rem; +} +input:not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"][aria-invalid="false"] { + background-image: var(--pico-icon-search), var(--pico-icon-valid); +} +input:not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"][aria-invalid="true"] { + background-image: var(--pico-icon-search), var(--pico-icon-invalid); +} +[dir="rtl"] + :where(input):not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"] { + background-position: center right 1.125rem; +} +[dir="rtl"] + :where(input):not( + [type="checkbox"], + [type="radio"], + [type="range"], + [type="file"] + )[type="search"][aria-invalid] { + background-position: + center right 1.125rem, + center left 0.75rem; +} +details { + display: block; + margin-bottom: var(--pico-spacing); +} +details summary { + line-height: 1rem; + list-style-type: none; + cursor: pointer; + transition: color var(--pico-transition); +} +details summary:not([role]) { + color: var(--pico-accordion-close-summary-color); +} +details summary::-webkit-details-marker { + display: none; +} +details summary::marker { + display: none; +} +details summary::-moz-list-bullet { + list-style-type: none; +} +details summary::after { + display: block; + width: 1rem; + height: 1rem; + margin-inline-start: calc(var(--pico-spacing, 1rem) * 0.5); + float: right; + transform: rotate(-90deg); + background-image: var(--pico-icon-chevron); + background-position: right center; + background-size: 1rem auto; + background-repeat: no-repeat; + content: ""; + transition: transform var(--pico-transition); +} +details summary:focus { + outline: 0; +} +details summary:focus:not([role]) { + color: var(--pico-accordion-active-summary-color); +} +details summary:focus-visible:not([role]) { + outline: var(--pico-outline-width) solid var(--pico-primary-focus); + outline-offset: calc(var(--pico-spacing, 1rem) * 0.5); + color: var(--pico-primary); +} +details summary[role="button"] { + width: 100%; + text-align: left; +} +details summary[role="button"]::after { + height: calc(1rem * var(--pico-line-height, 1.5)); +} +details[open] > summary { + margin-bottom: var(--pico-spacing); +} +details[open] > summary:not([role]):not(:focus) { + color: var(--pico-accordion-open-summary-color); +} +details[open] > summary::after { + transform: rotate(0); +} +[dir="rtl"] details summary { + text-align: right; +} +[dir="rtl"] details summary::after { + float: left; + background-position: left center; +} +article { + margin-bottom: var(--pico-block-spacing-vertical); + padding: var(--pico-block-spacing-vertical) + var(--pico-block-spacing-horizontal); + border-radius: var(--pico-border-radius); + background: var(--pico-card-background-color); + box-shadow: var(--pico-card-box-shadow); +} +article > footer, +article > header { + margin-right: calc(var(--pico-block-spacing-horizontal) * -1); + margin-left: calc(var(--pico-block-spacing-horizontal) * -1); + padding: calc(var(--pico-block-spacing-vertical) * 0.66) + var(--pico-block-spacing-horizontal); + background-color: var(--pico-card-sectioning-background-color); +} +article > header { + margin-top: calc(var(--pico-block-spacing-vertical) * -1); + margin-bottom: var(--pico-block-spacing-vertical); + border-bottom: var(--pico-border-width) solid var(--pico-card-border-color); + border-top-right-radius: var(--pico-border-radius); + border-top-left-radius: var(--pico-border-radius); +} +article > footer { + margin-top: var(--pico-block-spacing-vertical); + margin-bottom: calc(var(--pico-block-spacing-vertical) * -1); + border-top: var(--pico-border-width) solid var(--pico-card-border-color); + border-bottom-right-radius: var(--pico-border-radius); + border-bottom-left-radius: var(--pico-border-radius); +} +[role="group"], +[role="search"] { + display: inline-flex; + position: relative; + width: 100%; + margin-bottom: var(--pico-spacing); + border-radius: var(--pico-border-radius); + box-shadow: var(--pico-group-box-shadow, 0 0 0 transparent); + vertical-align: middle; + transition: box-shadow var(--pico-transition); +} +[role="group"] input:not([type="checkbox"], [type="radio"]), +[role="group"] select, +[role="group"] > *, +[role="search"] input:not([type="checkbox"], [type="radio"]), +[role="search"] select, +[role="search"] > * { + position: relative; + flex: 1 1 auto; + margin-bottom: 0; +} +[role="group"] input:not([type="checkbox"], [type="radio"]):not(:first-child), +[role="group"] select:not(:first-child), +[role="group"] > :not(:first-child), +[role="search"] input:not([type="checkbox"], [type="radio"]):not(:first-child), +[role="search"] select:not(:first-child), +[role="search"] > :not(:first-child) { + margin-left: 0; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +[role="group"] input:not([type="checkbox"], [type="radio"]):not(:last-child), +[role="group"] select:not(:last-child), +[role="group"] > :not(:last-child), +[role="search"] input:not([type="checkbox"], [type="radio"]):not(:last-child), +[role="search"] select:not(:last-child), +[role="search"] > :not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +[role="group"] input:not([type="checkbox"], [type="radio"]):focus, +[role="group"] select:focus, +[role="group"] > :focus, +[role="search"] input:not([type="checkbox"], [type="radio"]):focus, +[role="search"] select:focus, +[role="search"] > :focus { + z-index: 2; +} +[role="group"] [role="button"]:not(:first-child), +[role="group"] [type="button"]:not(:first-child), +[role="group"] [type="reset"]:not(:first-child), +[role="group"] [type="submit"]:not(:first-child), +[role="group"] button:not(:first-child), +[role="group"] input:not([type="checkbox"], [type="radio"]):not(:first-child), +[role="group"] select:not(:first-child), +[role="search"] [role="button"]:not(:first-child), +[role="search"] [type="button"]:not(:first-child), +[role="search"] [type="reset"]:not(:first-child), +[role="search"] [type="submit"]:not(:first-child), +[role="search"] button:not(:first-child), +[role="search"] input:not([type="checkbox"], [type="radio"]):not(:first-child), +[role="search"] select:not(:first-child) { + margin-left: calc(var(--pico-border-width) * -1); +} +[role="group"] [role="button"], +[role="group"] [type="button"], +[role="group"] [type="reset"], +[role="group"] [type="submit"], +[role="group"] button, +[role="search"] [role="button"], +[role="search"] [type="button"], +[role="search"] [type="reset"], +[role="search"] [type="submit"], +[role="search"] button { + width: auto; +} +@supports selector(:has(*)) { + [role="group"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ), + [role="search"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ) { + --pico-group-box-shadow: var(--pico-group-box-shadow-focus-with-button); + } + [role="group"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ) + input:not([type="checkbox"], [type="radio"]), + [role="group"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ) + select, + [role="search"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ) + input:not([type="checkbox"], [type="radio"]), + [role="search"]:has( + button:focus, + [type="submit"]:focus, + [type="button"]:focus, + [role="button"]:focus + ) + select { + border-color: transparent; + } + [role="group"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ), + [role="search"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) { + --pico-group-box-shadow: var(--pico-group-box-shadow-focus-with-input); + } + [role="group"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [role="button"], + [role="group"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [type="button"], + [role="group"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [type="submit"], + [role="group"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + button, + [role="search"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [role="button"], + [role="search"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [type="button"], + [role="search"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + [type="submit"], + [role="search"]:has( + input:not([type="submit"], [type="button"]):focus, + select:focus + ) + button { + --pico-button-box-shadow: 0 0 0 var(--pico-border-width) + var(--pico-primary-border); + --pico-button-hover-box-shadow: 0 0 0 var(--pico-border-width) + var(--pico-primary-hover-border); + } + [role="group"] [role="button"]:focus, + [role="group"] [type="button"]:focus, + [role="group"] [type="reset"]:focus, + [role="group"] [type="submit"]:focus, + [role="group"] button:focus, + [role="search"] [role="button"]:focus, + [role="search"] [type="button"]:focus, + [role="search"] [type="reset"]:focus, + [role="search"] [type="submit"]:focus, + [role="search"] button:focus { + box-shadow: none; + } +} +[role="search"] > :first-child { + border-top-left-radius: 5rem; + border-bottom-left-radius: 5rem; +} +[role="search"] > :last-child { + border-top-right-radius: 5rem; + border-bottom-right-radius: 5rem; +} +[aria-busy="true"]:not(input, select, textarea, html) { + white-space: nowrap; +} +[aria-busy="true"]:not(input, select, textarea, html)::before { + display: inline-block; + width: 1em; + height: 1em; + background-image: var(--pico-icon-loading); + background-size: 1em auto; + background-repeat: no-repeat; + content: ""; + vertical-align: -0.125em; +} +[aria-busy="true"]:not(input, select, textarea, html):not(:empty)::before { + margin-inline-end: calc(var(--pico-spacing) * 0.5); +} +[aria-busy="true"]:not(input, select, textarea, html):empty { + text-align: center; +} +[role="button"][aria-busy="true"], +[type="button"][aria-busy="true"], +[type="reset"][aria-busy="true"], +[type="submit"][aria-busy="true"], +a[aria-busy="true"], +button[aria-busy="true"] { + pointer-events: none; +} +:root { + --pico-scrollbar-width: 0px; +} +dialog { + display: flex; + z-index: 999; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + align-items: center; + justify-content: center; + width: inherit; + min-width: 100%; + height: inherit; + min-height: 100%; + padding: 0; + border: 0; + -webkit-backdrop-filter: var(--pico-modal-overlay-backdrop-filter); + backdrop-filter: var(--pico-modal-overlay-backdrop-filter); + background-color: var(--pico-modal-overlay-background-color); + color: var(--pico-color); +} +dialog article { + width: 100%; + max-height: calc(100vh - var(--pico-spacing) * 2); + margin: var(--pico-spacing); + overflow: auto; +} +@media (min-width: 576px) { + dialog article { + max-width: 510px; + } +} +@media (min-width: 768px) { + dialog article { + max-width: 700px; + } +} +dialog article > header > * { + margin-bottom: 0; +} +dialog article > header :is(a, button)[rel="prev"] { + margin: 0; + margin-left: var(--pico-spacing); + padding: 0; + float: right; +} +dialog article > footer { + text-align: right; +} +dialog article > footer [role="button"], +dialog article > footer button { + margin-bottom: 0; +} +dialog article > footer [role="button"]:not(:first-of-type), +dialog article > footer button:not(:first-of-type) { + margin-left: calc(var(--pico-spacing) * 0.5); +} +dialog article :is(a, button)[rel="prev"] { + display: block; + width: 1rem; + height: 1rem; + margin-top: calc(var(--pico-spacing) * -1); + margin-bottom: var(--pico-spacing); + margin-left: auto; + border: none; + background-image: var(--pico-icon-close); + background-position: center; + background-size: auto 1rem; + background-repeat: no-repeat; + background-color: transparent; + opacity: 0.5; + transition: opacity var(--pico-transition); +} +dialog + article + :is(a, button)[rel="prev"]:is( + [aria-current]:not([aria-current="false"]), + :hover, + :active, + :focus + ) { + opacity: 1; +} +dialog:not([open]), +dialog[open="false"] { + display: none; +} +:where(nav li)::before { + float: left; + content: "​"; +} +nav, +nav ul { + display: flex; +} +nav { + justify-content: space-between; + overflow: visible; +} +nav ol, +nav ul { + align-items: center; + margin-bottom: 0; + padding: 0; + list-style: none; +} +nav ol:first-of-type, +nav ul:first-of-type { + margin-left: calc(var(--pico-nav-element-spacing-horizontal) * -1); +} +nav ol:last-of-type, +nav ul:last-of-type { + margin-right: calc(var(--pico-nav-element-spacing-horizontal) * -1); +} +nav li { + display: inline-block; + margin: 0; + padding: var(--pico-nav-element-spacing-vertical) + var(--pico-nav-element-spacing-horizontal); +} +nav li :where(a, [role="link"]) { + display: inline-block; + margin: calc(var(--pico-nav-link-spacing-vertical) * -1) + calc(var(--pico-nav-link-spacing-horizontal) * -1); + padding: var(--pico-nav-link-spacing-vertical) + var(--pico-nav-link-spacing-horizontal); + border-radius: var(--pico-border-radius); +} +nav li :where(a, [role="link"]):not(:hover) { + text-decoration: none; +} +nav li [role="button"], +nav li [type="button"], +nav li button, +nav + li + input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"]), +nav li select { + height: auto; + margin-right: inherit; + margin-bottom: 0; + margin-left: inherit; + padding: calc( + var(--pico-nav-link-spacing-vertical) - + var(--pico-border-width) * + 2 + ) + var(--pico-nav-link-spacing-horizontal); +} +nav[aria-label="breadcrumb"] { + align-items: center; + justify-content: start; +} +nav[aria-label="breadcrumb"] ul li:not(:first-child) { + margin-inline-start: var(--pico-nav-link-spacing-horizontal); +} +nav[aria-label="breadcrumb"] ul li a { + margin: calc(var(--pico-nav-link-spacing-vertical) * -1) 0; + margin-inline-start: calc(var(--pico-nav-link-spacing-horizontal) * -1); +} +nav[aria-label="breadcrumb"] ul li:not(:last-child)::after { + display: inline-block; + position: absolute; + width: calc(var(--pico-nav-link-spacing-horizontal) * 4); + margin: 0 calc(var(--pico-nav-link-spacing-horizontal) * -1); + content: var(--pico-nav-breadcrumb-divider); + color: var(--pico-muted-color); + text-align: center; + text-decoration: none; + white-space: nowrap; +} +nav[aria-label="breadcrumb"] a[aria-current]:not([aria-current="false"]) { + background-color: transparent; + color: inherit; + text-decoration: none; + pointer-events: none; +} +aside li, +aside nav, +aside ol, +aside ul { + display: block; +} +aside li { + padding: calc(var(--pico-nav-element-spacing-vertical) * 0.5) + var(--pico-nav-element-spacing-horizontal); +} +aside li a { + display: block; +} +aside li [role="button"] { + margin: inherit; +} +[dir="rtl"] nav[aria-label="breadcrumb"] ul li:not(:last-child) ::after { + content: "\\"; +} +progress { + display: inline-block; + vertical-align: baseline; +} +progress { + -webkit-appearance: none; + -moz-appearance: none; + display: inline-block; + appearance: none; + width: 100%; + height: 0.5rem; + margin-bottom: calc(var(--pico-spacing) * 0.5); + overflow: hidden; + border: 0; + border-radius: var(--pico-border-radius); + background-color: var(--pico-progress-background-color); + color: var(--pico-progress-color); +} +progress::-webkit-progress-bar { + border-radius: var(--pico-border-radius); + background: 0 0; +} +progress[value]::-webkit-progress-value { + background-color: var(--pico-progress-color); + -webkit-transition: inline-size var(--pico-transition); + transition: inline-size var(--pico-transition); +} +progress::-moz-progress-bar { + background-color: var(--pico-progress-color); +} +@media (prefers-reduced-motion: no-preference) { + progress:indeterminate { + background: + var(--pico-progress-background-color) + linear-gradient( + to right, + var(--pico-progress-color) 30%, + var(--pico-progress-background-color) 30% + ) + top left / 150% 150% no-repeat; + animation: progress-indeterminate 1s linear infinite; + } + progress:indeterminate[value]::-webkit-progress-value { + background-color: transparent; + } + progress:indeterminate::-moz-progress-bar { + background-color: transparent; + } +} +@media (prefers-reduced-motion: no-preference) { + [dir="rtl"] progress:indeterminate { + animation-direction: reverse; + } +} +@keyframes progress-indeterminate { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} +[data-tooltip] { + position: relative; +} +[data-tooltip]:not(a, button, input) { + border-bottom: 1px dotted; + text-decoration: none; + cursor: help; +} +[data-tooltip]::after, +[data-tooltip]::before, +[data-tooltip][data-placement="top"]::after, +[data-tooltip][data-placement="top"]::before { + display: block; + z-index: 99; + position: absolute; + bottom: 100%; + left: 50%; + padding: 0.25rem 0.5rem; + overflow: hidden; + transform: translate(-50%, -0.25rem); + border-radius: var(--pico-border-radius); + background: var(--pico-tooltip-background-color); + content: attr(data-tooltip); + color: var(--pico-tooltip-color); + font-style: normal; + font-weight: var(--pico-font-weight); + font-size: 0.875rem; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; + opacity: 0; + pointer-events: none; +} +[data-tooltip]::after, +[data-tooltip][data-placement="top"]::after { + padding: 0; + transform: translate(-50%, 0); + border-top: 0.3rem solid; + border-right: 0.3rem solid transparent; + border-left: 0.3rem solid transparent; + border-radius: 0; + background-color: transparent; + content: ""; + color: var(--pico-tooltip-background-color); +} +[data-tooltip][data-placement="bottom"]::after, +[data-tooltip][data-placement="bottom"]::before { + top: 100%; + bottom: auto; + transform: translate(-50%, 0.25rem); +} +[data-tooltip][data-placement="bottom"]:after { + transform: translate(-50%, -0.3rem); + border: 0.3rem solid transparent; + border-bottom: 0.3rem solid; +} +[data-tooltip][data-placement="left"]::after, +[data-tooltip][data-placement="left"]::before { + top: 50%; + right: 100%; + bottom: auto; + left: auto; + transform: translate(-0.25rem, -50%); +} +[data-tooltip][data-placement="left"]:after { + transform: translate(0.3rem, -50%); + border: 0.3rem solid transparent; + border-left: 0.3rem solid; +} +[data-tooltip][data-placement="right"]::after, +[data-tooltip][data-placement="right"]::before { + top: 50%; + right: auto; + bottom: auto; + left: 100%; + transform: translate(0.25rem, -50%); +} +[data-tooltip][data-placement="right"]:after { + transform: translate(-0.3rem, -50%); + border: 0.3rem solid transparent; + border-right: 0.3rem solid; +} +[data-tooltip]:focus::after, +[data-tooltip]:focus::before, +[data-tooltip]:hover::after, +[data-tooltip]:hover::before { + opacity: 1; +} +@media (hover: hover) and (pointer: fine) { + [data-tooltip]:focus::after, + [data-tooltip]:focus::before, + [data-tooltip]:hover::after, + [data-tooltip]:hover::before { + --pico-tooltip-slide-to: translate(-50%, -0.25rem); + transform: translate(-50%, 0.75rem); + animation-duration: 0.2s; + animation-fill-mode: forwards; + animation-name: tooltip-slide; + opacity: 0; + } + [data-tooltip]:focus::after, + [data-tooltip]:hover::after { + --pico-tooltip-caret-slide-to: translate(-50%, 0rem); + transform: translate(-50%, -0.25rem); + animation-name: tooltip-caret-slide; + } + [data-tooltip][data-placement="bottom"]:focus::after, + [data-tooltip][data-placement="bottom"]:focus::before, + [data-tooltip][data-placement="bottom"]:hover::after, + [data-tooltip][data-placement="bottom"]:hover::before { + --pico-tooltip-slide-to: translate(-50%, 0.25rem); + transform: translate(-50%, -0.75rem); + animation-name: tooltip-slide; + } + [data-tooltip][data-placement="bottom"]:focus::after, + [data-tooltip][data-placement="bottom"]:hover::after { + --pico-tooltip-caret-slide-to: translate(-50%, -0.3rem); + transform: translate(-50%, -0.5rem); + animation-name: tooltip-caret-slide; + } + [data-tooltip][data-placement="left"]:focus::after, + [data-tooltip][data-placement="left"]:focus::before, + [data-tooltip][data-placement="left"]:hover::after, + [data-tooltip][data-placement="left"]:hover::before { + --pico-tooltip-slide-to: translate(-0.25rem, -50%); + transform: translate(0.75rem, -50%); + animation-name: tooltip-slide; + } + [data-tooltip][data-placement="left"]:focus::after, + [data-tooltip][data-placement="left"]:hover::after { + --pico-tooltip-caret-slide-to: translate(0.3rem, -50%); + transform: translate(0.05rem, -50%); + animation-name: tooltip-caret-slide; + } + [data-tooltip][data-placement="right"]:focus::after, + [data-tooltip][data-placement="right"]:focus::before, + [data-tooltip][data-placement="right"]:hover::after, + [data-tooltip][data-placement="right"]:hover::before { + --pico-tooltip-slide-to: translate(0.25rem, -50%); + transform: translate(-0.75rem, -50%); + animation-name: tooltip-slide; + } + [data-tooltip][data-placement="right"]:focus::after, + [data-tooltip][data-placement="right"]:hover::after { + --pico-tooltip-caret-slide-to: translate(-0.3rem, -50%); + transform: translate(-0.05rem, -50%); + animation-name: tooltip-caret-slide; + } +} +@keyframes tooltip-slide { + to { + transform: var(--pico-tooltip-slide-to); + opacity: 1; + } +} +@keyframes tooltip-caret-slide { + 50% { + opacity: 0; + } + to { + transform: var(--pico-tooltip-caret-slide-to); + opacity: 1; + } +} +[aria-controls] { + cursor: pointer; +} +[aria-disabled="true"], +[disabled] { + cursor: not-allowed; +} +[aria-hidden="false"][hidden] { + display: initial; +} +[aria-hidden="false"][hidden]:not(:focus) { + clip: rect(0, 0, 0, 0); + position: absolute; +} +[tabindex], +a, +area, +button, +input, +label, +select, +summary, +textarea { + -ms-touch-action: manipulation; +} +[dir="rtl"] { + direction: rtl; +} +@media (prefers-reduced-motion: reduce) { + :not([aria-busy="true"]), + :not([aria-busy="true"])::after, + :not([aria-busy="true"])::before { + background-attachment: initial !important; + animation-duration: 1ms !important; + animation-delay: -1ms !important; + animation-iteration-count: 1 !important; + scroll-behavior: auto !important; + transition-delay: 0s !important; + transition-duration: 0s !important; + } +} diff --git a/src/chat.js b/src/chat.js index 3f26b11..732f06f 100644 --- a/src/chat.js +++ b/src/chat.js @@ -1,156 +1,156 @@ // Chat functionality and message handling import { marked } from "https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js"; -import hljs from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/es/highlight.min.js'; -import { API_KEY } from './config.js'; -import { getSelectedModel, getSelectedModelEndpoint } from './models.js'; -import { speakText, generateTitleForTTS } from './tts.js'; -import { saveConversationHistory, initializeChatHistory } from './storage.js'; +import hljs from "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/es/highlight.min.js"; +import { API_KEY } from "./config.js"; +import { getSelectedModel, getSelectedModelEndpoint } from "./models.js"; +import { speakText, generateTitleForTTS } from "./tts.js"; +import { saveConversationHistory, initializeChatHistory } from "./storage.js"; // Initialize chat history export let chatHistory = initializeChatHistory(); // Generator function to send a message to the LLM and yield responses export async function* sendMessage(message) { - chatHistory.push({ role: "user", content: message }); + chatHistory.push({ role: "user", content: message }); - // Dynamically determine the API URL based on the selected model. - // (Assuming that the chat completions endpoint is at "/chat/completions") - const apiUrl = `${getSelectedModelEndpoint()}/chat/completions`; + // Dynamically determine the API URL based on the selected model. + // (Assuming that the chat completions endpoint is at "/chat/completions") + const apiUrl = `${getSelectedModelEndpoint()}/chat/completions`; - const response = await fetch(apiUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${API_KEY}` - }, - body: JSON.stringify({ - model: getSelectedModel(), - messages: chatHistory, - temperature: 0.5, - max_tokens: 8192, - stream: true - }) - }); + const response = await fetch(apiUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${API_KEY}`, + }, + body: JSON.stringify({ + model: getSelectedModel(), + messages: chatHistory, + temperature: 0.5, + max_tokens: 8192, + stream: true, + }), + }); - const reader = response.body.getReader(); - const decoder = new TextDecoder(); - let buffer = ''; + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; - while (true) { - const { done, value } = await reader.read(); - if (done) break; + while (true) { + const { done, value } = await reader.read(); + if (done) break; - buffer += decoder.decode(value, { stream: true }); - const lines = buffer.split('\n'); + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split("\n"); - for (let i = 0; i < lines.length - 1; i++) { - const line = lines[i].trim(); - if (line.startsWith('data: ')) { - const jsonData = line.slice(6); - if (jsonData === '[DONE]') continue; + for (let i = 0; i < lines.length - 1; i++) { + const line = lines[i].trim(); + if (line.startsWith("data: ")) { + const jsonData = line.slice(6); + if (jsonData === "[DONE]") continue; - try { - const parsedData = JSON.parse(jsonData); - const content = parsedData.choices[0].delta.content; - if (content) { - yield content; - } - } catch (error) { - console.error('Error parsing JSON:', error); - } - } - } + try { + const parsedData = JSON.parse(jsonData); + const content = parsedData.choices[0].delta.content; + if (content) { + yield content; + } + } catch (error) { + console.error("Error parsing JSON:", error); + } + } + } - buffer = lines[lines.length - 1]; - } + buffer = lines[lines.length - 1]; + } } // Function to handle user input and display responses export async function handleUserInput() { - const userInput = document.getElementById('user-input').value; - document.getElementById('user-input').value = ''; + const userInput = document.getElementById("user-input").value; + document.getElementById("user-input").value = ""; - const chatBox = document.getElementById('chat-box'); - chatBox.innerHTML += `

You: ${userInput}

`; + const chatBox = document.getElementById("chat-box"); + chatBox.innerHTML += `

You: ${userInput}

`; - const aiResponseParagraph = document.createElement('p'); - aiResponseParagraph.innerHTML = 'AI: '; - chatBox.appendChild(aiResponseParagraph); + const aiResponseParagraph = document.createElement("p"); + aiResponseParagraph.innerHTML = "AI: "; + chatBox.appendChild(aiResponseParagraph); - const responseContent = document.createElement('span'); - aiResponseParagraph.appendChild(responseContent); + const responseContent = document.createElement("span"); + aiResponseParagraph.appendChild(responseContent); - let accumulatedContent = ''; - for await (const chunk of sendMessage(userInput)) { - accumulatedContent += chunk; - const parsedChunk = marked.parse(accumulatedContent); - responseContent.innerHTML = parsedChunk; + let accumulatedContent = ""; + for await (const chunk of sendMessage(userInput)) { + accumulatedContent += chunk; + const parsedChunk = marked.parse(accumulatedContent); + responseContent.innerHTML = parsedChunk; - responseContent.querySelectorAll('pre code').forEach((block) => { - hljs.highlightElement(block); - }); - } + responseContent.querySelectorAll("pre code").forEach((block) => { + hljs.highlightElement(block); + }); + } - chatBox.scrollTop = chatBox.scrollHeight; + chatBox.scrollTop = chatBox.scrollHeight; - // Add the response to chat history - chatHistory.push({ role: "assistant", content: accumulatedContent }); - saveConversationHistory(chatHistory); + // Add the response to chat history + chatHistory.push({ role: "assistant", content: accumulatedContent }); + saveConversationHistory(chatHistory); - // Add play/pause button for TTS - const playPauseButton = document.createElement('button'); - playPauseButton.textContent = 'Generate TTS for AI Response'; - playPauseButton.style.margin = '5px'; - let aiAudio = null; - let aiBlob = null; - let isPaused = false; + // Add play/pause button for TTS + const playPauseButton = document.createElement("button"); + playPauseButton.textContent = "Generate TTS for AI Response"; + playPauseButton.style.margin = "5px"; + let aiAudio = null; + let aiBlob = null; + let isPaused = false; - playPauseButton.onclick = async () => { - if (!aiAudio) { - playPauseButton.textContent = 'Processing...'; - playPauseButton.disabled = true; // Disable button while processing - const mainVoiceSelect = document.getElementById('read-page-voice'); - const selectedVoice = mainVoiceSelect ? mainVoiceSelect.value : 'alloy'; - const result = await speakText(accumulatedContent, selectedVoice, 0.9); - aiAudio = result.audio; - aiBlob = result.blob; - playPauseButton.textContent = 'Pause AI Response'; - playPauseButton.disabled = false; // Re-enable button after processing - aiAudio.play(); + playPauseButton.onclick = async () => { + if (!aiAudio) { + playPauseButton.textContent = "Processing..."; + playPauseButton.disabled = true; // Disable button while processing + const mainVoiceSelect = document.getElementById("read-page-voice"); + const selectedVoice = mainVoiceSelect ? mainVoiceSelect.value : "alloy"; + const result = await speakText(accumulatedContent, selectedVoice, 0.9); + aiAudio = result.audio; + aiBlob = result.blob; + playPauseButton.textContent = "Pause AI Response"; + playPauseButton.disabled = false; // Re-enable button after processing + aiAudio.play(); - // Generate title for the MP3 file - const title = await generateTitleForTTS(accumulatedContent); + // Generate title for the MP3 file + const title = await generateTitleForTTS(accumulatedContent); - // Add download button - const downloadButton = document.createElement('button'); - downloadButton.textContent = 'Download MP3'; - downloadButton.style.margin = '5px'; - downloadButton.onclick = () => { - const a = document.createElement('a'); - a.href = URL.createObjectURL(aiBlob); - a.download = `${title}.mp3`; - a.click(); - }; - chatBox.appendChild(downloadButton); - } else { - if (isPaused) { - aiAudio.play(); - playPauseButton.textContent = 'Pause AI Response'; - } else { - aiAudio.pause(); - playPauseButton.textContent = 'Play AI Response'; - } - isPaused = !isPaused; - } - }; - chatBox.appendChild(playPauseButton); + // Add download button + const downloadButton = document.createElement("button"); + downloadButton.textContent = "Download MP3"; + downloadButton.style.margin = "5px"; + downloadButton.onclick = () => { + const a = document.createElement("a"); + a.href = URL.createObjectURL(aiBlob); + a.download = `${title}.mp3`; + a.click(); + }; + chatBox.appendChild(downloadButton); + } else { + if (isPaused) { + aiAudio.play(); + playPauseButton.textContent = "Pause AI Response"; + } else { + aiAudio.pause(); + playPauseButton.textContent = "Play AI Response"; + } + isPaused = !isPaused; + } + }; + chatBox.appendChild(playPauseButton); } // Update chat history reference (for external modules) export function updateChatHistory(newHistory) { - chatHistory = newHistory; + chatHistory = newHistory; } export function getChatHistory() { - return chatHistory; -} \ No newline at end of file + return chatHistory; +} diff --git a/src/config.js b/src/config.js index d667b29..fe3f7d3 100644 --- a/src/config.js +++ b/src/config.js @@ -6,32 +6,35 @@ export const API_KEY = "dummy-api-key"; export const MODEL = "adamo1139/Hermes-3-Llama-3.1-8B-FP8-Dynamic"; // default model (always available) // System message configuration - implementors can customize this -export const SYSTEM_MESSAGE_BASE = "You are Hermes, a large language model from Nous Research, embedded as an AI assistant on this webpage. Your primary task is to help users understand and interact with the content of this specific page, while also being capable of assisting with any other topics, coding, creative tasks, or questions they may have. You should be conversational, helpful, and knowledgeable about the webpage content as well as general topics. Always strive to provide accurate, useful responses."; +export const SYSTEM_MESSAGE_BASE = + "You are Hermes, a large language model from Nous Research, embedded as an AI assistant on this webpage. Your primary task is to help users understand and interact with the content of this specific page, while also being capable of assisting with any other topics, coding, creative tasks, or questions they may have. You should be conversational, helpful, and knowledgeable about the webpage content as well as general topics. Always strive to provide accurate, useful responses."; // Optional additional system message content - can be set by implementor export let SYSTEM_MESSAGE_APPEND = ""; // Function to set additional system message content export function setSystemMessageAppend(appendText) { - SYSTEM_MESSAGE_APPEND = appendText; + SYSTEM_MESSAGE_APPEND = appendText; } // Function to get the complete system message export function getSystemMessage() { - return SYSTEM_MESSAGE_APPEND ? `${SYSTEM_MESSAGE_BASE}\n\n${SYSTEM_MESSAGE_APPEND}` : SYSTEM_MESSAGE_BASE; + return SYSTEM_MESSAGE_APPEND + ? `${SYSTEM_MESSAGE_BASE}\n\n${SYSTEM_MESSAGE_APPEND}` + : SYSTEM_MESSAGE_BASE; } // Dynamic Endpoints Configuration for Chat API export const VLLM_ENDPOINTS = [ - { id: 'hermes.ai.unturf.com', url: 'https://hermes.ai.unturf.com/v1' }, - { id: 'hermes2.ai.unturf.com', url: 'https://hermes2.ai.unturf.com/v1' } + { id: "hermes.ai.unturf.com", url: "https://hermes.ai.unturf.com/v1" }, + { id: "hermes2.ai.unturf.com", url: "https://hermes2.ai.unturf.com/v1" }, ]; // Global state -export let lastTTSInput = ''; +export let lastTTSInput = ""; export let lastTTSResult = null; export function setLastTTS(input, result) { - lastTTSInput = input; - lastTTSResult = result; -} \ No newline at end of file + lastTTSInput = input; + lastTTSResult = result; +} diff --git a/src/content.js b/src/content.js index 7a25e79..9cba9dc 100644 --- a/src/content.js +++ b/src/content.js @@ -2,41 +2,41 @@ // Function to extract text content along with links and metadata from the webpage export function extractWebpageContent() { - let content = ''; + let content = ""; - // Extract title - const title = document.title; - if (title) { - content += `**Page Title**: ${title}\n\n`; - } + // Extract title + const title = document.title; + if (title) { + content += `**Page Title**: ${title}\n\n`; + } - // Extract meta description - const metaDescription = document.querySelector('meta[name="description"]'); - if (metaDescription) { - content += `**Meta Description**: ${metaDescription.content}\n\n`; - } + // Extract meta description + const metaDescription = document.querySelector('meta[name="description"]'); + if (metaDescription) { + content += `**Meta Description**: ${metaDescription.content}\n\n`; + } - // Extract other metadata (if needed) - const metaKeywords = document.querySelector('meta[name="keywords"]'); - if (metaKeywords) { - content += `**Meta Keywords**: ${metaKeywords.content}\n\n`; - } + // Extract other metadata (if needed) + const metaKeywords = document.querySelector('meta[name="keywords"]'); + if (metaKeywords) { + content += `**Meta Keywords**: ${metaKeywords.content}\n\n`; + } - // Recursively extract text and links from the body content - function getTextWithLinks(element) { - if (element.nodeType === Node.TEXT_NODE) { - content += element.textContent + ' '; - } else if (element.nodeType === Node.ELEMENT_NODE) { - if (element.tagName.toLowerCase() === 'a') { - // If it's a link, append the text and the href - content += `[${element.textContent}](${element.href}) `; - } else { - // Recursively process child nodes - element.childNodes.forEach(getTextWithLinks); - } - } - } + // Recursively extract text and links from the body content + function getTextWithLinks(element) { + if (element.nodeType === Node.TEXT_NODE) { + content += element.textContent + " "; + } else if (element.nodeType === Node.ELEMENT_NODE) { + if (element.tagName.toLowerCase() === "a") { + // If it's a link, append the text and the href + content += `[${element.textContent}](${element.href}) `; + } else { + // Recursively process child nodes + element.childNodes.forEach(getTextWithLinks); + } + } + } - getTextWithLinks(document.body); - return content.trim(); -} \ No newline at end of file + getTextWithLinks(document.body); + return content.trim(); +} diff --git a/src/file-upload.js b/src/file-upload.js index 7da1b02..6b6b2e4 100644 --- a/src/file-upload.js +++ b/src/file-upload.js @@ -1,18 +1,18 @@ // File upload and processing functionality import { marked } from "https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js"; -import hljs from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/es/highlight.min.js'; -import { MEGAPARCE_API_URL } from './config.js'; -import { speakText } from './tts.js'; -import { chatHistory } from './chat.js'; +import hljs from "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/es/highlight.min.js"; +import { MEGAPARCE_API_URL } from "./config.js"; +import { speakText } from "./tts.js"; +import { chatHistory } from "./chat.js"; // Progress indicator functions export function showProgressIndicator(message) { - // Remove existing indicator if any - hideProgressIndicator(); - - const indicator = document.createElement('div'); - indicator.id = 'progress-indicator'; - indicator.style.cssText = ` + // Remove existing indicator if any + hideProgressIndicator(); + + const indicator = document.createElement("div"); + indicator.id = "progress-indicator"; + indicator.style.cssText = ` position: fixed; top: 50%; left: 50%; @@ -23,114 +23,114 @@ export function showProgressIndicator(message) { border-radius: 5px; z-index: 9999; `; - indicator.textContent = message || 'Processing...'; - document.body.appendChild(indicator); + indicator.textContent = message || "Processing..."; + document.body.appendChild(indicator); } export function hideProgressIndicator() { - const indicator = document.getElementById('progress-indicator'); - if (indicator) { - indicator.remove(); - } + const indicator = document.getElementById("progress-indicator"); + if (indicator) { + indicator.remove(); + } } // Handle file upload from input element export async function handleFileUpload() { - const fileInput = document.getElementById('file-input'); - if (!fileInput.files[0]) { - alert('Please select a file first.'); - return; - } + const fileInput = document.getElementById("file-input"); + if (!fileInput.files[0]) { + alert("Please select a file first."); + return; + } - try { - showProgressIndicator('Uploading file...'); - const response = await uploadFile(fileInput.files[0]); - hideProgressIndicator(); - - await integrateMegafarceResponse(response); - fileInput.value = ''; // Clear the input after upload - } catch (error) { - console.error('File upload error:', error); - alert('Failed to upload the file.'); - hideProgressIndicator(); - } + try { + showProgressIndicator("Uploading file..."); + const response = await uploadFile(fileInput.files[0]); + hideProgressIndicator(); + + await integrateMegafarceResponse(response); + fileInput.value = ""; // Clear the input after upload + } catch (error) { + console.error("File upload error:", error); + alert("Failed to upload the file."); + hideProgressIndicator(); + } } // Upload File to MegaFarce with Progress Indicator export async function uploadFile(file) { - const formData = new FormData(); - formData.append('file', file); + const formData = new FormData(); + formData.append("file", file); - const response = await fetch(MEGAPARCE_API_URL, { - method: 'POST', - body: formData - }); + const response = await fetch(MEGAPARCE_API_URL, { + method: "POST", + body: formData, + }); - if (!response.ok) { - const errorText = await response.text(); - throw new Error(`Error uploading file: ${errorText}`); - } + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Error uploading file: ${errorText}`); + } - const responseData = await response.json(); - return responseData; + const responseData = await response.json(); + return responseData; } // Integrate MegaFarce Response into Chat export async function integrateMegafarceResponse(response) { - // Assuming the response contains a 'content' field with the parsed content - const content = response.content || response.result || "No content received."; + // Assuming the response contains a 'content' field with the parsed content + const content = response.content || response.result || "No content received."; - // Add a system message with the uploaded content to provide context - chatHistory.push({ role: "system", content: `Uploaded Context: ${content}` }); + // Add a system message with the uploaded content to provide context + chatHistory.push({ role: "system", content: `Uploaded Context: ${content}` }); - // Note: System message is added to chat history for context but not displayed to user - // The uploaded content context is available to the AI without showing it in the chat + // Note: System message is added to chat history for context but not displayed to user + // The uploaded content context is available to the AI without showing it in the chat - // Add Generate TTS button for the uploaded content - const generateTTSButton = document.createElement('button'); - generateTTSButton.textContent = 'Generate TTS for Uploaded Content'; - let ttsAudio = null; - let ttsBlob = null; - let isPaused = false; + // Add Generate TTS button for the uploaded content + const generateTTSButton = document.createElement("button"); + generateTTSButton.textContent = "Generate TTS for Uploaded Content"; + let ttsAudio = null; + let ttsBlob = null; + let isPaused = false; - generateTTSButton.onclick = async () => { - if (!ttsAudio) { - generateTTSButton.textContent = 'Processing...'; - generateTTSButton.disabled = true; // Disable button while processing - try { - const result = await speakText(content); - ttsAudio = result.audio; - ttsBlob = result.blob; - generateTTSButton.textContent = 'Pause TTS'; - generateTTSButton.disabled = false; // Re-enable button after processing - ttsAudio.play(); + generateTTSButton.onclick = async () => { + if (!ttsAudio) { + generateTTSButton.textContent = "Processing..."; + generateTTSButton.disabled = true; // Disable button while processing + try { + const result = await speakText(content); + ttsAudio = result.audio; + ttsBlob = result.blob; + generateTTSButton.textContent = "Pause TTS"; + generateTTSButton.disabled = false; // Re-enable button after processing + ttsAudio.play(); - // Add download button for the TTS audio - const downloadButton = document.createElement('button'); - downloadButton.textContent = 'Download MP3'; - downloadButton.onclick = () => { - const a = document.createElement('a'); - a.href = URL.createObjectURL(ttsBlob); - a.download = 'uploaded-content.mp3'; - a.click(); - }; - chatBox.appendChild(downloadButton); - } catch (error) { - console.error('Error generating TTS:', error); - alert('Failed to generate TTS for the uploaded content.'); - generateTTSButton.textContent = 'Generate TTS for Uploaded Content'; - generateTTSButton.disabled = false; - } - } else { - if (isPaused) { - ttsAudio.play(); - generateTTSButton.textContent = 'Pause TTS'; - } else { - ttsAudio.pause(); - generateTTSButton.textContent = 'Play TTS'; - } - isPaused = !isPaused; - } - }; - chatBox.appendChild(generateTTSButton); -} \ No newline at end of file + // Add download button for the TTS audio + const downloadButton = document.createElement("button"); + downloadButton.textContent = "Download MP3"; + downloadButton.onclick = () => { + const a = document.createElement("a"); + a.href = URL.createObjectURL(ttsBlob); + a.download = "uploaded-content.mp3"; + a.click(); + }; + chatBox.appendChild(downloadButton); + } catch (error) { + console.error("Error generating TTS:", error); + alert("Failed to generate TTS for the uploaded content."); + generateTTSButton.textContent = "Generate TTS for Uploaded Content"; + generateTTSButton.disabled = false; + } + } else { + if (isPaused) { + ttsAudio.play(); + generateTTSButton.textContent = "Pause TTS"; + } else { + ttsAudio.pause(); + generateTTSButton.textContent = "Play TTS"; + } + isPaused = !isPaused; + } + }; + chatBox.appendChild(generateTTSButton); +} diff --git a/src/models.js b/src/models.js index a44dadb..f5b4ef8 100644 --- a/src/models.js +++ b/src/models.js @@ -1,5 +1,5 @@ // Model registry and selection functionality -import { VLLM_ENDPOINTS } from './config.js'; +import { VLLM_ENDPOINTS } from "./config.js"; // This registry maps a model's ID to the endpoint where it resides. export const modelRegistry = {}; @@ -7,222 +7,231 @@ export const modelRegistry = {}; // Fetch models from each endpoint with caching. // Cache is busted if the endpoint array changes or TTL. export async function fetchModelsFromEndpoints() { - const cacheKey = 'modelRegistryCache'; - const endpointsKey = 'vllmEndpointsHash'; - const endpointsString = JSON.stringify(VLLM_ENDPOINTS); - const cachedEndpoints = localStorage.getItem(endpointsKey); - const cacheItem = localStorage.getItem(cacheKey); - const now = Date.now(); - const TTL = 300000; // 5 minutes in milliseconds + const cacheKey = "modelRegistryCache"; + const endpointsKey = "vllmEndpointsHash"; + const endpointsString = JSON.stringify(VLLM_ENDPOINTS); + const cachedEndpoints = localStorage.getItem(endpointsKey); + const cacheItem = localStorage.getItem(cacheKey); + const now = Date.now(); + const TTL = 300000; // 5 minutes in milliseconds - if (cacheItem && cachedEndpoints === endpointsString) { - try { - const cachedData = JSON.parse(cacheItem); - if (now - cachedData.timestamp < TTL) { - // Restore cached modelRegistry - Object.assign(modelRegistry, cachedData.modelRegistry); - return cachedData.models; - } - } catch (e) { - console.error("Error reading model registry from cache", e); - } - } + if (cacheItem && cachedEndpoints === endpointsString) { + try { + const cachedData = JSON.parse(cacheItem); + if (now - cachedData.timestamp < TTL) { + // Restore cached modelRegistry + Object.assign(modelRegistry, cachedData.modelRegistry); + return cachedData.models; + } + } catch (e) { + console.error("Error reading model registry from cache", e); + } + } - // If no valid cache, fetch models from all endpoints - const fetchPromises = VLLM_ENDPOINTS.map(async (endpoint) => { - try { - const res = await fetch(`${endpoint.url}/models`); - if (!res.ok) throw new Error(`HTTP error! status: ${res.status} from ${endpoint.url}`); - const jsonResponse = await res.json(); - // Expected JSON structure: { data: [ { id, ... }, ... ], object: "list" } - const models = jsonResponse.data || []; - // Map each model to include its endpoint ID, unique ID, and model name - return models.map((model) => ({ - ...model, - modelName: model.id, // Explicitly store model name - endpointId: endpoint.id, - uniqueId: `${endpoint.id}-${model.id}` // Unique ID with endpoint ID first - })); - } catch (error) { - console.error(`Error fetching models from ${endpoint.url}:`, error); - return []; - } - }); - const allModelsArrays = await Promise.all(fetchPromises); - const models = allModelsArrays.flat(); + // If no valid cache, fetch models from all endpoints + const fetchPromises = VLLM_ENDPOINTS.map(async (endpoint) => { + try { + const res = await fetch(`${endpoint.url}/models`); + if (!res.ok) + throw new Error( + `HTTP error! status: ${res.status} from ${endpoint.url}`, + ); + const jsonResponse = await res.json(); + // Expected JSON structure: { data: [ { id, ... }, ... ], object: "list" } + const models = jsonResponse.data || []; + // Map each model to include its endpoint ID, unique ID, and model name + return models.map((model) => ({ + ...model, + modelName: model.id, // Explicitly store model name + endpointId: endpoint.id, + uniqueId: `${endpoint.id}-${model.id}`, // Unique ID with endpoint ID first + })); + } catch (error) { + console.error(`Error fetching models from ${endpoint.url}:`, error); + return []; + } + }); + const allModelsArrays = await Promise.all(fetchPromises); + const models = allModelsArrays.flat(); - // Update modelRegistry with unique model instances - models.forEach((model) => { - modelRegistry[model.uniqueId] = { - url: VLLM_ENDPOINTS.find((e) => e.id === model.endpointId).url, - endpointId: model.endpointId - }; - }); + // Update modelRegistry with unique model instances + models.forEach((model) => { + modelRegistry[model.uniqueId] = { + url: VLLM_ENDPOINTS.find((e) => e.id === model.endpointId).url, + endpointId: model.endpointId, + }; + }); - // Cache the results - const cacheData = { - timestamp: now, - modelRegistry: modelRegistry, - models: models - }; - localStorage.setItem(cacheKey, JSON.stringify(cacheData)); - localStorage.setItem(endpointsKey, endpointsString); + // Cache the results + const cacheData = { + timestamp: now, + modelRegistry: modelRegistry, + models: models, + }; + localStorage.setItem(cacheKey, JSON.stringify(cacheData)); + localStorage.setItem(endpointsKey, endpointsString); - return models; + return models; } // Create a dynamic drop-down for model selection // This function creates a `; - - // Control buttons - const controlsDiv = document.createElement('div'); - controlsDiv.style.cssText = 'display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; margin-bottom: 10px;'; - - const readBtn = createButton('πŸ“– Read Page', () => readPageWithHermes()); - const ttsBtn = createButton('πŸ”Š TTS Anything', () => openTTSModal()); - const translateBtn = createButton('🌐 Translate', () => openTranslateModal()); - const uploadBtn = createButton('πŸ“ Upload File', () => document.querySelector('[data-uncloseai-file-input]')?.click()); - - controlsDiv.appendChild(readBtn); - controlsDiv.appendChild(ttsBtn); - controlsDiv.appendChild(translateBtn); - controlsDiv.appendChild(uploadBtn); - - // Hidden file input - const fileInput = document.createElement('input'); - fileInput.type = 'file'; - fileInput.setAttribute('data-uncloseai-file-input', ''); - fileInput.style.display = 'none'; - fileInput.onchange = handleFileUpload; - - container.appendChild(chatContainer); - container.appendChild(controlsDiv); - container.appendChild(fileInput); + + // Control buttons + const controlsDiv = document.createElement("div"); + controlsDiv.style.cssText = + "display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; margin-bottom: 10px;"; + + const readBtn = createButton("πŸ“– Read Page", () => readPageWithHermes()); + const ttsBtn = createButton("πŸ”Š TTS Anything", () => openTTSModal()); + const translateBtn = createButton("🌐 Translate", () => openTranslateModal()); + const uploadBtn = createButton("πŸ“ Upload File", () => + document.querySelector("[data-uncloseai-file-input]")?.click(), + ); + + controlsDiv.appendChild(readBtn); + controlsDiv.appendChild(ttsBtn); + controlsDiv.appendChild(translateBtn); + controlsDiv.appendChild(uploadBtn); + + // Hidden file input + const fileInput = document.createElement("input"); + fileInput.type = "file"; + fileInput.setAttribute("data-uncloseai-file-input", ""); + fileInput.style.display = "none"; + fileInput.onchange = handleFileUpload; + + container.appendChild(chatContainer); + container.appendChild(controlsDiv); + container.appendChild(fileInput); } // Create custom interface with specific features export function createCustomInterface(container, features) { - const div = document.createElement('div'); - div.style.cssText = 'display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; padding: 15px; border: 1px solid #ddd; border-radius: 8px;'; - - features.forEach(feature => { - switch(feature.trim()) { - case 'chat': - createChatFeature(div); - break; - case 'tts': - createTTSFeature(div); - break; - case 'translate': - createTranslateFeature(div); - break; - case 'upload': - createUploadFeature(div); - break; - case 'read': - createReadFeature(div); - break; - } - }); - - container.appendChild(div); + const div = document.createElement("div"); + div.style.cssText = + "display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; padding: 15px; border: 1px solid #ddd; border-radius: 8px;"; + + features.forEach((feature) => { + switch (feature.trim()) { + case "chat": + createChatFeature(div); + break; + case "tts": + createTTSFeature(div); + break; + case "translate": + createTranslateFeature(div); + break; + case "upload": + createUploadFeature(div); + break; + case "read": + createReadFeature(div); + break; + } + }); + + container.appendChild(div); } // Individual feature creators export function createChatFeature(container) { - const chatDiv = document.createElement('div'); - chatDiv.innerHTML = ` + const chatDiv = document.createElement("div"); + chatDiv.innerHTML = `

AI Chat

`; - container.appendChild(chatDiv); + container.appendChild(chatDiv); } export function createTTSFeature(container) { - const ttsDiv = document.createElement('div'); - ttsDiv.innerHTML = ` + const ttsDiv = document.createElement("div"); + ttsDiv.innerHTML = `

Text to Speech

`; - container.appendChild(ttsDiv); + container.appendChild(ttsDiv); } export function createUploadFeature(container) { - const uploadDiv = document.createElement('div'); - uploadDiv.innerHTML = ` + const uploadDiv = document.createElement("div"); + uploadDiv.innerHTML = `

File Upload

`; - container.appendChild(uploadDiv); + container.appendChild(uploadDiv); } export function createTranslateFeature(container) { - const translateDiv = document.createElement('div'); - translateDiv.innerHTML = ` + const translateDiv = document.createElement("div"); + translateDiv.innerHTML = `

Translation

`; - container.appendChild(translateDiv); + container.appendChild(translateDiv); } export function createReadFeature(container) { - const readDiv = document.createElement('div'); - readDiv.innerHTML = ` + const readDiv = document.createElement("div"); + readDiv.innerHTML = `

Page Reader

Read this page with AI voice

`; - container.appendChild(readDiv); + container.appendChild(readDiv); } // Helper functions for custom features export function createButton(text, onclick) { - const btn = document.createElement('button'); - btn.textContent = text; - btn.onclick = onclick; - btn.style.cssText = 'padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer;'; - return btn; + const btn = document.createElement("button"); + btn.textContent = text; + btn.onclick = onclick; + btn.style.cssText = + "padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer;"; + return btn; } export async function handleTTSFromElement(button) { - const container = button.closest('[data-tts-result]')?.parentElement || button.parentElement; - const textarea = container.querySelector('[data-tts-input]'); - const resultDiv = container.querySelector('[data-tts-result]'); - const text = textarea?.value?.trim(); - - if (!text) { - alert('Please enter some text first!'); - return; - } - - button.disabled = true; - button.textContent = 'Converting...'; - resultDiv.innerHTML = 'Converting to speech...'; - - try { - const result = await speakText(text, 'alloy', 0.9); - const audioControls = document.createElement('div'); - audioControls.style.cssText = 'margin: 10px 0;'; - - const playButton = document.createElement('button'); - playButton.textContent = '▢️ Play'; - playButton.style.cssText = 'margin: 2px; padding: 4px 8px;'; - playButton.onclick = () => result.audio.play(); - - const pauseButton = document.createElement('button'); - pauseButton.textContent = '⏸️ Pause'; - pauseButton.style.cssText = 'margin: 2px; padding: 4px 8px;'; - pauseButton.onclick = () => result.audio.pause(); - - audioControls.appendChild(playButton); - audioControls.appendChild(pauseButton); - - resultDiv.innerHTML = ''; - resultDiv.appendChild(result.audio); - resultDiv.appendChild(audioControls); - } catch (error) { - resultDiv.innerHTML = 'Error: ' + error.message; - } finally { - button.disabled = false; - button.textContent = 'πŸ”Š Convert to Speech'; - } + const container = + button.closest("[data-tts-result]")?.parentElement || button.parentElement; + const textarea = container.querySelector("[data-tts-input]"); + const resultDiv = container.querySelector("[data-tts-result]"); + const text = textarea?.value?.trim(); + + if (!text) { + alert("Please enter some text first!"); + return; + } + + button.disabled = true; + button.textContent = "Converting..."; + resultDiv.innerHTML = "Converting to speech..."; + + try { + const result = await speakText(text, "alloy", 0.9); + const audioControls = document.createElement("div"); + audioControls.style.cssText = "margin: 10px 0;"; + + const playButton = document.createElement("button"); + playButton.textContent = "▢️ Play"; + playButton.style.cssText = "margin: 2px; padding: 4px 8px;"; + playButton.onclick = () => result.audio.play(); + + const pauseButton = document.createElement("button"); + pauseButton.textContent = "⏸️ Pause"; + pauseButton.style.cssText = "margin: 2px; padding: 4px 8px;"; + pauseButton.onclick = () => result.audio.pause(); + + audioControls.appendChild(playButton); + audioControls.appendChild(pauseButton); + + resultDiv.innerHTML = ""; + resultDiv.appendChild(result.audio); + resultDiv.appendChild(audioControls); + } catch (error) { + resultDiv.innerHTML = "Error: " + error.message; + } finally { + button.disabled = false; + button.textContent = "πŸ”Š Convert to Speech"; + } } export async function handleUploadFromElement(button) { - const container = button.parentElement; - const fileInput = container.querySelector('[data-upload-input]'); - const resultDiv = container.querySelector('[data-upload-result]'); - - if (!fileInput.files[0]) { - alert('Please select a file first!'); - return; - } - - button.disabled = true; - button.textContent = 'Processing...'; - resultDiv.style.display = 'block'; - resultDiv.innerHTML = 'Uploading and analyzing file...'; - - try { - showProgressIndicator('Processing file...'); - const response = await uploadFile(fileInput.files[0]); - hideProgressIndicator(); - - resultDiv.innerHTML = `Analysis Result:
${response}`; - fileInput.value = ''; - } catch (error) { - hideProgressIndicator(); - resultDiv.innerHTML = 'Error: ' + error.message; - } finally { - button.disabled = false; - button.textContent = 'πŸ“ Upload & Analyze'; - } + const container = button.parentElement; + const fileInput = container.querySelector("[data-upload-input]"); + const resultDiv = container.querySelector("[data-upload-result]"); + + if (!fileInput.files[0]) { + alert("Please select a file first!"); + return; + } + + button.disabled = true; + button.textContent = "Processing..."; + resultDiv.style.display = "block"; + resultDiv.innerHTML = "Uploading and analyzing file..."; + + try { + showProgressIndicator("Processing file..."); + const response = await uploadFile(fileInput.files[0]); + hideProgressIndicator(); + + resultDiv.innerHTML = `Analysis Result:
${response}`; + fileInput.value = ""; + } catch (error) { + hideProgressIndicator(); + resultDiv.innerHTML = "Error: " + error.message; + } finally { + button.disabled = false; + button.textContent = "πŸ“ Upload & Analyze"; + } } // Initialize the legacy chat interface (for backward compatibility) export function initializeChatInterface() { - // Only initialize if there are legacy elements (chat-container, user-input, etc.) - const legacyElements = document.querySelector('#chat-container, #user-input, #chat-box'); - if (!legacyElements) return; - - const pageContent = extractWebpageContent(); - chatHistory.push({ - role: "system", - content: `Here's the content of the webpage: ${pageContent}` - }); + // Only initialize if there are legacy elements (chat-container, user-input, etc.) + const legacyElements = document.querySelector( + "#chat-container, #user-input, #chat-box", + ); + if (!legacyElements) return; - marked.setOptions({ - highlight: function(code, lang) { - const language = hljs.getLanguage(lang) ? lang : 'plaintext'; - return hljs.highlight(code, { language }).value; - } - }); - - // Add the Read Page button - addReadPageButton(); + const pageContent = extractWebpageContent(); + chatHistory.push({ + role: "system", + content: `Here's the content of the webpage: ${pageContent}`, + }); - // Add the File Upload button and picker - addFileUploadButton(); + marked.setOptions({ + highlight: function (code, lang) { + const language = hljs.getLanguage(lang) ? lang : "plaintext"; + return hljs.highlight(code, { language }).value; + }, + }); - // Event listeners - document.getElementById("user-input")?.addEventListener("keydown", function(event) { - if (event.key === "Enter" && !event.shiftKey) { - event.preventDefault(); - handleUserInput(); - } - }); + // Add the Read Page button + addReadPageButton(); + + // Add the File Upload button and picker + addFileUploadButton(); + + // Event listeners + document + .getElementById("user-input") + ?.addEventListener("keydown", function (event) { + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + handleUserInput(); + } + }); } // Add button to page export function addReadPageButton() { - const button = document.createElement('button'); - button.textContent = 'Read Page'; - button.onclick = readPageWithHermes; - button.style.margin = '10px'; + const button = document.createElement("button"); + button.textContent = "Read Page"; + button.onclick = readPageWithHermes; + button.style.margin = "10px"; - const ttsButton = document.createElement('button'); - ttsButton.textContent = 'TTS Anything'; - ttsButton.onclick = openTTSModal; - ttsButton.style.margin = '10px'; + const ttsButton = document.createElement("button"); + ttsButton.textContent = "TTS Anything"; + ttsButton.onclick = openTTSModal; + ttsButton.style.margin = "10px"; - document.body.appendChild(button); - document.body.appendChild(ttsButton); + document.body.appendChild(button); + document.body.appendChild(ttsButton); } // Add file upload button export function addFileUploadButton() { - const button = document.createElement('button'); - button.textContent = 'Upload File'; - button.style.margin = '10px'; + const button = document.createElement("button"); + button.textContent = "Upload File"; + button.style.margin = "10px"; - const fileInput = document.createElement('input'); - fileInput.type = 'file'; - fileInput.id = 'file-input'; - fileInput.style.display = 'none'; + const fileInput = document.createElement("input"); + fileInput.type = "file"; + fileInput.id = "file-input"; + fileInput.style.display = "none"; - button.onclick = () => fileInput.click(); - fileInput.onchange = handleFileUpload; + button.onclick = () => fileInput.click(); + fileInput.onchange = handleFileUpload; - document.body.appendChild(button); - document.body.appendChild(fileInput); + document.body.appendChild(button); + document.body.appendChild(fileInput); } // Function to create floating AI button export function createFloatingAIButton() { - console.log('uncloseai.js: createFloatingAIButton() called.'); - // Remove any existing floating button first - const existingButton = document.getElementById('floating-ai-button'); - if (existingButton) { - console.log('uncloseai.js: Found existing floating button, removing it.'); - existingButton.remove(); - } - - // Create the main floating button - const floatingButton = document.createElement('button'); - floatingButton.id = 'floating-ai-button'; - floatingButton.textContent = 'uncloseai.'; - console.log('uncloseai.js: Created new floating button element.'); - - // Function to update button theme - function updateButtonTheme() { - const isDark = document.documentElement.getAttribute('data-theme') === 'dark' || - (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches && - !document.documentElement.getAttribute('data-theme')); - - floatingButton.style.cssText = ` + console.log("uncloseai.js: createFloatingAIButton() called."); + // Remove any existing floating button first + const existingButton = document.getElementById("floating-ai-button"); + if (existingButton) { + console.log("uncloseai.js: Found existing floating button, removing it."); + existingButton.remove(); + } + + // Create the main floating button + const floatingButton = document.createElement("button"); + floatingButton.id = "floating-ai-button"; + floatingButton.textContent = "uncloseai."; + console.log("uncloseai.js: Created new floating button element."); + + // Function to update button theme + function updateButtonTheme() { + const isDark = + document.documentElement.getAttribute("data-theme") === "dark" || + (window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches && + !document.documentElement.getAttribute("data-theme")); + + floatingButton.style.cssText = ` position: fixed; bottom: 20px; right: 10px; width: 110px; height: 55px; border-radius: 22px; - background: ${isDark ? '#ffffff' : '#000000'}; - border: 2px solid ${isDark ? '#000000' : '#ffffff'}; - color: ${isDark ? '#000000' : '#ffffff'}; + background: ${isDark ? "#ffffff" : "#000000"}; + border: 2px solid ${isDark ? "#000000" : "#ffffff"}; + color: ${isDark ? "#000000" : "#ffffff"}; font-family: 'ChunkFiveRegular', monospace; font-size: 13px; cursor: pointer; @@ -352,75 +373,80 @@ export function createFloatingAIButton() { max-width: calc(100vw - 20px); box-sizing: border-box; `; - } - - // Initial theme setup - updateButtonTheme(); - - // Watch for theme changes - const observer = new MutationObserver(updateButtonTheme); - observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); - - // Watch for system theme changes - if (window.matchMedia) { - window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateButtonTheme); - } - - // Hover effects - floatingButton.onmouseenter = () => { - floatingButton.style.transform = 'scale(1.1)'; - floatingButton.style.boxShadow = '0 6px 16px rgba(0,0,0,0.4)'; - }; - floatingButton.onmouseleave = () => { - floatingButton.style.transform = 'scale(1)'; - floatingButton.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)'; - }; - - // Toggle modal on click - floatingButton.onclick = async () => { - // Show loading state - const originalText = floatingButton.textContent; - floatingButton.textContent = 'loading...'; - floatingButton.disabled = true; - floatingButton.style.opacity = '0.7'; - floatingButton.style.cursor = 'wait'; - - try { - await toggleHermesModal(); - } finally { - // Restore button state - floatingButton.textContent = originalText; - floatingButton.disabled = false; - floatingButton.style.opacity = '1'; - floatingButton.style.cursor = 'pointer'; - } - }; - - document.body.appendChild(floatingButton); - console.log('uncloseai.js: Appended floating button to document.body.'); + } + + // Initial theme setup + updateButtonTheme(); + + // Watch for theme changes + const observer = new MutationObserver(updateButtonTheme); + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ["data-theme"], + }); + + // Watch for system theme changes + if (window.matchMedia) { + window + .matchMedia("(prefers-color-scheme: dark)") + .addEventListener("change", updateButtonTheme); + } + + // Hover effects + floatingButton.onmouseenter = () => { + floatingButton.style.transform = "scale(1.1)"; + floatingButton.style.boxShadow = "0 6px 16px rgba(0,0,0,0.4)"; + }; + floatingButton.onmouseleave = () => { + floatingButton.style.transform = "scale(1)"; + floatingButton.style.boxShadow = "0 4px 12px rgba(0,0,0,0.3)"; + }; + + // Toggle modal on click + floatingButton.onclick = async () => { + // Show loading state + const originalText = floatingButton.textContent; + floatingButton.textContent = "loading..."; + floatingButton.disabled = true; + floatingButton.style.opacity = "0.7"; + floatingButton.style.cursor = "wait"; + + try { + await toggleHermesModal(); + } finally { + // Restore button state + floatingButton.textContent = originalText; + floatingButton.disabled = false; + floatingButton.style.opacity = "1"; + floatingButton.style.cursor = "pointer"; + } + }; + + document.body.appendChild(floatingButton); + console.log("uncloseai.js: Appended floating button to document.body."); } // Function to toggle Hermes modal export async function toggleHermesModal() { - const existingModal = document.getElementById('hermes-modal'); - if (existingModal) { - document.body.removeChild(existingModal); - hermesModalOpen = false; - } else { - await openHermesModal(); - hermesModalOpen = true; - } + const existingModal = document.getElementById("hermes-modal"); + if (existingModal) { + document.body.removeChild(existingModal); + hermesModalOpen = false; + } else { + await openHermesModal(); + hermesModalOpen = true; + } } // Function to open Hermes modal export async function openHermesModal() { - // Create modal using dialog element like TTS modal - const modal = document.createElement('dialog'); - modal.id = 'hermes-modal'; - modal.open = true; - - if (USE_CUSTOM_STYLING) { - modal.style.cssText = ` + // Create modal using dialog element like TTS modal + const modal = document.createElement("dialog"); + modal.id = "hermes-modal"; + modal.open = true; + + if (USE_CUSTOM_STYLING) { + modal.style.cssText = ` position: fixed; top: 0; left: 0; @@ -435,8 +461,8 @@ export async function openHermesModal() { z-index: 2000; margin: auto; `; - } else { - modal.style.cssText = ` + } else { + modal.style.cssText = ` position: fixed; top: 0; left: 0; @@ -450,23 +476,23 @@ export async function openHermesModal() { margin: 0; padding: 0; `; - } + } - const article = document.createElement('article'); - if (USE_CUSTOM_STYLING) { - article.style.cssText = ` + const article = document.createElement("article"); + if (USE_CUSTOM_STYLING) { + article.style.cssText = ` height: 100%; display: grid; grid-template-rows: auto auto 1fr auto; margin: 0; padding: 0; `; - } else { - // Responsive scaling based on screen width - const screenWidth = window.innerWidth; - const scale = screenWidth < 480 ? 0.9 : screenWidth < 768 ? 0.8 : 0.75; - - article.style.cssText = ` + } else { + // Responsive scaling based on screen width + const screenWidth = window.innerWidth; + const scale = screenWidth < 480 ? 0.9 : screenWidth < 768 ? 0.8 : 0.75; + + article.style.cssText = ` height: 100%; display: grid; grid-template-rows: auto auto 1fr auto; @@ -478,36 +504,36 @@ export async function openHermesModal() { max-width: none; box-sizing: border-box; `; - } - modal.appendChild(article); - - // Handle viewport changes (onscreen keyboard) - if (!USE_CUSTOM_STYLING) { - const handleViewportChange = () => { - // Use dvh (dynamic viewport height) for better mobile keyboard handling - modal.style.height = '100dvh'; - // Fallback for browsers that don't support dvh - if (window.visualViewport) { - modal.style.height = `${window.visualViewport.height}px`; - } - }; - - // Listen for visual viewport changes (keyboard open/close) - if (window.visualViewport) { - window.visualViewport.addEventListener('resize', handleViewportChange); - } - - // Also listen for window resize as fallback - window.addEventListener('resize', handleViewportChange); - - // Initial call - handleViewportChange(); - } - - // Create modal header - const header = document.createElement('div'); - if (USE_CUSTOM_STYLING) { - header.style.cssText = ` + } + modal.appendChild(article); + + // Handle viewport changes (onscreen keyboard) + if (!USE_CUSTOM_STYLING) { + const handleViewportChange = () => { + // Use dvh (dynamic viewport height) for better mobile keyboard handling + modal.style.height = "100dvh"; + // Fallback for browsers that don't support dvh + if (window.visualViewport) { + modal.style.height = `${window.visualViewport.height}px`; + } + }; + + // Listen for visual viewport changes (keyboard open/close) + if (window.visualViewport) { + window.visualViewport.addEventListener("resize", handleViewportChange); + } + + // Also listen for window resize as fallback + window.addEventListener("resize", handleViewportChange); + + // Initial call + handleViewportChange(); + } + + // Create modal header + const header = document.createElement("div"); + if (USE_CUSTOM_STYLING) { + header.style.cssText = ` background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); color: white; padding: 16px 20px; @@ -515,58 +541,59 @@ export async function openHermesModal() { grid-template-columns: 1fr auto; align-items: center; `; - } else { - header.style.cssText = ` + } else { + header.style.cssText = ` padding: 16px 20px; display: grid; grid-template-columns: 1fr auto; align-items: center; border-bottom: 1px solid #ccc; `; - } - - const titleContainer = document.createElement('div'); - - const title = document.createElement('h2'); - title.innerHTML = 'uncloseai. presents nous research\'s hermes large language model'; - if (USE_CUSTOM_STYLING) { - title.style.cssText = ` + } + + const titleContainer = document.createElement("div"); + + const title = document.createElement("h2"); + title.innerHTML = + 'uncloseai. presents nous research\'s hermes large language model'; + if (USE_CUSTOM_STYLING) { + title.style.cssText = ` margin: 0; font-family: 'ChunkFiveRegular', monospace; font-size: 16px; line-height: 1.2; `; - } else { - title.style.cssText = ` + } else { + title.style.cssText = ` margin: 0; font-size: 0.9em; line-height: 1.2; `; - } - - const pageTitle = document.createElement('div'); - pageTitle.textContent = `You are discussing: ${document.title}`; - if (USE_CUSTOM_STYLING) { - pageTitle.style.cssText = ` + } + + const pageTitle = document.createElement("div"); + pageTitle.textContent = `You are discussing: ${document.title}`; + if (USE_CUSTOM_STYLING) { + pageTitle.style.cssText = ` font-size: 12px; opacity: 0.8; margin-top: 4px; `; - } else { - pageTitle.style.cssText = ` + } else { + pageTitle.style.cssText = ` font-size: 0.75em; opacity: 0.7; margin-top: 4px; `; - } - - titleContainer.appendChild(title); - titleContainer.appendChild(pageTitle); - - const closeButton = document.createElement('button'); - closeButton.textContent = 'Γ—'; - if (USE_CUSTOM_STYLING) { - closeButton.style.cssText = ` + } + + titleContainer.appendChild(title); + titleContainer.appendChild(pageTitle); + + const closeButton = document.createElement("button"); + closeButton.textContent = "Γ—"; + if (USE_CUSTOM_STYLING) { + closeButton.style.cssText = ` background: none; border: none; color: white; @@ -579,8 +606,8 @@ export async function openHermesModal() { align-items: center; justify-content: center; `; - } else { - closeButton.style.cssText = ` + } else { + closeButton.style.cssText = ` background: var(--background-color); border: 1px solid var(--border-color); color: var(--color); @@ -595,21 +622,21 @@ export async function openHermesModal() { justify-content: center; transition: opacity 0.2s; `; - closeButton.onmouseenter = () => closeButton.style.opacity = '0.7'; - closeButton.onmouseleave = () => closeButton.style.opacity = '1'; - } - closeButton.onclick = () => { - document.body.removeChild(modal); - hermesModalOpen = false; - }; - - header.appendChild(titleContainer); - header.appendChild(closeButton); - - // Create controls section - const controls = document.createElement('div'); - if (USE_CUSTOM_STYLING) { - controls.style.cssText = ` + closeButton.onmouseenter = () => (closeButton.style.opacity = "0.7"); + closeButton.onmouseleave = () => (closeButton.style.opacity = "1"); + } + closeButton.onclick = () => { + document.body.removeChild(modal); + hermesModalOpen = false; + }; + + header.appendChild(titleContainer); + header.appendChild(closeButton); + + // Create controls section + const controls = document.createElement("div"); + if (USE_CUSTOM_STYLING) { + controls.style.cssText = ` padding: 16px 20px; border-bottom: 1px solid #e0e0e0; display: grid; @@ -617,337 +644,359 @@ export async function openHermesModal() { gap: 12px; align-items: center; `; - } else { - controls.className = 'uncloseai-controls'; - controls.style.cssText = ` + } else { + controls.className = "uncloseai-controls"; + controls.style.cssText = ` display: grid; grid-template-columns: auto 1fr auto 1fr; gap: 12px; padding: 0.5rem; align-items: center; `; - } - - // Add model selection dropdown - const modelLabel = document.createElement('label'); - modelLabel.textContent = 'Model: '; - if (USE_CUSTOM_STYLING) { - modelLabel.style.fontWeight = 'bold'; - } - - const modelSelect = document.createElement('select'); - modelSelect.id = 'modal-model-selection'; - if (USE_CUSTOM_STYLING) { - modelSelect.style.cssText = ` + } + + // Add model selection dropdown + const modelLabel = document.createElement("label"); + modelLabel.textContent = "Model: "; + if (USE_CUSTOM_STYLING) { + modelLabel.style.fontWeight = "bold"; + } + + const modelSelect = document.createElement("select"); + modelSelect.id = "modal-model-selection"; + if (USE_CUSTOM_STYLING) { + modelSelect.style.cssText = ` padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; `; - } - - // Add loading placeholder - const loadingOption = document.createElement('option'); - loadingOption.textContent = 'Loading models...'; - loadingOption.disabled = true; - modelSelect.appendChild(loadingOption); - - // Populate model dropdown dynamically (async) - fetchModelsFromEndpoints().then(models => { - modelSelect.innerHTML = ''; // Clear loading option - models.forEach(model => { - const option = document.createElement('option'); - option.value = model.uniqueId; - option.textContent = `${model.endpointId} | ${model.modelName}`; - modelSelect.appendChild(option); - }); - - // Restore saved model selection after loading - const savedModel = localStorage.getItem('hermes-selected-model'); - if (savedModel && modelSelect.querySelector(`option[value="${savedModel}"]`)) { - modelSelect.value = savedModel; - } - }).catch(error => { - console.error('Error loading models:', error); - modelSelect.innerHTML = ''; - const errorOption = document.createElement('option'); - errorOption.textContent = 'Error loading models'; - errorOption.disabled = true; - modelSelect.appendChild(errorOption); - }); - - // Save model selection on change - modelSelect.addEventListener('change', () => { - localStorage.setItem('hermes-selected-model', modelSelect.value); - }); - - // Add voice selection with newline - const voiceBreak = document.createElement('div'); - voiceBreak.style.width = '100%'; - - const voiceLabel = document.createElement('label'); - voiceLabel.textContent = 'Voice: '; - if (USE_CUSTOM_STYLING) { - voiceLabel.style.fontWeight = 'bold'; - } - - const voiceSelect = document.createElement('select'); - voiceSelect.id = 'modal-voice-selection'; - if (USE_CUSTOM_STYLING) { - voiceSelect.style.cssText = ` + } + + // Add loading placeholder + const loadingOption = document.createElement("option"); + loadingOption.textContent = "Loading models..."; + loadingOption.disabled = true; + modelSelect.appendChild(loadingOption); + + // Populate model dropdown dynamically (async) + fetchModelsFromEndpoints() + .then((models) => { + modelSelect.innerHTML = ""; // Clear loading option + models.forEach((model) => { + const option = document.createElement("option"); + option.value = model.uniqueId; + option.textContent = `${model.endpointId} | ${model.modelName}`; + modelSelect.appendChild(option); + }); + + // Restore saved model selection after loading + const savedModel = localStorage.getItem("hermes-selected-model"); + if ( + savedModel && + modelSelect.querySelector(`option[value="${savedModel}"]`) + ) { + modelSelect.value = savedModel; + } + }) + .catch((error) => { + console.error("Error loading models:", error); + modelSelect.innerHTML = ""; + const errorOption = document.createElement("option"); + errorOption.textContent = "Error loading models"; + errorOption.disabled = true; + modelSelect.appendChild(errorOption); + }); + + // Save model selection on change + modelSelect.addEventListener("change", () => { + localStorage.setItem("hermes-selected-model", modelSelect.value); + }); + + // Add voice selection with newline + const voiceBreak = document.createElement("div"); + voiceBreak.style.width = "100%"; + + const voiceLabel = document.createElement("label"); + voiceLabel.textContent = "Voice: "; + if (USE_CUSTOM_STYLING) { + voiceLabel.style.fontWeight = "bold"; + } + + const voiceSelect = document.createElement("select"); + voiceSelect.id = "modal-voice-selection"; + if (USE_CUSTOM_STYLING) { + voiceSelect.style.cssText = ` padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; `; - } - - const voices = ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer']; - voices.forEach(voice => { - const option = document.createElement('option'); - option.value = voice; - option.textContent = voice; - if (voice === 'alloy') option.selected = true; - voiceSelect.appendChild(option); - }); - - // Restore saved voice selection - const savedVoice = localStorage.getItem('hermes-selected-voice'); - if (savedVoice && voices.includes(savedVoice)) { - voiceSelect.value = savedVoice; - } - - // Save voice selection on change - voiceSelect.addEventListener('change', () => { - localStorage.setItem('hermes-selected-voice', voiceSelect.value); - }); - - // Add action buttons - const actionButtons = document.createElement('div'); - if (USE_CUSTOM_STYLING) { - actionButtons.style.cssText = ` + } + + const voices = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]; + voices.forEach((voice) => { + const option = document.createElement("option"); + option.value = voice; + option.textContent = voice; + if (voice === "alloy") option.selected = true; + voiceSelect.appendChild(option); + }); + + // Restore saved voice selection + const savedVoice = localStorage.getItem("hermes-selected-voice"); + if (savedVoice && voices.includes(savedVoice)) { + voiceSelect.value = savedVoice; + } + + // Save voice selection on change + voiceSelect.addEventListener("change", () => { + localStorage.setItem("hermes-selected-voice", voiceSelect.value); + }); + + // Add action buttons + const actionButtons = document.createElement("div"); + if (USE_CUSTOM_STYLING) { + actionButtons.style.cssText = ` display: grid; grid-template-columns: repeat(auto-fit, minmax(60px, 1fr)); gap: 4px; `; - } else { - actionButtons.className = 'uncloseai-button-group'; - actionButtons.style.cssText = ` + } else { + actionButtons.className = "uncloseai-button-group"; + actionButtons.style.cssText = ` display: grid; grid-template-columns: repeat(auto-fit, minmax(60px, 1fr)); gap: 4px; `; - } - - const readPageBtn = document.createElement('button'); - readPageBtn.textContent = 'πŸ“– Read Page'; - if (USE_CUSTOM_STYLING) { - readPageBtn.style.cssText = 'padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer;'; - } - readPageBtn.onclick = readPageWithHermes; - - const ttsBtn = document.createElement('button'); - ttsBtn.textContent = 'πŸ”Š TTS Anything'; - if (USE_CUSTOM_STYLING) { - ttsBtn.style.cssText = 'padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer;'; - } - ttsBtn.onclick = openTTSModal; - - const refreshBtn = document.createElement('button'); - refreshBtn.textContent = 'πŸ”„ Refresh'; - if (USE_CUSTOM_STYLING) { - refreshBtn.style.cssText = 'padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer;'; - } - refreshBtn.onclick = async () => { - // Clear cache and refresh models - localStorage.removeItem('modelRegistryCache'); - localStorage.removeItem('vllmEndpointsHash'); - const models = await fetchModelsFromEndpoints(); - - // Update modal dropdown - modelSelect.innerHTML = ''; - models.forEach(model => { - const option = document.createElement('option'); - option.value = model.uniqueId; - option.textContent = `${model.endpointId} | ${model.modelName}`; - modelSelect.appendChild(option); - }); - - // Update main page dropdown if it exists - const mainDropdown = document.getElementById('model-selection'); - if (mainDropdown) { - mainDropdown.innerHTML = ''; - models.forEach(model => { - const option = document.createElement('option'); - option.value = model.uniqueId; - option.textContent = `${model.endpointId} | ${model.modelName}`; - mainDropdown.appendChild(option); - }); - } - }; - - const clearBtn = document.createElement('button'); - clearBtn.textContent = 'πŸ—‘οΈ Clear'; - if (USE_CUSTOM_STYLING) { - clearBtn.style.cssText = 'padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer;'; - } - clearBtn.onclick = async () => { - if (confirm('Clear all conversation history?')) { - clearConversationHistory(); - chatBox.innerHTML = ''; - await addHermesIntroduction(); // Call addHermesIntroduction after clearing - } - }; - - actionButtons.appendChild(readPageBtn); - actionButtons.appendChild(ttsBtn); - actionButtons.appendChild(refreshBtn); - actionButtons.appendChild(clearBtn); - - // Make action buttons span all columns - actionButtons.style.gridColumn = 'span 4'; - - controls.appendChild(actionButtons); - controls.appendChild(modelLabel); - controls.appendChild(modelSelect); - controls.appendChild(voiceLabel); - controls.appendChild(voiceSelect); - - // Create chat area - const chatArea = document.createElement('div'); - if (USE_CUSTOM_STYLING) { - chatArea.style.cssText = ` + } + + const readPageBtn = document.createElement("button"); + readPageBtn.textContent = "πŸ“– Read Page"; + if (USE_CUSTOM_STYLING) { + readPageBtn.style.cssText = + "padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer;"; + } + readPageBtn.onclick = readPageWithHermes; + + const ttsBtn = document.createElement("button"); + ttsBtn.textContent = "πŸ”Š TTS Anything"; + if (USE_CUSTOM_STYLING) { + ttsBtn.style.cssText = + "padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer;"; + } + ttsBtn.onclick = openTTSModal; + + const refreshBtn = document.createElement("button"); + refreshBtn.textContent = "πŸ”„ Refresh"; + if (USE_CUSTOM_STYLING) { + refreshBtn.style.cssText = + "padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer;"; + } + refreshBtn.onclick = async () => { + // Clear cache and refresh models + localStorage.removeItem("modelRegistryCache"); + localStorage.removeItem("vllmEndpointsHash"); + const models = await fetchModelsFromEndpoints(); + + // Update modal dropdown + modelSelect.innerHTML = ""; + models.forEach((model) => { + const option = document.createElement("option"); + option.value = model.uniqueId; + option.textContent = `${model.endpointId} | ${model.modelName}`; + modelSelect.appendChild(option); + }); + + // Update main page dropdown if it exists + const mainDropdown = document.getElementById("model-selection"); + if (mainDropdown) { + mainDropdown.innerHTML = ""; + models.forEach((model) => { + const option = document.createElement("option"); + option.value = model.uniqueId; + option.textContent = `${model.endpointId} | ${model.modelName}`; + mainDropdown.appendChild(option); + }); + } + }; + + const clearBtn = document.createElement("button"); + clearBtn.textContent = "πŸ—‘οΈ Clear"; + if (USE_CUSTOM_STYLING) { + clearBtn.style.cssText = + "padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer;"; + } + clearBtn.onclick = async () => { + if (confirm("Clear all conversation history?")) { + clearConversationHistory(); + chatBox.innerHTML = ""; + await addHermesIntroduction(); // Call addHermesIntroduction after clearing + } + }; + + actionButtons.appendChild(readPageBtn); + actionButtons.appendChild(ttsBtn); + actionButtons.appendChild(refreshBtn); + actionButtons.appendChild(clearBtn); + + // Make action buttons span all columns + actionButtons.style.gridColumn = "span 4"; + + controls.appendChild(actionButtons); + controls.appendChild(modelLabel); + controls.appendChild(modelSelect); + controls.appendChild(voiceLabel); + controls.appendChild(voiceSelect); + + // Create chat area + const chatArea = document.createElement("div"); + if (USE_CUSTOM_STYLING) { + chatArea.style.cssText = ` grid-row: 3; padding: 20px; overflow-y: auto; border-bottom: 1px solid #e0e0e0; `; - } else { - chatArea.style.cssText = ` + } else { + chatArea.style.cssText = ` grid-row: 3; padding: 1rem; overflow-y: auto; border-bottom: 1px solid #e0e0e0; min-height: 200px; `; - } - - const chatBox = document.createElement('div'); - chatBox.id = 'modal-chat-box'; - if (USE_CUSTOM_STYLING) { - chatBox.style.cssText = ` + } + + const chatBox = document.createElement("div"); + chatBox.id = "modal-chat-box"; + if (USE_CUSTOM_STYLING) { + chatBox.style.cssText = ` height: 100%; overflow-y: auto; `; - } else { - chatBox.style.cssText = ` + } else { + chatBox.style.cssText = ` min-height: 150px; overflow-y: auto; `; - } - - chatArea.appendChild(chatBox); - - // Import necessary functions from other modules - const { loadConversationHistory, saveConversationHistory, clearConversationHistory } = await import('./storage.js'); - const { extractWebpageContent } = await import('./content.js'); - const { sendMessage } = await import('./chat.js'); - const { getSystemMessage } = await import('./config.js'); - - // Restore conversation history in modal - function restoreConversationHistory() { - const history = loadConversationHistory(); - chatBox.innerHTML = ''; - // Filter out system messages before displaying - const displayHistory = history.filter(msg => msg.role !== 'system'); - displayHistory.forEach((msg, index) => { - const messageDiv = document.createElement('div'); - messageDiv.style.cssText = 'position: relative; margin-bottom: 10px; padding: 5px; border-radius: 5px; background: rgba(0,0,0,0.05);'; - - const deleteBtn = document.createElement('button'); - deleteBtn.textContent = 'Γ—'; - deleteBtn.style.cssText = 'position: absolute; top: 2px; right: 2px; width: 20px; height: 20px; font-size: 12px; background: rgba(255,0,0,0.7); color: white; border: none; border-radius: 50%; cursor: pointer;'; - deleteBtn.onclick = ((messageIndex) => { - return () => { - if (confirm('Delete this message?')) { - const savedHistory = loadConversationHistory(); - if (messageIndex >= 0 && messageIndex < savedHistory.length) { - savedHistory.splice(messageIndex, 1); - localStorage.setItem(getPageSpecificKey('hermes-conversation-history'), JSON.stringify(savedHistory)); - // Update global chat history - chatHistory = [ - { - role: "system", - content: getSystemMessage() - }, - ...savedHistory - ]; - restoreConversationHistory(); - } - } - }; - })(index); - - if (msg.role === 'user') { - messageDiv.innerHTML = `

You: ${msg.content}

`; - } else if (msg.role === 'assistant') { - const parsedContent = marked.parse(msg.content); - messageDiv.innerHTML = `

AI: ${parsedContent}

`; - // Add TTS button to AI messages in history - addTTSButtonToMessage(messageDiv, msg.content); - } - - messageDiv.appendChild(deleteBtn); - chatBox.appendChild(messageDiv); - }); - chatBox.scrollTop = chatBox.scrollHeight; - } - restoreConversationHistory(); - - // Generate dynamic Hermes introduction using LLM - async function addHermesIntroduction() { - const history = loadConversationHistory(); - if (history.length === 0) { - const pageContent = extractWebpageContent(); - const pageTitle = document.title; - const pageUrl = window.location.href; - - // Create cache key based on page content hash (handle Unicode safely) - const contentForHash = pageContent.substring(0, 1000); - let contentHash; - try { - contentHash = btoa(unescape(encodeURIComponent(contentForHash))).replace(/[^a-zA-Z0-9]/g, '').substring(0, 32); - } catch (e) { - // Fallback: use simple string hash if btoa fails - let hash = 0; - for (let i = 0; i < contentForHash.length; i++) { - const char = contentForHash.charCodeAt(i); - hash = ((hash << 5) - hash) + char; - hash = hash & hash; // Convert to 32bit integer - } - contentHash = Math.abs(hash).toString(36).substring(0, 32); - } - const cacheKey = `hermes-intro-${contentHash}`; - - // Check if we have a cached introduction for this page content - const cachedIntro = localStorage.getItem(cacheKey); - if (cachedIntro) { - displayIntroduction(cachedIntro); - // Add cached intro to chat history - chatHistory.push({ role: "assistant", content: cachedIntro }); - saveConversationHistory(chatHistory); - return; - } - - // Generate new introduction using LLM - const introDiv = document.createElement('div'); - introDiv.style.cssText = 'position: relative; margin-bottom: 10px; padding: 10px; border-radius: 5px; background: rgba(0,100,200,0.1); border-left: 4px solid #0066cc;'; - introDiv.innerHTML = '

πŸ€– Hermes: ✨ Analyzing this page and crafting a personalized introduction... This may take a moment.

'; - chatBox.appendChild(introDiv); - chatBox.scrollTop = chatBox.scrollHeight; - - try { - const prompt = `You are Hermes, a large language model from Nous Research. Write a friendly 3-paragraph introduction for yourself when embedded on this webpage. Be specific about this page's content and identify 2-3 key takeaways. Keep it conversational and helpful. + } + + chatArea.appendChild(chatBox); + + // Import necessary functions from other modules + const { + loadConversationHistory, + saveConversationHistory, + clearConversationHistory, + } = await import("./storage.js"); + const { extractWebpageContent } = await import("./content.js"); + const { sendMessage } = await import("./chat.js"); + const { getSystemMessage } = await import("./config.js"); + + // Restore conversation history in modal + function restoreConversationHistory() { + const history = loadConversationHistory(); + chatBox.innerHTML = ""; + // Filter out system messages before displaying + const displayHistory = history.filter((msg) => msg.role !== "system"); + displayHistory.forEach((msg, index) => { + const messageDiv = document.createElement("div"); + messageDiv.style.cssText = + "position: relative; margin-bottom: 10px; padding: 5px; border-radius: 5px; background: rgba(0,0,0,0.05);"; + + const deleteBtn = document.createElement("button"); + deleteBtn.textContent = "Γ—"; + deleteBtn.style.cssText = + "position: absolute; top: 2px; right: 2px; width: 20px; height: 20px; font-size: 12px; background: rgba(255,0,0,0.7); color: white; border: none; border-radius: 50%; cursor: pointer;"; + deleteBtn.onclick = ((messageIndex) => { + return () => { + if (confirm("Delete this message?")) { + const savedHistory = loadConversationHistory(); + if (messageIndex >= 0 && messageIndex < savedHistory.length) { + savedHistory.splice(messageIndex, 1); + localStorage.setItem( + getPageSpecificKey("hermes-conversation-history"), + JSON.stringify(savedHistory), + ); + // Update global chat history + chatHistory = [ + { + role: "system", + content: getSystemMessage(), + }, + ...savedHistory, + ]; + restoreConversationHistory(); + } + } + }; + })(index); + + if (msg.role === "user") { + messageDiv.innerHTML = `

You: ${msg.content}

`; + } else if (msg.role === "assistant") { + const parsedContent = marked.parse(msg.content); + messageDiv.innerHTML = `

AI: ${parsedContent}

`; + // Add TTS button to AI messages in history + addTTSButtonToMessage(messageDiv, msg.content); + } + + messageDiv.appendChild(deleteBtn); + chatBox.appendChild(messageDiv); + }); + chatBox.scrollTop = chatBox.scrollHeight; + } + restoreConversationHistory(); + + // Generate dynamic Hermes introduction using LLM + async function addHermesIntroduction() { + const history = loadConversationHistory(); + if (history.length === 0) { + const pageContent = extractWebpageContent(); + const pageTitle = document.title; + const pageUrl = window.location.href; + + // Create cache key based on page content hash (handle Unicode safely) + const contentForHash = pageContent.substring(0, 1000); + let contentHash; + try { + contentHash = btoa(unescape(encodeURIComponent(contentForHash))) + .replace(/[^a-zA-Z0-9]/g, "") + .substring(0, 32); + } catch (e) { + // Fallback: use simple string hash if btoa fails + let hash = 0; + for (let i = 0; i < contentForHash.length; i++) { + const char = contentForHash.charCodeAt(i); + hash = (hash << 5) - hash + char; + hash = hash & hash; // Convert to 32bit integer + } + contentHash = Math.abs(hash).toString(36).substring(0, 32); + } + const cacheKey = `hermes-intro-${contentHash}`; + + // Check if we have a cached introduction for this page content + const cachedIntro = localStorage.getItem(cacheKey); + if (cachedIntro) { + displayIntroduction(cachedIntro); + // Add cached intro to chat history + chatHistory.push({ role: "assistant", content: cachedIntro }); + saveConversationHistory(chatHistory); + return; + } + + // Generate new introduction using LLM + const introDiv = document.createElement("div"); + introDiv.style.cssText = + "position: relative; margin-bottom: 10px; padding: 10px; border-radius: 5px; background: rgba(0,100,200,0.1); border-left: 4px solid #0066cc;"; + introDiv.innerHTML = + "

πŸ€– Hermes: ✨ Analyzing this page and crafting a personalized introduction... This may take a moment.

"; + chatBox.appendChild(introDiv); + chatBox.scrollTop = chatBox.scrollHeight; + + try { + const prompt = `You are Hermes, a large language model from Nous Research. Write a friendly 3-paragraph introduction for yourself when embedded on this webpage. Be specific about this page's content and identify 2-3 key takeaways. Keep it conversational and helpful. Page Title: ${pageTitle} Page URL: ${pageUrl} @@ -955,159 +1004,164 @@ Page Content: ${pageContent.substring(0, 2000)} Format: Start with "Greetings! I'm Hermes..." and make it sound natural and engaging. Write 3 full paragraphs that showcase your capabilities and how you can help with THIS specific page.`; - let generatedIntro = ''; - for await (const chunk of sendMessage(prompt)) { - generatedIntro += chunk; - // Update display in real-time - introDiv.innerHTML = `

πŸ€– Hermes: ${generatedIntro}

`; - // Scroll to bottom as content updates - chatBox.scrollTop = chatBox.scrollHeight; - } - - // Add to chat history and save - chatHistory.push({ role: "assistant", content: generatedIntro }); - saveConversationHistory(chatHistory); - - // Cache the generated introduction - localStorage.setItem(cacheKey, generatedIntro); - - // Add TTS button to the generated introduction - addTTSButtonToMessage(introDiv, generatedIntro); - - chatBox.scrollTop = chatBox.scrollHeight; - - } catch (error) { - console.error('Error generating introduction:', error); - const fallbackIntro = 'Greetings! I\'m Hermes, a large language model from Nous Research. I\'m here to help you understand this page and assist with any questions, coding, or creative tasks you might have. Feel free to ask me anything!'; - introDiv.innerHTML = `

πŸ€– Hermes: ${fallbackIntro}

`; - // Add fallback to chat history too - chatHistory.push({ role: "assistant", content: fallbackIntro }); - saveConversationHistory(chatHistory); - } - } - } - - function displayIntroduction(introText) { - const introDiv = document.createElement('div'); - introDiv.style.cssText = 'position: relative; margin-bottom: 10px; padding: 10px; border-radius: 5px; background: rgba(0,100,200,0.1); border-left: 4px solid #0066cc;'; - introDiv.innerHTML = `

πŸ€– Hermes: ${introText}

`; - chatBox.appendChild(introDiv); - - // Add TTS button to introduction - addTTSButtonToMessage(introDiv, introText); - - chatBox.scrollTop = chatBox.scrollHeight; - } - - // Add TTS button to any message - function addTTSButtonToMessage(messageDiv, messageText) { - const ttsContainer = document.createElement('div'); - ttsContainer.style.cssText = 'display: flex; gap: 8px; margin: 8px 0; align-items: center;'; - - const ttsBtn = document.createElement('button'); - ttsBtn.textContent = 'πŸ”Š Play Response'; - if (USE_CUSTOM_STYLING) { - ttsBtn.style.cssText = 'padding: 4px 8px; border: 1px solid #0066cc; border-radius: 4px; background: #f0f8ff; cursor: pointer; color: #0066cc; font-size: 12px;'; - } else { - ttsBtn.style.cssText = 'padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 12px;'; - } - - const downloadBtn = document.createElement('button'); - downloadBtn.textContent = 'πŸ’Ύ Download'; - downloadBtn.style.display = 'none'; - if (USE_CUSTOM_STYLING) { - downloadBtn.style.cssText = 'padding: 4px 8px; border: 1px solid #0066cc; border-radius: 4px; background: #f0f8ff; cursor: pointer; color: #0066cc; font-size: 12px;'; - } else { - downloadBtn.style.cssText = 'padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 12px;'; - } - - let messageAudio = null; - let messageBlob = null; - - ttsBtn.onclick = async () => { - if (!messageAudio) { - ttsBtn.textContent = 'Processing...'; - ttsBtn.disabled = true; - - try { - const selectedVoice = voiceSelect.value; - const result = await speakText(messageText, selectedVoice, 0.9); - messageAudio = result.audio; - messageBlob = result.blob; - - ttsBtn.textContent = '⏸️ Pause'; - ttsBtn.disabled = false; - downloadBtn.style.display = 'inline-block'; - - messageAudio.play(); - - messageAudio.onended = () => { - ttsBtn.textContent = 'πŸ”Š Play Response'; - }; - - } catch (error) { - console.error('Error generating TTS:', error); - ttsBtn.textContent = 'πŸ”Š Play Response'; - ttsBtn.disabled = false; - } - } else { - if (messageAudio.paused) { - messageAudio.play(); - ttsBtn.textContent = '⏸️ Pause'; - } else { - messageAudio.pause(); - ttsBtn.textContent = '▢️ Resume'; - } - } - }; - - downloadBtn.onclick = () => { - if (messageBlob) { - const a = document.createElement('a'); - a.href = URL.createObjectURL(messageBlob); - a.download = `hermes-response-${Date.now()}.mp3`; - a.click(); - } - }; - - ttsContainer.appendChild(ttsBtn); - ttsContainer.appendChild(downloadBtn); - messageDiv.appendChild(ttsContainer); - } - - // Show the modal first, then load intro asynchronously - document.body.appendChild(modal); - - // Load Hermes introduction asynchronously after modal is shown - addHermesIntroduction().catch(error => { - console.error('Error generating Hermes introduction:', error); - }); - - // Create input area - const inputArea = document.createElement('div'); - if (USE_CUSTOM_STYLING) { - inputArea.style.cssText = ` + let generatedIntro = ""; + for await (const chunk of sendMessage(prompt)) { + generatedIntro += chunk; + // Update display in real-time + introDiv.innerHTML = `

πŸ€– Hermes: ${generatedIntro}

`; + // Scroll to bottom as content updates + chatBox.scrollTop = chatBox.scrollHeight; + } + + // Add to chat history and save + chatHistory.push({ role: "assistant", content: generatedIntro }); + saveConversationHistory(chatHistory); + + // Cache the generated introduction + localStorage.setItem(cacheKey, generatedIntro); + + // Add TTS button to the generated introduction + addTTSButtonToMessage(introDiv, generatedIntro); + + chatBox.scrollTop = chatBox.scrollHeight; + } catch (error) { + console.error("Error generating introduction:", error); + const fallbackIntro = + "Greetings! I'm Hermes, a large language model from Nous Research. I'm here to help you understand this page and assist with any questions, coding, or creative tasks you might have. Feel free to ask me anything!"; + introDiv.innerHTML = `

πŸ€– Hermes: ${fallbackIntro}

`; + // Add fallback to chat history too + chatHistory.push({ role: "assistant", content: fallbackIntro }); + saveConversationHistory(chatHistory); + } + } + } + + function displayIntroduction(introText) { + const introDiv = document.createElement("div"); + introDiv.style.cssText = + "position: relative; margin-bottom: 10px; padding: 10px; border-radius: 5px; background: rgba(0,100,200,0.1); border-left: 4px solid #0066cc;"; + introDiv.innerHTML = `

πŸ€– Hermes: ${introText}

`; + chatBox.appendChild(introDiv); + + // Add TTS button to introduction + addTTSButtonToMessage(introDiv, introText); + + chatBox.scrollTop = chatBox.scrollHeight; + } + + // Add TTS button to any message + function addTTSButtonToMessage(messageDiv, messageText) { + const ttsContainer = document.createElement("div"); + ttsContainer.style.cssText = + "display: flex; gap: 8px; margin: 8px 0; align-items: center;"; + + const ttsBtn = document.createElement("button"); + ttsBtn.textContent = "πŸ”Š Play Response"; + if (USE_CUSTOM_STYLING) { + ttsBtn.style.cssText = + "padding: 4px 8px; border: 1px solid #0066cc; border-radius: 4px; background: #f0f8ff; cursor: pointer; color: #0066cc; font-size: 12px;"; + } else { + ttsBtn.style.cssText = + "padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 12px;"; + } + + const downloadBtn = document.createElement("button"); + downloadBtn.textContent = "πŸ’Ύ Download"; + downloadBtn.style.display = "none"; + if (USE_CUSTOM_STYLING) { + downloadBtn.style.cssText = + "padding: 4px 8px; border: 1px solid #0066cc; border-radius: 4px; background: #f0f8ff; cursor: pointer; color: #0066cc; font-size: 12px;"; + } else { + downloadBtn.style.cssText = + "padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 12px;"; + } + + let messageAudio = null; + let messageBlob = null; + + ttsBtn.onclick = async () => { + if (!messageAudio) { + ttsBtn.textContent = "Processing..."; + ttsBtn.disabled = true; + + try { + const selectedVoice = voiceSelect.value; + const result = await speakText(messageText, selectedVoice, 0.9); + messageAudio = result.audio; + messageBlob = result.blob; + + ttsBtn.textContent = "⏸️ Pause"; + ttsBtn.disabled = false; + downloadBtn.style.display = "inline-block"; + + messageAudio.play(); + + messageAudio.onended = () => { + ttsBtn.textContent = "πŸ”Š Play Response"; + }; + } catch (error) { + console.error("Error generating TTS:", error); + ttsBtn.textContent = "πŸ”Š Play Response"; + ttsBtn.disabled = false; + } + } else { + if (messageAudio.paused) { + messageAudio.play(); + ttsBtn.textContent = "⏸️ Pause"; + } else { + messageAudio.pause(); + ttsBtn.textContent = "▢️ Resume"; + } + } + }; + + downloadBtn.onclick = () => { + if (messageBlob) { + const a = document.createElement("a"); + a.href = URL.createObjectURL(messageBlob); + a.download = `hermes-response-${Date.now()}.mp3`; + a.click(); + } + }; + + ttsContainer.appendChild(ttsBtn); + ttsContainer.appendChild(downloadBtn); + messageDiv.appendChild(ttsContainer); + } + + // Show the modal first, then load intro asynchronously + document.body.appendChild(modal); + + // Load Hermes introduction asynchronously after modal is shown + addHermesIntroduction().catch((error) => { + console.error("Error generating Hermes introduction:", error); + }); + + // Create input area + const inputArea = document.createElement("div"); + if (USE_CUSTOM_STYLING) { + inputArea.style.cssText = ` grid-row: 4; padding: 20px; display: flex; gap: 12px; align-items: flex-end; `; - } else { - inputArea.style.cssText = ` + } else { + inputArea.style.cssText = ` grid-row: 4; padding: 1rem; display: flex; gap: 12px; align-items: flex-end; `; - } - - const userInput = document.createElement('textarea'); - userInput.id = 'modal-user-input'; - userInput.placeholder = 'Ask about this page...'; - if (USE_CUSTOM_STYLING) { - userInput.style.cssText = ` + } + + const userInput = document.createElement("textarea"); + userInput.id = "modal-user-input"; + userInput.placeholder = "Ask about this page..."; + if (USE_CUSTOM_STYLING) { + userInput.style.cssText = ` flex: 1; padding: 12px; border: 2px solid #e0e0e0; @@ -1118,20 +1172,20 @@ Format: Start with "Greetings! I'm Hermes..." and make it sound natural and enga min-height: 44px; max-height: 120px; `; - } else { - userInput.style.cssText = ` + } else { + userInput.style.cssText = ` flex: 1; padding: 0.5rem; min-height: 44px; max-height: 120px; resize: vertical; `; - } - - const sendButton = document.createElement('button'); - sendButton.textContent = 'Send'; - if (USE_CUSTOM_STYLING) { - sendButton.style.cssText = ` + } + + const sendButton = document.createElement("button"); + sendButton.textContent = "Send"; + if (USE_CUSTOM_STYLING) { + sendButton.style.cssText = ` padding: 12px 24px; background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); color: white; @@ -1141,120 +1195,123 @@ Format: Start with "Greetings! I'm Hermes..." and make it sound natural and enga font-weight: bold; min-height: 44px; `; - } else { - sendButton.style.cssText = ` + } else { + sendButton.style.cssText = ` padding: 0.75rem 1.5rem; min-height: 44px; `; - } - - // Handle message sending - const handleModalInput = async () => { - const message = userInput.value.trim(); - if (!message) return; - - userInput.value = ''; - userInput.style.height = 'auto'; // Reset height - - // Add user message to chat - const userDiv = document.createElement('div'); - userDiv.style.cssText = 'position: relative; margin-bottom: 10px; padding: 5px; border-radius: 5px; background: rgba(0,0,0,0.05);'; - userDiv.innerHTML = `

You: ${message}

`; - chatBox.appendChild(userDiv); - - // Add AI response placeholder - const aiDiv = document.createElement('div'); - aiDiv.style.cssText = 'position: relative; margin-bottom: 10px; padding: 5px; border-radius: 5px; background: rgba(0,0,0,0.05);'; - aiDiv.innerHTML = '

AI: thinking...

'; - chatBox.appendChild(aiDiv); - chatBox.scrollTop = chatBox.scrollHeight; - - try { - let response = ''; - for await (const chunk of sendMessage(message)) { - response += chunk; - const parsedResponse = marked.parse(response); - aiDiv.innerHTML = `

AI: ${parsedResponse}

`; - chatBox.scrollTop = chatBox.scrollHeight; - } - - // Add AI response to chat history and save - const { getChatHistory } = await import('./chat.js'); - const currentHistory = getChatHistory(); - currentHistory.push({ role: "assistant", content: response }); - saveConversationHistory(currentHistory); - - // Add delete button and TTS button to messages - [userDiv, aiDiv].forEach((div, index) => { - const deleteBtn = document.createElement('button'); - deleteBtn.textContent = 'Γ—'; - deleteBtn.style.cssText = 'position: absolute; top: 2px; right: 2px; width: 20px; height: 20px; font-size: 12px; background: rgba(255,0,0,0.7); color: white; border: none; border-radius: 50%; cursor: pointer;'; - deleteBtn.onclick = () => { - if (confirm('Delete this message?')) { - div.remove(); - // Update stored history - restoreConversationHistory(); - } - }; - div.appendChild(deleteBtn); - - // Add TTS button to AI response - if (index === 1) { // aiDiv - addTTSButtonToMessage(div, response); - } - }); - - } catch (error) { - aiDiv.innerHTML = `

Error: ${error.message}

`; - } - }; - - sendButton.onclick = handleModalInput; - - // Handle Enter key (Shift+Enter for new line) - userInput.addEventListener('keydown', (e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - handleModalInput(); - } - }); - - inputArea.appendChild(userInput); - inputArea.appendChild(sendButton); - - article.appendChild(header); - article.appendChild(controls); - article.appendChild(chatArea); - article.appendChild(inputArea); + } + + // Handle message sending + const handleModalInput = async () => { + const message = userInput.value.trim(); + if (!message) return; + + userInput.value = ""; + userInput.style.height = "auto"; // Reset height + + // Add user message to chat + const userDiv = document.createElement("div"); + userDiv.style.cssText = + "position: relative; margin-bottom: 10px; padding: 5px; border-radius: 5px; background: rgba(0,0,0,0.05);"; + userDiv.innerHTML = `

You: ${message}

`; + chatBox.appendChild(userDiv); + + // Add AI response placeholder + const aiDiv = document.createElement("div"); + aiDiv.style.cssText = + "position: relative; margin-bottom: 10px; padding: 5px; border-radius: 5px; background: rgba(0,0,0,0.05);"; + aiDiv.innerHTML = "

AI: thinking...

"; + chatBox.appendChild(aiDiv); + chatBox.scrollTop = chatBox.scrollHeight; + + try { + let response = ""; + for await (const chunk of sendMessage(message)) { + response += chunk; + const parsedResponse = marked.parse(response); + aiDiv.innerHTML = `

AI: ${parsedResponse}

`; + chatBox.scrollTop = chatBox.scrollHeight; + } + + // Add AI response to chat history and save + const { getChatHistory } = await import("./chat.js"); + const currentHistory = getChatHistory(); + currentHistory.push({ role: "assistant", content: response }); + saveConversationHistory(currentHistory); + + // Add delete button and TTS button to messages + [userDiv, aiDiv].forEach((div, index) => { + const deleteBtn = document.createElement("button"); + deleteBtn.textContent = "Γ—"; + deleteBtn.style.cssText = + "position: absolute; top: 2px; right: 2px; width: 20px; height: 20px; font-size: 12px; background: rgba(255,0,0,0.7); color: white; border: none; border-radius: 50%; cursor: pointer;"; + deleteBtn.onclick = () => { + if (confirm("Delete this message?")) { + div.remove(); + // Update stored history + restoreConversationHistory(); + } + }; + div.appendChild(deleteBtn); + + // Add TTS button to AI response + if (index === 1) { + // aiDiv + addTTSButtonToMessage(div, response); + } + }); + } catch (error) { + aiDiv.innerHTML = `

Error: ${error.message}

`; + } + }; + + sendButton.onclick = handleModalInput; + + // Handle Enter key (Shift+Enter for new line) + userInput.addEventListener("keydown", (e) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + handleModalInput(); + } + }); + + inputArea.appendChild(userInput); + inputArea.appendChild(sendButton); + + article.appendChild(header); + article.appendChild(controls); + article.appendChild(chatArea); + article.appendChild(inputArea); } // TTS Modal functionality export function openTTSModal() { - // Check if Hermes modal is open to set appropriate z-index - const hermesModal = document.getElementById('hermes-modal'); - const zIndex = hermesModal ? '2001' : '1001'; - - const modal = document.createElement('dialog'); - modal.open = true; - modal.style.zIndex = zIndex; - modal.style.position = 'fixed'; - - if (USE_CUSTOM_STYLING) { - modal.style.cssText += ` + // Check if Hermes modal is open to set appropriate z-index + const hermesModal = document.getElementById("hermes-modal"); + const zIndex = hermesModal ? "2001" : "1001"; + + const modal = document.createElement("dialog"); + modal.open = true; + modal.style.zIndex = zIndex; + modal.style.position = "fixed"; + + if (USE_CUSTOM_STYLING) { + modal.style.cssText += ` max-width: 720px; border: none; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.2); background: white; `; - } + } - const article = document.createElement('article'); - modal.appendChild(article); + const article = document.createElement("article"); + modal.appendChild(article); - const header = document.createElement('header'); - if (USE_CUSTOM_STYLING) { - header.style.cssText = ` + const header = document.createElement("header"); + if (USE_CUSTOM_STYLING) { + header.style.cssText = ` background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); color: white; padding: 16px 20px; @@ -1263,32 +1320,32 @@ export function openTTSModal() { justify-content: space-between; align-items: center; `; - } else { - header.style.cssText = ` + } else { + header.style.cssText = ` display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; margin: -1em -1em 1em -1em; `; - } - article.appendChild(header); + } + article.appendChild(header); - const h1 = document.createElement('h1'); - h1.textContent = 'TTS Anything!'; - if (USE_CUSTOM_STYLING) { - h1.style.cssText = ` + const h1 = document.createElement("h1"); + h1.textContent = "TTS Anything!"; + if (USE_CUSTOM_STYLING) { + h1.style.cssText = ` margin: 0; font-family: 'ChunkFiveRegular', monospace; font-size: 20px; `; - } - header.appendChild(h1); + } + header.appendChild(h1); - const closeButton = document.createElement('button'); - closeButton.textContent = 'X'; - if (USE_CUSTOM_STYLING) { - closeButton.style.cssText = ` + const closeButton = document.createElement("button"); + closeButton.textContent = "X"; + if (USE_CUSTOM_STYLING) { + closeButton.style.cssText = ` background: none; border: none; color: white; @@ -1301,8 +1358,8 @@ export function openTTSModal() { align-items: center; justify-content: center; `; - } else { - closeButton.style.cssText = ` + } else { + closeButton.style.cssText = ` background: var(--background-color); border: 1px solid var(--border-color); color: var(--color); @@ -1317,77 +1374,78 @@ export function openTTSModal() { justify-content: center; transition: opacity 0.2s; `; - closeButton.onmouseenter = () => closeButton.style.opacity = '0.7'; - closeButton.onmouseleave = () => closeButton.style.opacity = '1'; - } - closeButton.onclick = () => document.body.removeChild(modal); - header.appendChild(closeButton); + closeButton.onmouseenter = () => (closeButton.style.opacity = "0.7"); + closeButton.onmouseleave = () => (closeButton.style.opacity = "1"); + } + closeButton.onclick = () => document.body.removeChild(modal); + header.appendChild(closeButton); - const textArea = document.createElement('textarea'); - textArea.style.width = '100%'; - textArea.style.height = '240px'; - textArea.placeholder = 'text-to-speech: write any message & have the artificial intelligence speak it!'; - article.appendChild(textArea); + const textArea = document.createElement("textarea"); + textArea.style.width = "100%"; + textArea.style.height = "240px"; + textArea.placeholder = + "text-to-speech: write any message & have the artificial intelligence speak it!"; + article.appendChild(textArea); - // Voice selection - const voices = ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer']; - const voiceSelection = document.createElement('div'); - if (USE_CUSTOM_STYLING) { - voiceSelection.style.cssText = ` + // Voice selection + const voices = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]; + const voiceSelection = document.createElement("div"); + if (USE_CUSTOM_STYLING) { + voiceSelection.style.cssText = ` display: flex; gap: 10px; margin: 16px 0; `; - } - voices.forEach((voice) => { - const label = document.createElement('label'); - if (USE_CUSTOM_STYLING) { - label.style.cssText = ` + } + voices.forEach((voice) => { + const label = document.createElement("label"); + if (USE_CUSTOM_STYLING) { + label.style.cssText = ` display: flex; align-items: center; gap: 4px; cursor: pointer; `; - } else { - label.style.display = 'inline-block'; - label.style.marginRight = '10px'; - } - const radio = document.createElement('input'); - radio.type = 'radio'; - radio.name = 'tts-voice'; - radio.value = voice; - if (voice === 'alloy') radio.checked = true; - label.appendChild(radio); - label.appendChild(document.createTextNode(voice)); - voiceSelection.appendChild(label); - }); - article.appendChild(voiceSelection); + } else { + label.style.display = "inline-block"; + label.style.marginRight = "10px"; + } + const radio = document.createElement("input"); + radio.type = "radio"; + radio.name = "tts-voice"; + radio.value = voice; + if (voice === "alloy") radio.checked = true; + label.appendChild(radio); + label.appendChild(document.createTextNode(voice)); + voiceSelection.appendChild(label); + }); + article.appendChild(voiceSelection); - // Speed selection - const speedLabel = document.createElement('label'); - const speedValue = document.createElement('span'); - speedValue.textContent = '0.9'; - - speedLabel.textContent = `Speed: ${speedValue.textContent}`; - const speedSlider = document.createElement('input'); - speedSlider.type = 'range'; - speedSlider.min = '0.25'; - speedSlider.max = '4.0'; - speedSlider.step = '0.05'; - speedSlider.value = '0.9'; - - speedSlider.oninput = () => { - speedValue.textContent = speedSlider.value; - speedLabel.textContent = `Speed: ${speedSlider.value}`; - }; - - article.appendChild(speedLabel); - article.appendChild(speedSlider); + // Speed selection + const speedLabel = document.createElement("label"); + const speedValue = document.createElement("span"); + speedValue.textContent = "0.9"; - const playButton = document.createElement('button'); - playButton.textContent = 'Play Text'; - if (USE_CUSTOM_STYLING) { - playButton.style.cssText = ` + speedLabel.textContent = `Speed: ${speedValue.textContent}`; + const speedSlider = document.createElement("input"); + speedSlider.type = "range"; + speedSlider.min = "0.25"; + speedSlider.max = "4.0"; + speedSlider.step = "0.05"; + speedSlider.value = "0.9"; + + speedSlider.oninput = () => { + speedValue.textContent = speedSlider.value; + speedLabel.textContent = `Speed: ${speedSlider.value}`; + }; + + article.appendChild(speedLabel); + article.appendChild(speedSlider); + + const playButton = document.createElement("button"); + playButton.textContent = "Play Text"; + if (USE_CUSTOM_STYLING) { + playButton.style.cssText = ` padding: 12px 24px; background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); color: white; @@ -1397,41 +1455,43 @@ export function openTTSModal() { font-weight: bold; margin: 16px 10px 0 0; `; - } - - let ttsAudio = null; - let ttsBlob = null; - let lastTTSInput = ''; - - playButton.onclick = async () => { - const currentText = textArea.value.trim(); - const selectedVoice = document.querySelector('input[name="tts-voice"]:checked').value; - const selectedSpeed = parseFloat(speedSlider.value); - - if (!currentText) return; - - if (!ttsAudio || lastTTSInput !== currentText) { - if (ttsAudio) ttsAudio.pause(); - playButton.textContent = 'Processing...'; - playButton.disabled = true; - lastTTSInput = currentText; - const result = await speakText(currentText, selectedVoice, selectedSpeed); - ttsAudio = result.audio; - ttsBlob = result.blob; - playButton.textContent = 'Pause Text'; - playButton.disabled = false; - - // Auto-play the audio - ttsAudio.play(); + } - // Add download button if not exists - let downloadButton = article.querySelector('.download-btn'); - if (!downloadButton) { - downloadButton = document.createElement('button'); - downloadButton.className = 'download-btn'; - downloadButton.textContent = 'Download MP3'; - if (USE_CUSTOM_STYLING) { - downloadButton.style.cssText = ` + let ttsAudio = null; + let ttsBlob = null; + let lastTTSInput = ""; + + playButton.onclick = async () => { + const currentText = textArea.value.trim(); + const selectedVoice = document.querySelector( + 'input[name="tts-voice"]:checked', + ).value; + const selectedSpeed = parseFloat(speedSlider.value); + + if (!currentText) return; + + if (!ttsAudio || lastTTSInput !== currentText) { + if (ttsAudio) ttsAudio.pause(); + playButton.textContent = "Processing..."; + playButton.disabled = true; + lastTTSInput = currentText; + const result = await speakText(currentText, selectedVoice, selectedSpeed); + ttsAudio = result.audio; + ttsBlob = result.blob; + playButton.textContent = "Pause Text"; + playButton.disabled = false; + + // Auto-play the audio + ttsAudio.play(); + + // Add download button if not exists + let downloadButton = article.querySelector(".download-btn"); + if (!downloadButton) { + downloadButton = document.createElement("button"); + downloadButton.className = "download-btn"; + downloadButton.textContent = "Download MP3"; + if (USE_CUSTOM_STYLING) { + downloadButton.style.cssText = ` padding: 12px 24px; background: #f5f5f5; border: 1px solid #ccc; @@ -1440,61 +1500,61 @@ export function openTTSModal() { font-weight: bold; margin: 16px 0 0 10px; `; - } - downloadButton.onclick = () => { - const a = document.createElement('a'); - a.href = URL.createObjectURL(ttsBlob); - a.download = `tts-${Date.now()}.mp3`; - a.click(); - }; - article.appendChild(downloadButton); - } - } + } + downloadButton.onclick = () => { + const a = document.createElement("a"); + a.href = URL.createObjectURL(ttsBlob); + a.download = `tts-${Date.now()}.mp3`; + a.click(); + }; + article.appendChild(downloadButton); + } + } - if (ttsAudio.paused) { - ttsAudio.play(); - playButton.textContent = 'Pause Text'; - } else { - ttsAudio.pause(); - playButton.textContent = 'Play Text'; - } + if (ttsAudio.paused) { + ttsAudio.play(); + playButton.textContent = "Pause Text"; + } else { + ttsAudio.pause(); + playButton.textContent = "Play Text"; + } - ttsAudio.onended = () => { - playButton.textContent = 'Play Text'; - }; - }; - - article.appendChild(playButton); - document.body.appendChild(modal); + ttsAudio.onended = () => { + playButton.textContent = "Play Text"; + }; + }; + + article.appendChild(playButton); + document.body.appendChild(modal); } // Open translation modal export function openTranslateModal() { - // Check if Hermes modal is open to set appropriate z-index - const hermesModal = document.getElementById('hermes-modal'); - const zIndex = hermesModal ? '2001' : '1001'; - - const modal = document.createElement('dialog'); - modal.open = true; - modal.style.zIndex = zIndex; - modal.style.position = 'fixed'; - - if (USE_CUSTOM_STYLING) { - modal.style.cssText += ` + // Check if Hermes modal is open to set appropriate z-index + const hermesModal = document.getElementById("hermes-modal"); + const zIndex = hermesModal ? "2001" : "1001"; + + const modal = document.createElement("dialog"); + modal.open = true; + modal.style.zIndex = zIndex; + modal.style.position = "fixed"; + + if (USE_CUSTOM_STYLING) { + modal.style.cssText += ` max-width: 720px; border: none; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.2); background: white; `; - } + } - const article = document.createElement('article'); - modal.appendChild(article); + const article = document.createElement("article"); + modal.appendChild(article); - const header = document.createElement('header'); - if (USE_CUSTOM_STYLING) { - header.style.cssText = ` + const header = document.createElement("header"); + if (USE_CUSTOM_STYLING) { + header.style.cssText = ` background: linear-gradient(45deg, #4CAF50 0%, #45a049 100%); color: white; padding: 16px 20px; @@ -1503,32 +1563,32 @@ export function openTranslateModal() { justify-content: space-between; align-items: center; `; - } else { - header.style.cssText = ` + } else { + header.style.cssText = ` display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; margin: -1em -1em 1em -1em; `; - } - article.appendChild(header); + } + article.appendChild(header); - const h1 = document.createElement('h1'); - h1.textContent = '🌐 Omni-Directional Translation'; - if (USE_CUSTOM_STYLING) { - h1.style.cssText = ` + const h1 = document.createElement("h1"); + h1.textContent = "🌐 Omni-Directional Translation"; + if (USE_CUSTOM_STYLING) { + h1.style.cssText = ` margin: 0; font-family: 'ChunkFiveRegular', monospace; font-size: 20px; `; - } - header.appendChild(h1); + } + header.appendChild(h1); - const closeButton = document.createElement('button'); - closeButton.textContent = 'X'; - if (USE_CUSTOM_STYLING) { - closeButton.style.cssText = ` + const closeButton = document.createElement("button"); + closeButton.textContent = "X"; + if (USE_CUSTOM_STYLING) { + closeButton.style.cssText = ` background: none; border: none; color: white; @@ -1541,8 +1601,8 @@ export function openTranslateModal() { align-items: center; justify-content: center; `; - } else { - closeButton.style.cssText = ` + } else { + closeButton.style.cssText = ` background: var(--background-color); border: 1px solid var(--border-color); color: var(--color); @@ -1557,76 +1617,81 @@ export function openTranslateModal() { justify-content: center; transition: opacity 0.2s; `; - closeButton.onmouseenter = () => closeButton.style.opacity = '0.7'; - closeButton.onmouseleave = () => closeButton.style.opacity = '1'; - } - closeButton.onclick = () => document.body.removeChild(modal); - header.appendChild(closeButton); + closeButton.onmouseenter = () => (closeButton.style.opacity = "0.7"); + closeButton.onmouseleave = () => (closeButton.style.opacity = "1"); + } + closeButton.onclick = () => document.body.removeChild(modal); + header.appendChild(closeButton); - // Translation mode tabs - const modeContainer = document.createElement('div'); - modeContainer.style.cssText = 'display: flex; gap: 10px; margin-bottom: 20px;'; - - const pageTab = document.createElement('button'); - pageTab.textContent = 'πŸ“„ Translate Page'; - pageTab.style.cssText = 'flex: 1; padding: 10px; border: 2px solid #4CAF50; border-radius: 6px; background: #4CAF50; color: white; cursor: pointer;'; - - const customTab = document.createElement('button'); - customTab.textContent = '✏️ Custom Text'; - customTab.style.cssText = 'flex: 1; padding: 10px; border: 2px solid #4CAF50; border-radius: 6px; background: white; color: #4CAF50; cursor: pointer;'; - - modeContainer.appendChild(pageTab); - modeContainer.appendChild(customTab); - article.appendChild(modeContainer); + // Translation mode tabs + const modeContainer = document.createElement("div"); + modeContainer.style.cssText = + "display: flex; gap: 10px; margin-bottom: 20px;"; - // Content areas - const pageContent = document.createElement('div'); - const customContent = document.createElement('div'); - customContent.style.display = 'none'; - - // Page translation content - const pageInfo = document.createElement('p'); - pageInfo.textContent = 'Translate the current page content into your chosen language:'; - pageContent.appendChild(pageInfo); - - // Custom text content - const textArea = document.createElement('textarea'); - textArea.style.width = '100%'; - textArea.style.height = '150px'; - textArea.placeholder = 'Enter any text to translate...'; - customContent.appendChild(textArea); + const pageTab = document.createElement("button"); + pageTab.textContent = "πŸ“„ Translate Page"; + pageTab.style.cssText = + "flex: 1; padding: 10px; border: 2px solid #4CAF50; border-radius: 6px; background: #4CAF50; color: white; cursor: pointer;"; - article.appendChild(pageContent); - article.appendChild(customContent); + const customTab = document.createElement("button"); + customTab.textContent = "✏️ Custom Text"; + customTab.style.cssText = + "flex: 1; padding: 10px; border: 2px solid #4CAF50; border-radius: 6px; background: white; color: #4CAF50; cursor: pointer;"; - // Language selection - const languageContainer = document.createElement('div'); - languageContainer.style.cssText = 'margin: 20px 0;'; - - const languageLabel = document.createElement('label'); - languageLabel.textContent = 'Translate to: '; - languageLabel.style.display = 'block'; - languageLabel.style.marginBottom = '8px'; - - const languageSelect = document.createElement('select'); - languageSelect.style.cssText = 'width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px;'; - - Object.entries(SUPPORTED_LANGUAGES).forEach(([code, name]) => { - const option = document.createElement('option'); - option.value = code; - option.textContent = name; - languageSelect.appendChild(option); - }); - - languageContainer.appendChild(languageLabel); - languageContainer.appendChild(languageSelect); - article.appendChild(languageContainer); + modeContainer.appendChild(pageTab); + modeContainer.appendChild(customTab); + article.appendChild(modeContainer); - // Translate button - const translateButton = document.createElement('button'); - translateButton.textContent = '🌐 Translate'; - if (USE_CUSTOM_STYLING) { - translateButton.style.cssText = ` + // Content areas + const pageContent = document.createElement("div"); + const customContent = document.createElement("div"); + customContent.style.display = "none"; + + // Page translation content + const pageInfo = document.createElement("p"); + pageInfo.textContent = + "Translate the current page content into your chosen language:"; + pageContent.appendChild(pageInfo); + + // Custom text content + const textArea = document.createElement("textarea"); + textArea.style.width = "100%"; + textArea.style.height = "150px"; + textArea.placeholder = "Enter any text to translate..."; + customContent.appendChild(textArea); + + article.appendChild(pageContent); + article.appendChild(customContent); + + // Language selection + const languageContainer = document.createElement("div"); + languageContainer.style.cssText = "margin: 20px 0;"; + + const languageLabel = document.createElement("label"); + languageLabel.textContent = "Translate to: "; + languageLabel.style.display = "block"; + languageLabel.style.marginBottom = "8px"; + + const languageSelect = document.createElement("select"); + languageSelect.style.cssText = + "width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px;"; + + Object.entries(SUPPORTED_LANGUAGES).forEach(([code, name]) => { + const option = document.createElement("option"); + option.value = code; + option.textContent = name; + languageSelect.appendChild(option); + }); + + languageContainer.appendChild(languageLabel); + languageContainer.appendChild(languageSelect); + article.appendChild(languageContainer); + + // Translate button + const translateButton = document.createElement("button"); + translateButton.textContent = "🌐 Translate"; + if (USE_CUSTOM_STYLING) { + translateButton.style.cssText = ` padding: 12px 24px; background: linear-gradient(45deg, #4CAF50 0%, #45a049 100%); color: white; @@ -1637,189 +1702,208 @@ export function openTranslateModal() { margin: 16px 10px 0 0; width: 100%; `; - } else { - translateButton.style.cssText = 'width: 100%; padding: 12px; margin: 16px 0;'; - } + } else { + translateButton.style.cssText = + "width: 100%; padding: 12px; margin: 16px 0;"; + } - // Result area - const resultContainer = document.createElement('div'); - resultContainer.style.cssText = 'margin-top: 20px; display: none;'; - - const resultLabel = document.createElement('h3'); - resultLabel.textContent = 'Translation:'; - - const resultArea = document.createElement('div'); - resultArea.style.cssText = 'border: 1px solid #ccc; border-radius: 4px; padding: 15px; max-height: 400px; overflow-y: auto; background: #f9f9f9; white-space: pre-wrap;'; - - resultContainer.appendChild(resultLabel); - resultContainer.appendChild(resultArea); - article.appendChild(resultContainer); + // Result area + const resultContainer = document.createElement("div"); + resultContainer.style.cssText = "margin-top: 20px; display: none;"; - article.appendChild(translateButton); + const resultLabel = document.createElement("h3"); + resultLabel.textContent = "Translation:"; - // Tab switching - let isPageMode = true; - - pageTab.onclick = () => { - if (!isPageMode) { - isPageMode = true; - pageTab.style.background = '#4CAF50'; - pageTab.style.color = 'white'; - customTab.style.background = 'white'; - customTab.style.color = '#4CAF50'; - pageContent.style.display = 'block'; - customContent.style.display = 'none'; - translateButton.textContent = '🌐 Translate Page'; - } - }; - - customTab.onclick = () => { - if (isPageMode) { - isPageMode = false; - customTab.style.background = '#4CAF50'; - customTab.style.color = 'white'; - pageTab.style.background = 'white'; - pageTab.style.color = '#4CAF50'; - customContent.style.display = 'block'; - pageContent.style.display = 'none'; - translateButton.textContent = '🌐 Translate Text'; - } - }; + const resultArea = document.createElement("div"); + resultArea.style.cssText = + "border: 1px solid #ccc; border-radius: 4px; padding: 15px; max-height: 400px; overflow-y: auto; background: #f9f9f9; white-space: pre-wrap;"; - // Translation logic - translateButton.onclick = async () => { - const targetLanguage = languageSelect.value; - const targetLanguageName = SUPPORTED_LANGUAGES[targetLanguage]; - - try { - translateButton.textContent = 'Processing...'; - translateButton.disabled = true; - resultContainer.style.display = 'none'; - - let translatedText; - - if (isPageMode) { - translatedText = await translateCurrentPage(targetLanguage); - } else { - const customText = textArea.value.trim(); - if (!customText) { - alert('Please enter some text to translate!'); - return; - } - translatedText = await translateText(customText, targetLanguage); - } - - resultLabel.textContent = `Translation (${targetLanguageName}):`; - - // Check if the translated content looks like HTML - const isHTML = translatedText.includes('<') && translatedText.includes('>') && - (translatedText.includes(']*>/i, `if (previewWindow) { - // Inject base URL for relative resources to work - const baseUrl = window.location.origin + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/') + 1); - - let htmlWithBase; - const scriptTag = ` + resultContainer.appendChild(resultLabel); + resultContainer.appendChild(resultArea); + article.appendChild(resultContainer); + + article.appendChild(translateButton); + + // Tab switching + let isPageMode = true; + + pageTab.onclick = () => { + if (!isPageMode) { + isPageMode = true; + pageTab.style.background = "#4CAF50"; + pageTab.style.color = "white"; + customTab.style.background = "white"; + customTab.style.color = "#4CAF50"; + pageContent.style.display = "block"; + customContent.style.display = "none"; + translateButton.textContent = "🌐 Translate Page"; + } + }; + + customTab.onclick = () => { + if (isPageMode) { + isPageMode = false; + customTab.style.background = "#4CAF50"; + customTab.style.color = "white"; + pageTab.style.background = "white"; + pageTab.style.color = "#4CAF50"; + customContent.style.display = "block"; + pageContent.style.display = "none"; + translateButton.textContent = "🌐 Translate Text"; + } + }; + + // Translation logic + translateButton.onclick = async () => { + const targetLanguage = languageSelect.value; + const targetLanguageName = SUPPORTED_LANGUAGES[targetLanguage]; + + try { + translateButton.textContent = "Processing..."; + translateButton.disabled = true; + resultContainer.style.display = "none"; + + let translatedText; + + if (isPageMode) { + translatedText = await translateCurrentPage(targetLanguage); + } else { + const customText = textArea.value.trim(); + if (!customText) { + alert("Please enter some text to translate!"); + return; + } + translatedText = await translateText(customText, targetLanguage); + } + + resultLabel.textContent = `Translation (${targetLanguageName}):`; + + // Check if the translated content looks like HTML + const isHTML = + translatedText.includes("<") && + translatedText.includes(">") && + (translatedText.includes(" window.UNCLOSEAI_SKIP_INIT = true; `; - - if (translatedText.includes('') - ? translatedText.replace('', `${scriptTag}`) - : translatedText.replace('', `${scriptTag}`); - } else if (translatedText.includes('')) { - // Has head but no DOCTYPE - htmlWithBase = `\n${translatedText.replace('', `${scriptTag}`)}`; - } else { - // No DOCTYPE or head - htmlWithBase = `\n${scriptTag}${translatedText}`; - } - - previewWindow.document.write(htmlWithBase); - previewWindow.document.close();`); - - previewWindow.document.write(htmlWithBase); - previewWindow.document.close(); - previewWindow.document.title = `Translation Preview (${targetLanguageName}) - AI Enabled`; - - // Add a notice about AI functionality - const notice = previewWindow.document.createElement('div'); - notice.style.cssText = 'position: fixed; top: 10px; right: 10px; background: #4CAF50; color: white; padding: 8px 12px; border-radius: 4px; font-size: 12px; z-index: 10000; box-shadow: 0 2px 8px rgba(0,0,0,0.2);'; - notice.textContent = `πŸ€– AI features should work in ${targetLanguageName}!`; - previewWindow.document.body.appendChild(notice); - - // Auto-hide notice after 5 seconds - setTimeout(() => { - if (notice.parentNode) notice.parentNode.removeChild(notice); - }, 5000); - } - - // Show raw HTML toggle in modal - resultArea.innerHTML = ` + + if (translatedText.includes("") + ? translatedText.replace( + "", + `${scriptTag}`, + ) + : translatedText.replace( + "", + `${scriptTag}`, + ); + } else if (translatedText.includes("")) { + // Has head but no DOCTYPE + htmlWithBase = `\n${translatedText.replace("", `${scriptTag}`)}`; + } else { + // No DOCTYPE or head + htmlWithBase = `\n${scriptTag}${translatedText}`; + } + + previewWindow.document.write(htmlWithBase); + previewWindow.document.close(); + previewWindow.document.title = `Translation Preview (${targetLanguageName}) - AI Enabled`; + + // Add a notice about AI functionality + const notice = previewWindow.document.createElement("div"); + notice.style.cssText = + "position: fixed; top: 10px; right: 10px; background: #4CAF50; color: white; padding: 8px 12px; border-radius: 4px; font-size: 12px; z-index: 10000; box-shadow: 0 2px 8px rgba(0,0,0,0.2);"; + notice.textContent = `πŸ€– AI features should work in ${targetLanguageName}!`; + previewWindow.document.body.appendChild(notice); + + // Auto-hide notice after 5 seconds + setTimeout(() => { + if (notice.parentNode) notice.parentNode.removeChild(notice); + }, 5000); + } + + // Show raw HTML toggle in modal + resultArea.innerHTML = `
-
${translatedText.replace(//g, '>')}
+
${translatedText.replace(//g, ">")}
βœ… Preview Opened in New Window

The translated HTML page has opened in a new window with full AI functionality!

- ${previewWindow ? '' : '

Note: Please allow popups for preview window

'} + ${previewWindow ? "" : '

Note: Please allow popups for preview window

'}
`; - resultArea.style.whiteSpace = 'normal'; - } else { - resultArea.textContent = translatedText; - resultArea.style.whiteSpace = 'pre-wrap'; - } - - resultContainer.style.display = 'block'; - - // Scroll to result - resultContainer.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); - - } catch (error) { - alert(`Translation failed: ${error.message}`); - } finally { - translateButton.textContent = isPageMode ? '🌐 Translate Page' : '🌐 Translate Text'; - translateButton.disabled = false; - } - }; + resultArea.style.whiteSpace = "normal"; + } else { + resultArea.textContent = translatedText; + resultArea.style.whiteSpace = "pre-wrap"; + } - document.body.appendChild(modal); + resultContainer.style.display = "block"; + + // Scroll to result + resultContainer.scrollIntoView({ behavior: "smooth", block: "nearest" }); + } catch (error) { + alert(`Translation failed: ${error.message}`); + } finally { + translateButton.textContent = isPageMode + ? "🌐 Translate Page" + : "🌐 Translate Text"; + translateButton.disabled = false; + } + }; + + document.body.appendChild(modal); } // Initialize the system export function initializeSystem() { - // Prevent duplicate initialization in popup windows - if (window.UNCLOSEAI_SKIP_INIT === true) { - console.log('uncloseai.js: Skipping initialization as requested by flag.'); - return; - } + // Prevent duplicate initialization in popup windows + if (window.UNCLOSEAI_SKIP_INIT === true) { + console.log("uncloseai.js: Skipping initialization as requested by flag."); + return; + } - console.log('uncloseai.js: Initializing system'); - initializeChatInterface(); - - // Only create floating button if not disabled - if (SHOW_FLOATING_BUTTON) { - console.log('uncloseai.js: SHOW_FLOATING_BUTTON is true, calling createFloatingAIButton()'); - createFloatingAIButton(); - } else { - console.log('uncloseai.js: SHOW_FLOATING_BUTTON is false, not creating floating button.'); - } - - // Initialize class-based elements - initializeUncloseaiElements(); + console.log("uncloseai.js: Initializing system"); + initializeChatInterface(); + + // Only create floating button if not disabled + if (SHOW_FLOATING_BUTTON) { + console.log( + "uncloseai.js: SHOW_FLOATING_BUTTON is true, calling createFloatingAIButton()", + ); + createFloatingAIButton(); + } else { + console.log( + "uncloseai.js: SHOW_FLOATING_BUTTON is false, not creating floating button.", + ); + } + + // Initialize class-based elements + initializeUncloseaiElements(); } diff --git a/uncloseai.js b/uncloseai.js index c99469d..e94fe5b 100644 --- a/uncloseai.js +++ b/uncloseai.js @@ -1,24 +1,24 @@ -/* +/* * uncloseai.js - Modular AI Integration Library - * + * * Main entry point that imports all modules and exports the public API * This maintains backward compatibility while enabling better code organization */ // External dependencies import { marked } from "https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js"; -import hljs from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/es/highlight.min.js'; +import hljs from "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/es/highlight.min.js"; // Internal modules -import * as Config from './src/config.js'; -import * as Models from './src/models.js'; -import * as Content from './src/content.js'; -import * as TTS from './src/tts.js'; -import * as Chat from './src/chat.js'; -import * as FileUpload from './src/file-upload.js'; -import * as Storage from './src/storage.js'; -import * as PageReader from './src/page-reader.js'; -import * as UI from './src/ui.js'; +import * as Config from "./src/config.js"; +import * as Models from "./src/models.js"; +import * as Content from "./src/content.js"; +import * as TTS from "./src/tts.js"; +import * as Chat from "./src/chat.js"; +import * as FileUpload from "./src/file-upload.js"; +import * as Storage from "./src/storage.js"; +import * as PageReader from "./src/page-reader.js"; +import * as UI from "./src/ui.js"; // ------------------------- // Public API - Main Functions @@ -81,18 +81,18 @@ export const VLLM_ENDPOINTS = Config.VLLM_ENDPOINTS; export const modelRegistry = Models.modelRegistry; // Export chat history (for external access) -export { chatHistory } from './src/chat.js'; +export { chatHistory } from "./src/chat.js"; // ------------------------- // Initialization // ------------------------- // Initialize on page load -window.addEventListener('load', () => { - console.log('uncloseai.js: window.onload event fired.'); - UI.initializeSystem(); - Models.createModelSelectionDropdown(); - Models.addRefreshModelsButton(); +window.addEventListener("load", () => { + console.log("uncloseai.js: window.onload event fired."); + UI.initializeSystem(); + Models.createModelSelectionDropdown(); + Models.addRefreshModelsButton(); }); // ------------------------- @@ -117,35 +117,36 @@ window.handleTTSFromElement = UI.handleTTSFromElement; window.handleUploadFromElement = UI.handleUploadFromElement; // Export helper functions for class-based integrations -window.handleCustomChat = async function(button) { - const container = button.parentElement; - const input = container.querySelector('[data-chat-input]'); - const chatBox = container.querySelector('[data-chat-box]'); - const message = input.value.trim(); - - if (!message) return; - - // Add user message - chatBox.innerHTML += `
You: ${message}
`; - input.value = ''; - - // Add AI thinking indicator - const thinkingDiv = document.createElement('div'); - thinkingDiv.style.cssText = 'margin: 2px 0; padding: 2px; background: #f5f5f5; border-radius: 2px; font-size: 0.9em;'; - thinkingDiv.innerHTML = 'AI: thinking...'; - chatBox.appendChild(thinkingDiv); - chatBox.scrollTop = chatBox.scrollHeight; - - try { - let response = ''; - for await (const chunk of Chat.sendMessage(message)) { - response += chunk; - thinkingDiv.innerHTML = `AI: ${response}`; - chatBox.scrollTop = chatBox.scrollHeight; - } - } catch (error) { - thinkingDiv.innerHTML = `Error: ${error.message}`; - } +window.handleCustomChat = async function (button) { + const container = button.parentElement; + const input = container.querySelector("[data-chat-input]"); + const chatBox = container.querySelector("[data-chat-box]"); + const message = input.value.trim(); + + if (!message) return; + + // Add user message + chatBox.innerHTML += `
You: ${message}
`; + input.value = ""; + + // Add AI thinking indicator + const thinkingDiv = document.createElement("div"); + thinkingDiv.style.cssText = + "margin: 2px 0; padding: 2px; background: #f5f5f5; border-radius: 2px; font-size: 0.9em;"; + thinkingDiv.innerHTML = "AI: thinking..."; + chatBox.appendChild(thinkingDiv); + chatBox.scrollTop = chatBox.scrollHeight; + + try { + let response = ""; + for await (const chunk of Chat.sendMessage(message)) { + response += chunk; + thinkingDiv.innerHTML = `AI: ${response}`; + chatBox.scrollTop = chatBox.scrollHeight; + } + } catch (error) { + thinkingDiv.innerHTML = `Error: ${error.message}`; + } }; -console.log('uncloseai.js: Modular version loaded successfully'); \ No newline at end of file +console.log("uncloseai.js: Modular version loaded successfully");