commit 1b1cd2e06479691045a2ed8fbbd27d063ddf3435
Author: aOK
Date: Mon Sep 25 22:51:18 2023 +0300
first commit
diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 0000000..b0014df
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,3 @@
+[build]
+# `leptonic` depends on some `leptos-use` functions requiring this opt-in. This may change in the future.
+rustflags = ["--cfg=web_sys_unstable_apis"]
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a30ece6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/target
+/generated/**
+!/generated/.gitkeep
+/dist
+/Cargo.lock
diff --git a/Caddyfile b/Caddyfile
new file mode 100644
index 0000000..57060dc
--- /dev/null
+++ b/Caddyfile
@@ -0,0 +1,55 @@
+{
+ # https://caddyserver.com/docs/caddyfile/options
+ email "privat@lukas-potthast.de"
+ http_port {$HTTP_PORT}
+ https_port {$HTTPS_PORT}
+ storage consul {
+ address "{$CONSUL_HTTP_ADDR}"
+ token "consul-access-token"
+ timeout 10
+ prefix ""
+ value_prefix "caddytlsprefix"
+ aes_key "consultls-1234567890-caddytls-32"
+ tls_enabled "false"
+ tls_insecure "true"
+ }
+}
+
+{$ADDRESS_PREFIX}:{$HTTPS_PORT} {
+ tls /etc/certs/letsencrypt/www.leptonic.dev-cert.pem /etc/certs/letsencrypt/www.leptonic.dev-key.pem
+
+ # redir {$URL_PREFIX} {$URL_PREFIX}/
+
+ handle_path {$URL_PREFIX}* {
+ root * /srv
+ try_files {path} /index.html
+ encode {
+ gzip
+ match {
+ header Content-Type text/*
+ header Content-Type application/json*
+ header Content-Type application/js*
+ header Content-Type application/javascript*
+ header Content-Type application/wasm*
+ header Content-Type application/xhtml+xml*
+ header Content-Type application/atom+xml*
+ header Content-Type application/rss+xml*
+ header Content-Type image/svg+xml*
+ header Content-Type image/*
+ }
+ }
+ # header Cache-Control max-age=0,no-cache,no-store,must-revalidate
+ # header index.html Cache-Control max-age=0,no-cache,no-store,must-revalidate
+ # header service-worker.js Cache-Control max-age=0,no-cache,no-store,must-revalidate
+ # header manifest.json Cache-Control max-age=0,no-cache,no-store,must-revalidate
+ # header robots.txt Cache-Control max-age=0,no-cache,no-store,must-revalidate
+ # header js/* Cache-Control max-age=0,no-cache,no-store,must-revalidate
+ # header * Cache-Control max-age=31536000
+ header Cache-Control max-age=0,no-cache,no-store,must-revalidate
+ file_server
+ }
+
+ handle {
+ respond "Not found" 404
+ }
+}
\ No newline at end of file
diff --git a/Caddyfile.local b/Caddyfile.local
new file mode 100644
index 0000000..ea871b7
--- /dev/null
+++ b/Caddyfile.local
@@ -0,0 +1,35 @@
+{
+ # https://caddyserver.com/docs/caddyfile/options
+ email "privat@lukas-potthast.de"
+ http_port {$HTTP_PORT}
+ auto_https disable_redirects
+}
+
+{$ADDRESS_PREFIX}:{$HTTP_PORT} {
+
+ handle_path {$URL_PREFIX}* {
+ root * /srv
+ try_files {path} /index.html
+ encode {
+ gzip
+ match {
+ header Content-Type text/*
+ header Content-Type application/json*
+ header Content-Type application/js*
+ header Content-Type application/javascript*
+ header Content-Type application/wasm*
+ header Content-Type application/xhtml+xml*
+ header Content-Type application/atom+xml*
+ header Content-Type application/rss+xml*
+ header Content-Type image/svg+xml*
+ header Content-Type image/*
+ }
+ }
+ header Cache-Control max-age=0,no-cache,no-store,must-revalidate
+ file_server
+ }
+
+ handle {
+ respond "Not found" 404
+ }
+}
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..56a86cb
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,57 @@
+[package]
+name = "book-ui"
+version = "0.1.0"
+edition = "2021"
+build = "build.rs"
+
+[profile.release]
+opt-level = "z"
+lto = "fat"
+debug = 0
+strip = true
+codegen-units = 1
+
+[dependencies]
+console_error_panic_hook = "0.1.7"
+indoc = "2.0.1"
+leptonic = { git = "https://github.com/lpotthast/leptonic", default-features = false, features = ["csr"] }
+leptos = { version = "0.5.0-rc1", features = ["csr"] }
+leptos-use = { git = "https://github.com/Synphonyte/leptos-use" }
+leptos_icons = { version = "0.0.16-beta", features = [
+ "BsGithub",
+ "BsSearch",
+ "BsList",
+ "BsThreeDots",
+ "BsFolder",
+ "BsFolderFill",
+ "BsBook",
+ "BsColumnsGap",
+ "BsToggles",
+ "BsChatSquare",
+ "BsCircleSquare",
+ "BsArrowsMove",
+ "BsVolumeDownFill",
+ "BsVolumeUpFill",
+ "BsBell",
+ "BsPower",
+] }
+leptos_meta = { version = "0.5.0-rc1", features = ["csr"] }
+leptos_router = { version = "0.5.0-rc1", features = ["csr"] }
+ordered-float = "3.9.0"
+serde = "1.0.171"
+serde-wasm-bindgen = "0.5.0"
+serde_json = "1.0.103"
+strum = { version = "0.25.0", features = ["derive"] }
+time = { version = "0.3.23", features = ["wasm-bindgen"] }
+tracing = "0.1.37"
+tracing-subscriber = "0.3.17"
+tracing-wasm = "0.2.1"
+uuid = { version = "1.4.0", features = ["js", "v4", "v7", "serde"] }
+wasm-bindgen = "0.2.87"
+
+[build-dependencies]
+leptonic-theme = "0.2.0"
+leptos-tiptap-build = "0.2.3"
+
+[workspace]
+members = ["src-tauri"]
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..cf1c9e6
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,8 @@
+FROM registry.gitlab.com/lukaspotthast/docker-images/caddy-consul:latest
+
+COPY {{LEPTONIC_DEV_CERT_PEM_FILE}} /etc/certs/letsencrypt/www.leptonic.dev-cert.pem
+COPY {{LEPTONIC_DEV_KEY_PEM_FILE}} /etc/certs/letsencrypt/www.leptonic.dev-key.pem
+
+COPY Caddyfile /etc/caddy/Caddyfile
+
+COPY dist/ /srv
diff --git a/Dockerfile.local b/Dockerfile.local
new file mode 100644
index 0000000..e27ba64
--- /dev/null
+++ b/Dockerfile.local
@@ -0,0 +1,4 @@
+FROM caddy:latest
+
+COPY Caddyfile.local /etc/caddy/Caddyfile
+COPY dist/ /srv
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d10e7b4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+# Leptonic book
+
+
+to run it with [trunk](https://trunkrs.dev/) use `trunk serve --open` in the root of this example directory.
diff --git a/Trunk.toml b/Trunk.toml
new file mode 100644
index 0000000..040642d
--- /dev/null
+++ b/Trunk.toml
@@ -0,0 +1,14 @@
+[build]
+target = "./index.html"
+
+[watch]
+ignore = [
+ "./src-tauri",
+ "./generated",
+ ]
+
+[serve]
+address = "0.0.0.0"
+port = 1420
+open = false
+ws_protocol = "ws"
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..e7e892b
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,28 @@
+use std::io::Write;
+
+pub fn main() {
+ println!("cargo:rerun-if-changed=build.rs");
+ println!("cargo:rerun-if-changed=Cargo.lock");
+
+ let root: std::path::PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into();
+ let generated_dir = root.join("generated");
+ let js_dir = generated_dir.join("js");
+
+ leptonic_theme::generate(generated_dir.join("leptonic"));
+ println!("cargo:warning=theme written");
+
+ std::fs::create_dir_all(js_dir.clone()).unwrap();
+ println!("cargo:warning=js dir created");
+
+ std::fs::File::create(js_dir.join("tiptap-bundle.min.js"))
+ .unwrap()
+ .write_all(leptos_tiptap_build::TIPTAP_BUNDLE_MIN_JS.as_bytes())
+ .unwrap();
+ println!("cargo:warning=tiptap-bundle.min.js written");
+
+ std::fs::File::create(js_dir.join("tiptap.js"))
+ .unwrap()
+ .write_all(leptos_tiptap_build::TIPTAP_JS.as_bytes())
+ .unwrap();
+ println!("cargo:warning=tiptap.js written");
+}
diff --git a/deployment/leptonic-book.jobspec.hcl b/deployment/leptonic-book.jobspec.hcl
new file mode 100644
index 0000000..fc89d8a
--- /dev/null
+++ b/deployment/leptonic-book.jobspec.hcl
@@ -0,0 +1,63 @@
+job "leptonic-book" {
+ datacenters = ["dc1"]
+ type = "service"
+
+ group "leptonic-book" {
+ count = 1
+
+ network {
+ port "http" {}
+ port "https" {}
+ }
+
+ restart {
+ attempts = 10
+ interval = "5m"
+ delay = "25s"
+ mode = "delay"
+ }
+
+ task "leptonic-book" {
+ driver = "docker"
+
+ env {
+ DOMAIN = "leptonic.dev"
+ CONSUL_HTTP_ADDR = "172.17.0.1:8500"
+ HTTP_PORT = "${NOMAD_PORT_http}"
+ HTTPS_PORT = "${NOMAD_PORT_https}"
+ URL_PREFIX = "/"
+ }
+
+ config {
+ image = "registry.gitlab.com/lukaspotthast/leptonic/leptonic-book:{{TAG}}"
+ auth {
+ username = "{{REGISTRY_USERNAME}}"
+ password = "{{REGISTRY_TOKEN}}"
+ }
+ ports = ["http", "https"]
+ }
+
+ resources {
+ cpu = 256
+ memory = 128
+ }
+
+ service {
+ name = "leptonic-book"
+ tags = [
+ # Redirect HTTP to HTTPS
+ "urlprefix-leptonic.dev:80/ redirect=301,https://leptonic.dev/",
+ # Serve the app using delayed SSL termination
+ "urlprefix-leptonic.dev/ proto=https tlsskipverify=true",
+ ]
+ port = "https"
+
+ check {
+ type = "tcp"
+ interval = "10s"
+ timeout = "2s"
+ }
+ }
+ }
+ }
+}
diff --git a/extra.txt b/extra.txt
new file mode 100644
index 0000000..72561f8
--- /dev/null
+++ b/extra.txt
@@ -0,0 +1,17 @@
+cargo leptos new --git https://github.com/leptos-rs/start
+
+
+Tauri CLI │ Run `cargo install tauri-cli --version '^2.0.0-alpha'`
+cargo create-tauri-app nigiginc --alpha
+cd nigiginc
+cargo tauri android init
+cargo tauri ios init
+
+For Desktop development, run:
+cargo tauri dev
+
+For Android development, run:
+cargo tauri android dev
+
+For iOS development, run:
+cargo tauri ios dev
\ No newline at end of file
diff --git a/generated/.gitkeep b/generated/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..1e13bb1
--- /dev/null
+++ b/index.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+ Leptonic
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/icon/ferris-does-not-compute.svg b/res/icon/ferris-does-not-compute.svg
new file mode 100644
index 0000000..57764ee
--- /dev/null
+++ b/res/icon/ferris-does-not-compute.svg
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/icon/ferris-does-not-compute_transparent.svg b/res/icon/ferris-does-not-compute_transparent.svg
new file mode 100644
index 0000000..42bd1bc
--- /dev/null
+++ b/res/icon/ferris-does-not-compute_transparent.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/res/icon/ferris-gesture.svg b/res/icon/ferris-gesture.svg
new file mode 100644
index 0000000..d96dd2f
--- /dev/null
+++ b/res/icon/ferris-gesture.svg
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/icon/ferris-panic.svg b/res/icon/ferris-panic.svg
new file mode 100644
index 0000000..d1ad341
--- /dev/null
+++ b/res/icon/ferris-panic.svg
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/icon/ferris-panic_transparent.svg b/res/icon/ferris-panic_transparent.svg
new file mode 100644
index 0000000..253573a
--- /dev/null
+++ b/res/icon/ferris-panic_transparent.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/res/icon/ferris-unsafe.svg b/res/icon/ferris-unsafe.svg
new file mode 100644
index 0000000..1f6c0bc
--- /dev/null
+++ b/res/icon/ferris-unsafe.svg
@@ -0,0 +1,291 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/icon/ferris.svg b/res/icon/ferris.svg
new file mode 100644
index 0000000..900007d
--- /dev/null
+++ b/res/icon/ferris.svg
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/icon/leptonic_x1024.png b/res/icon/leptonic_x1024.png
new file mode 100644
index 0000000..6f8cc5e
Binary files /dev/null and b/res/icon/leptonic_x1024.png differ
diff --git a/res/icon/leptonic_x128.png b/res/icon/leptonic_x128.png
new file mode 100644
index 0000000..d5c8084
Binary files /dev/null and b/res/icon/leptonic_x128.png differ
diff --git a/res/icon/leptonic_x256.png b/res/icon/leptonic_x256.png
new file mode 100644
index 0000000..b36cf83
Binary files /dev/null and b/res/icon/leptonic_x256.png differ
diff --git a/res/icon/leptonic_x512.png b/res/icon/leptonic_x512.png
new file mode 100644
index 0000000..b9460f5
Binary files /dev/null and b/res/icon/leptonic_x512.png differ
diff --git a/res/icon/leptonic_x64.png b/res/icon/leptonic_x64.png
new file mode 100644
index 0000000..2c55e5c
Binary files /dev/null and b/res/icon/leptonic_x64.png differ
diff --git a/res/icon/maskable_icon.png b/res/icon/maskable_icon.png
new file mode 100644
index 0000000..320fa97
Binary files /dev/null and b/res/icon/maskable_icon.png differ
diff --git a/res/icon/maskable_icon_x128.png b/res/icon/maskable_icon_x128.png
new file mode 100644
index 0000000..ec6af29
Binary files /dev/null and b/res/icon/maskable_icon_x128.png differ
diff --git a/res/icon/maskable_icon_x192.png b/res/icon/maskable_icon_x192.png
new file mode 100644
index 0000000..a38db7e
Binary files /dev/null and b/res/icon/maskable_icon_x192.png differ
diff --git a/res/icon/maskable_icon_x384.png b/res/icon/maskable_icon_x384.png
new file mode 100644
index 0000000..70d52c0
Binary files /dev/null and b/res/icon/maskable_icon_x384.png differ
diff --git a/res/icon/maskable_icon_x48.png b/res/icon/maskable_icon_x48.png
new file mode 100644
index 0000000..0c9a625
Binary files /dev/null and b/res/icon/maskable_icon_x48.png differ
diff --git a/res/icon/maskable_icon_x512.png b/res/icon/maskable_icon_x512.png
new file mode 100644
index 0000000..e5ccaf1
Binary files /dev/null and b/res/icon/maskable_icon_x512.png differ
diff --git a/res/icon/maskable_icon_x72.png b/res/icon/maskable_icon_x72.png
new file mode 100644
index 0000000..62f422c
Binary files /dev/null and b/res/icon/maskable_icon_x72.png differ
diff --git a/res/icon/maskable_icon_x96.png b/res/icon/maskable_icon_x96.png
new file mode 100644
index 0000000..dfa9e28
Binary files /dev/null and b/res/icon/maskable_icon_x96.png differ
diff --git a/res/leptonic.svg b/res/leptonic.svg
new file mode 100644
index 0000000..663ae4d
--- /dev/null
+++ b/res/leptonic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/robots.txt b/robots.txt
new file mode 100644
index 0000000..c2a49f4
--- /dev/null
+++ b/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Allow: /
diff --git a/scss/style.scss b/scss/style.scss
new file mode 100644
index 0000000..3ae23c4
--- /dev/null
+++ b/scss/style.scss
@@ -0,0 +1,286 @@
+* {
+ font-family: Roboto, sans-serif;
+}
+
+html {
+ font-size: 16px;
+ height: 100%;
+}
+
+body {
+ margin: 0;
+ min-height: var(--leptonic-vh, 100vh);
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+@import "../generated/leptonic/leptonic-themes";
+
+[data-theme="light"] {
+ --brand-color: #e66956;
+
+ --drawer-background-color: none;
+ --drawer-box-shadow: none;
+
+ --book-menu-background-color: var(--box-background-color);
+ --book-menu-header-color: #3e3434;
+ --book-menu-item-color: #3e3434;
+ --book-menu-item-active-color: var(--std-text-bright);
+ --book-menu-item-hover-color: var(--std-text-bright);
+}
+
+[data-theme="dark"] {
+ --brand-color: #e66956;
+
+ --drawer-background-color: none;
+ --drawer-box-shadow: none;
+
+ --book-menu-background-color: var(--box-background-color);
+ --book-menu-header-color: var(--std-text-bright);
+ --book-menu-item-color: var(--std-text-bright);
+ --book-menu-item-active-color: var(--std-text-bright);
+ --book-menu-item-hover-color: var(--std-text-bright);
+}
+
+#app-bar {
+ display: flex;
+ justify-content: center;
+
+ #app-bar-content {
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+ max-width: 1450px;
+
+ #mobile-menu-trigger {
+ font-size: 1.5em;
+ padding: 0.7em;
+ cursor: pointer;
+ }
+ }
+
+ #logo {
+ display: inline-flex;
+ height: 2.5em;
+ width: 2.5em;
+ margin-left: 0.5em;
+ }
+
+ #github-icon {
+ font-size: 1.5em;
+ }
+}
+
+#content {
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+ padding: 0;
+ overflow-y: auto;
+}
+
+#main-drawer {
+ overflow: none;
+
+ position: absolute;
+ right: 0;
+ top: var(--app-bar-height);
+ bottom: 0;
+ left: 0;
+
+ width: 100%;
+
+ background-color: var(--book-menu-background-color);
+ padding: 1em 0;
+}
+
+#doc-layout {
+ display: flex;
+ flex-direction: row;
+ height: 100%;
+ width: 100%;
+ max-width: 1400px;
+ margin: auto;
+
+ #doc-drawer {
+ overflow: auto;
+ width: auto;
+ min-width: 15em;
+ padding: 1em 0;
+ margin: 0;
+
+ &.mobile {
+ width: 100%;
+ min-width: 100%;
+ overflow: none;
+ position: absolute;
+ right: 0;
+ top: var(--app-bar-height);
+ bottom: 0;
+ left: 0;
+ background-color: var(--book-menu-background-color);
+ padding: 1em 0;
+ }
+
+ .drawer-section {
+ display: inline-block;
+ width: 100%;
+ margin: 1em 0;
+ padding-left: 3.5em;
+ font-size: 0.9em;
+
+ .section-header {
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-start;
+ align-items: center;
+ color: var(--book-menu-header-color);
+ font-weight: 900;
+ margin-bottom: 0.25em;
+ font-size: 1.1em;
+ }
+
+ .item {
+ width: 100%;
+
+ a {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ color: var(--book-menu-item-color);
+ font-weight: 400;
+ border-radius: 0.4em;
+ padding: 0.25em 0.75em;
+ margin: 0.075em 0.75em;
+ margin-left: 1.5em;
+ cursor: pointer;
+ user-select: none;
+ }
+
+ a[aria-current] {
+ background-color: var(--brand-color);
+ color: var(--book-menu-item-active-color);
+ }
+
+ a:hover {
+ background-color: var(--brand-color);
+ color: var(--book-menu-item-hover-color);
+ }
+ }
+ }
+
+ @media only screen and (max-width: 800px) {
+ .menu {
+ display: block;
+ column-count: 2;
+ column-gap: 0;
+ column-fill: balance;
+
+ }
+
+ .drawer-section {
+ padding-left: 1.5em;
+ }
+ }
+ }
+
+ #doc-content {
+ display: block;
+ height: 100%;
+ width: 100%;
+ padding: 0 1.5em;
+ padding-bottom: 6em;
+ overflow-y: auto;
+ }
+}
+
+.search-link {
+ width: 100%;
+ height: 100%;
+
+ a {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: flex-start;
+ padding: 1em;
+ }
+}
+
+#welcome-page {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 1em;
+
+ #big-logo {
+ height: 10em;
+ width: 10em;
+ margin: 3.5em 0;
+ }
+
+ #slogan {
+ font-family: monospace;
+ font-size: 4em;
+ letter-spacing: 0.2em;
+ margin-top: 0em;
+ }
+
+ #sub-slogan {
+ font-size: 1.75em;
+ max-width: 75%;
+ text-align: center;
+ }
+
+ @media only screen and (max-width: 600px) {
+ #big-logo {
+ margin: 2.5em 0;
+ }
+
+ #slogan {
+ font-size: 3em;
+ }
+
+ #sub-slogan {
+ font-size: 1.5em;
+ }
+ }
+}
+
+.err-404 {
+ margin: auto;
+ max-width: 60em;
+ margin-top: 2em;
+ width: 100%;
+
+ .info {
+ #error {
+ font-size: 6em;
+ font-weight: bold;
+ margin: 0;
+ margin-bottom: 0.1em;
+ }
+ #whoops {
+ font-size: 3em;
+ font-weight: 500;
+ margin: 0;
+ margin-left: 0.5rem;
+ margin-right: 0.5rem;
+ margin-bottom: 1.5em;
+ text-align: center;
+ }
+ #back-btn {
+ font-size: 1.5em;
+ }
+ }
+
+ #ferris {
+ height: 14em;
+ width: auto;
+ margin-top: 2em;
+ }
+}
diff --git a/src-tauri/.cargo/config.toml b/src-tauri/.cargo/config.toml
new file mode 100644
index 0000000..7eaca81
--- /dev/null
+++ b/src-tauri/.cargo/config.toml
@@ -0,0 +1,4 @@
+[build]
+target = 'x86_64-apple-darwin'
+
+[target]
diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore
new file mode 100644
index 0000000..f4dfb82
--- /dev/null
+++ b/src-tauri/.gitignore
@@ -0,0 +1,4 @@
+# Generated by Cargo
+# will have compiled files and executables
+/target/
+
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
new file mode 100644
index 0000000..d90ffed
--- /dev/null
+++ b/src-tauri/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+name = "book"
+version = "0.0.0"
+description = "A Tauri App"
+authors = ["you"]
+license = ""
+repository = ""
+edition = "2021"
+
+[lib]
+name = "book_lib"
+crate-type = ["staticlib", "cdylib", "rlib"]
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[build-dependencies]
+tauri-build = { version = "2.0.0-alpha", features = [] }
+
+[dependencies]
+tauri = { version = "2.0.0-alpha", features = [] }
+tauri-plugin-window = "2.0.0-alpha"
+tauri-plugin-shell = "2.0.0-alpha"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+
+[features]
+# this feature is used for production builds or when `devPath` points to the filesystem
+# DO NOT REMOVE!!
+custom-protocol = ["tauri/custom-protocol"]
diff --git a/src-tauri/build.rs b/src-tauri/build.rs
new file mode 100644
index 0000000..795b9b7
--- /dev/null
+++ b/src-tauri/build.rs
@@ -0,0 +1,3 @@
+fn main() {
+ tauri_build::build()
+}
diff --git a/src-tauri/gen/android/.editorconfig b/src-tauri/gen/android/.editorconfig
new file mode 100644
index 0000000..ebe51d3
--- /dev/null
+++ b/src-tauri/gen/android/.editorconfig
@@ -0,0 +1,12 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = false
+insert_final_newline = false
\ No newline at end of file
diff --git a/src-tauri/gen/android/.gitignore b/src-tauri/gen/android/.gitignore
new file mode 100644
index 0000000..b248203
--- /dev/null
+++ b/src-tauri/gen/android/.gitignore
@@ -0,0 +1,19 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
+key.properties
+
+/.tauri
+/tauri.settings.gradle
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/.gitignore b/src-tauri/gen/android/app/.gitignore
new file mode 100644
index 0000000..eddfaf3
--- /dev/null
+++ b/src-tauri/gen/android/app/.gitignore
@@ -0,0 +1,5 @@
+/src/main/java/com/book/book/generated
+/src/main/jniLibs/**/*.so
+/src/main/assets/tauri.conf.json
+/tauri.build.gradle.kts
+/proguard-tauri.pro
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/build.gradle.kts b/src-tauri/gen/android/app/build.gradle.kts
new file mode 100644
index 0000000..8b9b008
--- /dev/null
+++ b/src-tauri/gen/android/app/build.gradle.kts
@@ -0,0 +1,57 @@
+plugins {
+ id("com.android.application")
+ id("org.jetbrains.kotlin.android")
+ id("rust")
+}
+
+android {
+ compileSdk = 33
+ namespace = "com.book.book"
+ defaultConfig {
+ manifestPlaceholders["usesCleartextTraffic"] = "false"
+ applicationId = "com.book.book"
+ minSdk = 24
+ targetSdk = 33
+ versionCode = 1
+ versionName = "1.0"
+ }
+ buildTypes {
+ getByName("debug") {
+ manifestPlaceholders["usesCleartextTraffic"] = "true"
+ isDebuggable = true
+ isJniDebuggable = true
+ isMinifyEnabled = false
+ packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
+ jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
+ jniLibs.keepDebugSymbols.add("*/x86/*.so")
+ jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
+ }
+ }
+ getByName("release") {
+ isMinifyEnabled = true
+ proguardFiles(
+ *fileTree(".") { include("**/*.pro") }
+ .plus(getDefaultProguardFile("proguard-android-optimize.txt"))
+ .toList().toTypedArray()
+ )
+ }
+ }
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
+
+rust {
+ rootDirRel = "../../../"
+}
+
+dependencies {
+ implementation("androidx.webkit:webkit:1.6.1")
+ implementation("androidx.appcompat:appcompat:1.6.1")
+ implementation("com.google.android.material:material:1.8.0")
+ testImplementation("junit:junit:4.13.2")
+ androidTestImplementation("androidx.test.ext:junit:1.1.4")
+ androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
+}
+
+apply(from = "tauri.build.gradle.kts")
diff --git a/src-tauri/gen/android/app/proguard-rules.pro b/src-tauri/gen/android/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/src-tauri/gen/android/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/src/main/AndroidManifest.xml b/src-tauri/gen/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e1866f5
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src-tauri/gen/android/app/src/main/java/com/book/book/MainActivity.kt b/src-tauri/gen/android/app/src/main/java/com/book/book/MainActivity.kt
new file mode 100644
index 0000000..d3f547f
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/java/com/book/book/MainActivity.kt
@@ -0,0 +1,3 @@
+package com.book.book
+
+class MainActivity : TauriActivity()
diff --git a/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml b/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml b/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..4fc2444
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..28f1aa1
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..85d0c88
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..28f1aa1
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..73e48db
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..13dd214
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..73e48db
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..1d98044
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..a888b33
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..1d98044
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..0818324
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..a2a838e
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..0818324
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b18bceb
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..3f8a57f
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b18bceb
Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/src-tauri/gen/android/app/src/main/res/values-night/themes.xml b/src-tauri/gen/android/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..ec25883
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/src-tauri/gen/android/app/src/main/res/values/colors.xml b/src-tauri/gen/android/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/src/main/res/values/strings.xml b/src-tauri/gen/android/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..e3d8b10
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ book
+ book
+
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/src/main/res/values/themes.xml b/src-tauri/gen/android/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..ec25883
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/values/themes.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml b/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml
new file mode 100644
index 0000000..782d63b
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src-tauri/gen/android/build.gradle.kts b/src-tauri/gen/android/build.gradle.kts
new file mode 100644
index 0000000..5ce764e
--- /dev/null
+++ b/src-tauri/gen/android/build.gradle.kts
@@ -0,0 +1,22 @@
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ dependencies {
+ classpath("com.android.tools.build:gradle:8.0.0")
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+tasks.register("clean").configure {
+ delete("build")
+}
+
diff --git a/src-tauri/gen/android/buildSrc/build.gradle.kts b/src-tauri/gen/android/buildSrc/build.gradle.kts
new file mode 100644
index 0000000..099feff
--- /dev/null
+++ b/src-tauri/gen/android/buildSrc/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ `kotlin-dsl`
+}
+
+gradlePlugin {
+ plugins {
+ create("pluginsForCoolKids") {
+ id = "rust"
+ implementationClass = "RustPlugin"
+ }
+ }
+}
+
+repositories {
+ google()
+ mavenCentral()
+}
+
+dependencies {
+ compileOnly(gradleApi())
+ implementation("com.android.tools.build:gradle:8.0.0")
+}
+
diff --git a/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/BuildTask.kt b/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/BuildTask.kt
new file mode 100644
index 0000000..13e84b7
--- /dev/null
+++ b/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/BuildTask.kt
@@ -0,0 +1,52 @@
+import java.io.File
+import org.apache.tools.ant.taskdefs.condition.Os
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
+import org.gradle.api.logging.LogLevel
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.TaskAction
+
+open class BuildTask : DefaultTask() {
+ @Input
+ var rootDirRel: String? = null
+ @Input
+ var target: String? = null
+ @Input
+ var release: Boolean? = null
+
+ @TaskAction
+ fun assemble() {
+ val executable = """/Users/aok/.cargo/bin/cargo-tauri""";
+ try {
+ runTauriCli(executable)
+ } catch (e: Exception) {
+ if (Os.isFamily(Os.FAMILY_WINDOWS)) {
+ runTauriCli("$executable.cmd")
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ fun runTauriCli(executable: String) {
+ val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null")
+ val target = target ?: throw GradleException("target cannot be null")
+ val release = release ?: throw GradleException("release cannot be null")
+ val args = listOf("tauri", "android", "android-studio-script");
+
+ project.exec {
+ workingDir(File(project.projectDir, rootDirRel))
+ executable(executable)
+ args(args)
+ if (project.logger.isEnabled(LogLevel.DEBUG)) {
+ args("-vv")
+ } else if (project.logger.isEnabled(LogLevel.INFO)) {
+ args("-v")
+ }
+ if (release) {
+ args("--release")
+ }
+ args(listOf("--target", target))
+ }.assertNormalExitValue()
+ }
+}
\ No newline at end of file
diff --git a/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/RustPlugin.kt b/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/RustPlugin.kt
new file mode 100644
index 0000000..4aa7fca
--- /dev/null
+++ b/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/RustPlugin.kt
@@ -0,0 +1,85 @@
+import com.android.build.api.dsl.ApplicationExtension
+import org.gradle.api.DefaultTask
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.get
+
+const val TASK_GROUP = "rust"
+
+open class Config {
+ lateinit var rootDirRel: String
+}
+
+open class RustPlugin : Plugin {
+ private lateinit var config: Config
+
+ override fun apply(project: Project) = with(project) {
+ config = extensions.create("rust", Config::class.java)
+
+ val defaultAbiList = listOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64");
+ val abiList = (findProperty("abiList") as? String)?.split(',') ?: defaultAbiList
+
+ val defaultArchList = listOf("arm64", "arm", "x86", "x86_64");
+ val archList = (findProperty("archList") as? String)?.split(',') ?: defaultArchList
+
+ val targetsList = (findProperty("targetList") as? String)?.split(',') ?: listOf("aarch64", "armv7", "i686", "x86_64")
+
+ extensions.configure {
+ @Suppress("UnstableApiUsage")
+ flavorDimensions.add("abi")
+ productFlavors {
+ create("universal") {
+ dimension = "abi"
+ ndk {
+ abiFilters += abiList
+ }
+ }
+ defaultArchList.forEachIndexed { index, arch ->
+ create(arch) {
+ dimension = "abi"
+ ndk {
+ abiFilters.add(defaultAbiList[index])
+ }
+ }
+ }
+ }
+ }
+
+ afterEvaluate {
+ for (profile in listOf("debug", "release")) {
+ val profileCapitalized = profile.replaceFirstChar { it.uppercase() }
+ val buildTask = tasks.maybeCreate(
+ "rustBuildUniversal$profileCapitalized",
+ DefaultTask::class.java
+ ).apply {
+ group = TASK_GROUP
+ description = "Build dynamic library in $profile mode for all targets"
+ }
+
+ tasks["mergeUniversal${profileCapitalized}JniLibFolders"].dependsOn(buildTask)
+
+ for (targetPair in targetsList.withIndex()) {
+ val targetName = targetPair.value
+ val targetArch = archList[targetPair.index]
+ val targetArchCapitalized = targetArch.replaceFirstChar { it.uppercase() }
+ val targetBuildTask = project.tasks.maybeCreate(
+ "rustBuild$targetArchCapitalized$profileCapitalized",
+ BuildTask::class.java
+ ).apply {
+ group = TASK_GROUP
+ description = "Build dynamic library in $profile mode for $targetArch"
+ rootDirRel = config.rootDirRel
+ target = targetName
+ release = profile == "release"
+ }
+
+ buildTask.dependsOn(targetBuildTask)
+ tasks["merge$targetArchCapitalized${profileCapitalized}JniLibFolders"].dependsOn(
+ targetBuildTask
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src-tauri/gen/android/gradle.properties b/src-tauri/gen/android/gradle.properties
new file mode 100644
index 0000000..022338b
--- /dev/null
+++ b/src-tauri/gen/android/gradle.properties
@@ -0,0 +1,25 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
+android.defaults.buildfeatures.buildconfig=true
+android.nonFinalResIds=false
\ No newline at end of file
diff --git a/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..40a4350
--- /dev/null
+++ b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue May 10 19:22:52 CST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/src-tauri/gen/android/gradlew b/src-tauri/gen/android/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/src-tauri/gen/android/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/src-tauri/gen/android/gradlew.bat b/src-tauri/gen/android/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/src-tauri/gen/android/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src-tauri/gen/android/settings.gradle b/src-tauri/gen/android/settings.gradle
new file mode 100644
index 0000000..3939116
--- /dev/null
+++ b/src-tauri/gen/android/settings.gradle
@@ -0,0 +1,3 @@
+include ':app'
+
+apply from: 'tauri.settings.gradle'
diff --git a/src-tauri/gen/apple/.gitignore b/src-tauri/gen/apple/.gitignore
new file mode 100644
index 0000000..6726e2f
--- /dev/null
+++ b/src-tauri/gen/apple/.gitignore
@@ -0,0 +1,3 @@
+xcuserdata/
+build/
+Externals/
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png
new file mode 100644
index 0000000..f8b128e
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png
new file mode 100644
index 0000000..6bbd9e3
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png
new file mode 100644
index 0000000..6bbd9e3
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png
new file mode 100644
index 0000000..f702cc0
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png
new file mode 100644
index 0000000..c5e92f7
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png
new file mode 100644
index 0000000..1c607d5
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png
new file mode 100644
index 0000000..1c607d5
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png
new file mode 100644
index 0000000..60e93a6
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png
new file mode 100644
index 0000000..6bbd9e3
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png
new file mode 100644
index 0000000..819410f
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png
new file mode 100644
index 0000000..819410f
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png
new file mode 100644
index 0000000..e00ae5a
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png
new file mode 100644
index 0000000..f5301f3
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512x512@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512x512@2x.png
new file mode 100644
index 0000000..5e9add7
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512x512@2x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png
new file mode 100644
index 0000000..e00ae5a
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png
new file mode 100644
index 0000000..3546ca1
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png
new file mode 100644
index 0000000..d836710
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png
new file mode 100644
index 0000000..29925f2
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png
new file mode 100644
index 0000000..dfd2261
Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png differ
diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..90eea7e
--- /dev/null
+++ b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,116 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "AppIcon-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "AppIcon-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "AppIcon-29x29@2x-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "AppIcon-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "AppIcon-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "AppIcon-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "AppIcon-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "AppIcon-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "AppIcon-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "AppIcon-20x20@2x-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "AppIcon-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "AppIcon-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "AppIcon-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "AppIcon-40x40@2x-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "AppIcon-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "AppIcon-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "AppIcon-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "AppIcon-512@2x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/src-tauri/gen/apple/Assets.xcassets/Contents.json b/src-tauri/gen/apple/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/src-tauri/gen/apple/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/src-tauri/gen/apple/ExportOptions.plist b/src-tauri/gen/apple/ExportOptions.plist
new file mode 100644
index 0000000..b69cf1d
--- /dev/null
+++ b/src-tauri/gen/apple/ExportOptions.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ method
+ development
+
+
diff --git a/src-tauri/gen/apple/Podfile b/src-tauri/gen/apple/Podfile
new file mode 100644
index 0000000..f1ce4c9
--- /dev/null
+++ b/src-tauri/gen/apple/Podfile
@@ -0,0 +1,21 @@
+# Uncomment the next line to define a global platform for your project
+
+target 'book_iOS' do
+platform :ios, '13.0'
+ # Pods for book_iOS
+end
+
+target 'book_macOS' do
+platform :osx, '11.0'
+ # Pods for book_macOS
+end
+
+# Delete the deployment target for iOS and macOS, causing it to be inherited from the Podfile
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ target.build_configurations.each do |config|
+ config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET'
+ config.build_settings.delete 'MACOSX_DEPLOYMENT_TARGET'
+ end
+ end
+end
diff --git a/src-tauri/gen/apple/Sources/book/bindings/bindings.h b/src-tauri/gen/apple/Sources/book/bindings/bindings.h
new file mode 100644
index 0000000..5152200
--- /dev/null
+++ b/src-tauri/gen/apple/Sources/book/bindings/bindings.h
@@ -0,0 +1,8 @@
+#pragma once
+
+namespace ffi {
+ extern "C" {
+ void start_app();
+ }
+}
+
diff --git a/src-tauri/gen/apple/Sources/book/main.mm b/src-tauri/gen/apple/Sources/book/main.mm
new file mode 100644
index 0000000..7793a9d
--- /dev/null
+++ b/src-tauri/gen/apple/Sources/book/main.mm
@@ -0,0 +1,6 @@
+#include "bindings/bindings.h"
+
+int main(int argc, char * argv[]) {
+ ffi::start_app();
+ return 0;
+}
diff --git a/src-tauri/gen/apple/book.xcodeproj/project.pbxproj b/src-tauri/gen/apple/book.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..b6816df
--- /dev/null
+++ b/src-tauri/gen/apple/book.xcodeproj/project.pbxproj
@@ -0,0 +1,456 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 07EB9EB55F68AFDC19286EB1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 502FD66F2D828502B6937E84 /* QuartzCore.framework */; };
+ 1F9EC8B62EE609F4DBC46C39 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 19272F761A553F8712399DDB /* UIKit.framework */; };
+ 3697FED17A38FE770D96191C /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9D8315FCB24011671E081CB /* WebKit.framework */; };
+ 47F0B0477D7FB05BC10C3404 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D86B221DC7163BC0FAA2A25 /* CoreGraphics.framework */; };
+ 6621EB723ECC6FFE5DEC54D1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A4E645CA73EFA14278503F9B /* Assets.xcassets */; };
+ 8381B7964402AC6E747AFB60 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1437ED7A1A5ADBD385AE606 /* Metal.framework */; };
+ 8DD816FF5392E18B02C30F53 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 4D55D8A35C2F7A8DF97EDDB5 /* assets */; };
+ C362EA7E1CC525DE197BEEA5 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8D11288099C194F3FC723F1 /* MetalKit.framework */; };
+ C60953C455E968E495E01F54 /* libbook_lib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D955991A316E82D5DB047DCD /* libbook_lib.a */; };
+ F687BCB8A175588940AEAA95 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAABE02D4E665E5685D26317 /* Security.framework */; };
+ FD7F76E5402B6D3EA93CFB50 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC1C9BD5E9ECC5AA72C14E55 /* main.mm */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 19272F761A553F8712399DDB /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ 223F6FA376249CCB1A474447 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; };
+ 34C6202E04831D540886F324 /* bindings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bindings.h; sourceTree = ""; };
+ 3D86B221DC7163BC0FAA2A25 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ 4D55D8A35C2F7A8DF97EDDB5 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = SOURCE_ROOT; };
+ 502FD66F2D828502B6937E84 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+ A33B99D3ADEA2990135854C7 /* book_iOS.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = book_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ A4E645CA73EFA14278503F9B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ B47A9B12CF9D828061100052 /* lib.rs */ = {isa = PBXFileReference; path = lib.rs; sourceTree = ""; };
+ B9D8315FCB24011671E081CB /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
+ BC1C9BD5E9ECC5AA72C14E55 /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; };
+ D1437ED7A1A5ADBD385AE606 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
+ D955991A316E82D5DB047DCD /* libbook_lib.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libbook_lib.a; sourceTree = ""; };
+ D96D9232829CE72A68D66701 /* book_iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = book_iOS.entitlements; sourceTree = ""; };
+ DC024FE1F811AD3F9CF97367 /* main.rs */ = {isa = PBXFileReference; path = main.rs; sourceTree = ""; };
+ EAABE02D4E665E5685D26317 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
+ F8D11288099C194F3FC723F1 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 2773691936923BDC5A173329 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ C60953C455E968E495E01F54 /* libbook_lib.a in Frameworks */,
+ 47F0B0477D7FB05BC10C3404 /* CoreGraphics.framework in Frameworks */,
+ 8381B7964402AC6E747AFB60 /* Metal.framework in Frameworks */,
+ C362EA7E1CC525DE197BEEA5 /* MetalKit.framework in Frameworks */,
+ 07EB9EB55F68AFDC19286EB1 /* QuartzCore.framework in Frameworks */,
+ F687BCB8A175588940AEAA95 /* Security.framework in Frameworks */,
+ 1F9EC8B62EE609F4DBC46C39 /* UIKit.framework in Frameworks */,
+ 3697FED17A38FE770D96191C /* WebKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 2A199E4F9B7AED783C841B21 /* Externals */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ path = Externals;
+ sourceTree = "";
+ };
+ 30C5B33B7BC6299E874056AB /* book_iOS */ = {
+ isa = PBXGroup;
+ children = (
+ D96D9232829CE72A68D66701 /* book_iOS.entitlements */,
+ 223F6FA376249CCB1A474447 /* Info.plist */,
+ );
+ path = book_iOS;
+ sourceTree = "";
+ };
+ 49B669CD106B148DDBE79AD3 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ A33B99D3ADEA2990135854C7 /* book_iOS.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 91C5C5ABC648A50BC038E9B0 /* src */ = {
+ isa = PBXGroup;
+ children = (
+ B47A9B12CF9D828061100052 /* lib.rs */,
+ DC024FE1F811AD3F9CF97367 /* main.rs */,
+ );
+ name = src;
+ path = ../../src;
+ sourceTree = "";
+ };
+ 91FA5F2617012ECB991EA4B8 /* bindings */ = {
+ isa = PBXGroup;
+ children = (
+ 34C6202E04831D540886F324 /* bindings.h */,
+ );
+ path = bindings;
+ sourceTree = "";
+ };
+ A1DAE41A85C4748AE7627587 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 3D86B221DC7163BC0FAA2A25 /* CoreGraphics.framework */,
+ D955991A316E82D5DB047DCD /* libbook_lib.a */,
+ D1437ED7A1A5ADBD385AE606 /* Metal.framework */,
+ F8D11288099C194F3FC723F1 /* MetalKit.framework */,
+ 502FD66F2D828502B6937E84 /* QuartzCore.framework */,
+ EAABE02D4E665E5685D26317 /* Security.framework */,
+ 19272F761A553F8712399DDB /* UIKit.framework */,
+ B9D8315FCB24011671E081CB /* WebKit.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ CD2F05A02DDE2A901917CC91 /* book */ = {
+ isa = PBXGroup;
+ children = (
+ BC1C9BD5E9ECC5AA72C14E55 /* main.mm */,
+ 91FA5F2617012ECB991EA4B8 /* bindings */,
+ );
+ path = book;
+ sourceTree = "";
+ };
+ D2D94A4F380D848B244C8EEA /* Sources */ = {
+ isa = PBXGroup;
+ children = (
+ CD2F05A02DDE2A901917CC91 /* book */,
+ );
+ path = Sources;
+ sourceTree = "";
+ };
+ EAA221D6DC39CFA4976B82C0 = {
+ isa = PBXGroup;
+ children = (
+ 4D55D8A35C2F7A8DF97EDDB5 /* assets */,
+ A4E645CA73EFA14278503F9B /* Assets.xcassets */,
+ 30C5B33B7BC6299E874056AB /* book_iOS */,
+ 2A199E4F9B7AED783C841B21 /* Externals */,
+ D2D94A4F380D848B244C8EEA /* Sources */,
+ 91C5C5ABC648A50BC038E9B0 /* src */,
+ A1DAE41A85C4748AE7627587 /* Frameworks */,
+ 49B669CD106B148DDBE79AD3 /* Products */,
+ );
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 298FD83A23DC2F275E467343 /* book_iOS */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 3C0367FC0AEC53BA985AD036 /* Build configuration list for PBXNativeTarget "book_iOS" */;
+ buildPhases = (
+ 010947584CD0C112EEA21FA4 /* Build Rust Code */,
+ 45AB425E9092D28C15026378 /* Sources */,
+ 20E0C8EC49AE78785547EC11 /* Resources */,
+ 2773691936923BDC5A173329 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = book_iOS;
+ productName = book_iOS;
+ productReference = A33B99D3ADEA2990135854C7 /* book_iOS.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 76D41858814BBE80481186FD /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = YES;
+ LastUpgradeCheck = 1430;
+ TargetAttributes = {
+ };
+ };
+ buildConfigurationList = 75E8567F6082E44D7D3577E8 /* Build configuration list for PBXProject "book" */;
+ compatibilityVersion = "Xcode 14.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ Base,
+ en,
+ );
+ mainGroup = EAA221D6DC39CFA4976B82C0;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 298FD83A23DC2F275E467343 /* book_iOS */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 20E0C8EC49AE78785547EC11 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6621EB723ECC6FFE5DEC54D1 /* Assets.xcassets in Resources */,
+ 8DD816FF5392E18B02C30F53 /* assets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 010947584CD0C112EEA21FA4 /* Build Rust Code */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ name = "Build Rust Code";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(SRCROOT)/target/aarch64-apple-ios/${CONFIGURATION}/deps/libbook_lib.a",
+ "$(SRCROOT)/target/x86_64-apple-ios/${CONFIGURATION}/deps/libbook_lib.a",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/Users/aok/.cargo/bin/cargo-tauri tauri ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths \"${FRAMEWORK_SEARCH_PATHS:?}\" --header-search-paths \"${HEADER_SEARCH_PATHS:?}\" --gcc-preprocessor-definitions \"${GCC_PREPROCESSOR_DEFINITIONS:-}\" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?}";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 45AB425E9092D28C15026378 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FD7F76E5402B6D3EA93CFB50 /* main.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 6E917E4BF8CB18D312EAFCBE /* debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(inherited)",
+ "DEBUG=1",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = debug;
+ };
+ 8FC2757CBDB1C98971E170A1 /* debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ ARCHS = (
+ arm64,
+ x86_64,
+ );
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_ENTITLEMENTS = book_iOS/book_iOS.entitlements;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ DEVELOPMENT_TEAM = 12345;
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\".\"",
+ );
+ INFOPLIST_FILE = book_iOS/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ "LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
+ "LIBRARY_SEARCH_PATHS[arch=arm64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
+ "LIBRARY_SEARCH_PATHS[arch=x86_64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
+ PRODUCT_BUNDLE_IDENTIFIER = com.book.book;
+ PRODUCT_NAME = book;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALID_ARCHS = "arm64 x86_64";
+ };
+ name = debug;
+ };
+ CA70F0C850C9C2B1A2C72960 /* release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ SWIFT_VERSION = 5.0;
+ };
+ name = release;
+ };
+ F551B91FFE058B0C9E3492B7 /* release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ ARCHS = (
+ arm64,
+ x86_64,
+ );
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_ENTITLEMENTS = book_iOS/book_iOS.entitlements;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ DEVELOPMENT_TEAM = 12345;
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\".\"",
+ );
+ INFOPLIST_FILE = book_iOS/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ "LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
+ "LIBRARY_SEARCH_PATHS[arch=arm64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
+ "LIBRARY_SEARCH_PATHS[arch=x86_64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
+ PRODUCT_BUNDLE_IDENTIFIER = com.book.book;
+ PRODUCT_NAME = book;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALID_ARCHS = "arm64 x86_64";
+ };
+ name = release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 3C0367FC0AEC53BA985AD036 /* Build configuration list for PBXNativeTarget "book_iOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 8FC2757CBDB1C98971E170A1 /* debug */,
+ F551B91FFE058B0C9E3492B7 /* release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = debug;
+ };
+ 75E8567F6082E44D7D3577E8 /* Build configuration list for PBXProject "book" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 6E917E4BF8CB18D312EAFCBE /* debug */,
+ CA70F0C850C9C2B1A2C72960 /* release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = debug;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 76D41858814BBE80481186FD /* Project object */;
+}
diff --git a/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..ac90d5a
--- /dev/null
+++ b/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,10 @@
+
+
+
+
+ BuildSystemType
+ Original
+ DisableBuildSystemDeprecationDiagnostic
+
+
+
diff --git a/src-tauri/gen/apple/book.xcodeproj/xcshareddata/xcschemes/book_iOS.xcscheme b/src-tauri/gen/apple/book.xcodeproj/xcshareddata/xcschemes/book_iOS.xcscheme
new file mode 100644
index 0000000..187da61
--- /dev/null
+++ b/src-tauri/gen/apple/book.xcodeproj/xcshareddata/xcschemes/book_iOS.xcscheme
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src-tauri/gen/apple/book_iOS/Info.plist b/src-tauri/gen/apple/book_iOS/Info.plist
new file mode 100644
index 0000000..9cbeec6
--- /dev/null
+++ b/src-tauri/gen/apple/book_iOS/Info.plist
@@ -0,0 +1,44 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 0.0.0
+ CFBundleVersion
+ 0.0.0
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiredDeviceCapabilities
+
+ arm64
+ metal
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/src-tauri/gen/apple/book_iOS/book_iOS.entitlements b/src-tauri/gen/apple/book_iOS/book_iOS.entitlements
new file mode 100644
index 0000000..0c67376
--- /dev/null
+++ b/src-tauri/gen/apple/book_iOS/book_iOS.entitlements
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src-tauri/gen/apple/project.yml b/src-tauri/gen/apple/project.yml
new file mode 100644
index 0000000..4ca58e5
--- /dev/null
+++ b/src-tauri/gen/apple/project.yml
@@ -0,0 +1,88 @@
+name: book
+options:
+ bundleIdPrefix: com.book
+ deploymentTarget:
+ iOS: 13.0
+fileGroups: [../../src]
+configs:
+ debug: debug
+ release: release
+settingGroups:
+ app:
+ base:
+ PRODUCT_NAME: book
+ PRODUCT_BUNDLE_IDENTIFIER: com.book.book
+ DEVELOPMENT_TEAM: 12345
+targetTemplates:
+ app:
+ type: application
+ sources:
+ - path: Sources
+ scheme:
+ environmentVariables:
+ RUST_BACKTRACE: full
+ RUST_LOG: info
+ settings:
+ groups: [app]
+targets:
+ book_iOS:
+ type: application
+ platform: iOS
+ sources:
+ - path: Sources
+ - path: Assets.xcassets
+ - path: Externals
+ - path: book_iOS
+ - path: assets
+ buildPhase: resources
+ type: folder
+ info:
+ path: book_iOS/Info.plist
+ properties:
+ LSRequiresIPhoneOS: true
+ UILaunchStoryboardName: LaunchScreen
+ UIRequiredDeviceCapabilities: [arm64, metal]
+ UISupportedInterfaceOrientations:
+ - UIInterfaceOrientationPortrait
+ - UIInterfaceOrientationLandscapeLeft
+ - UIInterfaceOrientationLandscapeRight
+ UISupportedInterfaceOrientations~ipad:
+ - UIInterfaceOrientationPortrait
+ - UIInterfaceOrientationPortraitUpsideDown
+ - UIInterfaceOrientationLandscapeLeft
+ - UIInterfaceOrientationLandscapeRight
+ CFBundleShortVersionString: 0.0.0
+ CFBundleVersion: 0.0.0
+ entitlements:
+ path: book_iOS/book_iOS.entitlements
+ scheme:
+ environmentVariables:
+ RUST_BACKTRACE: full
+ RUST_LOG: info
+ settings:
+ base:
+ ENABLE_BITCODE: false
+ ARCHS: [arm64, x86_64]
+ VALID_ARCHS: arm64 x86_64
+ LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
+ LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
+ LIBRARY_SEARCH_PATHS[arch=arm64-sim]: $(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true
+ groups: [app]
+ dependencies:
+ - framework: libbook_lib.a
+ embed: false
+ - sdk: CoreGraphics.framework
+ - sdk: Metal.framework
+ - sdk: MetalKit.framework
+ - sdk: QuartzCore.framework
+ - sdk: Security.framework
+ - sdk: UIKit.framework
+ - sdk: WebKit.framework
+ preBuildScripts:
+ - script: /Users/aok/.cargo/bin/cargo-tauri tauri ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths "${FRAMEWORK_SEARCH_PATHS:?}" --header-search-paths "${HEADER_SEARCH_PATHS:?}" --gcc-preprocessor-definitions "${GCC_PREPROCESSOR_DEFINITIONS:-}" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?}
+ name: Build Rust Code
+ basedOnDependencyAnalysis: false
+ outputFiles:
+ - $(SRCROOT)/target/aarch64-apple-ios/${CONFIGURATION}/deps/libbook_lib.a
+ - $(SRCROOT)/target/x86_64-apple-ios/${CONFIGURATION}/deps/libbook_lib.a
\ No newline at end of file
diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png
new file mode 100644
index 0000000..6be5e50
Binary files /dev/null and b/src-tauri/icons/128x128.png differ
diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png
new file mode 100644
index 0000000..e81bece
Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ
diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png
new file mode 100644
index 0000000..a437dd5
Binary files /dev/null and b/src-tauri/icons/32x32.png differ
diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png
new file mode 100644
index 0000000..0ca4f27
Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ
diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png
new file mode 100644
index 0000000..b81f820
Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ
diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png
new file mode 100644
index 0000000..624c7bf
Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ
diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png
new file mode 100644
index 0000000..c021d2b
Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ
diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png
new file mode 100644
index 0000000..6219700
Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ
diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png
new file mode 100644
index 0000000..f9bc048
Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ
diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png
new file mode 100644
index 0000000..d5fbfb2
Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ
diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png
new file mode 100644
index 0000000..63440d7
Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ
diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png
new file mode 100644
index 0000000..f3f705a
Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ
diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png
new file mode 100644
index 0000000..4556388
Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ
diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns
new file mode 100644
index 0000000..12a5bce
Binary files /dev/null and b/src-tauri/icons/icon.icns differ
diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico
new file mode 100644
index 0000000..b3636e4
Binary files /dev/null and b/src-tauri/icons/icon.ico differ
diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png
new file mode 100644
index 0000000..e1cd261
Binary files /dev/null and b/src-tauri/icons/icon.png differ
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
new file mode 100644
index 0000000..5f48bdb
--- /dev/null
+++ b/src-tauri/src/lib.rs
@@ -0,0 +1,15 @@
+// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
+#[tauri::command]
+fn greet(name: &str) -> String {
+ format!("Hello, {}! You've been greeted from Rust!", name)
+}
+
+#[cfg_attr(mobile, tauri::mobile_entry_point)]
+pub fn run() {
+ tauri::Builder::default()
+ .plugin(tauri_plugin_window::init())
+ .plugin(tauri_plugin_shell::init())
+ .invoke_handler(tauri::generate_handler![greet])
+ .run(tauri::generate_context!())
+ .expect("error while running tauri application");
+}
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
new file mode 100644
index 0000000..914ef39
--- /dev/null
+++ b/src-tauri/src/main.rs
@@ -0,0 +1,7 @@
+// Prevents additional console window on Windows in release, DO NOT REMOVE!!
+#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+
+fn main() {
+ #[cfg(desktop)]
+ nigiginc_lib::run();
+}
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
new file mode 100644
index 0000000..e5a9328
--- /dev/null
+++ b/src-tauri/tauri.conf.json
@@ -0,0 +1,44 @@
+{
+ "build": {
+ "beforeDevCommand": "trunk serve",
+ "beforeBuildCommand": "trunk build",
+ "devPath": "http://localhost:1420",
+ "distDir": "../dist",
+ "withGlobalTauri": true
+ },
+ "package": {
+ "productName": "book",
+ "version": "0.0.0"
+ },
+ "tauri": {
+ "bundle": {
+ "active": true,
+ "targets": "all",
+ "identifier": "com.book.dev",
+ "icon": [
+ "icons/32x32.png",
+ "icons/128x128.png",
+ "icons/128x128@2x.png",
+ "icons/icon.icns",
+ "icons/icon.ico"
+ ]
+ },
+ "security": {
+ "csp": null
+ },
+ "windows": [
+ {
+ "fullscreen": false,
+ "resizable": true,
+ "title": "book",
+ "width": 800,
+ "height": 600
+ }
+ ]
+ },
+ "plugins": {
+ "shell": {
+ "open": true
+ }
+ }
+}
diff --git a/src/app.rs b/src/app.rs
new file mode 100644
index 0000000..6e771f2
--- /dev/null
+++ b/src/app.rs
@@ -0,0 +1,273 @@
+use std::fmt::Display;
+
+use leptos::{leptos_dom::Callback, *};
+use leptos_icons::BsIcon;
+use leptos_meta::{provide_meta_context, Title};
+use leptos_router::*;
+use leptos_use::use_media_query;
+
+use leptonic::prelude::*;
+
+use crate::pages::{documentation::doc_root::DocRoutes, err404::PageErr404, welcome::PageWelcome};
+
+#[derive(Debug, Copy, Clone)]
+pub enum AppRoutes {
+ Welcome,
+ Doc,
+ NotFound,
+}
+
+impl AppRoutes {
+ pub fn route(self) -> &'static str {
+ match self {
+ AppRoutes::Welcome => "",
+ AppRoutes::Doc => "doc",
+ AppRoutes::NotFound => "*", // Leptos requires this to be be named "*"!
+ }
+ }
+}
+
+/// Required so that `Routes` variants can be used in `` definitions.
+impl Display for AppRoutes {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(self.route())
+ }
+}
+
+/// Required so that `Routes` variants can be used in ` ` definitions.
+impl ToHref for AppRoutes {
+ fn to_href(&self) -> Box String + '_> {
+ Box::new(move || format!("/{}", self.route()))
+ }
+}
+
+#[component]
+pub fn App() -> impl IntoView {
+ provide_meta_context();
+
+ view! {
+
+
+
+
+ }>
+ }/>
+
+ }/>
+
+
+
+
+ }
+}
+
+pub const APP_BAR_HEIGHT: Height = Height::Em(3.5);
+
+#[derive(Debug, Clone, Copy)]
+pub struct AppLayoutContext {
+ pub is_small: Signal,
+ pub main_drawer_closed: Signal,
+ set_main_drawer_closed: WriteSignal,
+ pub doc_drawer_closed: Signal,
+ set_doc_drawer_closed: WriteSignal,
+}
+
+impl AppLayoutContext {
+ #[allow(unused)]
+ pub fn close_main_drawer(&self) {
+ self.set_main_drawer_closed.set(true);
+ }
+
+ pub fn close_doc_drawer(&self) {
+ self.set_doc_drawer_closed.set(true);
+ }
+
+ pub fn toggle_main_drawer(&self) {
+ let currently_closed = self.main_drawer_closed.get_untracked();
+ self.set_main_drawer_closed.set(!currently_closed);
+ if currently_closed {
+ self.close_doc_drawer();
+ }
+ }
+
+ pub fn toggle_doc_drawer(&self) {
+ let currently_closed = self.doc_drawer_closed.get_untracked();
+ self.set_doc_drawer_closed.set(!currently_closed);
+ if currently_closed {
+ self.close_main_drawer();
+ }
+ }
+}
+
+#[component]
+pub fn Layout() -> impl IntoView {
+ let is_small = use_media_query("(max-width: 800px)");
+ let router_context = use_router();
+ let is_doc = create_memo(move |_| router_context.pathname().get().starts_with("/doc"));
+
+ // The main drawer is only used on mobile / small screens!.
+ let (main_drawer_closed, set_main_drawer_closed) = create_signal(true);
+ let (doc_drawer_closed, set_doc_drawer_closed) = create_signal(false);
+
+ // Always close the doc-drawer when the application is now small.
+ // Always open the doc-drawer when the application is no longer small.
+ create_effect(move |_| {
+ if is_small.get() {
+ set_doc_drawer_closed.set(true);
+ } else {
+ set_doc_drawer_closed.set(false);
+ }
+ });
+
+ // Always close the main-drawer when the application is no longer small.
+ create_effect(move |_| {
+ if !is_small.get() {
+ set_main_drawer_closed.set(true);
+ }
+ });
+
+ let ctx = AppLayoutContext {
+ is_small,
+ main_drawer_closed: main_drawer_closed.into(),
+ set_main_drawer_closed,
+ doc_drawer_closed: doc_drawer_closed.into(),
+ set_doc_drawer_closed,
+ };
+
+ provide_context(ctx);
+
+ let search_options = vec![
+ (
+ "overview",
+ QuicksearchOption {
+ view: Callback::new(move |()| {
+ view! {
+
+ "Overview"
+
+ }
+ }),
+ on_select: Callback::new(move |()| {}),
+ },
+ ),
+ (
+ "installation",
+ QuicksearchOption {
+ view: Callback::new(move |()| {
+ view! {
+
+ "Installation"
+
+ }
+ }),
+ on_select: Callback::new(move |()| {}),
+ },
+ ),
+ (
+ "usage",
+ QuicksearchOption {
+ view: Callback::new(move |()| {
+ view! {
+
+ "Usage"
+
+ }
+ }),
+ on_select: Callback::new(move |()| {}),
+ },
+ ),
+ ];
+
+ let logo = move || {
+ view! {
+
+
+
+ }
+ };
+
+ view! {
+
+
+
+ { move || match (is_doc.get(), is_small.get()) {
+ (false, true) => logo().into_view(),
+ (true, true) => view! {
+
+ { logo }
+ }.into_view(),
+ (_, false) => view! {
+ { logo }
+
+
+ "Docs"
+
+
+ }.into_view(),
+ } }
+
+
+
+
+ { move || match is_small.get() {
+ true => view! { }.into_view(),
+ false => view! { "Search"}.into_view(),
+ } }
+
+ }
+ query=move |search: String| {
+ if search.is_empty() {
+ return vec![];
+ }
+ let lower_search = search.to_lowercase();
+ // TODO: Re-enable in rc3
+ //search_options.iter()
+ // .filter(|it| it.0.to_lowercase().contains(&lower_search))
+ // .map(|it| it.1.clone())
+ // .collect::>()
+ Vec::new()
+ }
+ />
+
+
+
+ { move || match is_small.get() {
+ true => view! {
+
+ }.into_view(),
+ false => view! {
+ "v0.2.0"
+
+
+
+
+
+
+ }.into_view(),
+ } }
+
+
+
+
+
+ // will show nested child routes.
+
+
+
+
+
+
+ }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..7d4bcb2
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,18 @@
+use leptos::*;
+
+mod app;
+mod pages;
+
+use crate::app::*;
+
+fn main() {
+ console_error_panic_hook::set_once();
+ tracing_wasm::set_as_global_default_with_config(
+ tracing_wasm::WASMLayerConfigBuilder::default()
+ .set_max_level(tracing::Level::DEBUG)
+ .build(),
+ );
+ mount_to_body(|| {
+ view! { }
+ })
+}
diff --git a/src/pages/documentation/alert.rs b/src/pages/documentation/alert.rs
new file mode 100644
index 0000000..2dd6310
--- /dev/null
+++ b/src/pages/documentation/alert.rs
@@ -0,0 +1,47 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageAlert() -> impl IntoView {
+ let (centered, set_centered) = create_signal(false);
+ view! {
+ "Alerts"
+
+
+ {indoc!(r#"
+ "Action completed."
+ "#)}
+
+
+ "Action completed."
+ "This concept is based on [...]"
+ "This seems not plausible."
+ "There was an error!"
+
+ "Alerts can be dynamically centered using a signal."
+
+ "Center toggle"
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --alert-margin
+ --alert-padding
+ --alert-primary-background-color
+ --alert-primary-color
+ --alert-info-background-color
+ --alert-info-color
+ --alert-success-background-color
+ --alert-success-color
+ --alert-warn-background-color
+ --alert-warn-color
+ --alert-danger-background-color
+ --alert-danger-color
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/anchor.rs b/src/pages/documentation/anchor.rs
new file mode 100644
index 0000000..b24bb98
--- /dev/null
+++ b/src/pages/documentation/anchor.rs
@@ -0,0 +1,14 @@
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageAnchor() -> impl IntoView {
+ view! {
+ "Anchor"
+
+
+ "Test Heading"
+
+
+ }
+}
diff --git a/src/pages/documentation/app_bar.rs b/src/pages/documentation/app_bar.rs
new file mode 100644
index 0000000..adfac60
--- /dev/null
+++ b/src/pages/documentation/app_bar.rs
@@ -0,0 +1,66 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+use leptos_icons::BsIcon;
+
+#[component]
+pub fn PageAppBar() -> impl IntoView {
+ let app_bar_height = Height::Em(3.0);
+
+ view! {
+ "App Bar"
+
+ "The """
" component sticks to the top of its parent and provides a convenient entrypoint for many app layouts."
+
+
+
+ "Leptonic"
+
+
+
+
+
+
+
+ "Scroll ↓"
+
+ {(0..10).map(|_| view! { }).collect_view()}
+
+
+
+
+
+ {indoc!(r#"
+
+
+ "Leptonic"
+
+
+
+
+
+
+
+ "Scroll ↓"
+
+ {(0..10).map(|_| view! { }).collect_view()}
+
+
+
+ "#)}
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --app-bar-height
+ --app-bar-background-color
+ --app-bar-border-bottom
+ --app-bar-box-shadow
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/button.rs b/src/pages/documentation/button.rs
new file mode 100644
index 0000000..ff5a443
--- /dev/null
+++ b/src/pages/documentation/button.rs
@@ -0,0 +1,240 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageButton() -> impl IntoView {
+ let (disabled, set_disabled) = create_signal(false);
+ let (num_main_action_activated, set_num_main_action_activated) = create_signal(0);
+ let (num_secondary_action_activated, set_num_secondary_action_activated) = create_signal(0);
+ view! {
+ "Buttons"
+
+ "Buttons are one of the most common input mechanisms with which your users can interact with your software."
+ "Buttons only require an action handler and can therefor be created like in this minimal example."
+
+
+ {indoc!(r#"
+
+ "My Button"
+
+ "#)}
+
+
+
+ "My Button"
+
+
+ "Variants"
+
+
+ "Buttons come in three different " "ButtonVariant"
"s."
+ "Flat"
", "
+ "Outlined"
" and "
+ "Filled"
", with the Filled variant being the default, hence the visual of our simple button above."
+
+
+
+ {indoc!(r#"
+ "Flat"
+ "Outlined"
+ "Filled"
+ "#)}
+
+
+
+ "Flat"
+ "Outlined"
+ "Filled"
+
+
+ "Button group"
+
+ "Buttons can be displayed in a group. This lets adjacent buttons snap to each other, creating a seamless row of buttons. It is recommended to only use the Filled button variant when putting buttons inside a group."
+
+
+ {indoc!(r#"
+
+ "Button 1"
+ "Button 2"
+ "Button 3"
+
+ "#)}
+
+
+
+ "Button 1"
+ "Button 2"
+ "Button 3"
+
+
+ "Disabled"
+
+ "Buttons can be set disabled using a signal."
+
+
+ "Buttons can be disabled using the "
+ "disabled"
+ " property. You can supply anything evaluating to a boolean, including signals."
+
+
+
+ {indoc!(r#"
+ "Disabled"
+ "Disabled"
+ "Disabled"
+ "#)}
+
+
+
+ "Disable: "
+
+
+
+ "Disabled"
+ "Disabled"
+ "Disabled"
+
+
+ "Variations"
+
+
+ {indoc!(r#"
+ "Secondary action"
+ }.into_view()>
+ "MainAction"
+
+ "#)}
+
+
+ "Secondary action"
+ }.into_view()>
+ "MainAction"
+
+
+ "Main action activated: " { move || num_main_action_activated.get() }
+ "Secondary action activated: " { move || num_secondary_action_activated.get() }
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --button-border-size
+ --button-border-radius
+ --button-box-shadow-opacity
+
+ --button-flat-primary-text-color
+ --button-flat-primary-text-color-hover
+ --button-flat-primary-background-color-hover
+
+ --button-outlined-primary-text-color
+ --button-outlined-primary-text-color-hover
+ --button-outlined-primary-border-color
+ --button-outlined-primary-border-color-hover
+ --button-outlined-primary-box-shadow-color
+
+ --button-filled-primary-text-color
+ --button-filled-primary-text-color-hover
+ --button-filled-primary-background-color
+ --button-filled-primary-background-color-hover
+ --button-filled-primary-border-color
+ --button-filled-primary-border-color-hover
+ --button-filled-primary-box-shadow-color
+
+ --button-flat-secondary-text-color
+ --button-flat-secondary-text-color-hover
+ --button-flat-secondary-background-color-hover
+
+ --button-outlined-secondary-text-color
+ --button-outlined-secondary-text-color-hover
+ --button-outlined-secondary-border-color
+ --button-outlined-secondary-border-color-hover
+ --button-outlined-secondary-box-shadow-color
+
+ --button-filled-secondary-text-color
+ --button-filled-secondary-text-color-hover
+ --button-filled-secondary-background-color
+ --button-filled-secondary-background-color-hover
+ --button-filled-secondary-border-color
+ --button-filled-secondary-border-color-hover
+ --button-filled-secondary-box-shadow-color
+
+ --button-flat-success-text-color
+ --button-flat-success-text-color-hover
+ --button-flat-success-background-color-hover
+
+ --button-outlined-success-text-color
+ --button-outlined-success-text-color-hover
+ --button-outlined-success-border-color
+ --button-outlined-success-border-color-hover
+ --button-outlined-success-box-shadow-color
+
+ --button-filled-success-text-color
+ --button-filled-success-text-color-hover
+ --button-filled-success-background-color
+ --button-filled-success-background-color-hover
+ --button-filled-success-border-color
+ --button-filled-success-border-color-hover
+ --button-filled-success-box-shadow-color
+
+ --button-flat-info-text-color
+ --button-flat-info-text-color-hover
+ --button-flat-info-background-color-hover
+
+ --button-outlined-info-text-color
+ --button-outlined-info-text-color-hover
+ --button-outlined-info-border-color
+ --button-outlined-info-border-color-hover
+ --button-outlined-info-box-shadow-color
+
+ --button-filled-info-text-color
+ --button-filled-info-text-color-hover
+ --button-filled-info-background-color
+ --button-filled-info-background-color-hover
+ --button-filled-info-border-color
+ --button-filled-info-border-color-hover
+ --button-filled-info-box-shadow-color
+
+ --button-flat-warning-text-color
+ --button-flat-warning-text-color-hover
+ --button-flat-warning-background-color-hover
+
+ --button-outlined-warning-text-color
+ --button-outlined-warning-text-color-hover
+ --button-outlined-warning-border-color
+ --button-outlined-warning-border-color-hover
+ --button-outlined-warning-box-shadow-color
+
+ --button-filled-warning-text-color
+ --button-filled-warning-text-color-hover
+ --button-filled-warning-background-color
+ --button-filled-warning-background-color-hover
+ --button-filled-warning-border-color
+ --button-filled-warning-border-color-hover
+ --button-filled-warning-box-shadow-color
+
+ --button-flat-danger-text-color
+ --button-flat-danger-text-color-hover
+ --button-flat-danger-background-color-hover
+
+ --button-outlined-danger-text-color
+ --button-outlined-danger-text-color-hover
+ --button-outlined-danger-border-color
+ --button-outlined-danger-border-color-hover
+ --button-outlined-danger-box-shadow-color
+
+ --button-filled-danger-text-color
+ --button-filled-danger-text-color-hover
+ --button-filled-danger-background-color
+ --button-filled-danger-background-color-hover
+ --button-filled-danger-border-color
+ --button-filled-danger-border-color-hover
+ --button-filled-danger-box-shadow-color
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/callback.rs b/src/pages/documentation/callback.rs
new file mode 100644
index 0000000..e6f12e8
--- /dev/null
+++ b/src/pages/documentation/callback.rs
@@ -0,0 +1,130 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageCallback() -> impl IntoView {
+ view! {
+ "Callbacks"
+
+
+ {indoc!(r#"
+ #[prop(into)] set_value: Callback,
+ #[prop(into, optional)] maybe_render: Option>
+
+ ...
+
+ set_value.call(42);
+
+ if let Some(render) = maybe_render {
+ let _view = render.call(("foo".to_owned()));
+ }
+ "#)}
+
+
+
+ "Easy, isn't it?"
+
+
+
+ "Callbacks can sometimes be tricky in Leptos. When realized with generics, they are easy to use as a component-consumer and fast, "
+ "but hard(er) to write as an author and, because of their generic nature, increase WASM binary size."
+
+
+
+ "If the property of a component specifying a callback should be ""Option"
"al, the generic approach is of no help, "
+ "as Rust will have problems inferring unspecified, and not otherwise explicitly nameable, types "
+ "for the properties left out when instantiating such a component."
+
+
+
+ "An additional burden with the generic approach: ""Copy"
". "
+ "A simple generic ""Fn"
" prop can only be moved once inside your component when not specified with an additional "
+ "Copy"
" bound. "
+ "But doing so will limit your users ability to write compiling closures for the callback, "
+ "as only closures not taking ownership of non-copyable-values, implicitly making the closure copy, can be used."
+
+
+
+ "If you, as a component author, need to move a callback into more places, you can put it inside a call to "
+ "store_value"
" to get an easily copyable and therefore moveable value. "
+ "Using a generic ""Fn"
" in the non-optional case or a ""Box"
" in "
+ "case the prop is optional and always storing it boxed in a ""StoredValue"
" is good solution. "
+ "Arguments could be made that "
+
+
+
+ "(a) components are not often recreated, as we always want to reactively updated their content instead"
+ "(b) some callbacks aren't called often, as they are only used to propagate user input and users are incredibly slow compared to machines."
+
+
+
+ "These make both the performance hit of creating a Box and calling it later somewhat irrelevant. "
+ "The penalty of managing an extra StoredValue through Leptos should not be overlooked though."
+
+
+
+ "Because creating boxed closures manually and storing them in is not ergonomic, "
+ "Leptonic provides an easy to use ""Callback"
" type, which is defined as"
+
+
+
+ "pub struct Callback(leptos::StoredValue R>>);"
+
+
+
+ "This callback type is both Clone and Copy and can be used at multiple locations in a component "
+ "without requiring an explicitly Clone/Copy closure to be provided by the user of the component."
+
+
+
+ "Some Leptonic components use this type for some of their callbacks when the generic approach was not ergonomic. "
+ "You can also use the Callback type in your own components props. For example:"
+
+
+
+ {indoc!(r#"
+ #[prop(into, optional)] set_value: Option>
+ "#)}
+
+
+
+ "As seen in the definition. Two generic arguments are available. Use a tuple for T if the callback should receive multiple values. "
+ "Provide the second type, otherwise defaulting to (), when the callback should return a value. "
+
+
+
+ {indoc!(r#"
+ #[prop(into, optional)] render: Option>
+ "#)}
+
+
+ "Create it when instantiating your component using the ""create_callback"
" convenience function."
+
+
+ {indoc!(r#"
+ view! {
+
+ }
+ "#)}
+
+
+
+ "Calling a callback in a component is as simple as this:"
+
+
+
+ {indoc!(r#"
+ set_value.call("foo".to_owned())
+ "#)}
+
+
+ "Passing the callback further down to children is no problem."
+
+ "There are two drawbacks though:"
+
+ "Callbacks cannot take (non-static) references, only owned values."
+ "If a child requires a callback of slightly changed signature, you have to pay for creating an intermediate callback.."
+
+ }
+}
diff --git a/src/pages/documentation/changelog.rs b/src/pages/documentation/changelog.rs
new file mode 100644
index 0000000..fe8cc79
--- /dev/null
+++ b/src/pages/documentation/changelog.rs
@@ -0,0 +1,105 @@
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageChangelog() -> impl IntoView {
+ view! {
+ "Changelog"
+
+ "0.2.0"
+
+ "Added:"
+
+
+ "Added the `Out` type. An enum abstracting over `Callback`s and `WriteSignal`s."
+ " Components can use this type when it is equally likely that a user will provide either of the previously mentioned types."
+ " In case of the WriteSignal, the user just wants a new value to be stored."
+ " In case of a callback, the user wants fine control over how a new value is handled."
+ " The input component is the first one using it, as mentioned in the `Changed` section."
+
+ "The Select, OptionalSelect and Multiselect components now accept a `class` prop with which custom classes can be attached to a rendered element."
+ "The `Kbd` component together with `KbdShortcut`, displaying keyboard keys and shortcuts."
+ "The `Chip` component now accepts custom `id`, `class` and `style` props."
+ "You can now use the new `--slider-bar-background-image` CSS variable to style the bar of a `Slider` with more control. Defaults to `none`. `--slider-bar-background-color` is still the preferred way to style the bar if no image is needed. The image property will overwrite the color."
+ "Also added `--slider-range-background-color`, `--slider-range-background-image`, `--slider-knob-border-width`, `--slider-knob-border-color`, `--slider-knob-border-style`, `--slider-knob-background-color` and `--slider-knob-halo-background-color`."
+ "The background color/image of the selection of a `Slider` can now be styled using `--slider-range-background-color`, defaulting to `var(--brand-color)`, and `--slider-range-background-image`, defaulting to `none`."
+ "Initial version of a `ColorPicker` component."
+
+
+ "Changed:"
+
+ "The DateSelector components on_change prop now takes a Callback instead of a generic function."
+ "Buttons of type `outlined` now use --button-outlined-[color]-... variables for their styling."
+ "Buttons of type `filled` now use --button-filled-[color]-... variables for their styling."
+ "Buttons of type `outlined` and color `primary` now use a dark text color, contrasting the default bright background."
+ "When using an input of type `Number`, you now have to supply optional `min`, `max` and `step` values which are propagated to the underlying input element."
+ "The Input `set` prop is now optional."
+ "The Input `set` prop is no longer generic. It now expects an `Out`, which can either be a `WriteSignal` or a custom `Callback`."
+ "The Slider and RangerSlider `set_value` props are no longer generic. They now expect an `Out`, which can either be a `WriteSignal` or a custom `Callback`."
+ "The Toggle `on_toggle` prop is now called `set_value` and is no longer generic. It now expect an `Out`, which can either be a `WriteSignal` or a custom `Callback`."
+ "The TiptapEditor `set_value` prop is no longer generic. It now expect an `Option>`, which can either be a `WriteSignal` or a custom `Callback`."
+
+ "All components using custom attributes now render them with a \"data-\" prefix, allowing them to be standard-compliant and distinguishable from well-known / standard attributes."
+ " `leptonic-theme` styling changed appropriately."
+
+ "Prop `max` of the ProgressBar is now a MaybeSignal."
+ "Prop `progress` of the ProgressBar is now a MaybeSignal."
+
+ "Prop `title` of the Alert is now a Callback instead of a generic Fn closure."
+ " Expect the now necessary `create_callback(move |()| {})` when instantiating a component with a callback prop to become a simple `move || {}` after a migration to leptos 0.5!"
+
+ "The Slider `step` prop is now optional, making continuous sliders with maximum precision easier to set up."
+ "The `Input` component was split into `TextInput`, `PasswordInput` and `NumberInput`. Their `label` prop was renamed to `placeholder`. The `InputType` enum was removed."
+ "All `Select` components now require a `search_text_provider` prop. The `SelectOption` trait no longer enforces `Display` to be implemented."
+
+
+ "Fixed:"
+
+ "A button with variants now properly respects its disabled state."
+ "A button with variants now only triggers one of its actions (either main or variant) per interaction."
+ "Buttons of type `flat` and color `info` now receive correct styling."
+ "The installation instructions now include a section describing how to enable the required web_sys_unstable_apis opt-in."
+
+
+ "0.1.0"
+
+ "Initial release."
+
+ "Added utilities:"
+
+ "Callback types"
+ "OptionalMaybeSignal type"
+ "Global event listener contexts"
+
+
+ "Added components:"
+
+ "Root component"
+ "Skeleton component and styles"
+ "Stack component and styles"
+ "Grid component and styles"
+ "Separator component and styles"
+ "Tab components and styles"
+ "Collapsible components and styles"
+ "AppBar components and styles"
+ "Drawer components and styles"
+ "Button component and styles"
+ "Input component and styles"
+ "Date selector component and styles"
+ "Slider component and styles"
+ "Select component and styles"
+ "Toggle component and styles"
+ "Alert component and styles"
+ "Toast component and styles"
+ "Modal components and styles"
+ "Progress component and styles"
+ "Popover component and styles"
+ "Chip component and styles"
+ "Icon component and styles"
+ "Link component and styles"
+ "Anchor component and styles"
+ "Typography components and styles"
+ "Transition components and styles"
+
+ }
+}
diff --git a/src/pages/documentation/chip.rs b/src/pages/documentation/chip.rs
new file mode 100644
index 0000000..3e59c6a
--- /dev/null
+++ b/src/pages/documentation/chip.rs
@@ -0,0 +1,88 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageChip() -> impl IntoView {
+ let (dismissed, set_dismissed) = create_signal(false);
+
+ view! {
+ "Chips"
+
+
+ {indoc!(r#"
+ "Primary"
+ "#)}
+
+
+ "Primary"
+ "Secondary"
+ "Success"
+ "Info"
+ "Warn"
+ "Danger"
+
+ "Dismissible chips"
+
+
+ "As chips are often used to convey mutable state, we allow chips to be dismissible. "
+ "Dismissible chips display an ""X"
" icon which lets the user dismiss the chip. "
+ "The component embedding the chip is responsible of actually removing it, e.g. not rendering it again."
+
+
+
+ {indoc!(r#"
+
+ "Dismissible"
+
+ "#)}
+
+
+ "Reveal chip"}
+ >
+
+ "Dismissible"
+
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --chip-font-size
+ --chip-margin
+ --chip-padding
+ --chip-border
+ --chip-border-radius
+ --chip-primary-text-color
+ --chip-primary-text-color-hover
+ --chip-primary-background-color
+ --chip-primary-background-color-hover
+ --chip-secondary-text-color
+ --chip-secondary-text-color-hover
+ --chip-secondary-background-color
+ --chip-secondary-background-color-hover
+ --chip-success-text-color
+ --chip-success-text-color-hover
+ --chip-success-background-color
+ --chip-success-background-color-hover
+ --chip-info-text-color
+ --chip-info-text-color-hover
+ --chip-info-background-color
+ --chip-info-background-color-hover
+ --chip-warn-text-color
+ --chip-warn-text-color-hover
+ --chip-warn-background-color
+ --chip-warn-background-color-hover
+ --chip-danger-text-color
+ --chip-danger-text-color-hover
+ --chip-danger-background-color
+ --chip-danger-background-color-hover
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/collapsible.rs b/src/pages/documentation/collapsible.rs
new file mode 100644
index 0000000..3f0df96
--- /dev/null
+++ b/src/pages/documentation/collapsible.rs
@@ -0,0 +1,24 @@
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageCollapsible() -> impl IntoView {
+ view! {
+ "Collapsibles"
+
+
+
+
+
+
+
+
+ }
+}
diff --git a/src/pages/documentation/color_picker.rs b/src/pages/documentation/color_picker.rs
new file mode 100644
index 0000000..7cf7871
--- /dev/null
+++ b/src/pages/documentation/color_picker.rs
@@ -0,0 +1,129 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageColorPicker() -> impl IntoView {
+ let (hsv, set_hsv) = create_signal(HSV::new());
+
+ let (hsv_test, set_hsv_test) = create_signal(HSV::new());
+ let hsv_test_rgb_preview = Signal::derive(move || hsv_test.get().into_rgb8());
+
+ view! {
+ "Color Picker"
+
+ "Select colors using the """
" component."
+
+
+ {indoc!(r#"
+ let (hsv, set_hsv) = create_signal(HSV::new());
+ view! {
+
+ }
+ "#)}
+
+
+
+
+ "Parts"
+
+ "The """
" build on top of a few other components build to help work with colors. You may use them directly and build your own color picker."
+
+ "Let's define a HSV color with a derived RGB representation. We will use them for the next component on this page."
+
+
+ {indoc!(r#"
+ let (hsv, set_hsv) = create_signal(HSV::new());
+ let rgb = Signal::derive(move || hsv.get().into_rgb8());
+ "#)}
+
+
+ ColorPreview
+
+ "The """
" component simply displays a reactive color patch based on the given RGB color signal."
+
+
+ {indoc!(r#"
+ view! {
+
+ }
+ "#)}
+
+
+
+
+ ColorPalette
+
+
+ "The """
" component works on an HSV color signal, "
+ "displays the color-gradient field for any given hue value and allows selecting new values for "
+ "saturation (S, x-axis) and value (V, y-axis) of the HSV color by dragging a handle on the displayed surface."
+
+
+
+ {indoc!(r#"
+ view! {
+
+ }
+ "#)}
+
+
+
+
+ HueSlider
+
+
+ "The """
" component renders a specialized """
", "
+ "allowing you to pick a hue, a floating-point value between 0° and 360°. "
+ "The slider background displays the hue range as a color band, the knob displays the currently selected hue value at maximum saturation and value."
+
+
+
+ {indoc!(r#"
+ view! {
+
+ }
+ "#)}
+
+
+
+
+ "If you look at the source of Leptonic's , you will see that there is not much more to it as what you saw here!"
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --color-palette-knob-size
+ --color-palette-knob-border-width
+ --color-palette-knob-border-color
+ --color-palette-knob-border-style
+ --color-palette-knob-background-color
+ --color-palette-knob-halo-size
+ --color-palette-knob-halo-size-while-dragged
+ --color-palette-knob-halo-opacity
+ --color-palette-knob-halo-background-color
+ --color-palette-knob-transition-speed
+ --color-palette-knob-box-shadow
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/date_time.rs b/src/pages/documentation/date_time.rs
new file mode 100644
index 0000000..d004311
--- /dev/null
+++ b/src/pages/documentation/date_time.rs
@@ -0,0 +1,35 @@
+use indoc::indoc;
+use leptonic::{datetime::GuideMode, prelude::*};
+use leptos::*;
+use time::OffsetDateTime;
+
+#[component]
+pub fn PageDateTime() -> impl IntoView {
+ view! {
+ "Date & Time"
+
+ "Select dates using the calendar-like """
" component."
+
+
+ {indoc!(r#"
+
+ "#)}
+
+
+
+
+ "The date selector can also start with the year selection."
+
+
+ {indoc!(r#"
+
+ "#)}
+
+
+
+
+ "Input fields"
+
+ "Selecting dates and times through input fields will be supported soon. Stay tuned."
+ }
+}
diff --git a/src/pages/documentation/doc_root.rs b/src/pages/documentation/doc_root.rs
new file mode 100644
index 0000000..066b5bd
--- /dev/null
+++ b/src/pages/documentation/doc_root.rs
@@ -0,0 +1,365 @@
+use std::fmt::Display;
+
+use leptonic::prelude::*;
+use leptos::*;
+use leptos_icons::BsIcon;
+use leptos_router::*;
+
+use crate::app::{AppLayoutContext, AppRoutes};
+use crate::pages::documentation::alert::PageAlert;
+use crate::pages::documentation::anchor::PageAnchor;
+use crate::pages::documentation::app_bar::PageAppBar;
+use crate::pages::documentation::button::PageButton;
+use crate::pages::documentation::callback::PageCallback;
+use crate::pages::documentation::changelog::PageChangelog;
+use crate::pages::documentation::chip::PageChip;
+use crate::pages::documentation::collapsible::PageCollapsible;
+use crate::pages::documentation::color_picker::PageColorPicker;
+use crate::pages::documentation::date_time::PageDateTime;
+use crate::pages::documentation::drawer::PageDrawer;
+use crate::pages::documentation::grid::PageGrid;
+use crate::pages::documentation::icon::PageIcon;
+use crate::pages::documentation::input::PageInput;
+use crate::pages::documentation::installation::PageInstallation;
+use crate::pages::documentation::kbd::PageKbd;
+use crate::pages::documentation::link::PageLink;
+use crate::pages::documentation::modal::PageModal;
+use crate::pages::documentation::overview::PageOverview;
+use crate::pages::documentation::popover::PagePopover;
+use crate::pages::documentation::progress::PageProgress;
+use crate::pages::documentation::select::PageSelect;
+use crate::pages::documentation::separator::PageSeparator;
+use crate::pages::documentation::skeleton::PageSkeleton;
+use crate::pages::documentation::slider::PageSlider;
+use crate::pages::documentation::stack::PageStack;
+use crate::pages::documentation::tab::PageTab;
+use crate::pages::documentation::table::PageTable;
+use crate::pages::documentation::themes::PageThemes;
+use crate::pages::documentation::tiptap_editor::PageTiptapEditor;
+use crate::pages::documentation::toast::PageToast;
+use crate::pages::documentation::toggle::PageToggle;
+use crate::pages::documentation::transition::PageTransition;
+use crate::pages::documentation::typography::PageTypography;
+use crate::pages::documentation::usage::PageUsage;
+use crate::APP_BAR_HEIGHT;
+
+#[derive(Debug, Copy, Clone)]
+pub enum DocRoutes {
+ // Getting started
+ Overview,
+ Installation,
+ Usage,
+ Themes,
+ Changelog,
+
+ // Layout
+ Stack,
+ Grid,
+ Separator,
+ Skeleton,
+ AppBar,
+ Drawer,
+ Tab,
+ Table,
+ Collapsible,
+
+ // Input
+ Button,
+ Input,
+ TiptapEditor,
+ DateTime,
+ Slider,
+ Select,
+ Toggle,
+ ColorPicker,
+
+ // Feedback
+ Alert,
+ Toast,
+ Modal,
+ Progress,
+ Popover,
+ Chip,
+ Kbd,
+
+ // General
+ Typography,
+ Icon,
+ Link,
+ Anchor,
+ Callback,
+
+ // Animation
+ Transition,
+
+ // Technical
+ NotFound,
+}
+
+impl DocRoutes {
+ pub fn route(self) -> &'static str {
+ match self {
+ DocRoutes::Overview => "overview",
+ DocRoutes::Installation => "installation",
+ DocRoutes::Usage => "usage",
+ DocRoutes::Themes => "themes",
+ DocRoutes::Changelog => "changelog",
+
+ DocRoutes::Stack => "stack",
+ DocRoutes::Grid => "grid",
+ DocRoutes::Separator => "separator",
+ DocRoutes::Skeleton => "skeleton",
+ DocRoutes::AppBar => "app-bar",
+ DocRoutes::Drawer => "drawer",
+ DocRoutes::Tab => "tabs",
+ DocRoutes::Table => "table",
+ DocRoutes::Collapsible => "collapsible",
+
+ DocRoutes::Button => "button",
+ DocRoutes::Input => "input",
+ DocRoutes::TiptapEditor => "tiptap-editor",
+ DocRoutes::DateTime => "date-time",
+ DocRoutes::Slider => "slider",
+ DocRoutes::Select => "select",
+ DocRoutes::Toggle => "toggle",
+ DocRoutes::ColorPicker => "color-picker",
+
+ DocRoutes::Alert => "alert",
+ DocRoutes::Toast => "toast",
+ DocRoutes::Modal => "modal",
+ DocRoutes::Progress => "progress",
+ DocRoutes::Popover => "popover",
+ DocRoutes::Chip => "chip",
+ DocRoutes::Kbd => "kbd",
+
+ DocRoutes::Typography => "typography",
+ DocRoutes::Icon => "icon",
+ DocRoutes::Link => "link",
+ DocRoutes::Anchor => "anchor",
+ DocRoutes::Callback => "callback",
+
+ DocRoutes::Transition => "transition",
+ DocRoutes::NotFound => "*", // Leptos requires this to be be named "*"!
+ }
+ }
+}
+
+/// Required so that `Routes` variants can be used in `` definitions.
+impl Display for DocRoutes {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(self.route())
+ }
+}
+
+/// Required so that `Routes` variants can be used in ` ` definitions.
+impl ToHref for DocRoutes {
+ fn to_href(&self) -> Box String + '_> {
+ Box::new(move || format!("/{}/{}", AppRoutes::Doc.route(), self.route()))
+ }
+}
+
+// You can define other routes in their own component.
+// Use a #[component(transparent)] that returns a .
+#[component(transparent)]
+pub fn DocRoutes(path: P) -> impl IntoView
+where
+ P: std::fmt::Display,
+{
+ view! {
+ }>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+
+ }/>
+
+ }/>
+
+ }
+}
+
+#[component]
+pub fn DocLayout() -> impl IntoView {
+ let app_layout_context = expect_context::();
+
+ let drawer_class = move || match app_layout_context.is_small.get() {
+ true => "mobile",
+ false => "",
+ };
+
+ let close_doc_drawer_on_mobile = move || {
+ if app_layout_context.is_small.get_untracked() {
+ app_layout_context.close_doc_drawer();
+ }
+ };
+
+ let drawer_content = view! {
+ "Getting started"
+ }>
+
+ "Overview"
+ "Installation"
+ "Usage"
+ "Themes"
+ "Changelog"
+
+
+
+ "Layout"
+ }>
+
+ "Stack"
+ "Grid"
+ "Separator"
+ "Skeleton"
+ "App Bar"
+ "Drawer"
+ "Tabs"
+
+ "Table"
+
+
+ "Collapsible"
+
+
+
+ "Input"
+ }>
+
+ "Button"
+ "Input"
+ "Tiptap editor"
+ "Date & Time"
+ "Slider"
+ "Select"
+ "Toggle"
+
+ "Color Picker"
+
+
+
+
+
+ "Feedback"
+ }>
+
+ "Alert"
+ "Toast"
+ "Modal"
+ "Progress"
+ "Popover"
+ "Chip"
+
+ "Kbd"
+
+
+
+
+
+ "General"
+ }>
+
+ "Typography"
+ "Icon"
+ "Link"
+ "Anchor"
+ "Callback"
+
+
+
+ "Animation"
+ }>
+
+ "Transitions"
+
+
+ };
+
+ view! {
+
+
+
+
+
+
+ // will show nested child routes.
+
+
+
+ }
+}
+
+#[component]
+pub fn DrawerSection(header: H, children: Children) -> impl IntoView
+where
+ H: Fn() -> IV + 'static,
+ IV: IntoView + 'static,
+{
+ view! {
+
+
+ { children() }
+
+ }
+}
+
+#[component]
+pub fn New() -> impl IntoView {
+ view! {
+
+ "NEW"
+
+ }
+}
diff --git a/src/pages/documentation/drawer.rs b/src/pages/documentation/drawer.rs
new file mode 100644
index 0000000..09e9a7c
--- /dev/null
+++ b/src/pages/documentation/drawer.rs
@@ -0,0 +1,105 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageDrawer() -> impl IntoView {
+ let (shown, set_shown) = create_signal(true);
+ let (shown2, set_shown2) = create_signal(true);
+
+ view! {
+ "Drawer"
+
+
+ "The """
" component is intended to be used as a side menu. It is animated to conditionally move in and out of visibility."
+ "The required ""side"
" prop controls to which side the drawer should move when hiding."
+
+
+
+
+
+
+
+ {(0..8).map(|_| view! { }).collect_view()}
+
+
+
+ "Scroll ↓"
+
+ {(0..8).map(|_| view! { }).collect_view()}
+
+
+
+
+
+ {indoc!(r#"
+
+
+
+ {(0..8).map(|_| view! { }).collect_view()}
+
+
+
+ "Scroll ↓"
+
+ {(0..8).map(|_| view! { }).collect_view()}
+
+
+
+ "#)}
+
+
+ "Layout shifts"
+
+
+ "To avoid layout shifts, you may declare the drawer as absolutely positioned to let it overlay your content when shown. "
+ "This is especially useful when the menu is only animated on user action on small / mobile screens and fills the whole width of the viewport when shown. "
+ "When viewing this documentation on a small device, the open- and closeable main and documentation menus are created this way."
+
+
+
+
+
+
+ "Scroll ↓"
+
+ {(0..8).map(|_| view! { }).collect_view()}
+
+
+
+
+ {(0..8).map(|_| view! { }).collect_view()}
+
+
+
+
+
+ {indoc!(r#"
+
+
+ "Scroll ↓"
+
+ {(0..8).map(|_| view! { }).collect_view()}
+
+
+
+
+ {(0..8).map(|_| view! { }).collect_view()}
+
+
+
+ "#)}
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --drawer-background-color
+ --drawer-box-shadow
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/grid.rs b/src/pages/documentation/grid.rs
new file mode 100644
index 0000000..06d38bb
--- /dev/null
+++ b/src/pages/documentation/grid.rs
@@ -0,0 +1,64 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageGrid() -> impl IntoView {
+ view! {
+ "Grid"
+
+
+ {indoc!(r#"
+
+
+
+ "Item 1"
+
+
+ "Item 2"
+
+
+ "Item 3"
+
+
+ "Item 4"
+
+
+
+
+ "Item 5"
+
+
+ "Item 6"
+
+
+
+ "#)}
+
+
+
+
+
+ "Item 1"
+
+
+ "Item 2"
+
+
+ "Item 3"
+
+
+ "Item 4"
+
+
+
+
+ "Item 5"
+
+
+ "Item 6"
+
+
+
+ }
+}
diff --git a/src/pages/documentation/icon.rs b/src/pages/documentation/icon.rs
new file mode 100644
index 0000000..c0ce207
--- /dev/null
+++ b/src/pages/documentation/icon.rs
@@ -0,0 +1,48 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+use leptos_icons::BsIcon;
+
+#[component]
+pub fn PageIcon() -> impl IntoView {
+ view! {
+ "Icons"
+
+ "Icons are supported through the ""https://github.com/Carlosted/leptos-icons" " crate."
+ "Feel free to check out the associated icon index site at ""https://carlosted.github.io/icondata/" "."
+
+ "With the dependency set up, adding our required icons as features."
+
+
+ {indoc!(r#"
+ leptos_icons = { version = "0.0.15", features = [
+ "BsFolderFill",
+ "BsFolder",
+ ] }
+ "#)}
+
+
+ "We can simply include these icons like this, using the """
" component provided by this library."
+
+
+ {indoc!(r#"
+
+
+ "#)}
+
+
+
+
+
+ "Styling"
+
+ "There are currently no CSS variables exposed, targeting the """
" component."
+
+ "Notes:"
+
+ "Scale icons by setting the css ""font-size"
" attribute."
+ "Color icons by setting the css ""color"
" attribute."
+ "Some icons may also render different when altering the css ""background-color"
" attribute."
+
+ }
+}
diff --git a/src/pages/documentation/input.rs b/src/pages/documentation/input.rs
new file mode 100644
index 0000000..8d47778
--- /dev/null
+++ b/src/pages/documentation/input.rs
@@ -0,0 +1,146 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageInput() -> impl IntoView {
+ let (text, set_text) = create_signal("text".to_owned());
+ let (password, set_password) = create_signal("secret".to_owned());
+ let (number, set_number) = create_signal(4.2);
+ let number_string = Signal::derive(move || format!("{:.1}", number.get()));
+
+ let (placeholder_input, set_placeholder_input) = create_signal("".to_owned());
+
+ view! {
+ "Inputs"
+
+ "Creating an input is as simple as doing the following"
+
+ {indoc!(r#"
+ let (text, set_text) = create_signal("text".to_owned());
+ view! {
+
+ }
+ "#)}
+
+
+
+ "Text is: " {move || text.get()}
+
+ "Types"
+
+
+ "You can use the ""InputType"
" enum, to either create a "
+ "Text"
+ ", ""Password"
+ " or ""Number"
+ " input, ""Text"
" being the default type when unspecified."
+
+
+
+ {indoc!(r#"
+ let (password, set_password) = create_signal("secret".to_owned());
+ view! {
+
+ }
+ "#)}
+
+
+
+ "Password is: " {move || password.get()}
+
+
+ "Note that the input is always given back to you as a ""String"
" no matter the type."
+ " When using a number input, you may want to convert the String like this: "
+ "str::parse::(v.as_str()).ok()"
" to receive an ""Option"
+ ", being ""None"
" on invalid input."
+
+
+
+ {indoc!(r#"
+ let (number, set_number) = create_signal(Some(42.0));
+ let number_string = Signal::derive(move || format!("{:.1}", number.get()));
+ view! {
+
+ }
+ "#)}
+
+
+
+ "Number is: " {move || number_string.get()}
+
+ "Value updates"
+
+
+ "An input can be used without providing the ""set"
" prop. "
+ "You will not be notified about changes to the input value. "
+ "This can be useful when you know that the input will always be ""disabled"
" and you never expect changes."
+
+
+
+ "The ""set"
" prop, providing you with new values whenever the inputs content changes"
+ ", accepts an ""Out"
+ ", allowing you to either provide a ""WriteSignal"
" whose value is set when the input changes"
+ " or a custom ""Callback"
" called whenever the input changes"
+ ", allowing you to handle values by yourself if required."
+
+
+
+ "You can define the ""set"
" prop in one of the following ways."
+
+
+
+ {indoc!(r#"
+ view! {
+
+
+ }
+ "#)}
+
+
+ "Placeholder"
+
+ "You can supply a placeholder to the input. It is shown as when the input is empty."
+
+
+ {indoc!(r#"
+ let (text, set_text) = create_signal("".to_owned());
+ view! {
+
+
+ "Clear input"
+
+ }
+ "#)}
+
+
+
+ "Clear input"
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --input-padding
+ --input-color
+ --input-background-color
+ --input-border:
+ --input-border-bottom
+ --input-border-radius
+ --input-min-height
+ --input-focused-border-color
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/installation.rs b/src/pages/documentation/installation.rs
new file mode 100644
index 0000000..08899a5
--- /dev/null
+++ b/src/pages/documentation/installation.rs
@@ -0,0 +1,165 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+use crate::pages::documentation::doc_root::DocRoutes;
+
+#[component]
+pub fn PageInstallation() -> impl IntoView {
+ view! {
+
+ "Installation"
+
+
+
+
+ "We assume that you already have an app depending on ""leptos"
" in version ""0.4.3"
" or higher."
+
+
+
+ "Start by adding ""leptonic"
", ""leptonic-theme"
" and ""leptos-tiptap-build"
" as dependencies of your app. "
+ "The later ones are ""[build-dependencies]"
" as they will only be used in a ""build.rs"
" script which we define later."
+
+
+
+ {indoc!(r#"
+ cargo add leptonic
+ cargo add --build leptonic-theme
+ cargo add --build leptos-tiptap-build
+ "#)}
+
+
+
+ "Leptonic comes with default styling in form of the ""leptonic-theme"
" crate. "
+ "In order to build your app with these styles, a build script is required. "
+
+
+ "Let's create our ""build.rs"
" file, generating our theme and copying required JS files."
+
+
+ {indoc!(r#"
+ use std::io::Write;
+
+ pub fn main() {
+ println!("cargo:rerun-if-changed=build.rs");
+ println!("cargo:rerun-if-changed=Cargo.lock");
+
+ let root_dir: std::path::PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into();
+ let generated_dir = root_dir.join("generated");
+ let js_dir = generated_dir.join("js");
+
+ leptonic_theme::generate(generated_dir.join("leptonic"));
+ println!("cargo:warning=theme written");
+
+ std::fs::create_dir_all(js_dir.clone()).unwrap();
+ println!("cargo:warning=js dir created");
+
+ std::fs::File::create(js_dir.join("tiptap-bundle.min.js"))
+ .unwrap()
+ .write_all(leptos_tiptap_build::TIPTAP_BUNDLE_MIN_JS.as_bytes())
+ .unwrap();
+ println!("cargo:warning=tiptap-bundle.min.js written");
+
+ std::fs::File::create(js_dir.join("tiptap.js"))
+ .unwrap()
+ .write_all(leptos_tiptap_build::TIPTAP_JS.as_bytes())
+ .unwrap();
+ println!("cargo:warning=tiptap.js written");
+ }
+ "#)}
+
+
+
+ "Currently, Leptonic focuses on integration with client-side-rendering and building with Trunk. "
+ "Let's set up a custom ""Trunk.toml"
" file:"
+
+
+
+ "The ""[watch]"
" section is used to ignore changes in the \"./generated\" directory (which our build script writes to). When omitted, Trunk would recompile our app in an endless loop."
+
+
+
+ {indoc!(r#"
+ [watch]
+ # Paths to watch. The `build.target`'s parent folder is watched by default.
+ ignore = [
+ # These files are generated from our build.rs script, not excluding them would result in an endless restart-cycle!
+ # Keep this list in sync with what the build script generates.
+ "./generated",
+ ]
+
+ [serve]
+ address = "127.0.0.1"
+ port = 4001
+ open = false
+ "#)}
+
+
+ "The styling of our app must include the leptonic themes. Let's ensure that by adding the following line to our ""scss/style.scss"
" file. This is the default location for a Trunk-based project. Create the file if you do not have it already."
+
+
+ {indoc!(r#"
+ @import "../generated/leptonic/leptonic-themes";
+ "#)}
+
+
+ "You can overwrite or add styles for a particular theme using a ""[data-theme=\"...\"]"
" selector like so:"
+
+
+ {indoc!(r#"
+ [data-theme="light"] {
+ --brand-color: #e66956;
+ }
+ "#)}
+
+
+ "Make sure that you are using a reasonable index.html file like the following. This should work out of the box when you followed the previous instructions."
+
+
+ {indoc!(r##"
+
+
+
+
+
+
+
+
+
+
+ Leptonic
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ "##)}
+
+
+
+ "Leptonic depends on the ""leptos-use"
" crate. Some of the features used require an opt-in."
+ " In order for your app to compile properly, add a folder named "".cargo"
" besides your ""Cargo.toml"
" file."
+ " Place a ""config.toml"
" file inside it containing the following content:"
+
+
+
+ {indoc!(r#"
+ [build]
+ # `leptonic` depends on some `leptos-use` functions requiring this opt-in. This may change in the future.
+ rustflags = ["--cfg=web_sys_unstable_apis"]
+ "#)}
+
+
+ "You should now be ready to use leptonic components in your leptos app. Continue reading the " "Usage"" section, to see how to use your first component."
+ }
+}
diff --git a/src/pages/documentation/kbd.rs b/src/pages/documentation/kbd.rs
new file mode 100644
index 0000000..56427a7
--- /dev/null
+++ b/src/pages/documentation/kbd.rs
@@ -0,0 +1,106 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+use strum::IntoEnumIterator;
+
+#[component]
+pub fn PageKbd() -> impl IntoView {
+ let all_keys = Key::iter();
+
+ view! {
+ "Keyboard"
+
+
+ "Display labeled keyboard key-caps using the """
" component."
+ " Leptonic provides the ""Key"
" enum which provides well-known keys and their display properties."
+
+
+
+ {indoc!(r#"
+
+ "#)}
+
+
+
+
+ "Shortcuts"
+
+
+ "You may use this component to display a hint to a keyboard shortcut your users can use to interact with your app."
+ " As shortcuts mostly consist of two or more keys, Leptonic also provide the """
+ " component to make this task as easy as possible. Simply provide the keys which must be pressed in order to activate the shortcut."
+
+
+ "Note that these component do not listen for key-presses. Their sole purpose is to unify rendering of key caps and shortcuts!"
+
+
+ {indoc!(r#"
+
+ "#)}
+
+
+
+
+ "This could also be rendered manually using the following markup."
+
+
+ {indoc!(r#"
+
+
+
+
+
+ "#)}
+
+
+
+
+
+
+
+
+ Keys
+
+ "Here is a list of all keys provided by the ""Key"
" enum."
+
+ {
+ all_keys
+ .into_iter()
+ .filter(|key| std::mem::discriminant(key) != std::mem::discriminant(&Key::Custom("")))
+ .map(|key: Key| view! {
+
+ })
+ .collect_view()
+ }
+
+ "If you need custom content in a """
" element, use the ""Key::Custom(String)"
" variant."
+
+
+ {indoc!(r#"
+
+ "#)}
+
+
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --leptonic-kbd-key-color
+ --leptonic-kbd-key-background-color
+ --leptonic-kbd-key-margin
+ --leptonic-kbd-key-padding
+ --leptonic-kbd-key-border-radius
+ --leptonic-kbd-key-border-color
+ --leptonic-kbd-concatenate-color
+ --leptonic-kbd-concatenate-background-color
+ --leptonic-kbd-concatenate-margin
+ --leptonic-kbd-concatenate-padding
+ --leptonic-kbd-concatenate-border-radius
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/link.rs b/src/pages/documentation/link.rs
new file mode 100644
index 0000000..0333baf
--- /dev/null
+++ b/src/pages/documentation/link.rs
@@ -0,0 +1,76 @@
+use indoc::{formatdoc, indoc};
+use leptonic::prelude::*;
+use leptos::*;
+use leptos_icons::BsIcon;
+use leptos_router::ToHref;
+
+use crate::pages::documentation::doc_root::DocRoutes;
+
+#[component]
+pub fn PageLink() -> impl IntoView {
+ view! {
+ "Links"
+
+ "Links bring your users to a different place of your application."
+
+ "Internal links"
+
+ "These links, created with the "" "
" component, use the leptos router under the hood and are meant to direct users to a different location inside your app, as the given ""href"
" prop is always considered to be relative to your site."
+
+
+ {formatdoc!(r#"
+
+ "This is a link to the current route."
+
+ "#, DocRoutes::Link.to_href()())}
+
+
+ "This is a link to the current route."
+
+ "External links"
+
+ "These links, created with the """
" component, do not use the leptos router and must be used when directing users to external sources."
+
+
+ {indoc!(r#"
+
+
+
+ "#)}
+
+
+
+
+
+
+ "Link Buttons"
+
+ "It is likely that you want to render a link in the form of a button. Please respect the HTML standard and do not render a inside a ."
+
+ "Use the """
" component, which accepts most props from both the as well as the component."
+
+ "The \"Read the docs\" button on the welcome page was implemented this way!"
+
+
+ {indoc!(r#"
+
+ "Read the docs"
+
+ "#)}
+
+
+
+ "Read the docs"
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --link-color
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/mod.rs b/src/pages/documentation/mod.rs
new file mode 100644
index 0000000..bcc5340
--- /dev/null
+++ b/src/pages/documentation/mod.rs
@@ -0,0 +1,36 @@
+pub mod alert;
+pub mod anchor;
+pub mod app_bar;
+pub mod button;
+pub mod callback;
+pub mod changelog;
+pub mod chip;
+pub mod collapsible;
+pub mod color_picker;
+pub mod date_time;
+pub mod doc_root;
+pub mod drawer;
+pub mod grid;
+pub mod icon;
+pub mod input;
+pub mod installation;
+pub mod kbd;
+pub mod link;
+pub mod modal;
+pub mod overview;
+pub mod popover;
+pub mod progress;
+pub mod select;
+pub mod separator;
+pub mod skeleton;
+pub mod slider;
+pub mod stack;
+pub mod tab;
+pub mod table;
+pub mod themes;
+pub mod tiptap_editor;
+pub mod toast;
+pub mod toggle;
+pub mod transition;
+pub mod typography;
+pub mod usage;
diff --git a/src/pages/documentation/modal.rs b/src/pages/documentation/modal.rs
new file mode 100644
index 0000000..86d673e
--- /dev/null
+++ b/src/pages/documentation/modal.rs
@@ -0,0 +1,97 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageModal() -> impl IntoView {
+ let (show_modal, set_show_modal) = create_signal(false);
+ let (show_modal2, set_show_modal2) = create_signal(false);
+ let (show_confirm_modal, set_show_input_modal) = create_signal(false);
+
+ view! {
+ "Modals"
+
+ "Show staged modal"
+ "Show confirmation modal"
+
+
+ "Sure?"
+ "This ia a test modal."
+
+
+ "Accept"
+ "Next"
+ "Cancel"
+
+
+
+
+
+ "Next one"
+ "This overlays..."
+
+
+ "Back"
+
+
+
+
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --modal-color
+ --modal-background-color
+ --modal-padding
+ --modal-font-size
+ --modal-header-padding
+ --modal-body-padding
+ --modal-footer-padding
+ --modal-border-radius
+ --modal-box-shadow
+ "#)}
+
+ }
+}
+
+#[component]
+pub fn ConfirmModal(
+ #[prop(into)] show_when: Signal,
+ requires_confirmation_of: String,
+ on_accept: A,
+ on_cancel: C,
+) -> impl IntoView
+where
+ A: Fn() + Copy + 'static,
+ C: Fn() + Copy + 'static,
+{
+ let (input, set_input) = create_signal("".to_owned());
+ let required = requires_confirmation_of.clone();
+ let confirmed = Signal::derive(move || input.get() == required);
+ let disabled: OptionalMaybeSignal = Signal::derive(move || !confirmed.get()).into();
+
+ view! {
+
+ "Delete repository?"
+
+ "Please enter \""{requires_confirmation_of}"\" to confirm."
+
+
+
+
+ "Accept"
+ "Cancel"
+
+
+
+ }
+}
diff --git a/src/pages/documentation/overview.rs b/src/pages/documentation/overview.rs
new file mode 100644
index 0000000..14b3277
--- /dev/null
+++ b/src/pages/documentation/overview.rs
@@ -0,0 +1,28 @@
+use leptonic::prelude::*;
+use leptos::*;
+
+use crate::pages::documentation::doc_root::DocRoutes;
+
+#[component]
+pub fn PageOverview() -> impl IntoView {
+ view! {
+ "Overview"
+
+
+ "Nigig Limited is a consulatncy for the ""Civil and Structural Engineering" " Projects."
+
+
+
+ "It provides \"ready to be used\" components for capturing user input through buttons, inputs fields, select inputs, sliders, date & time inputs or even a rich text editor. "
+ "Well known mechanisms for providing user-feedback are also available, ranging from modal windows, toast messages and alerts to progress indicators. "
+ "Leptonic also includes components helping you lay out all these elements on your pages. These include stacks, a full grid system, tabs and collapsibles as well as components for app bars and side drawers. "
+ "Common tasks such as linking to other parts in or outside your site or including a plethora of icons are also made simple. "
+
+
+ "Explore the available components and other features using the side menu to get acquainted with what Leptonic has to offer."
+
+
+ "If you want to dive right in, follow our " "Installation" " instructions."
+
+ }
+}
diff --git a/src/pages/documentation/popover.rs b/src/pages/documentation/popover.rs
new file mode 100644
index 0000000..75a2bbd
--- /dev/null
+++ b/src/pages/documentation/popover.rs
@@ -0,0 +1,53 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+use leptos_use::use_element_hover;
+
+#[component]
+pub fn PagePopover() -> impl IntoView {
+ let el = create_node_ref();
+ let is_hovered = use_element_hover(el);
+
+ view! {
+ "Popover"
+
+
+ "Floating information which can be shown or hidden using a signal."
+
+
+
+ {indoc!(r#"
+ let el = create_node_ref();
+ let is_hovered = use_element_hover(el);
+
+ view! {
+
+
+ "1"
+
+ "Hover me!"
+
+ }
+ "#)}
+
+
+
+
+
+ "1"
+
+ "Hover me!"
+
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --popover-content-background-color
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/progress.rs b/src/pages/documentation/progress.rs
new file mode 100644
index 0000000..4c5a1e0
--- /dev/null
+++ b/src/pages/documentation/progress.rs
@@ -0,0 +1,76 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageProgress() -> impl IntoView {
+ let (progress, set_progress) = create_signal(Some(34.0));
+
+ view! {
+ "Progress"
+
+
+ "Display how much work of an operation is already completed using the """
" component."
+
+
+
+ {indoc!(r#"
+ let (progress, set_progress) = create_signal(Some(34.0));
+
+ view! {
+
+ }
+ "#)}
+
+
+
+
+
+
+
+
+ "Indeterminate state"
+
+
+ "As you have probably spotted in the above example, progress is stored as ""Option"
". "
+ "In our earlier example, we always had ""Some(progress)"
" which the progress bar displayed for us. "
+ "Whenever the signal stores a ""None"
" value, the progress bar is in the ""indeterminate"
" state, "
+ "telling the user that something is going on, but we cannot exactly say how much of the total work already completed."
+
+
+
+ {indoc!(r#"
+
+ "#)}
+
+
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --progress-bar-height
+ --progress-bar-border-radius
+ --progress-bar-background-color
+ --progress-bar-background-color-transparent
+ --progress-bar-background-box-shadow
+ --progress-bar-fill-background-color
+ --progress-bar-fill-transition
+ --progress-bar-color
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/select.rs b/src/pages/documentation/select.rs
new file mode 100644
index 0000000..cf6014a
--- /dev/null
+++ b/src/pages/documentation/select.rs
@@ -0,0 +1,278 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+enum Foo {
+ A,
+ B,
+ C,
+}
+
+impl std::fmt::Display for Foo {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Foo::A => f.write_str("A"),
+ Foo::B => f.write_str("B"),
+ Foo::C => f.write_str("C"),
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct User {
+ name: String,
+ value: ordered_float::OrderedFloat,
+}
+
+impl std::fmt::Display for User {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_fmt(format_args!("{} - {}", &self.name, &self.value))
+ }
+}
+
+#[component]
+pub fn PageSelect() -> impl IntoView {
+ let (selected, set_selected) = create_signal(Foo::A);
+ let (selected_opt, set_selected_opt) = create_signal(Option::::None);
+ let (selected_multi, set_selected_multi) = create_signal(vec![Foo::A, Foo::B]);
+ let (selected_multi2, set_selected_multi2) = create_signal(vec![Foo::A]);
+
+ let selectable_users = vec![
+ User {
+ name: "Tom".to_owned(),
+ value: ordered_float::OrderedFloat(1.0),
+ },
+ User {
+ name: "Bob".to_owned(),
+ value: ordered_float::OrderedFloat(42.0),
+ },
+ ];
+
+ let (selected_user, set_selected_user) = create_signal(selectable_users[0].clone());
+
+ view! {
+ "Selects"
+
+ "Select inputs allow you to choose between different predefined values."
+
+ "Lets assume this type definition, providing us with a set of values to choose from."
+
+
+ {indoc!(r#"
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ enum Foo {
+ A,
+ B,
+ C,
+ }
+ "#)}
+
+
+ "Variants"
+
+ "There are three variants of the select component, accepting different inputs and changing only slightly in its behavior."
+
+ "Select"
+
+ "The simplest form, requiring a selected option to be present the whole time."
+
+
+ {indoc!(r#"
+ let (selected, set_selected) = create_signal(Foo::A);
+
+ view! {
+
+ }
+ "#)}
+
+
+
+
+ "OptionalSelect"
+
+ "As the name implies, this variant stores its chosen value in an " "Option"
", allowing the select to be initialized without a value and optionally allowing the user to deselect the current value."
+
+
+ {indoc!(r#"
+ let (selected_opt, set_selected_opt) = create_signal(Option::::None);
+
+ view! {
+
+ }
+ "#)}
+
+
+
+
+ "Multiselect"
+
+ "In its simplest form, the Select component can be created with a static list of options to choose from."
+
+
+ {indoc!(r#"
+ let (selected_multi, set_selected_multi) = create_signal(vec![Foo::A, Foo::B]);
+
+ view! {
+
+ }
+ "#)}
+
+
+
+
+ "Using the ""max"
" prop, a maximum number of selectable elements can be specified. Here: 2"
+
+
+
+ "Keyboard navigation"
+
+
+ "The select component was designed with keyboard navigation in mind. "
+ "Press ""Tab"
" to jump to the next or ""Shift + Tab"
" to jump to the previous select. "
+ "Open the dropdown using ""Enter"
". "
+ "Preselect an available option using the ""ArrowDown"
" and ""ArrowUp"
" keys. "
+ "When the dropdown is not open, starting to preselect an element using the arrow keys will open it. "
+ "Choose an option by pressing ""Enter"
". "
+ "Close the dropdown by pressing ""Escape"
"."
+
+
+
+ "Select options can be searched. When opening the dropdown of available options, focus will automatically jump to the search input, allowing you to type instantly. "
+ "When closing the dropdown, focus is automatically restored to the select, allowing you to ""Tab"
" to the next element."
+
+
+ "Customization"
+
+ "Let's define a select component which allows selection from a list of struct values."
+
+
+ {indoc!(r#"
+ #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ struct User {
+ name: String,
+ value: ordered_float::OrderedFloat,
+ }
+
+ impl std::fmt::Display for User {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_fmt(format_args!("{} - {}", &self.name, &self.value))
+ }
+ }
+
+ let selectable_users = vec![
+ User {
+ name: "Tom".to_owned(),
+ value: ordered_float::OrderedFloat(1.0),
+ },
+ User {
+ name: "Bob".to_owned(),
+ value: ordered_float::OrderedFloat(42.0),
+ },
+ ];
+
+ let (selected_user, set_selected_user) = create_signal(selectable_users[0].clone());
+
+ view! {
+ "Selected user is: " { move || selected_user.get().to_string() }
+
+ }
+ "#)}
+
+
+ "Selected user is: " { move || selected_user.get().to_string() }
+
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --select-padding
+ --select-min-height
+ --select-selected-color
+ --select-selected-background-color
+ --select-selected-border
+ --select-selected-border-bottom
+ --select-selected-border-radius
+ --select-selected-badge-color
+ --select-selected-badge-background-color
+ --select-selected-placeholder-color
+ --select-focused-border-color
+ --select-dropdown-background-color
+ --select-dropdown-shadow
+ --select-search-color
+ --select-search-background-color
+ --select-no-items-color
+ --select-no-items-background-color
+ --select-item-color
+ --select-item-background-color
+ --select-item-padding
+ --select-item-disabled-background-color
+ --select-item-disabled-color
+ --select-item-preselected-background-color
+ --select-item-hover-background-color
+ --select-item-selected-background-color
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/separator.rs b/src/pages/documentation/separator.rs
new file mode 100644
index 0000000..31546ab
--- /dev/null
+++ b/src/pages/documentation/separator.rs
@@ -0,0 +1,18 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageSeparator() -> impl IntoView {
+ view! {
+ "Separators"
+
+
+ {indoc!(r#"
+
+ "#)}
+
+
+
+ }
+}
diff --git a/src/pages/documentation/skeleton.rs b/src/pages/documentation/skeleton.rs
new file mode 100644
index 0000000..8e024e5
--- /dev/null
+++ b/src/pages/documentation/skeleton.rs
@@ -0,0 +1,91 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageSkeleton() -> impl IntoView {
+ view! {
+ "Skeletons"
+
+
+ "A skeleton is a placeholder element of a specific shape and size which can be displayed in place of some actual content, "
+ "whenever, for example, this content cannot be displayed because required data is still being fetched from a network resource. "
+ "This reduces layout shifts and prepares the user for where content will be visible when available."
+
+
+
+ {indoc!(r#"
+
+ "#)}
+
+
+
+ "An explicit ""width"
" property of ""Size::Percent(100.0)"
" can be omitting as this is the default width of any skeleton."
+ "The ""height"
" property defaults to ""Size::Auto"
", so setting an explicit height is alway advised when not embedding children in the skeleton."
+
+
+ "The skeleton will render as:"
+
+
+
+ "Animation"
+
+
+ "By default, skeleton components contain an animation, suggesting that something is waiting to replace this component shortly. "
+ "If for any reason, this animation is not desired, it can be disabled using the ""animated"
" property."
+
+
+
+ {indoc!(r#"
+
+ "#)}
+
+
+ "The skeleton will render as:"
+
+
+
+
+ "Albeit used quite often these days, I would like to remind you that this concept is only tries to mitigate the problem of slowly loading resources. "
+ "All that might just not be required, if resources are preloaded, if services providing data do that in a few milliseconds, and so on and so forth... Try avoiding overly aggressive use of the skeleton component."
+ "But, even if your services respond quickly, keep in mind that the (uncontrollable) user-network-speeds may still result in slow resources."
+
+
+ "Children"
+
+
+ "As already mentioned briefly, the skeleton component optionally accepts children, so that content con be rendered if desired. "
+ "In this case, a specific ""height"
" property may not be specified."
+
+
+
+ {indoc!(r#"
+
+ "I am a skeleton!"
+
+ "#)}
+
+
+ "The skeleton will render as:"
+
+
+ "I am a skeleton!"
+
+
+ "We will encounter these skeletons on other pages of this layout chapter."
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --skeleton-background-color
+ --skeleton-animation-highlight-color
+ --skeleton-border-radius
+ --skeleton-padding
+ --skeleton-cursor
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/slider.rs b/src/pages/documentation/slider.rs
new file mode 100644
index 0000000..ffc0340
--- /dev/null
+++ b/src/pages/documentation/slider.rs
@@ -0,0 +1,210 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+use leptos_icons::BsIcon;
+
+#[component]
+pub fn PageSlider() -> impl IntoView {
+ let (value1, set_value1) = create_signal(6.0);
+ let (value2, set_value2) = create_signal(4.2);
+ let (value3, set_value3) = create_signal(-3.0);
+ let (value4, set_value4) = create_signal(0.5);
+ let (value5, set_value5) = create_signal(0.5);
+ let (range_a, set_range_a) = create_signal(0.5);
+ let (range_b, set_range_b) = create_signal(0.75);
+ let (range_a_step, set_range_a_step) = create_signal(2.0);
+ let (range_b_step, set_range_b_step) = create_signal(4.0);
+
+ view! {
+ "Slider"
+
+ "Allow users to adjust a value within a specified range by sliding a handle."
+
+
+ "All sliders require the ""min"
", ""max"
" and ""step"
" properties, specifying the range of values the slider provides. "
+ "Using smaller step values results in ever so slightly smoother sliders until they can be considered \"continuous\". "
+ "You may exclude the ""step"
" prop altogether to let the sliders use its full ""f64"
" precision."
+
+
+
+ {indoc!(r#"
+ let (value, set_value) = create_signal(6.0);
+ view! {
+
+ }
+ "#)}
+
+
+
+ "The slider always operates with ""f64"
" values and may suffer from typical IEEE-math rounding problems. "
+ "We use the ""value_display"
" property to specify how a selected value should be rendered."
+
+
+
+
+ "Volume slider"
+
+ "Continuous sliders are perfect when the exact value selected is of no particular interest to your user. For example, when operating a volume slider."
+
+
+ {indoc!(r#"
+ let (value, set_value) = create_signal(0.5);
+ view! {
+
+
+
+
+
+ }
+ "#)}
+
+
+
+
+
+
+
+
+ "Marks"
+
+
+ "Small step values result in lesser selectable values, as only values starting from min and increased by multiples of step are selectable. "
+ "To help visualize the selectable values of the slider, marks can be automatically generated."
+
+
+
+ {indoc!(r#"
+ let (value, set_value) = create_signal(6.0);
+ view! {
+
+ }
+ "#)}
+
+
+
+
+
+ "Note that marks are only helpful when dealing with sliders having a limited number of selectable values, meaning ones with small ranges and a big stepping values. "
+ "Automatic mark generation is currently limited to creating 20 evenly spaced marks. Continuous sliders will not create thousands of them."
+
+
+ "Arbitrary ranges"
+
+ "Sliders can use any combination of min, max and step values."
+
+
+
+ "You can also use a positive value for the ""min"
" prop, and a negative value for the ""max"
" prop, resulting in a reversed axis."
+
+
+
+ "Range sliders"
+
+
+ "A range of values can be selected using the ""RangeSlider"
" component. "
+ "The component requires two values and in return provides a slider with two control knobs, allowing you to select a range of values. "
+ "One knob can be dragged over the other, letting them switch places."
+
+
+
+ {indoc!(r#"
+ let (value_a, set_value_a) = create_signal(0.5);
+ let (value_b, set_value_b) = create_signal(0.75);
+ view! {
+
+ }
+ "#)}
+
+
+
+
+ "Range sliders can also use marks, just like the normal slider."
+
+
+
+ "Keyboard input"
+
+
+ "Slider knobs are keyboard-interactable and can be cycled through using the ""Tab"
" key. "
+ "Manipulation of slider knobs using the error keys will come in a future update."
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --slider-margin
+ --slider-bar-height
+ --slider-bar-background-color
+ --slider-bar-background-image
+ --slider-range-height
+ --slider-range-background-color
+ --slider-range-background-image
+ --slider-knob-size
+ --slider-knob-border-width
+ --slider-knob-border-color
+ --slider-knob-border-style
+ --slider-knob-background-color
+ --slider-knob-halo-size
+ --slider-knob-halo-size-while-dragged
+ --slider-knob-halo-opacity
+ --slider-knob-halo-background-color
+ --slider-knob-transition-speed
+ --slider-knob-box-shadow
+ --slider-mark-size
+ --slider-mark-color
+ --slider-mark-color-in-range
+ --slider-mark-title-color
+ --slider-mark-title-color-in-range
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/stack.rs b/src/pages/documentation/stack.rs
new file mode 100644
index 0000000..79ba749
--- /dev/null
+++ b/src/pages/documentation/stack.rs
@@ -0,0 +1,54 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageStack() -> impl IntoView {
+ view! {
+ "Stack"
+
+ "Vertical stacks"
+
+ "Use a stack to create a container displaying its list of children one after the other while spacing them out by a predefined distance."
+
+
+ {indoc!(r#"
+
+ "Item 1"
+ "Item 2"
+ "Item 3"
+
+ "#)}
+
+
+
+ "Item 1"
+ "Item 2"
+ "Item 3"
+
+
+ "Horizontal stacks"
+
+
+ "A stacks default orientation is ""StackOrientation::Vertical"
". "
+ "You can explicitly set the orientation to be ""StackOrientation::Horizontal"
" "
+ "to let the stack display its children horizontally."
+
+
+
+ {indoc!(r#"
+
+ "Item 1"
+ "Item 2"
+ "Item 3"
+
+ "#)}
+
+
+
+ "Item 1"
+ "Item 2"
+ "Item 3"
+
+ }
+}
diff --git a/src/pages/documentation/tab.rs b/src/pages/documentation/tab.rs
new file mode 100644
index 0000000..14049c7
--- /dev/null
+++ b/src/pages/documentation/tab.rs
@@ -0,0 +1,147 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageTab() -> impl IntoView {
+ let (test_reactive_label_bool, set_test_reactive_label_bool) = create_signal(false);
+ view! {
+ "Tabs"
+
+
+ ""
" allow you to spread out your UI components into multiple pages, where only one page is shown at any given time. "
+ "Every """
" inside represents a page with a label to select it. A user can interact with labels to bring the tab associated to it into view."
+
+
+
+ {indoc!(r#"
+
+ "Content of tab 1"
+ "Content of tab 2"
+ "Content of tab 3"
+
+ "#)}
+
+
+
+ "Content of tab 1"
+ "Content of tab 2"
+ "Content of tab 3"
+
+
+ "Reactivity"
+
+
+ "Labels can be anything implementing Leptos's ""IntoView"
" trait and are therefore as reactive as anything else."
+
+
+
+ {indoc!(r#"
+ let (bool, set_bool) = create_signal(false);
+
+ view! {
+
+
+
+
+
+ "Content of tab 2"
+
+
+ }
+ "#)}
+
+
+
+
+
+
+
+ "Content of tab 2"
+
+
+
+ "Nesting"
+
+
+ "Tabs can be nested just as one would expect."
+
+
+
+ {indoc!(r#"
+
+
+
+
+ "This is a nested tab."
+
+
+ "This tah is nested as well."
+
+
+
+
+
+ "#)}
+
+
+
+
+
+
+ "This is a nested tab."
+
+
+ "This tab is nested as well."
+
+
+
+
+
+
+ "When are tabs rendered?"
+
+
+ "You might have spotted a particular behavior in the above example. "
+ "When switching to the \"Inner 2\" tab, then switching to \"Outer 2\" and back to \"Outer 1\", "
+ "we still see \"Inner 2\" and not the default tab \"Inner 1\" again."
+
+
+
+ "This is where the ""mount"
" property comes into play. We had it set to ""Mount::Once"
" in all of our examples. "
+ "There are two variants to choose from:"
+
+
+
+
+ "Mount::Once"
+
+ "Tab content is rendered once. Tabs are simply hidden when not shown."
+
+
+
+ "Mount::WhenShown"
+
+ "Tab content is rendered every time a tab is shown. The dom of the tab is unmounted when hidden. "
+ "This means that there is only ever one tab in the final dom, not requiring any hiding-mechanism as in the ""Mount::Once"
" case."
+
+
+
+
+
+
+
+
+ "This is a nested tab."
+
+
+ "This tab is nested as well."
+
+
+
+
+
+
+ // "Default tab"
+ }
+}
diff --git a/src/pages/documentation/table.rs b/src/pages/documentation/table.rs
new file mode 100644
index 0000000..d8a35d9
--- /dev/null
+++ b/src/pages/documentation/table.rs
@@ -0,0 +1,100 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageTable() -> impl IntoView {
+ view! {
+ "Table"
+
+ "Tables..."
+
+
+ {indoc!(r##"
+
+
+
+
+ "#"
+ "Name"
+ "Appearance"
+ "Num. eyes"
+
+
+
+
+ "1"
+ "Kevin"
+ "Tall"
+ "2"
+
+ // ...
+
+
+
+ "##)}
+
+
+
+
+
+
+ "#"
+ "Name"
+ "Appearance"
+ "Num. eyes"
+
+
+
+
+ "1"
+ "Kevin"
+ "Tall"
+ "2"
+
+
+ "2"
+ "Bob"
+ "Short"
+ "2"
+
+
+ "3"
+ "Stuart"
+ "Medium"
+ "1"
+
+
+ "4"
+ "Otto"
+ "Round"
+ "2"
+
+
+
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ // Table wrapper
+ --table-wrapper-box-shadow-color
+
+ // Table
+ --table-color
+ --table-background-color
+ --table-background-color-on-hover
+ --table-background-color-of-striped-rows
+ --table-header-background-color
+ --table-border-color
+ --table-cell-box-shadow-on-hover
+ --table-column-background-if-ordered
+ --table-header-cell-padding
+ --table-body-cell-padding
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/themes.rs b/src/pages/documentation/themes.rs
new file mode 100644
index 0000000..a0ad9fa
--- /dev/null
+++ b/src/pages/documentation/themes.rs
@@ -0,0 +1,80 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+use crate::pages::documentation::doc_root::DocRoutes;
+
+#[component]
+pub fn PageThemes() -> impl IntoView {
+ view! {
+ "Theming"
+
+ "From the first day on, Leptonic was built with theming in mind."
+
+
+ "Styles are not attached to components directly, are not injected in any way upon rendering, "
+ "which means that all components could be describes as \"headless\" in that regard."
+
+
+
+ "All styling is provided through the ""leptonic-theme" " crate. "
+ "When used as a build.rs dependency, this crate can write out SCSS code, styling the Leptonic components in two themes: ""light"
" and ""dark"
"."
+
+
+
+ "The """
" component already discussed in the " "Usage"" section provides everything required (namely rendering the """
" component) to active a theme."
+
+
+
+ "A simple theme-switching component, realized using a " "Toggle"", is provided, which can be used simply like this anywhere you want:"
+
+
+
+ {indoc!(r#"
+
+ "#)}
+
+
+
+ "We are using the """
" enum here, which describes the two out-of-the-box themes (light and dark). "
+ "This is not mandatory though as you could create your own theme-defining type and your own theme toggle components."
+
+
+ "Customization"
+
+
+ "Having a theme, even better a theme provided by default is, is great, but only if that them can be customized in a meaningful way."
+
+
+
+ "All components styles therefore make broad use of CSS variables, with which many aspects of the two default themes can be changed. "
+ "In every page of this book explaining a component, we hint you to the styles you might want to override. A theme generator may come in the future..."
+
+
+
+ "Have a look at this excerpt from this book's main ""style.scss"
" file, "
+ "showing you how we include the leptonic standard themes previously written by our build script "
+ "and overwrite a few variables to meet our design needs."
+
+
+
+ {indoc!(r#"
+ @import "../generated/leptonic/leptonic-themes";
+
+ [data-theme="light"] {
+ --brand-color: #e66956;
+
+ --drawer-background-color: none;
+ --drawer-box-shadow: none;
+ }
+
+ [data-theme="dark"] {
+ --brand-color: #e66956;
+
+ --drawer-background-color: none;
+ --drawer-box-shadow: none;
+ }
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/tiptap_editor.rs b/src/pages/documentation/tiptap_editor.rs
new file mode 100644
index 0000000..c2d5fc8
--- /dev/null
+++ b/src/pages/documentation/tiptap_editor.rs
@@ -0,0 +1,44 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageTiptapEditor() -> impl IntoView {
+ let (value, set_value) = create_signal(r#"This is a simple paragraph ... H1 ! Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
"#.to_owned());
+ let (disabled, set_disabled) = create_signal(false);
+
+ view! {
+ "Tiptap editor"
+
+ "Embed a simple WYSIWYG editor into your app."
+
+
+ "The provided editor is a wrapper around a headless Tiptap editor instance obtainable via the leptos-tiptap integration. "
+ "You may want to look into building you own editor UI!"
+
+
+
+
+
+ {
+ move || match disabled.get() {
+ true => "disabled",
+ false => "enabled",
+ }
+ }
+
+
+
+
+ {indoc!(r#"
+ set_value.set(content),
+ }/>
+ "#)}
+
+
+ set_value.set(content),
+ }/>
+ }
+}
diff --git a/src/pages/documentation/toast.rs b/src/pages/documentation/toast.rs
new file mode 100644
index 0000000..1ef22a0
--- /dev/null
+++ b/src/pages/documentation/toast.rs
@@ -0,0 +1,101 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+use strum::IntoEnumIterator;
+use uuid::Uuid;
+
+#[component]
+pub fn PageToast() -> impl IntoView {
+ let (variant, set_variant) = create_signal(ToastVariant::Success);
+ let (timeout, set_timeout) = create_signal(ToastTimeout::DefaultDelay);
+ let (header, set_header) = create_signal("Header".to_owned());
+ let (body, set_body) = create_signal("Body".to_owned());
+
+ let toasts = expect_context::();
+
+ view! {
+ "Toasts"
+
+
+
+
+ >()}
+ selected=variant
+ set_selected=set_variant
+ search_text_provider=move |o| format!("{o}")
+ render_option=move |o| format!("{o:?}").into_view()
+ style="margin-bottom: 1em;"
+ />
+
+
+
+
+ "Create Toast"
+
+
+
+ {indoc!(r#"
+ let toasts = expect_context::();
+
+ view! {
+
+ "Create Toast"
+
+ }
+ "#)}
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --toast-border-radius
+ --toast-header-border-bottom
+ --toast-header-padding
+ --toast-message-padding
+ --toast-info-header-background
+ --toast-info-header-color
+ --toast-info-message-background
+ --toast-info-message-color
+ --toast-success-header-background
+ --toast-success-header-color
+ --toast-success-message-background
+ --toast-success-message-color
+ --toast-warn-header-background
+ --toast-warn-header-color
+ --toast-warn-message-background
+ --toast-warn-message-color
+ --toast-error-header-background
+ --toast-error-header-color
+ --toast-error-message-background
+ --toast-error-message-color
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/toggle.rs b/src/pages/documentation/toggle.rs
new file mode 100644
index 0000000..2f17663
--- /dev/null
+++ b/src/pages/documentation/toggle.rs
@@ -0,0 +1,59 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+use leptos_icons::BsIcon;
+
+#[component]
+pub fn PageToggle() -> impl IntoView {
+ let (state, set_state) = create_signal(false);
+
+ view! {
+ "Toggle"
+
+ "A toggle is a representation of a boolean value."
+
+
+ {indoc!(r#"
+ let (state, set_state) = create_signal(false);
+
+ view! {
+
+ }
+ "#)}
+
+
+
+
+ "Icons"
+
+ "A toggle can be configured with a pair of icons. One icon being rendered in the off position, the other being rendered in the on position."
+
+
+ {indoc!(r#"
+ let (state, set_state) = create_signal(false);
+
+ view! {
+
+ }
+ "#)}
+
+
+
+
+ "Variations"
+
+ "The toggle comes in two variants: Sliding and Stationary. Sliding toggles are the default and the ones we have used so far."
+ "Stationary toggles are not animated and only consist of a single circle."
+
+
+ }
+}
diff --git a/src/pages/documentation/transition.rs b/src/pages/documentation/transition.rs
new file mode 100644
index 0000000..2b51a23
--- /dev/null
+++ b/src/pages/documentation/transition.rs
@@ -0,0 +1,62 @@
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageTransition() -> impl IntoView {
+ let (transition_collapse_h, set_transition_collapse_h) = create_signal(false);
+ let (transition_collapse_v, set_transition_collapse_v) = create_signal(false);
+ let (transition_fade, set_transition_fade) = create_signal(false);
+ //let (transition_grow, set_transition_grow) = create_signal(false);
+ //let (transition_slide, set_transition_slide) = create_signal(false);
+ //let (transition_zoom, set_transition_zoom) = create_signal(false);
+
+ view! {
+ "Transitions"
+
+ "Transition - Collapse - horizontally"
+
+
+ "Collapse"
+
+
+
+
+ "Transition - Collapse - vertically"
+
+
+ "Collapse"
+
+
+
+
+ "Transition - Fade"
+
+
+ "Fade"
+
+
+
+
+ //"Transition - Grow"
+ //
+ //
+ // "Grow"
+ //
+
+ //
+
+ //"Transition - Slide"
+ //
+ //
+ // "Slide"
+ //
+
+ //
+
+ //"Transition - Zoom"
+ //
+ //
+ // "Zoom"
+ //
+ }
+}
diff --git a/src/pages/documentation/typography.rs b/src/pages/documentation/typography.rs
new file mode 100644
index 0000000..4237981
--- /dev/null
+++ b/src/pages/documentation/typography.rs
@@ -0,0 +1,72 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+#[component]
+pub fn PageTypography() -> impl IntoView {
+ view! {
+ "Typography"
+
+
+
+ "Typography - H1"
+ "Typography - H2"
+ "Typography - H3"
+ "Typography - H4"
+ "Typography - H5"
+ "Typography - H6"
+
+ "This is a paragraph"
+
+ "Typography - Code"
+
+
+ "This is a paragraph containing an "
+ "inlined"
+ " piece of code."
+
+
+ "Styling"
+
+ "You may overwrite any of the following CSS variables to meet your styling needs."
+
+
+ {indoc!(r#"
+ --typography-font-family
+ --typography-h1-margin
+ --typography-h1-font-size
+ --typography-h1-font-weight
+ --typography-h2-margin
+ --typography-h2-font-size
+ --typography-h2-font-weight
+ --typography-h3-margin
+ --typography-h3-font-size
+ --typography-h3-font-weight
+ --typography-h4-margin
+ --typography-h4-font-size
+ --typography-h4-font-weight
+ --typography-h5-margin
+ --typography-h5-font-size
+ --typography-h5-font-weight
+ --typography-h6-margin
+ --typography-h6-font-size
+ --typography-h6-font-weight
+ --typography-p-margin
+ --typography-p-font-size
+ --typography-p-font-weight
+ --typography-p-line-height
+ --typography-code-margin
+ --typography-code-padding
+ --typography-code-font-size
+ --typography-code-font-weight
+ --typography-code-line-height
+ --typography-code-border-radius
+ --typography-code-background-color
+ --typography-code-color
+ --typography-inline-code-margin
+ --typography-inline-code-padding
+ --typography-inline-code-line-height
+ "#)}
+
+ }
+}
diff --git a/src/pages/documentation/usage.rs b/src/pages/documentation/usage.rs
new file mode 100644
index 0000000..67843f4
--- /dev/null
+++ b/src/pages/documentation/usage.rs
@@ -0,0 +1,71 @@
+use indoc::indoc;
+use leptonic::prelude::*;
+use leptos::*;
+
+use crate::pages::documentation::doc_root::DocRoutes;
+
+#[component]
+pub fn PageUsage() -> impl IntoView {
+ view! {
+ "Usage"
+
+ "Similar to Leptos, this crate comes with a prelude module."
+
+
+ "Just "
+ "use leptonic::prelude::*;"
+ " and you are almost ready to go."
+
+
+
+ "Leptonic provides the """
" component. "
+ "It is responsible for enabling the " "Theming"", " "Modal"" and "
+ "Toast"" functionality of Leptonic as well as providing global event-listening capabilities."
+ "You have to include it in your app once, and render all your content inside it. Many Leptonic components require the """
" component to be present."
+
+
+
+ "Using the component is as simple as this:"
+
+
+
+ {indoc!(r#"
+ #[component]
+ pub fn App() -> impl IntoView {
+ view! {
+
+ "Content goes here :)"
+
+ }
+ }
+ "#)}
+
+
+ "Need help?"
+
+
+ "If you get stuck at any point integrating or using Leptonic, these are things you may find helpful: "
+
+ "Look for help in the Leptos ""Discord" " server."
+ "If you think you encountered a bug, open a ticket in our " "repository"
+ "Compare the implementation of this book at ""GitHub" " with what you currently have."
+
+
+
+ "Contribute"
+
+
+ "If you have anything to say about Leptonic, be it a component you miss, a feature you feel missing, or a bug you encountered, "
+ "feel free to contribute back to the project by reaching the community or us through"
+
+ "the Leptos ""Discord" " server or "
+ "the ""Issues" " section of the Leptonic repository."
+
+
+
+
+ "Writing a component library is a big undertaking. Feel free to contribute by writing new or updating existing code yourself. "
+ "As more people are on board, more and better ideas, concepts and implementation details follow. Code contributions are always welcomed!"
+
+ }
+}
diff --git a/src/pages/err404.rs b/src/pages/err404.rs
new file mode 100644
index 0000000..811a556
--- /dev/null
+++ b/src/pages/err404.rs
@@ -0,0 +1,27 @@
+use leptonic::prelude::*;
+use leptos::*;
+
+use crate::app::AppRoutes;
+
+#[component]
+pub fn PageErr404() -> impl IntoView {
+ view! {
+
+
+
+ "404"
+ "Whoops, this page doesn't exist :-("
+
+
+
+ "Back"
+
+
+
+
+
+
+
+
+ }
+}
diff --git a/src/pages/mod.rs b/src/pages/mod.rs
new file mode 100644
index 0000000..cb0dd49
--- /dev/null
+++ b/src/pages/mod.rs
@@ -0,0 +1,3 @@
+pub mod documentation;
+pub mod err404;
+pub mod welcome;
diff --git a/src/pages/welcome.rs b/src/pages/welcome.rs
new file mode 100644
index 0000000..5c5e991
--- /dev/null
+++ b/src/pages/welcome.rs
@@ -0,0 +1,27 @@
+use leptonic::prelude::*;
+use leptos::*;
+
+use crate::pages::documentation::doc_root::DocRoutes;
+
+#[component]
+pub fn PageWelcome() -> impl IntoView {
+ view! {
+
+
+
+
+ "NIGIG LIMITED"
+
+
+
+ "Build blazingly fast, ergonomic sites with an outstanding developer experience."
+ //"Elevate Web Development with Unleashed Performance and Ergonomic Design using Leptos!"
+
+
+ // TODO: Investigae: When using AppRoutes::Doc, browser navigation (back) does nothing...
+
+ "Read the docs"
+
+
+ }
+}