considering tera and minijinja

This commit is contained in:
Per Stark
2024-12-18 18:24:18 +01:00
parent 291c473d00
commit fa6283485c
22 changed files with 1211 additions and 2125 deletions

138
Cargo.lock generated
View File

@@ -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",

View File

@@ -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"

852
assets/style.css Normal file
View File

@@ -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;
}
}

View File

@@ -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<dyn std::error::Error>> {
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::<User, String, SessionSurrealPool<Any>, Surreal<Any>>::new(Some(
db_client,

File diff suppressed because it is too large Load Diff

View File

@@ -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<RabbitMQProducer>,
pub rabbitmq_consumer: Arc<RabbitMQConsumer>,
pub surreal_db_client: Arc<SurrealDbClient>,
pub tera: Arc<Tera>,
// pub tera: Arc<Tera>,
pub openai_client: Arc<async_openai::Client<async_openai::config::OpenAIConfig>>,
pub templates: Arc<AutoReloader>,
}

View File

@@ -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<AppState>) -> Html<String> {
let context = tera::Context::new();
let html = state
.tera
.render("auth/signup.html", &context)
.unwrap_or_else(|_| "<h1>Error rendering template</h1>".to_string());
Html(html)
pub async fn show_signup_form(
State(state): State<AppState>,
HxBoosted(boosted): HxBoosted,
) -> Html<String> {
let mut context = tera::Context::new();
context.insert("boosted", &boosted);
// let html = state
// .tera
// .render("auth/signup_form.html", &context)
// .unwrap_or_else(|_| "<h1>Error rendering template</h1>".to_string());
Html("html".to_string())
}
pub async fn signup_handler(

View File

@@ -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())
}

View File

@@ -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())
}

View File

@@ -1,62 +0,0 @@
{% extends "base.html" %}
{% block content %}
<div class="min-h-screen bg-base-200 flex items-center justify-center">
<div class="card w-96 bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl font-bold text-center mb-4">Sign Up</h2>
<form hx-post="/signup" hx-target="#signup-result" class="space-y-4">
<div class="form-control w-full">
<label class="label">
<span class="label-text">Email</span>
</label>
<input type="text" name="email" placeholder="Enter username" class="input input-bordered w-full" required />
</div>
<div class="form-control w-full">
<label class="label">
<span class="label-text">Password</span>
</label>
<input type="password" name="password" placeholder="Enter password" class="input input-bordered w-full"
required />
</div>
<div class="form-control mt-6">
<button class="btn btn-primary">
Sign Up
<span class="loading loading-spinner hidden"></span>
</button>
</div>
<div id="signup-result"></div>
<div class="divider">OR</div>
<div class="text-center text-sm">
Already have an account?
<a href="/login" class="link link-primary">Login</a>
</div>
</form>
</div>
</div>
</div>
<!-- Add loading indicator when form is submitting -->
<script>
document.body.addEventListener('htmx:beforeRequest', function (evt) {
if (evt.target.tagName === 'FORM') {
evt.target.querySelector('.loading-spinner').classList.remove('hidden');
evt.target.querySelector('button').disabled = true;
}
});
document.body.addEventListener('htmx:afterRequest', function (evt) {
if (evt.target.tagName === 'FORM') {
evt.target.querySelector('.loading-spinner').classList.add('hidden');
evt.target.querySelector('button').disabled = false;
}
});
</script>
{% endblock %}

View File

@@ -8,7 +8,7 @@ module.exports = {
},
plugins: [require('daisyui')],
daisyui: {
themes: ["dark"],
themes: ["light", "dark"],
},
}

47
templates/auth/login.html Normal file
View File

@@ -0,0 +1,47 @@
{% block content %}
<div class="max-w-md mx-auto">
<h2 class="text-2xl font-bold text-center mb-8">Sign in to your account</h2>
<form hx-post="/signin" hx-target="#signin-result" hx-indicator="#spinner" class="space-y-6">
<div class="form-control">
<label class="label">
<span class="label-text">Email</span>
</label>
<input type="email" name="email" placeholder="Enter your email" class="input input-bordered w-full" required
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$" />
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Password</span>
</label>
<input type="password" name="password" placeholder="Enter your password" class="input input-bordered w-full"
required />
</div>
<div class="flex items-center justify-between">
<label class="label cursor-pointer">
<input type="checkbox" name="remember" class="checkbox checkbox-primary checkbox-sm mr-2" />
<span class="label-text">Remember me</span>
</label>
<a href="/forgot-password" class="link link-primary text-sm">Forgot password?</a>
</div>
<div class="form-control mt-6">
<button class="btn btn-primary w-full">
Sign in
<span id="spinner" class="loading loading-spinner text-primary htmx-indicator"></span>
</button>
</div>
<div id="signin-result"></div>
<div class="divider">OR</div>
<div class="text-center text-sm">
Don't have an account?
<a href="/signup" class="link link-primary">Create one</a>
</div>
</form>
</div>
{% endblock %}

