diff --git a/Cargo.lock b/Cargo.lock index bc7a283..b9eda87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -682,6 +682,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-htmx" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36cdb6062317f732ed3acf4e9c28c3824092e226726616f46ebdd8cd32c82a41" +dependencies = [ + "async-trait", + "axum-core", + "http", +] + [[package]] name = "axum-macros" version = "0.4.2" @@ -1702,6 +1713,18 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -1762,6 +1785,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "fst" version = "0.4.7" @@ -2329,6 +2361,26 @@ dependencies = [ "serde", ] +[[package]] +name = "inotify" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "inout" version = "0.1.3" @@ -2422,6 +2474,26 @@ dependencies = [ "simple_asn1", ] +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lalrpop" version = "0.20.2" @@ -2511,6 +2583,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", + "redox_syscall", ] [[package]] @@ -2620,6 +2693,12 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memo-map" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b" + [[package]] name = "miette" version = "5.10.0" @@ -2659,6 +2738,27 @@ dependencies = [ "unicase", ] +[[package]] +name = "minijinja" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c37e1b517d1dcd0e51dc36c4567b9d5a29262b3ec8da6cb5d35e27a8fb529b5" +dependencies = [ + "memo-map", + "self_cell", + "serde", +] + +[[package]] +name = "minijinja-autoreload" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f1f548cc19d59543f8e69ffb71e9da745f996c688384307835ce7cb4dabe06" +dependencies = [ + "minijinja", + "notify", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2682,6 +2782,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi 0.3.9", "libc", + "log", "wasi", "windows-sys 0.52.0", ] @@ -2807,6 +2908,34 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" +[[package]] +name = "notify" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" +dependencies = [ + "bitflags 2.6.0", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.52.0", +] + +[[package]] +name = "notify-types" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585d3cb5e12e01aed9e8a1f70d5c6b5e86fe2a6e48fc8cd0b3e0b8df6f6eb174" +dependencies = [ + "instant", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -4144,6 +4273,12 @@ dependencies = [ "libc", ] +[[package]] +name = "self_cell" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" + [[package]] name = "semver" version = "1.0.23" @@ -5846,6 +5981,7 @@ dependencies = [ "anyhow", "async-openai", "axum", + "axum-htmx", "axum_session", "axum_session_auth", "axum_session_surreal", @@ -5854,6 +5990,8 @@ dependencies = [ "lapin", "mime", "mime_guess", + "minijinja", + "minijinja-autoreload", "mockall", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index b405e5e..8c56efc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" anyhow = "1.0.94" async-openai = "0.24.1" axum = { version = "0.7.5", features = ["multipart", "macros"] } +axum-htmx = "0.6.0" axum_session = "0.14.4" axum_session_auth = "0.14.1" axum_session_surreal = "0.2.1" @@ -15,6 +16,8 @@ futures = "0.3.31" lapin = { version = "2.5.0", features = ["serde_json"] } mime = "0.3.17" mime_guess = "2.0.5" +minijinja = { version = "2.5.0", features = ["loader", "multi_template"] } +minijinja-autoreload = "2.5.0" mockall = "0.13.0" serde = { version = "1.0.210", features = ["derive"] } serde_json = "1.0.128" diff --git a/src/server/assets/hi.txt b/assets/hi.txt similarity index 100% rename from src/server/assets/hi.txt rename to assets/hi.txt diff --git a/src/server/assets/input.css b/assets/input.css similarity index 100% rename from src/server/assets/input.css rename to assets/input.css diff --git a/assets/style.css b/assets/style.css new file mode 100644 index 0000000..ac0d94f --- /dev/null +++ b/assets/style.css @@ -0,0 +1,852 @@ +*, ::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: ; +} + +/* +! tailwindcss v3.4.17 | 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; +} + +:root, +[data-theme] { + background-color: var(--fallback-b1,oklch(var(--b1)/1)); + color: var(--fallback-bc,oklch(var(--bc)/1)); +} + +@supports not (color: oklch(0% 0 0)) { + :root { + color-scheme: light; + --fallback-p: #491eff; + --fallback-pc: #d4dbff; + --fallback-s: #ff41c7; + --fallback-sc: #fff9fc; + --fallback-a: #00cfbd; + --fallback-ac: #00100d; + --fallback-n: #2b3440; + --fallback-nc: #d7dde4; + --fallback-b1: #ffffff; + --fallback-b2: #e5e6e6; + --fallback-b3: #e5e6e6; + --fallback-bc: #1f2937; + --fallback-in: #00b3f0; + --fallback-inc: #000000; + --fallback-su: #00ca92; + --fallback-suc: #000000; + --fallback-wa: #ffc22d; + --fallback-wac: #000000; + --fallback-er: #ff6f70; + --fallback-erc: #000000; + } + + @media (prefers-color-scheme: dark) { + :root { + color-scheme: dark; + --fallback-p: #7582ff; + --fallback-pc: #050617; + --fallback-s: #ff71cf; + --fallback-sc: #190211; + --fallback-a: #00c7b5; + --fallback-ac: #000e0c; + --fallback-n: #2a323c; + --fallback-nc: #a6adbb; + --fallback-b1: #1d232a; + --fallback-b2: #191e24; + --fallback-b3: #15191e; + --fallback-bc: #a6adbb; + --fallback-in: #00b3f0; + --fallback-inc: #000000; + --fallback-su: #00ca92; + --fallback-suc: #000000; + --fallback-wa: #ffc22d; + --fallback-wac: #000000; + --fallback-er: #ff6f70; + --fallback-erc: #000000; + } + } +} + +html { + -webkit-tap-highlight-color: transparent; +} + +* { + scrollbar-color: color-mix(in oklch, currentColor 35%, transparent) transparent; +} + +*:hover { + scrollbar-color: color-mix(in oklch, currentColor 60%, transparent) transparent; +} + +:root { + color-scheme: light; + --in: 72.06% 0.191 231.6; + --su: 64.8% 0.150 160; + --wa: 84.71% 0.199 83.87; + --er: 71.76% 0.221 22.18; + --pc: 89.824% 0.06192 275.75; + --ac: 15.352% 0.0368 183.61; + --inc: 0% 0 0; + --suc: 0% 0 0; + --wac: 0% 0 0; + --erc: 0% 0 0; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: .2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; + --p: 49.12% 0.3096 275.75; + --s: 69.71% 0.329 342.55; + --sc: 98.71% 0.0106 342.55; + --a: 76.76% 0.184 183.61; + --n: 32.1785% 0.02476 255.701624; + --nc: 89.4994% 0.011585 252.096176; + --b1: 100% 0 0; + --b2: 96.1151% 0 0; + --b3: 92.4169% 0.00108 197.137559; + --bc: 27.8078% 0.029596 256.847952; +} + +@media (prefers-color-scheme: dark) { + :root { + color-scheme: dark; + --in: 72.06% 0.191 231.6; + --su: 64.8% 0.150 160; + --wa: 84.71% 0.199 83.87; + --er: 71.76% 0.221 22.18; + --pc: 13.138% 0.0392 275.75; + --sc: 14.96% 0.052 342.55; + --ac: 14.902% 0.0334 183.61; + --inc: 0% 0 0; + --suc: 0% 0 0; + --wac: 0% 0 0; + --erc: 0% 0 0; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: .2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; + --p: 65.69% 0.196 275.75; + --s: 74.8% 0.26 342.55; + --a: 74.51% 0.167 183.61; + --n: 31.3815% 0.021108 254.139175; + --nc: 74.6477% 0.0216 264.435964; + --b1: 25.3267% 0.015896 252.417568; + --b2: 23.2607% 0.013807 253.100675; + --b3: 21.1484% 0.01165 254.087939; + --bc: 74.6477% 0.0216 264.435964; + } +} + +[data-theme=light] { + color-scheme: light; + --in: 72.06% 0.191 231.6; + --su: 64.8% 0.150 160; + --wa: 84.71% 0.199 83.87; + --er: 71.76% 0.221 22.18; + --pc: 89.824% 0.06192 275.75; + --ac: 15.352% 0.0368 183.61; + --inc: 0% 0 0; + --suc: 0% 0 0; + --wac: 0% 0 0; + --erc: 0% 0 0; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: .2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; + --p: 49.12% 0.3096 275.75; + --s: 69.71% 0.329 342.55; + --sc: 98.71% 0.0106 342.55; + --a: 76.76% 0.184 183.61; + --n: 32.1785% 0.02476 255.701624; + --nc: 89.4994% 0.011585 252.096176; + --b1: 100% 0 0; + --b2: 96.1151% 0 0; + --b3: 92.4169% 0.00108 197.137559; + --bc: 27.8078% 0.029596 256.847952; +} + +[data-theme=dark] { + color-scheme: dark; + --in: 72.06% 0.191 231.6; + --su: 64.8% 0.150 160; + --wa: 84.71% 0.199 83.87; + --er: 71.76% 0.221 22.18; + --pc: 13.138% 0.0392 275.75; + --sc: 14.96% 0.052 342.55; + --ac: 14.902% 0.0334 183.61; + --inc: 0% 0 0; + --suc: 0% 0 0; + --wac: 0% 0 0; + --erc: 0% 0 0; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: .2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; + --p: 65.69% 0.196 275.75; + --s: 74.8% 0.26 342.55; + --a: 74.51% 0.167 183.61; + --n: 31.3815% 0.021108 254.139175; + --nc: 74.6477% 0.0216 264.435964; + --b1: 25.3267% 0.015896 252.417568; + --b2: 23.2607% 0.013807 253.100675; + --b3: 21.1484% 0.01165 254.087939; + --bc: 74.6477% 0.0216 264.435964; +} + +@keyframes button-pop { + 0% { + transform: scale(var(--btn-focus-scale, 0.98)); + } + + 40% { + transform: scale(1.02); + } + + 100% { + transform: scale(1); + } +} + +@keyframes checkmark { + 0% { + background-position-y: 5px; + } + + 50% { + background-position-y: -2px; + } + + 100% { + background-position-y: 0; + } +} + +@keyframes modal-pop { + 0% { + opacity: 0; + } +} + +@keyframes progress-loading { + 50% { + background-position-x: -115%; + } +} + +@keyframes radiomark { + 0% { + box-shadow: 0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset, + 0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset; + } + + 50% { + box-shadow: 0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset, + 0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset; + } + + 100% { + box-shadow: 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset, + 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset; + } +} + +@keyframes rating-pop { + 0% { + transform: translateY(-0.125em); + } + + 40% { + transform: translateY(-0.125em); + } + + 100% { + transform: translateY(0); + } +} + +@keyframes skeleton { + from { + background-position: 150%; + } + + to { + background-position: -50%; + } +} + +@keyframes toast-pop { + 0% { + transform: scale(0.9); + opacity: 0; + } + + 100% { + transform: scale(1); + opacity: 1; + } +} \ No newline at end of file diff --git a/src/bin/server.rs b/src/bin/server.rs index f9d9113..a7d9168 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -7,7 +7,9 @@ use axum::{ use axum_session::{SessionConfig, SessionLayer, SessionStore}; use axum_session_auth::{AuthConfig, AuthSessionLayer}; use axum_session_surreal::SessionSurrealPool; -use std::sync::Arc; +use minijinja::{path_loader, Environment}; +use minijinja_autoreload::AutoReloader; +use std::{path::PathBuf, sync::Arc}; use surrealdb::{engine::any::Any, Surreal}; use tera::Tera; use tower_http::services::ServeDir; @@ -48,12 +50,23 @@ async fn main() -> Result<(), Box> { routing_key: "my_key".to_string(), }; + let reloader = AutoReloader::new(move |notifier| { + let template_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("templates"); + let mut env = Environment::new(); + env.set_loader(path_loader(&template_path)); + + notifier.set_fast_reload(true); + notifier.watch_path(&template_path, true); + Ok(env) + }); + let app_state = AppState { rabbitmq_producer: Arc::new(RabbitMQProducer::new(&config).await?), rabbitmq_consumer: Arc::new(RabbitMQConsumer::new(&config, false).await?), surreal_db_client: Arc::new(SurrealDbClient::new().await?), - tera: Arc::new(Tera::new("src/server/templates/**/*.html").unwrap()), + // tera: Arc::new(Tera::new("templates/**/*.html").unwrap()), openai_client: Arc::new(async_openai::Client::new()), + templates: Arc::new(reloader), }; // setup_auth(&app_state.surreal_db_client).await?; @@ -116,7 +129,7 @@ fn html_routes( .route("/", get(index_handler)) .route("/search", get(search_result_handler)) .route("/signup", get(show_signup_form).post(signup_handler)) - .nest_service("/assets", ServeDir::new("src/server/assets")) + .nest_service("/assets", ServeDir::new("assets/")) .layer( AuthSessionLayer::, Surreal>::new(Some( db_client, diff --git a/src/server/assets/style.css b/src/server/assets/style.css deleted file mode 100644 index 97d4dc0..0000000 --- a/src/server/assets/style.css +++ /dev/null @@ -1,2009 +0,0 @@ -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 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: ; -} - -/* -! tailwindcss v3.4.16 | 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; -} - -:root, -[data-theme] { - background-color: var(--fallback-b1,oklch(var(--b1)/1)); - color: var(--fallback-bc,oklch(var(--bc)/1)); -} - -@supports not (color: oklch(0% 0 0)) { - :root { - color-scheme: light; - --fallback-p: #491eff; - --fallback-pc: #d4dbff; - --fallback-s: #ff41c7; - --fallback-sc: #fff9fc; - --fallback-a: #00cfbd; - --fallback-ac: #00100d; - --fallback-n: #2b3440; - --fallback-nc: #d7dde4; - --fallback-b1: #ffffff; - --fallback-b2: #e5e6e6; - --fallback-b3: #e5e6e6; - --fallback-bc: #1f2937; - --fallback-in: #00b3f0; - --fallback-inc: #000000; - --fallback-su: #00ca92; - --fallback-suc: #000000; - --fallback-wa: #ffc22d; - --fallback-wac: #000000; - --fallback-er: #ff6f70; - --fallback-erc: #000000; - } - - @media (prefers-color-scheme: dark) { - :root { - color-scheme: dark; - --fallback-p: #7582ff; - --fallback-pc: #050617; - --fallback-s: #ff71cf; - --fallback-sc: #190211; - --fallback-a: #00c7b5; - --fallback-ac: #000e0c; - --fallback-n: #2a323c; - --fallback-nc: #a6adbb; - --fallback-b1: #1d232a; - --fallback-b2: #191e24; - --fallback-b3: #15191e; - --fallback-bc: #a6adbb; - --fallback-in: #00b3f0; - --fallback-inc: #000000; - --fallback-su: #00ca92; - --fallback-suc: #000000; - --fallback-wa: #ffc22d; - --fallback-wac: #000000; - --fallback-er: #ff6f70; - --fallback-erc: #000000; - } - } -} - -html { - -webkit-tap-highlight-color: transparent; -} - -* { - scrollbar-color: color-mix(in oklch, currentColor 35%, transparent) transparent; -} - -*:hover { - scrollbar-color: color-mix(in oklch, currentColor 60%, transparent) transparent; -} - -:root { - color-scheme: dark; - --in: 72.06% 0.191 231.6; - --su: 64.8% 0.150 160; - --wa: 84.71% 0.199 83.87; - --er: 71.76% 0.221 22.18; - --pc: 13.138% 0.0392 275.75; - --sc: 14.96% 0.052 342.55; - --ac: 14.902% 0.0334 183.61; - --inc: 0% 0 0; - --suc: 0% 0 0; - --wac: 0% 0 0; - --erc: 0% 0 0; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: .2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; - --p: 65.69% 0.196 275.75; - --s: 74.8% 0.26 342.55; - --a: 74.51% 0.167 183.61; - --n: 31.3815% 0.021108 254.139175; - --nc: 74.6477% 0.0216 264.435964; - --b1: 25.3267% 0.015896 252.417568; - --b2: 23.2607% 0.013807 253.100675; - --b3: 21.1484% 0.01165 254.087939; - --bc: 74.6477% 0.0216 264.435964; -} - -.container { - width: 100%; -} - -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} - -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} - -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} - -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} - -.avatar.placeholder > div { - display: flex; - align-items: center; - justify-content: center; -} - -@media (hover:hover) { - .label a:hover { - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - } - - .menu li > *:not(ul, .menu-title, details, .btn):active, -.menu li > *:not(ul, .menu-title, details, .btn).active, -.menu li > details > summary:active { - --tw-bg-opacity: 1; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); - } -} - -.btn { - display: inline-flex; - height: 3rem; - min-height: 3rem; - flex-shrink: 0; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - flex-wrap: wrap; - align-items: center; - justify-content: center; - border-radius: var(--rounded-btn, 0.5rem); - border-color: transparent; - border-color: oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity)); - padding-left: 1rem; - padding-right: 1rem; - text-align: center; - font-size: 0.875rem; - line-height: 1em; - gap: 0.5rem; - font-weight: 600; - text-decoration-line: none; - transition-duration: 200ms; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - border-width: var(--border-btn, 1px); - transition-property: color, background-color, border-color, opacity, box-shadow, transform; - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --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); - outline-color: var(--fallback-bc,oklch(var(--bc)/1)); - background-color: oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity)); - --tw-bg-opacity: 1; - --tw-border-opacity: 1; -} - -.btn-disabled, - .btn[disabled], - .btn:disabled { - pointer-events: none; -} - -:where(.btn:is(input[type="checkbox"])), -:where(.btn:is(input[type="radio"])) { - width: auto; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -.btn:is(input[type="checkbox"]):after, -.btn:is(input[type="radio"]):after { - --tw-content: attr(aria-label); - content: var(--tw-content); -} - -.card { - position: relative; - display: flex; - flex-direction: column; - border-radius: var(--rounded-box, 1rem); -} - -.card:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.card-body { - display: flex; - flex: 1 1 auto; - flex-direction: column; - padding: var(--padding-card, 2rem); - gap: 0.5rem; -} - -.card-body :where(p) { - flex-grow: 1; -} - -.card figure { - display: flex; - align-items: center; - justify-content: center; -} - -.card.image-full { - display: grid; -} - -.card.image-full:before { - position: relative; - content: ""; - z-index: 10; - border-radius: var(--rounded-box, 1rem); - --tw-bg-opacity: 1; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - opacity: 0.75; -} - -.card.image-full:before, - .card.image-full > * { - grid-column-start: 1; - grid-row-start: 1; -} - -.card.image-full > figure img { - height: 100%; - -o-object-fit: cover; - object-fit: cover; -} - -.card.image-full > .card-body { - position: relative; - z-index: 20; - --tw-text-opacity: 1; - color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); -} - -.chat { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - -moz-column-gap: 0.75rem; - column-gap: 0.75rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} - -.chat-bubble { - position: relative; - display: block; - width: -moz-fit-content; - width: fit-content; - padding-left: 1rem; - padding-right: 1rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; - max-width: 90%; - border-radius: var(--rounded-box, 1rem); - min-height: 2.75rem; - min-width: 2.75rem; - --tw-bg-opacity: 1; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); -} - -.chat-bubble:before { - position: absolute; - bottom: 0px; - height: 0.75rem; - width: 0.75rem; - background-color: inherit; - content: ""; - -webkit-mask-size: contain; - mask-size: contain; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-position: center; - mask-position: center; -} - -.chat-start { - place-items: start; - grid-template-columns: auto 1fr; -} - -.chat-start .chat-header { - grid-column-start: 2; -} - -.chat-start .chat-footer { - grid-column-start: 2; -} - -.chat-start .chat-image { - grid-column-start: 1; -} - -.chat-start .chat-bubble { - grid-column-start: 2; - border-end-start-radius: 0px; -} - -.chat-start .chat-bubble:before { - -webkit-mask-image: url("data:image/svg+xml,%3csvg width='3' height='3' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='m 0 3 L 3 3 L 3 0 C 3 1 1 3 0 3'/%3e%3c/svg%3e"); - mask-image: url("data:image/svg+xml,%3csvg width='3' height='3' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='m 0 3 L 3 3 L 3 0 C 3 1 1 3 0 3'/%3e%3c/svg%3e"); - inset-inline-start: -0.749rem; -} - -[dir="rtl"] .chat-start .chat-bubble:before { - -webkit-mask-image: url("data:image/svg+xml,%3csvg width='3' height='3' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='m 0 3 L 1 3 L 3 3 C 2 3 0 1 0 0'/%3e%3c/svg%3e"); - mask-image: url("data:image/svg+xml,%3csvg width='3' height='3' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='m 0 3 L 1 3 L 3 3 C 2 3 0 1 0 0'/%3e%3c/svg%3e"); -} - -.chat-end .chat-bubble { - grid-column-start: 1; - border-end-end-radius: 0px; -} - -.chat-end .chat-bubble:before { - -webkit-mask-image: url("data:image/svg+xml,%3csvg width='3' height='3' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='m 0 3 L 1 3 L 3 3 C 2 3 0 1 0 0'/%3e%3c/svg%3e"); - mask-image: url("data:image/svg+xml,%3csvg width='3' height='3' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='m 0 3 L 1 3 L 3 3 C 2 3 0 1 0 0'/%3e%3c/svg%3e"); - inset-inline-start: 99.9%; -} - -[dir="rtl"] .chat-end .chat-bubble:before { - -webkit-mask-image: url("data:image/svg+xml,%3csvg width='3' height='3' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='m 0 3 L 3 3 L 3 0 C 3 1 1 3 0 3'/%3e%3c/svg%3e"); - mask-image: url("data:image/svg+xml,%3csvg width='3' height='3' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='m 0 3 L 3 3 L 3 0 C 3 1 1 3 0 3'/%3e%3c/svg%3e"); -} - -.divider { - display: flex; - flex-direction: row; - align-items: center; - align-self: stretch; - margin-top: 1rem; - margin-bottom: 1rem; - height: 1rem; - white-space: nowrap; -} - -.divider:before, - .divider:after { - height: 0.125rem; - width: 100%; - flex-grow: 1; - --tw-content: ''; - content: var(--tw-content); - background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); -} - -@media (hover: hover) { - .btm-nav > *.disabled:hover, - .btm-nav > *[disabled]:hover { - pointer-events: none; - --tw-border-opacity: 0; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-bg-opacity: 0.1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; - } - - .btn:hover { - --tw-border-opacity: 1; - border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn:hover { - background-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity, 1)) 90%, - black - ); - border-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity, 1)) 90%, - black - ); - } - } - - @supports not (color: oklch(0% 0 0)) { - .btn:hover { - background-color: var(--btn-color, var(--fallback-b2)); - border-color: var(--btn-color, var(--fallback-b2)); - } - } - - .btn.glass:hover { - --glass-opacity: 25%; - --glass-border-opacity: 15%; - } - - .btn-ghost:hover { - border-color: transparent; - } - - @supports (color: oklch(0% 0 0)) { - .btn-ghost:hover { - background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); - } - } - - .btn-outline.btn-primary:hover { - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn-outline.btn-primary:hover { - background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - } - } - - .btn-disabled:hover, - .btn[disabled]:hover, - .btn:disabled:hover { - --tw-border-opacity: 0; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-bg-opacity: 0.2; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn:is(input[type="checkbox"]:checked):hover, .btn:is(input[type="radio"]:checked):hover { - background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - } - } - - :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(.active, .btn):hover, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(.active, .btn):hover { - cursor: pointer; - outline: 2px solid transparent; - outline-offset: 2px; - } - - @supports (color: oklch(0% 0 0)) { - :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(.active, .btn):hover, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(.active, .btn):hover { - background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); - } - } -} - -.form-control { - display: flex; - flex-direction: column; -} - -.label { - display: flex; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - align-items: center; - justify-content: space-between; - padding-left: 0.25rem; - padding-right: 0.25rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.indicator { - position: relative; - display: inline-flex; - width: -moz-max-content; - width: max-content; -} - -.indicator :where(.indicator-item) { - z-index: 1; - position: absolute; - 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)); - white-space: nowrap; -} - -.input { - flex-shrink: 1; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - height: 3rem; - padding-left: 1rem; - padding-right: 1rem; - font-size: 1rem; - line-height: 2; - line-height: 1.5rem; - border-radius: var(--rounded-btn, 0.5rem); - border-width: 1px; - border-color: transparent; - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); -} - -.input[type="number"]::-webkit-inner-spin-button, -.input-md[type="number"]::-webkit-inner-spin-button { - margin-top: -1rem; - margin-bottom: -1rem; - margin-inline-end: -1rem; -} - -.link { - cursor: pointer; - text-decoration-line: underline; -} - -.menu { - display: flex; - flex-direction: column; - flex-wrap: wrap; - font-size: 0.875rem; - line-height: 1.25rem; - padding: 0.5rem; -} - -.menu :where(li ul) { - position: relative; - white-space: nowrap; - margin-inline-start: 1rem; - padding-inline-start: 0.5rem; -} - -.menu :where(li:not(.menu-title) > *:not(ul, details, .menu-title, .btn)), .menu :where(li:not(.menu-title) > details > summary:not(.menu-title)) { - display: grid; - grid-auto-flow: column; - align-content: flex-start; - align-items: center; - gap: 0.5rem; - grid-auto-columns: minmax(auto, max-content) auto max-content; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} - -.menu li.disabled { - cursor: not-allowed; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - color: var(--fallback-bc,oklch(var(--bc)/0.3)); -} - -.menu :where(li > .menu-dropdown:not(.menu-dropdown-show)) { - display: none; -} - -:where(.menu li) { - position: relative; - display: flex; - flex-shrink: 0; - flex-direction: column; - flex-wrap: wrap; - align-items: stretch; -} - -:where(.menu li) .badge { - justify-self: end; -} - -.navbar { - display: flex; - align-items: center; - padding: var(--navbar-padding, 0.5rem); - min-height: 4rem; - width: 100%; -} - -:where(.navbar > *:not(script, style)) { - display: inline-flex; - align-items: center; -} - -.btm-nav > *.disabled, - .btm-nav > *[disabled] { - pointer-events: none; - --tw-border-opacity: 0; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-bg-opacity: 0.1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; -} - -.btm-nav > * .label { - font-size: 1rem; - line-height: 1.5rem; -} - -@media (prefers-reduced-motion: no-preference) { - .btn { - animation: button-pop var(--animation-btn, 0.25s) ease-out; - } -} - -.btn:active:hover, - .btn:active:focus { - animation: button-pop 0s ease-out; - transform: scale(var(--btn-focus-scale, 0.97)); -} - -@supports not (color: oklch(0% 0 0)) { - .btn { - background-color: var(--btn-color, var(--fallback-b2)); - border-color: var(--btn-color, var(--fallback-b2)); - } - - .btn-primary { - --btn-color: var(--fallback-p); - } -} - -@supports (color: color-mix(in oklab, black, black)) { - .btn-outline.btn-primary.btn-active { - background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - } -} - -.btn:focus-visible { - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; -} - -.btn-primary { - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); - outline-color: var(--fallback-p,oklch(var(--p)/1)); -} - -@supports (color: oklch(0% 0 0)) { - .btn-primary { - --btn-color: var(--p); - } -} - -.btn.glass { - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - outline-color: currentColor; -} - -.btn.glass.btn-active { - --glass-opacity: 25%; - --glass-border-opacity: 15%; -} - -.btn-ghost { - border-width: 1px; - border-color: transparent; - background-color: transparent; - color: currentColor; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - outline-color: currentColor; -} - -.btn-ghost.btn-active { - border-color: transparent; - background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.btn-outline.btn-primary { - --tw-text-opacity: 1; - color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); -} - -.btn-outline.btn-primary.btn-active { - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); -} - -.btn.btn-disabled, - .btn[disabled], - .btn:disabled { - --tw-border-opacity: 0; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-bg-opacity: 0.2; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; -} - -.btn:is(input[type="checkbox"]:checked), -.btn:is(input[type="radio"]:checked) { - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); -} - -.btn:is(input[type="checkbox"]:checked):focus-visible, .btn:is(input[type="radio"]:checked):focus-visible { - outline-color: var(--fallback-p,oklch(var(--p)/1)); -} - -@keyframes button-pop { - 0% { - transform: scale(var(--btn-focus-scale, 0.98)); - } - - 40% { - transform: scale(1.02); - } - - 100% { - transform: scale(1); - } -} - -.card :where(figure:first-child) { - overflow: hidden; - border-start-start-radius: inherit; - border-start-end-radius: inherit; - border-end-start-radius: unset; - border-end-end-radius: unset; -} - -.card :where(figure:last-child) { - overflow: hidden; - border-start-start-radius: unset; - border-start-end-radius: unset; - border-end-start-radius: inherit; - border-end-end-radius: inherit; -} - -.card:focus-visible { - outline: 2px solid currentColor; - outline-offset: 2px; -} - -.card.bordered { - border-width: 1px; - --tw-border-opacity: 1; - border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); -} - -.card.compact .card-body { - padding: 1rem; - font-size: 0.875rem; - line-height: 1.25rem; -} - -.card-title { - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 1.25rem; - line-height: 1.75rem; - font-weight: 600; -} - -.card.image-full :where(figure) { - overflow: hidden; - border-radius: inherit; -} - -@keyframes checkmark { - 0% { - background-position-y: 5px; - } - - 50% { - background-position-y: -2px; - } - - 100% { - background-position-y: 0; - } -} - -.divider:not(:empty) { - gap: 1rem; -} - -.label-text { - font-size: 0.875rem; - line-height: 1.25rem; - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); -} - -.input input { - --tw-bg-opacity: 1; - background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); - background-color: transparent; -} - -.input input:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.input[list]::-webkit-calendar-picker-indicator { - line-height: 1em; -} - -.input-bordered { - border-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.input:focus, - .input:focus-within { - box-shadow: none; - border-color: var(--fallback-bc,oklch(var(--bc)/0.2)); - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; - outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.input:has(> input[disabled]), - .input-disabled, - .input:disabled, - .input[disabled] { - cursor: not-allowed; - --tw-border-opacity: 1; - border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); - color: var(--fallback-bc,oklch(var(--bc)/0.4)); -} - -.input:has(> input[disabled])::-moz-placeholder, .input-disabled::-moz-placeholder, .input:disabled::-moz-placeholder, .input[disabled]::-moz-placeholder { - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); - --tw-placeholder-opacity: 0.2; -} - -.input:has(> input[disabled])::placeholder, - .input-disabled::placeholder, - .input:disabled::placeholder, - .input[disabled]::placeholder { - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); - --tw-placeholder-opacity: 0.2; -} - -.input:has(> input[disabled]) > input[disabled] { - cursor: not-allowed; -} - -.input::-webkit-date-and-time-value { - text-align: inherit; -} - -.join > :where(*:not(:first-child)):is(.btn) { - margin-inline-start: calc(var(--border-btn) * -1); -} - -.link-primary { - --tw-text-opacity: 1; - color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); -} - -@supports (color:color-mix(in oklab,black,black)) { - @media (hover:hover) { - .link-primary:hover { - color: color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 80%,black); - } - } -} - -.link:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.link:focus-visible { - outline: 2px solid currentColor; - outline-offset: 2px; -} - -.loading { - pointer-events: none; - display: inline-block; - aspect-ratio: 1 / 1; - width: 1.5rem; - background-color: currentColor; - -webkit-mask-size: 100%; - mask-size: 100%; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-position: center; - mask-position: center; - -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); - mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); -} - -.loading-spinner { - -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); - mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); -} - -:where(.menu li:empty) { - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); - opacity: 0.1; - margin: 0.5rem 1rem; - height: 1px; -} - -.menu :where(li ul):before { - position: absolute; - bottom: 0.75rem; - inset-inline-start: 0px; - top: 0.75rem; - width: 1px; - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); - opacity: 0.1; - content: ""; -} - -.menu :where(li:not(.menu-title) > *:not(ul, details, .menu-title, .btn)), -.menu :where(li:not(.menu-title) > details > summary:not(.menu-title)) { - border-radius: var(--rounded-btn, 0.5rem); - padding-left: 1rem; - padding-right: 1rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; - text-align: start; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-duration: 200ms; - text-wrap: balance; -} - -:where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(summary, .active, .btn).focus, :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(summary, .active, .btn):focus, :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):is(summary):not(.active, .btn):focus-visible, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(summary, .active, .btn).focus, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(summary, .active, .btn):focus, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):is(summary):not(.active, .btn):focus-visible { - cursor: pointer; - background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - outline: 2px solid transparent; - outline-offset: 2px; -} - -.menu li > *:not(ul, .menu-title, details, .btn):active, -.menu li > *:not(ul, .menu-title, details, .btn).active, -.menu li > details > summary:active { - --tw-bg-opacity: 1; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); -} - -.menu :where(li > details > summary)::-webkit-details-marker { - display: none; -} - -.menu :where(li > details > summary):after, -.menu :where(li > .menu-dropdown-toggle):after { - justify-self: end; - display: block; - margin-top: -0.5rem; - height: 0.5rem; - width: 0.5rem; - transform: rotate(45deg); - transition-property: transform, margin-top; - transition-duration: 0.3s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - content: ""; - transform-origin: 75% 75%; - box-shadow: 2px 2px; - pointer-events: none; -} - -.menu :where(li > details[open] > summary):after, -.menu :where(li > .menu-dropdown-toggle.menu-dropdown-show):after { - transform: rotate(225deg); - margin-top: 0; -} - -.mockup-browser .mockup-browser-toolbar .input { - position: relative; - margin-left: auto; - margin-right: auto; - display: block; - height: 1.75rem; - width: 24rem; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); - padding-left: 2rem; - direction: ltr; -} - -.mockup-browser .mockup-browser-toolbar .input:before { - content: ""; - position: absolute; - left: 0.5rem; - top: 50%; - aspect-ratio: 1 / 1; - height: 0.75rem; - --tw-translate-y: -50%; - 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)); - border-radius: 9999px; - border-width: 2px; - border-color: currentColor; - opacity: 0.6; -} - -.mockup-browser .mockup-browser-toolbar .input:after { - content: ""; - position: absolute; - left: 1.25rem; - top: 50%; - height: 0.5rem; - --tw-translate-y: 25%; - --tw-rotate: -45deg; - 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)); - border-radius: 9999px; - border-width: 1px; - border-color: currentColor; - opacity: 0.6; -} - -@keyframes modal-pop { - 0% { - opacity: 0; - } -} - -@keyframes progress-loading { - 50% { - background-position-x: -115%; - } -} - -@keyframes radiomark { - 0% { - box-shadow: 0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset, - 0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset; - } - - 50% { - box-shadow: 0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset, - 0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset; - } - - 100% { - box-shadow: 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset, - 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset; - } -} - -@keyframes rating-pop { - 0% { - transform: translateY(-0.125em); - } - - 40% { - transform: translateY(-0.125em); - } - - 100% { - transform: translateY(0); - } -} - -@keyframes skeleton { - from { - background-position: 150%; - } - - to { - background-position: -50%; - } -} - -@keyframes toast-pop { - 0% { - transform: scale(0.9); - opacity: 0; - } - - 100% { - transform: scale(1); - opacity: 1; - } -} - -.indicator :where(.indicator-item) { - bottom: auto; - inset-inline-end: 0px; - inset-inline-start: auto; - top: 0px; - --tw-translate-y: -50%; - --tw-translate-x: 50%; - 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)); -} - -.indicator :where(.indicator-item):where([dir="rtl"], [dir="rtl"] *) { - --tw-translate-x: -50%; - 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)); -} - -.indicator :where(.indicator-item.indicator-start) { - inset-inline-end: auto; - inset-inline-start: 0px; - --tw-translate-x: -50%; - 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)); -} - -.indicator :where(.indicator-item.indicator-start):where([dir="rtl"], [dir="rtl"] *) { - --tw-translate-x: 50%; - 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)); -} - -.indicator :where(.indicator-item.indicator-center) { - inset-inline-end: 50%; - inset-inline-start: 50%; - --tw-translate-x: -50%; - 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)); -} - -.indicator :where(.indicator-item.indicator-center):where([dir="rtl"], [dir="rtl"] *) { - --tw-translate-x: 50%; - 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)); -} - -.indicator :where(.indicator-item.indicator-end) { - inset-inline-end: 0px; - inset-inline-start: auto; - --tw-translate-x: 50%; - 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)); -} - -.indicator :where(.indicator-item.indicator-end):where([dir="rtl"], [dir="rtl"] *) { - --tw-translate-x: -50%; - 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)); -} - -.indicator :where(.indicator-item.indicator-bottom) { - bottom: 0px; - top: auto; - --tw-translate-y: 50%; - 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)); -} - -.indicator :where(.indicator-item.indicator-middle) { - bottom: 50%; - top: 50%; - --tw-translate-y: -50%; - 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)); -} - -.indicator :where(.indicator-item.indicator-top) { - bottom: auto; - top: 0px; - --tw-translate-y: -50%; - 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)); -} - -.menu-horizontal { - display: inline-flex; - flex-direction: row; -} - -.menu-horizontal > li:not(.menu-title) > details > ul { - position: absolute; -} - -.card-compact .card-body { - padding: 1rem; - font-size: 0.875rem; - line-height: 1.25rem; -} - -.card-compact .card-title { - margin-bottom: 0.25rem; -} - -.card-normal .card-body { - padding: var(--padding-card, 2rem); - font-size: 1rem; - line-height: 1.5rem; -} - -.card-normal .card-title { - margin-bottom: 0.75rem; -} - -.join.join-vertical > :where(*:not(:first-child)):is(.btn) { - margin-top: calc(var(--border-btn) * -1); -} - -.join.join-horizontal > :where(*:not(:first-child)):is(.btn) { - margin-inline-start: calc(var(--border-btn) * -1); - margin-top: 0px; -} - -.menu-horizontal > li:not(.menu-title) > details > ul { - margin-inline-start: 0px; - margin-top: 1rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-inline-end: 0.5rem; -} - -.menu-horizontal > li > details > ul:before { - content: none; -} - -:where(.menu-horizontal > li:not(.menu-title) > details > ul) { - border-radius: var(--rounded-box, 1rem); - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); - --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); -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.mb-8 { - margin-bottom: 2rem; -} - -.mt-4 { - margin-top: 1rem; -} - -.mt-6 { - margin-top: 1.5rem; -} - -.block { - display: block; -} - -.flex { - display: flex; -} - -.hidden { - display: none; -} - -.min-h-\[80vh\] { - min-height: 80vh; -} - -.min-h-screen { - min-height: 100vh; -} - -.w-96 { - width: 24rem; -} - -.w-full { - width: 100%; -} - -.max-w-2xl { - max-width: 42rem; -} - -.flex-1 { - flex: 1 1 0%; -} - -.flex-none { - flex: none; -} - -.flex-col { - flex-direction: column; -} - -.items-center { - align-items: center; -} - -.justify-center { - justify-content: center; -} - -.space-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); -} - -.space-y-8 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(2rem * var(--tw-space-y-reverse)); -} - -.rounded-t-none { - border-top-left-radius: 0px; - border-top-right-radius: 0px; -} - -.bg-base-100 { - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity, 1))); -} - -.bg-base-200 { - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity, 1))); -} - -.bg-gradient-to-r { - background-image: linear-gradient(to right, var(--tw-gradient-stops)); -} - -.from-blue-400 { - --tw-gradient-from: #60a5fa var(--tw-gradient-from-position); - --tw-gradient-to: rgb(96 165 250 / 0) var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} - -.via-purple-500 { - --tw-gradient-to: rgb(168 85 247 / 0) var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), #a855f7 var(--tw-gradient-via-position), var(--tw-gradient-to); -} - -.to-pink-500 { - --tw-gradient-to: #ec4899 var(--tw-gradient-to-position); -} - -.bg-clip-text { - -webkit-background-clip: text; - background-clip: text; -} - -.p-2 { - padding: 0.5rem; -} - -.px-1 { - padding-left: 0.25rem; - padding-right: 0.25rem; -} - -.px-4 { - padding-left: 1rem; - padding-right: 1rem; -} - -.py-8 { - padding-top: 2rem; - padding-bottom: 2rem; -} - -.text-center { - text-align: center; -} - -.text-2xl { - font-size: 1.5rem; - line-height: 2rem; -} - -.text-5xl { - font-size: 3rem; - line-height: 1; -} - -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} - -.text-sm { - font-size: 0.875rem; - line-height: 1.25rem; -} - -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - -.font-bold { - font-weight: 700; -} - -.text-gray-400 { - --tw-text-opacity: 1; - color: rgb(156 163 175 / var(--tw-text-opacity, 1)); -} - -.text-transparent { - color: transparent; -} - -.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); -} \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index c5070ee..e8f1d97 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,8 +1,9 @@ use crate::rabbitmq::consumer::RabbitMQConsumer; use crate::rabbitmq::publisher::RabbitMQProducer; use crate::storage::db::SurrealDbClient; +use minijinja_autoreload::AutoReloader; use std::sync::Arc; -use tera::Tera; +// use tera::Tera; pub mod middleware_api_auth; pub mod routes; @@ -12,6 +13,7 @@ pub struct AppState { pub rabbitmq_producer: Arc, pub rabbitmq_consumer: Arc, pub surreal_db_client: Arc, - pub tera: Arc, + // pub tera: Arc, pub openai_client: Arc>, + pub templates: Arc, } diff --git a/src/server/routes/auth.rs b/src/server/routes/auth.rs index e0b61f3..c26c7c6 100644 --- a/src/server/routes/auth.rs +++ b/src/server/routes/auth.rs @@ -3,6 +3,7 @@ use axum::{ response::{Html, IntoResponse}, Form, }; +use axum_htmx::HxBoosted; use axum_session_auth::AuthSession; use axum_session_surreal::SessionSurrealPool; use serde::{Deserialize, Serialize}; @@ -16,13 +17,17 @@ pub struct SignupParams { pub password: String, } -pub async fn show_signup_form(State(state): State) -> Html { - let context = tera::Context::new(); - let html = state - .tera - .render("auth/signup.html", &context) - .unwrap_or_else(|_| "

Error rendering template

".to_string()); - Html(html) +pub async fn show_signup_form( + State(state): State, + HxBoosted(boosted): HxBoosted, +) -> Html { + let mut context = tera::Context::new(); + context.insert("boosted", &boosted); + // let html = state + // .tera + // .render("auth/signup_form.html", &context) + // .unwrap_or_else(|_| "

Error rendering template

".to_string()); + Html("html".to_string()) } pub async fn signup_handler( diff --git a/src/server/routes/index.rs b/src/server/routes/index.rs index c544ece..82dd44e 100644 --- a/src/server/routes/index.rs +++ b/src/server/routes/index.rs @@ -1,9 +1,11 @@ use axum::{extract::State, response::Html}; use axum_session_auth::AuthSession; use axum_session_surreal::SessionSurrealPool; +use minijinja::context; use serde_json::json; -use surrealdb::{engine::any::Any, Surreal}; +use surrealdb::{engine::any::Any, sql::Relation, Surreal}; use tera::Context; +// use tera::Context; use tracing::info; use crate::{error::ApiError, server::AppState, storage::types::user::User}; @@ -18,14 +20,21 @@ pub async fn index_handler( let queue_length = state.rabbitmq_consumer.get_queue_length().await?; - let output = state - .tera - .render( - "index.html", - &Context::from_value(json!({"adjective": "CRAYCRAY", "queue_length": queue_length})) - .unwrap(), - ) - .unwrap(); + // let output = state + // .tera + // .render( + // "index.html", + // &Context::from_value(json!({"adjective": "CRAYCRAY", "queue_length": queue_length})) + // .unwrap(), + // ) + // .unwrap(); + + // Ok(output.into()) + // + let env = state.templates.acquire_env().unwrap(); + let context = context!(queue_length => "2000"); + let tmpl = env.get_template("index.html").unwrap(); + let output = tmpl.render(context).unwrap(); Ok(output.into()) } diff --git a/src/server/routes/search_result.rs b/src/server/routes/search_result.rs index b348ce3..992a58f 100644 --- a/src/server/routes/search_result.rs +++ b/src/server/routes/search_result.rs @@ -37,16 +37,17 @@ pub async fn search_result_handler( ) .await?; - let output = state - .tera - .render( - "search_result.html", - &Context::from_value( - json!({"result": answer.content, "references": answer.references}), - ) - .unwrap(), - ) - .unwrap(); + Ok(Html("Hello".to_string())) + // let output = state + // .tera + // .render( + // "search_result.html", + // &Context::from_value( + // json!({"result": answer.content, "references": answer.references}), + // ) + // .unwrap(), + // ) + // .unwrap(); - Ok(output.into()) + // Ok(output.into()) } diff --git a/src/server/templates/auth/signup.html b/src/server/templates/auth/signup.html deleted file mode 100644 index 4761c57..0000000 --- a/src/server/templates/auth/signup.html +++ /dev/null @@ -1,62 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -
-
-
-

Sign Up

- -
- -
- - -
- -
- - -
- -
- -
- -
- -
OR
- -
- Already have an account? - Login -
-
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 354ad6e..f4b1d3d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -8,7 +8,7 @@ module.exports = { }, plugins: [require('daisyui')], daisyui: { - themes: ["dark"], + themes: ["light", "dark"], }, } diff --git a/templates/auth/login.html b/templates/auth/login.html new file mode 100644 index 0000000..f508d35 --- /dev/null +++ b/templates/auth/login.html @@ -0,0 +1,47 @@ +{% block content %} +
+

Sign in to your account

+ +
+
+ + +
+ +
+ + +
+ +
+ + Forgot password? +
+ +
+ +
+ +
+ +
OR
+ +
+ Don't have an account? + Create one +
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/auth/signup.html b/templates/auth/signup.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/auth/signup_form.html b/templates/auth/signup_form.html new file mode 100644 index 0000000..a3e5eca --- /dev/null +++ b/templates/auth/signup_form.html @@ -0,0 +1,43 @@ +{% block content %} +
+
+

Create your account

+ +
+
+ + +
+ +
+ + + +
+ +
+ +
+ +
+ +
OR
+ +
+ Already have an account? + Sign in +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/base_template.html b/templates/base_template.html new file mode 100644 index 0000000..6bb170e --- /dev/null +++ b/templates/base_template.html @@ -0,0 +1,14 @@ + + + + + + + {% block title %}minnet{% endblock %} + + + {% block head %}{% endblock %} + +{% block body %}{% endblock %} + + \ No newline at end of file diff --git a/templates/body_base.html b/templates/body_base.html new file mode 100644 index 0000000..edd12e8 --- /dev/null +++ b/templates/body_base.html @@ -0,0 +1,31 @@ +{% extends "head_base.html" %} + +{% block body %} + + + + +
+ {% block content %}{% endblock %} +
+ +{% endblock %} \ No newline at end of file diff --git a/src/server/templates/base.html b/templates/full_body_template.html similarity index 57% rename from src/server/templates/base.html rename to templates/full_body_template.html index ec8fa87..ddf7df0 100644 --- a/src/server/templates/base.html +++ b/templates/full_body_template.html @@ -1,23 +1,13 @@ - - - - - - - {% block title %}radien{% endblock %} - - - -