mirror of
https://github.com/perstarkse/minne.git
synced 2026-03-12 21:35:40 +01:00
wip: plotly & additional documentation
This commit is contained in:
125
Cargo.lock
generated
125
Cargo.lock
generated
@@ -661,6 +661,15 @@ version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "basic-toml"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "0.15.1"
|
||||
@@ -1403,6 +1412,12 @@ dependencies = [
|
||||
"dtoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35"
|
||||
|
||||
[[package]]
|
||||
name = "earcutr"
|
||||
version = "0.4.3"
|
||||
@@ -1471,6 +1486,16 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"typeid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.9"
|
||||
@@ -2036,6 +2061,15 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
@@ -3353,6 +3387,36 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "plotly"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0746e9faf2b051db76470fd428cbc0db792db05346dedaae4a75b16d7be503b5"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"erased-serde",
|
||||
"once_cell",
|
||||
"plotly_derive",
|
||||
"rand",
|
||||
"rinja",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plotly_derive"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d683930282f098b9f524e2596e3e63483507ac499231c96127fcb166bc05d26"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.6.2"
|
||||
@@ -3826,6 +3890,49 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rinja"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5"
|
||||
dependencies = [
|
||||
"humansize",
|
||||
"itoa",
|
||||
"percent-encoding",
|
||||
"rinja_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rinja_derive"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b"
|
||||
dependencies = [
|
||||
"basic-toml",
|
||||
"memchr",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rinja_parser",
|
||||
"rustc-hash 2.0.0",
|
||||
"serde",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rinja_parser"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"nom",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv"
|
||||
version = "0.7.45"
|
||||
@@ -4275,6 +4382,17 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
@@ -5264,6 +5382,12 @@ dependencies = [
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typeid"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
@@ -5925,6 +6049,7 @@ dependencies = [
|
||||
"minijinja-autoreload",
|
||||
"minijinja-contrib",
|
||||
"mockall",
|
||||
"plotly",
|
||||
"reqwest",
|
||||
"scraper",
|
||||
"serde",
|
||||
|
||||
@@ -23,6 +23,7 @@ minijinja = { version = "2.5.0", features = ["loader", "multi_template"] }
|
||||
minijinja-autoreload = "2.5.0"
|
||||
minijinja-contrib = { version = "2.6.0", features = ["datetime", "timezone"] }
|
||||
mockall = "0.13.0"
|
||||
plotly = "0.12.1"
|
||||
reqwest = {version = "0.12.12", features = ["charset", "json"]}
|
||||
scraper = "0.22.0"
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
|
||||
43
assets/font/README.md
Normal file
43
assets/font/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Installing Webfonts
|
||||
Follow these simple Steps.
|
||||
|
||||
## 1.
|
||||
Put `satoshi/` Folder into a Folder called `fonts/`.
|
||||
|
||||
## 2.
|
||||
Put `satoshi.css` into your `css/` Folder.
|
||||
|
||||
## 3. (Optional)
|
||||
You may adapt the `url('path')` in `satoshi.css` depends on your Website Filesystem.
|
||||
|
||||
## 4.
|
||||
Import `satoshi.css` at the top of you main Stylesheet.
|
||||
|
||||
```
|
||||
@import url('satoshi.css');
|
||||
```
|
||||
|
||||
## 5.
|
||||
You are now ready to use the following Rules in your CSS to specify each Font Style:
|
||||
```
|
||||
font-family: Satoshi-Light;
|
||||
font-family: Satoshi-LightItalic;
|
||||
font-family: Satoshi-Regular;
|
||||
font-family: Satoshi-Italic;
|
||||
font-family: Satoshi-Medium;
|
||||
font-family: Satoshi-MediumItalic;
|
||||
font-family: Satoshi-Bold;
|
||||
font-family: Satoshi-BoldItalic;
|
||||
font-family: Satoshi-Black;
|
||||
font-family: Satoshi-BlackItalic;
|
||||
font-family: Satoshi-Variable;
|
||||
font-family: Satoshi-VariableItalic;
|
||||
|
||||
```
|
||||
## 6. (Optional)
|
||||
Use `font-variation-settings` rule to controll axes of variable fonts:
|
||||
wght 900.0
|
||||
|
||||
Available axes:
|
||||
'wght' (range from 300.0 to 900.0
|
||||
|
||||
148
assets/font/css/satoshi.css
Normal file
148
assets/font/css/satoshi.css
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* @license
|
||||
*
|
||||
* Font Family: Satoshi
|
||||
* Designed by: Deni Anggara
|
||||
* URL: https://www.fontshare.com/fonts/satoshi
|
||||
* © 2025 Indian Type Foundry
|
||||
*
|
||||
* Satoshi Light
|
||||
* Satoshi LightItalic
|
||||
* Satoshi Regular
|
||||
* Satoshi Italic
|
||||
* Satoshi Medium
|
||||
* Satoshi MediumItalic
|
||||
* Satoshi Bold
|
||||
* Satoshi BoldItalic
|
||||
* Satoshi Black
|
||||
* Satoshi BlackItalic
|
||||
* Satoshi Variable (Variable font)
|
||||
* Satoshi VariableItalic (Variable font)
|
||||
|
||||
*
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Satoshi-Light';
|
||||
src: url('../fonts/Satoshi-Light.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-Light.woff') format('woff'),
|
||||
url('../fonts/Satoshi-Light.ttf') format('truetype');
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Satoshi-LightItalic';
|
||||
src: url('../fonts/Satoshi-LightItalic.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-LightItalic.woff') format('woff'),
|
||||
url('../fonts/Satoshi-LightItalic.ttf') format('truetype');
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Satoshi-Regular';
|
||||
src: url('../fonts/Satoshi-Regular.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-Regular.woff') format('woff'),
|
||||
url('../fonts/Satoshi-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Satoshi-Italic';
|
||||
src: url('../fonts/Satoshi-Italic.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-Italic.woff') format('woff'),
|
||||
url('../fonts/Satoshi-Italic.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Satoshi-Medium';
|
||||
src: url('../fonts/Satoshi-Medium.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-Medium.woff') format('woff'),
|
||||
url('../fonts/Satoshi-Medium.ttf') format('truetype');
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Satoshi-MediumItalic';
|
||||
src: url('../fonts/Satoshi-MediumItalic.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-MediumItalic.woff') format('woff'),
|
||||
url('../fonts/Satoshi-MediumItalic.ttf') format('truetype');
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Satoshi-Bold';
|
||||
src: url('../fonts/Satoshi-Bold.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-Bold.woff') format('woff'),
|
||||
url('../fonts/Satoshi-Bold.ttf') format('truetype');
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Satoshi-BoldItalic';
|
||||
src: url('../fonts/Satoshi-BoldItalic.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-BoldItalic.woff') format('woff'),
|
||||
url('../fonts/Satoshi-BoldItalic.ttf') format('truetype');
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Satoshi-Black';
|
||||
src: url('../fonts/Satoshi-Black.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-Black.woff') format('woff'),
|
||||
url('../fonts/Satoshi-Black.ttf') format('truetype');
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Satoshi-BlackItalic';
|
||||
src: url('../fonts/Satoshi-BlackItalic.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-BlackItalic.woff') format('woff'),
|
||||
url('../fonts/Satoshi-BlackItalic.ttf') format('truetype');
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
/**
|
||||
* This is a variable font
|
||||
* You can control variable axes as shown below:
|
||||
* font-variation-settings: wght 900.0;
|
||||
*
|
||||
* available axes:
|
||||
'wght' (range from 300.0 to 900.0
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Satoshi-Variable';
|
||||
src: url('../fonts/Satoshi-Variable.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-Variable.woff') format('woff'),
|
||||
url('../fonts/Satoshi-Variable.ttf') format('truetype');
|
||||
font-weight: 300 900;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
/**
|
||||
* This is a variable font
|
||||
* You can control variable axes as shown below:
|
||||
* font-variation-settings: wght 900.0;
|
||||
*
|
||||
* available axes:
|
||||
'wght' (range from 300.0 to 900.0
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Satoshi-VariableItalic';
|
||||
src: url('../fonts/Satoshi-VariableItalic.woff2') format('woff2'),
|
||||
url('../fonts/Satoshi-VariableItalic.woff') format('woff'),
|
||||
url('../fonts/Satoshi-VariableItalic.ttf') format('truetype');
|
||||
font-weight: 300 900;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
BIN
assets/font/fonts/Satoshi-Black.eot
Normal file
BIN
assets/font/fonts/Satoshi-Black.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Black.ttf
Normal file
BIN
assets/font/fonts/Satoshi-Black.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Black.woff
Normal file
BIN
assets/font/fonts/Satoshi-Black.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Black.woff2
Normal file
BIN
assets/font/fonts/Satoshi-Black.woff2
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-BlackItalic.eot
Normal file
BIN
assets/font/fonts/Satoshi-BlackItalic.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-BlackItalic.ttf
Normal file
BIN
assets/font/fonts/Satoshi-BlackItalic.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-BlackItalic.woff
Normal file
BIN
assets/font/fonts/Satoshi-BlackItalic.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-BlackItalic.woff2
Normal file
BIN
assets/font/fonts/Satoshi-BlackItalic.woff2
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Bold.eot
Normal file
BIN
assets/font/fonts/Satoshi-Bold.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Bold.ttf
Normal file
BIN
assets/font/fonts/Satoshi-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Bold.woff
Normal file
BIN
assets/font/fonts/Satoshi-Bold.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Bold.woff2
Normal file
BIN
assets/font/fonts/Satoshi-Bold.woff2
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-BoldItalic.eot
Normal file
BIN
assets/font/fonts/Satoshi-BoldItalic.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-BoldItalic.ttf
Normal file
BIN
assets/font/fonts/Satoshi-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-BoldItalic.woff
Normal file
BIN
assets/font/fonts/Satoshi-BoldItalic.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-BoldItalic.woff2
Normal file
BIN
assets/font/fonts/Satoshi-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Italic.eot
Normal file
BIN
assets/font/fonts/Satoshi-Italic.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Italic.ttf
Normal file
BIN
assets/font/fonts/Satoshi-Italic.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Italic.woff
Normal file
BIN
assets/font/fonts/Satoshi-Italic.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Italic.woff2
Normal file
BIN
assets/font/fonts/Satoshi-Italic.woff2
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Light.eot
Normal file
BIN
assets/font/fonts/Satoshi-Light.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Light.ttf
Normal file
BIN
assets/font/fonts/Satoshi-Light.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Light.woff
Normal file
BIN
assets/font/fonts/Satoshi-Light.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Light.woff2
Normal file
BIN
assets/font/fonts/Satoshi-Light.woff2
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-LightItalic.eot
Normal file
BIN
assets/font/fonts/Satoshi-LightItalic.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-LightItalic.ttf
Normal file
BIN
assets/font/fonts/Satoshi-LightItalic.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-LightItalic.woff
Normal file
BIN
assets/font/fonts/Satoshi-LightItalic.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-LightItalic.woff2
Normal file
BIN
assets/font/fonts/Satoshi-LightItalic.woff2
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Medium.eot
Normal file
BIN
assets/font/fonts/Satoshi-Medium.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Medium.ttf
Normal file
BIN
assets/font/fonts/Satoshi-Medium.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Medium.woff
Normal file
BIN
assets/font/fonts/Satoshi-Medium.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Medium.woff2
Normal file
BIN
assets/font/fonts/Satoshi-Medium.woff2
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-MediumItalic.eot
Normal file
BIN
assets/font/fonts/Satoshi-MediumItalic.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-MediumItalic.ttf
Normal file
BIN
assets/font/fonts/Satoshi-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-MediumItalic.woff
Normal file
BIN
assets/font/fonts/Satoshi-MediumItalic.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-MediumItalic.woff2
Normal file
BIN
assets/font/fonts/Satoshi-MediumItalic.woff2
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Regular.eot
Normal file
BIN
assets/font/fonts/Satoshi-Regular.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Regular.ttf
Normal file
BIN
assets/font/fonts/Satoshi-Regular.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Regular.woff
Normal file
BIN
assets/font/fonts/Satoshi-Regular.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Regular.woff2
Normal file
BIN
assets/font/fonts/Satoshi-Regular.woff2
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Variable.eot
Normal file
BIN
assets/font/fonts/Satoshi-Variable.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Variable.ttf
Normal file
BIN
assets/font/fonts/Satoshi-Variable.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Variable.woff
Normal file
BIN
assets/font/fonts/Satoshi-Variable.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-Variable.woff2
Normal file
BIN
assets/font/fonts/Satoshi-Variable.woff2
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-VariableItalic.eot
Normal file
BIN
assets/font/fonts/Satoshi-VariableItalic.eot
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-VariableItalic.ttf
Normal file
BIN
assets/font/fonts/Satoshi-VariableItalic.ttf
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-VariableItalic.woff
Normal file
BIN
assets/font/fonts/Satoshi-VariableItalic.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/Satoshi-VariableItalic.woff2
Normal file
BIN
assets/font/fonts/Satoshi-VariableItalic.woff2
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -29,12 +29,14 @@ use zettle_db::{
|
||||
html::{
|
||||
account::{delete_account, set_api_key, show_account_page, update_timezone},
|
||||
admin_panel::{show_admin_panel, toggle_registration_status},
|
||||
documentation::index::show_documentation_index,
|
||||
documentation::{
|
||||
show_documentation_index, show_get_started, show_mobile_friendly,
|
||||
show_privacy_policy,
|
||||
},
|
||||
gdpr::{accept_gdpr, deny_gdpr},
|
||||
index::{delete_job, delete_text_content, index_handler},
|
||||
ingress_form::{hide_ingress_form, process_ingress_form, show_ingress_form},
|
||||
knowledge::entities::show_knowledge_page,
|
||||
privacy_policy::show_privacy_policy,
|
||||
knowledge::show_knowledge_page,
|
||||
search_result::search_result_handler,
|
||||
signin::{authenticate_user, show_signin_form},
|
||||
signout::sign_out_user,
|
||||
@@ -184,6 +186,8 @@ fn html_routes(
|
||||
)
|
||||
.route("/documentation", get(show_documentation_index))
|
||||
.route("/documentation/privacy-policy", get(show_privacy_policy))
|
||||
.route("/documentation/get-started", get(show_get_started))
|
||||
.route("/documentation/mobile-friendly", get(show_mobile_friendly))
|
||||
.nest_service("/assets", ServeDir::new("assets/"))
|
||||
.layer(from_fn_with_state(app_state.clone(), analytics_middleware))
|
||||
.layer(
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
use axum::{extract::State, response::IntoResponse};
|
||||
use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
|
||||
use crate::{
|
||||
error::HtmlError,
|
||||
page_data,
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::types::user::User,
|
||||
};
|
||||
|
||||
page_data!(IndexData, "documentation/index.html", {
|
||||
user: Option<User>
|
||||
});
|
||||
|
||||
pub async fn show_documentation_index(
|
||||
State(state): State<AppState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let output = render_template(
|
||||
IndexData::template_name(),
|
||||
IndexData {
|
||||
user: auth.current_user,
|
||||
},
|
||||
state.templates.clone(),
|
||||
)?;
|
||||
|
||||
Ok(output.into_response())
|
||||
}
|
||||
@@ -1 +1,79 @@
|
||||
pub mod index;
|
||||
use axum::{extract::State, response::IntoResponse};
|
||||
use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
|
||||
use crate::{
|
||||
error::HtmlError,
|
||||
page_data,
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::types::user::User,
|
||||
};
|
||||
|
||||
page_data!(DocumentationData, "do_not_use_this", {
|
||||
user: Option<User>,
|
||||
current_path: String
|
||||
});
|
||||
|
||||
pub async fn show_privacy_policy(
|
||||
State(state): State<AppState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let output = render_template(
|
||||
"documentation/privacy.html",
|
||||
DocumentationData {
|
||||
user: auth.current_user,
|
||||
current_path: "/privacy_policy".to_string(),
|
||||
},
|
||||
state.templates.clone(),
|
||||
)?;
|
||||
|
||||
Ok(output.into_response())
|
||||
}
|
||||
|
||||
pub async fn show_get_started(
|
||||
State(state): State<AppState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let output = render_template(
|
||||
"documentation/get_started.html",
|
||||
DocumentationData {
|
||||
user: auth.current_user,
|
||||
current_path: "/get-started".to_string(),
|
||||
},
|
||||
state.templates.clone(),
|
||||
)?;
|
||||
|
||||
Ok(output.into_response())
|
||||
}
|
||||
pub async fn show_mobile_friendly(
|
||||
State(state): State<AppState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let output = render_template(
|
||||
"documentation/mobile_friendly.html",
|
||||
DocumentationData {
|
||||
user: auth.current_user,
|
||||
current_path: "/mobile-friendly".to_string(),
|
||||
},
|
||||
state.templates.clone(),
|
||||
)?;
|
||||
|
||||
Ok(output.into_response())
|
||||
}
|
||||
|
||||
pub async fn show_documentation_index(
|
||||
State(state): State<AppState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let output = render_template(
|
||||
"documentation/index.html",
|
||||
DocumentationData {
|
||||
user: auth.current_user,
|
||||
current_path: "/index".to_string(),
|
||||
},
|
||||
state.templates.clone(),
|
||||
)?;
|
||||
|
||||
Ok(output.into_response())
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{IntoResponse, Redirect},
|
||||
};
|
||||
use axum_session::Session;
|
||||
use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
use tokio::join;
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
error::{AppError, HtmlError},
|
||||
page_data,
|
||||
server::{
|
||||
routes::html::{render_block, render_template},
|
||||
AppState,
|
||||
},
|
||||
storage::{
|
||||
db::{delete_item, get_item},
|
||||
types::{
|
||||
file_info::FileInfo, job::Job, knowledge_entity::KnowledgeEntity,
|
||||
knowledge_relationship::KnowledgeRelationship, text_chunk::TextChunk,
|
||||
text_content::TextContent, user::User,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
page_data!(KnowledgeBaseData, "knowledge/base.html", {
|
||||
entities: Vec<KnowledgeEntity>,
|
||||
relationships: Vec<KnowledgeRelationship>,
|
||||
user: User
|
||||
});
|
||||
|
||||
pub async fn show_knowledge_page(
|
||||
State(state): State<AppState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
// Early return if the user is not authenticated
|
||||
let user = match auth.current_user {
|
||||
Some(user) => user,
|
||||
None => return Ok(Redirect::to("/signin").into_response()),
|
||||
};
|
||||
|
||||
let entities = User::get_knowledge_entities(&user.id, &state.surreal_db_client)
|
||||
.await
|
||||
.map_err(|e| HtmlError::new(e, state.templates.clone()))?;
|
||||
|
||||
info!("Got entities ok");
|
||||
|
||||
let relationships = User::get_knowledge_relationships(&user.id, &state.surreal_db_client)
|
||||
.await
|
||||
.map_err(|e| HtmlError::new(e, state.templates.clone()))?;
|
||||
|
||||
let output = render_template(
|
||||
KnowledgeBaseData::template_name(),
|
||||
KnowledgeBaseData {
|
||||
entities,
|
||||
relationships,
|
||||
user,
|
||||
},
|
||||
state.templates,
|
||||
)?;
|
||||
|
||||
Ok(output.into_response())
|
||||
}
|
||||
@@ -1 +1,112 @@
|
||||
pub mod entities;
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{IntoResponse, Redirect},
|
||||
};
|
||||
use axum_session::Session;
|
||||
use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use plotly::{Configuration, Layout, Plot, Scatter};
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
use tokio::join;
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
error::{AppError, HtmlError},
|
||||
page_data,
|
||||
server::{
|
||||
routes::html::{render_block, render_template},
|
||||
AppState,
|
||||
},
|
||||
storage::{
|
||||
db::{delete_item, get_item},
|
||||
types::{
|
||||
file_info::FileInfo, job::Job, knowledge_entity::KnowledgeEntity,
|
||||
knowledge_relationship::KnowledgeRelationship, text_chunk::TextChunk,
|
||||
text_content::TextContent, user::User,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
page_data!(KnowledgeBaseData, "knowledge/base.html", {
|
||||
entities: Vec<KnowledgeEntity>,
|
||||
relationships: Vec<KnowledgeRelationship>,
|
||||
user: User,
|
||||
plot_html: String
|
||||
});
|
||||
|
||||
pub async fn show_knowledge_page(
|
||||
State(state): State<AppState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
// Early return if the user is not authenticated
|
||||
let user = match auth.current_user {
|
||||
Some(user) => user,
|
||||
None => return Ok(Redirect::to("/signin").into_response()),
|
||||
};
|
||||
|
||||
let entities = User::get_knowledge_entities(&user.id, &state.surreal_db_client)
|
||||
.await
|
||||
.map_err(|e| HtmlError::new(e, state.templates.clone()))?;
|
||||
|
||||
info!("Got entities ok");
|
||||
|
||||
let relationships = User::get_knowledge_relationships(&user.id, &state.surreal_db_client)
|
||||
.await
|
||||
.map_err(|e| HtmlError::new(e, state.templates.clone()))?;
|
||||
|
||||
// In your handler function
|
||||
let mut plot = Plot::new();
|
||||
|
||||
// Create node positions (you might want to use a proper layout algorithm)
|
||||
let node_x: Vec<f64> = entities.iter().enumerate().map(|(i, _)| i as f64).collect();
|
||||
let node_y: Vec<f64> = vec![0.0; entities.len()];
|
||||
let node_text: Vec<String> = entities.iter().map(|e| e.description.clone()).collect();
|
||||
|
||||
// Add nodes
|
||||
let nodes = Scatter::new(node_x.clone(), node_y.clone())
|
||||
.mode(plotly::common::Mode::Markers)
|
||||
.text_array(node_text)
|
||||
.name("Entities")
|
||||
.hover_template("%{text}");
|
||||
|
||||
// Add edges
|
||||
let mut edge_x = Vec::new();
|
||||
let mut edge_y = Vec::new();
|
||||
for rel in &relationships {
|
||||
let from_idx = entities.iter().position(|e| e.id == rel.out).unwrap_or(0);
|
||||
let to_idx = entities.iter().position(|e| e.id == rel.in_).unwrap_or(0);
|
||||
|
||||
edge_x.extend_from_slice(&[from_idx as f64, to_idx as f64, std::f64::NAN]);
|
||||
edge_y.extend_from_slice(&[0.0, 0.0, std::f64::NAN]);
|
||||
}
|
||||
|
||||
let edges = Scatter::new(edge_x, edge_y)
|
||||
.mode(plotly::common::Mode::Lines)
|
||||
.name("Relationships");
|
||||
|
||||
plot.add_trace(edges);
|
||||
plot.add_trace(nodes);
|
||||
|
||||
let layout = Layout::new()
|
||||
.title("Knowledge Graph")
|
||||
.show_legend(false)
|
||||
.height(600);
|
||||
|
||||
plot.set_layout(layout);
|
||||
|
||||
// Convert to HTML
|
||||
let html = plot.to_html();
|
||||
|
||||
let output = render_template(
|
||||
KnowledgeBaseData::template_name(),
|
||||
KnowledgeBaseData {
|
||||
entities,
|
||||
relationships,
|
||||
user,
|
||||
plot_html: html,
|
||||
},
|
||||
state.templates,
|
||||
)?;
|
||||
|
||||
Ok(output.into_response())
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ pub mod gdpr;
|
||||
pub mod index;
|
||||
pub mod ingress_form;
|
||||
pub mod knowledge;
|
||||
pub mod privacy_policy;
|
||||
pub mod search_result;
|
||||
pub mod signin;
|
||||
pub mod signout;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
use axum::{extract::State, response::IntoResponse};
|
||||
use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
|
||||
use crate::{
|
||||
error::HtmlError,
|
||||
page_data,
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::types::user::User,
|
||||
};
|
||||
|
||||
page_data!(PrivacyPolicyData, "documentation/privacy.html", {
|
||||
user: Option<User>
|
||||
});
|
||||
|
||||
pub async fn show_privacy_policy(
|
||||
State(state): State<AppState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let output = render_template(
|
||||
PrivacyPolicyData::template_name(),
|
||||
PrivacyPolicyData {
|
||||
user: auth.current_user,
|
||||
},
|
||||
state.templates.clone(),
|
||||
)?;
|
||||
|
||||
Ok(output.into_response())
|
||||
}
|
||||
56
templates/documentation/get_started.html
Normal file
56
templates/documentation/get_started.html
Normal file
@@ -0,0 +1,56 @@
|
||||
{% extends 'documentation/base.html' %}
|
||||
{% block article %}
|
||||
<h2>Get Started with Minne</h2>
|
||||
<p>Minne offers two installation options to suit your needs:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<strong>Hosted Version:</strong> Enjoy a hassle‐free experience by signing up for the ready‐to‐use service.
|
||||
Simply navigate to <code>/signup</code> to create an account.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Self-Hosted:</strong> Gain full control by running Minne on your own infrastructure. Visit
|
||||
<a href="https://github.com/perstarkse/minne">GitHub</a> to download the latest release. After extracting the
|
||||
release, open the <code>config.yaml</code> file and set the following configurations:
|
||||
</li>
|
||||
</ol>
|
||||
<pre><code>OPENAI_API_KEY: your_api_key
|
||||
SMTP_EMAIL_RELAYER: your_email_relayer
|
||||
SMTP_USERNAME: your_smtp_username
|
||||
SMTP_PASSWORD: your_smtp_password
|
||||
DB_ADDRESS: your_db_address
|
||||
DB_USER: your_db_user
|
||||
DB_PASSWORD: your_db_password</code></pre>
|
||||
<p>The database settings relate to a running instance of SurrealDB. You can opt for their cloud solution or run your
|
||||
own instance.</p>
|
||||
|
||||
<p>Once your configuration is complete, start both the server and the worker. They can be hosted on separate
|
||||
machines, with different resource requirements:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Server:</strong> Lightweight, using roughly 50MB of RAM. A minimum of 1 core and 256MB of RAM is
|
||||
recommended.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Worker:</strong> Handles content parsing, typically consuming about 60MB of RAM—occasionally peaking up
|
||||
to 1GB. We recommend allocating at least 2 cores and 1024MB of RAM.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>After launching the services, navigate to <code><your_url>:3000/signup</code> to register. The first
|
||||
account created will automatically receive admin permissions, allowing you to later disable further registrations
|
||||
via the <code>/admin</code> page if desired.</p>
|
||||
|
||||
<p>From the homepage (<code>/</code>), you can:</p>
|
||||
<ul>
|
||||
<li>Submit content, including files, videos, and URLs for ingestion.</li>
|
||||
<li>Monitor job statuses and manage your existing content.</li>
|
||||
<li>Search your content or start a chat conversation for assistance.</li>
|
||||
</ul>
|
||||
|
||||
<p>Visit the <code>/knowledge</code> page to view your content organized by different sections. This page also
|
||||
provides a visual demonstration of the graph database structure, enhancing your understanding of content
|
||||
relationships.</p>
|
||||
|
||||
<p>This streamlined setup ensures intuitive onboarding while offering robust customization options. Whether you are
|
||||
a novice or an advanced user, Minne is designed to deliver a smooth experience and reliable performance.</p>
|
||||
{% endblock %}
|
||||
@@ -1,6 +1,11 @@
|
||||
<ul class="menu bg-base-200 rounded-box w-full">
|
||||
<li><a hx-boost="true" href="/documentation">Start</a></li>
|
||||
<li><a hx-boost="true" class="" href="/documentation/privacy-policy">Privacy Policy</a></li>
|
||||
<ul class="menu bg-base-200 rounded-box w-full ">
|
||||
<li><a hx-boost="true" class="{{'menu-active' if current_path=='/index' }}" href="/documentation">Start</a></li>
|
||||
<li><a hx-boost="true" class="{{'menu-active' if current_path=='/get-started' }}"
|
||||
href="/documentation/get-started">Get Started</a></li>
|
||||
<li><a hx-boost="true" class="{{'menu-active' if current_path=='/mobile-friendly' }}"
|
||||
href="/documentation/mobile-friendly">Mobile friendly</a></li>
|
||||
<li><a hx-boost="true" class="{{'menu-active' if current_path=='/privacy-policy' }}"
|
||||
href="/documentation/privacy-policy">Privacy Policy</a></li>
|
||||
<li>
|
||||
<details open>
|
||||
<summary>Core Concepts</summary>
|
||||
|
||||
22
templates/documentation/mobile_friendly.html
Normal file
22
templates/documentation/mobile_friendly.html
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends 'documentation/base.html' %}
|
||||
{% block article %}
|
||||
<header>
|
||||
<h2>Mobile Friendly Ingression: How to Submit Content from iOS to Minne</h2>
|
||||
</header>
|
||||
<p>Minne is built with simplicity in mind. Whether you wish to save a file, capture a thought, or share a page,
|
||||
submitting content is effortless. Our server provides API access that enables users to perform actions using a
|
||||
personalized API key.</p>
|
||||
|
||||
<p>An iOS shortcut has been developed to streamline the process of sending content. To begin, navigate to
|
||||
<code>/account</code> and generate an API key. Once created, you will see an option to download the iOS shortcut.
|
||||
</p>
|
||||
|
||||
<p>After downloading the shortcut, update the "Get response from URL" authentication headers with your API key. If
|
||||
you are self-hosting, ensure the URL is adjusted accordingly.</p>
|
||||
|
||||
<p>The shortcut integrates seamlessly with iOS. When you "share with Minne," you will be prompted to provide
|
||||
instructions to the AI and to either choose an existing category or create a new one for your submission.</p>
|
||||
|
||||
<p>While an Android solution is in the works, for now you can add the web app to your home screen as a Progressive
|
||||
Web App (PWA) for a similar mobile-friendly experience.</p>
|
||||
{% endblock %}
|
||||
@@ -1,6 +1,6 @@
|
||||
{% block active_jobs_section %}
|
||||
<ul id="active_jobs_section" class="list ">
|
||||
<li class="py-4 text-xs opacity-60 tracking-wide">Active Jobs</li>
|
||||
<li class="py-4 text-center text-xs opacity-60 tracking-wide">Active Jobs</li>
|
||||
{% for item in active_jobs %}
|
||||
<li class="list-row">
|
||||
<div class="bg-secondary rounded-box size-10 flex justify-center items-center text-secondary-content">
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<div class="shadow mt-4 rounded-box">
|
||||
<div class="flex gap-4">
|
||||
<button class="btn btn-primary" hx-get="/ingress-form" hx-swap="outerHTML">Add Content</button>
|
||||
<a class="btn btn-secondary" href="/knowledge" hx-boost="true">View Knowledge</a>
|
||||
</div>
|
||||
<div class="flex gap-4 flex-col">
|
||||
<a class="btn btn-secondary" href="/knowledge" hx-boost="true">View Knowledge</a>
|
||||
<button class="btn btn-primary" hx-get="/ingress-form" hx-swap="outerHTML">Add Content</button>
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
{% block latest_content_section %}
|
||||
<ul id="latest_content_section" class="list">
|
||||
<li class="py-4 text-xs opacity-60 tracking-wide">Recently added content</li>
|
||||
<li class="py-4 text-center text-xs opacity-60 tracking-wide">Recently added content</li>
|
||||
{% for item in latest_text_contents %}
|
||||
<li class="list-row">
|
||||
<div class="bg-accent rounded-box size-10 flex justify-center items-center text-accent-content">
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<div class="py-4 shadow rounded-box">
|
||||
<h2>
|
||||
Search your content
|
||||
</h2>
|
||||
<input type="text" placeholder="Search your knowledge base" class="input input-bordered w-full" name="query"
|
||||
hx-get="/search" hx-target="#search-results" />
|
||||
<div id="search-results" class="mt-4">
|
||||
<!-- Results will be populated here by HTMX -->
|
||||
</div>
|
||||
<h2>
|
||||
Search your content
|
||||
</h2>
|
||||
<input type="text" placeholder="Search your knowledge base" class="input input-bordered w-full" name="query"
|
||||
hx-get="/search" hx-target="#search-results" />
|
||||
<div id="search-results" class="mt-4">
|
||||
<!-- Results will be populated here by HTMX -->
|
||||
</div>
|
||||
@@ -34,8 +34,8 @@
|
||||
</div>
|
||||
<div id="error-message" class="text-error text-center {% if not error %}hidden{% endif %}">{{ error }}</div>
|
||||
<div class="form-control mt-6 flex flex-col sm:flex-row gap-1">
|
||||
<button type="submit" class="btn btn-primary w-full sm:w-fit">Submit</button>
|
||||
<button hx-get="/hide-ingress-form" hx-target="#ingress-form" hx-swap="outerHTML"
|
||||
class="btn btn-outline w-full sm:w-fit">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary w-full sm:w-fit">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -2,12 +2,12 @@
|
||||
{% block main %}
|
||||
<main class="flex justify-center grow mt-2 sm:mt-4 gap-6">
|
||||
<div class="container">
|
||||
|
||||
{{plot_html|safe}}
|
||||
<h2>Entities</h2>
|
||||
|
||||
{% for entity in entities %}
|
||||
<p>{{entity.description}} /p>
|
||||
{% endfor %}
|
||||
<p>{{entity.id}} - {{entity.description}} </p>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
<h2 class="mt-10">Relationships</h2>
|
||||
|
||||
Reference in New Issue
Block a user