View File

View File

@@ -0,0 +1,43 @@
{% block content %}
<div class="max-h-full grid place-items-center place-content-center">
<div class="max-w-md mx-auto">
<h2 class="text-2xl font-bold text-center mb-8">Create your account</h2>
<form hx-post="/signup" hx-target="#signup-result" hx-indicator="#spinner" class="" psac-y-e6>
<div class="form-control">
<label class="label">
<span class="label-text">Email</span>
</label>
<input type="email" name="email" placeholder="Enter your email" class="input input-bordered w-full" required
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$" />
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Password</span>
</label>
<input type="password" name="password" placeholder="Create a password" class="input input-bordered w-full"
required minlength="8" />
<input type="password" name="confirm_password" placeholder="Confirm your password"
class="input mt-2 input-bordered w-full" required minlength="8" />
</div>
<div class="form-control mt-6">
<button class="btn btn-primary w-full">
Create Account
<span id="spinner" class="loading loading-spinner text-primary htmx-indicator"></span>
</button>
</div>
<div id="signup-result"></div>
<div class="divider">OR</div>
<div class="text-center text-sm">
Already have an account?
<a href="/signin" class="link link-primary">Sign in</a>
</div>
</form>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}minnet{% endblock %}</title>
<script src="https://unpkg.com/htmx.org@1.9.2"></script>
<link rel="stylesheet" href="assets/style.css">
{% block head %}{% endblock %}
</head>
{% block body %}{% endblock %}
</html>

31
templates/body_base.html Normal file
View File

@@ -0,0 +1,31 @@
{% extends "head_base.html" %}
{% block body %}
<body class="min-h-screen">
<nav class="navbar bg-base-200">
<div class="flex-1">
<a class="btn text-xl border-transparent btn-outline btn-primary" href="/">minnet</a>
</div>
<div class="flex-none">
<ul class="menu menu-horizontal px-1">
<li><a hx-boost="true" href="/signin">Login</a></li>
<li><a hx-boost="true" href="/signup">Sign up</a></li>
<li>
<details>
<summary>Parent</summary>
<ul class="bg-base-100 rounded-t-none p-2">
<li><a>Link 1</a></li>
<li><a>Link 2</a></li>
</ul>
</details>
</li>
</ul>
</div>
</nav>
<main class="container mx-auto px-4 py-8">
{% block content %}{% endblock %}
</main>
</body>
{% endblock %}

View File

@@ -1,23 +1,13 @@
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}radien{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="assets/style.css">
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
</head>
<body class="min-h-screen">
<!-- Navbar -->
<nav class="navbar bg-base-200">
<div class="flex-1">
<a class="btn btn-ghost text-xl">radien</a>
<a class="btn text-xl border-transparent btn-outline btn-primary" href="/">minnet</a>
</div>
<div class="flex-none">
<ul class="menu menu-horizontal px-1">
<li><a>Link</a></li>
<li><a href="/signin">Login</a></li>
<li><a hx-boost="true" href="/signup">Sign up</a></li>
<li>
<details>
<summary>Parent</summary>
@@ -35,6 +25,4 @@
<main class="container mx-auto px-4 py-8">
{% block content %}{% endblock %}
</main>
</body>
</html>
</body>

14
templates/head_base.html Normal file
View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}minnet{% endblock %}</title>
<script src="https://unpkg.com/htmx.org@1.9.2"></script>
<link rel="stylesheet" href="assets/style.css">
{% block head %}{% endblock %}
</head>
{% block body %}{% endblock %}
</html>

View File

@@ -1,11 +1,9 @@
{% extends "base.html" %}
{% block content %}
<div class="flex flex-col items-center justify-center min-h-[80vh] space-y-8">
<!-- Hero Section -->
<div class="text-center space-y-4 mb-8">
<h1
class="text-5xl font-bold bg-gradient-to-r from-blue-400 via-purple-500 to-pink-500 text-transparent bg-clip-text">
Welcome to radien
Welcome to Minnet
</h1>
<p class="text-gray-400 text-xl">
An experiment in creating a second brain
@@ -26,5 +24,4 @@
<div id="search-results" class="w-full max-w-2xl mt-4">
<!-- Results will be populated here by HTMX -->
</div>
</div>
{% endblock %}
</div>