diff --git a/assets/app.css b/assets/app.css index b5c61c9..a2ecaa4 100644 --- a/assets/app.css +++ b/assets/app.css @@ -1,3 +1,141 @@ @tailwind base; @tailwind components; @tailwind utilities; + +@layer base { + @font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url('../fonts/InterVariable.woff2') format('woff2'); + } + + @font-face { + font-family: 'JetBrains Mono'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('../fonts/JetBrainsMono-Regular.woff2') format('woff2'); + } + + /* Accent shades as RGB triples so Tailwind opacity modifiers keep working + (see tailwind.config.js). Dark values first, light remaps below. */ + :root { + --color-primary-light: 52 130 239; + --color-secondary-light: 130 103 243; + --color-tertiary-light: 21 211 241; + color-scheme: dark; + } + + :root[data-theme="light"] { + --color-primary-light: 17 102 221; + --color-secondary-light: 99 65 240; + --color-tertiary-light: 14 116 144; + color-scheme: light; + } + + html { + @apply h-full bg-slate-950 text-gray-300 antialiased; + /* Always reserve the scrollbar's space, otherwise the centered content + shifts sideways between short pages and pages that scroll. */ + scrollbar-gutter: stable; + } + + html[data-theme="light"] { + @apply bg-slate-50 text-slate-600; + } + + body { + @apply h-full font-sans; + } + + /* Code ligatures would turn `->` into an arrow glyph; readers copy what + they see, so they stay off. */ + :where(pre, code, kbd, samp, .font-mono) { + font-variant-ligatures: none; + font-feature-settings: "liga" 0, "calt" 0; + } + + ::selection { + background-color: #1e3a8a; /* blue-900 */ + } + + :root[data-theme="light"] ::selection { + background-color: #bfdbfe; /* blue-200 */ + } + + * { + scrollbar-width: thin; + scrollbar-color: #334155 transparent; /* slate-700 */ + } + + :root[data-theme="light"] * { + scrollbar-color: #cbd5e1 transparent; /* slate-300 */ + } + + @media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } + } +} + +@layer components { + /* Code and data windows stay dark in both themes, like the code samples on + patchlevel.dev: the dump is a terminal, not a document. */ + .code-window { + @apply rounded-xl border border-slate-800 bg-slate-950 text-gray-300 light:border-slate-800 light:bg-slate-900; + } + + .json { + @apply code-window overflow-x-auto px-4 py-3 font-mono text-xs leading-relaxed whitespace-pre; + } + + .json_key, .json_key span { + @apply text-tertiary-light light:text-[#15d3f1]; + } + + .json_string, .json_string span { + @apply text-green-400; + } + + .json_number { + @apply text-primary-light light:text-[#3482ef]; + } + + .json_bool { + @apply text-amber-300; + } +} + +/* Plain CSS, outside @layer: these selectors never appear in the templates, so a + layered rule would be purged as unused. */ +dialog[open] { + animation: dialog-in 200ms cubic-bezier(0.16, 1, 0.3, 1); +} + +dialog::backdrop { + background: rgb(2 6 23 / 0.7); + backdrop-filter: blur(4px); +} + +:root[data-theme="light"] dialog::backdrop { + background: rgb(15 23 42 / 0.4); +} + +@keyframes dialog-in { + from { + opacity: 0; + transform: translateY(8px) scale(0.98); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} diff --git a/assets/app.js b/assets/app.js new file mode 100644 index 0000000..e4d0ca2 --- /dev/null +++ b/assets/app.js @@ -0,0 +1,169 @@ +import './app.css'; + +/* --- Preferences: theme and content width ------------------------------ */ + +const lightQuery = window.matchMedia('(prefers-color-scheme: light)'); + +function applyTheme(preference) { + const light = preference === 'light' || (preference === 'auto' && lightQuery.matches); + document.documentElement.setAttribute('data-theme', light ? 'light' : 'dark'); +} + +function applyWidth(preference) { + if (preference === 'full') { + document.documentElement.setAttribute('data-width', 'full'); + } else { + document.documentElement.removeAttribute('data-width'); + } +} + +// The inline script in base.html.twig applies the stored values before paint; this only wires the switches. +const preferences = { + theme: {key: 'patchlevel-dashboard-theme', values: ['light', 'dark', 'auto'], fallback: 'auto', apply: applyTheme}, + width: {key: 'patchlevel-dashboard-width', values: ['fixed', 'full'], fallback: 'fixed', apply: applyWidth}, +}; + +function readPreference(name) { + const {key, values, fallback} = preferences[name]; + + try { + const stored = localStorage.getItem(key); + if (values.includes(stored)) { + return stored; + } + } catch { + // storage disabled + } + + return fallback; +} + +function syncSwitch(name, value) { + document.querySelectorAll(`[data-pref="${name}"]`).forEach((button) => { + button.setAttribute('aria-checked', String(button.dataset.value === value)); + }); +} + +document.querySelectorAll('[data-pref]').forEach((button) => { + button.addEventListener('click', () => { + const name = button.dataset.pref; + const value = button.dataset.value; + + try { + localStorage.setItem(preferences[name].key, value); + } catch { + // storage disabled + } + + preferences[name].apply(value); + syncSwitch(name, value); + }); +}); + +Object.keys(preferences).forEach((name) => syncSwitch(name, readPreference(name))); + +lightQuery.addEventListener('change', () => { + if (readPreference('theme') === 'auto') { + applyTheme('auto'); + } +}); + +/* --- Mobile navigation -------------------------------------------------- */ + +const sidebar = document.getElementById('sidebar'); +const sidebarBackdrop = document.getElementById('sidebar-backdrop'); + +function setSidebar(open) { + if (!sidebar) { + return; + } + + sidebar.classList.toggle('-translate-x-full', !open); + sidebarBackdrop?.classList.toggle('hidden', !open); + document.querySelectorAll('[data-sidebar-toggle]').forEach((button) => { + button.setAttribute('aria-expanded', String(open)); + }); + + if (open) { + sidebar.querySelector('a, button')?.focus(); + } +} + +document.querySelectorAll('[data-sidebar-toggle]').forEach((button) => { + button.addEventListener('click', () => { + setSidebar(sidebar.classList.contains('-translate-x-full')); + }); +}); + +sidebarBackdrop?.addEventListener('click', () => setSidebar(false)); + +window.addEventListener('keydown', (event) => { + if (event.key === 'Escape' && sidebar && !sidebar.classList.contains('-translate-x-full')) { + setSidebar(false); + } +}); + +/* --- Detail dialogs ----------------------------------------------------- */ + +document.querySelectorAll('[data-modal]').forEach((button) => { + button.addEventListener('click', (event) => { + event.preventDefault(); + document.getElementById(button.dataset.modal)?.showModal(); + }); +}); + +document.querySelectorAll('dialog').forEach((dialog) => { + dialog.querySelectorAll('[data-modal-close]').forEach((button) => { + button.addEventListener('click', () => dialog.close()); + }); + + // A click on the backdrop lands on the dialog element itself. + dialog.addEventListener('click', (event) => { + if (event.target === dialog) { + dialog.close(); + } + }); +}); + +/* --- Copy to clipboard -------------------------------------------------- */ + +document.querySelectorAll('[data-clipboard]').forEach((button) => { + button.addEventListener('click', (event) => { + event.preventDefault(); + navigator.clipboard.writeText(button.dataset.clipboard).then(() => { + button.setAttribute('data-copied', ''); + window.setTimeout(() => button.removeAttribute('data-copied'), 1500); + }); + }); +}); + +/* --- Inspection: mark the event the URL hash points at ------------------ */ + +if (window.location.hash) { + const marker = document.getElementById('marker-' + window.location.hash.slice(1)); + marker?.classList.remove('invisible'); +} + +document.querySelectorAll('[data-add-hash]').forEach((element) => { + element.href += window.location.hash; +}); + +/* --- JSON highlighting -------------------------------------------------- */ + +function escapeHtml(text) { + return text.replace(/[<>&]/g, (char) => ({'<': '<', '>': '>', '&': '&'}[char])); +} + +function highlightJson(text) { + return text + .replace(/(("([^"]|\\")+?[^\\]")|"")(\s*.)/ig, (str, g1, g2, g3, g4) => { + const type = g4.trim() === ':' ? 'json_key' : 'json_string'; + return "" + escapeHtml(g1) + '' + g4; + }) + .replace(/-?\d+\.?\d*((E|e)[+]\d+)?/ig, "$&") + .replace(/false|true|null/ig, "$&"); +} + +document.querySelectorAll('.json').forEach((element) => { + element.innerHTML = highlightJson(element.innerHTML); +}); diff --git a/docs/getting-started.md b/docs/getting-started.md index aa69bbc..d37f05e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -72,6 +72,13 @@ bundle assets are picked up automatically. The bundle depends on `symfony/asset` `symfony/asset-mapper` for this. ::: +## Appearance + +The dashboard follows your operating system's color scheme by default. The switches at the +bottom of the sidebar let you pin it to light or dark, and choose between a fixed content width +and one that fits the window. Both choices are stored in the browser's local storage, so they +survive reloads and apply to every dashboard page. + ## Open the dashboard Start your application and visit the prefix you chose: diff --git a/docs/introduction.md b/docs/introduction.md index f90336f..496759c 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -17,6 +17,7 @@ production once it is placed behind proper authentication. * List all registered [events](events.md) together with their listeners and subscribers * View and control [subscriptions](subscriptions.md): boot, run, pause, reactivate, rebuild or remove * [Customize](events.md) how events are rendered with the `#[Inspect]` attribute +* Light and dark mode and a fixed or full content width, chosen in the sidebar ## Installation diff --git a/public/build/app.css b/public/build/app.css index 4d2f114..bb284ba 100644 --- a/public/build/app.css +++ b/public/build/app.css @@ -1,1832 +1,3 @@ -/*!**************************************************************************************************************************************************************************************!*\ - !*** css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[1].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[1].use[2]!./assets/app.css ***! - \**************************************************************************************************************************************************************************************/ -*, ::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 / 0.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 / 0.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: ; -}/* +*,:after,:before{--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:rgba(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:rgba(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: }/* ! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com -*//* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; /* 1 */ - border-width: 0; /* 2 */ - border-style: solid; /* 2 */ - border-color: #e5e7eb; /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -6. Use the user's configured `sans` font-variation-settings by default. -7. Disable tap highlights on iOS -*/ - -html, -:host { - line-height: 1.5; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ - -moz-tab-size: 4; /* 3 */ - -o-tab-size: 4; - tab-size: 4; /* 3 */ - font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */ - font-feature-settings: normal; /* 5 */ - font-variation-settings: normal; /* 6 */ - -webkit-tap-highlight-color: transparent; /* 7 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; /* 1 */ - line-height: inherit; /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; /* 1 */ - color: inherit; /* 2 */ - border-top-width: 1px; /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font-family by default. -2. Use the user's configured `mono` font-feature-settings by default. -3. Use the user's configured `mono` font-variation-settings by default. -4. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */ - font-feature-settings: normal; /* 2 */ - font-variation-settings: normal; /* 3 */ - font-size: 1em; /* 4 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; /* 1 */ - border-color: inherit; /* 2 */ - border-collapse: collapse; /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; /* 1 */ - font-feature-settings: inherit; /* 1 */ - font-variation-settings: inherit; /* 1 */ - font-size: 100%; /* 1 */ - font-weight: inherit; /* 1 */ - line-height: inherit; /* 1 */ - letter-spacing: inherit; /* 1 */ - color: inherit; /* 1 */ - margin: 0; /* 2 */ - padding: 0; /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -input:where([type='button']), -input:where([type='reset']), -input:where([type='submit']) { - -webkit-appearance: button; /* 1 */ - background-color: transparent; /* 2 */ - background-image: none; /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -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; -} - -/* -Reset default styling for dialogs. -*/ -dialog { - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; /* 1 */ - color: #9ca3af; /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; /* 1 */ - color: #9ca3af; /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; /* 1 */ - vertical-align: middle; /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ -[hidden]:where(:not([hidden="until-found"])) { - display: none; -} - -input:where([type='text']),input:where(:not([type])),input:where([type='email']),input:where([type='url']),input:where([type='password']),input:where([type='number']),input:where([type='date']),input:where([type='datetime-local']),input:where([type='month']),input:where([type='search']),input:where([type='tel']),input:where([type='time']),input:where([type='week']),select:where([multiple]),textarea,select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: #fff; - border-color: #6b7280; - border-width: 1px; - border-radius: 0px; - padding-top: 0.5rem; - padding-right: 0.75rem; - padding-bottom: 0.5rem; - padding-left: 0.75rem; - font-size: 1rem; - line-height: 1.5rem; - --tw-shadow: 0 0 #0000; -} - -input:where([type='text']):focus, input:where(:not([type])):focus, input:where([type='email']):focus, input:where([type='url']):focus, input:where([type='password']):focus, input:where([type='number']):focus, input:where([type='date']):focus, input:where([type='datetime-local']):focus, input:where([type='month']):focus, input:where([type='search']):focus, input:where([type='tel']):focus, input:where([type='time']):focus, input:where([type='week']):focus, select:where([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=%27http://www.w3.org/2000/svg%27 fill=%27none%27 viewBox=%270 0 20 20%27%3e%3cpath stroke=%27%236b7280%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%271.5%27 d=%27M6 8l4 4 4-4%27/%3e%3c/svg%3e"); - background-position: right 0.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; -} - -select:where([multiple]),select:where([size]:not([size="1"])) { - background-image: initial; - background-position: initial; - background-repeat: unset; - background-size: initial; - padding-right: 0.75rem; - -webkit-print-color-adjust: unset; - print-color-adjust: unset; -} - -input:where([type='checkbox']),input:where([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; -} - -input:where([type='checkbox']) { - border-radius: 0px; -} - -input:where([type='radio']) { - border-radius: 100%; -} - -input:where([type='checkbox']):focus,input:where([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); -} - -input:where([type='checkbox']):checked,input:where([type='radio']):checked { - border-color: transparent; - background-color: currentColor; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; -} - -input:where([type='checkbox']):checked { - background-image: url("data:image/svg+xml,%3csvg viewBox=%270 0 16 16%27 fill=%27white%27 xmlns=%27http://www.w3.org/2000/svg%27%3e%3cpath d=%27M12.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%27/%3e%3c/svg%3e"); -} - -@media (forced-colors: active) { - - input:where([type='checkbox']):checked { - -webkit-appearance: auto; - -moz-appearance: auto; - appearance: auto; - } -} - -input:where([type='radio']):checked { - background-image: url("data:image/svg+xml,%3csvg viewBox=%270 0 16 16%27 fill=%27white%27 xmlns=%27http://www.w3.org/2000/svg%27%3e%3ccircle cx=%278%27 cy=%278%27 r=%273%27/%3e%3c/svg%3e"); -} - -@media (forced-colors: active) { - - input:where([type='radio']):checked { - -webkit-appearance: auto; - -moz-appearance: auto; - appearance: auto; - } -} - -input:where([type='checkbox']):checked:hover,input:where([type='checkbox']):checked:focus,input:where([type='radio']):checked:hover,input:where([type='radio']):checked:focus { - border-color: transparent; - background-color: currentColor; -} - -input:where([type='checkbox']):indeterminate { - background-image: url("data:image/svg+xml,%3csvg xmlns=%27http://www.w3.org/2000/svg%27 fill=%27none%27 viewBox=%270 0 16 16%27%3e%3cpath stroke=%27white%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%272%27 d=%27M4 8h8%27/%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) { - - input:where([type='checkbox']):indeterminate { - -webkit-appearance: auto; - -moz-appearance: auto; - appearance: auto; - } -} - -input:where([type='checkbox']):indeterminate:hover,input:where([type='checkbox']):indeterminate:focus { - border-color: transparent; - background-color: currentColor; -} - -input:where([type='file']) { - background: unset; - border-color: inherit; - border-width: 0; - border-radius: 0; - padding: 0; - font-size: unset; - line-height: inherit; -} - -input:where([type='file']):focus { - outline: 1px solid ButtonText; - outline: 1px auto -webkit-focus-ring-color; -} -.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; -} -.visible { - visibility: visible; -} -.invisible { - visibility: hidden; -} -.static { - position: static; -} -.fixed { - position: fixed; -} -.absolute { - position: absolute; -} -.relative { - position: relative; -} -.sticky { - position: sticky; -} -.inset-0 { - inset: 0px; -} -.inset-x-0 { - left: 0px; - right: 0px; -} -.-left-10 { - left: -2.5rem; -} -.-top-48 { - top: -12rem; -} -.-top-px { - top: -1px; -} -.bottom-0 { - bottom: 0px; -} -.left-4 { - left: 1rem; -} -.right-0 { - right: 0px; -} -.top-0 { - top: 0px; -} -.top-4 { - top: 1rem; -} -.z-10 { - z-index: 10; -} -.z-20 { - z-index: 20; -} -.z-40 { - z-index: 40; -} -.-m-2\.5 { - margin: -0.625rem; -} -.-mx-2 { - margin-left: -0.5rem; - margin-right: -0.5rem; -} -.-mx-4 { - margin-left: -1rem; - margin-right: -1rem; -} -.-mx-6 { - margin-left: -1.5rem; - margin-right: -1.5rem; -} -.-my-2 { - margin-top: -0.5rem; - margin-bottom: -0.5rem; -} -.mx-auto { - margin-left: auto; - margin-right: auto; -} -.-mb-8 { - margin-bottom: -2rem; -} -.-ml-0\.5 { - margin-left: -0.125rem; -} -.-ml-px { - margin-left: -1px; -} -.-mt-1 { - margin-top: -0.25rem; -} -.-mt-px { - margin-top: -1px; -} -.mb-2 { - margin-bottom: 0.5rem; -} -.mb-8 { - margin-bottom: 2rem; -} -.ml-3 { - margin-left: 0.75rem; -} -.mr-2 { - margin-right: 0.5rem; -} -.mr-3 { - margin-right: 0.75rem; -} -.mr-4 { - margin-right: 1rem; -} -.mt-0\.5 { - margin-top: 0.125rem; -} -.mt-1 { - margin-top: 0.25rem; -} -.mt-10 { - margin-top: 2.5rem; -} -.mt-2 { - margin-top: 0.5rem; -} -.mt-3 { - margin-top: 0.75rem; -} -.mt-4 { - margin-top: 1rem; -} -.mt-5 { - margin-top: 1.25rem; -} -.mt-8 { - margin-top: 2rem; -} -.mt-auto { - margin-top: auto; -} -.block { - display: block; -} -.inline-block { - display: inline-block; -} -.inline { - display: inline; -} -.flex { - display: flex; -} -.inline-flex { - display: inline-flex; -} -.table { - display: table; -} -.flow-root { - display: flow-root; -} -.grid { - display: grid; -} -.hidden { - display: none; -} -.size-5 { - width: 1.25rem; - height: 1.25rem; -} -.h-1\.5 { - height: 0.375rem; -} -.h-12 { - height: 3rem; -} -.h-20 { - height: 5rem; -} -.h-4 { - height: 1rem; -} -.h-5 { - height: 1.25rem; -} -.h-6 { - height: 1.5rem; -} -.h-8 { - height: 2rem; -} -.h-full { - height: 100%; -} -.min-h-full { - min-height: 100%; -} -.w-0 { - width: 0px; -} -.w-0\.5 { - width: 0.125rem; -} -.w-1\.5 { - width: 0.375rem; -} -.w-1\/2 { - width: 50%; -} -.w-1\/3 { - width: 33.333333%; -} -.w-12 { - width: 3rem; -} -.w-2\/3 { - width: 66.666667%; -} -.w-3\/4 { - width: 75%; -} -.w-4 { - width: 1rem; -} -.w-48 { - width: 12rem; -} -.w-5 { - width: 1.25rem; -} -.w-6 { - width: 1.5rem; -} -.w-8 { - width: 2rem; -} -.w-96 { - width: 24rem; -} -.w-full { - width: 100%; -} -.w-screen { - width: 100vw; -} -.min-w-0 { - min-width: 0px; -} -.min-w-full { - min-width: 100%; -} -.flex-1 { - flex: 1 1 0%; -} -.flex-auto { - flex: 1 1 auto; -} -.flex-none { - flex: none; -} -.flex-shrink-0 { - flex-shrink: 0; -} -.shrink-0 { - flex-shrink: 0; -} -.grow { - flex-grow: 1; -} -.translate-y-0 { - --tw-translate-y: 0px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} -.translate-y-4 { - --tw-translate-y: 1rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(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)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} -.cursor-help { - cursor: help; -} -.grid-cols-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); -} -.flex-col { - flex-direction: column; -} -.flex-wrap { - flex-wrap: wrap; -} -.place-content-between { - place-content: space-between; -} -.items-start { - align-items: flex-start; -} -.items-end { - align-items: flex-end; -} -.items-center { - align-items: center; -} -.justify-end { - justify-content: flex-end; -} -.justify-center { - justify-content: center; -} -.justify-between { - justify-content: space-between; -} -.gap-1 { - gap: 0.25rem; -} -.gap-2 { - gap: 0.5rem; -} -.gap-3 { - gap: 0.75rem; -} -.gap-4 { - gap: 1rem; -} -.gap-x-1\.5 { - -moz-column-gap: 0.375rem; - column-gap: 0.375rem; -} -.gap-x-2 { - -moz-column-gap: 0.5rem; - column-gap: 0.5rem; -} -.gap-x-3 { - -moz-column-gap: 0.75rem; - column-gap: 0.75rem; -} -.gap-x-4 { - -moz-column-gap: 1rem; - column-gap: 1rem; -} -.gap-x-6 { - -moz-column-gap: 1.5rem; - column-gap: 1.5rem; -} -.gap-y-5 { - row-gap: 1.25rem; -} -.gap-y-7 { - row-gap: 1.75rem; -} -.space-x-3 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.75rem * var(--tw-space-x-reverse)); - margin-left: calc(0.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-y-1 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.25rem * 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-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, 1)); -} -.divide-gray-200 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 1; - border-color: rgb(229 231 235 / var(--tw-divide-opacity, 1)); -} -.divide-gray-300 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 1; - border-color: rgb(209 213 219 / var(--tw-divide-opacity, 1)); -} -.overflow-hidden { - overflow: hidden; -} -.overflow-x-auto { - overflow-x: auto; -} -.overflow-y-auto { - overflow-y: auto; -} -.overflow-ellipsis { - text-overflow: ellipsis; -} -.whitespace-nowrap { - white-space: nowrap; -} -.text-nowrap { - text-wrap: nowrap; -} -.rounded { - border-radius: 0.25rem; -} -.rounded-full { - border-radius: 9999px; -} -.rounded-lg { - border-radius: 0.5rem; -} -.rounded-md { - border-radius: 0.375rem; -} -.border-0 { - border-width: 0px; -} -.border-t { - border-top-width: 1px; -} -.border-t-2 { - border-top-width: 2px; -} -.border-gray-200 { - --tw-border-opacity: 1; - border-color: rgb(229 231 235 / var(--tw-border-opacity, 1)); -} -.border-indigo-500 { - --tw-border-opacity: 1; - border-color: rgb(99 102 241 / var(--tw-border-opacity, 1)); -} -.border-transparent { - border-color: transparent; -} -.bg-blue-100 { - --tw-bg-opacity: 1; - background-color: rgb(219 234 254 / var(--tw-bg-opacity, 1)); -} -.bg-gray-100 { - --tw-bg-opacity: 1; - background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1)); -} -.bg-gray-200 { - --tw-bg-opacity: 1; - background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1)); -} -.bg-gray-400 { - --tw-bg-opacity: 1; - background-color: rgb(156 163 175 / var(--tw-bg-opacity, 1)); -} -.bg-gray-50 { - --tw-bg-opacity: 1; - background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1)); -} -.bg-gray-500 { - --tw-bg-opacity: 1; - background-color: rgb(107 114 128 / var(--tw-bg-opacity, 1)); -} -.bg-green-100 { - --tw-bg-opacity: 1; - background-color: rgb(220 252 231 / var(--tw-bg-opacity, 1)); -} -.bg-green-50 { - --tw-bg-opacity: 1; - background-color: rgb(240 253 244 / var(--tw-bg-opacity, 1)); -} -.bg-indigo-600 { - --tw-bg-opacity: 1; - background-color: rgb(79 70 229 / var(--tw-bg-opacity, 1)); -} -.bg-indigo-700 { - --tw-bg-opacity: 1; - background-color: rgb(67 56 202 / var(--tw-bg-opacity, 1)); -} -.bg-red-50 { - --tw-bg-opacity: 1; - background-color: rgb(254 242 242 / var(--tw-bg-opacity, 1)); -} -.bg-red-600 { - --tw-bg-opacity: 1; - background-color: rgb(220 38 38 / var(--tw-bg-opacity, 1)); -} -.bg-white { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1)); -} -.bg-white\/10 { - background-color: rgb(255 255 255 / 0.1); -} -.bg-yellow-50 { - --tw-bg-opacity: 1; - background-color: rgb(254 252 232 / var(--tw-bg-opacity, 1)); -} -.bg-opacity-75 { - --tw-bg-opacity: 0.75; -} -.fill-blue-500 { - fill: #3b82f6; -} -.fill-gray-500 { - fill: #6b7280; -} -.fill-green-500 { - fill: #22c55e; -} -.fill-red-500 { - fill: #ef4444; -} -.fill-yellow-500 { - fill: #eab308; -} -.p-2 { - padding: 0.5rem; -} -.p-2\.5 { - padding: 0.625rem; -} -.p-3 { - padding: 0.75rem; -} -.p-4 { - padding: 1rem; -} -.p-6 { - padding: 1.5rem; -} -.p-8 { - padding: 2rem; -} -.px-2 { - padding-left: 0.5rem; - padding-right: 0.5rem; -} -.px-2\.5 { - padding-left: 0.625rem; - padding-right: 0.625rem; -} -.px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} -.px-4 { - padding-left: 1rem; - padding-right: 1rem; -} -.px-6 { - padding-left: 1.5rem; - padding-right: 1.5rem; -} -.py-1 { - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} -.py-1\.5 { - padding-top: 0.375rem; - padding-bottom: 0.375rem; -} -.py-10 { - padding-top: 2.5rem; - padding-bottom: 2.5rem; -} -.py-12 { - padding-top: 3rem; - padding-bottom: 3rem; -} -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} -.py-24 { - padding-top: 6rem; - padding-bottom: 6rem; -} -.py-3 { - padding-top: 0.75rem; - padding-bottom: 0.75rem; -} -.py-3\.5 { - padding-top: 0.875rem; - padding-bottom: 0.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; -} -.pb-4 { - padding-bottom: 1rem; -} -.pb-8 { - padding-bottom: 2rem; -} -.pl-3 { - padding-left: 0.75rem; -} -.pl-4 { - padding-left: 1rem; -} -.pr-10 { - padding-right: 2.5rem; -} -.pr-3 { - padding-right: 0.75rem; -} -.pr-4 { - padding-right: 1rem; -} -.pt-1\.5 { - padding-top: 0.375rem; -} -.pt-5 { - padding-top: 1.25rem; -} -.text-left { - text-align: left; -} -.text-center { - text-align: center; -} -.text-right { - text-align: right; -} -.align-middle { - vertical-align: middle; -} -.text-2xl { - font-size: 1.5rem; - line-height: 2rem; -} -.text-base { - font-size: 1rem; - line-height: 1.5rem; -} -.text-sm { - font-size: 0.875rem; - line-height: 1.25rem; -} -.text-sm\/6 { - font-size: 0.875rem; - line-height: 1.5rem; -} -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} -.text-xs { - font-size: 0.75rem; - line-height: 1rem; -} -.text-xs\/5 { - font-size: 0.75rem; - line-height: 1.25rem; -} -.font-bold { - font-weight: 700; -} -.font-medium { - font-weight: 500; -} -.font-semibold { - font-weight: 600; -} -.capitalize { - text-transform: capitalize; -} -.tabular-nums { - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction); -} -.leading-6 { - line-height: 1.5rem; -} -.leading-8 { - line-height: 2rem; -} -.text-gray-400 { - --tw-text-opacity: 1; - color: rgb(156 163 175 / var(--tw-text-opacity, 1)); -} -.text-gray-500 { - --tw-text-opacity: 1; - color: rgb(107 114 128 / var(--tw-text-opacity, 1)); -} -.text-gray-600 { - --tw-text-opacity: 1; - color: rgb(75 85 99 / var(--tw-text-opacity, 1)); -} -.text-gray-700 { - --tw-text-opacity: 1; - color: rgb(55 65 81 / var(--tw-text-opacity, 1)); -} -.text-gray-900 { - --tw-text-opacity: 1; - color: rgb(17 24 39 / var(--tw-text-opacity, 1)); -} -.text-green-600 { - --tw-text-opacity: 1; - color: rgb(22 163 74 / var(--tw-text-opacity, 1)); -} -.text-green-700 { - --tw-text-opacity: 1; - color: rgb(21 128 61 / var(--tw-text-opacity, 1)); -} -.text-indigo-200 { - --tw-text-opacity: 1; - color: rgb(199 210 254 / var(--tw-text-opacity, 1)); -} -.text-indigo-600 { - --tw-text-opacity: 1; - color: rgb(79 70 229 / var(--tw-text-opacity, 1)); -} -.text-red-400 { - --tw-text-opacity: 1; - color: rgb(248 113 113 / var(--tw-text-opacity, 1)); -} -.text-red-700 { - --tw-text-opacity: 1; - color: rgb(185 28 28 / var(--tw-text-opacity, 1)); -} -.text-red-800 { - --tw-text-opacity: 1; - color: rgb(153 27 27 / var(--tw-text-opacity, 1)); -} -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity, 1)); -} -.text-yellow-500 { - --tw-text-opacity: 1; - color: rgb(234 179 8 / var(--tw-text-opacity, 1)); -} -.text-yellow-800 { - --tw-text-opacity: 1; - color: rgb(133 77 14 / var(--tw-text-opacity, 1)); -} -.opacity-0 { - opacity: 0; -} -.opacity-100 { - opacity: 1; -} -.shadow { - --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 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-sm { - --tw-shadow: 0 1px 2px 0 rgb(0 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 / 0.1), 0 8px 10px -6px rgb(0 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); -} -.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-8 { - --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(8px + 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-black { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity, 1)); -} -.ring-gray-200 { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(229 231 235 / var(--tw-ring-opacity, 1)); -} -.ring-gray-300 { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity, 1)); -} -.ring-gray-500\/10 { - --tw-ring-color: rgb(107 114 128 / 0.1); -} -.ring-gray-900\/5 { - --tw-ring-color: rgb(17 24 39 / 0.05); -} -.ring-green-600\/20 { - --tw-ring-color: rgb(22 163 74 / 0.2); -} -.ring-red-600\/20 { - --tw-ring-color: rgb(220 38 38 / 0.2); -} -.ring-white { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(255 255 255 / var(--tw-ring-opacity, 1)); -} -.ring-yellow-600\/20 { - --tw-ring-color: rgb(202 138 4 / 0.2); -} -.ring-opacity-5 { - --tw-ring-opacity: 0.05; -} -.saturate-0 { - --tw-saturate: saturate(0); - 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-all { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} -.transition-opacity { - transition-property: opacity; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} -.duration-200 { - transition-duration: 200ms; -} -.duration-300 { - transition-duration: 300ms; -} -.ease-in { - transition-timing-function: cubic-bezier(0.4, 0, 1, 1); -} -.ease-out { - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); -} -.placeholder\:text-gray-400::-moz-placeholder { - --tw-text-opacity: 1; - color: rgb(156 163 175 / var(--tw-text-opacity, 1)); -} -.placeholder\:text-gray-400::placeholder { - --tw-text-opacity: 1; - color: rgb(156 163 175 / var(--tw-text-opacity, 1)); -} -.hover\:border-gray-300:hover { - --tw-border-opacity: 1; - border-color: rgb(209 213 219 / var(--tw-border-opacity, 1)); -} -.hover\:bg-gray-50:hover { - --tw-bg-opacity: 1; - background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1)); -} -.hover\:bg-indigo-500:hover { - --tw-bg-opacity: 1; - background-color: rgb(99 102 241 / var(--tw-bg-opacity, 1)); -} -.hover\:bg-indigo-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(67 56 202 / var(--tw-bg-opacity, 1)); -} -.hover\:bg-red-500:hover { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1)); -} -.hover\:bg-white\/20:hover { - background-color: rgb(255 255 255 / 0.2); -} -.hover\:text-gray-700:hover { - --tw-text-opacity: 1; - color: rgb(55 65 81 / var(--tw-text-opacity, 1)); -} -.hover\:text-gray-900:hover { - --tw-text-opacity: 1; - color: rgb(17 24 39 / var(--tw-text-opacity, 1)); -} -.hover\:text-indigo-500:hover { - --tw-text-opacity: 1; - color: rgb(99 102 241 / var(--tw-text-opacity, 1)); -} -.hover\:text-white:hover { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity, 1)); -} -.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-inset:focus { - --tw-ring-inset: inset; -} -.focus\:ring-indigo-600:focus { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(79 70 229 / var(--tw-ring-opacity, 1)); -} -.focus-visible\:outline:focus-visible { - outline-style: solid; -} -.focus-visible\:outline-2:focus-visible { - outline-width: 2px; -} -.focus-visible\:outline-offset-2:focus-visible { - outline-offset: 2px; -} -.focus-visible\:outline-indigo-600:focus-visible { - outline-color: #4f46e5; -} -.focus-visible\:outline-red-600:focus-visible { - outline-color: #dc2626; -} -.group\/message:hover .group-hover\/message\:visible { - visibility: visible; -} -.group\/parent:hover .group-hover\/parent\:visible { - visibility: visible; -} -.group:hover .group-hover\:text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity, 1)); -} -@media (min-width: 640px) { - - .sm\:col-span-2 { - grid-column: span 2 / span 2; - } - - .sm\:-mx-6 { - margin-left: -1.5rem; - margin-right: -1.5rem; - } - - .sm\:mx-auto { - margin-left: auto; - margin-right: auto; - } - - .sm\:my-8 { - margin-top: 2rem; - margin-bottom: 2rem; - } - - .sm\:mt-0 { - margin-top: 0px; - } - - .sm\:mt-5 { - margin-top: 1.25rem; - } - - .sm\:mt-6 { - margin-top: 1.5rem; - } - - .sm\:grid { - display: grid; - } - - .sm\:w-full { - width: 100%; - } - - .sm\:max-w-\[480px\] { - max-width: 480px; - } - - .sm\:max-w-sm { - max-width: 24rem; - } - - .sm\:translate-y-0 { - --tw-translate-y: 0px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - - .sm\:scale-100 { - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - - .sm\:scale-95 { - --tw-scale-x: .95; - --tw-scale-y: .95; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - - .sm\:grid-cols-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .sm\:grid-cols-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - - .sm\:items-center { - align-items: center; - } - - .sm\:gap-4 { - gap: 1rem; - } - - .sm\:rounded-lg { - border-radius: 0.5rem; - } - - .sm\:rounded-xl { - border-radius: 0.75rem; - } - - .sm\:p-0 { - padding: 0px; - } - - .sm\:p-6 { - padding: 1.5rem; - } - - .sm\:px-0 { - padding-left: 0px; - padding-right: 0px; - } - - .sm\:px-12 { - padding-left: 3rem; - padding-right: 3rem; - } - - .sm\:px-6 { - padding-left: 1.5rem; - padding-right: 1.5rem; - } - - .sm\:pl-6 { - padding-left: 1.5rem; - } - - .sm\:pr-6 { - padding-right: 1.5rem; - } - - .sm\:text-sm { - font-size: 0.875rem; - line-height: 1.25rem; - } - - .sm\:leading-6 { - line-height: 1.5rem; - } -} -@media (min-width: 768px) { - - .md\:-mt-px { - margin-top: -1px; - } - - .md\:flex { - display: flex; - } -} -@media (min-width: 1024px) { - - .lg\:fixed { - position: fixed; - } - - .lg\:inset-y-0 { - top: 0px; - bottom: 0px; - } - - .lg\:z-50 { - z-index: 50; - } - - .lg\:-mx-8 { - margin-left: -2rem; - margin-right: -2rem; - } - - .lg\:flex { - display: flex; - } - - .lg\:hidden { - display: none; - } - - .lg\:w-72 { - width: 18rem; - } - - .lg\:grid-cols-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - - .lg\:flex-col { - flex-direction: column; - } - - .lg\:px-8 { - padding-left: 2rem; - padding-right: 2rem; - } - - .lg\:pl-72 { - padding-left: 18rem; - } -} - +*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:Inter,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;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}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,pre,samp{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height: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,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}@font-face{font-display:swap;font-family:Inter;font-style:normal;font-weight:100 900;src:url(../fonts/InterVariable.woff2) format("woff2")}@font-face{font-display:swap;font-family:JetBrains Mono;font-style:normal;font-weight:400;src:url(../fonts/JetBrainsMono-Regular.woff2) format("woff2")}:root{--color-primary-light:52 130 239;--color-secondary-light:130 103 243;--color-tertiary-light:21 211 241;color-scheme:dark}:root[data-theme=light]{--color-primary-light:17 102 221;--color-secondary-light:99 65 240;--color-tertiary-light:14 116 144;color-scheme:light}html{background-color:rgb(2 6 23/var(--tw-bg-opacity,1));color:rgb(209 213 219/var(--tw-text-opacity,1));height:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;scrollbar-gutter:stable}html,html[data-theme=light]{--tw-bg-opacity:1;--tw-text-opacity:1}html[data-theme=light]{background-color:rgb(248 250 252/var(--tw-bg-opacity,1));color:rgb(71 85 105/var(--tw-text-opacity,1))}body{font-family:Inter,ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;height:100%}:where(pre,code,kbd,samp,.font-mono){font-feature-settings:"liga" 0,"calt" 0;font-variant-ligatures:none}::-moz-selection{background-color:#1e3a8a}::selection{background-color:#1e3a8a}:root[data-theme=light] ::-moz-selection{background-color:#bfdbfe}:root[data-theme=light] ::selection{background-color:#bfdbfe}*{scrollbar-color:#334155 transparent;scrollbar-width:thin}:root[data-theme=light] *{scrollbar-color:#cbd5e1 transparent}@media (prefers-reduced-motion:reduce){*,:after,:before{animation-duration:.01ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-duration:.01ms!important}}.form-input,.form-multiselect,.form-select,.form-textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem;--tw-shadow:0 0 #0000}.form-input:focus,.form-multiselect:focus,.form-select:focus,.form-textarea: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);border-color:#2563eb;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.form-input::-moz-placeholder,.form-textarea::-moz-placeholder{color:#6b7280;opacity:1}.form-input::placeholder,.form-textarea::placeholder{color:#6b7280;opacity:1}.form-input::-webkit-datetime-edit-fields-wrapper{padding:0}.form-input::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}.form-input::-webkit-datetime-edit{display:inline-flex}.form-input::-webkit-datetime-edit,.form-input::-webkit-datetime-edit-day-field,.form-input::-webkit-datetime-edit-hour-field,.form-input::-webkit-datetime-edit-meridiem-field,.form-input::-webkit-datetime-edit-millisecond-field,.form-input::-webkit-datetime-edit-minute-field,.form-input::-webkit-datetime-edit-month-field,.form-input::-webkit-datetime-edit-second-field,.form-input::-webkit-datetime-edit-year-field{padding-bottom:0;padding-top:0}.form-select{background-image:url("data:image/svg+xml;charset=utf-8,%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 8 4 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}.form-select:where([size]:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}.code-window{border-radius:.75rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(2 6 23/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}[data-theme=light] .code-window,[data-theme=light].code-window{--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity,1))}:where(pre,code,kbd,samp,.json){font-feature-settings:"liga" 0,"calt" 0;font-variant-ligatures:none}.json{border-radius:.75rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(2 6 23/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}[data-theme=light] .json,[data-theme=light].json{--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity,1))}.json{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.75rem;line-height:1rem;line-height:1.625;overflow-x:auto;padding:.75rem 1rem;white-space:pre}.json_key,.json_key span{--tw-text-opacity:1;color:rgb(var(--color-tertiary-light)/var(--tw-text-opacity,1))}[data-theme=light] .json_key,[data-theme=light] .json_key span,[data-theme=light].json_key{--tw-text-opacity:1;color:rgb(21 211 241/var(--tw-text-opacity,1))}.json_string,.json_string span{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.json_number{--tw-text-opacity:1;color:rgb(var(--color-primary-light)/var(--tw-text-opacity,1))}[data-theme=light] .json_number,[data-theme=light].json_number{--tw-text-opacity:1;color:rgb(52 130 239/var(--tw-text-opacity,1))}.json_bool{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.sr-only{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border-width:0;white-space:nowrap}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-y-0{bottom:0;top:0}.-left-9{left:-2.25rem}.-top-24{top:-6rem}.left-0{left:0}.left-4{left:1rem}.top-0{top:0}.top-1{top:.25rem}.top-8{top:2rem}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.-mb-px{margin-bottom:-1px}.-ml-0\.5{margin-left:-.125rem}.-ml-2{margin-left:-.5rem}.-ml-px{margin-left:-1px}.-mr-2{margin-right:-.5rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-5{margin-bottom:1.25rem}.ml-1{margin-left:.25rem}.ml-auto{margin-left:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.max-h-\[85vh\]{max-height:85vh}.max-h-\[calc\(85vh-3\.75rem\)\]{max-height:calc(85vh - 3.75rem)}.w-0\.5{width:.125rem}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-3\.5{width:.875rem}.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-\[min\(64rem\2c calc\(100vw-2rem\)\)\]{width:min(64rem,calc(100vw - 2rem))}.w-full{width:100%}.min-w-0{min-width:0}.min-w-full{min-width:100%}.max-w-md{max-width:28rem}.max-w-screen-2xl{max-width:1536px}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-translate-x-full{--tw-translate-x:-100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-px{gap:1px}.gap-x-1\.5{-moz-column-gap:.375rem;column-gap:.375rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.25rem*var(--tw-space-y-reverse));margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-slate-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(30 41 59/var(--tw-divide-opacity,1))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.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}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-0{border-width:0}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-amber-900{--tw-border-opacity:1;border-color:rgb(120 53 15/var(--tw-border-opacity,1))}.border-blue-900{--tw-border-opacity:1;border-color:rgb(30 58 138/var(--tw-border-opacity,1))}.border-green-900{--tw-border-opacity:1;border-color:rgb(20 83 45/var(--tw-border-opacity,1))}.border-primary-light{--tw-border-opacity:1;border-color:rgb(var(--color-primary-light)/var(--tw-border-opacity,1))}.border-red-900{--tw-border-opacity:1;border-color:rgb(127 29 29/var(--tw-border-opacity,1))}.border-slate-700{--tw-border-opacity:1;border-color:rgb(51 65 85/var(--tw-border-opacity,1))}.border-slate-800{--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity,1))}.border-transparent{border-color:transparent}.bg-amber-950{--tw-bg-opacity:1;background-color:rgb(69 26 3/var(--tw-bg-opacity,1))}.bg-blue-950{--tw-bg-opacity:1;background-color:rgb(23 37 84/var(--tw-bg-opacity,1))}.bg-current{background-color:currentColor}.bg-cyan-950{--tw-bg-opacity:1;background-color:rgb(8 51 68/var(--tw-bg-opacity,1))}.bg-green-950{--tw-bg-opacity:1;background-color:rgb(5 46 22/var(--tw-bg-opacity,1))}.bg-primary{--tw-bg-opacity:1;background-color:rgb(17 102 221/var(--tw-bg-opacity,1))}.bg-red-950{--tw-bg-opacity:1;background-color:rgb(69 10 10/var(--tw-bg-opacity,1))}.bg-slate-600{--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity,1))}.bg-slate-800{--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity,1))}.bg-slate-900{--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity,1))}.bg-slate-950\/70{background-color:rgba(2,6,23,.7)}.bg-violet-950{--tw-bg-opacity:1;background-color:rgb(46 16 101/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-secondary{--tw-gradient-from:#6341f0 var(--tw-gradient-from-position);--tw-gradient-to:rgba(99,65,240,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-primary{--tw-gradient-to:#16d var(--tw-gradient-to-position)}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.py-16{padding-bottom:4rem;padding-top:4rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-2\.5{padding-bottom:.625rem;padding-top:.625rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-8{padding-bottom:2rem;padding-top:2rem}.pb-0{padding-bottom:0}.pb-6{padding-bottom:1.5rem}.pl-3{padding-left:.75rem}.pr-10{padding-right:2.5rem}.pt-1{padding-top:.25rem}.pt-1\.5{padding-top:.375rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.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-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-5{line-height:1.25rem}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-amber-200{--tw-text-opacity:1;color:rgb(253 230 138/var(--tw-text-opacity,1))}.text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.text-blue-100{--tw-text-opacity:1;color:rgb(219 234 254/var(--tw-text-opacity,1))}.text-blue-200{--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity,1))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-green-300{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.text-primary{--tw-text-opacity:1;color:rgb(17 102 221/var(--tw-text-opacity,1))}.text-primary-light{--tw-text-opacity:1;color:rgb(var(--color-primary-light)/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-secondary-light{--tw-text-opacity:1;color:rgb(var(--color-secondary-light)/var(--tw-text-opacity,1))}.text-tertiary-light{--tw-text-opacity:1;color:rgb(var(--color-tertiary-light)/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.opacity-0{opacity:0}.opacity-40{opacity:.4}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.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-inset{--tw-ring-inset:inset}.ring-amber-900{--tw-ring-opacity:1;--tw-ring-color:rgb(120 53 15/var(--tw-ring-opacity,1))}.ring-blue-900{--tw-ring-opacity:1;--tw-ring-color:rgb(30 58 138/var(--tw-ring-opacity,1))}.ring-green-900{--tw-ring-opacity:1;--tw-ring-color:rgb(20 83 45/var(--tw-ring-opacity,1))}.ring-red-900{--tw-ring-opacity:1;--tw-ring-color:rgb(127 29 29/var(--tw-ring-opacity,1))}.ring-slate-700{--tw-ring-opacity:1;--tw-ring-color:rgb(51 65 85/var(--tw-ring-opacity,1))}.saturate-0{--tw-saturate:saturate(0)}.filter,.saturate-0{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-200{transition-duration:.2s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}dialog[open]{animation:dialog-in .2s cubic-bezier(.16,1,.3,1)}dialog::backdrop{backdrop-filter:blur(4px);background:rgba(2,6,23,.7)}:root[data-theme=light] dialog::backdrop{background:rgba(15,23,42,.4)}@keyframes dialog-in{0%{opacity:0;transform:translateY(8px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}.placeholder\:text-gray-500::-moz-placeholder{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.placeholder\:text-gray-500::placeholder{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.focus-within\:opacity-100:focus-within{opacity:1}.hover\:border-slate-600:hover{--tw-border-opacity:1;border-color:rgb(71 85 105/var(--tw-border-opacity,1))}.hover\:bg-blue-50:hover{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.hover\:bg-slate-700:hover{--tw-bg-opacity:1;background-color:rgb(51 65 85/var(--tw-bg-opacity,1))}.hover\:bg-slate-800:hover{--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity,1))}.hover\:text-gray-300:hover{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:brightness-125:hover{--tw-brightness:brightness(1.25);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)}.focus\:border-primary-light:focus{--tw-border-opacity:1;border-color:rgb(var(--color-primary-light)/var(--tw-border-opacity,1))}.focus\:ring-primary-light:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(var(--color-primary-light)/var(--tw-ring-opacity,1))}.focus-visible\:opacity-100:focus-visible{opacity:1}.focus-visible\:outline-2:focus-visible{outline-width:2px}.focus-visible\:-outline-offset-2:focus-visible{outline-offset:-2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\:outline-primary-light:focus-visible{outline-color:rgb(var(--color-primary-light)/1)}.focus-visible\:outline-white:focus-visible{outline-color:#fff}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/message:hover .group-hover\/message\:opacity-100,.group\/parent:hover .group-hover\/parent\:opacity-100{opacity:1}.aria-checked\:bg-slate-700[aria-checked=true]{--tw-bg-opacity:1;background-color:rgb(51 65 85/var(--tw-bg-opacity,1))}.aria-checked\:text-white[aria-checked=true]{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.data-\[copied\]\:opacity-100[data-copied]{opacity:1}.group\/copy[data-copied] .group-data-\[copied\]\/copy\:inline{display:inline}.group\/copy[data-copied] .group-data-\[copied\]\/copy\:hidden{display:none}[data-theme=light] .light\:block{display:block}[data-theme=light] .light\:hidden{display:none}[data-theme=light] .light\:divide-slate-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(241 245 249/var(--tw-divide-opacity,1))}[data-theme=light] .light\:divide-slate-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(226 232 240/var(--tw-divide-opacity,1))}[data-theme=light] .light\:divide-slate-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(30 41 59/var(--tw-divide-opacity,1))}[data-theme=light] .light\:border-amber-200{--tw-border-opacity:1;border-color:rgb(253 230 138/var(--tw-border-opacity,1))}[data-theme=light] .light\:border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity,1))}[data-theme=light] .light\:border-green-200{--tw-border-opacity:1;border-color:rgb(187 247 208/var(--tw-border-opacity,1))}[data-theme=light] .light\:border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}[data-theme=light] .light\:border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity,1))}[data-theme=light] .light\:border-slate-300{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity,1))}[data-theme=light] .light\:border-slate-800{--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity,1))}[data-theme=light] .light\:bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}[data-theme=light] .light\:bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}[data-theme=light] .light\:bg-cyan-50{--tw-bg-opacity:1;background-color:rgb(236 254 255/var(--tw-bg-opacity,1))}[data-theme=light] .light\:bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}[data-theme=light] .light\:bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}[data-theme=light] .light\:bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}[data-theme=light] .light\:bg-slate-200{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity,1))}[data-theme=light] .light\:bg-slate-400{--tw-bg-opacity:1;background-color:rgb(148 163 184/var(--tw-bg-opacity,1))}[data-theme=light] .light\:bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}[data-theme=light] .light\:bg-transparent{background-color:transparent}[data-theme=light] .light\:bg-violet-50{--tw-bg-opacity:1;background-color:rgb(245 243 255/var(--tw-bg-opacity,1))}[data-theme=light] .light\:bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}[data-theme=light] .light\:text-\[\#15d3f1\]{--tw-text-opacity:1;color:rgb(21 211 241/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-amber-700{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-green-800{--tw-text-opacity:1;color:rgb(22 101 52/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-slate-300{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-slate-800{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity,1))}[data-theme=light] .light\:text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}[data-theme=light] .light\:shadow-sm{--tw-shadow:0 1px 2px 0 rgba(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)}[data-theme=light] .light\:ring-amber-200{--tw-ring-opacity:1;--tw-ring-color:rgb(253 230 138/var(--tw-ring-opacity,1))}[data-theme=light] .light\:ring-blue-200{--tw-ring-opacity:1;--tw-ring-color:rgb(191 219 254/var(--tw-ring-opacity,1))}[data-theme=light] .light\:ring-green-200{--tw-ring-opacity:1;--tw-ring-color:rgb(187 247 208/var(--tw-ring-opacity,1))}[data-theme=light] .light\:ring-red-200{--tw-ring-opacity:1;--tw-ring-color:rgb(254 202 202/var(--tw-ring-opacity,1))}[data-theme=light] .light\:ring-slate-200{--tw-ring-opacity:1;--tw-ring-color:rgb(226 232 240/var(--tw-ring-opacity,1))}[data-theme=light] .light\:placeholder\:text-slate-400::-moz-placeholder{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}[data-theme=light] .light\:placeholder\:text-slate-400::placeholder{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}[data-theme=light] .light\:hover\:border-slate-300:hover{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity,1))}[data-theme=light] .light\:hover\:bg-slate-100:hover{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}[data-theme=light] .light\:hover\:bg-slate-200:hover{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity,1))}[data-theme=light] .light\:hover\:bg-slate-50:hover{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}[data-theme=light] .light\:hover\:text-primary:hover{--tw-text-opacity:1;color:rgb(17 102 221/var(--tw-text-opacity,1))}[data-theme=light] .light\:hover\:text-slate-800:hover{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity,1))}[data-theme=light] .light\:hover\:text-slate-900:hover{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}[data-theme=light] .light\:hover\:brightness-95:hover{--tw-brightness:brightness(.95);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)}[data-theme=light] .light\:aria-checked\:bg-white[aria-checked=true]{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}[data-theme=light] .light\:aria-checked\:text-slate-900[aria-checked=true]{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}[data-theme=light] .light\:aria-checked\:shadow-sm[aria-checked=true]{--tw-shadow:0 1px 2px 0 rgba(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)}[data-theme=light].light\:block{display:block}[data-theme=light].light\:hidden{display:none}[data-theme=light].light\:divide-slate-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(241 245 249/var(--tw-divide-opacity,1))}[data-theme=light].light\:divide-slate-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(226 232 240/var(--tw-divide-opacity,1))}[data-theme=light].light\:divide-slate-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(30 41 59/var(--tw-divide-opacity,1))}[data-theme=light].light\:border-amber-200{--tw-border-opacity:1;border-color:rgb(253 230 138/var(--tw-border-opacity,1))}[data-theme=light].light\:border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity,1))}[data-theme=light].light\:border-green-200{--tw-border-opacity:1;border-color:rgb(187 247 208/var(--tw-border-opacity,1))}[data-theme=light].light\:border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}[data-theme=light].light\:border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity,1))}[data-theme=light].light\:border-slate-300{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity,1))}[data-theme=light].light\:border-slate-800{--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity,1))}[data-theme=light].light\:bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}[data-theme=light].light\:bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}[data-theme=light].light\:bg-cyan-50{--tw-bg-opacity:1;background-color:rgb(236 254 255/var(--tw-bg-opacity,1))}[data-theme=light].light\:bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}[data-theme=light].light\:bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}[data-theme=light].light\:bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}[data-theme=light].light\:bg-slate-200{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity,1))}[data-theme=light].light\:bg-slate-400{--tw-bg-opacity:1;background-color:rgb(148 163 184/var(--tw-bg-opacity,1))}[data-theme=light].light\:bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}[data-theme=light].light\:bg-transparent{background-color:transparent}[data-theme=light].light\:bg-violet-50{--tw-bg-opacity:1;background-color:rgb(245 243 255/var(--tw-bg-opacity,1))}[data-theme=light].light\:bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}[data-theme=light].light\:text-\[\#15d3f1\]{--tw-text-opacity:1;color:rgb(21 211 241/var(--tw-text-opacity,1))}[data-theme=light].light\:text-amber-700{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity,1))}[data-theme=light].light\:text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/var(--tw-text-opacity,1))}[data-theme=light].light\:text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}[data-theme=light].light\:text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity,1))}[data-theme=light].light\:text-green-800{--tw-text-opacity:1;color:rgb(22 101 52/var(--tw-text-opacity,1))}[data-theme=light].light\:text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}[data-theme=light].light\:text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}[data-theme=light].light\:text-slate-300{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity,1))}[data-theme=light].light\:text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}[data-theme=light].light\:text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}[data-theme=light].light\:text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}[data-theme=light].light\:text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}[data-theme=light].light\:text-slate-800{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity,1))}[data-theme=light].light\:text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}[data-theme=light].light\:shadow-sm{--tw-shadow:0 1px 2px 0 rgba(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)}[data-theme=light].light\:ring-amber-200{--tw-ring-opacity:1;--tw-ring-color:rgb(253 230 138/var(--tw-ring-opacity,1))}[data-theme=light].light\:ring-blue-200{--tw-ring-opacity:1;--tw-ring-color:rgb(191 219 254/var(--tw-ring-opacity,1))}[data-theme=light].light\:ring-green-200{--tw-ring-opacity:1;--tw-ring-color:rgb(187 247 208/var(--tw-ring-opacity,1))}[data-theme=light].light\:ring-red-200{--tw-ring-opacity:1;--tw-ring-color:rgb(254 202 202/var(--tw-ring-opacity,1))}[data-theme=light].light\:ring-slate-200{--tw-ring-opacity:1;--tw-ring-color:rgb(226 232 240/var(--tw-ring-opacity,1))}[data-theme=light].light\:placeholder\:text-slate-400::-moz-placeholder{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}[data-theme=light].light\:placeholder\:text-slate-400::placeholder{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}[data-theme=light].light\:hover\:border-slate-300:hover{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity,1))}[data-theme=light].light\:hover\:bg-slate-100:hover{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}[data-theme=light].light\:hover\:bg-slate-200:hover{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity,1))}[data-theme=light].light\:hover\:bg-slate-50:hover{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}[data-theme=light].light\:hover\:text-primary:hover{--tw-text-opacity:1;color:rgb(17 102 221/var(--tw-text-opacity,1))}[data-theme=light].light\:hover\:text-slate-800:hover{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity,1))}[data-theme=light].light\:hover\:text-slate-900:hover{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}[data-theme=light].light\:hover\:brightness-95:hover{--tw-brightness:brightness(.95);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)}[data-theme=light].light\:aria-checked\:bg-white[aria-checked=true]{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}[data-theme=light].light\:aria-checked\:text-slate-900[aria-checked=true]{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}[data-theme=light].light\:aria-checked\:shadow-sm[aria-checked=true]{--tw-shadow:0 1px 2px 0 rgba(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)}[data-width=full] .wide\:max-w-none{max-width:none}@media (prefers-reduced-motion:reduce){.motion-reduce\:transition-none{transition-property:none}}@media (min-width:640px){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:w-44{width:11rem}.sm\:w-52{width:13rem}.sm\:w-72{width:18rem}.sm\:w-80{width:20rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:flex-col{flex-direction:column}.sm\:items-start{align-items:flex-start}.sm\:items-end{align-items:flex-end}.sm\:justify-between{justify-content:space-between}.sm\:gap-0{gap:0}.sm\:gap-4{gap:1rem}.sm\:p-8{padding:2rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}}@media (min-width:768px){.md\:flex{display:flex}.md\:hidden{display:none}}@media (min-width:1024px){.lg\:sticky{position:sticky}.lg\:top-6{top:1.5rem}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:hidden{display:none}.lg\:translate-x-0{--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:px-10{padding-left:2.5rem;padding-right:2.5rem}.lg\:pl-72{padding-left:18rem}} \ No newline at end of file diff --git a/public/build/app.js b/public/build/app.js index a6c082a..d0e3eca 100644 --- a/public/build/app.js +++ b/public/build/app.js @@ -1,27 +1 @@ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ // The require scope -/******/ var __webpack_require__ = {}; -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -var __webpack_exports__ = {}; -/*!************************!*\ - !*** ./assets/app.css ***! - \************************/ -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - -/******/ })() -; \ No newline at end of file +(()=>{"use strict";var e=window.matchMedia("(prefers-color-scheme: light)");function t(t){var a="light"===t||"auto"===t&&e.matches;document.documentElement.setAttribute("data-theme",a?"light":"dark")}var a={theme:{key:"patchlevel-dashboard-theme",values:["light","dark","auto"],fallback:"auto",apply:t},width:{key:"patchlevel-dashboard-width",values:["fixed","full"],fallback:"fixed",apply:function(e){"full"===e?document.documentElement.setAttribute("data-width","full"):document.documentElement.removeAttribute("data-width")}}};function n(e){var t=a[e],n=t.key,c=t.values,l=t.fallback;try{var o=localStorage.getItem(n);if(c.includes(o))return o}catch(e){}return l}function c(e,t){document.querySelectorAll('[data-pref="'.concat(e,'"]')).forEach(function(e){e.setAttribute("aria-checked",String(e.dataset.value===t))})}document.querySelectorAll("[data-pref]").forEach(function(e){e.addEventListener("click",function(){var t=e.dataset.pref,n=e.dataset.value;try{localStorage.setItem(a[t].key,n)}catch(e){}a[t].apply(n),c(t,n)})}),Object.keys(a).forEach(function(e){return c(e,n(e))}),e.addEventListener("change",function(){"auto"===n("theme")&&t("auto")});var l=document.getElementById("sidebar"),o=document.getElementById("sidebar-backdrop");function r(e){var t;l&&(l.classList.toggle("-translate-x-full",!e),null==o||o.classList.toggle("hidden",!e),document.querySelectorAll("[data-sidebar-toggle]").forEach(function(t){t.setAttribute("aria-expanded",String(e))}),e&&(null===(t=l.querySelector("a, button"))||void 0===t||t.focus()))}if(document.querySelectorAll("[data-sidebar-toggle]").forEach(function(e){e.addEventListener("click",function(){r(l.classList.contains("-translate-x-full"))})}),null==o||o.addEventListener("click",function(){return r(!1)}),window.addEventListener("keydown",function(e){"Escape"===e.key&&l&&!l.classList.contains("-translate-x-full")&&r(!1)}),document.querySelectorAll("[data-modal]").forEach(function(e){e.addEventListener("click",function(t){var a;t.preventDefault(),null===(a=document.getElementById(e.dataset.modal))||void 0===a||a.showModal()})}),document.querySelectorAll("dialog").forEach(function(e){e.querySelectorAll("[data-modal-close]").forEach(function(t){t.addEventListener("click",function(){return e.close()})}),e.addEventListener("click",function(t){t.target===e&&e.close()})}),document.querySelectorAll("[data-clipboard]").forEach(function(e){e.addEventListener("click",function(t){t.preventDefault(),navigator.clipboard.writeText(e.dataset.clipboard).then(function(){e.setAttribute("data-copied",""),window.setTimeout(function(){return e.removeAttribute("data-copied")},1500)})})}),window.location.hash){var i=document.getElementById("marker-"+window.location.hash.slice(1));null==i||i.classList.remove("invisible")}document.querySelectorAll("[data-add-hash]").forEach(function(e){e.href+=window.location.hash}),document.querySelectorAll(".json").forEach(function(e){e.innerHTML=e.innerHTML.replace(/(("([^"]|\\")+?[^\\]")|"")(\s*.)/gi,function(e,t,a,n,c){return""+function(e){return e.replace(/[<>&]/g,function(e){return{"<":"<",">":">","&":"&"}[e]})}(t)+""+c}).replace(/-?\d+\.?\d*((E|e)[+]\d+)?/gi,"$&").replace(/false|true|null/gi,"$&")})})(); \ No newline at end of file diff --git a/public/fonts/InterVariable.woff2 b/public/fonts/InterVariable.woff2 new file mode 100644 index 0000000..b315fa0 Binary files /dev/null and b/public/fonts/InterVariable.woff2 differ diff --git a/public/fonts/JetBrainsMono-LICENSE.txt b/public/fonts/JetBrainsMono-LICENSE.txt new file mode 100644 index 0000000..5ceee00 --- /dev/null +++ b/public/fonts/JetBrainsMono-LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/public/fonts/JetBrainsMono-Regular.woff2 b/public/fonts/JetBrainsMono-Regular.woff2 new file mode 100644 index 0000000..f9da252 Binary files /dev/null and b/public/fonts/JetBrainsMono-Regular.woff2 differ diff --git a/public/hightlight.css b/public/hightlight.css deleted file mode 100644 index 83a1186..0000000 --- a/public/hightlight.css +++ /dev/null @@ -1,19 +0,0 @@ -.json_key, .json_key span { - color: #6d28d9; -} - -.json_string, .json_string span { - color: #15803d; -} - -.json_number { - color: #1d4ed8; -} - -.json_bool { - color: #b91c1c; -} - -.json { - white-space: pre; -} diff --git a/public/hightlight.js b/public/hightlight.js deleted file mode 100644 index 626ddda..0000000 --- a/public/hightlight.js +++ /dev/null @@ -1,20 +0,0 @@ -function escapeHtml(text) { - return text.replace(/[<>&]/g, function(char) { - return {'<': '<', '>': '>', '&': '&'}[char]; - }); -} - -function highlightJson(text) { - return text - .replace(/(("([^"]|\\")+?[^\\]")|"")(\s*.)/ig, function(str, g1, g2, g3, g4) { - var type = g4.trim() === ":" ? "json_key" : "json_string"; - return "" + escapeHtml(g1) + "" + g4; - }) - .replace(/-?\d+\.?\d*((E|e)[\+]\d+)?/ig, "$&") - .replace(/false|true|null/ig, "$&") -} - - -document.querySelectorAll('.json').forEach(function (el) { - el.innerHTML = highlightJson(el.innerHTML); -}); diff --git a/public/logo-light.png b/public/logo-light.png new file mode 100644 index 0000000..7154508 Binary files /dev/null and b/public/logo-light.png differ diff --git a/public/logo.png b/public/logo.png index 223245b..be170fe 100644 Binary files a/public/logo.png and b/public/logo.png differ diff --git a/tailwind.config.js b/tailwind.config.js index 6537276..d4cff6c 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,3 +1,6 @@ +const defaultTheme = require('tailwindcss/defaultTheme'); +const plugin = require('tailwindcss/plugin'); + /** @type {import('tailwindcss').Config} */ module.exports = { content: [ @@ -5,10 +8,33 @@ module.exports = { "./templates/**/*.html.twig", ], theme: { - extend: {}, + extend: { + colors: { + primary: { + DEFAULT: '#1166dd', + light: 'rgb(var(--color-primary-light) / )', + }, + secondary: { + DEFAULT: '#6341f0', + light: 'rgb(var(--color-secondary-light) / )', + }, + tertiary: { + DEFAULT: '#0ba6be', + light: 'rgb(var(--color-tertiary-light) / )', + }, + }, + fontFamily: { + sans: ['Inter', ...defaultTheme.fontFamily.sans], + mono: ['JetBrains Mono', ...defaultTheme.fontFamily.mono], + }, + }, }, plugins: [ - require('@tailwindcss/forms'), + require('@tailwindcss/forms')({ strategy: 'class' }), + plugin(({ addVariant }) => { + addVariant('light', ['[data-theme="light"] &', '[data-theme="light"]&']); + // Content width preference, see the switch in layout.html.twig. + addVariant('wide', '[data-width="full"] &'); + }), ], } - diff --git a/templates/base.html.twig b/templates/base.html.twig index 5d763cb..d168801 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -1,12 +1,14 @@ - + - - EventSourcing Dashboard + {% block title %}{% endblock %}Event Sourcing Dashboard + {# Runs before paint so the page never flashes the wrong theme or width. Stored preferences win, otherwise the OS decides the theme. #} + + - + @@ -19,6 +21,5 @@ {% block body %}{% endblock %} - diff --git a/templates/components.html.twig b/templates/components.html.twig new file mode 100644 index 0000000..8ccc655 --- /dev/null +++ b/templates/components.html.twig @@ -0,0 +1,161 @@ +{# Shared building blocks. Import with: {% import '@PatchlevelEventSourcingDashboard/components.html.twig' as ui %} #} + +{% macro title(text) %} +

{{ text }}

+{% endmacro %} + +{% macro subtitle(text) %} +

{{ text }}

+{% endmacro %} + +{% macro section_title(text) %} +

{{ text }}

+{% endmacro %} + +{#- ---- Form controls ------------------------------------------------- -#} + +{% macro label(for, text) %} + +{% endmacro %} + +{% macro input(name, value, placeholder, required = false) %} + +{% endmacro %} + +{% macro select(name, options, selected, labels = {}, required = false, placeholder = 'All') %} + +{% endmacro %} + +{#- ---- Buttons ------------------------------------------------------- -#} + +{% macro button_primary(text, icon = null, type = 'submit') %} + +{% endmacro %} + +{% macro link_primary(url, text, icon = null, title = null) %} + + {% if icon %}{{ heroicon(icon, '-ml-0.5 h-4 w-4') }}{% endif %}{{ text }} + +{% endmacro %} + +{% macro link_secondary(url, text, icon = null, title = null) %} + + {% if icon %}{{ heroicon(icon, '-ml-0.5 h-4 w-4') }}{% endif %}{{ text }} + +{% endmacro %} + +{# Icon-only control inside a row: always visible, labelled for screen readers. #} +{% macro icon_link(url, icon, title) %} + + {{ heroicon(icon, 'h-4 w-4') }} + +{% endmacro %} + +{# Secondary navigation next to a value: revealed on hover of the enclosing .group/parent, always reachable by keyboard. #} +{% macro row_action(url, icon, title) %} + + {{ heroicon(icon, 'h-4 w-4') }} + +{% endmacro %} + +{% macro clipboard(text) %} + +{% endmacro %} + +{% macro dialog_button(id, title = 'Show details') %} + +{% endmacro %} + +{#- ---- Badges -------------------------------------------------------- -#} + +{% macro badge(text, tone = 'gray') %} + {% set tones = { + green: 'bg-green-950 text-green-400 ring-green-900 light:bg-green-50 light:text-green-700 light:ring-green-200', + yellow: 'bg-amber-950 text-amber-300 ring-amber-900 light:bg-amber-50 light:text-amber-800 light:ring-amber-200', + red: 'bg-red-950 text-red-400 ring-red-900 light:bg-red-50 light:text-red-700 light:ring-red-200', + blue: 'bg-blue-950 light:bg-blue-50 text-primary-light ring-blue-900 light:ring-blue-200', + gray: 'bg-slate-900 text-gray-400 ring-slate-700 light:bg-slate-100 light:text-slate-600 light:ring-slate-200', + } %} + + + {{ text }} + +{% endmacro %} + +{% macro status_tone(status) -%} + {%- if status in ['active', 'finished'] -%}green + {%- elseif status == 'booting' -%}yellow + {%- elseif status in ['error', 'failed'] -%}red + {%- elseif status == 'new' -%}blue + {%- else -%}gray + {%- endif -%} +{%- endmacro %} + +{% macro status_badge(status) %} + {{ _self.badge(status|capitalize, _self.status_tone(status)|trim) }} +{% endmacro %} + +{#- ---- Description lists --------------------------------------------- -#} + +{% macro dl_row(name, content, mono = false) %} +
+
{{ name }}
+
+ {%- if content is null or content == '' -%} + ~ + {%- else -%} + {{ content }} + {%- endif -%} +
+
+{% endmacro %} + +{% macro dl_json(name, content) %} +
+
{{ name }}
+
+
+ {{- content -}} +
+
+
+{% endmacro %} + +{#- ---- Feedback ------------------------------------------------------ -#} + +{% macro empty(icon, text, hint = null) %} +
+ +

{{ text }}

+ {% if hint %}

{{ hint }}

{% endif %} +
+{% endmacro %} + +{% macro error(message) %} +
+ {{ heroicon('exclamation-circle', 'h-5 w-5 shrink-0') }} +

{{ message }}

+
+{% endmacro %} + +{#- ---- Tables -------------------------------------------------------- -#} + +{% macro th(text, align = 'left') %} + {{ text }} +{% endmacro %} diff --git a/templates/dashboard/index.html.twig b/templates/dashboard/index.html.twig index e3175af..2b00512 100644 --- a/templates/dashboard/index.html.twig +++ b/templates/dashboard/index.html.twig @@ -1,174 +1,119 @@ {% extends '@PatchlevelEventSourcingDashboard/layout.html.twig' %} +{% import '@PatchlevelEventSourcingDashboard/components.html.twig' as ui %} -{% block content %} -
- - -
- Dashboard -
+{% block title %}Dashboard · {% endblock %} - -
- {{ _self.stat_card( - "Events in store", - messageCount, - "circle-stack", - path('patchlevel_event_sourcing_dashboard_store_show') - ) }} - {{ _self.stat_card( - "Aggregates", - aggregateCount, - "identification", - path('patchlevel_event_sourcing_dashboard_inspection_index') - ) }} - {{ _self.stat_card( - "Registered events", - eventCount, - "calendar", - path('patchlevel_event_sourcing_dashboard_event_index') - ) }} - {{ _self.stat_card( - "Subscriptions", - subscriptionCount, - "video-camera", - path('patchlevel_event_sourcing_dashboard_subscription_show') - ) }} -
+{% block content %} + {{ ui.title('Dashboard') }} + {{ ui.subtitle('Your event sourcing setup at a glance.') }} - -
-
- Subscription health -
+ {# One strip, four cells: the numbers read as one system, not four widgets. #} +
+ {{ _self.stat('Events in store', messageCount, 'circle-stack', 'bg-blue-950 light:bg-blue-50 text-primary-light', path('patchlevel_event_sourcing_dashboard_store_show')) }} + {{ _self.stat('Aggregates', aggregateCount, 'identification', 'bg-violet-950 light:bg-violet-50 text-secondary-light', path('patchlevel_event_sourcing_dashboard_inspection_index')) }} + {{ _self.stat('Registered events', eventCount, 'calendar', 'bg-cyan-950 light:bg-cyan-50 text-tertiary-light', path('patchlevel_event_sourcing_dashboard_event_index')) }} + {{ _self.stat('Subscriptions', subscriptionCount, 'video-camera', 'bg-blue-950 light:bg-blue-50 text-primary-light', path('patchlevel_event_sourcing_dashboard_subscription_show')) }} +
-
- {% for status, count in subscriptionStatuses %} - - {{ status }} - {{ count }} - - {% endfor %} -
-
+
+ {{ ui.section_title('Subscription health') }} +

+ {% if subscriptionStatuses.error + subscriptionStatuses.failed > 0 %} + Some subscriptions stopped processing events. Open the red ones first. + {% else %} + No subscription is in an error state. + {% endif %} +

- - -
-
-
- - - - {% if eventsourcing_uses_stream_store() %} - - {% else %} - - - {% endif %} - - - - - - - {% for message in messages %} - - {% if eventsourcing_uses_stream_store() %} - - {% else %} - - - {% endif %} +
+
+ {{ ui.section_title('Latest events') }} + + Open the store {{ heroicon('arrow-right', 'h-4 w-4') }} + +
-
- - - - {% else %} - - - - {% endfor %} - -
- Stream - - Aggregate - ID - - Playhead - - Event - - Recorded On -
- {{ eventsourcing_stream_name(message) }} - - - {{ eventsourcing_aggregate_name(message) }} - - - {{ eventsourcing_short_id(eventsourcing_aggregate_id(message)) }} - - {{ eventsourcing_playhead(message) }} - {{ eventsourcing_event_name(message) }} - - {{ eventsourcing_recorded_on(message) ? eventsourcing_recorded_on(message)|date('Y-m-d H:i:s') : '-' }}
No events found
-
-
-
+
+ {% set td = 'whitespace-nowrap px-4 py-3 text-sm text-gray-300 light:text-slate-600' %} + + + + {% if eventsourcing_uses_stream_store() %} + {{ ui.th('Stream') }} + {% else %} + {{ ui.th('Aggregate') }} + {{ ui.th('ID') }} + {% endif %} + {{ ui.th('Playhead', 'right') }} + {{ ui.th('Event') }} + {{ ui.th('Recorded on') }} + + + + {% for message in messages %} + + {% if eventsourcing_uses_stream_store() %} + + {% else %} + + + {% endif %} + + + + + {% else %} + + + + {% endfor %} + +
+ {{ eventsourcing_stream_name(message) }} + {{ ui.row_action(path('patchlevel_event_sourcing_dashboard_inspection_stream', { stream: eventsourcing_stream_name(message) }) ~ '#' ~ eventsourcing_playhead(message), 'identification', 'Inspect stream') }} + + {{ eventsourcing_aggregate_name(message) }} + {{ ui.row_action(path('patchlevel_event_sourcing_dashboard_store_show', { aggregate: eventsourcing_aggregate_name(message) }), 'funnel', 'Filter by aggregate') }} + + {{ eventsourcing_short_id(eventsourcing_aggregate_id(message)) }} + {{ ui.row_action(path('patchlevel_event_sourcing_dashboard_inspection_show', { aggregateName: eventsourcing_aggregate_name(message), aggregateId: eventsourcing_aggregate_id(message) }) ~ '#' ~ eventsourcing_playhead(message), 'identification', 'Inspect aggregate') }} + {{ eventsourcing_playhead(message) }} + {{ eventsourcing_event_name(message) }} + {{ ui.row_action(path('patchlevel_event_sourcing_dashboard_event_index', { name: eventsourcing_event_name(message) }) ~ '#' ~ eventsourcing_event_name(message), 'calendar', 'Show event definition') }} + {{ eventsourcing_recorded_on(message) ? eventsourcing_recorded_on(message)|date('Y-m-d H:i:s') : '-' }}
{{ ui.empty('inbox', 'No events recorded yet', 'Events show up here as soon as your application stores them.') }}
{% endblock %} -{% macro stat_card(label, value, icon, url) %} +{% macro stat(label, value, icon, tile, url) %} -
- {{ heroicon(icon, 'h-6 w-6 text-white') }} + class="group flex items-center justify-between gap-4 bg-slate-900 p-5 transition-colors hover:bg-slate-800 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary-light light:bg-white light:hover:bg-slate-50"> +
+
{{ label }}
+
{{ value }}
-
-
{{ label }}
-
{{ value }}
+
{% endmacro %} - -{% macro status_classes(status, count) %} - {%- if count == 0 -%} - bg-white text-gray-500 ring-gray-300 - {%- elseif status in ['error', 'failed'] -%} - bg-red-50 text-red-700 ring-red-600/20 - {%- elseif status in ['paused', 'detached'] -%} - bg-yellow-50 text-yellow-800 ring-yellow-600/20 - {%- elseif status == 'active' -%} - bg-green-50 text-green-700 ring-green-600/20 - {%- else -%} - bg-gray-50 text-gray-600 ring-gray-500/10 - {%- endif -%} -{% endmacro %} diff --git a/templates/dialog.html.twig b/templates/dialog.html.twig new file mode 100644 index 0000000..f435316 --- /dev/null +++ b/templates/dialog.html.twig @@ -0,0 +1,13 @@ +{# Native , opened by a [data-modal="id"] trigger. Use with embed and fill the content block. #} + +
+

{{ title }}

+ +
+
+ {% block content %}{% endblock %} +
+
diff --git a/templates/event/index.html.twig b/templates/event/index.html.twig index 20070de..ab909a8 100644 --- a/templates/event/index.html.twig +++ b/templates/event/index.html.twig @@ -1,160 +1,98 @@ {% extends '@PatchlevelEventSourcingDashboard/layout.html.twig' %} +{% import '@PatchlevelEventSourcingDashboard/components.html.twig' as ui %} -{% block content %} -
- - -
- Events -
- - -
-
-
    - {% set current = null %} - {% for event in events|sort((a, b) => a.name <=> b.name)|sort((a, b) => min(b.name|split('.')|length, 2) <=> min(a.name|split('.')|length, 2)) %} - {% set parts = event.name|split('.') %} +{% block title %}Events · {% endblock %} - {% set category = parts|length > 1 ? parts|first : 'uncategorized' %} - {% set name = parts|length > 1 ? parts|slice(1)|join('.') : event.name %} - - {% if current != category %} - {% set current = category %} -
  • - {{ category }} -
  • - {% endif %} -
  • - - -
    -
    - {{ heroicon(eventsourcing_inspection_icon(event.class, 'calendar'), 'h-5 w-5 text-white') }} -
    -
    - - {{ name }} -
    -
    -
    - {{ heroicon('chevron-right', 'h-5 w-5 text-gray-400') }} -
    -
    +{% block content %} + {% set selectedName = app.request.query.get('name') %} + {% set selected = null %} + {% for event in events %} + {% if event.name == selectedName %}{% set selected = event %}{% endif %} + {% endfor %} + + {{ ui.title('Events') }} + {{ ui.subtitle(events|length ~ ' registered ' ~ (events|length == 1 ? 'event' : 'events') ~ ' and who reacts to them.') }} + +
    +
    +
      + {% set current = null %} + {% for event in events|sort((a, b) => a.name <=> b.name)|sort((a, b) => min(b.name|split('.')|length, 2) <=> min(a.name|split('.')|length, 2)) %} + {% set parts = event.name|split('.') %} + {% set category = parts|length > 1 ? parts|first : 'uncategorized' %} + {% set name = parts|length > 1 ? parts|slice(1)|join('.') : event.name %} + {% set active = event.name == selectedName %} + + {% if current != category %} + {% set current = category %} +
    • + {{ category }}
    • - {% endfor %} -
    -
    - - -
    -
    - {% for event in events %} - {% if event.name == app.request.query.get('name') %} - -
    - Details -
    - -
    - {{ _self.text('Name', event.name) }} - {{ _self.text('Class', _self.methodFormat(event.class)) }} -
    - -
    - Subscribers ({{ event.subscribers|length }}) -
    - -
      - {% for subscriber in event.subscribers %} - {{ _self.subscriber(subscriber) }} - {% else %} -
    • no subscriber
    • - {% endfor %} -
    - - {% if event.listeners %} -
    - Listeners ({{ event.listeners|length }}) -
    - -
      - {% for listener in event.listeners %} - {{ _self.subscriber(listener) }} - {% else %} -
    • no listeners
    • - {% endfor %} -
    - {% endif %} - - {% endif %} - {% endfor %} - - {% if not app.request.query.get('name') %} - -
    - {{ heroicon('calendar', 'h-12 w-12 text-gray-400 mx-auto mb-2') }} - Select an event to see details. -
    - {% endif %} - -
    -
    +
  • + + + + {{ name }} + {{ heroicon('chevron-right', 'h-4 w-4 shrink-0 text-gray-500') }} + +
  • + {% else %} +
  • {{ ui.empty('calendar', 'No events registered', 'Events appear here once they are known to the event registry.') }}
  • + {% endfor %} +
-
- - +
+ {% if selected %} +
+

{{ selected.name }}

+

{{ _self.class_name(selected.class) }}

+
-{% endblock %} +
+

+ Subscribers {{ selected.subscribers|length }} +

+ {% if selected.subscribers %} +
    + {% for subscriber in selected.subscribers %}{{ _self.handler(subscriber) }}{% endfor %} +
+ {% else %} +

No subscriber handles this event.

+ {% endif %} +
-{% macro text(name, content) %} -
-
{{ name }}
-
- {% if content == null %} - ~ + {% if selected.listeners %} +
+

+ Listeners {{ selected.listeners|length }} +

+
    + {% for listener in selected.listeners %}{{ _self.handler(listener) }}{% endfor %} +
+
+ {% endif %} {% else %} - {{ content }} + {{ ui.empty('calendar', 'Select an event', 'Its class, subscribers and listeners show up here.') }} {% endif %} -
+
-{% endmacro %} +{% endblock %} -{% macro subscriber(name) %} +{% macro handler(name) %} {% set parts = name|split('\\') %} - -
  • -
    {{ parts|last }}
    -
    {{ name }}
    -
  • -{% endmacro %} - -{% macro node(node) %} - {% set parts = node.name|split('\\') %} - -
  • - {% if parts|length > 1 %} -
    {{ parts|last }}
    -
    {{ node.name }}
    - {% else %} -
    {{ node.name }}
    -
    {{ node.category }}
    - {% endif %} +
  • +
    {{ parts|last }}
    +
    {{ name }}
  • {% endmacro %} -{% macro methodFormat(name) %} +{% macro class_name(name) %} {% set parts = name|split('\\') %} - {{ parts|slice(0, parts|length - 1)|join('\\') }}\{{ parts|last }} + {{ parts|slice(0, parts|length - 1)|join('\\') }}\{{ parts|last }} {% endmacro %} diff --git a/templates/inspection/index.html.twig b/templates/inspection/index.html.twig index 7fa7fe3..c1f7952 100644 --- a/templates/inspection/index.html.twig +++ b/templates/inspection/index.html.twig @@ -1,52 +1,25 @@ {% extends '@PatchlevelEventSourcingDashboard/layout.html.twig' %} +{% import '@PatchlevelEventSourcingDashboard/components.html.twig' as ui %} -{% block content %} -
    - - - +{% block title %}Inspection · {% endblock %} -
    -
    -
    - Inspection +{% block content %} + {{ ui.title('Inspection') }} + {{ ui.subtitle('Pick an aggregate to see its state, its event history and travel back in time.') }} + +
    +
    +
    + {{ ui.label('aggregate', 'Aggregate') }} + {{ ui.select('aggregate', aggregates, app.request.query.get('aggregate'), {}, true, 'Choose an aggregate') }}
    - - -
    - - -
    - -
    - -
    - -
    -
    - -
    - -
    -
    -
    - -
    - +
    + {{ ui.label('aggregateId', 'Aggregate ID') }} + {{ ui.input('aggregateId', app.request.query.get('aggregateId'), '018d233e-7906-73f1-8a0c-f76a4fdac34e', true) }} +
    +
    + {{ ui.button_primary('Inspect', 'identification') }} +
    + +
    {% endblock %} diff --git a/templates/inspection/select.html.twig b/templates/inspection/select.html.twig index 8fc2f16..b0b3d7b 100644 --- a/templates/inspection/select.html.twig +++ b/templates/inspection/select.html.twig @@ -1,50 +1,28 @@ {% extends '@PatchlevelEventSourcingDashboard/layout.html.twig' %} +{% import '@PatchlevelEventSourcingDashboard/components.html.twig' as ui %} + +{% block title %}Inspection · {% endblock %} {% block content %} -
    -
    -
    - - {% endblock %} diff --git a/templates/inspection/show.html.twig b/templates/inspection/show.html.twig index 7b3294b..3d5dda6 100644 --- a/templates/inspection/show.html.twig +++ b/templates/inspection/show.html.twig @@ -1,256 +1,155 @@ {% extends '@PatchlevelEventSourcingDashboard/layout.html.twig' %} +{% import '@PatchlevelEventSourcingDashboard/components.html.twig' as ui %} -{% block content %} -
    +{% block title %}{{ aggregateName }} · Inspection · {% endblock %} - -
    - Inspection - {% if until %} - - {{ heroicon('stop', '-ml-0.5 h-5 w-5') }} Stop time travel - - {% else %} - - {{ heroicon('clock', '-ml-0.5 h-5 w-5') }} Time travel - - {% endif %} +{% block content %} + {% set base = { aggregateId: aggregateId, aggregateName: aggregateName, tab: tab } %} + +
    +
    + {{ ui.title('Inspection') }} +

    + {{ aggregateName }} + {{ aggregateId }} {{ ui.clipboard(aggregateId) }} +

    + {% if until %} + {{ ui.link_secondary(path('patchlevel_event_sourcing_dashboard_inspection_show', base), 'Stop time travel', 'stop', 'Back to the current state') }} + {% else %} + {{ ui.link_primary(path('patchlevel_event_sourcing_dashboard_inspection_show', base|merge({ until: 1 })) ~ '#1', 'Time travel', 'clock', 'Replay the aggregate event by event') }} + {% endif %} +
    - -
    -
    - - {% if until %} -
    -
    - {{ heroicon('clock', 'h-4 w-4 inline') }} - Time travel: - - playhead {{ aggregate.playhead }} from {{ count }} - -
    -
    - {% if aggregate.playhead != 1 %} - - {{ heroicon('backward', '-ml-0.5 h-5 w-5') }} - - {% endif %} - - - {{ heroicon('stop', '-ml-0.5 h-5 w-5') }} - - - {% if aggregate.playhead < count %} - - {{ heroicon('forward', '-ml-0.5 h-5 w-5') }} - - {% endif %} - -
    +
    +
    + {% if until %} + {# The one loud surface on the page: the brand gradient says "you are not looking at now". #} +
    +
    + {{ heroicon('clock', 'h-4 w-4') }} + Time travel + playhead {{ aggregate.playhead }} of {{ count }}
    - {% endif %} - -
    -
    - +
    + {{ _self.travel(aggregate.playhead != 1 ? path('patchlevel_event_sourcing_dashboard_inspection_show', base|merge({ until: until - 1 })) ~ '#' ~ (until - 1) : null, 'backward', 'One event back') }} + {{ _self.travel(path('patchlevel_event_sourcing_dashboard_inspection_show', base), 'stop', 'Stop time travel') }} + {{ _self.travel(aggregate.playhead < count ? path('patchlevel_event_sourcing_dashboard_inspection_show', base|merge({ until: until + 1 })) ~ '#' ~ (until + 1) : null, 'forward', 'One event forward') }}
    +
    + {% endif %} - {% if tab == 'details' %} -
    - {{ _self.text('ID', aggregateId) }} - {{ _self.text('Name', aggregateName) }} - {{ _self.text('Class', aggregateClass) }} - {{ _self.text('Playhead', aggregate.playhead) }} - - {% if metadata.snapshot %} - {{ _self.text('Snapshot Adapter', metadata.snapshot.store) }} - {{ _self.text('Snapshot Version', metadata.snapshot.version) }} - {{ _self.text('Snapshot Batch', metadata.snapshot.batch) }} - {% endif %} - -
    + + +
    + {% if tab == 'details' %} +
    + {{ ui.dl_row('ID', aggregateId, true) }} + {{ ui.dl_row('Name', aggregateName) }} + {{ ui.dl_row('Class', aggregateClass, true) }} + {{ ui.dl_row('Playhead', aggregate.playhead, true) }} + {% if metadata.snapshot %} + {{ ui.dl_row('Snapshot adapter', metadata.snapshot.store) }} + {{ ui.dl_row('Snapshot version', metadata.snapshot.version, true) }} + {{ ui.dl_row('Snapshot batch', metadata.snapshot.batch, true) }} + {% endif %} +
    + {% elseif tab == 'dump' %} + {{ eventsourcing_dump(aggregate) }} + {% elseif tab == 'serialized' %} + {% if serializedError %} + {{ ui.error(serializedError) }} {% endif %} - - {% if tab == 'dump' %} - {{ eventsourcing_dump(aggregate) }} + {% if serializedAggregate %} +
    + {{- serializedAggregate -}} +
    {% endif %} - - {% if tab == 'serialized' %} - {% if serializedError %} -
    -
    -
    - -
    -
    -

    {{ serializedError }}

    -
    -
    -
    - {% endif %} - - {% if serializedAggregate %} -
    - {{~ serializedAggregate ~}} -
    - {% endif %} + {% elseif tab == 'snapshot' %} + {% if snapshotError %} + {{ ui.error(snapshotError) }} {% endif %} - - - {% if tab == 'snapshot' %} - {% if snapshotError %} -
    -
    -
    - -
    -
    -

    {{ snapshotError }}

    -
    -
    -
    - {% endif %} - - {% if snapshot %} - {{ eventsourcing_dump(snapshot) }} - {% endif %} + {% if snapshot %} + {{ eventsourcing_dump(snapshot) }} + {% elseif not snapshotError %} + {{ ui.empty('document-magnifying-glass', 'No snapshot stored', 'This aggregate is rebuilt from its events every time.') }} {% endif %} -
    + {% endif %}
    +
    +
    +

    + Event history {{ count }} +

    + +
      + {% for message in messages %} + {% set playhead = eventsourcing_playhead(message) %} + {% set color = eventsourcing_inspection_color(message.event) %} + {% set future = until and loop.index > until %} + {% set dialog = 'message-' ~ loop.index %} +
    1. + + + + {% if loop.index != count %} + + {% endif %} -
      -
      -
      -
        - {% for message in messages %} -
      • - - -
        - {% if loop.index != count %} - - {% endif %} -
        -
        - - {{ heroicon(eventsourcing_inspection_icon(message.event, 'calendar'), 'h-5 w-5 text-white') }} + + +
        +
        + {{ eventsourcing_inspection_description(message.event, eventsourcing_event_name(message)) }} + + + + {{ heroicon('play-pause', 'h-4 w-4') }} + -
        -
        -
        -

        - {{ eventsourcing_inspection_description(message.event, eventsourcing_event_name(message)) }} - - -

        - -
        - -
        - - {{ include('@PatchlevelEventSourcingDashboard/store/detail.html.twig', { - message: message, - }) }} -
        -
        -
        - {{ eventsourcing_recorded_on(message) ? eventsourcing_recorded_on(message)|date('Y-m-d H:i:s') : '-' }} -
        -
        -
        -
        -
      • - {% endfor %} -
      -
      - -
      -
      - + {% embed '@PatchlevelEventSourcingDashboard/dialog.html.twig' with { id: dialog, title: eventsourcing_event_name(message), message: message } %} + {% block content %} + {{ include('@PatchlevelEventSourcingDashboard/store/detail.html.twig', { message: message }) }} + {% endblock %} + {% endembed %} +
    +
    + {{ eventsourcing_recorded_on(message) ? eventsourcing_recorded_on(message)|date('Y-m-d H:i:s') : '-' }} + #{{ playhead }} +
    +
    + + {% endfor %} +
    - - - {% endblock %} -{% macro text(name, content) %} -
    -
    {{ name }}
    -
    - {% if content == null %} - ~ - {% else %} - {{ content }} - {% endif %} -
    -
    +{% macro travel(url, icon, label) %} + {% if url %} + + {{ heroicon(icon, 'h-4 w-4') }} + + {% else %} + + {% endif %} {% endmacro %} - diff --git a/templates/layout.html.twig b/templates/layout.html.twig index 88bce41..f0afbcd 100644 --- a/templates/layout.html.twig +++ b/templates/layout.html.twig @@ -1,69 +1,90 @@ {% extends '@PatchlevelEventSourcingDashboard/base.html.twig' %} {% block body %} -