From 0cb1cae655f61d9c994f39d7763650ec10264a6a Mon Sep 17 00:00:00 2001 From: AlexJump24 Date: Sun, 13 Oct 2024 18:48:00 +0000 Subject: [PATCH 01/35] Compile Assets --- public/build/manifest.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/build/manifest.json b/public/build/manifest.json index eb93cbf8..d692ffcd 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -4,14 +4,14 @@ "src": "resources/js/cachet.js", "isEntry": true }, - "resources/css/cachet.css": { - "file": "assets/cachet.30cb9e62.css", - "src": "resources/css/cachet.css", - "isEntry": true - }, "resources/css/dashboard/theme.css": { "file": "assets/theme.8251fad4.css", "src": "resources/css/dashboard/theme.css", "isEntry": true + }, + "resources/css/cachet.css": { + "file": "assets/cachet.30cb9e62.css", + "src": "resources/css/cachet.css", + "isEntry": true } } \ No newline at end of file From 3a983bbb31333fe7f8c493fab4966181555c722c Mon Sep 17 00:00:00 2001 From: AlexJump24 Date: Sun, 13 Oct 2024 18:48:00 +0000 Subject: [PATCH 02/35] Compile Assets --- public/build/assets/cachet-BIHPJ10n.css | 1 - public/build/assets/cachet-DCZQ8JcZ.js | 18 ------------------ public/build/assets/cachet.30cb9e62.css | 1 + public/build/assets/cachet.6122e927.js | 18 ++++++++++++++++++ public/build/assets/theme-BbibcaDc.css | 1 - public/build/assets/theme.8251fad4.css | 1 + 6 files changed, 20 insertions(+), 20 deletions(-) delete mode 100644 public/build/assets/cachet-BIHPJ10n.css delete mode 100644 public/build/assets/cachet-DCZQ8JcZ.js create mode 100644 public/build/assets/cachet.30cb9e62.css create mode 100644 public/build/assets/cachet.6122e927.js delete mode 100644 public/build/assets/theme-BbibcaDc.css create mode 100644 public/build/assets/theme.8251fad4.css diff --git a/public/build/assets/cachet-BIHPJ10n.css b/public/build/assets/cachet-BIHPJ10n.css deleted file mode 100644 index 02f57e8a..00000000 --- a/public/build/assets/cachet-BIHPJ10n.css +++ /dev/null @@ -1 +0,0 @@ -*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[type=text],input:where(:not([type])),[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}[type=text]:focus,input:where(:not([type])):focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow: 0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}@media (forced-colors: active){[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}@media (forced-colors: active){[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}@media (forced-colors: active){[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%),0 3px rgb(var(--tw-prose-kbd-shadows) / 10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: 17 24 39;--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: 255 255 255;--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-sm{font-size:.875rem;line-height:1.7142857}.prose-sm :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;line-height:1.5555556;margin-top:.8888889em;margin-bottom:.8888889em}.prose-sm :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.1111111em}.prose-sm :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.1428571em;margin-top:0;margin-bottom:.8em;line-height:1.2}.prose-sm :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.4285714em;margin-top:1.6em;margin-bottom:.8em;line-height:1.4}.prose-sm :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;margin-top:1.5555556em;margin-bottom:.4444444em;line-height:1.5555556}.prose-sm :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.4285714em;margin-bottom:.5714286em;line-height:1.4285714}.prose-sm :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;border-radius:.3125rem;padding-top:.1428571em;padding-inline-end:.3571429em;padding-bottom:.1428571em;padding-inline-start:.3571429em}.prose-sm :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em}.prose-sm :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.prose-sm :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.prose-sm :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.6666667;margin-top:1.6666667em;margin-bottom:1.6666667em;border-radius:.25rem;padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;margin-bottom:.2857143em}.prose-sm :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;padding-inline-start:1.5714286em}.prose-sm :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2.8571429em;margin-bottom:2.8571429em}.prose-sm :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.5}.prose-sm :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.3333333;margin-top:.6666667em}.prose-sm :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-base{font-size:1rem;line-height:1.75}.prose-base :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose-base :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose-base :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose-base :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose-base :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose-base :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose-base :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose-base :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose-base :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose-base :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-base :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose-base :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose-base :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em}.prose-base :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em}.prose-base :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.prose-base :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose-base :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose-base :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose-base :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose-base :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose-base :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose-base :where(.prose-base>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose-base :where(.prose-base>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose-base :where(.prose-base>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose-base :where(.prose-base>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose-base :where(.prose-base>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose-base :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose-base :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose-base :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose-base :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose-base :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:3em;margin-bottom:3em}.prose-base :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-base :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-base :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-base :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-base :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;line-height:1.7142857}.prose-base :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose-base :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-base :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-base :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose-base :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-base :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-base :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose-base :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-base :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose-base :where(.prose-base>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-base :where(.prose-base>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-lg{font-size:1.125rem;line-height:1.7777778}.prose-lg :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em}.prose-lg :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2222222em;line-height:1.4545455;margin-top:1.0909091em;margin-bottom:1.0909091em}.prose-lg :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.6666667em;margin-bottom:1.6666667em;padding-inline-start:1em}.prose-lg :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.6666667em;margin-top:0;margin-bottom:.8333333em;line-height:1}.prose-lg :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.6666667em;margin-top:1.8666667em;margin-bottom:1.0666667em;line-height:1.3333333}.prose-lg :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.3333333em;margin-top:1.6666667em;margin-bottom:.6666667em;line-height:1.5}.prose-lg :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:.4444444em;line-height:1.5555556}.prose-lg :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.prose-lg :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.prose-lg :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-lg :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.prose-lg :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;border-radius:.3125rem;padding-top:.2222222em;padding-inline-end:.4444444em;padding-bottom:.2222222em;padding-inline-start:.4444444em}.prose-lg :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.prose-lg :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8666667em}.prose-lg :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em}.prose-lg :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;line-height:1.75;margin-top:2em;margin-bottom:2em;border-radius:.375rem;padding-top:1em;padding-inline-end:1.5em;padding-bottom:1em;padding-inline-start:1.5em}.prose-lg :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.5555556em}.prose-lg :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.5555556em}.prose-lg :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.6666667em;margin-bottom:.6666667em}.prose-lg :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4444444em}.prose-lg :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4444444em}.prose-lg :where(.prose-lg>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.8888889em;margin-bottom:.8888889em}.prose-lg :where(.prose-lg>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em}.prose-lg :where(.prose-lg>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.3333333em}.prose-lg :where(.prose-lg>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em}.prose-lg :where(.prose-lg>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.3333333em}.prose-lg :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.8888889em;margin-bottom:.8888889em}.prose-lg :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em}.prose-lg :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em}.prose-lg :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.6666667em;padding-inline-start:1.5555556em}.prose-lg :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:3.1111111em;margin-bottom:3.1111111em}.prose-lg :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-lg :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-lg :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-lg :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-lg :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;line-height:1.5}.prose-lg :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:.75em;padding-bottom:.75em;padding-inline-start:.75em}.prose-lg :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-lg :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-lg :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.75em;padding-inline-end:.75em;padding-bottom:.75em;padding-inline-start:.75em}.prose-lg :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-lg :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-lg :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7777778em;margin-bottom:1.7777778em}.prose-lg :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-lg :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em;line-height:1.5;margin-top:1em}.prose-lg :where(.prose-lg>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-lg :where(.prose-lg>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-zinc{--tw-prose-body: #3f3f46;--tw-prose-headings: #18181b;--tw-prose-lead: #52525b;--tw-prose-links: #18181b;--tw-prose-bold: #18181b;--tw-prose-counters: #71717a;--tw-prose-bullets: #d4d4d8;--tw-prose-hr: #e4e4e7;--tw-prose-quotes: #18181b;--tw-prose-quote-borders: #e4e4e7;--tw-prose-captions: #71717a;--tw-prose-kbd: #18181b;--tw-prose-kbd-shadows: 24 24 27;--tw-prose-code: #18181b;--tw-prose-pre-code: #e4e4e7;--tw-prose-pre-bg: #27272a;--tw-prose-th-borders: #d4d4d8;--tw-prose-td-borders: #e4e4e7;--tw-prose-invert-body: #d4d4d8;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #a1a1aa;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #a1a1aa;--tw-prose-invert-bullets: #52525b;--tw-prose-invert-hr: #3f3f46;--tw-prose-invert-quotes: #f4f4f5;--tw-prose-invert-quote-borders: #3f3f46;--tw-prose-invert-captions: #a1a1aa;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: 255 255 255;--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d4d4d8;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #52525b;--tw-prose-invert-td-borders: #3f3f46}.button{display:flex;width:100%;justify-content:center;border-radius:.375rem;padding:.375rem .75rem;font-size:.875rem;font-weight:600;line-height:1.5rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity));--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.button:focus-visible{outline-style:solid;outline-width:2px;outline-offset:2px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-4{top:1rem;right:1rem;bottom:1rem;left:1rem}.inset-x-0{left:0;right:0}.inset-x-4{left:1rem;right:1rem}.inset-y-0{top:0;bottom:0}.-bottom-1\/2{bottom:-50%}.-left-1{left:-.25rem}.-left-9{left:-2.25rem}.-left-\[calc\(28px\+10px\+13px\)\]{left:-51px}.-top-1{top:-.25rem}.-top-1\/2{top:-50%}.-top-2{top:-.5rem}.-top-3{top:-.75rem}.bottom-0{bottom:0}.bottom-1\/2{bottom:50%}.end-0{inset-inline-end:0px}.end-4{inset-inline-end:1rem}.end-6{inset-inline-end:1.5rem}.left-3{left:.75rem}.start-0{inset-inline-start:0px}.start-full{inset-inline-start:100%}.top-0{top:0}.top-1{top:.25rem}.top-1\.5{top:.375rem}.top-1\/2{top:50%}.top-4{top:1rem}.top-6{top:1.5rem}.isolate{isolation:isolate}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[1\]{z-index:1}.order-first{order:-9999}.col-\[--col-span-default\]{grid-column:var(--col-span-default)}.col-span-full{grid-column:1 / -1}.col-start-2{grid-column-start:2}.col-start-3{grid-column-start:3}.col-start-\[--col-start-default\]{grid-column-start:var(--col-start-default)}.row-start-2{grid-row-start:2}.-m-0\.5{margin:-.125rem}.-m-1{margin:-.25rem}.-m-1\.5{margin:-.375rem}.-m-2{margin:-.5rem}.-m-2\.5{margin:-.625rem}.-m-3{margin:-.75rem}.-m-3\.5{margin:-.875rem}.-mx-2{margin-left:-.5rem;margin-right:-.5rem}.-mx-4{margin-left:-1rem;margin-right:-1rem}.-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.-my-1{margin-top:-.25rem;margin-bottom:-.25rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-3{margin-left:.75rem;margin-right:.75rem}.mx-auto{margin-left:auto;margin-right:auto}.my-16{margin-top:4rem;margin-bottom:4rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-auto{margin-top:auto;margin-bottom:auto}.\!mt-0{margin-top:0!important}.-mb-4{margin-bottom:-1rem}.-mb-6{margin-bottom:-1.5rem}.-me-2{margin-inline-end:-.5rem}.-ms-0\.5{margin-inline-start:-.125rem}.-ms-1{margin-inline-start:-.25rem}.-ms-2{margin-inline-start:-.5rem}.-mt-3{margin-top:-.75rem}.-mt-4{margin-top:-1rem}.-mt-6{margin-top:-1.5rem}.-mt-7{margin-top:-1.75rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.me-1{margin-inline-end:.25rem}.me-4{margin-inline-end:1rem}.me-6{margin-inline-end:1.5rem}.ml-2{margin-left:.5rem}.ml-3\.5{margin-left:.875rem}.ml-9{margin-left:2.25rem}.ml-auto{margin-left:auto}.ms-1{margin-inline-start:.25rem}.ms-auto{margin-inline-start:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-auto{margin-top:auto}.line-clamp-\[--line-clamp\]{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--line-clamp)}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.inline-grid{display:inline-grid}.hidden{display:none}.size-4{width:1rem;height:1rem}.size-5{width:1.25rem;height:1.25rem}.h-0{height:0px}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-16{height:4rem}.h-24{height:6rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-96{height:24rem}.h-\[100dvh\],.h-dvh{height:100dvh}.h-full{height:100%}.h-screen{height:100vh}.max-h-96{max-height:24rem}.min-h-\[theme\(spacing\.48\)\]{min-height:12rem}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-16{width:4rem}.w-20{width:5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-32{width:8rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[--sidebar-width\]{width:var(--sidebar-width)}.w-\[calc\(100\%\+2rem\)\]{width:calc(100% + 2rem)}.w-auto{width:auto}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-px{width:1px}.w-screen{width:100vw}.min-w-0{min-width:0px}.min-w-\[theme\(spacing\.4\)\]{min-width:1rem}.min-w-\[theme\(spacing\.5\)\]{min-width:1.25rem}.min-w-\[theme\(spacing\.6\)\]{min-width:1.5rem}.min-w-\[theme\(spacing\.8\)\]{min-width:2rem}.\!max-w-2xl{max-width:42rem!important}.\!max-w-3xl{max-width:48rem!important}.\!max-w-4xl{max-width:56rem!important}.\!max-w-5xl{max-width:64rem!important}.\!max-w-6xl{max-width:72rem!important}.\!max-w-7xl{max-width:80rem!important}.\!max-w-\[14rem\]{max-width:14rem!important}.\!max-w-lg{max-width:32rem!important}.\!max-w-md{max-width:28rem!important}.\!max-w-sm{max-width:24rem!important}.\!max-w-xl{max-width:36rem!important}.\!max-w-xs{max-width:20rem!important}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-fit{max-width:-moz-fit-content;max-width:fit-content}.max-w-full{max-width:100%}.max-w-lg{max-width:32rem}.max-w-max{max-width:-moz-max-content;max-width:max-content}.max-w-md{max-width:28rem}.max-w-min{max-width:-moz-min-content;max-width:min-content}.max-w-none{max-width:none}.max-w-prose{max-width:65ch}.max-w-screen-2xl{max-width:1536px}.max-w-screen-lg{max-width:1024px}.max-w-screen-md{max-width:768px}.max-w-screen-sm{max-width:640px}.max-w-screen-xl{max-width:1280px}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.table-auto{table-layout:auto}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-1\/4{--tw-translate-x: -25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-12{--tw-translate-x: -3rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-5{--tw-translate-x: -1.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-full{--tw-translate-x: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-12{--tw-translate-y: -3rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-3\/4{--tw-translate-y: -75%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-12{--tw-translate-x: 3rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-5{--tw-translate-x: 1.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-full{--tw-translate-x: 100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-12{--tw-translate-y: 3rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-180{--tw-rotate: -180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-45{--tw-rotate: 45deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-help{cursor:help}.cursor-move{cursor:move}.cursor-pointer{cursor:pointer}.cursor-wait{cursor:wait}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.resize-none{resize:none}.resize{resize:both}.scroll-mt-9{scroll-margin-top:2.25rem}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.columns-\[--cols-default\]{-moz-columns:var(--cols-default);columns:var(--cols-default)}.break-inside-avoid{-moz-column-break-inside:avoid;break-inside:avoid}.auto-cols-fr{grid-auto-columns:minmax(0,1fr)}.grid-flow-col{grid-auto-flow:column}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.grid-cols-\[--cols-default\]{grid-template-columns:var(--cols-default)}.grid-cols-\[1fr_auto_1fr\]{grid-template-columns:1fr auto 1fr}.grid-cols-\[repeat\(7\,minmax\(theme\(spacing\.7\)\,1fr\)\)\]{grid-template-columns:repeat(7,minmax(1.75rem,1fr))}.grid-cols-\[repeat\(auto-fit\,minmax\(0\,1fr\)\)\]{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.grid-rows-\[1fr_auto_1fr\]{grid-template-rows:1fr auto 1fr}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.content-start{align-content:flex-start}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-start{justify-items:start}.justify-items-center{justify-items:center}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-14{gap:3.5rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.gap-x-1{-moz-column-gap:.25rem;column-gap:.25rem}.gap-x-1\.5{-moz-column-gap:.375rem;column-gap:.375rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-2\.5{-moz-column-gap:.625rem;column-gap:.625rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-x-5{-moz-column-gap:1.25rem;column-gap:1.25rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.gap-y-1{row-gap:.25rem}.gap-y-1\.5{row-gap:.375rem}.gap-y-2{row-gap:.5rem}.gap-y-3{row-gap:.75rem}.gap-y-4{row-gap:1rem}.gap-y-6{row-gap:1.5rem}.gap-y-7{row-gap:1.75rem}.gap-y-8{row-gap:2rem}.gap-y-px{row-gap:1px}.-space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(-.25rem * var(--tw-space-x-reverse));margin-left:calc(-.25rem * calc(1 - var(--tw-space-x-reverse)))}.-space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(-.5rem * var(--tw-space-x-reverse));margin-left:calc(-.5rem * calc(1 - var(--tw-space-x-reverse)))}.-space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(-.75rem * var(--tw-space-x-reverse));margin-left:calc(-.75rem * calc(1 - var(--tw-space-x-reverse)))}.-space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(-1rem * var(--tw-space-x-reverse));margin-left:calc(-1rem * calc(1 - var(--tw-space-x-reverse)))}.-space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(-1.25rem * var(--tw-space-x-reverse));margin-left:calc(-1.25rem * calc(1 - var(--tw-space-x-reverse)))}.-space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(-1.5rem * var(--tw-space-x-reverse));margin-left:calc(-1.5rem * calc(1 - var(--tw-space-x-reverse)))}.-space-x-7>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(-1.75rem * var(--tw-space-x-reverse));margin-left:calc(-1.75rem * calc(1 - var(--tw-space-x-reverse)))}.-space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(-2rem * var(--tw-space-x-reverse));margin-left:calc(-2rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-x>:not([hidden])~:not([hidden]){--tw-divide-x-reverse: 0;border-right-width:calc(1px * var(--tw-divide-x-reverse));border-left-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(243 244 246 / var(--tw-divide-opacity))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(229 231 235 / var(--tw-divide-opacity))}.self-start{align-self:flex-start}.self-stretch{align-self:stretch}.justify-self-start{justify-self:start}.justify-self-end{justify-self:end}.justify-self-center{justify-self:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-x-clip{overflow-x:clip}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-b-xl{border-bottom-right-radius:.75rem;border-bottom-left-radius:.75rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.rounded-t-xl{border-top-left-radius:.75rem;border-top-right-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-x-\[0\.5px\]{border-left-width:.5px;border-right-width:.5px}.border-y{border-top-width:1px;border-bottom-width:1px}.\!border-t-0{border-top-width:0px!important}.border-b{border-bottom-width:1px}.border-b-0{border-bottom-width:0px}.border-e{border-inline-end-width:1px}.border-l-2{border-left-width:2px}.border-s{border-inline-start-width:1px}.border-t{border-top-width:1px}.border-t-4{border-top-width:4px}.border-dashed{border-style:dashed}.\!border-none{border-style:none!important}.border-none{border-style:none}.border-custom-400{--tw-border-opacity: 1;border-color:rgba(var(--c-400),var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(243 244 246 / var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity))}.border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity))}.border-primary-500{--tw-border-opacity: 1;border-color:rgb(4 193 71 / var(--tw-border-opacity))}.border-primary-600{--tw-border-opacity: 1;border-color:rgb(3 173 64 / var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-zinc-200{--tw-border-opacity: 1;border-color:rgb(228 228 231 / var(--tw-border-opacity))}.border-zinc-300{--tw-border-opacity: 1;border-color:rgb(212 212 216 / var(--tw-border-opacity))}.border-zinc-400{--tw-border-opacity: 1;border-color:rgb(161 161 170 / var(--tw-border-opacity))}.border-zinc-800{--tw-border-opacity: 1;border-color:rgb(39 39 42 / var(--tw-border-opacity))}.border-t-gray-200{--tw-border-opacity: 1;border-top-color:rgb(229 231 235 / var(--tw-border-opacity))}.\!bg-gray-50{--tw-bg-opacity: 1 !important;background-color:rgb(249 250 251 / var(--tw-bg-opacity))!important}.\!bg-gray-700{--tw-bg-opacity: 1 !important;background-color:rgb(55 65 81 / var(--tw-bg-opacity))!important}.bg-background-light{background-color:var(--background-light)}.bg-black\/50{background-color:#00000080}.bg-custom-100{--tw-bg-opacity: 1;background-color:rgba(var(--c-100),var(--tw-bg-opacity))}.bg-custom-200{--tw-bg-opacity: 1;background-color:rgba(var(--c-200),var(--tw-bg-opacity))}.bg-custom-400{--tw-bg-opacity: 1;background-color:rgba(var(--c-400),var(--tw-bg-opacity))}.bg-custom-50{--tw-bg-opacity: 1;background-color:rgba(var(--c-50),var(--tw-bg-opacity))}.bg-custom-600{--tw-bg-opacity: 1;background-color:rgba(var(--c-600),var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.bg-gray-300{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.bg-gray-950\/50{background-color:#03071280}.bg-orange-200{--tw-bg-opacity: 1;background-color:rgb(254 215 170 / var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity: 1;background-color:rgb(4 193 71 / var(--tw-bg-opacity))}.bg-primary-600{--tw-bg-opacity: 1;background-color:rgb(3 173 64 / var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-white\/0{background-color:#fff0}.bg-white\/5{background-color:#ffffff0d}.bg-zinc-50{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity))}.\!bg-none{background-image:none!important}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-transparent{--tw-gradient-from: transparent var(--tw-gradient-from-position);--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-zinc-50{--tw-gradient-to: #fafafa var(--tw-gradient-to-position)}.bg-cover{background-size:cover}.bg-center{background-position:center}.object-cover{-o-object-fit:cover;object-fit:cover}.object-center{-o-object-position:center;object-position:center}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pe-0{padding-inline-end:0px}.pe-1{padding-inline-end:.25rem}.pe-2{padding-inline-end:.5rem}.pe-3{padding-inline-end:.75rem}.pe-4{padding-inline-end:1rem}.pe-6{padding-inline-end:1.5rem}.pe-8{padding-inline-end:2rem}.ps-0{padding-inline-start:0px}.ps-1{padding-inline-start:.25rem}.ps-2{padding-inline-start:.5rem}.ps-3{padding-inline-start:.75rem}.ps-4{padding-inline-start:1rem}.ps-\[5\.25rem\]{padding-inline-start:5.25rem}.pt-0{padding-top:0}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-justify{text-align:justify}.text-start{text-align:start}.text-end{text-align:end}.align-top{vertical-align:top}.align-middle{vertical-align:middle}.align-bottom{vertical-align:bottom}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.font-serif{font-family:ui-serif,Georgia,Cambria,Times New Roman,Times,serif}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-extralight{font-weight:200}.font-light{font-weight:300}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.font-thin{font-weight:100}.capitalize{text-transform:capitalize}.italic{font-style:italic}.leading-5{line-height:1.25rem}.leading-6{line-height:1.5rem}.leading-loose{line-height:2}.tracking-tight{letter-spacing:-.025em}.tracking-tighter{letter-spacing:-.05em}.text-\[\#141414\]{--tw-text-opacity: 1;color:rgb(20 20 20 / var(--tw-text-opacity))}.text-base-light{color:var(--text-light)}.text-custom-400{--tw-text-opacity: 1;color:rgba(var(--c-400),var(--tw-text-opacity))}.text-custom-50{--tw-text-opacity: 1;color:rgba(var(--c-50),var(--tw-text-opacity))}.text-custom-500{--tw-text-opacity: 1;color:rgba(var(--c-500),var(--tw-text-opacity))}.text-custom-600{--tw-text-opacity: 1;color:rgba(var(--c-600),var(--tw-text-opacity))}.text-custom-700{--tw-text-opacity: 1;color:rgba(var(--c-700),var(--tw-text-opacity))}.text-custom-700\/50{color:rgba(var(--c-700),.5)}.text-custom-800{--tw-text-opacity: 1;color:rgba(var(--c-800),var(--tw-text-opacity))}.text-custom-900{--tw-text-opacity: 1;color:rgba(var(--c-900),var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.text-gray-700\/50{color:#37415180}.text-gray-950{--tw-text-opacity: 1;color:rgb(3 7 18 / var(--tw-text-opacity))}.text-primary-400{--tw-text-opacity: 1;color:rgb(77 206 118 / var(--tw-text-opacity))}.text-primary-500{--tw-text-opacity: 1;color:rgb(4 193 71 / var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity: 1;color:rgb(3 173 64 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-zinc-400{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity))}.text-zinc-500{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity))}.text-zinc-700{--tw-text-opacity: 1;color:rgb(63 63 70 / var(--tw-text-opacity))}.text-zinc-800{--tw-text-opacity: 1;color:rgb(39 39 42 / var(--tw-text-opacity))}.underline{text-decoration-line:underline}.decoration-dotted{text-decoration-style:dotted}.underline-offset-2{text-underline-offset:2px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-0{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-4{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-inset{--tw-ring-inset: inset}.ring-custom-600{--tw-ring-opacity: 1;--tw-ring-color: rgba(var(--c-600), var(--tw-ring-opacity))}.ring-custom-600\/10{--tw-ring-color: rgba(var(--c-600), .1)}.ring-custom-600\/20{--tw-ring-color: rgba(var(--c-600), .2)}.ring-gray-200{--tw-ring-opacity: 1;--tw-ring-color: rgb(229 231 235 / var(--tw-ring-opacity))}.ring-gray-300{--tw-ring-opacity: 1;--tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity))}.ring-gray-600\/10{--tw-ring-color: rgb(75 85 99 / .1)}.ring-gray-900\/10{--tw-ring-color: rgb(17 24 39 / .1)}.ring-gray-950\/10{--tw-ring-color: rgb(3 7 18 / .1)}.ring-gray-950\/5{--tw-ring-color: rgb(3 7 18 / .05)}.ring-white{--tw-ring-opacity: 1;--tw-ring-color: rgb(255 255 255 / var(--tw-ring-opacity))}.ring-white\/10{--tw-ring-color: rgb(255 255 255 / .1)}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow{--tw-drop-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / .1)) drop-shadow(0 1px 1px rgb(0 0 0 / .06));filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.delay-100{transition-delay:.1s}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.duration-75{transition-duration:75ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}[x-cloak]{display:none!important}.\[transform\:translateZ\(0\)\]{transform:translateZ(0)}@media (min-width: 768px){.md\:prose{color:var(--tw-prose-body);max-width:65ch}.md\:prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.md\:prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.md\:prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.md\:prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.md\:prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.md\:prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.md\:prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.md\:prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.md\:prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.md\:prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.md\:prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.md\:prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.md\:prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.md\:prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.md\:prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.md\:prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.md\:prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.md\:prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.md\:prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.md\:prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.md\:prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.md\:prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.md\:prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.md\:prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.md\:prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.md\:prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.md\:prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.md\:prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.md\:prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.md\:prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.md\:prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.md\:prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.md\:prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.md\:prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.md\:prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.md\:prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.md\:prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%),0 3px rgb(var(--tw-prose-kbd-shadows) / 10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.md\:prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.md\:prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.md\:prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.md\:prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.md\:prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.md\:prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.md\:prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.md\:prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.md\:prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.md\:prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.md\:prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.md\:prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.md\:prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.md\:prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.md\:prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.md\:prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.md\:prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.md\:prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.md\:prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.md\:prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.md\:prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.md\:prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.md\:prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.md\:prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.md\:prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.md\:prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: 17 24 39;--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: 255 255 255;--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.md\:prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.md\:prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.md\:prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.md\:prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.md\:prose :where(.md\:prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.md\:prose :where(.md\:prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.md\:prose :where(.md\:prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.md\:prose :where(.md\:prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.md\:prose :where(.md\:prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.md\:prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.md\:prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.md\:prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.md\:prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.md\:prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.md\:prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.md\:prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.md\:prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.md\:prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.md\:prose :where(.md\:prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.md\:prose :where(.md\:prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}}@media (prefers-color-scheme: dark){.dark\:prose-invert{--tw-prose-body: var(--tw-prose-invert-body);--tw-prose-headings: var(--tw-prose-invert-headings);--tw-prose-lead: var(--tw-prose-invert-lead);--tw-prose-links: var(--tw-prose-invert-links);--tw-prose-bold: var(--tw-prose-invert-bold);--tw-prose-counters: var(--tw-prose-invert-counters);--tw-prose-bullets: var(--tw-prose-invert-bullets);--tw-prose-hr: var(--tw-prose-invert-hr);--tw-prose-quotes: var(--tw-prose-invert-quotes);--tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);--tw-prose-captions: var(--tw-prose-invert-captions);--tw-prose-kbd: var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows);--tw-prose-code: var(--tw-prose-invert-code);--tw-prose-pre-code: var(--tw-prose-invert-pre-code);--tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);--tw-prose-th-borders: var(--tw-prose-invert-th-borders);--tw-prose-td-borders: var(--tw-prose-invert-td-borders)}}.placeholder\:text-gray-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.placeholder\:text-gray-400::placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.before\:absolute:before{content:var(--tw-content);position:absolute}.before\:inset-y-0:before{content:var(--tw-content);top:0;bottom:0}.before\:start-0:before{content:var(--tw-content);inset-inline-start:0px}.before\:h-full:before{content:var(--tw-content);height:100%}.before\:w-0\.5:before{content:var(--tw-content);width:.125rem}.before\:bg-primary-600:before{content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(3 173 64 / var(--tw-bg-opacity))}.first\:border-s-0:first-child{border-inline-start-width:0px}.first\:border-t-0:first-child{border-top-width:0px}.last\:border-e-0:last-child{border-inline-end-width:0px}.first-of-type\:ps-1:first-of-type{padding-inline-start:.25rem}.last-of-type\:pe-1:last-of-type{padding-inline-end:.25rem}.checked\:ring-0:checked{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-within\:bg-gray-50:focus-within{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.hover\:bg-custom-400\/10:hover{background-color:rgba(var(--c-400),.1)}.hover\:bg-custom-50:hover{--tw-bg-opacity: 1;background-color:rgba(var(--c-50),var(--tw-bg-opacity))}.hover\:bg-custom-500:hover{--tw-bg-opacity: 1;background-color:rgba(var(--c-500),var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.hover\:bg-gray-400\/10:hover{background-color:#9ca3af1a}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.hover\:bg-primary-600:hover{--tw-bg-opacity: 1;background-color:rgb(3 173 64 / var(--tw-bg-opacity))}.hover\:bg-zinc-50:hover{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity))}.hover\:text-custom-600:hover{--tw-text-opacity: 1;color:rgba(var(--c-600),var(--tw-text-opacity))}.hover\:text-custom-700\/75:hover{color:rgba(var(--c-700),.75)}.hover\:text-gray-500:hover{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.hover\:text-gray-700\/75:hover{color:#374151bf}.hover\:text-zinc-400:hover{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity))}.hover\:text-zinc-700:hover{--tw-text-opacity: 1;color:rgb(63 63 70 / var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:opacity-80:hover{opacity:.8}.focus\:ring-0:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-primary-600:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(3 173 64 / var(--tw-ring-opacity))}.focus\:ring-offset-0:focus{--tw-ring-offset-width: 0px}.checked\:focus\:ring-primary-500\/50:focus:checked{--tw-ring-color: rgb(4 193 71 / .5)}.focus-visible\:z-10:focus-visible{z-index:10}.focus-visible\:border-primary-500:focus-visible{--tw-border-opacity: 1;border-color:rgb(4 193 71 / var(--tw-border-opacity))}.focus-visible\:bg-custom-50:focus-visible{--tw-bg-opacity: 1;background-color:rgba(var(--c-50),var(--tw-bg-opacity))}.focus-visible\:bg-gray-100:focus-visible{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.focus-visible\:bg-gray-50:focus-visible{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.focus-visible\:text-custom-700\/75:focus-visible{color:rgba(var(--c-700),.75)}.focus-visible\:text-gray-500:focus-visible{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.focus-visible\:text-gray-700\/75:focus-visible{color:#374151bf}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-1:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-inset:focus-visible{--tw-ring-inset: inset}.focus-visible\:ring-custom-500\/50:focus-visible{--tw-ring-color: rgba(var(--c-500), .5)}.focus-visible\:ring-custom-600:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgba(var(--c-600), var(--tw-ring-opacity))}.focus-visible\:ring-gray-400\/40:focus-visible{--tw-ring-color: rgb(156 163 175 / .4)}.focus-visible\:ring-primary-500:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(4 193 71 / var(--tw-ring-opacity))}.focus-visible\:ring-primary-600:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(3 173 64 / var(--tw-ring-opacity))}.enabled\:cursor-wait:enabled{cursor:wait}.enabled\:opacity-70:enabled{opacity:.7}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:bg-gray-50:disabled{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.disabled\:text-gray-50:disabled{--tw-text-opacity: 1;color:rgb(249 250 251 / var(--tw-text-opacity))}.disabled\:text-gray-500:disabled{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.disabled\:opacity-70:disabled{opacity:.7}.disabled\:\[-webkit-text-fill-color\:theme\(colors\.gray\.500\)\]:disabled{-webkit-text-fill-color:#6b7280}.disabled\:placeholder\:\[-webkit-text-fill-color\:theme\(colors\.gray\.400\)\]:disabled::-moz-placeholder{-webkit-text-fill-color:#9ca3af}.disabled\:placeholder\:\[-webkit-text-fill-color\:theme\(colors\.gray\.400\)\]:disabled::placeholder{-webkit-text-fill-color:#9ca3af}.disabled\:checked\:bg-current:checked:disabled{background-color:currentColor}.disabled\:checked\:text-gray-400:checked:disabled{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.group\/item:first-child .group-first\/item\:rounded-s-lg{border-start-start-radius:.5rem;border-end-start-radius:.5rem}.group\/item:last-child .group-last\/item\:rounded-e-lg{border-start-end-radius:.5rem;border-end-end-radius:.5rem}.group\/button:hover .group-hover\/button\:text-gray-500,.group:hover .group-hover\:text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.group:hover .group-hover\:text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.group\/item:hover .group-hover\/item\:underline,.group\/link:hover .group-hover\/link\:underline{text-decoration-line:underline}.group:focus-visible .group-focus-visible\:text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.group:focus-visible .group-focus-visible\:text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.group\/item:focus-visible .group-focus-visible\/item\:underline{text-decoration-line:underline}.group\/link:focus-visible .group-focus-visible\/link\:underline{text-decoration-line:underline}.prose-p\:leading-normal :is(:where(p):not(:where([class~=not-prose],[class~=not-prose] *))){line-height:1.5}.prose-a\:text-primary-500 :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))){--tw-text-opacity: 1;color:rgb(4 193 71 / var(--tw-text-opacity))}.prose-a\:underline :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))){text-decoration-line:underline}@media (min-width: 640px){.sm\:relative{position:relative}.sm\:inset-x-auto{left:auto;right:auto}.sm\:end-0{inset-inline-end:0px}.sm\:col-\[--col-span-sm\]{grid-column:var(--col-span-sm)}.sm\:col-span-2{grid-column:span 2 / span 2}.sm\:col-start-\[--col-start-sm\]{grid-column-start:var(--col-start-sm)}.sm\:-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.sm\:-my-2{margin-top:-.5rem;margin-bottom:-.5rem}.sm\:ms-auto{margin-inline-start:auto}.sm\:mt-3{margin-top:.75rem}.sm\:mt-7{margin-top:1.75rem}.sm\:block{display:block}.sm\:flex{display:flex}.sm\:table-cell{display:table-cell}.sm\:grid{display:grid}.sm\:inline-grid{display:inline-grid}.sm\:hidden{display:none}.sm\:w-\[calc\(100\%\+3rem\)\]{width:calc(100% + 3rem)}.sm\:w-screen{width:100vw}.sm\:max-w-2xl{max-width:42rem}.sm\:max-w-3xl{max-width:48rem}.sm\:max-w-4xl{max-width:56rem}.sm\:max-w-5xl{max-width:64rem}.sm\:max-w-6xl{max-width:72rem}.sm\:max-w-7xl{max-width:80rem}.sm\:max-w-lg{max-width:32rem}.sm\:max-w-md{max-width:28rem}.sm\:max-w-sm{max-width:24rem}.sm\:max-w-xl{max-width:36rem}.sm\:max-w-xs{max-width:20rem}.sm\:columns-\[--cols-sm\]{-moz-columns:var(--cols-sm);columns:var(--cols-sm)}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-\[--cols-sm\]{grid-template-columns:var(--cols-sm)}.sm\:grid-cols-\[repeat\(auto-fit\,minmax\(0\,1fr\)\)\]{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.sm\:grid-rows-\[1fr_auto_3fr\]{grid-template-rows:1fr auto 3fr}.sm\:flex-row{flex-direction:row}.sm\:flex-nowrap{flex-wrap:nowrap}.sm\:items-start{align-items:flex-start}.sm\:items-end{align-items:flex-end}.sm\:items-center{align-items:center}.sm\:justify-end{justify-content:flex-end}.sm\:justify-between{justify-content:space-between}.sm\:gap-1{gap:.25rem}.sm\:gap-3{gap:.75rem}.sm\:gap-5{gap:1.25rem}.sm\:gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.sm\:rounded-xl{border-radius:.75rem}.sm\:p-10{padding:2.5rem}.sm\:px-12{padding-left:3rem;padding-right:3rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:px-8{padding-left:2rem;padding-right:2rem}.sm\:py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.sm\:pb-8{padding-bottom:2rem}.sm\:pe-3{padding-inline-end:.75rem}.sm\:pe-6{padding-inline-end:1.5rem}.sm\:ps-3{padding-inline-start:.75rem}.sm\:ps-6{padding-inline-start:1.5rem}.sm\:pt-1\.5{padding-top:.375rem}.sm\:pt-10{padding-top:2.5rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-base{font-size:1rem;line-height:1.5rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}.sm\:text-xl{font-size:1.25rem;line-height:1.75rem}.sm\:leading-6{line-height:1.5rem}.sm\:first-of-type\:ps-3:first-of-type{padding-inline-start:.75rem}.sm\:first-of-type\:ps-6:first-of-type{padding-inline-start:1.5rem}.sm\:last-of-type\:pe-3:last-of-type{padding-inline-end:.75rem}.sm\:last-of-type\:pe-6:last-of-type{padding-inline-end:1.5rem}}@media (min-width: 768px){.md\:bottom-4{bottom:1rem}.md\:order-first{order:-9999}.md\:col-\[--col-span-md\]{grid-column:var(--col-span-md)}.md\:col-span-2{grid-column:span 2 / span 2}.md\:col-start-\[--col-start-md\]{grid-column-start:var(--col-start-md)}.md\:ml-6{margin-left:1.5rem}.md\:mt-0{margin-top:0}.md\:block{display:block}.md\:flex{display:flex}.md\:table-cell{display:table-cell}.md\:inline-grid{display:inline-grid}.md\:hidden{display:none}.md\:w-max{width:-moz-max-content;width:max-content}.md\:max-w-60{max-width:15rem}.md\:columns-\[--cols-md\]{-moz-columns:var(--cols-md);columns:var(--cols-md)}.md\:grid-flow-col{grid-auto-flow:column}.md\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-\[--cols-md\]{grid-template-columns:var(--cols-md)}.md\:flex-row{flex-direction:row}.md\:items-start{align-items:flex-start}.md\:items-end{align-items:flex-end}.md\:items-center{align-items:center}.md\:justify-end{justify-content:flex-end}.md\:gap-1{gap:.25rem}.md\:gap-3{gap:.75rem}.md\:divide-y-0>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(0px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(0px * var(--tw-divide-y-reverse))}.md\:overflow-x-auto{overflow-x:auto}.md\:rounded-xl{border-radius:.75rem}.md\:p-20{padding:5rem}.md\:px-6{padding-left:1.5rem;padding-right:1.5rem}.md\:pe-6{padding-inline-end:1.5rem}.md\:ps-3{padding-inline-start:.75rem}.md\:text-base{font-size:1rem;line-height:1.5rem}}@media (min-width: 1024px){.lg\:sticky{position:sticky}.lg\:z-0{z-index:0}.lg\:col-\[--col-span-lg\]{grid-column:var(--col-span-lg)}.lg\:col-span-3{grid-column:span 3 / span 3}.lg\:col-start-\[--col-start-lg\]{grid-column-start:var(--col-start-lg)}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:table-cell{display:table-cell}.lg\:inline-grid{display:inline-grid}.lg\:hidden{display:none}.lg\:h-full{height:100%}.lg\:max-w-xs{max-width:20rem}.lg\:-translate-x-full{--tw-translate-x: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.lg\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.lg\:columns-\[--cols-lg\]{-moz-columns:var(--cols-lg);columns:var(--cols-lg)}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:grid-cols-\[--cols-lg\]{grid-template-columns:var(--cols-lg)}.lg\:flex-row{flex-direction:row}.lg\:items-start{align-items:flex-start}.lg\:items-end{align-items:flex-end}.lg\:items-center{align-items:center}.lg\:gap-1{gap:.25rem}.lg\:gap-3{gap:.75rem}.lg\:bg-transparent{background-color:transparent}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:pe-8{padding-inline-end:2rem}.lg\:shadow-none{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.lg\:shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.lg\:ring-0{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.lg\:transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.lg\:transition-none{transition-property:none}.lg\:delay-100{transition-delay:.1s}}@media (min-width: 1280px){.xl\:col-\[--col-span-xl\]{grid-column:var(--col-span-xl)}.xl\:col-start-\[--col-start-xl\]{grid-column-start:var(--col-start-xl)}.xl\:block{display:block}.xl\:table-cell{display:table-cell}.xl\:inline-grid{display:inline-grid}.xl\:hidden{display:none}.xl\:columns-\[--cols-xl\]{-moz-columns:var(--cols-xl);columns:var(--cols-xl)}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-\[--cols-xl\]{grid-template-columns:var(--cols-xl)}.xl\:flex-row{flex-direction:row}.xl\:items-start{align-items:flex-start}.xl\:items-end{align-items:flex-end}.xl\:items-center{align-items:center}.xl\:gap-1{gap:.25rem}.xl\:gap-3{gap:.75rem}}@media (min-width: 1536px){.\32xl\:col-\[--col-span-2xl\]{grid-column:var(--col-span-2xl)}.\32xl\:col-start-\[--col-start-2xl\]{grid-column-start:var(--col-start-2xl)}.\32xl\:block{display:block}.\32xl\:table-cell{display:table-cell}.\32xl\:inline-grid{display:inline-grid}.\32xl\:hidden{display:none}.\32xl\:columns-\[--cols-2xl\]{-moz-columns:var(--cols-2xl);columns:var(--cols-2xl)}.\32xl\:grid-cols-\[--cols-2xl\]{grid-template-columns:var(--cols-2xl)}.\32xl\:flex-row{flex-direction:row}.\32xl\:items-start{align-items:flex-start}.\32xl\:items-end{align-items:flex-end}.\32xl\:items-center{align-items:center}.\32xl\:gap-1{gap:.25rem}.\32xl\:gap-3{gap:.75rem}}.ltr\:hidden:where([dir=ltr],[dir=ltr] *){display:none}.rtl\:hidden:where([dir=rtl],[dir=rtl] *){display:none}.rtl\:-translate-x-0:where([dir=rtl],[dir=rtl] *){--tw-translate-x: -0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rtl\:-translate-x-5:where([dir=rtl],[dir=rtl] *){--tw-translate-x: -1.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rtl\:-translate-x-full:where([dir=rtl],[dir=rtl] *){--tw-translate-x: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rtl\:translate-x-1\/2:where([dir=rtl],[dir=rtl] *){--tw-translate-x: 50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rtl\:translate-x-1\/4:where([dir=rtl],[dir=rtl] *){--tw-translate-x: 25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rtl\:translate-x-full:where([dir=rtl],[dir=rtl] *){--tw-translate-x: 100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rtl\:rotate-180:where([dir=rtl],[dir=rtl] *){--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rtl\:flex-row-reverse:where([dir=rtl],[dir=rtl] *){flex-direction:row-reverse}.rtl\:divide-x-reverse:where([dir=rtl],[dir=rtl] *)>:not([hidden])~:not([hidden]){--tw-divide-x-reverse: 1}@media (min-width: 1024px){.rtl\:lg\:-translate-x-0:where([dir=rtl],[dir=rtl] *){--tw-translate-x: -0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rtl\:lg\:translate-x-full:where([dir=rtl],[dir=rtl] *){--tw-translate-x: 100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}}@media (prefers-color-scheme: dark){.dark\:flex{display:flex}.dark\:hidden{display:none}.dark\:divide-white\/10>:not([hidden])~:not([hidden]){border-color:#ffffff1a}.dark\:divide-white\/5>:not([hidden])~:not([hidden]){border-color:#ffffff0d}.dark\:divide-zinc-700>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(63 63 70 / var(--tw-divide-opacity))}.dark\:border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity))}.dark\:border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity))}.dark\:border-primary-500{--tw-border-opacity: 1;border-color:rgb(4 193 71 / var(--tw-border-opacity))}.dark\:border-white\/10{border-color:#ffffff1a}.dark\:border-white\/5{border-color:#ffffff0d}.dark\:border-zinc-400{--tw-border-opacity: 1;border-color:rgb(161 161 170 / var(--tw-border-opacity))}.dark\:border-zinc-600{--tw-border-opacity: 1;border-color:rgb(82 82 91 / var(--tw-border-opacity))}.dark\:border-zinc-700{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity))}.dark\:border-t-white\/10{border-top-color:#ffffff1a}.dark\:\!bg-gray-700{--tw-bg-opacity: 1 !important;background-color:rgb(55 65 81 / var(--tw-bg-opacity))!important}.dark\:bg-background-dark{background-color:var(--background-dark)}.dark\:bg-custom-200\/80{background-color:rgba(var(--c-200),.8)}.dark\:bg-custom-400\/10{background-color:rgba(var(--c-400),.1)}.dark\:bg-custom-400\/40{background-color:rgba(var(--c-400),.4)}.dark\:bg-custom-500{--tw-bg-opacity: 1;background-color:rgba(var(--c-500),var(--tw-bg-opacity))}.dark\:bg-custom-500\/20{background-color:rgba(var(--c-500),.2)}.dark\:bg-gray-400\/10{background-color:#9ca3af1a}.dark\:bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity))}.dark\:bg-gray-500\/20{background-color:#6b728033}.dark\:bg-gray-600{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity))}.dark\:bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity))}.dark\:bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity))}.dark\:bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity))}.dark\:bg-gray-900\/30{background-color:#1118274d}.dark\:bg-gray-950{--tw-bg-opacity: 1;background-color:rgb(3 7 18 / var(--tw-bg-opacity))}.dark\:bg-gray-950\/75{background-color:#030712bf}.dark\:bg-primary-400{--tw-bg-opacity: 1;background-color:rgb(77 206 118 / var(--tw-bg-opacity))}.dark\:bg-primary-500{--tw-bg-opacity: 1;background-color:rgb(4 193 71 / var(--tw-bg-opacity))}.dark\:bg-transparent{background-color:transparent}.dark\:bg-white\/10{background-color:#ffffff1a}.dark\:bg-white\/5{background-color:#ffffff0d}.dark\:bg-zinc-800{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / var(--tw-bg-opacity))}.dark\:bg-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity))}.dark\:from-transparent{--tw-gradient-from: transparent var(--tw-gradient-from-position);--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.dark\:to-zinc-900{--tw-gradient-to: #18181b var(--tw-gradient-to-position)}.dark\:fill-current{fill:currentColor}.dark\:text-base-dark{color:var(--text-dark)}.dark\:text-custom-200{--tw-text-opacity: 1;color:rgba(var(--c-200),var(--tw-text-opacity))}.dark\:text-custom-300\/50{color:rgba(var(--c-300),.5)}.dark\:text-custom-400{--tw-text-opacity: 1;color:rgba(var(--c-400),var(--tw-text-opacity))}.dark\:text-custom-400\/10{color:rgba(var(--c-400),.1)}.dark\:text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.dark\:text-gray-300\/50{color:#d1d5db80}.dark\:text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.dark\:text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.dark\:text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.dark\:text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.dark\:text-primary-400{--tw-text-opacity: 1;color:rgb(77 206 118 / var(--tw-text-opacity))}.dark\:text-primary-500{--tw-text-opacity: 1;color:rgb(4 193 71 / var(--tw-text-opacity))}.dark\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.dark\:text-white\/5{color:#ffffff0d}.dark\:text-zinc-300{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity))}.dark\:text-zinc-400{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity))}.dark\:text-zinc-500{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity))}.dark\:text-zinc-800{--tw-text-opacity: 1;color:rgb(39 39 42 / var(--tw-text-opacity))}.dark\:ring-custom-400\/30{--tw-ring-color: rgba(var(--c-400), .3)}.dark\:ring-custom-500{--tw-ring-opacity: 1;--tw-ring-color: rgba(var(--c-500), var(--tw-ring-opacity))}.dark\:ring-gray-400\/20{--tw-ring-color: rgb(156 163 175 / .2)}.dark\:ring-gray-50\/10{--tw-ring-color: rgb(249 250 251 / .1)}.dark\:ring-gray-700{--tw-ring-opacity: 1;--tw-ring-color: rgb(55 65 81 / var(--tw-ring-opacity))}.dark\:ring-gray-900{--tw-ring-opacity: 1;--tw-ring-color: rgb(17 24 39 / var(--tw-ring-opacity))}.dark\:ring-white\/10{--tw-ring-color: rgb(255 255 255 / .1)}.dark\:ring-white\/20{--tw-ring-color: rgb(255 255 255 / .2)}.dark\:placeholder\:text-gray-500::-moz-placeholder{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.dark\:placeholder\:text-gray-500::placeholder{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.dark\:before\:bg-primary-500:before{content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(4 193 71 / var(--tw-bg-opacity))}.dark\:checked\:bg-primary-500:checked{--tw-bg-opacity: 1;background-color:rgb(4 193 71 / var(--tw-bg-opacity))}.dark\:focus-within\:bg-white\/5:focus-within{background-color:#ffffff0d}.dark\:hover\:bg-custom-400:hover{--tw-bg-opacity: 1;background-color:rgba(var(--c-400),var(--tw-bg-opacity))}.dark\:hover\:bg-custom-400\/10:hover{background-color:rgba(var(--c-400),.1)}.dark\:hover\:bg-white\/10:hover{background-color:#ffffff1a}.dark\:hover\:bg-white\/5:hover{background-color:#ffffff0d}.dark\:hover\:bg-zinc-900\/50:hover{background-color:#18181b80}.dark\:hover\:text-custom-300:hover{--tw-text-opacity: 1;color:rgba(var(--c-300),var(--tw-text-opacity))}.dark\:hover\:text-custom-300\/75:hover{color:rgba(var(--c-300),.75)}.dark\:hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.dark\:hover\:text-gray-300\/75:hover{color:#d1d5dbbf}.dark\:hover\:text-gray-400:hover{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.dark\:hover\:text-zinc-300:hover{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity))}.dark\:hover\:ring-white\/20:hover{--tw-ring-color: rgb(255 255 255 / .2)}.dark\:focus\:ring-primary-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(4 193 71 / var(--tw-ring-opacity))}.dark\:checked\:focus\:ring-primary-400\/50:focus:checked{--tw-ring-color: rgb(77 206 118 / .5)}.dark\:focus-visible\:border-primary-500:focus-visible{--tw-border-opacity: 1;border-color:rgb(4 193 71 / var(--tw-border-opacity))}.dark\:focus-visible\:bg-custom-400\/10:focus-visible{background-color:rgba(var(--c-400),.1)}.dark\:focus-visible\:bg-white\/5:focus-visible{background-color:#ffffff0d}.dark\:focus-visible\:text-custom-300\/75:focus-visible{color:rgba(var(--c-300),.75)}.dark\:focus-visible\:text-gray-300\/75:focus-visible{color:#d1d5dbbf}.dark\:focus-visible\:text-gray-400:focus-visible{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.dark\:focus-visible\:ring-custom-400\/50:focus-visible{--tw-ring-color: rgba(var(--c-400), .5)}.dark\:focus-visible\:ring-custom-500:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgba(var(--c-500), var(--tw-ring-opacity))}.dark\:focus-visible\:ring-primary-500:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(4 193 71 / var(--tw-ring-opacity))}.dark\:disabled\:bg-transparent:disabled{background-color:transparent}.dark\:disabled\:text-gray-400:disabled{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.dark\:disabled\:ring-white\/10:disabled{--tw-ring-color: rgb(255 255 255 / .1)}.dark\:disabled\:\[-webkit-text-fill-color\:theme\(colors\.gray\.400\)\]:disabled{-webkit-text-fill-color:#9ca3af}.dark\:disabled\:placeholder\:\[-webkit-text-fill-color\:theme\(colors\.gray\.500\)\]:disabled::-moz-placeholder{-webkit-text-fill-color:#6b7280}.dark\:disabled\:placeholder\:\[-webkit-text-fill-color\:theme\(colors\.gray\.500\)\]:disabled::placeholder{-webkit-text-fill-color:#6b7280}.dark\:disabled\:checked\:bg-gray-600:checked:disabled{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity))}.group\/button:hover .dark\:group-hover\/button\:text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.group:hover .dark\:group-hover\:text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.group:hover .dark\:group-hover\:text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.group:focus-visible .dark\:group-focus-visible\:text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.group:focus-visible .dark\:group-focus-visible\:text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}@media (min-width: 1024px){.dark\:lg\:bg-transparent{background-color:transparent}}}.\[\&\.trix-active\]\:bg-gray-50.trix-active{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.\[\&\.trix-active\]\:text-primary-600.trix-active{--tw-text-opacity: 1;color:rgb(3 173 64 / var(--tw-text-opacity))}@media (prefers-color-scheme: dark){.dark\:\[\&\.trix-active\]\:bg-white\/5.trix-active{background-color:#ffffff0d}.dark\:\[\&\.trix-active\]\:text-primary-400.trix-active{--tw-text-opacity: 1;color:rgb(77 206 118 / var(--tw-text-opacity))}}.\[\&\:\:-ms-reveal\]\:hidden::-ms-reveal{display:none}.\[\&\:not\(\:first-of-type\)\]\:border-s:not(:first-of-type){border-inline-start-width:1px}.\[\&\:not\(\:has\(\.fi-ac-action\:focus\)\)\]\:focus-within\:ring-2:focus-within:not(:has(.fi-ac-action:focus)){--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.\[\&\:not\(\:has\(\.fi-ac-action\:focus\)\)\]\:focus-within\:ring-primary-600:focus-within:not(:has(.fi-ac-action:focus)){--tw-ring-opacity: 1;--tw-ring-color: rgb(3 173 64 / var(--tw-ring-opacity))}@media (prefers-color-scheme: dark){.dark\:\[\&\:not\(\:has\(\.fi-ac-action\:focus\)\)\]\:focus-within\:ring-primary-500:focus-within:not(:has(.fi-ac-action:focus)){--tw-ring-opacity: 1;--tw-ring-color: rgb(4 193 71 / var(--tw-ring-opacity))}}.\[\&\:not\(\:last-of-type\)\]\:border-e:not(:last-of-type){border-inline-end-width:1px}.\[\&\:not\(\:nth-child\(1_of_\.fi-btn\)\)\]\:shadow-\[-1px_0_0_0_theme\(colors\.gray\.200\)\]:not(:nth-child(1 of.fi-btn)){--tw-shadow: -1px 0 0 0 #e5e7eb;--tw-shadow-colored: -1px 0 0 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}@media (prefers-color-scheme: dark){.dark\:\[\&\:not\(\:nth-child\(1_of_\.fi-btn\)\)\]\:shadow-\[-1px_0_0_0_theme\(colors\.white\/20\%\)\]:not(:nth-child(1 of.fi-btn)){--tw-shadow: -1px 0 0 0 rgb(255 255 255 / 20%);--tw-shadow-colored: -1px 0 0 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}}.\[\&\:not\(\:nth-last-child\(1_of_\.fi-btn\)\)\]\:me-px:not(:nth-last-child(1 of.fi-btn)){margin-inline-end:1px}.\[\&\:nth-child\(1_of_\.fi-btn\)\]\:rounded-s-lg:nth-child(1 of.fi-btn){border-start-start-radius:.5rem;border-end-start-radius:.5rem}.\[\&\:nth-last-child\(1_of_\.fi-btn\)\]\:rounded-e-lg:nth-last-child(1 of.fi-btn){border-start-end-radius:.5rem;border-end-end-radius:.5rem}.\[\&\>\*\:first-child\]\:relative>*:first-child{position:relative}.\[\&\>\*\:first-child\]\:mt-0>*:first-child{margin-top:0}.\[\&\>\*\:first-child\]\:before\:absolute>*:first-child:before{content:var(--tw-content);position:absolute}.\[\&\>\*\:first-child\]\:before\:inset-y-0>*:first-child:before{content:var(--tw-content);top:0;bottom:0}.\[\&\>\*\:first-child\]\:before\:start-0>*:first-child:before{content:var(--tw-content);inset-inline-start:0px}.\[\&\>\*\:first-child\]\:before\:w-0\.5>*:first-child:before{content:var(--tw-content);width:.125rem}.\[\&\>\*\:first-child\]\:before\:bg-primary-600>*:first-child:before{content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(3 173 64 / var(--tw-bg-opacity))}@media (prefers-color-scheme: dark){.\[\&\>\*\:first-child\]\:dark\:before\:bg-primary-500>*:first-child:before{content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(4 193 71 / var(--tw-bg-opacity))}}.\[\&\>\*\:last-child\]\:mb-0>*:last-child{margin-bottom:0}.\[\&_\.choices\\_\\_inner\]\:ps-0 .choices__inner{padding-inline-start:0px}.\[\&_\.fi-badge-delete-button\]\:hidden .fi-badge-delete-button{display:none}.\[\&_\.filepond--root\]\:font-sans .filepond--root{font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.\[\&_optgroup\]\:bg-white optgroup{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}@media (prefers-color-scheme: dark){.\[\&_optgroup\]\:dark\:bg-gray-900 optgroup{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity))}}.\[\&_option\]\:bg-white option{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}@media (prefers-color-scheme: dark){.\[\&_option\]\:dark\:bg-gray-900 option{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity))}}:checked+*>.\[\:checked\+\*\>\&\]\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}@media (hover:hover){.\[\@media\(hover\:hover\)\]\:transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.\[\@media\(hover\:hover\)\]\:duration-75{transition-duration:75ms}}input:checked+.\[input\:checked\+\&\]\:bg-custom-600{--tw-bg-opacity: 1;background-color:rgba(var(--c-600),var(--tw-bg-opacity))}input:checked+.\[input\:checked\+\&\]\:bg-gray-400{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity))}input:checked+.\[input\:checked\+\&\]\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}input:checked+.\[input\:checked\+\&\]\:ring-0{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}input:checked+.\[input\:checked\+\&\]\:hover\:bg-custom-500:hover{--tw-bg-opacity: 1;background-color:rgba(var(--c-500),var(--tw-bg-opacity))}input:checked+.\[input\:checked\+\&\]\:hover\:bg-gray-300:hover{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity))}@media (prefers-color-scheme: dark){input:checked+.dark\:\[input\:checked\+\&\]\:bg-custom-500{--tw-bg-opacity: 1;background-color:rgba(var(--c-500),var(--tw-bg-opacity))}input:checked+.dark\:\[input\:checked\+\&\]\:bg-gray-600{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity))}input:checked+.dark\:\[input\:checked\+\&\]\:hover\:bg-custom-400:hover{--tw-bg-opacity: 1;background-color:rgba(var(--c-400),var(--tw-bg-opacity))}input:checked+.dark\:\[input\:checked\+\&\]\:hover\:bg-gray-500:hover{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity))}}input:checked:focus-visible+.\[input\:checked\:focus-visible\+\&\]\:ring-custom-500\/50{--tw-ring-color: rgba(var(--c-500), .5)}@media (prefers-color-scheme: dark){input:checked:focus-visible+.dark\:\[input\:checked\:focus-visible\+\&\]\:ring-custom-400\/50{--tw-ring-color: rgba(var(--c-400), .5)}}input:focus-visible+.\[input\:focus-visible\+\&\]\:z-10{z-index:10}input:focus-visible+.\[input\:focus-visible\+\&\]\:ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}input:focus-visible+.\[input\:focus-visible\+\&\]\:ring-gray-950\/10{--tw-ring-color: rgb(3 7 18 / .1)}@media (prefers-color-scheme: dark){input:focus-visible+.dark\:\[input\:focus-visible\+\&\]\:ring-white\/20{--tw-ring-color: rgb(255 255 255 / .2)}} diff --git a/public/build/assets/cachet-DCZQ8JcZ.js b/public/build/assets/cachet-DCZQ8JcZ.js deleted file mode 100644 index 8a73d659..00000000 --- a/public/build/assets/cachet-DCZQ8JcZ.js +++ /dev/null @@ -1,18 +0,0 @@ -var ye=!1,xe=!1,rt=[],we=-1;function yr(t){xr(t)}function xr(t){rt.includes(t)||rt.push(t),wr()}function Mi(t){let e=rt.indexOf(t);e!==-1&&e>we&&rt.splice(e,1)}function wr(){!xe&&!ye&&(ye=!0,queueMicrotask($r))}function $r(){ye=!1,xe=!0;for(let t=0;tt.effect(e,{scheduler:i=>{$e?yr(i):i()}}),Li=t.raw}function ui(t){ft=t}function Sr(t){let e=()=>{};return[n=>{let r=ft(n);return t._x_effects||(t._x_effects=new Set,t._x_runEffects=()=>{t._x_effects.forEach(s=>s())}),t._x_effects.add(r),e=()=>{r!==void 0&&(t._x_effects.delete(r),gt(r))},r},()=>{e()}]}function Bi(t,e){let i=!0,n,r=ft(()=>{let s=t();JSON.stringify(s),i?n=s:queueMicrotask(()=>{e(s,n),n=s}),i=!1});return()=>gt(r)}var ji=[],Vi=[],Wi=[];function Tr(t){Wi.push(t)}function Be(t,e){typeof e=="function"?(t._x_cleanups||(t._x_cleanups=[]),t._x_cleanups.push(e)):(e=t,Vi.push(e))}function qi(t){ji.push(t)}function Hi(t,e,i){t._x_attributeCleanups||(t._x_attributeCleanups={}),t._x_attributeCleanups[e]||(t._x_attributeCleanups[e]=[]),t._x_attributeCleanups[e].push(i)}function zi(t,e){t._x_attributeCleanups&&Object.entries(t._x_attributeCleanups).forEach(([i,n])=>{(e===void 0||e.includes(i))&&(n.forEach(r=>r()),delete t._x_attributeCleanups[i])})}function Cr(t){if(t._x_cleanups)for(;t._x_cleanups.length;)t._x_cleanups.pop()()}var je=new MutationObserver(He),Ve=!1;function We(){je.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),Ve=!0}function Ui(){kr(),je.disconnect(),Ve=!1}var xt=[];function kr(){let t=je.takeRecords();xt.push(()=>t.length>0&&He(t));let e=xt.length;queueMicrotask(()=>{if(xt.length===e)for(;xt.length>0;)xt.shift()()})}function k(t){if(!Ve)return t();Ui();let e=t();return We(),e}var qe=!1,Ut=[];function Ir(){qe=!0}function Dr(){qe=!1,He(Ut),Ut=[]}function He(t){if(qe){Ut=Ut.concat(t);return}let e=new Set,i=new Set,n=new Map,r=new Map;for(let s=0;sa.nodeType===1&&e.add(a)),t[s].removedNodes.forEach(a=>a.nodeType===1&&i.add(a))),t[s].type==="attributes")){let a=t[s].target,o=t[s].attributeName,l=t[s].oldValue,u=()=>{n.has(a)||n.set(a,[]),n.get(a).push({name:o,value:a.getAttribute(o)})},c=()=>{r.has(a)||r.set(a,[]),r.get(a).push(o)};a.hasAttribute(o)&&l===null?u():a.hasAttribute(o)?(c(),u()):c()}r.forEach((s,a)=>{zi(a,s)}),n.forEach((s,a)=>{ji.forEach(o=>o(a,s))});for(let s of i)e.has(s)||Vi.forEach(a=>a(s));e.forEach(s=>{s._x_ignoreSelf=!0,s._x_ignore=!0});for(let s of e)i.has(s)||s.isConnected&&(delete s._x_ignoreSelf,delete s._x_ignore,Wi.forEach(a=>a(s)),s._x_ignore=!0,s._x_ignoreSelf=!0);e.forEach(s=>{delete s._x_ignoreSelf,delete s._x_ignore}),e=null,i=null,n=null,r=null}function Gi(t){return Dt(ht(t))}function It(t,e,i){return t._x_dataStack=[e,...ht(i||t)],()=>{t._x_dataStack=t._x_dataStack.filter(n=>n!==e)}}function ht(t){return t._x_dataStack?t._x_dataStack:typeof ShadowRoot=="function"&&t instanceof ShadowRoot?ht(t.host):t.parentNode?ht(t.parentNode):[]}function Dt(t){return new Proxy({objects:t},Nr)}var Nr={ownKeys({objects:t}){return Array.from(new Set(t.flatMap(e=>Object.keys(e))))},has({objects:t},e){return e==Symbol.unscopables?!1:t.some(i=>Object.prototype.hasOwnProperty.call(i,e)||Reflect.has(i,e))},get({objects:t},e,i){return e=="toJSON"?Rr:Reflect.get(t.find(n=>Reflect.has(n,e))||{},e,i)},set({objects:t},e,i,n){const r=t.find(a=>Object.prototype.hasOwnProperty.call(a,e))||t[t.length-1],s=Object.getOwnPropertyDescriptor(r,e);return s!=null&&s.set&&(s!=null&&s.get)?s.set.call(n,i)||!0:Reflect.set(r,e,i)}};function Rr(){return Reflect.ownKeys(this).reduce((e,i)=>(e[i]=Reflect.get(this,i),e),{})}function Qi(t){let e=n=>typeof n=="object"&&!Array.isArray(n)&&n!==null,i=(n,r="")=>{Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(([s,{value:a,enumerable:o}])=>{if(o===!1||a===void 0||typeof a=="object"&&a!==null&&a.__v_skip)return;let l=r===""?s:`${r}.${s}`;typeof a=="object"&&a!==null&&a._x_interceptor?n[s]=a.initialize(t,l,s):e(a)&&a!==n&&!(a instanceof Element)&&i(a,l)})};return i(t)}function Ji(t,e=()=>{}){let i={initialValue:void 0,_x_interceptor:!0,initialize(n,r,s){return t(this.initialValue,()=>Pr(n,r),a=>Ee(n,r,a),r,s)}};return e(i),n=>{if(typeof n=="object"&&n!==null&&n._x_interceptor){let r=i.initialize.bind(i);i.initialize=(s,a,o)=>{let l=n.initialize(s,a,o);return i.initialValue=l,r(s,a,o)}}else i.initialValue=n;return i}}function Pr(t,e){return e.split(".").reduce((i,n)=>i[n],t)}function Ee(t,e,i){if(typeof e=="string"&&(e=e.split(".")),e.length===1)t[e[0]]=i;else{if(e.length===0)throw error;return t[e[0]]||(t[e[0]]={}),Ee(t[e[0]],e.slice(1),i)}}var Yi={};function L(t,e){Yi[t]=e}function Oe(t,e){return Object.entries(Yi).forEach(([i,n])=>{let r=null;function s(){if(r)return r;{let[a,o]=rn(e);return r={interceptor:Ji,...a},Be(e,o),r}}Object.defineProperty(t,`$${i}`,{get(){return n(e,s())},enumerable:!1})}),t}function Fr(t,e,i,...n){try{return i(...n)}catch(r){Ct(r,t,e)}}function Ct(t,e,i=void 0){t=Object.assign(t??{message:"No error message given."},{el:e,expression:i}),console.warn(`Alpine Expression Error: ${t.message} - -${i?'Expression: "'+i+`" - -`:""}`,e),setTimeout(()=>{throw t},0)}var qt=!0;function Xi(t){let e=qt;qt=!1;let i=t();return qt=e,i}function st(t,e,i={}){let n;return N(t,e)(r=>n=r,i),n}function N(...t){return Zi(...t)}var Zi=tn;function Ar(t){Zi=t}function tn(t,e){let i={};Oe(i,t);let n=[i,...ht(t)],r=typeof e=="function"?Kr(n,e):Lr(n,e,t);return Fr.bind(null,t,e,r)}function Kr(t,e){return(i=()=>{},{scope:n={},params:r=[]}={})=>{let s=e.apply(Dt([n,...t]),r);Gt(i,s)}}var pe={};function Mr(t,e){if(pe[t])return pe[t];let i=Object.getPrototypeOf(async function(){}).constructor,n=/^[\n\s]*if.*\(.*\)/.test(t.trim())||/^(let|const)\s/.test(t.trim())?`(async()=>{ ${t} })()`:t,s=(()=>{try{let a=new i(["__self","scope"],`with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`);return Object.defineProperty(a,"name",{value:`[Alpine] ${t}`}),a}catch(a){return Ct(a,e,t),Promise.resolve()}})();return pe[t]=s,s}function Lr(t,e,i){let n=Mr(e,i);return(r=()=>{},{scope:s={},params:a=[]}={})=>{n.result=void 0,n.finished=!1;let o=Dt([s,...t]);if(typeof n=="function"){let l=n(n,o).catch(u=>Ct(u,i,e));n.finished?(Gt(r,n.result,o,a,i),n.result=void 0):l.then(u=>{Gt(r,u,o,a,i)}).catch(u=>Ct(u,i,e)).finally(()=>n.result=void 0)}}}function Gt(t,e,i,n,r){if(qt&&typeof e=="function"){let s=e.apply(i,n);s instanceof Promise?s.then(a=>Gt(t,a,i,n)).catch(a=>Ct(a,r,e)):t(s)}else typeof e=="object"&&e instanceof Promise?e.then(s=>t(s)):t(e)}var ze="x-";function mt(t=""){return ze+t}function Br(t){ze=t}var Qt={};function C(t,e){return Qt[t]=e,{before(i){if(!Qt[i]){console.warn(String.raw`Cannot find directive \`${i}\`. \`${t}\` will use the default order of execution`);return}const n=nt.indexOf(i);nt.splice(n>=0?n:nt.indexOf("DEFAULT"),0,t)}}}function jr(t){return Object.keys(Qt).includes(t)}function Ue(t,e,i){if(e=Array.from(e),t._x_virtualDirectives){let s=Object.entries(t._x_virtualDirectives).map(([o,l])=>({name:o,value:l})),a=en(s);s=s.map(o=>a.find(l=>l.name===o.name)?{name:`x-bind:${o.name}`,value:`"${o.value}"`}:o),e=e.concat(s)}let n={};return e.map(on((s,a)=>n[s]=a)).filter(un).map(qr(n,i)).sort(Hr).map(s=>Wr(t,s))}function en(t){return Array.from(t).map(on()).filter(e=>!un(e))}var Se=!1,Ot=new Map,nn=Symbol();function Vr(t){Se=!0;let e=Symbol();nn=e,Ot.set(e,[]);let i=()=>{for(;Ot.get(e).length;)Ot.get(e).shift()();Ot.delete(e)},n=()=>{Se=!1,i()};t(i),n()}function rn(t){let e=[],i=o=>e.push(o),[n,r]=Sr(t);return e.push(r),[{Alpine:Rt,effect:n,cleanup:i,evaluateLater:N.bind(N,t),evaluate:st.bind(st,t)},()=>e.forEach(o=>o())]}function Wr(t,e){let i=()=>{},n=Qt[e.type]||i,[r,s]=rn(t);Hi(t,e.original,s);let a=()=>{t._x_ignore||t._x_ignoreSelf||(n.inline&&n.inline(t,e,r),n=n.bind(n,t,e,r),Se?Ot.get(nn).push(n):n())};return a.runCleanups=s,a}var sn=(t,e)=>({name:i,value:n})=>(i.startsWith(t)&&(i=i.replace(t,e)),{name:i,value:n}),an=t=>t;function on(t=()=>{}){return({name:e,value:i})=>{let{name:n,value:r}=ln.reduce((s,a)=>a(s),{name:e,value:i});return n!==e&&t(n,e),{name:n,value:r}}}var ln=[];function Ge(t){ln.push(t)}function un({name:t}){return cn().test(t)}var cn=()=>new RegExp(`^${ze}([^:^.]+)\\b`);function qr(t,e){return({name:i,value:n})=>{let r=i.match(cn()),s=i.match(/:([a-zA-Z0-9\-_:]+)/),a=i.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],o=e||t[i]||i;return{type:r?r[1]:null,value:s?s[1]:null,modifiers:a.map(l=>l.replace(".","")),expression:n,original:o}}}var Te="DEFAULT",nt=["ignore","ref","data","id","anchor","bind","init","for","model","modelable","transition","show","if",Te,"teleport"];function Hr(t,e){let i=nt.indexOf(t.type)===-1?Te:t.type,n=nt.indexOf(e.type)===-1?Te:e.type;return nt.indexOf(i)-nt.indexOf(n)}function St(t,e,i={}){t.dispatchEvent(new CustomEvent(e,{detail:i,bubbles:!0,composed:!0,cancelable:!0}))}function J(t,e){if(typeof ShadowRoot=="function"&&t instanceof ShadowRoot){Array.from(t.children).forEach(r=>J(r,e));return}let i=!1;if(e(t,()=>i=!0),i)return;let n=t.firstElementChild;for(;n;)J(n,e),n=n.nextElementSibling}function P(t,...e){console.warn(`Alpine Warning: ${t}`,...e)}var ci=!1;function zr(){ci&&P("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),ci=!0,document.body||P("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` diff --git a/src/CachetCoreServiceProvider.php b/src/CachetCoreServiceProvider.php index e60aae31..3c314e03 100644 --- a/src/CachetCoreServiceProvider.php +++ b/src/CachetCoreServiceProvider.php @@ -151,6 +151,7 @@ private function registerCommands(): void { if ($this->app->runningInConsole()) { $this->commands([ + Commands\InstallCommand::class, Commands\SendBeaconCommand::class, Commands\VersionCommand::class, ]); diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php new file mode 100644 index 00000000..021dc146 --- /dev/null +++ b/src/Commands/InstallCommand.php @@ -0,0 +1,59 @@ +seconds(); + + if (confirm('Do you want to configure Cachet before installing?', true)) { + info('Configuring Cachet...'); + $this->configureDatabaseSettings($settings); + } + + info('Installing Cachet...'); + + info('Cachet has been installed successfully!'); + + return Command::SUCCESS; + } + + protected function configureDatabaseSettings(AppSettings $settings): void + { + collect( + (new ReflectionClass($settings))->getProperties(ReflectionProperty::IS_PUBLIC) + ) + ->filter(fn (ReflectionProperty $property) => array_key_exists($property->getName(), $settings->installable()) ) + ->each(function (ReflectionProperty $property) use ($settings) { + $description = $property->getAttributes(Description::class)[0]->getArguments()[0]; + $value = match($property->getType()?->getName()) { + 'bool' => confirm($description ?? $property->getName()), + default => text($description ?? $property->getName(), default: $property->getDefaultValue() ?? '', required: true), + }; + + $settings->{$property->getName()} = $value; + }) + ->pluck('name'); + + $settings->save(); + } +} \ No newline at end of file diff --git a/src/Settings/AppSettings.php b/src/Settings/AppSettings.php index 805264b8..baa58e58 100644 --- a/src/Settings/AppSettings.php +++ b/src/Settings/AppSettings.php @@ -2,34 +2,54 @@ namespace Cachet\Settings; +use Cachet\Settings\Attributes\Description; +use Illuminate\Support\Arr; use Spatie\LaravelSettings\Settings; class AppSettings extends Settings { + #[Description('The unique install ID of the application for telemetry.')] public string $install_id; + #[Description('What is the name of your application?')] public ?string $name = 'Cachet'; + #[Description('What is your application about?')] public ?string $about; + #[Description('Do you want to show support for Cachet?')] public bool $show_support = true; + #[Description('What timezone is is the application located in?')] public string $timezone = 'UTC'; + #[Description('Would you like to show your timezone on the status page?')] public bool $show_timezone = false; + #[Description('Would you like to only show the days with disruption?')] public bool $only_disrupted_days = false; + #[Description('How many incident days should be shown in the timeline?')] public int $incident_days = 7; + #[Description('After how many seconds should the status page automatically refresh?')] public ?int $refresh_rate; + #[Description('Should the dashboard login link be shown?')] public bool $dashboard_login_link = true; + #[Description('Major outage threshold %')] public int $major_outage_threshold = 25; public static function group(): string { return 'app'; } + + public function installable(): array + { + return Arr::except(get_class_vars(__CLASS__), [ + 'install_id', + ]); + } } diff --git a/src/Settings/Attributes/Description.php b/src/Settings/Attributes/Description.php new file mode 100644 index 00000000..d4199ae2 --- /dev/null +++ b/src/Settings/Attributes/Description.php @@ -0,0 +1,11 @@ +artisan('cachet:install') + ->expectsOutputToContain('Welcome to the Cachet installer!') + ->expectsConfirmation('Do you want to configure Cachet before installing?', 'no') + ->expectsOutputToContain('Installing Cachet...') + ->expectsOutputToContain('Cachet has been installed successfully!') + ->assertSuccessful(); +}); + +it('updates app settings when configuration is passed', function () { + $this->artisan('cachet:install') + ->expectsOutputToContain('Welcome to the Cachet installer!') + ->expectsConfirmation('Do you want to configure Cachet before installing?', 'yes') + ->expectsOutputToContain('Configuring Cachet...') + ->expectsQuestion('What is the name of your application?', 'Laravel Envoyer') + ->expectsQuestion('What is your application about?', 'Zero downtime deployment tool.') + ->expectsConfirmation('Do you want to show support for Cachet?', 'yes') + ->expectsQuestion('What timezone is is the application located in?', 'America/New_York') + ->expectsConfirmation('Would you like to show your timezone on the status page?', 'yes') + ->expectsConfirmation('Would you like to only show the days with disruption?', 'yes') + ->expectsQuestion('How many incident days should be shown in the timeline?', 14) + ->expectsQuestion('After how many seconds should the status page automatically refresh?', 10) + ->expectsConfirmation('Should the dashboard login link be shown?', 'no') + ->expectsQuestion('Major outage threshold %', 50) + ->expectsOutputToContain('Installing Cachet...') + ->expectsOutputToContain('Cachet has been installed successfully!') + ->assertSuccessful(); + + $settings = app(AppSettings::class); + expect($settings->name)->toBe('Laravel Envoyer') + ->and($settings->about)->toBe('Zero downtime deployment tool.') + ->and($settings->show_support)->toBeTrue() + ->and($settings->timezone)->toBe('America/New_York') + ->and($settings->show_timezone)->toBeTrue() + ->and($settings->only_disrupted_days)->toBeTrue() + ->and($settings->incident_days)->toBe(14) + ->and($settings->refresh_rate)->toBe(10) + ->and($settings->dashboard_login_link)->toBeFalse() + ->and($settings->major_outage_threshold)->toBe(50); +}); \ No newline at end of file From c21b990d964af9df98e35132445939ec6f5da080 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Mon, 14 Oct 2024 19:18:11 +0100 Subject: [PATCH 04/35] Minor tweaks --- src/Commands/InstallCommand.php | 5 +++-- tests/Unit/Commands/InstallCommandTest.php | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index 021dc146..928f02af 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -10,6 +10,7 @@ use ReflectionProperty; use function Laravel\Prompts\confirm; use function Laravel\Prompts\info; +use function Laravel\Prompts\intro; use function Laravel\Prompts\text; @@ -21,7 +22,7 @@ class InstallCommand extends Command public function handle(AppSettings $settings) { - info('Welcome to the Cachet installer!'); + intro('Welcome to the Cachet installer!'); Sleep::for(2)->seconds(); @@ -32,7 +33,7 @@ public function handle(AppSettings $settings) info('Installing Cachet...'); - info('Cachet has been installed successfully!'); + info('Cachet is installed ⚡'); return Command::SUCCESS; } diff --git a/tests/Unit/Commands/InstallCommandTest.php b/tests/Unit/Commands/InstallCommandTest.php index 8abe6591..7b01861c 100644 --- a/tests/Unit/Commands/InstallCommandTest.php +++ b/tests/Unit/Commands/InstallCommandTest.php @@ -3,16 +3,13 @@ namespace Tests\Unit\Commands; use Cachet\Settings\AppSettings; -use Illuminate\Foundation\Testing\RefreshDatabase; - -uses(RefreshDatabase::class); it('runs install command successfully without configuration', function () { $this->artisan('cachet:install') ->expectsOutputToContain('Welcome to the Cachet installer!') ->expectsConfirmation('Do you want to configure Cachet before installing?', 'no') ->expectsOutputToContain('Installing Cachet...') - ->expectsOutputToContain('Cachet has been installed successfully!') + ->expectsOutputToContain('Cachet is installed ⚡') ->assertSuccessful(); }); @@ -32,7 +29,7 @@ ->expectsConfirmation('Should the dashboard login link be shown?', 'no') ->expectsQuestion('Major outage threshold %', 50) ->expectsOutputToContain('Installing Cachet...') - ->expectsOutputToContain('Cachet has been installed successfully!') + ->expectsOutputToContain('Cachet is installed ⚡') ->assertSuccessful(); $settings = app(AppSettings::class); From 299e1d9c468bb100fd47b6f9d15b95917254354a Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Mon, 14 Oct 2024 19:20:20 +0100 Subject: [PATCH 05/35] Add config item ready to set up configuring own database connection for cachet if required --- config/cachet.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/config/cachet.php b/config/cachet.php index bd1dc9de..8372b302 100644 --- a/config/cachet.php +++ b/config/cachet.php @@ -107,4 +107,14 @@ | */ 'docker' => env('CACHET_DOCKER', false), + + /* + |-------------------------------------------------------------------------- + | Cachet Database Connection + |-------------------------------------------------------------------------- + | + | Support using an alternative database connection, defaults to default connecton of application + | + */ + 'database_connection' => env('CACHET_DB_CONNECTION'), ]; From c32bc4fec0e0cb0be9b9a78377d42132e18f40a1 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Mon, 14 Oct 2024 19:43:41 +0100 Subject: [PATCH 06/35] Move items out of testbech to install command itself. --- src/Commands/InstallCommand.php | 19 ++++++++++++++++--- testbench.yaml | 4 ---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index 928f02af..ac13ae16 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -2,6 +2,7 @@ namespace Cachet\Commands; +use Cachet\Database\Seeders\DatabaseSeeder; use Cachet\Settings\AppSettings; use Cachet\Settings\Attributes\Description; use Illuminate\Console\Command; @@ -28,17 +29,29 @@ public function handle(AppSettings $settings) if (confirm('Do you want to configure Cachet before installing?', true)) { info('Configuring Cachet...'); - $this->configureDatabaseSettings($settings); + $this->configureEnvironmentSettings(); + $settings = $this->configureDatabaseSettings($settings); } info('Installing Cachet...'); + $this->call('filament:assets'); + + $this->call('migrate', ['--seed' => true, '--seeder' => DatabaseSeeder::class]); + + $settings->save(); + info('Cachet is installed ⚡'); return Command::SUCCESS; } - protected function configureDatabaseSettings(AppSettings $settings): void + protected function configureEnvironmentSettings(): void + { + //@todo configure environment variables inside cachet.php + } + + protected function configureDatabaseSettings(AppSettings $settings): AppSettings { collect( (new ReflectionClass($settings))->getProperties(ReflectionProperty::IS_PUBLIC) @@ -55,6 +68,6 @@ protected function configureDatabaseSettings(AppSettings $settings): void }) ->pluck('name'); - $settings->save(); + return $settings; } } \ No newline at end of file diff --git a/testbench.yaml b/testbench.yaml index 560bc04d..463fd774 100644 --- a/testbench.yaml +++ b/testbench.yaml @@ -18,12 +18,8 @@ workbench: to: public/vendor/cachethq/cachet build: - asset-publish - - filament:assets - create-sqlite-db - db:wipe - - migrate:refresh: - --seed: true - --seeder: Cachet\Database\Seeders\DatabaseSeeder - cachet:install assets: - query-builder-config From 2fc0720fe4232a1b4cc9e9b88c4c2634e9dea923 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Mon, 14 Oct 2024 19:58:18 +0100 Subject: [PATCH 07/35] move migrations higher in chain at least for now --- src/Commands/InstallCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index ac13ae16..b4a32387 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -27,6 +27,8 @@ public function handle(AppSettings $settings) Sleep::for(2)->seconds(); + $this->call('migrate', ['--seed' => true, '--seeder' => DatabaseSeeder::class]); + if (confirm('Do you want to configure Cachet before installing?', true)) { info('Configuring Cachet...'); $this->configureEnvironmentSettings(); @@ -37,8 +39,6 @@ public function handle(AppSettings $settings) $this->call('filament:assets'); - $this->call('migrate', ['--seed' => true, '--seeder' => DatabaseSeeder::class]); - $settings->save(); info('Cachet is installed ⚡'); From a42fea20b48b6ebd035b435766eace4a4b6dd22c Mon Sep 17 00:00:00 2001 From: AlexJump24 Date: Mon, 14 Oct 2024 18:45:02 +0000 Subject: [PATCH 08/35] Compile Assets --- public/build/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/build/manifest.json b/public/build/manifest.json index 7d0e7a90..01ef8d59 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -15,4 +15,4 @@ "src": "resources/js/cachet.js", "isEntry": true } -} \ No newline at end of file +} From 760bd43fd4801f243602128ca4a9a8dfbbcf4982 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Tue, 15 Oct 2024 06:26:39 +0100 Subject: [PATCH 09/35] update --- .../2024_01_22_205110_create_default_settings.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/database/migrations/2024_01_22_205110_create_default_settings.php b/database/migrations/2024_01_22_205110_create_default_settings.php index 5f019700..b0e5c8f1 100644 --- a/database/migrations/2024_01_22_205110_create_default_settings.php +++ b/database/migrations/2024_01_22_205110_create_default_settings.php @@ -12,8 +12,9 @@ public function up(): void { // Cachet settings... rescue(fn () => $this->migrator->add('app.install_id', Str::random(40))); - rescue(fn () => $this->migrator->add('app.name', 'Cachet V3')); - rescue(fn () => $this->migrator->add('app.domain', <<<'ABOUT' + rescue(fn () => $this->migrator->add('app.name', 'Cachet')); + rescue(fn () => $this->migrator->add('app.domain')); + rescue(fn () => $this->migrator->add('app.about', <<<'ABOUT' Cachet is a **beautiful** and **powerful** open-source status page system. To access the [dashboard](/dashboard), use the following credentials: @@ -22,7 +23,6 @@ public function up(): void Please [consider sponsoring](https://github.com/cachethq/cachet?sponsor=1) the continued development of Cachet. ABOUT)); - rescue(fn () => $this->migrator->add('app.about')); rescue(fn () => $this->migrator->add('app.timezone', 'UTC')); rescue(fn () => $this->migrator->add('app.locale', 'en')); rescue(fn () => $this->migrator->add('app.incident_days', 7)); From c08c45062144ad798ec02db0514fff461e41e63e Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Wed, 16 Oct 2024 21:04:32 +0100 Subject: [PATCH 10/35] Write to env file if prompted for configuration --- config/cachet.php | 2 +- src/Commands/InstallCommand.php | 64 +++++++++++++++++++++- tests/Unit/Commands/InstallCommandTest.php | 21 ++++++- 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/config/cachet.php b/config/cachet.php index 8372b302..0abe4a85 100644 --- a/config/cachet.php +++ b/config/cachet.php @@ -116,5 +116,5 @@ | Support using an alternative database connection, defaults to default connecton of application | */ - 'database_connection' => env('CACHET_DB_CONNECTION'), + 'database_connection' => env('CACHET_DB_CONNECTION', 'default'), ]; diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index b4a32387..a7229875 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -6,7 +6,9 @@ use Cachet\Settings\AppSettings; use Cachet\Settings\Attributes\Description; use Illuminate\Console\Command; +use Illuminate\Support\Facades\File; use Illuminate\Support\Sleep; +use Illuminate\Support\Str; use ReflectionClass; use ReflectionProperty; use function Laravel\Prompts\confirm; @@ -48,7 +50,67 @@ public function handle(AppSettings $settings) protected function configureEnvironmentSettings(): void { - //@todo configure environment variables inside cachet.php + $path = text( + 'Which path do you want Cachet to be accessible from?', + default: config('cachet.path') + ); + + $title = text( + 'What will the title of your status page be?', + default: config('cachet.title') + ); + + $connection = text( + 'Which database connection do you wish to use for Cachet?', + default: config('cachet.database_connection') + ); + + $beacon = confirm( + 'Do you wish to send anonymous data to cachet to help us understand how Cachet is used?', + default: config('cachet.beacon') + ); + + $this->writeEnv([ + 'CACHET_PATH' => $path, + 'CACHET_TITLE' => $title, + 'CACHET_DB_CONNECTION' => $connection, + 'CACHET_BEACON' => $beacon, + ]); + } + + protected function writeEnv(array $values): void + { + $environmentFile = app()->environmentFile(); + $environmentPath = app()->environmentPath(); + $fullPath = $environmentPath . '/' . $environmentFile; + + $envFileContents = File::lines($fullPath)->collect(); + + foreach ($values as $key => $value) { + $existingKey = $envFileContents->search(function ($line) use ($key) { + return stripos($line, $key) !== false; + }); + + if (Str::contains($value, ' ')) { + $value = Str::wrap($value,'"'); + } + + if ($value === true) { + $value = 'true'; + } + + if ($value === false) { + $value = 'false'; + } + + if ($existingKey === false) { + $envFileContents->push($key . '=' . $value); + } else { + $envFileContents->put($existingKey, $key . '=' . $value); + } + } + + File::put($fullPath, $envFileContents->implode("\n")); } protected function configureDatabaseSettings(AppSettings $settings): AppSettings diff --git a/tests/Unit/Commands/InstallCommandTest.php b/tests/Unit/Commands/InstallCommandTest.php index 7b01861c..7ca4894c 100644 --- a/tests/Unit/Commands/InstallCommandTest.php +++ b/tests/Unit/Commands/InstallCommandTest.php @@ -3,6 +3,7 @@ namespace Tests\Unit\Commands; use Cachet\Settings\AppSettings; +use Illuminate\Support\Facades\File; it('runs install command successfully without configuration', function () { $this->artisan('cachet:install') @@ -13,11 +14,17 @@ ->assertSuccessful(); }); -it('updates app settings when configuration is passed', function () { +it('updates app settings and config filewhen configuration is passed', function () { + File::copy(base_path('.env.example'), base_path('.env')); + $this->artisan('cachet:install') ->expectsOutputToContain('Welcome to the Cachet installer!') ->expectsConfirmation('Do you want to configure Cachet before installing?', 'yes') ->expectsOutputToContain('Configuring Cachet...') + ->expectsQuestion('Which path do you want Cachet to be accessible from?', '/status') + ->expectsQuestion('What will the title of your status page be?', 'Laravel Envoyer') + ->expectsQuestion('Which database connection do you wish to use for Cachet?', 'default') + ->expectsQuestion('Do you wish to send anonymous data to cachet to help us understand how Cachet is used?', true) ->expectsQuestion('What is the name of your application?', 'Laravel Envoyer') ->expectsQuestion('What is your application about?', 'Zero downtime deployment tool.') ->expectsConfirmation('Do you want to show support for Cachet?', 'yes') @@ -32,6 +39,14 @@ ->expectsOutputToContain('Cachet is installed ⚡') ->assertSuccessful(); + $envContent = file_get_contents(base_path('.env')); + + expect($envContent) + ->toContain('CACHET_PATH=/status') + ->toContain('CACHET_TITLE="Laravel Envoyer"') + ->toContain('CACHET_DB_CONNECTION=default') + ->toContain('CACHET_BEACON=true'); + $settings = app(AppSettings::class); expect($settings->name)->toBe('Laravel Envoyer') ->and($settings->about)->toBe('Zero downtime deployment tool.') @@ -43,4 +58,8 @@ ->and($settings->refresh_rate)->toBe(10) ->and($settings->dashboard_login_link)->toBeFalse() ->and($settings->major_outage_threshold)->toBe(50); +}); + +afterAll(function() { + File::delete(base_path('.env')); }); \ No newline at end of file From a3aeab4e6caa82dda20a2471e43f67307be40839 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Thu, 17 Oct 2024 09:40:05 +0100 Subject: [PATCH 11/35] tidy up --- src/Commands/InstallCommand.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index a7229875..10752e52 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -88,20 +88,15 @@ protected function writeEnv(array $values): void foreach ($values as $key => $value) { $existingKey = $envFileContents->search(function ($line) use ($key) { - return stripos($line, $key) !== false; + return Str::contains($line, $key, true); }); - if (Str::contains($value, ' ')) { - $value = Str::wrap($value,'"'); - } - - if ($value === true) { - $value = 'true'; - } - - if ($value === false) { - $value = 'false'; - } + $value = match (true) { + Str::contains($value, ' ') => Str::wrap($value,'"'), + $value === true => 'true', + $value === false => 'false', + default => $value + }; if ($existingKey === false) { $envFileContents->push($key . '=' . $value); From 44c3478b9f6ce8c888f2f5c5fb1e3fe55b07d179 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Thu, 17 Oct 2024 15:22:57 +0100 Subject: [PATCH 12/35] changes from feedback --- src/Commands/InstallCommand.php | 19 ++++++++++++++++--- src/Settings/AppSettings.php | 4 ++-- src/Settings/Attributes/Description.php | 12 +++++++++++- tests/Unit/Commands/InstallCommandTest.php | 4 ---- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index 10752e52..e543fc5f 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -115,10 +115,23 @@ protected function configureDatabaseSettings(AppSettings $settings): AppSettings ) ->filter(fn (ReflectionProperty $property) => array_key_exists($property->getName(), $settings->installable()) ) ->each(function (ReflectionProperty $property) use ($settings) { - $description = $property->getAttributes(Description::class)[0]->getArguments()[0]; + $descriptionAttribute = $property->getAttributes(Description::class); + + if (empty($descriptionAttribute)) { + return; + } + + $descriptionAttributeClass = $descriptionAttribute[0]->newInstance(); + $default = $descriptionAttributeClass->getDefault(); + $required = $descriptionAttributeClass->getRequired(); + + if ($required === false) { + return; + } + $value = match($property->getType()?->getName()) { - 'bool' => confirm($description ?? $property->getName()), - default => text($description ?? $property->getName(), default: $property->getDefaultValue() ?? '', required: true), + 'bool' => confirm($default ?? $property->getName()), + default => text($default ?? $property->getName(), default: $property->getDefaultValue() ?? '', required: true), }; $settings->{$property->getName()} = $value; diff --git a/src/Settings/AppSettings.php b/src/Settings/AppSettings.php index baa58e58..13d3d6f2 100644 --- a/src/Settings/AppSettings.php +++ b/src/Settings/AppSettings.php @@ -14,7 +14,7 @@ class AppSettings extends Settings #[Description('What is the name of your application?')] public ?string $name = 'Cachet'; - #[Description('What is your application about?')] + #[Description('What is your application about?', required: false)] public ?string $about; #[Description('Do you want to show support for Cachet?')] @@ -32,7 +32,7 @@ class AppSettings extends Settings #[Description('How many incident days should be shown in the timeline?')] public int $incident_days = 7; - #[Description('After how many seconds should the status page automatically refresh?')] + #[Description('After how many seconds should the status page automatically refresh?', required: false)] public ?int $refresh_rate; #[Description('Should the dashboard login link be shown?')] diff --git a/src/Settings/Attributes/Description.php b/src/Settings/Attributes/Description.php index d4199ae2..6f9ad157 100644 --- a/src/Settings/Attributes/Description.php +++ b/src/Settings/Attributes/Description.php @@ -7,5 +7,15 @@ #[Attribute(Attribute::TARGET_PROPERTY)] final class Description { - public function __construct(private string $description) {} + public function __construct(private readonly string $default, private readonly bool $required = true) {} + + public function getDefault(): string + { + return $this->default; + } + + public function getRequired(): bool + { + return $this->required; + } } \ No newline at end of file diff --git a/tests/Unit/Commands/InstallCommandTest.php b/tests/Unit/Commands/InstallCommandTest.php index 7ca4894c..91a354a2 100644 --- a/tests/Unit/Commands/InstallCommandTest.php +++ b/tests/Unit/Commands/InstallCommandTest.php @@ -26,13 +26,11 @@ ->expectsQuestion('Which database connection do you wish to use for Cachet?', 'default') ->expectsQuestion('Do you wish to send anonymous data to cachet to help us understand how Cachet is used?', true) ->expectsQuestion('What is the name of your application?', 'Laravel Envoyer') - ->expectsQuestion('What is your application about?', 'Zero downtime deployment tool.') ->expectsConfirmation('Do you want to show support for Cachet?', 'yes') ->expectsQuestion('What timezone is is the application located in?', 'America/New_York') ->expectsConfirmation('Would you like to show your timezone on the status page?', 'yes') ->expectsConfirmation('Would you like to only show the days with disruption?', 'yes') ->expectsQuestion('How many incident days should be shown in the timeline?', 14) - ->expectsQuestion('After how many seconds should the status page automatically refresh?', 10) ->expectsConfirmation('Should the dashboard login link be shown?', 'no') ->expectsQuestion('Major outage threshold %', 50) ->expectsOutputToContain('Installing Cachet...') @@ -49,13 +47,11 @@ $settings = app(AppSettings::class); expect($settings->name)->toBe('Laravel Envoyer') - ->and($settings->about)->toBe('Zero downtime deployment tool.') ->and($settings->show_support)->toBeTrue() ->and($settings->timezone)->toBe('America/New_York') ->and($settings->show_timezone)->toBeTrue() ->and($settings->only_disrupted_days)->toBeTrue() ->and($settings->incident_days)->toBe(14) - ->and($settings->refresh_rate)->toBe(10) ->and($settings->dashboard_login_link)->toBeFalse() ->and($settings->major_outage_threshold)->toBe(50); }); From 6b153a1fc666edd5f565bb87f3cdf724e3fb7e2b Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Thu, 17 Oct 2024 15:52:47 +0100 Subject: [PATCH 13/35] fix pipeline issue --- src/Commands/InstallCommand.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index e543fc5f..9c4030ab 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -9,6 +9,7 @@ use Illuminate\Support\Facades\File; use Illuminate\Support\Sleep; use Illuminate\Support\Str; +use Laravel\Prompts\Prompt; use ReflectionClass; use ReflectionProperty; use function Laravel\Prompts\confirm; @@ -25,6 +26,8 @@ class InstallCommand extends Command public function handle(AppSettings $settings) { + Prompt::fallbackWhen(!$this->input->isInteractive()); + intro('Welcome to the Cachet installer!'); Sleep::for(2)->seconds(); From 0305ec8ba8604a7340ba05400c4ddc075eb472d5 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Fri, 18 Oct 2024 07:42:56 +0100 Subject: [PATCH 14/35] Pipeline before passed oddly so not needed --- src/Commands/InstallCommand.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index 9c4030ab..d980d9d2 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -26,8 +26,6 @@ class InstallCommand extends Command public function handle(AppSettings $settings) { - Prompt::fallbackWhen(!$this->input->isInteractive()); - intro('Welcome to the Cachet installer!'); Sleep::for(2)->seconds(); From 44bb28264b54b43af7a725d25b889f68f2241f64 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Fri, 18 Oct 2024 14:57:16 +0100 Subject: [PATCH 15/35] feedback + tidy up --- src/Commands/InstallCommand.php | 7 +++---- src/Settings/Attributes/Description.php | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index d980d9d2..a64a01e4 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -9,7 +9,6 @@ use Illuminate\Support\Facades\File; use Illuminate\Support\Sleep; use Illuminate\Support\Str; -use Laravel\Prompts\Prompt; use ReflectionClass; use ReflectionProperty; use function Laravel\Prompts\confirm; @@ -24,7 +23,7 @@ class InstallCommand extends Command protected $description = 'Install Cachet'; - public function handle(AppSettings $settings) + public function handle(AppSettings $settings): int { intro('Welcome to the Cachet installer!'); @@ -123,8 +122,8 @@ protected function configureDatabaseSettings(AppSettings $settings): AppSettings } $descriptionAttributeClass = $descriptionAttribute[0]->newInstance(); - $default = $descriptionAttributeClass->getDefault(); - $required = $descriptionAttributeClass->getRequired(); + $default = $descriptionAttributeClass->default(); + $required = $descriptionAttributeClass->required(); if ($required === false) { return; diff --git a/src/Settings/Attributes/Description.php b/src/Settings/Attributes/Description.php index 6f9ad157..39134bca 100644 --- a/src/Settings/Attributes/Description.php +++ b/src/Settings/Attributes/Description.php @@ -9,12 +9,12 @@ final class Description { public function __construct(private readonly string $default, private readonly bool $required = true) {} - public function getDefault(): string + public function default(): string { return $this->default; } - public function getRequired(): bool + public function required(): bool { return $this->required; } From f001b7ae1e06c854b22078751d5db0c6b789ef6c Mon Sep 17 00:00:00 2001 From: AlexJump24 Date: Sun, 13 Oct 2024 18:48:00 +0000 Subject: [PATCH 16/35] Compile Assets --- public/build/assets/cachet-DCZQ8JcZ.js | 18 ------------------ public/build/assets/cachet.30cb9e62.css | 1 + public/build/assets/cachet.6122e927.js | 18 ++++++++++++++++++ public/build/assets/theme.8251fad4.css | 1 + 4 files changed, 20 insertions(+), 18 deletions(-) delete mode 100644 public/build/assets/cachet-DCZQ8JcZ.js create mode 100644 public/build/assets/cachet.30cb9e62.css create mode 100644 public/build/assets/cachet.6122e927.js create mode 100644 public/build/assets/theme.8251fad4.css diff --git a/public/build/assets/cachet-DCZQ8JcZ.js b/public/build/assets/cachet-DCZQ8JcZ.js deleted file mode 100644 index 8a73d659..00000000 --- a/public/build/assets/cachet-DCZQ8JcZ.js +++ /dev/null @@ -1,18 +0,0 @@ -var ye=!1,xe=!1,rt=[],we=-1;function yr(t){xr(t)}function xr(t){rt.includes(t)||rt.push(t),wr()}function Mi(t){let e=rt.indexOf(t);e!==-1&&e>we&&rt.splice(e,1)}function wr(){!xe&&!ye&&(ye=!0,queueMicrotask($r))}function $r(){ye=!1,xe=!0;for(let t=0;tt.effect(e,{scheduler:i=>{$e?yr(i):i()}}),Li=t.raw}function ui(t){ft=t}function Sr(t){let e=()=>{};return[n=>{let r=ft(n);return t._x_effects||(t._x_effects=new Set,t._x_runEffects=()=>{t._x_effects.forEach(s=>s())}),t._x_effects.add(r),e=()=>{r!==void 0&&(t._x_effects.delete(r),gt(r))},r},()=>{e()}]}function Bi(t,e){let i=!0,n,r=ft(()=>{let s=t();JSON.stringify(s),i?n=s:queueMicrotask(()=>{e(s,n),n=s}),i=!1});return()=>gt(r)}var ji=[],Vi=[],Wi=[];function Tr(t){Wi.push(t)}function Be(t,e){typeof e=="function"?(t._x_cleanups||(t._x_cleanups=[]),t._x_cleanups.push(e)):(e=t,Vi.push(e))}function qi(t){ji.push(t)}function Hi(t,e,i){t._x_attributeCleanups||(t._x_attributeCleanups={}),t._x_attributeCleanups[e]||(t._x_attributeCleanups[e]=[]),t._x_attributeCleanups[e].push(i)}function zi(t,e){t._x_attributeCleanups&&Object.entries(t._x_attributeCleanups).forEach(([i,n])=>{(e===void 0||e.includes(i))&&(n.forEach(r=>r()),delete t._x_attributeCleanups[i])})}function Cr(t){if(t._x_cleanups)for(;t._x_cleanups.length;)t._x_cleanups.pop()()}var je=new MutationObserver(He),Ve=!1;function We(){je.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),Ve=!0}function Ui(){kr(),je.disconnect(),Ve=!1}var xt=[];function kr(){let t=je.takeRecords();xt.push(()=>t.length>0&&He(t));let e=xt.length;queueMicrotask(()=>{if(xt.length===e)for(;xt.length>0;)xt.shift()()})}function k(t){if(!Ve)return t();Ui();let e=t();return We(),e}var qe=!1,Ut=[];function Ir(){qe=!0}function Dr(){qe=!1,He(Ut),Ut=[]}function He(t){if(qe){Ut=Ut.concat(t);return}let e=new Set,i=new Set,n=new Map,r=new Map;for(let s=0;sa.nodeType===1&&e.add(a)),t[s].removedNodes.forEach(a=>a.nodeType===1&&i.add(a))),t[s].type==="attributes")){let a=t[s].target,o=t[s].attributeName,l=t[s].oldValue,u=()=>{n.has(a)||n.set(a,[]),n.get(a).push({name:o,value:a.getAttribute(o)})},c=()=>{r.has(a)||r.set(a,[]),r.get(a).push(o)};a.hasAttribute(o)&&l===null?u():a.hasAttribute(o)?(c(),u()):c()}r.forEach((s,a)=>{zi(a,s)}),n.forEach((s,a)=>{ji.forEach(o=>o(a,s))});for(let s of i)e.has(s)||Vi.forEach(a=>a(s));e.forEach(s=>{s._x_ignoreSelf=!0,s._x_ignore=!0});for(let s of e)i.has(s)||s.isConnected&&(delete s._x_ignoreSelf,delete s._x_ignore,Wi.forEach(a=>a(s)),s._x_ignore=!0,s._x_ignoreSelf=!0);e.forEach(s=>{delete s._x_ignoreSelf,delete s._x_ignore}),e=null,i=null,n=null,r=null}function Gi(t){return Dt(ht(t))}function It(t,e,i){return t._x_dataStack=[e,...ht(i||t)],()=>{t._x_dataStack=t._x_dataStack.filter(n=>n!==e)}}function ht(t){return t._x_dataStack?t._x_dataStack:typeof ShadowRoot=="function"&&t instanceof ShadowRoot?ht(t.host):t.parentNode?ht(t.parentNode):[]}function Dt(t){return new Proxy({objects:t},Nr)}var Nr={ownKeys({objects:t}){return Array.from(new Set(t.flatMap(e=>Object.keys(e))))},has({objects:t},e){return e==Symbol.unscopables?!1:t.some(i=>Object.prototype.hasOwnProperty.call(i,e)||Reflect.has(i,e))},get({objects:t},e,i){return e=="toJSON"?Rr:Reflect.get(t.find(n=>Reflect.has(n,e))||{},e,i)},set({objects:t},e,i,n){const r=t.find(a=>Object.prototype.hasOwnProperty.call(a,e))||t[t.length-1],s=Object.getOwnPropertyDescriptor(r,e);return s!=null&&s.set&&(s!=null&&s.get)?s.set.call(n,i)||!0:Reflect.set(r,e,i)}};function Rr(){return Reflect.ownKeys(this).reduce((e,i)=>(e[i]=Reflect.get(this,i),e),{})}function Qi(t){let e=n=>typeof n=="object"&&!Array.isArray(n)&&n!==null,i=(n,r="")=>{Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(([s,{value:a,enumerable:o}])=>{if(o===!1||a===void 0||typeof a=="object"&&a!==null&&a.__v_skip)return;let l=r===""?s:`${r}.${s}`;typeof a=="object"&&a!==null&&a._x_interceptor?n[s]=a.initialize(t,l,s):e(a)&&a!==n&&!(a instanceof Element)&&i(a,l)})};return i(t)}function Ji(t,e=()=>{}){let i={initialValue:void 0,_x_interceptor:!0,initialize(n,r,s){return t(this.initialValue,()=>Pr(n,r),a=>Ee(n,r,a),r,s)}};return e(i),n=>{if(typeof n=="object"&&n!==null&&n._x_interceptor){let r=i.initialize.bind(i);i.initialize=(s,a,o)=>{let l=n.initialize(s,a,o);return i.initialValue=l,r(s,a,o)}}else i.initialValue=n;return i}}function Pr(t,e){return e.split(".").reduce((i,n)=>i[n],t)}function Ee(t,e,i){if(typeof e=="string"&&(e=e.split(".")),e.length===1)t[e[0]]=i;else{if(e.length===0)throw error;return t[e[0]]||(t[e[0]]={}),Ee(t[e[0]],e.slice(1),i)}}var Yi={};function L(t,e){Yi[t]=e}function Oe(t,e){return Object.entries(Yi).forEach(([i,n])=>{let r=null;function s(){if(r)return r;{let[a,o]=rn(e);return r={interceptor:Ji,...a},Be(e,o),r}}Object.defineProperty(t,`$${i}`,{get(){return n(e,s())},enumerable:!1})}),t}function Fr(t,e,i,...n){try{return i(...n)}catch(r){Ct(r,t,e)}}function Ct(t,e,i=void 0){t=Object.assign(t??{message:"No error message given."},{el:e,expression:i}),console.warn(`Alpine Expression Error: ${t.message} - -${i?'Expression: "'+i+`" - -`:""}`,e),setTimeout(()=>{throw t},0)}var qt=!0;function Xi(t){let e=qt;qt=!1;let i=t();return qt=e,i}function st(t,e,i={}){let n;return N(t,e)(r=>n=r,i),n}function N(...t){return Zi(...t)}var Zi=tn;function Ar(t){Zi=t}function tn(t,e){let i={};Oe(i,t);let n=[i,...ht(t)],r=typeof e=="function"?Kr(n,e):Lr(n,e,t);return Fr.bind(null,t,e,r)}function Kr(t,e){return(i=()=>{},{scope:n={},params:r=[]}={})=>{let s=e.apply(Dt([n,...t]),r);Gt(i,s)}}var pe={};function Mr(t,e){if(pe[t])return pe[t];let i=Object.getPrototypeOf(async function(){}).constructor,n=/^[\n\s]*if.*\(.*\)/.test(t.trim())||/^(let|const)\s/.test(t.trim())?`(async()=>{ ${t} })()`:t,s=(()=>{try{let a=new i(["__self","scope"],`with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`);return Object.defineProperty(a,"name",{value:`[Alpine] ${t}`}),a}catch(a){return Ct(a,e,t),Promise.resolve()}})();return pe[t]=s,s}function Lr(t,e,i){let n=Mr(e,i);return(r=()=>{},{scope:s={},params:a=[]}={})=>{n.result=void 0,n.finished=!1;let o=Dt([s,...t]);if(typeof n=="function"){let l=n(n,o).catch(u=>Ct(u,i,e));n.finished?(Gt(r,n.result,o,a,i),n.result=void 0):l.then(u=>{Gt(r,u,o,a,i)}).catch(u=>Ct(u,i,e)).finally(()=>n.result=void 0)}}}function Gt(t,e,i,n,r){if(qt&&typeof e=="function"){let s=e.apply(i,n);s instanceof Promise?s.then(a=>Gt(t,a,i,n)).catch(a=>Ct(a,r,e)):t(s)}else typeof e=="object"&&e instanceof Promise?e.then(s=>t(s)):t(e)}var ze="x-";function mt(t=""){return ze+t}function Br(t){ze=t}var Qt={};function C(t,e){return Qt[t]=e,{before(i){if(!Qt[i]){console.warn(String.raw`Cannot find directive \`${i}\`. \`${t}\` will use the default order of execution`);return}const n=nt.indexOf(i);nt.splice(n>=0?n:nt.indexOf("DEFAULT"),0,t)}}}function jr(t){return Object.keys(Qt).includes(t)}function Ue(t,e,i){if(e=Array.from(e),t._x_virtualDirectives){let s=Object.entries(t._x_virtualDirectives).map(([o,l])=>({name:o,value:l})),a=en(s);s=s.map(o=>a.find(l=>l.name===o.name)?{name:`x-bind:${o.name}`,value:`"${o.value}"`}:o),e=e.concat(s)}let n={};return e.map(on((s,a)=>n[s]=a)).filter(un).map(qr(n,i)).sort(Hr).map(s=>Wr(t,s))}function en(t){return Array.from(t).map(on()).filter(e=>!un(e))}var Se=!1,Ot=new Map,nn=Symbol();function Vr(t){Se=!0;let e=Symbol();nn=e,Ot.set(e,[]);let i=()=>{for(;Ot.get(e).length;)Ot.get(e).shift()();Ot.delete(e)},n=()=>{Se=!1,i()};t(i),n()}function rn(t){let e=[],i=o=>e.push(o),[n,r]=Sr(t);return e.push(r),[{Alpine:Rt,effect:n,cleanup:i,evaluateLater:N.bind(N,t),evaluate:st.bind(st,t)},()=>e.forEach(o=>o())]}function Wr(t,e){let i=()=>{},n=Qt[e.type]||i,[r,s]=rn(t);Hi(t,e.original,s);let a=()=>{t._x_ignore||t._x_ignoreSelf||(n.inline&&n.inline(t,e,r),n=n.bind(n,t,e,r),Se?Ot.get(nn).push(n):n())};return a.runCleanups=s,a}var sn=(t,e)=>({name:i,value:n})=>(i.startsWith(t)&&(i=i.replace(t,e)),{name:i,value:n}),an=t=>t;function on(t=()=>{}){return({name:e,value:i})=>{let{name:n,value:r}=ln.reduce((s,a)=>a(s),{name:e,value:i});return n!==e&&t(n,e),{name:n,value:r}}}var ln=[];function Ge(t){ln.push(t)}function un({name:t}){return cn().test(t)}var cn=()=>new RegExp(`^${ze}([^:^.]+)\\b`);function qr(t,e){return({name:i,value:n})=>{let r=i.match(cn()),s=i.match(/:([a-zA-Z0-9\-_:]+)/),a=i.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],o=e||t[i]||i;return{type:r?r[1]:null,value:s?s[1]:null,modifiers:a.map(l=>l.replace(".","")),expression:n,original:o}}}var Te="DEFAULT",nt=["ignore","ref","data","id","anchor","bind","init","for","model","modelable","transition","show","if",Te,"teleport"];function Hr(t,e){let i=nt.indexOf(t.type)===-1?Te:t.type,n=nt.indexOf(e.type)===-1?Te:e.type;return nt.indexOf(i)-nt.indexOf(n)}function St(t,e,i={}){t.dispatchEvent(new CustomEvent(e,{detail:i,bubbles:!0,composed:!0,cancelable:!0}))}function J(t,e){if(typeof ShadowRoot=="function"&&t instanceof ShadowRoot){Array.from(t.children).forEach(r=>J(r,e));return}let i=!1;if(e(t,()=>i=!0),i)return;let n=t.firstElementChild;for(;n;)J(n,e),n=n.nextElementSibling}function P(t,...e){console.warn(`Alpine Warning: ${t}`,...e)}var ci=!1;function zr(){ci&&P("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),ci=!0,document.body||P("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` diff --git a/src/CachetCoreServiceProvider.php b/src/CachetCoreServiceProvider.php index e60aae31..3c314e03 100644 --- a/src/CachetCoreServiceProvider.php +++ b/src/CachetCoreServiceProvider.php @@ -151,6 +151,7 @@ private function registerCommands(): void { if ($this->app->runningInConsole()) { $this->commands([ + Commands\InstallCommand::class, Commands\SendBeaconCommand::class, Commands\VersionCommand::class, ]); diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php new file mode 100644 index 00000000..021dc146 --- /dev/null +++ b/src/Commands/InstallCommand.php @@ -0,0 +1,59 @@ +seconds(); + + if (confirm('Do you want to configure Cachet before installing?', true)) { + info('Configuring Cachet...'); + $this->configureDatabaseSettings($settings); + } + + info('Installing Cachet...'); + + info('Cachet has been installed successfully!'); + + return Command::SUCCESS; + } + + protected function configureDatabaseSettings(AppSettings $settings): void + { + collect( + (new ReflectionClass($settings))->getProperties(ReflectionProperty::IS_PUBLIC) + ) + ->filter(fn (ReflectionProperty $property) => array_key_exists($property->getName(), $settings->installable()) ) + ->each(function (ReflectionProperty $property) use ($settings) { + $description = $property->getAttributes(Description::class)[0]->getArguments()[0]; + $value = match($property->getType()?->getName()) { + 'bool' => confirm($description ?? $property->getName()), + default => text($description ?? $property->getName(), default: $property->getDefaultValue() ?? '', required: true), + }; + + $settings->{$property->getName()} = $value; + }) + ->pluck('name'); + + $settings->save(); + } +} \ No newline at end of file diff --git a/src/Settings/AppSettings.php b/src/Settings/AppSettings.php index 805264b8..baa58e58 100644 --- a/src/Settings/AppSettings.php +++ b/src/Settings/AppSettings.php @@ -2,34 +2,54 @@ namespace Cachet\Settings; +use Cachet\Settings\Attributes\Description; +use Illuminate\Support\Arr; use Spatie\LaravelSettings\Settings; class AppSettings extends Settings { + #[Description('The unique install ID of the application for telemetry.')] public string $install_id; + #[Description('What is the name of your application?')] public ?string $name = 'Cachet'; + #[Description('What is your application about?')] public ?string $about; + #[Description('Do you want to show support for Cachet?')] public bool $show_support = true; + #[Description('What timezone is is the application located in?')] public string $timezone = 'UTC'; + #[Description('Would you like to show your timezone on the status page?')] public bool $show_timezone = false; + #[Description('Would you like to only show the days with disruption?')] public bool $only_disrupted_days = false; + #[Description('How many incident days should be shown in the timeline?')] public int $incident_days = 7; + #[Description('After how many seconds should the status page automatically refresh?')] public ?int $refresh_rate; + #[Description('Should the dashboard login link be shown?')] public bool $dashboard_login_link = true; + #[Description('Major outage threshold %')] public int $major_outage_threshold = 25; public static function group(): string { return 'app'; } + + public function installable(): array + { + return Arr::except(get_class_vars(__CLASS__), [ + 'install_id', + ]); + } } diff --git a/src/Settings/Attributes/Description.php b/src/Settings/Attributes/Description.php new file mode 100644 index 00000000..d4199ae2 --- /dev/null +++ b/src/Settings/Attributes/Description.php @@ -0,0 +1,11 @@ +artisan('cachet:install') + ->expectsOutputToContain('Welcome to the Cachet installer!') + ->expectsConfirmation('Do you want to configure Cachet before installing?', 'no') + ->expectsOutputToContain('Installing Cachet...') + ->expectsOutputToContain('Cachet has been installed successfully!') + ->assertSuccessful(); +}); + +it('updates app settings when configuration is passed', function () { + $this->artisan('cachet:install') + ->expectsOutputToContain('Welcome to the Cachet installer!') + ->expectsConfirmation('Do you want to configure Cachet before installing?', 'yes') + ->expectsOutputToContain('Configuring Cachet...') + ->expectsQuestion('What is the name of your application?', 'Laravel Envoyer') + ->expectsQuestion('What is your application about?', 'Zero downtime deployment tool.') + ->expectsConfirmation('Do you want to show support for Cachet?', 'yes') + ->expectsQuestion('What timezone is is the application located in?', 'America/New_York') + ->expectsConfirmation('Would you like to show your timezone on the status page?', 'yes') + ->expectsConfirmation('Would you like to only show the days with disruption?', 'yes') + ->expectsQuestion('How many incident days should be shown in the timeline?', 14) + ->expectsQuestion('After how many seconds should the status page automatically refresh?', 10) + ->expectsConfirmation('Should the dashboard login link be shown?', 'no') + ->expectsQuestion('Major outage threshold %', 50) + ->expectsOutputToContain('Installing Cachet...') + ->expectsOutputToContain('Cachet has been installed successfully!') + ->assertSuccessful(); + + $settings = app(AppSettings::class); + expect($settings->name)->toBe('Laravel Envoyer') + ->and($settings->about)->toBe('Zero downtime deployment tool.') + ->and($settings->show_support)->toBeTrue() + ->and($settings->timezone)->toBe('America/New_York') + ->and($settings->show_timezone)->toBeTrue() + ->and($settings->only_disrupted_days)->toBeTrue() + ->and($settings->incident_days)->toBe(14) + ->and($settings->refresh_rate)->toBe(10) + ->and($settings->dashboard_login_link)->toBeFalse() + ->and($settings->major_outage_threshold)->toBe(50); +}); \ No newline at end of file From fe389a3b06017a804516393fe2959cdc8dcc936a Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Mon, 14 Oct 2024 19:18:11 +0100 Subject: [PATCH 18/35] Minor tweaks --- src/Commands/InstallCommand.php | 5 +++-- tests/Unit/Commands/InstallCommandTest.php | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index 021dc146..928f02af 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -10,6 +10,7 @@ use ReflectionProperty; use function Laravel\Prompts\confirm; use function Laravel\Prompts\info; +use function Laravel\Prompts\intro; use function Laravel\Prompts\text; @@ -21,7 +22,7 @@ class InstallCommand extends Command public function handle(AppSettings $settings) { - info('Welcome to the Cachet installer!'); + intro('Welcome to the Cachet installer!'); Sleep::for(2)->seconds(); @@ -32,7 +33,7 @@ public function handle(AppSettings $settings) info('Installing Cachet...'); - info('Cachet has been installed successfully!'); + info('Cachet is installed ⚡'); return Command::SUCCESS; } diff --git a/tests/Unit/Commands/InstallCommandTest.php b/tests/Unit/Commands/InstallCommandTest.php index 8abe6591..7b01861c 100644 --- a/tests/Unit/Commands/InstallCommandTest.php +++ b/tests/Unit/Commands/InstallCommandTest.php @@ -3,16 +3,13 @@ namespace Tests\Unit\Commands; use Cachet\Settings\AppSettings; -use Illuminate\Foundation\Testing\RefreshDatabase; - -uses(RefreshDatabase::class); it('runs install command successfully without configuration', function () { $this->artisan('cachet:install') ->expectsOutputToContain('Welcome to the Cachet installer!') ->expectsConfirmation('Do you want to configure Cachet before installing?', 'no') ->expectsOutputToContain('Installing Cachet...') - ->expectsOutputToContain('Cachet has been installed successfully!') + ->expectsOutputToContain('Cachet is installed ⚡') ->assertSuccessful(); }); @@ -32,7 +29,7 @@ ->expectsConfirmation('Should the dashboard login link be shown?', 'no') ->expectsQuestion('Major outage threshold %', 50) ->expectsOutputToContain('Installing Cachet...') - ->expectsOutputToContain('Cachet has been installed successfully!') + ->expectsOutputToContain('Cachet is installed ⚡') ->assertSuccessful(); $settings = app(AppSettings::class); From 67769aa3b099bb62bb7c7f5e45fa9a2e7591a439 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Mon, 14 Oct 2024 19:20:20 +0100 Subject: [PATCH 19/35] Add config item ready to set up configuring own database connection for cachet if required --- config/cachet.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/config/cachet.php b/config/cachet.php index bd1dc9de..8372b302 100644 --- a/config/cachet.php +++ b/config/cachet.php @@ -107,4 +107,14 @@ | */ 'docker' => env('CACHET_DOCKER', false), + + /* + |-------------------------------------------------------------------------- + | Cachet Database Connection + |-------------------------------------------------------------------------- + | + | Support using an alternative database connection, defaults to default connecton of application + | + */ + 'database_connection' => env('CACHET_DB_CONNECTION'), ]; From 419249ee583020e2cbaf958380104fb80971c60b Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Mon, 14 Oct 2024 19:43:41 +0100 Subject: [PATCH 20/35] Move items out of testbech to install command itself. --- src/Commands/InstallCommand.php | 19 ++++++++++++++++--- testbench.yaml | 4 ---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index 928f02af..ac13ae16 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -2,6 +2,7 @@ namespace Cachet\Commands; +use Cachet\Database\Seeders\DatabaseSeeder; use Cachet\Settings\AppSettings; use Cachet\Settings\Attributes\Description; use Illuminate\Console\Command; @@ -28,17 +29,29 @@ public function handle(AppSettings $settings) if (confirm('Do you want to configure Cachet before installing?', true)) { info('Configuring Cachet...'); - $this->configureDatabaseSettings($settings); + $this->configureEnvironmentSettings(); + $settings = $this->configureDatabaseSettings($settings); } info('Installing Cachet...'); + $this->call('filament:assets'); + + $this->call('migrate', ['--seed' => true, '--seeder' => DatabaseSeeder::class]); + + $settings->save(); + info('Cachet is installed ⚡'); return Command::SUCCESS; } - protected function configureDatabaseSettings(AppSettings $settings): void + protected function configureEnvironmentSettings(): void + { + //@todo configure environment variables inside cachet.php + } + + protected function configureDatabaseSettings(AppSettings $settings): AppSettings { collect( (new ReflectionClass($settings))->getProperties(ReflectionProperty::IS_PUBLIC) @@ -55,6 +68,6 @@ protected function configureDatabaseSettings(AppSettings $settings): void }) ->pluck('name'); - $settings->save(); + return $settings; } } \ No newline at end of file diff --git a/testbench.yaml b/testbench.yaml index c8e7504c..48db24a3 100644 --- a/testbench.yaml +++ b/testbench.yaml @@ -18,13 +18,9 @@ workbench: to: public/vendor/cachethq/cachet build: - asset-publish - - filament:assets - create-sqlite-db - storage-link - db:wipe - - migrate:refresh: - --seed: true - --seeder: Cachet\Database\Seeders\DatabaseSeeder - cachet:install assets: - query-builder-config From 612e94e7bbeef2ea8f75d613946e3ef7f56debc7 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Mon, 14 Oct 2024 19:58:18 +0100 Subject: [PATCH 21/35] move migrations higher in chain at least for now --- src/Commands/InstallCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index ac13ae16..b4a32387 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -27,6 +27,8 @@ public function handle(AppSettings $settings) Sleep::for(2)->seconds(); + $this->call('migrate', ['--seed' => true, '--seeder' => DatabaseSeeder::class]); + if (confirm('Do you want to configure Cachet before installing?', true)) { info('Configuring Cachet...'); $this->configureEnvironmentSettings(); @@ -37,8 +39,6 @@ public function handle(AppSettings $settings) $this->call('filament:assets'); - $this->call('migrate', ['--seed' => true, '--seeder' => DatabaseSeeder::class]); - $settings->save(); info('Cachet is installed ⚡'); From 5f87f582977b83ab29e893c5083cf2689d583146 Mon Sep 17 00:00:00 2001 From: AlexJump24 Date: Mon, 14 Oct 2024 18:45:02 +0000 Subject: [PATCH 22/35] Compile Assets --- public/build/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/build/manifest.json b/public/build/manifest.json index b566fa26..b8522229 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -15,4 +15,4 @@ "src": "resources/js/cachet.js", "isEntry": true } -} \ No newline at end of file +} From 9d2411bebbe2f8f37e0ab52a2be0aea644b737ef Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Tue, 15 Oct 2024 06:26:39 +0100 Subject: [PATCH 23/35] update --- .../2024_01_22_205110_create_default_settings.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/database/migrations/2024_01_22_205110_create_default_settings.php b/database/migrations/2024_01_22_205110_create_default_settings.php index 5f019700..b0e5c8f1 100644 --- a/database/migrations/2024_01_22_205110_create_default_settings.php +++ b/database/migrations/2024_01_22_205110_create_default_settings.php @@ -12,8 +12,9 @@ public function up(): void { // Cachet settings... rescue(fn () => $this->migrator->add('app.install_id', Str::random(40))); - rescue(fn () => $this->migrator->add('app.name', 'Cachet V3')); - rescue(fn () => $this->migrator->add('app.domain', <<<'ABOUT' + rescue(fn () => $this->migrator->add('app.name', 'Cachet')); + rescue(fn () => $this->migrator->add('app.domain')); + rescue(fn () => $this->migrator->add('app.about', <<<'ABOUT' Cachet is a **beautiful** and **powerful** open-source status page system. To access the [dashboard](/dashboard), use the following credentials: @@ -22,7 +23,6 @@ public function up(): void Please [consider sponsoring](https://github.com/cachethq/cachet?sponsor=1) the continued development of Cachet. ABOUT)); - rescue(fn () => $this->migrator->add('app.about')); rescue(fn () => $this->migrator->add('app.timezone', 'UTC')); rescue(fn () => $this->migrator->add('app.locale', 'en')); rescue(fn () => $this->migrator->add('app.incident_days', 7)); From d5c85b47a7b1e3a3cc2217eacb7f10077a1474c7 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Wed, 16 Oct 2024 21:04:32 +0100 Subject: [PATCH 24/35] Write to env file if prompted for configuration --- config/cachet.php | 2 +- src/Commands/InstallCommand.php | 64 +++++++++++++++++++++- tests/Unit/Commands/InstallCommandTest.php | 21 ++++++- 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/config/cachet.php b/config/cachet.php index 8372b302..0abe4a85 100644 --- a/config/cachet.php +++ b/config/cachet.php @@ -116,5 +116,5 @@ | Support using an alternative database connection, defaults to default connecton of application | */ - 'database_connection' => env('CACHET_DB_CONNECTION'), + 'database_connection' => env('CACHET_DB_CONNECTION', 'default'), ]; diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index b4a32387..a7229875 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -6,7 +6,9 @@ use Cachet\Settings\AppSettings; use Cachet\Settings\Attributes\Description; use Illuminate\Console\Command; +use Illuminate\Support\Facades\File; use Illuminate\Support\Sleep; +use Illuminate\Support\Str; use ReflectionClass; use ReflectionProperty; use function Laravel\Prompts\confirm; @@ -48,7 +50,67 @@ public function handle(AppSettings $settings) protected function configureEnvironmentSettings(): void { - //@todo configure environment variables inside cachet.php + $path = text( + 'Which path do you want Cachet to be accessible from?', + default: config('cachet.path') + ); + + $title = text( + 'What will the title of your status page be?', + default: config('cachet.title') + ); + + $connection = text( + 'Which database connection do you wish to use for Cachet?', + default: config('cachet.database_connection') + ); + + $beacon = confirm( + 'Do you wish to send anonymous data to cachet to help us understand how Cachet is used?', + default: config('cachet.beacon') + ); + + $this->writeEnv([ + 'CACHET_PATH' => $path, + 'CACHET_TITLE' => $title, + 'CACHET_DB_CONNECTION' => $connection, + 'CACHET_BEACON' => $beacon, + ]); + } + + protected function writeEnv(array $values): void + { + $environmentFile = app()->environmentFile(); + $environmentPath = app()->environmentPath(); + $fullPath = $environmentPath . '/' . $environmentFile; + + $envFileContents = File::lines($fullPath)->collect(); + + foreach ($values as $key => $value) { + $existingKey = $envFileContents->search(function ($line) use ($key) { + return stripos($line, $key) !== false; + }); + + if (Str::contains($value, ' ')) { + $value = Str::wrap($value,'"'); + } + + if ($value === true) { + $value = 'true'; + } + + if ($value === false) { + $value = 'false'; + } + + if ($existingKey === false) { + $envFileContents->push($key . '=' . $value); + } else { + $envFileContents->put($existingKey, $key . '=' . $value); + } + } + + File::put($fullPath, $envFileContents->implode("\n")); } protected function configureDatabaseSettings(AppSettings $settings): AppSettings diff --git a/tests/Unit/Commands/InstallCommandTest.php b/tests/Unit/Commands/InstallCommandTest.php index 7b01861c..7ca4894c 100644 --- a/tests/Unit/Commands/InstallCommandTest.php +++ b/tests/Unit/Commands/InstallCommandTest.php @@ -3,6 +3,7 @@ namespace Tests\Unit\Commands; use Cachet\Settings\AppSettings; +use Illuminate\Support\Facades\File; it('runs install command successfully without configuration', function () { $this->artisan('cachet:install') @@ -13,11 +14,17 @@ ->assertSuccessful(); }); -it('updates app settings when configuration is passed', function () { +it('updates app settings and config filewhen configuration is passed', function () { + File::copy(base_path('.env.example'), base_path('.env')); + $this->artisan('cachet:install') ->expectsOutputToContain('Welcome to the Cachet installer!') ->expectsConfirmation('Do you want to configure Cachet before installing?', 'yes') ->expectsOutputToContain('Configuring Cachet...') + ->expectsQuestion('Which path do you want Cachet to be accessible from?', '/status') + ->expectsQuestion('What will the title of your status page be?', 'Laravel Envoyer') + ->expectsQuestion('Which database connection do you wish to use for Cachet?', 'default') + ->expectsQuestion('Do you wish to send anonymous data to cachet to help us understand how Cachet is used?', true) ->expectsQuestion('What is the name of your application?', 'Laravel Envoyer') ->expectsQuestion('What is your application about?', 'Zero downtime deployment tool.') ->expectsConfirmation('Do you want to show support for Cachet?', 'yes') @@ -32,6 +39,14 @@ ->expectsOutputToContain('Cachet is installed ⚡') ->assertSuccessful(); + $envContent = file_get_contents(base_path('.env')); + + expect($envContent) + ->toContain('CACHET_PATH=/status') + ->toContain('CACHET_TITLE="Laravel Envoyer"') + ->toContain('CACHET_DB_CONNECTION=default') + ->toContain('CACHET_BEACON=true'); + $settings = app(AppSettings::class); expect($settings->name)->toBe('Laravel Envoyer') ->and($settings->about)->toBe('Zero downtime deployment tool.') @@ -43,4 +58,8 @@ ->and($settings->refresh_rate)->toBe(10) ->and($settings->dashboard_login_link)->toBeFalse() ->and($settings->major_outage_threshold)->toBe(50); +}); + +afterAll(function() { + File::delete(base_path('.env')); }); \ No newline at end of file From 4331b0d63b5db0b37d393b7d754658e1b8985aba Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Thu, 17 Oct 2024 09:40:05 +0100 Subject: [PATCH 25/35] tidy up --- src/Commands/InstallCommand.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index a7229875..10752e52 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -88,20 +88,15 @@ protected function writeEnv(array $values): void foreach ($values as $key => $value) { $existingKey = $envFileContents->search(function ($line) use ($key) { - return stripos($line, $key) !== false; + return Str::contains($line, $key, true); }); - if (Str::contains($value, ' ')) { - $value = Str::wrap($value,'"'); - } - - if ($value === true) { - $value = 'true'; - } - - if ($value === false) { - $value = 'false'; - } + $value = match (true) { + Str::contains($value, ' ') => Str::wrap($value,'"'), + $value === true => 'true', + $value === false => 'false', + default => $value + }; if ($existingKey === false) { $envFileContents->push($key . '=' . $value); From c8baa93d342770dfbac54dbb67ee154ec1fe5c79 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Thu, 17 Oct 2024 15:22:57 +0100 Subject: [PATCH 26/35] changes from feedback --- src/Commands/InstallCommand.php | 19 ++++++++++++++++--- src/Settings/AppSettings.php | 4 ++-- src/Settings/Attributes/Description.php | 12 +++++++++++- tests/Unit/Commands/InstallCommandTest.php | 4 ---- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index 10752e52..e543fc5f 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -115,10 +115,23 @@ protected function configureDatabaseSettings(AppSettings $settings): AppSettings ) ->filter(fn (ReflectionProperty $property) => array_key_exists($property->getName(), $settings->installable()) ) ->each(function (ReflectionProperty $property) use ($settings) { - $description = $property->getAttributes(Description::class)[0]->getArguments()[0]; + $descriptionAttribute = $property->getAttributes(Description::class); + + if (empty($descriptionAttribute)) { + return; + } + + $descriptionAttributeClass = $descriptionAttribute[0]->newInstance(); + $default = $descriptionAttributeClass->getDefault(); + $required = $descriptionAttributeClass->getRequired(); + + if ($required === false) { + return; + } + $value = match($property->getType()?->getName()) { - 'bool' => confirm($description ?? $property->getName()), - default => text($description ?? $property->getName(), default: $property->getDefaultValue() ?? '', required: true), + 'bool' => confirm($default ?? $property->getName()), + default => text($default ?? $property->getName(), default: $property->getDefaultValue() ?? '', required: true), }; $settings->{$property->getName()} = $value; diff --git a/src/Settings/AppSettings.php b/src/Settings/AppSettings.php index baa58e58..13d3d6f2 100644 --- a/src/Settings/AppSettings.php +++ b/src/Settings/AppSettings.php @@ -14,7 +14,7 @@ class AppSettings extends Settings #[Description('What is the name of your application?')] public ?string $name = 'Cachet'; - #[Description('What is your application about?')] + #[Description('What is your application about?', required: false)] public ?string $about; #[Description('Do you want to show support for Cachet?')] @@ -32,7 +32,7 @@ class AppSettings extends Settings #[Description('How many incident days should be shown in the timeline?')] public int $incident_days = 7; - #[Description('After how many seconds should the status page automatically refresh?')] + #[Description('After how many seconds should the status page automatically refresh?', required: false)] public ?int $refresh_rate; #[Description('Should the dashboard login link be shown?')] diff --git a/src/Settings/Attributes/Description.php b/src/Settings/Attributes/Description.php index d4199ae2..6f9ad157 100644 --- a/src/Settings/Attributes/Description.php +++ b/src/Settings/Attributes/Description.php @@ -7,5 +7,15 @@ #[Attribute(Attribute::TARGET_PROPERTY)] final class Description { - public function __construct(private string $description) {} + public function __construct(private readonly string $default, private readonly bool $required = true) {} + + public function getDefault(): string + { + return $this->default; + } + + public function getRequired(): bool + { + return $this->required; + } } \ No newline at end of file diff --git a/tests/Unit/Commands/InstallCommandTest.php b/tests/Unit/Commands/InstallCommandTest.php index 7ca4894c..91a354a2 100644 --- a/tests/Unit/Commands/InstallCommandTest.php +++ b/tests/Unit/Commands/InstallCommandTest.php @@ -26,13 +26,11 @@ ->expectsQuestion('Which database connection do you wish to use for Cachet?', 'default') ->expectsQuestion('Do you wish to send anonymous data to cachet to help us understand how Cachet is used?', true) ->expectsQuestion('What is the name of your application?', 'Laravel Envoyer') - ->expectsQuestion('What is your application about?', 'Zero downtime deployment tool.') ->expectsConfirmation('Do you want to show support for Cachet?', 'yes') ->expectsQuestion('What timezone is is the application located in?', 'America/New_York') ->expectsConfirmation('Would you like to show your timezone on the status page?', 'yes') ->expectsConfirmation('Would you like to only show the days with disruption?', 'yes') ->expectsQuestion('How many incident days should be shown in the timeline?', 14) - ->expectsQuestion('After how many seconds should the status page automatically refresh?', 10) ->expectsConfirmation('Should the dashboard login link be shown?', 'no') ->expectsQuestion('Major outage threshold %', 50) ->expectsOutputToContain('Installing Cachet...') @@ -49,13 +47,11 @@ $settings = app(AppSettings::class); expect($settings->name)->toBe('Laravel Envoyer') - ->and($settings->about)->toBe('Zero downtime deployment tool.') ->and($settings->show_support)->toBeTrue() ->and($settings->timezone)->toBe('America/New_York') ->and($settings->show_timezone)->toBeTrue() ->and($settings->only_disrupted_days)->toBeTrue() ->and($settings->incident_days)->toBe(14) - ->and($settings->refresh_rate)->toBe(10) ->and($settings->dashboard_login_link)->toBeFalse() ->and($settings->major_outage_threshold)->toBe(50); }); From 9d7bc2fc7b15bdb584fad78f1b3bd5efc65d8fe5 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Thu, 17 Oct 2024 15:52:47 +0100 Subject: [PATCH 27/35] fix pipeline issue --- src/Commands/InstallCommand.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index e543fc5f..9c4030ab 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -9,6 +9,7 @@ use Illuminate\Support\Facades\File; use Illuminate\Support\Sleep; use Illuminate\Support\Str; +use Laravel\Prompts\Prompt; use ReflectionClass; use ReflectionProperty; use function Laravel\Prompts\confirm; @@ -25,6 +26,8 @@ class InstallCommand extends Command public function handle(AppSettings $settings) { + Prompt::fallbackWhen(!$this->input->isInteractive()); + intro('Welcome to the Cachet installer!'); Sleep::for(2)->seconds(); From da9f99416dd3c3f9c894c374ae266324811fbd71 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Fri, 18 Oct 2024 07:42:56 +0100 Subject: [PATCH 28/35] Pipeline before passed oddly so not needed --- src/Commands/InstallCommand.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index 9c4030ab..d980d9d2 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -26,8 +26,6 @@ class InstallCommand extends Command public function handle(AppSettings $settings) { - Prompt::fallbackWhen(!$this->input->isInteractive()); - intro('Welcome to the Cachet installer!'); Sleep::for(2)->seconds(); From c628bc59f689fad2b8ee89160075b6e072f62701 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Fri, 18 Oct 2024 14:57:16 +0100 Subject: [PATCH 29/35] feedback + tidy up --- src/Commands/InstallCommand.php | 7 +++---- src/Settings/Attributes/Description.php | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index d980d9d2..a64a01e4 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -9,7 +9,6 @@ use Illuminate\Support\Facades\File; use Illuminate\Support\Sleep; use Illuminate\Support\Str; -use Laravel\Prompts\Prompt; use ReflectionClass; use ReflectionProperty; use function Laravel\Prompts\confirm; @@ -24,7 +23,7 @@ class InstallCommand extends Command protected $description = 'Install Cachet'; - public function handle(AppSettings $settings) + public function handle(AppSettings $settings): int { intro('Welcome to the Cachet installer!'); @@ -123,8 +122,8 @@ protected function configureDatabaseSettings(AppSettings $settings): AppSettings } $descriptionAttributeClass = $descriptionAttribute[0]->newInstance(); - $default = $descriptionAttributeClass->getDefault(); - $required = $descriptionAttributeClass->getRequired(); + $default = $descriptionAttributeClass->default(); + $required = $descriptionAttributeClass->required(); if ($required === false) { return; diff --git a/src/Settings/Attributes/Description.php b/src/Settings/Attributes/Description.php index 6f9ad157..39134bca 100644 --- a/src/Settings/Attributes/Description.php +++ b/src/Settings/Attributes/Description.php @@ -9,12 +9,12 @@ final class Description { public function __construct(private readonly string $default, private readonly bool $required = true) {} - public function getDefault(): string + public function default(): string { return $this->default; } - public function getRequired(): bool + public function required(): bool { return $this->required; } From af31e0a33086b69199b7b4bccad6d6a16f48fcd2 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Fri, 27 Dec 2024 13:28:47 +0000 Subject: [PATCH 30/35] Add optional create user command --- src/Commands/InstallCommand.php | 4 ++++ tests/Unit/Commands/InstallCommandTest.php | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index a64a01e4..dbc073e4 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -31,6 +31,10 @@ public function handle(AppSettings $settings): int $this->call('migrate', ['--seed' => true, '--seeder' => DatabaseSeeder::class]); + if (confirm('Do you want to create a new user?', true)) { + $this->call(MakeUserCommand::class); + } + if (confirm('Do you want to configure Cachet before installing?', true)) { info('Configuring Cachet...'); $this->configureEnvironmentSettings(); diff --git a/tests/Unit/Commands/InstallCommandTest.php b/tests/Unit/Commands/InstallCommandTest.php index 91a354a2..e70903a7 100644 --- a/tests/Unit/Commands/InstallCommandTest.php +++ b/tests/Unit/Commands/InstallCommandTest.php @@ -8,6 +8,7 @@ it('runs install command successfully without configuration', function () { $this->artisan('cachet:install') ->expectsOutputToContain('Welcome to the Cachet installer!') + ->expectsConfirmation('Do you want to create a new user?', 'no') ->expectsConfirmation('Do you want to configure Cachet before installing?', 'no') ->expectsOutputToContain('Installing Cachet...') ->expectsOutputToContain('Cachet is installed ⚡') @@ -19,6 +20,7 @@ $this->artisan('cachet:install') ->expectsOutputToContain('Welcome to the Cachet installer!') + ->expectsConfirmation('Do you want to create a new user?', 'no') ->expectsConfirmation('Do you want to configure Cachet before installing?', 'yes') ->expectsOutputToContain('Configuring Cachet...') ->expectsQuestion('Which path do you want Cachet to be accessible from?', '/status') From 790926c1f3c92ea9fd7475ab0033656f2e1704f2 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Fri, 27 Dec 2024 13:50:59 +0000 Subject: [PATCH 31/35] change default --- src/Commands/InstallCommand.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index dbc073e4..37304d9f 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -16,7 +16,6 @@ use function Laravel\Prompts\intro; use function Laravel\Prompts\text; - class InstallCommand extends Command { protected $name = 'cachet:install'; @@ -31,8 +30,8 @@ public function handle(AppSettings $settings): int $this->call('migrate', ['--seed' => true, '--seeder' => DatabaseSeeder::class]); - if (confirm('Do you want to create a new user?', true)) { - $this->call(MakeUserCommand::class); + if (confirm('Do you want to create a new user?', false)) { + $this->call('cachet:make:user'); } if (confirm('Do you want to configure Cachet before installing?', true)) { From 4ff5de2f286a0591db0c675fe550a7b506756920 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Fri, 27 Dec 2024 15:46:39 +0000 Subject: [PATCH 32/35] fix stan issues --- src/Commands/InstallCommand.php | 9 ++++++--- tests/Unit/Commands/InstallCommandTest.php | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index 37304d9f..00a9fa8b 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -132,9 +132,12 @@ protected function configureDatabaseSettings(AppSettings $settings): AppSettings return; } - $value = match($property->getType()?->getName()) { - 'bool' => confirm($default ?? $property->getName()), - default => text($default ?? $property->getName(), default: $property->getDefaultValue() ?? '', required: true), + /** @var \ReflectionNamedType $namedType */ + $namedType = $property->getType(); + + $value = match($namedType->getName()) { + 'bool' => confirm($default ?: $property->getName()), + default => text($default ?: $property->getName(), default: $property->getDefaultValue() ?: '', required: true), }; $settings->{$property->getName()} = $value; diff --git a/tests/Unit/Commands/InstallCommandTest.php b/tests/Unit/Commands/InstallCommandTest.php index e70903a7..bb08680b 100644 --- a/tests/Unit/Commands/InstallCommandTest.php +++ b/tests/Unit/Commands/InstallCommandTest.php @@ -15,7 +15,7 @@ ->assertSuccessful(); }); -it('updates app settings and config filewhen configuration is passed', function () { +it('updates app settings and config file when configuration is passed', function () { File::copy(base_path('.env.example'), base_path('.env')); $this->artisan('cachet:install') From 4184a498a1ea8c11165b7a35931fd29aebeb3cd1 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Sun, 29 Dec 2024 13:05:07 +0000 Subject: [PATCH 33/35] Prevent user table being truncated on install in another Laravel project --- config/cachet.php | 2 +- database/seeders/DatabaseSeeder.php | 27 ++++++++-------------- src/Commands/InstallCommand.php | 6 ++++- tests/Unit/Commands/InstallCommandTest.php | 2 ++ 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/config/cachet.php b/config/cachet.php index e7b2c191..455cb5da 100644 --- a/config/cachet.php +++ b/config/cachet.php @@ -116,5 +116,5 @@ | Support using an alternative database connection, defaults to default connecton of application | */ - 'database_connection' => env('CACHET_DB_CONNECTION', 'default'), + 'database_connection' => env('CACHET_DB_CONNECTION', config('database.default')), ]; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 9b91d2c1..a01aab28 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -31,33 +31,24 @@ class DatabaseSeeder extends Seeder */ public function run(): void { - DB::table('users')->truncate(); - DB::table('incidents')->truncate(); - DB::table('components')->truncate(); - DB::table('component_groups')->truncate(); - DB::table('schedules')->truncate(); - DB::table('metrics')->truncate(); - DB::table('metric_points')->truncate(); - DB::table('updates')->truncate(); - /** @var \Illuminate\Foundation\Auth\User $userModel */ $userModel = config('cachet.user_model'); - $user = $userModel::create([ + $user = $userModel::query()->firstOrCreate([ 'name' => 'Cachet Demo', 'email' => 'test@test.com', 'password' => bcrypt('test123'), 'email_verified_at' => now(), ]); - Schedule::create([ + Schedule::query()->firstOrCreate([ 'name' => 'Documentation Maintenance', 'message' => 'We will be conducting maintenance on our documentation servers. Documentation may not be available during this time.', 'scheduled_at' => now()->subHours(12)->subMinutes(45), 'completed_at' => now()->subHours(12), ]); - tap(Schedule::create([ + tap(Schedule::query()->firstOrCreate([ 'name' => 'Documentation Maintenance', 'message' => 'We will be conducting maintenance on our documentation servers. You may experience degraded performance during this time.', 'scheduled_at' => now()->addHours(24), @@ -76,7 +67,7 @@ public function run(): void $schedule->updates()->save($update); }); - $componentGroup = ComponentGroup::create([ + $componentGroup = ComponentGroup::query()->firstOrCreate([ 'name' => 'Cachet', 'collapsed' => ComponentGroupVisibilityEnum::expanded, 'visible' => ResourceVisibilityEnum::guest, @@ -104,14 +95,14 @@ public function run(): void ], ]); - Component::create([ + Component::query()->firstOrCreate([ 'name' => 'Laravel Artisan Cheatsheet', 'description' => 'The Laravel Artisan Cheatsheet.', 'link' => 'https://artisan.page', 'status' => ComponentStatusEnum::operational, ]); - $metric = Metric::create([ + $metric = Metric::query()->firstOrCreate([ 'name' => 'Cachet API Requests', 'suffix' => 'req/s', 'description' => 'The number of requests to the Cachet API.', @@ -127,7 +118,7 @@ public function run(): void 'created_at' => now()->subMinutes(random_int(0, $i * 60)), ])); - tap(Incident::create([ + tap(Incident::query()->firstOrCreate([ 'name' => 'DNS Provider Outage', 'message' => 'We\'re investigating an issue with our DNS provider causing the site to be offline.', 'status' => IncidentStatusEnum::fixed, @@ -164,7 +155,7 @@ public function run(): void $incident->updates()->save($update); }); - $incident = Incident::create([ + $incident = Incident::query()->firstOrCreate([ 'name' => 'Documentation Performance Degradation', 'message' => 'We\'re investigating an issue with our documentation causing the site to be slow.', 'status' => IncidentStatusEnum::fixed, @@ -191,7 +182,7 @@ public function run(): void $incident->updates()->save($update); - IncidentTemplate::create([ + IncidentTemplate::query()->firstOrCreate([ 'name' => 'Third-Party Service Outage', 'slug' => 'third-party-service-outage', 'template' => 'We\'re investigating an issue with a third-party provider ({{ name }}) causing our services to be offline.', diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index 00a9fa8b..97b57a94 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -28,7 +28,11 @@ public function handle(AppSettings $settings): int Sleep::for(2)->seconds(); - $this->call('migrate', ['--seed' => true, '--seeder' => DatabaseSeeder::class]); + $this->call('migrate', ['--database' => config('cachet.database_connection')]); + + if (confirm('Do you wish to seed any sample data?', true)) { + $this->call('db:seed'); + } if (confirm('Do you want to create a new user?', false)) { $this->call('cachet:make:user'); diff --git a/tests/Unit/Commands/InstallCommandTest.php b/tests/Unit/Commands/InstallCommandTest.php index bb08680b..ea05748d 100644 --- a/tests/Unit/Commands/InstallCommandTest.php +++ b/tests/Unit/Commands/InstallCommandTest.php @@ -8,6 +8,7 @@ it('runs install command successfully without configuration', function () { $this->artisan('cachet:install') ->expectsOutputToContain('Welcome to the Cachet installer!') + ->expectsConfirmation('Do you wish to seed any sample data?', 'no') ->expectsConfirmation('Do you want to create a new user?', 'no') ->expectsConfirmation('Do you want to configure Cachet before installing?', 'no') ->expectsOutputToContain('Installing Cachet...') @@ -20,6 +21,7 @@ $this->artisan('cachet:install') ->expectsOutputToContain('Welcome to the Cachet installer!') + ->expectsConfirmation('Do you wish to seed any sample data?', 'no') ->expectsConfirmation('Do you want to create a new user?', 'no') ->expectsConfirmation('Do you want to configure Cachet before installing?', 'yes') ->expectsOutputToContain('Configuring Cachet...') From 470e9828750b96e06a086943fdb0780e97873d2b Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Sun, 29 Dec 2024 13:48:24 +0000 Subject: [PATCH 34/35] Change order of things to prevent issues with database connection --- src/Commands/InstallCommand.php | 29 ++++++++++++++++------ src/Settings/AppSettings.php | 6 +++++ tests/Unit/Commands/InstallCommandTest.php | 6 ++--- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php index 97b57a94..91baceee 100644 --- a/src/Commands/InstallCommand.php +++ b/src/Commands/InstallCommand.php @@ -26,23 +26,35 @@ public function handle(AppSettings $settings): int { intro('Welcome to the Cachet installer!'); + $configureCachet = false; + + if (confirm('Do you want to configure Cachet before installing?', true)) { + info('Configuring Cachet...'); + $this->configureEnvironmentSettings(); + $configureCachet = true; + } + Sleep::for(2)->seconds(); $this->call('migrate', ['--database' => config('cachet.database_connection')]); + if ($configureCachet) { + $settings = $this->configureDatabaseSettings($settings); + } + if (confirm('Do you wish to seed any sample data?', true)) { - $this->call('db:seed'); + $this->call( + 'db:seed', + [ + 'class' => DatabaseSeeder::class, + '--database' => config('cachet.database_connection') + ] + ); } if (confirm('Do you want to create a new user?', false)) { $this->call('cachet:make:user'); } - - if (confirm('Do you want to configure Cachet before installing?', true)) { - info('Configuring Cachet...'); - $this->configureEnvironmentSettings(); - $settings = $this->configureDatabaseSettings($settings); - } info('Installing Cachet...'); @@ -77,6 +89,9 @@ protected function configureEnvironmentSettings(): void default: config('cachet.beacon') ); + // Override default connection to Laravel Settings saves to correct connection + app('db')->setDefaultConnection(config('cachet.database_connection')); + $this->writeEnv([ 'CACHET_PATH' => $path, 'CACHET_TITLE' => $title, diff --git a/src/Settings/AppSettings.php b/src/Settings/AppSettings.php index 13d3d6f2..79d1e2cf 100644 --- a/src/Settings/AppSettings.php +++ b/src/Settings/AppSettings.php @@ -5,9 +5,15 @@ use Cachet\Settings\Attributes\Description; use Illuminate\Support\Arr; use Spatie\LaravelSettings\Settings; +use Spatie\LaravelSettings\SettingsRepositories\SettingsRepository; class AppSettings extends Settings { + public function getRepository(): SettingsRepository + { + return parent::getRepository(); // TODO: Change the autogenerated stub + } + #[Description('The unique install ID of the application for telemetry.')] public string $install_id; diff --git a/tests/Unit/Commands/InstallCommandTest.php b/tests/Unit/Commands/InstallCommandTest.php index ea05748d..7ad86b62 100644 --- a/tests/Unit/Commands/InstallCommandTest.php +++ b/tests/Unit/Commands/InstallCommandTest.php @@ -8,9 +8,9 @@ it('runs install command successfully without configuration', function () { $this->artisan('cachet:install') ->expectsOutputToContain('Welcome to the Cachet installer!') + ->expectsConfirmation('Do you want to configure Cachet before installing?', 'no') ->expectsConfirmation('Do you wish to seed any sample data?', 'no') ->expectsConfirmation('Do you want to create a new user?', 'no') - ->expectsConfirmation('Do you want to configure Cachet before installing?', 'no') ->expectsOutputToContain('Installing Cachet...') ->expectsOutputToContain('Cachet is installed ⚡') ->assertSuccessful(); @@ -21,8 +21,6 @@ $this->artisan('cachet:install') ->expectsOutputToContain('Welcome to the Cachet installer!') - ->expectsConfirmation('Do you wish to seed any sample data?', 'no') - ->expectsConfirmation('Do you want to create a new user?', 'no') ->expectsConfirmation('Do you want to configure Cachet before installing?', 'yes') ->expectsOutputToContain('Configuring Cachet...') ->expectsQuestion('Which path do you want Cachet to be accessible from?', '/status') @@ -37,6 +35,8 @@ ->expectsQuestion('How many incident days should be shown in the timeline?', 14) ->expectsConfirmation('Should the dashboard login link be shown?', 'no') ->expectsQuestion('Major outage threshold %', 50) + ->expectsConfirmation('Do you wish to seed any sample data?', 'no') + ->expectsConfirmation('Do you want to create a new user?', 'no') ->expectsOutputToContain('Installing Cachet...') ->expectsOutputToContain('Cachet is installed ⚡') ->assertSuccessful(); From 1c0c33235ee2613ab611851a05f765235d93c2ed Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Wed, 1 Jan 2025 08:27:05 +0000 Subject: [PATCH 35/35] Add trait to models to overload connection name to use config value for cachet --- src/Models/Component.php | 3 +++ src/Models/ComponentGroup.php | 3 +++ src/Models/Concerns/ManagesConnections.php | 11 +++++++++++ src/Models/Incident.php | 4 ++++ src/Models/IncidentComponent.php | 3 +++ src/Models/IncidentTemplate.php | 3 +++ src/Models/Metric.php | 3 +++ src/Models/MetricPoint.php | 3 +++ src/Models/Schedule.php | 3 +++ src/Models/ScheduleComponent.php | 3 +++ src/Models/Subscriber.php | 3 +++ src/Models/Subscription.php | 3 +++ src/Models/Update.php | 3 +++ tests/Architecture/ModelsTest.php | 4 +++- 14 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/Models/Concerns/ManagesConnections.php diff --git a/src/Models/Component.php b/src/Models/Component.php index ed752a35..1f217365 100644 --- a/src/Models/Component.php +++ b/src/Models/Component.php @@ -7,6 +7,7 @@ use Cachet\Events\Components\ComponentCreated; use Cachet\Events\Components\ComponentDeleted; use Cachet\Events\Components\ComponentUpdated; +use Cachet\Models\Concerns\ManagesConnections; use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\Factory; @@ -42,6 +43,8 @@ class Component extends Model /** @use HasFactory */ use HasFactory; + use ManagesConnections; + use SoftDeletes; /** @var array */ diff --git a/src/Models/ComponentGroup.php b/src/Models/ComponentGroup.php index e078911f..0b032ae3 100644 --- a/src/Models/ComponentGroup.php +++ b/src/Models/ComponentGroup.php @@ -6,6 +6,7 @@ use Cachet\Database\Factories\ComponentGroupFactory; use Cachet\Enums\ComponentGroupVisibilityEnum; use Cachet\Enums\ResourceVisibilityEnum; +use Cachet\Models\Concerns\ManagesConnections; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -30,6 +31,8 @@ class ComponentGroup extends Model /** @use HasFactory */ use HasFactory; + use ManagesConnections; + use HasVisibility; /** @var array */ diff --git a/src/Models/Concerns/ManagesConnections.php b/src/Models/Concerns/ManagesConnections.php new file mode 100644 index 00000000..9ec62037 --- /dev/null +++ b/src/Models/Concerns/ManagesConnections.php @@ -0,0 +1,11 @@ + */ diff --git a/src/Models/IncidentComponent.php b/src/Models/IncidentComponent.php index 85840839..1fddb9e9 100644 --- a/src/Models/IncidentComponent.php +++ b/src/Models/IncidentComponent.php @@ -4,6 +4,7 @@ use Cachet\Database\Factories\IncidentComponentFactory; use Cachet\Enums\ComponentStatusEnum; +use Cachet\Models\Concerns\ManagesConnections; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -27,6 +28,8 @@ class IncidentComponent extends Pivot /** @use HasFactory */ use HasFactory; + use ManagesConnections; + /** @var array */ protected $casts = [ 'component_status' => ComponentStatusEnum::class, diff --git a/src/Models/IncidentTemplate.php b/src/Models/IncidentTemplate.php index 06c85525..bee61c16 100644 --- a/src/Models/IncidentTemplate.php +++ b/src/Models/IncidentTemplate.php @@ -4,6 +4,7 @@ use Cachet\Database\Factories\IncidentTemplateFactory; use Cachet\Enums\IncidentTemplateEngineEnum; +use Cachet\Models\Concerns\ManagesConnections; use Cachet\Renderers\BladeRenderer; use Cachet\Renderers\TwigRenderer; use Carbon\Carbon; @@ -28,6 +29,8 @@ class IncidentTemplate extends Model /** @use HasFactory */ use HasFactory; + use ManagesConnections; + /** @var array */ protected $casts = [ 'engine' => IncidentTemplateEngineEnum::class, diff --git a/src/Models/Metric.php b/src/Models/Metric.php index 67c2ecbe..049972d4 100644 --- a/src/Models/Metric.php +++ b/src/Models/Metric.php @@ -10,6 +10,7 @@ use Cachet\Events\Metrics\MetricCreated; use Cachet\Events\Metrics\MetricDeleted; use Cachet\Events\Metrics\MetricUpdated; +use Cachet\Models\Concerns\ManagesConnections; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -44,6 +45,8 @@ class Metric extends Model use HasVisibility; + use ManagesConnections; + /** @var array */ protected $casts = [ 'calc_type' => MetricTypeEnum::class, diff --git a/src/Models/MetricPoint.php b/src/Models/MetricPoint.php index 81c14580..b79d1a8a 100644 --- a/src/Models/MetricPoint.php +++ b/src/Models/MetricPoint.php @@ -5,6 +5,7 @@ use Cachet\Database\Factories\MetricPointFactory; use Cachet\Events\Metrics\MetricPointCreated; use Cachet\Events\Metrics\MetricPointDeleted; +use Cachet\Models\Concerns\ManagesConnections; use DateTime; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\Factory; @@ -30,6 +31,8 @@ class MetricPoint extends Model /** @use HasFactory */ use HasFactory; + use ManagesConnections; + /** @var array */ protected $casts = [ 'value' => 'float', diff --git a/src/Models/Schedule.php b/src/Models/Schedule.php index ee759013..ed3231d2 100644 --- a/src/Models/Schedule.php +++ b/src/Models/Schedule.php @@ -4,6 +4,7 @@ use Cachet\Database\Factories\ScheduleFactory; use Cachet\Enums\ScheduleStatusEnum; +use Cachet\Models\Concerns\ManagesConnections; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\Factory; @@ -40,6 +41,8 @@ class Schedule extends Model /** @use HasFactory */ use HasFactory; + use ManagesConnections; + use SoftDeletes; /** @var array */ diff --git a/src/Models/ScheduleComponent.php b/src/Models/ScheduleComponent.php index d34c7f98..460ee7f0 100644 --- a/src/Models/ScheduleComponent.php +++ b/src/Models/ScheduleComponent.php @@ -3,6 +3,7 @@ namespace Cachet\Models; use Cachet\Database\Factories\ScheduleComponentFactory; +use Cachet\Models\Concerns\ManagesConnections; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -26,6 +27,8 @@ class ScheduleComponent extends Model /** @use HasFactory */ use HasFactory; + use ManagesConnections; + /** @var list */ protected $fillable = [ 'schedule_id', diff --git a/src/Models/Subscriber.php b/src/Models/Subscriber.php index 4694713f..cb94bc8c 100644 --- a/src/Models/Subscriber.php +++ b/src/Models/Subscriber.php @@ -6,6 +6,7 @@ use Cachet\Events\Subscribers\SubscriberCreated; use Cachet\Events\Subscribers\SubscriberUnsubscribed; use Cachet\Events\Subscribers\SubscriberVerified; +use Cachet\Models\Concerns\ManagesConnections; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -31,6 +32,8 @@ class Subscriber extends Model /** @use HasFactory */ use HasFactory; + use ManagesConnections; + /** @var array */ protected $casts = [ 'verified_at' => 'datetime', diff --git a/src/Models/Subscription.php b/src/Models/Subscription.php index f1f7a4d0..0591b755 100644 --- a/src/Models/Subscription.php +++ b/src/Models/Subscription.php @@ -3,6 +3,7 @@ namespace Cachet\Models; use Cachet\Database\Factories\SubscriptionFactory; +use Cachet\Models\Concerns\ManagesConnections; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -25,6 +26,8 @@ class Subscription extends Model /** @use HasFactory */ use HasFactory; + use ManagesConnections; + /** * Get the component the subscription is for. */ diff --git a/src/Models/Update.php b/src/Models/Update.php index 1030178b..ada65b70 100644 --- a/src/Models/Update.php +++ b/src/Models/Update.php @@ -4,6 +4,7 @@ use Cachet\Database\Factories\UpdateFactory; use Cachet\Enums\IncidentStatusEnum; +use Cachet\Models\Concerns\ManagesConnections; use Carbon\Carbon; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Factories\Factory; @@ -34,6 +35,8 @@ class Update extends Model /** @use HasFactory */ use HasFactory; + use ManagesConnections; + /** @var array */ protected $casts = [ 'status' => IncidentStatusEnum::class, diff --git a/tests/Architecture/ModelsTest.php b/tests/Architecture/ModelsTest.php index ddb8bd71..01a99830 100644 --- a/tests/Architecture/ModelsTest.php +++ b/tests/Architecture/ModelsTest.php @@ -5,4 +5,6 @@ test('models test') ->expect('Cachet\Models') ->toBeClasses() - ->toExtend(Model::class); + ->ignoring('Cachet\Models\Concerns') + ->toExtend(Model::class) + ->ignoring('Cachet\Models\Concerns');