From c2fbdecce0d6e51329acab2b219a9cb3500de535 Mon Sep 17 00:00:00 2001 From: Per Stark Date: Sun, 4 May 2025 21:16:09 +0200 Subject: [PATCH] feat: database migrations --- .gitignore | 3 +- Cargo.lock | 599 +++++++++++++----- api-router/src/api_state.rs | 2 +- common/Cargo.toml | 1 + .../20250503_215025_initial_setup.surql | 15 + .../20250503_215025_initial_setup.json | 1 + common/migrations/definitions/_initial.json | 1 + common/schemas/analytics.surql | 7 + common/schemas/auth.surql | 6 + common/schemas/conversation.surql | 15 + common/schemas/file.surql | 19 + common/schemas/ingestion_task.surql | 19 + common/schemas/knowledge_entity.surql | 30 + common/schemas/message.surql | 19 + common/schemas/relates_to.surql | 19 + common/schemas/script_migration.surql | 7 + common/schemas/system_settings.surql | 11 + common/schemas/text_chunk.surql | 23 + common/schemas/text_content.surql | 22 + common/schemas/user.surql | 20 + common/src/storage/db.rs | 105 +-- common/src/storage/types/ingestion_task.rs | 2 +- common/src/storage/types/user.rs | 6 +- flake.nix | 139 ++++ html-router/assets/style.css | 2 + main/src/main.rs | 2 +- main/src/server.rs | 2 +- result | 1 + 28 files changed, 880 insertions(+), 218 deletions(-) create mode 100644 common/migrations/20250503_215025_initial_setup.surql create mode 100644 common/migrations/definitions/20250503_215025_initial_setup.json create mode 100644 common/migrations/definitions/_initial.json create mode 100644 common/schemas/analytics.surql create mode 100644 common/schemas/auth.surql create mode 100644 common/schemas/conversation.surql create mode 100644 common/schemas/file.surql create mode 100644 common/schemas/ingestion_task.surql create mode 100644 common/schemas/knowledge_entity.surql create mode 100644 common/schemas/message.surql create mode 100644 common/schemas/relates_to.surql create mode 100644 common/schemas/script_migration.surql create mode 100644 common/schemas/system_settings.surql create mode 100644 common/schemas/text_chunk.surql create mode 100644 common/schemas/text_content.surql create mode 100644 common/schemas/user.surql create mode 100644 flake.nix create mode 100644 html-router/assets/style.css create mode 120000 result diff --git a/.gitignore b/.gitignore index 6e20fe6..747eeca 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ database node_modules config.yaml -flake.nix target @@ -20,5 +19,5 @@ devenv.local.nix # pre-commit .pre-commit-config.yaml -html-router/assets/style.css +# html-router/assets/style.css html-router/node_modules diff --git a/Cargo.lock b/Cargo.lock index a970824..86f8811 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,6 +150,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "any_ascii" version = "0.3.2" @@ -172,6 +222,7 @@ dependencies = [ "common", "futures", "serde", + "serde_json", "tempfile", "thiserror 1.0.69", "tokio", @@ -267,14 +318,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", + "pin-project-lite", "slab", ] @@ -321,7 +373,7 @@ dependencies = [ "proc-macro2", "quote", "strum", - "syn 2.0.100", + "syn 2.0.101", "thiserror 1.0.69", ] @@ -383,7 +435,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -405,7 +457,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -422,7 +474,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -451,7 +503,7 @@ dependencies = [ "derive_utils", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -476,9 +528,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de45108900e1f9b9242f7f2e254aa3e2c029c921c258fe9e6b4217eeebd54288" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ "axum-core", "axum-macros", @@ -548,7 +600,7 @@ checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -646,10 +698,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abc6886ec1cf5a7e06c3163c2655d576996f0e5c6bdb05fbb2bb002b1872dd6a" dependencies = [ "darling", - "heck 0.5.0", + "heck", "proc-macro-error2", "quote", - "syn 2.0.100", + "syn 2.0.101", "ubyte", ] @@ -849,7 +901,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -882,9 +934,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" [[package]] name = "byteorder" @@ -931,9 +983,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.19" +version = "1.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "jobserver", "libc", @@ -1011,9 +1063,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1024,6 +1076,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "chrono-human-duration" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19283df3a144dfdeee6568e42ad7f0939d3c261bcdfe954b1a1098f6f7c1b908" +dependencies = [ + "chrono", +] + [[package]] name = "chrono-tz" version = "0.10.3" @@ -1082,6 +1143,102 @@ dependencies = [ "inout", ] +[[package]] +name = "clap" +version = "4.5.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "cli-table" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbb116d9e2c4be7011360d0c0bee565712c11e969c9609b25b619366dc379d" +dependencies = [ + "cli-table-derive", + "csv", + "termcolor", + "unicode-width", +] + +[[package]] +name = "cli-table-derive" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e83a93253aaae7c74eb7428ce4faa6e219ba94886908048888701819f82fb94" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "color-eyre" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e1761c0e16f8883bbbb8ce5990867f4f06bf11a0253da6495a04ce4b6ef0ec" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ddd8d5bfda1e11a501d0a7303f3bfed9aa632ebdb859be40d0fd70478ed70d5" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "common" version = "0.1.0" @@ -1099,6 +1256,7 @@ dependencies = [ "config", "dom_smoothie", "futures", + "include_dir", "mime", "mime_guess", "minijinja", @@ -1110,6 +1268,7 @@ dependencies = [ "serde_json", "sha2", "surrealdb", + "surrealdb-migrations", "tempfile", "thiserror 1.0.69", "tokio", @@ -1354,7 +1513,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.101", +] + +[[package]] +name = "csv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +dependencies = [ + "memchr", ] [[package]] @@ -1387,7 +1567,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1398,7 +1578,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1458,7 +1638,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1479,7 +1659,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1489,7 +1669,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1500,7 +1680,7 @@ checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1511,14 +1691,23 @@ checksum = "ccfae181bab5ab6c5478b2ccb69e4c68a02f8c3ec72f6616bfec9dbc599d2ee0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "deunicode" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc55fe0d1f6c107595572ec8b107c0999bb1a2e0b75e37429a4fb0d6474a0e7d" +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" + +[[package]] +name = "diffy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b545b8c50194bdd008283985ab0b31dba153cfd5b3066a92770634fbc0d7d291" +dependencies = [ + "nu-ansi-term 0.50.1", +] [[package]] name = "digest" @@ -1581,7 +1770,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1603,12 +1792,6 @@ dependencies = [ "urlencoding", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "dom_query" version = "0.17.0" @@ -1644,6 +1827,12 @@ dependencies = [ "url", ] +[[package]] +name = "double-ended-peekable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d05e1c0dbad51b52c38bda7adceef61b9efc2baf04acfe8726a8c4630a6f57" + [[package]] name = "dtoa" version = "1.0.10" @@ -1782,6 +1971,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -1874,6 +2073,12 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -1974,7 +2179,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2066,7 +2271,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62ddb1950450d67efee2bbc5e429c68d052a822de3aad010d28b351fbb705224" dependencies = [ "approx 0.5.1", - "arbitrary", "num-traits", "rstar", "serde", @@ -2185,9 +2389,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", @@ -2200,7 +2404,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -2239,12 +2443,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -2622,7 +2820,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2671,6 +2869,12 @@ dependencies = [ "quote", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" version = "1.9.3" @@ -2689,7 +2893,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "serde", ] @@ -2762,6 +2966,12 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -2789,6 +2999,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -2914,6 +3133,15 @@ dependencies = [ "any_ascii", ] +[[package]] +name = "lexicmp" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e8f89da8fd95c4eb6274e914694bea90c7826523b26f2a2fd863d44b9d42c43" +dependencies = [ + "deunicode", +] + [[package]] name = "libc" version = "0.2.172" @@ -2983,7 +3211,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -3073,7 +3301,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3143,7 +3371,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3393,6 +3621,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -3458,19 +3695,20 @@ dependencies = [ [[package]] name = "object_store" -version = "0.10.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6da452820c715ce78221e8202ccc599b4a52f3e1eb3eedb487b680c81a8e3f3" +checksum = "e9ce831b09395f933addbc56d894d889e4b226eba304d4e7adbab591e26daf1e" dependencies = [ "async-trait", "bytes", "chrono", "futures", + "http", "humantime", - "itertools 0.13.0", + "itertools 0.14.0", "parking_lot", "percent-encoding", - "snafu", + "thiserror 2.0.12", "tokio", "tracing", "url", @@ -3512,7 +3750,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3523,9 +3761,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.107" +version = "0.9.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" dependencies = [ "cc", "libc", @@ -3555,6 +3793,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owo-colors" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" + [[package]] name = "parking" version = "2.2.1" @@ -3681,7 +3925,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3755,7 +3999,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "unicase", ] @@ -3792,7 +4036,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3840,7 +4084,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3867,7 +4111,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.24", + "zerocopy 0.8.25", ] [[package]] @@ -3904,7 +4148,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3924,9 +4168,9 @@ checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" [[package]] name = "psm" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" +checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" dependencies = [ "cc", ] @@ -3971,7 +4215,7 @@ checksum = "287e56aac5a2b4fb25a6fb050961d157635924c8696305a5c937a76f29841a0f" dependencies = [ "ahash 0.8.11", "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "parking_lot", ] @@ -4017,9 +4261,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" dependencies = [ "cfg_aliases", "libc", @@ -4154,9 +4398,9 @@ checksum = "bbc4a4ea2a66a41a1152c4b3d86e8954dc087bdf33af35446e6e176db4e73c8c" [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags 2.9.0", ] @@ -4200,7 +4444,7 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4235,6 +4479,12 @@ dependencies = [ "regex-syntax 0.8.5", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -4357,7 +4607,7 @@ checksum = "5f0ec466e5d8dca9965eb6871879677bef5590cf7525ad96cae14376efb75073" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4368,7 +4618,7 @@ checksum = "d3415e1bc838c36f9a0a2ac60c0fa0851c72297685e66592c44870d82834dfa2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4414,7 +4664,7 @@ dependencies = [ "rinja_parser", "rustc-hash", "serde", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4598,9 +4848,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags 2.9.0", "errno", @@ -4852,7 +5102,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4886,7 +5136,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4937,7 +5187,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4962,9 +5212,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -5049,28 +5299,6 @@ dependencies = [ "serde", ] -[[package]] -name = "snafu" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" -dependencies = [ - "doc-comment", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "snap" version = "1.1.1" @@ -5104,7 +5332,7 @@ version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ece03ff43cd2a9b57ebf776ea5e78bd30b3b4185a619f041079f4109f385034" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", "num-traits", "robust", "smallvec", @@ -5124,9 +5352,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stacker" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" dependencies = [ "cc", "cfg-if", @@ -5199,11 +5427,11 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -5214,9 +5442,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "surrealdb" -version = "2.2.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd369a114a892aa1d0851d743f35c4c48672d6b0fb5aaf2b3e4dcacfac68a070" +checksum = "8b9f7f8154fa85c96e9a3339477512b3249e4bbfd503b122a867e8f3abdad456" dependencies = [ "arrayvec", "async-channel", @@ -5225,6 +5453,7 @@ dependencies = [ "dmp", "futures", "geo", + "getrandom 0.3.2", "indexmap 2.9.0", "path-clean", "pharos", @@ -5255,9 +5484,9 @@ dependencies = [ [[package]] name = "surrealdb-core" -version = "2.2.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a5383db5d230e9a0c2481342e71349d4a9efc52c37ffe60e6756953246a690" +checksum = "6ec1aa60b00aff1958e8b56130398d531f403d88379987bd3fa5e368f92eac5d" dependencies = [ "addr", "affinitypool", @@ -5286,11 +5515,12 @@ dependencies = [ "fuzzy-matcher", "geo", "geo-types", + "getrandom 0.3.2", "hex", "http", "ipnet", "jsonwebtoken", - "lexicmp", + "lexicmp 0.1.0", "linfa-linalg", "md-5", "nanoid", @@ -5310,6 +5540,7 @@ dependencies = [ "rayon", "reblessive", "regex", + "reqwest", "revision 0.11.0", "ring", "rmpv", @@ -5345,25 +5576,46 @@ dependencies = [ ] [[package]] -name = "surrealkv" -version = "0.8.1" +name = "surrealdb-migrations" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28eec56aca077c245bf5f9e08876fdcce58b5361e77a0b94a92fd47e1990ad4" +checksum = "30978bcc14090620664e96b45b0927c0dd42bba0681706208b8ee5d6dfaebdcf" +dependencies = [ + "chrono", + "chrono-human-duration", + "clap", + "cli-table", + "color-eyre", + "diffy", + "fs_extra", + "include_dir", + "itertools 0.14.0", + "lexicmp 0.2.0", + "regex-lite", + "rust-ini", + "serde", + "serde_json", + "surrealdb", + "tokio", +] + +[[package]] +name = "surrealkv" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43d55edab1e65c7704486016f98e9eac61c97474921dbac094af2cd16e16c3" dependencies = [ "ahash 0.8.11", - "async-channel", "bytes", "chrono", "crc32fast", - "futures", + "double-ended-peekable", "getrandom 0.2.16", "lru", "parking_lot", "quick_cache 0.6.13", "revision 0.10.0", - "tokio", "vart 0.9.2", - "wasm-bindgen-futures", ] [[package]] @@ -5379,9 +5631,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -5399,13 +5651,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -5484,6 +5736,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "text-splitter" version = "0.18.1" @@ -5527,7 +5788,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -5538,7 +5799,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -5659,7 +5920,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -5725,9 +5986,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -5737,18 +5998,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap 2.9.0", "serde", @@ -5851,7 +6112,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -5864,6 +6125,16 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-error" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", +] + [[package]] name = "tracing-log" version = "0.2.0" @@ -5882,7 +6153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", - "nu-ansi-term", + "nu-ansi-term 0.46.0", "once_cell", "regex", "sharded-slab", @@ -6149,6 +6420,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.16.0" @@ -6247,7 +6524,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -6282,7 +6559,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6344,9 +6621,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "954c5a41f2bcb7314344079d0891505458cc2f4b422bdea1d5bfbe6d1a04903b" +checksum = "08bcbdcad8fb2e316072ba6bbe09419afdb550285668ac2534f4230a6f2da0ee" dependencies = [ "phf", "phf_codegen", @@ -6356,9 +6633,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.8" +version = "0.26.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +checksum = "37493cadf42a2a939ed404698ded7fb378bf301b5011f973361779a3a74f8c93" dependencies = [ "rustls-pki-types", ] @@ -6449,7 +6726,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -6460,7 +6737,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -6471,7 +6748,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -6482,7 +6759,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -6686,9 +6963,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.6" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" dependencies = [ "memchr", ] @@ -6804,7 +7081,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "synstructure", ] @@ -6819,11 +7096,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive 0.8.24", + "zerocopy-derive 0.8.25", ] [[package]] @@ -6834,18 +7111,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -6865,7 +7142,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "synstructure", ] @@ -6886,7 +7163,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -6908,7 +7185,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] diff --git a/api-router/src/api_state.rs b/api-router/src/api_state.rs index 416b4ce..67efc57 100644 --- a/api-router/src/api_state.rs +++ b/api-router/src/api_state.rs @@ -20,7 +20,7 @@ impl ApiState { .await?, ); - surreal_db_client.ensure_initialized().await?; + surreal_db_client.apply_migrations().await?; let app_state = ApiState { db: surreal_db_client.clone(), diff --git a/common/Cargo.toml b/common/Cargo.toml index d6ca0ee..11352ec 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -38,6 +38,7 @@ minijinja = { version = "2.5.0", features = ["loader", "multi_template"] } minijinja-autoreload = "2.5.0" minijinja-embed = { version = "2.8.0" } minijinja-contrib = { version = "2.6.0", features = ["datetime", "timezone"] } +include_dir = "0.7.4" [features] test-utils = [] diff --git a/common/migrations/20250503_215025_initial_setup.surql b/common/migrations/20250503_215025_initial_setup.surql new file mode 100644 index 0000000..c772503 --- /dev/null +++ b/common/migrations/20250503_215025_initial_setup.surql @@ -0,0 +1,15 @@ +# Ensure 'analytics:current' record exists +CREATE analytics:current CONTENT { + page_loads: 0, + visitors: 0, +}; + +# Ensure 'system_settings:current' record exists +CREATE system_settings:current CONTENT { + registrations_enabled: true, + require_email_verification: false, + query_model: "gpt-4o-mini", + processing_model: "gpt-4o-mini", + query_system_prompt: "", + ingestion_system_prompt: "" +}; diff --git a/common/migrations/definitions/20250503_215025_initial_setup.json b/common/migrations/definitions/20250503_215025_initial_setup.json new file mode 100644 index 0000000..e25933f --- /dev/null +++ b/common/migrations/definitions/20250503_215025_initial_setup.json @@ -0,0 +1 @@ +{"schemas":"--- original\n+++ modified\n@@ -147,6 +147,7 @@\n\n DEFINE FIELD OVERWRITE script_name ON script_migration TYPE string;\n DEFINE FIELD OVERWRITE executed_at ON script_migration TYPE datetime VALUE time::now() READONLY;\n+\n # Defines the schema for the 'system_settings' table.\n\n DEFINE TABLE IF NOT EXISTS system_settings SCHEMALESS;\n","events":null} \ No newline at end of file diff --git a/common/migrations/definitions/_initial.json b/common/migrations/definitions/_initial.json new file mode 100644 index 0000000..b3bee42 --- /dev/null +++ b/common/migrations/definitions/_initial.json @@ -0,0 +1 @@ +{"schemas":"# Defines the schema for the 'analytics' table.\n\nDEFINE TABLE IF NOT EXISTS analytics SCHEMALESS;\n\n# Custom fields from the Analytics struct\nDEFINE FIELD IF NOT EXISTS page_loads ON analytics TYPE number;\nDEFINE FIELD IF NOT EXISTS visitors ON analytics TYPE number;\n\n# Defines authentication scope and access rules.\n# This mirrors the logic previously in SurrealDbClient::setup_auth\n\nDEFINE ACCESS IF NOT EXISTS account ON DATABASE TYPE RECORD\n SIGNUP ( CREATE user SET email = $email, password = crypto::argon2::generate($password), anonymous = false, user_id = $user_id) # Ensure user_id is provided if needed\n SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(password, $password) );\n\n# Defines the schema for the 'conversation' table.\n\nDEFINE TABLE IF NOT EXISTS conversation SCHEMALESS;\n\n# Standard fields\nDEFINE FIELD IF NOT EXISTS created_at ON conversation TYPE string;\nDEFINE FIELD IF NOT EXISTS updated_at ON conversation TYPE string;\n\n# Custom fields from the Conversation struct\nDEFINE FIELD IF NOT EXISTS user_id ON conversation TYPE string;\nDEFINE FIELD IF NOT EXISTS title ON conversation TYPE string;\n\n# Add indexes based on query patterns (get_complete_conversation ownership check, get_user_conversations)\nDEFINE INDEX IF NOT EXISTS conversation_user_id_idx ON conversation FIELDS user_id;\nDEFINE INDEX IF NOT EXISTS conversation_created_at_idx ON conversation FIELDS created_at; # For get_user_conversations ORDER BY\n\n# Defines the schema for the 'file' table (used by FileInfo).\n\nDEFINE TABLE IF NOT EXISTS file SCHEMALESS;\n\n# Standard fields\nDEFINE FIELD IF NOT EXISTS created_at ON file TYPE string;\nDEFINE FIELD IF NOT EXISTS updated_at ON file TYPE string;\n\n# Custom fields from the FileInfo struct\nDEFINE FIELD IF NOT EXISTS sha256 ON file TYPE string;\nDEFINE FIELD IF NOT EXISTS path ON file TYPE string;\nDEFINE FIELD IF NOT EXISTS file_name ON file TYPE string;\nDEFINE FIELD IF NOT EXISTS mime_type ON file TYPE string;\nDEFINE FIELD IF NOT EXISTS user_id ON file TYPE string;\n\n# Indexes based on usage (get_by_sha, potentially user lookups)\n# Using UNIQUE based on the logic in FileInfo::new to prevent duplicates\nDEFINE INDEX IF NOT EXISTS file_sha256_idx ON file FIELDS sha256 UNIQUE;\nDEFINE INDEX IF NOT EXISTS file_user_id_idx ON file FIELDS user_id;\n\n# Defines the schema for the 'ingestion_task' table (used by IngestionTask).\n\nDEFINE TABLE IF NOT EXISTS job SCHEMALESS;\n\n# Standard fields\nDEFINE FIELD IF NOT EXISTS created_at ON job TYPE string;\nDEFINE FIELD IF NOT EXISTS updated_at ON job TYPE string;\n\n# Custom fields from the IngestionTask struct\n# IngestionPayload is complex, store as object\nDEFINE FIELD IF NOT EXISTS content ON job TYPE object;\n# IngestionTaskStatus can hold data (InProgress), store as object\nDEFINE FIELD IF NOT EXISTS status ON job TYPE object;\nDEFINE FIELD IF NOT EXISTS user_id ON job TYPE string;\n\n# Indexes explicitly defined in build_indexes and useful for get_unfinished_tasks\nDEFINE INDEX IF NOT EXISTS idx_job_status ON job FIELDS status;\nDEFINE INDEX IF NOT EXISTS idx_job_user ON job FIELDS user_id;\nDEFINE INDEX IF NOT EXISTS idx_job_created ON job FIELDS created_at;\n\n# Defines the schema for the 'knowledge_entity' table.\n\nDEFINE TABLE IF NOT EXISTS knowledge_entity SCHEMALESS;\n\n# Standard fields\nDEFINE FIELD IF NOT EXISTS created_at ON knowledge_entity TYPE string;\nDEFINE FIELD IF NOT EXISTS updated_at ON knowledge_entity TYPE string;\n\n# Custom fields from the KnowledgeEntity struct\nDEFINE FIELD IF NOT EXISTS source_id ON knowledge_entity TYPE string;\nDEFINE FIELD IF NOT EXISTS name ON knowledge_entity TYPE string;\nDEFINE FIELD IF NOT EXISTS description ON knowledge_entity TYPE string;\n# KnowledgeEntityType is an enum, store as string\nDEFINE FIELD IF NOT EXISTS entity_type ON knowledge_entity TYPE string;\n# metadata is Option, store as object\nDEFINE FIELD IF NOT EXISTS metadata ON knowledge_entity TYPE option;\n\n# Define embedding as a standard array of floats for schema definition\nDEFINE FIELD IF NOT EXISTS embedding ON knowledge_entity TYPE array;\n# The specific vector nature is handled by the index definition below\n\nDEFINE FIELD IF NOT EXISTS user_id ON knowledge_entity TYPE string;\n\n# Indexes based on build_indexes and query patterns\n# The INDEX definition correctly specifies the vector properties\nDEFINE INDEX IF NOT EXISTS idx_embedding_entities ON knowledge_entity FIELDS embedding HNSW DIMENSION 1536;\nDEFINE INDEX IF NOT EXISTS knowledge_entity_user_id_idx ON knowledge_entity FIELDS user_id;\nDEFINE INDEX IF NOT EXISTS knowledge_entity_source_id_idx ON knowledge_entity FIELDS source_id;\nDEFINE INDEX IF NOT EXISTS knowledge_entity_entity_type_idx ON knowledge_entity FIELDS entity_type;\nDEFINE INDEX IF NOT EXISTS knowledge_entity_created_at_idx ON knowledge_entity FIELDS created_at; # For get_latest_knowledge_entities\n\n# Defines the schema for the 'message' table.\n\nDEFINE TABLE IF NOT EXISTS message SCHEMALESS;\n\n# Standard fields\nDEFINE FIELD IF NOT EXISTS created_at ON message TYPE string;\nDEFINE FIELD IF NOT EXISTS updated_at ON message TYPE string;\n\n# Custom fields from the Message struct\nDEFINE FIELD IF NOT EXISTS conversation_id ON message TYPE string;\n# MessageRole is an enum, store as string\nDEFINE FIELD IF NOT EXISTS role ON message TYPE string;\nDEFINE FIELD IF NOT EXISTS content ON message TYPE string;\n# references is Option>, store as array\nDEFINE FIELD IF NOT EXISTS references ON message TYPE option>;\n\n# Indexes based on query patterns (get_complete_conversation)\nDEFINE INDEX IF NOT EXISTS message_conversation_id_idx ON message FIELDS conversation_id;\nDEFINE INDEX IF NOT EXISTS message_updated_at_idx ON message FIELDS updated_at; # For ORDER BY\n\n# Defines the 'relates_to' edge table for KnowledgeRelationships.\n# Edges connect nodes, in this case knowledge_entity records.\n\n# Define the edge table itself, enforcing connections between knowledge_entity records\n# SCHEMAFULL requires all fields to be defined, maybe start with SCHEMALESS if metadata might vary\nDEFINE TABLE IF NOT EXISTS relates_to SCHEMALESS TYPE RELATION FROM knowledge_entity TO knowledge_entity;\n\n# Define the metadata field within the edge\n# RelationshipMetadata is a struct, store as object\nDEFINE FIELD IF NOT EXISTS metadata ON relates_to TYPE object;\n\n# Optionally, define fields within the metadata object for stricter schema (requires SCHEMAFULL on table)\n# DEFINE FIELD IF NOT EXISTS metadata.user_id ON relates_to TYPE string;\n# DEFINE FIELD IF NOT EXISTS metadata.source_id ON relates_to TYPE string;\n# DEFINE FIELD IF NOT EXISTS metadata.relationship_type ON relates_to TYPE string;\n\n# Add indexes based on query patterns (delete_relationships_by_source_id, get_knowledge_relationships)\nDEFINE INDEX IF NOT EXISTS relates_to_metadata_source_id_idx ON relates_to FIELDS metadata.source_id;\nDEFINE INDEX IF NOT EXISTS relates_to_metadata_user_id_idx ON relates_to FIELDS metadata.user_id;\n\nDEFINE TABLE OVERWRITE script_migration SCHEMAFULL\n PERMISSIONS\n FOR select FULL\n FOR create, update, delete NONE;\n\nDEFINE FIELD OVERWRITE script_name ON script_migration TYPE string;\nDEFINE FIELD OVERWRITE executed_at ON script_migration TYPE datetime VALUE time::now() READONLY;\n# Defines the schema for the 'system_settings' table.\n\nDEFINE TABLE IF NOT EXISTS system_settings SCHEMALESS;\n\n# Custom fields from the SystemSettings struct\nDEFINE FIELD IF NOT EXISTS registrations_enabled ON system_settings TYPE bool;\nDEFINE FIELD IF NOT EXISTS require_email_verification ON system_settings TYPE bool;\nDEFINE FIELD IF NOT EXISTS query_model ON system_settings TYPE string;\nDEFINE FIELD IF NOT EXISTS processing_model ON system_settings TYPE string;\nDEFINE FIELD IF NOT EXISTS query_system_prompt ON system_settings TYPE string;\nDEFINE FIELD IF NOT EXISTS ingestion_system_prompt ON system_settings TYPE string;\n\n# Defines the schema for the 'text_chunk' table.\n\nDEFINE TABLE IF NOT EXISTS text_chunk SCHEMALESS;\n\n# Standard fields\nDEFINE FIELD IF NOT EXISTS created_at ON text_chunk TYPE string;\nDEFINE FIELD IF NOT EXISTS updated_at ON text_chunk TYPE string;\n\n# Custom fields from the TextChunk struct\nDEFINE FIELD IF NOT EXISTS source_id ON text_chunk TYPE string;\nDEFINE FIELD IF NOT EXISTS chunk ON text_chunk TYPE string;\n\n# Define embedding as a standard array of floats for schema definition\nDEFINE FIELD IF NOT EXISTS embedding ON text_chunk TYPE array;\n# The specific vector nature is handled by the index definition below\n\nDEFINE FIELD IF NOT EXISTS user_id ON text_chunk TYPE string;\n\n# Indexes based on build_indexes and query patterns (delete_by_source_id)\n# The INDEX definition correctly specifies the vector properties\nDEFINE INDEX IF NOT EXISTS idx_embedding_chunks ON text_chunk FIELDS embedding HNSW DIMENSION 1536;\nDEFINE INDEX IF NOT EXISTS text_chunk_source_id_idx ON text_chunk FIELDS source_id;\nDEFINE INDEX IF NOT EXISTS text_chunk_user_id_idx ON text_chunk FIELDS user_id;\n\n# Defines the schema for the 'text_content' table.\n\nDEFINE TABLE IF NOT EXISTS text_content SCHEMALESS;\n\n# Standard fields\nDEFINE FIELD IF NOT EXISTS created_at ON text_content TYPE string;\nDEFINE FIELD IF NOT EXISTS updated_at ON text_content TYPE string;\n\n# Custom fields from the TextContent struct\nDEFINE FIELD IF NOT EXISTS text ON text_content TYPE string;\n# FileInfo is a struct, store as object\nDEFINE FIELD IF NOT EXISTS file_info ON text_content TYPE option;\n# UrlInfo is a struct, store as object\nDEFINE FIELD IF NOT EXISTS url_info ON text_content TYPE option;\nDEFINE FIELD IF NOT EXISTS instructions ON text_content TYPE string;\nDEFINE FIELD IF NOT EXISTS category ON text_content TYPE string;\nDEFINE FIELD IF NOT EXISTS user_id ON text_content TYPE string;\n\n# Indexes based on query patterns (get_latest_text_contents, get_text_contents_by_category)\nDEFINE INDEX IF NOT EXISTS text_content_user_id_idx ON text_content FIELDS user_id;\nDEFINE INDEX IF NOT EXISTS text_content_created_at_idx ON text_content FIELDS created_at;\nDEFINE INDEX IF NOT EXISTS text_content_category_idx ON text_content FIELDS category;\n\n# Defines the schema for the 'user' table.\n# NOTE: Authentication scope and access rules are defined in auth.surql\n\nDEFINE TABLE IF NOT EXISTS user SCHEMALESS;\n\n# Standard fields\nDEFINE FIELD IF NOT EXISTS created_at ON user TYPE string;\nDEFINE FIELD IF NOT EXISTS updated_at ON user TYPE string;\n\n# Custom fields from the User struct\nDEFINE FIELD IF NOT EXISTS email ON user TYPE string;\nDEFINE FIELD IF NOT EXISTS password ON user TYPE string; # Stores the hashed password\nDEFINE FIELD IF NOT EXISTS anonymous ON user TYPE bool;\nDEFINE FIELD IF NOT EXISTS api_key ON user TYPE option;\nDEFINE FIELD IF NOT EXISTS admin ON user TYPE bool;\nDEFINE FIELD IF NOT EXISTS timezone ON user TYPE string;\n\n# Indexes based on query patterns (find_by_email, find_by_api_key, unique constraint from setup_auth)\nDEFINE INDEX IF NOT EXISTS user_email_idx ON user FIELDS email UNIQUE;\nDEFINE INDEX IF NOT EXISTS user_api_key_idx ON user FIELDS api_key;\n","events":""} \ No newline at end of file diff --git a/common/schemas/analytics.surql b/common/schemas/analytics.surql new file mode 100644 index 0000000..a9f9073 --- /dev/null +++ b/common/schemas/analytics.surql @@ -0,0 +1,7 @@ +# Defines the schema for the 'analytics' table. + +DEFINE TABLE IF NOT EXISTS analytics SCHEMALESS; + +# Custom fields from the Analytics struct +DEFINE FIELD IF NOT EXISTS page_loads ON analytics TYPE number; +DEFINE FIELD IF NOT EXISTS visitors ON analytics TYPE number; diff --git a/common/schemas/auth.surql b/common/schemas/auth.surql new file mode 100644 index 0000000..b9a89e2 --- /dev/null +++ b/common/schemas/auth.surql @@ -0,0 +1,6 @@ +# Defines authentication scope and access rules. +# This mirrors the logic previously in SurrealDbClient::setup_auth + +DEFINE ACCESS IF NOT EXISTS account ON DATABASE TYPE RECORD + SIGNUP ( CREATE user SET email = $email, password = crypto::argon2::generate($password), anonymous = false, user_id = $user_id) # Ensure user_id is provided if needed + SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(password, $password) ); diff --git a/common/schemas/conversation.surql b/common/schemas/conversation.surql new file mode 100644 index 0000000..84e63b0 --- /dev/null +++ b/common/schemas/conversation.surql @@ -0,0 +1,15 @@ +# Defines the schema for the 'conversation' table. + +DEFINE TABLE IF NOT EXISTS conversation SCHEMALESS; + +# Standard fields +DEFINE FIELD IF NOT EXISTS created_at ON conversation TYPE string; +DEFINE FIELD IF NOT EXISTS updated_at ON conversation TYPE string; + +# Custom fields from the Conversation struct +DEFINE FIELD IF NOT EXISTS user_id ON conversation TYPE string; +DEFINE FIELD IF NOT EXISTS title ON conversation TYPE string; + +# Add indexes based on query patterns (get_complete_conversation ownership check, get_user_conversations) +DEFINE INDEX IF NOT EXISTS conversation_user_id_idx ON conversation FIELDS user_id; +DEFINE INDEX IF NOT EXISTS conversation_created_at_idx ON conversation FIELDS created_at; # For get_user_conversations ORDER BY diff --git a/common/schemas/file.surql b/common/schemas/file.surql new file mode 100644 index 0000000..49fec18 --- /dev/null +++ b/common/schemas/file.surql @@ -0,0 +1,19 @@ +# Defines the schema for the 'file' table (used by FileInfo). + +DEFINE TABLE IF NOT EXISTS file SCHEMALESS; + +# Standard fields +DEFINE FIELD IF NOT EXISTS created_at ON file TYPE string; +DEFINE FIELD IF NOT EXISTS updated_at ON file TYPE string; + +# Custom fields from the FileInfo struct +DEFINE FIELD IF NOT EXISTS sha256 ON file TYPE string; +DEFINE FIELD IF NOT EXISTS path ON file TYPE string; +DEFINE FIELD IF NOT EXISTS file_name ON file TYPE string; +DEFINE FIELD IF NOT EXISTS mime_type ON file TYPE string; +DEFINE FIELD IF NOT EXISTS user_id ON file TYPE string; + +# Indexes based on usage (get_by_sha, potentially user lookups) +# Using UNIQUE based on the logic in FileInfo::new to prevent duplicates +DEFINE INDEX IF NOT EXISTS file_sha256_idx ON file FIELDS sha256 UNIQUE; +DEFINE INDEX IF NOT EXISTS file_user_id_idx ON file FIELDS user_id; diff --git a/common/schemas/ingestion_task.surql b/common/schemas/ingestion_task.surql new file mode 100644 index 0000000..200de2e --- /dev/null +++ b/common/schemas/ingestion_task.surql @@ -0,0 +1,19 @@ +# Defines the schema for the 'ingestion_task' table (used by IngestionTask). + +DEFINE TABLE IF NOT EXISTS job SCHEMALESS; + +# Standard fields +DEFINE FIELD IF NOT EXISTS created_at ON job TYPE string; +DEFINE FIELD IF NOT EXISTS updated_at ON job TYPE string; + +# Custom fields from the IngestionTask struct +# IngestionPayload is complex, store as object +DEFINE FIELD IF NOT EXISTS content ON job TYPE object; +# IngestionTaskStatus can hold data (InProgress), store as object +DEFINE FIELD IF NOT EXISTS status ON job TYPE object; +DEFINE FIELD IF NOT EXISTS user_id ON job TYPE string; + +# Indexes explicitly defined in build_indexes and useful for get_unfinished_tasks +DEFINE INDEX IF NOT EXISTS idx_job_status ON job FIELDS status; +DEFINE INDEX IF NOT EXISTS idx_job_user ON job FIELDS user_id; +DEFINE INDEX IF NOT EXISTS idx_job_created ON job FIELDS created_at; diff --git a/common/schemas/knowledge_entity.surql b/common/schemas/knowledge_entity.surql new file mode 100644 index 0000000..902ffa4 --- /dev/null +++ b/common/schemas/knowledge_entity.surql @@ -0,0 +1,30 @@ +# Defines the schema for the 'knowledge_entity' table. + +DEFINE TABLE IF NOT EXISTS knowledge_entity SCHEMALESS; + +# Standard fields +DEFINE FIELD IF NOT EXISTS created_at ON knowledge_entity TYPE string; +DEFINE FIELD IF NOT EXISTS updated_at ON knowledge_entity TYPE string; + +# Custom fields from the KnowledgeEntity struct +DEFINE FIELD IF NOT EXISTS source_id ON knowledge_entity TYPE string; +DEFINE FIELD IF NOT EXISTS name ON knowledge_entity TYPE string; +DEFINE FIELD IF NOT EXISTS description ON knowledge_entity TYPE string; +# KnowledgeEntityType is an enum, store as string +DEFINE FIELD IF NOT EXISTS entity_type ON knowledge_entity TYPE string; +# metadata is Option, store as object +DEFINE FIELD IF NOT EXISTS metadata ON knowledge_entity TYPE option; + +# Define embedding as a standard array of floats for schema definition +DEFINE FIELD IF NOT EXISTS embedding ON knowledge_entity TYPE array; +# The specific vector nature is handled by the index definition below + +DEFINE FIELD IF NOT EXISTS user_id ON knowledge_entity TYPE string; + +# Indexes based on build_indexes and query patterns +# The INDEX definition correctly specifies the vector properties +DEFINE INDEX IF NOT EXISTS idx_embedding_entities ON knowledge_entity FIELDS embedding HNSW DIMENSION 1536; +DEFINE INDEX IF NOT EXISTS knowledge_entity_user_id_idx ON knowledge_entity FIELDS user_id; +DEFINE INDEX IF NOT EXISTS knowledge_entity_source_id_idx ON knowledge_entity FIELDS source_id; +DEFINE INDEX IF NOT EXISTS knowledge_entity_entity_type_idx ON knowledge_entity FIELDS entity_type; +DEFINE INDEX IF NOT EXISTS knowledge_entity_created_at_idx ON knowledge_entity FIELDS created_at; # For get_latest_knowledge_entities diff --git a/common/schemas/message.surql b/common/schemas/message.surql new file mode 100644 index 0000000..4681f05 --- /dev/null +++ b/common/schemas/message.surql @@ -0,0 +1,19 @@ +# Defines the schema for the 'message' table. + +DEFINE TABLE IF NOT EXISTS message SCHEMALESS; + +# Standard fields +DEFINE FIELD IF NOT EXISTS created_at ON message TYPE string; +DEFINE FIELD IF NOT EXISTS updated_at ON message TYPE string; + +# Custom fields from the Message struct +DEFINE FIELD IF NOT EXISTS conversation_id ON message TYPE string; +# MessageRole is an enum, store as string +DEFINE FIELD IF NOT EXISTS role ON message TYPE string; +DEFINE FIELD IF NOT EXISTS content ON message TYPE string; +# references is Option>, store as array +DEFINE FIELD IF NOT EXISTS references ON message TYPE option>; + +# Indexes based on query patterns (get_complete_conversation) +DEFINE INDEX IF NOT EXISTS message_conversation_id_idx ON message FIELDS conversation_id; +DEFINE INDEX IF NOT EXISTS message_updated_at_idx ON message FIELDS updated_at; # For ORDER BY diff --git a/common/schemas/relates_to.surql b/common/schemas/relates_to.surql new file mode 100644 index 0000000..4eda17f --- /dev/null +++ b/common/schemas/relates_to.surql @@ -0,0 +1,19 @@ +# Defines the 'relates_to' edge table for KnowledgeRelationships. +# Edges connect nodes, in this case knowledge_entity records. + +# Define the edge table itself, enforcing connections between knowledge_entity records +# SCHEMAFULL requires all fields to be defined, maybe start with SCHEMALESS if metadata might vary +DEFINE TABLE IF NOT EXISTS relates_to SCHEMALESS TYPE RELATION FROM knowledge_entity TO knowledge_entity; + +# Define the metadata field within the edge +# RelationshipMetadata is a struct, store as object +DEFINE FIELD IF NOT EXISTS metadata ON relates_to TYPE object; + +# Optionally, define fields within the metadata object for stricter schema (requires SCHEMAFULL on table) +# DEFINE FIELD IF NOT EXISTS metadata.user_id ON relates_to TYPE string; +# DEFINE FIELD IF NOT EXISTS metadata.source_id ON relates_to TYPE string; +# DEFINE FIELD IF NOT EXISTS metadata.relationship_type ON relates_to TYPE string; + +# Add indexes based on query patterns (delete_relationships_by_source_id, get_knowledge_relationships) +DEFINE INDEX IF NOT EXISTS relates_to_metadata_source_id_idx ON relates_to FIELDS metadata.source_id; +DEFINE INDEX IF NOT EXISTS relates_to_metadata_user_id_idx ON relates_to FIELDS metadata.user_id; diff --git a/common/schemas/script_migration.surql b/common/schemas/script_migration.surql new file mode 100644 index 0000000..9f66d88 --- /dev/null +++ b/common/schemas/script_migration.surql @@ -0,0 +1,7 @@ +DEFINE TABLE OVERWRITE script_migration SCHEMAFULL + PERMISSIONS + FOR select FULL + FOR create, update, delete NONE; + +DEFINE FIELD OVERWRITE script_name ON script_migration TYPE string; +DEFINE FIELD OVERWRITE executed_at ON script_migration TYPE datetime VALUE time::now() READONLY; diff --git a/common/schemas/system_settings.surql b/common/schemas/system_settings.surql new file mode 100644 index 0000000..af8daa8 --- /dev/null +++ b/common/schemas/system_settings.surql @@ -0,0 +1,11 @@ +# Defines the schema for the 'system_settings' table. + +DEFINE TABLE IF NOT EXISTS system_settings SCHEMALESS; + +# Custom fields from the SystemSettings struct +DEFINE FIELD IF NOT EXISTS registrations_enabled ON system_settings TYPE bool; +DEFINE FIELD IF NOT EXISTS require_email_verification ON system_settings TYPE bool; +DEFINE FIELD IF NOT EXISTS query_model ON system_settings TYPE string; +DEFINE FIELD IF NOT EXISTS processing_model ON system_settings TYPE string; +DEFINE FIELD IF NOT EXISTS query_system_prompt ON system_settings TYPE string; +DEFINE FIELD IF NOT EXISTS ingestion_system_prompt ON system_settings TYPE string; diff --git a/common/schemas/text_chunk.surql b/common/schemas/text_chunk.surql new file mode 100644 index 0000000..3bd9571 --- /dev/null +++ b/common/schemas/text_chunk.surql @@ -0,0 +1,23 @@ +# Defines the schema for the 'text_chunk' table. + +DEFINE TABLE IF NOT EXISTS text_chunk SCHEMALESS; + +# Standard fields +DEFINE FIELD IF NOT EXISTS created_at ON text_chunk TYPE string; +DEFINE FIELD IF NOT EXISTS updated_at ON text_chunk TYPE string; + +# Custom fields from the TextChunk struct +DEFINE FIELD IF NOT EXISTS source_id ON text_chunk TYPE string; +DEFINE FIELD IF NOT EXISTS chunk ON text_chunk TYPE string; + +# Define embedding as a standard array of floats for schema definition +DEFINE FIELD IF NOT EXISTS embedding ON text_chunk TYPE array; +# The specific vector nature is handled by the index definition below + +DEFINE FIELD IF NOT EXISTS user_id ON text_chunk TYPE string; + +# Indexes based on build_indexes and query patterns (delete_by_source_id) +# The INDEX definition correctly specifies the vector properties +DEFINE INDEX IF NOT EXISTS idx_embedding_chunks ON text_chunk FIELDS embedding HNSW DIMENSION 1536; +DEFINE INDEX IF NOT EXISTS text_chunk_source_id_idx ON text_chunk FIELDS source_id; +DEFINE INDEX IF NOT EXISTS text_chunk_user_id_idx ON text_chunk FIELDS user_id; diff --git a/common/schemas/text_content.surql b/common/schemas/text_content.surql new file mode 100644 index 0000000..abe104a --- /dev/null +++ b/common/schemas/text_content.surql @@ -0,0 +1,22 @@ +# Defines the schema for the 'text_content' table. + +DEFINE TABLE IF NOT EXISTS text_content SCHEMALESS; + +# Standard fields +DEFINE FIELD IF NOT EXISTS created_at ON text_content TYPE string; +DEFINE FIELD IF NOT EXISTS updated_at ON text_content TYPE string; + +# Custom fields from the TextContent struct +DEFINE FIELD IF NOT EXISTS text ON text_content TYPE string; +# FileInfo is a struct, store as object +DEFINE FIELD IF NOT EXISTS file_info ON text_content TYPE option; +# UrlInfo is a struct, store as object +DEFINE FIELD IF NOT EXISTS url_info ON text_content TYPE option; +DEFINE FIELD IF NOT EXISTS instructions ON text_content TYPE string; +DEFINE FIELD IF NOT EXISTS category ON text_content TYPE string; +DEFINE FIELD IF NOT EXISTS user_id ON text_content TYPE string; + +# Indexes based on query patterns (get_latest_text_contents, get_text_contents_by_category) +DEFINE INDEX IF NOT EXISTS text_content_user_id_idx ON text_content FIELDS user_id; +DEFINE INDEX IF NOT EXISTS text_content_created_at_idx ON text_content FIELDS created_at; +DEFINE INDEX IF NOT EXISTS text_content_category_idx ON text_content FIELDS category; diff --git a/common/schemas/user.surql b/common/schemas/user.surql new file mode 100644 index 0000000..bfd1865 --- /dev/null +++ b/common/schemas/user.surql @@ -0,0 +1,20 @@ +# Defines the schema for the 'user' table. +# NOTE: Authentication scope and access rules are defined in auth.surql + +DEFINE TABLE IF NOT EXISTS user SCHEMALESS; + +# Standard fields +DEFINE FIELD IF NOT EXISTS created_at ON user TYPE string; +DEFINE FIELD IF NOT EXISTS updated_at ON user TYPE string; + +# Custom fields from the User struct +DEFINE FIELD IF NOT EXISTS email ON user TYPE string; +DEFINE FIELD IF NOT EXISTS password ON user TYPE string; # Stores the hashed password +DEFINE FIELD IF NOT EXISTS anonymous ON user TYPE bool; +DEFINE FIELD IF NOT EXISTS api_key ON user TYPE option; +DEFINE FIELD IF NOT EXISTS admin ON user TYPE bool; +DEFINE FIELD IF NOT EXISTS timezone ON user TYPE string; + +# Indexes based on query patterns (find_by_email, find_by_api_key, unique constraint from setup_auth) +DEFINE INDEX IF NOT EXISTS user_email_idx ON user FIELDS email UNIQUE; +DEFINE INDEX IF NOT EXISTS user_api_key_idx ON user FIELDS api_key; diff --git a/common/src/storage/db.rs b/common/src/storage/db.rs index fdc4791..1b9801b 100644 --- a/common/src/storage/db.rs +++ b/common/src/storage/db.rs @@ -1,15 +1,18 @@ -use crate::error::AppError; - use super::types::{analytics::Analytics, system_settings::SystemSettings, StoredObject}; +use crate::error::AppError; use axum_session::{SessionConfig, SessionError, SessionStore}; use axum_session_surreal::SessionSurrealPool; use futures::Stream; +use include_dir::{include_dir, Dir}; use std::{ops::Deref, sync::Arc}; use surrealdb::{ engine::any::{connect, Any}, opt::auth::Root, Error, Notification, Surreal, }; +use surrealdb_migrations::MigrationRunner; + +static MIGRATIONS_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/"); #[derive(Clone)] pub struct SurrealDbClient { @@ -56,44 +59,60 @@ impl SurrealDbClient { .await } - pub async fn ensure_initialized(&self) -> Result<(), AppError> { - Self::build_indexes(self).await?; - Self::setup_auth(self).await?; - - Analytics::ensure_initialized(self).await?; - SystemSettings::ensure_initialized(self).await?; + /// Applies all pending database migrations found in the embedded MIGRATIONS_DIR. + /// + /// This function should be called during application startup, after connecting to + /// the database and selecting the appropriate namespace and database, but before + /// the application starts performing operations that rely on the schema. + pub async fn apply_migrations(&self) -> Result<(), AppError> { + // Instantiate the runner, load embedded files, and run 'up' + MigrationRunner::new(&self.client) + .load_files(&MIGRATIONS_DIR) + .up() + .await + .map_err(|e| AppError::Processing(e.to_string()))?; Ok(()) } - pub async fn setup_auth(&self) -> Result<(), Error> { - self.client.query( - "DEFINE TABLE user SCHEMALESS; - DEFINE INDEX unique_name ON TABLE user FIELDS email UNIQUE; - DEFINE ACCESS account ON DATABASE TYPE RECORD - SIGNUP ( CREATE user SET email = $email, password = crypto::argon2::generate($password), anonymous = false, user_id = $user_id) - SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(password, $password) );", - ) - .await?; - Ok(()) - } + // pub async fn ensure_initialized(&self) -> Result<(), AppError> { + // Self::build_indexes(self).await?; + // Self::setup_auth(self).await?; - pub async fn build_indexes(&self) -> Result<(), Error> { - self.client.query("DEFINE INDEX idx_embedding_chunks ON text_chunk FIELDS embedding HNSW DIMENSION 1536").await?; - self.client.query("DEFINE INDEX idx_embedding_entities ON knowledge_entity FIELDS embedding HNSW DIMENSION 1536").await?; + // Analytics::ensure_initialized(self).await?; + // SystemSettings::ensure_initialized(self).await?; - self.client - .query("DEFINE INDEX idx_job_status ON job FIELDS status") - .await?; - self.client - .query("DEFINE INDEX idx_job_user ON job FIELDS user_id") - .await?; - self.client - .query("DEFINE INDEX idx_job_created ON job FIELDS created_at") - .await?; + // Ok(()) + // } - Ok(()) - } + // pub async fn setup_auth(&self) -> Result<(), Error> { + // self.client.query( + // "DEFINE TABLE user SCHEMALESS; + // DEFINE INDEX unique_name ON TABLE user FIELDS email UNIQUE; + // DEFINE ACCESS account ON DATABASE TYPE RECORD + // SIGNUP ( CREATE user SET email = $email, password = crypto::argon2::generate($password), anonymous = false, user_id = $user_id) + // SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(password, $password) );", + // ) + // .await?; + // Ok(()) + // } + + // pub async fn build_indexes(&self) -> Result<(), Error> { + // self.client.query("DEFINE INDEX idx_embedding_chunks ON text_chunk FIELDS embedding HNSW DIMENSION 1536").await?; + // self.client.query("DEFINE INDEX idx_embedding_entities ON knowledge_entity FIELDS embedding HNSW DIMENSION 1536").await?; + + // self.client + // .query("DEFINE INDEX idx_job_status ON job FIELDS status") + // .await?; + // self.client + // .query("DEFINE INDEX idx_job_user ON job FIELDS user_id") + // .await?; + // self.client + // .query("DEFINE INDEX idx_job_created ON job FIELDS created_at") + // .await?; + + // Ok(()) + // } pub async fn rebuild_indexes(&self) -> Result<(), Error> { self.client @@ -222,7 +241,7 @@ mod tests { .expect("Failed to start in-memory surrealdb"); // Call your initialization - db.ensure_initialized() + db.apply_migrations() .await .expect("Failed to initialize schema"); @@ -268,25 +287,15 @@ mod tests { } #[tokio::test] - async fn test_setup_auth() { - let namespace = "test_ns"; - let database = &Uuid::new_v4().to_string(); // ensures isolation per test run - let db = SurrealDbClient::memory(namespace, database) - .await - .expect("Failed to start in-memory surrealdb"); - - // Should not panic or fail - db.setup_auth().await.expect("Failed to setup auth"); - } - - #[tokio::test] - async fn test_build_indexes() { + async fn test_applying_migrations() { let namespace = "test_ns"; let database = &Uuid::new_v4().to_string(); let db = SurrealDbClient::memory(namespace, database) .await .expect("Failed to start in-memory surrealdb"); - db.build_indexes().await.expect("Failed to build indexes"); + db.apply_migrations() + .await + .expect("Failed to build indexes"); } } diff --git a/common/src/storage/types/ingestion_task.rs b/common/src/storage/types/ingestion_task.rs index a7d4957..427e221 100644 --- a/common/src/storage/types/ingestion_task.rs +++ b/common/src/storage/types/ingestion_task.rs @@ -18,7 +18,7 @@ pub enum IngestionTaskStatus { Cancelled, } -stored_object!(IngestionTask, "job", { +stored_object!(IngestionTask, "ingestion_task", { content: IngestionPayload, status: IngestionTaskStatus, user_id: String diff --git a/common/src/storage/types/user.rs b/common/src/storage/types/user.rs index ab47967..0157381 100644 --- a/common/src/storage/types/user.rs +++ b/common/src/storage/types/user.rs @@ -199,7 +199,7 @@ impl User { .client .query( "UPDATE type::thing('user', $id) - SET api_key = NULL + SET api_key = test_string_nullish RETURN AFTER", ) .bind(("id", id.to_owned())) @@ -520,9 +520,9 @@ mod tests { .await .expect("Failed to start in-memory surrealdb"); - db.ensure_initialized() + db.apply_migrations() .await - .expect("Failed to setup the systemsettings"); + .expect("Failed to setup the migrations"); db } diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..2761f83 --- /dev/null +++ b/flake.nix @@ -0,0 +1,139 @@ +# flake.nix +{ + description = "Minne application flake"; + + # Specify the inputs for our flake (nixpkgs for packages, flake-utils for convenience) + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; # Or pin to a specific release/commit + flake-utils.url = "github:numtide/flake-utils"; + }; + + # Define the outputs of our flake (packages, apps, shells, etc.) + outputs = { + self, + nixpkgs, + flake-utils, + }: + # Use flake-utils to generate outputs for common systems (x86_64-linux, aarch64-linux, x86_64-darwin) + flake-utils.lib.eachDefaultSystem ( + system: let + # Get the package set for the current system + pkgs = nixpkgs.legacyPackages.${system}; + + # --- Minne Package Definition --- + # This is your core rust application build + minne-pkg = pkgs.rustPlatform.buildRustPackage { + pname = "minne"; + version = "0.1.0"; # Consider fetching this from Cargo.toml later + + # Source is the flake's root directory + src = self; + + # Assuming you switched to crates.io headless_chrome + cargoLock = { + lockFile = ./Cargo.lock; + # outputHashes = { ... }; # Only if other git dependencies still exist + }; + + nativeBuildInputs = [ + pkgs.pkg-config + pkgs.rustfmt + pkgs.makeWrapper # For the postInstall hook + ]; + buildInputs = [ + pkgs.openssl + pkgs.chromium # Runtime dependency for the browser + ]; + + # Wrap the actual executables to provide CHROME_BIN at runtime + postInstall = let + # Define path to nix-provided chromium executable + chromium_executable = "${pkgs.chromium}/bin/chromium"; + in '' + echo "Wrapping binaries in postInstall hook..." + ls -l $out/bin # Add this line to debug which binaries are present + wrapProgram $out/bin/main \ + --set CHROME_BIN "${chromium_executable}" + wrapProgram $out/bin/worker \ + --set CHROME_BIN "${chromium_executable}" + echo "Finished wrapping." + ''; + + meta = with pkgs.lib; { + description = "Minne Application"; + license = licenses.mit; # Adjust if needed + }; + }; + + # --- Docker Image Definition (using dockerTools) --- + minne-docker-image = pkgs.dockerTools.buildImage { + name = "minne"; # Docker image repository name + tag = minne-pkg.version; # Use the package version as the image tag + + # Include the runtime closure of our minne package in the image layers + # Also add bash for easier debugging inside the container + contents = [minne-pkg pkgs.bashInteractive]; + + # Configure the runtime behavior of the Docker image + config = { + # Set the default command to run when the container starts + # Assumes 'main' is your primary entrypoint + Cmd = ["${minne-pkg}/bin/main"]; + + # Add other Docker config as needed: + # ExposedPorts = { "8080/tcp" = {}; }; # Example port exposure + WorkingDir = "/data"; # Example working directory + # Volumes = { "/data" = {}; }; # Example volume mount point + Env = [ + # The wrapper should set CHROME_BIN, but you can add other env vars + "PATH=${pkgs.lib.makeBinPath [minne-pkg pkgs.coreutils]}" # Ensure coreutils are in PATH + "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" # Common requirement + ]; + }; + }; + in { + # --- Flake Outputs --- + + # Packages: Accessible via 'nix build .#minne' or '.#minne-docker' + packages = { + minne = minne-pkg; + minne-docker = minne-docker-image; + # Default package for 'nix build .' + default = self.packages.${system}.minne; + }; + + # Apps: Accessible via 'nix run .#main' or '.#worker' + apps = { + main = flake-utils.lib.mkApp { + drv = minne-pkg; + name = "main"; + }; + worker = flake-utils.lib.mkApp { + drv = minne-pkg; + name = "worker"; + }; + # Default app for 'nix run .' + default = self.apps.${system}.main; + }; + + # # Development Shell: Accessible via 'nix develop' + # devShells.default = pkgs.mkShell { + # # Use inputs from the main package derivation + # inputsFrom = [minne-pkg]; + # # Add development tools + # nativeBuildInputs = minne-pkg.nativeBuildInputs; + # buildInputs = + # minne-pkg.buildInputs + # ++ [ + # pkgs.cargo + # pkgs.rustc + # pkgs.clippy # Add other dev tools as needed + # ]; + # # Add shell hooks or env vars if needed + # # shellHook = '' + # # export MY_DEV_VAR="hello" + # # ''; + # }; + } + ); +} diff --git a/html-router/assets/style.css b/html-router/assets/style.css new file mode 100644 index 0000000..4fd52c5 --- /dev/null +++ b/html-router/assets/style.css @@ -0,0 +1,2 @@ +/*! tailwindcss v4.1.2 | MIT License | https://tailwindcss.com */ +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:rotateX(0);--tw-rotate-y:rotateY(0);--tw-rotate-z:rotateZ(0);--tw-skew-x:skewX(0);--tw-skew-y:skewY(0);--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-content:""}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-blue-500:oklch(62.3% .214 259.815);--color-blue-700:oklch(48.8% .243 264.376);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-700:oklch(37.3% .034 259.733);--color-neutral-700:oklch(37.1% 0 0);--color-neutral-800:oklch(26.9% 0 0);--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-md:28rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25/1.875);--text-5xl:3rem;--text-5xl--line-height:1;--text-6xl:3.75rem;--text-6xl--line-height:1;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--radius-md:.375rem;--radius-2xl:1rem;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}body{font-family:Satoshi,sans-serif}html{scrollbar-gutter:stable}*,:after,:before,::backdrop{border-color:var(--color-gray-200,currentColor)}::file-selector-button{border-color:var(--color-gray-200,currentColor)}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax: ""; inherits: true; initial-value: 0%;}:root:has(.modal-open,.modal[open],.modal:target,.modal-toggle:checked,.drawer:not([class*=drawer-open])>.drawer-toggle:checked){overflow:hidden}:root,[data-theme]{background-color:var(--root-bg,var(--color-base-100));color:var(--color-base-content)}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E")}.chat{--mask-chat:url("data:image/svg+xml,%3csvg width='13' height='13' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='M0 11.5004C0 13.0004 2 13.0004 2 13.0004H12H13V0.00036329L12.5 0C12.5 0 11.977 2.09572 11.8581 2.50033C11.6075 3.35237 10.9149 4.22374 9 5.50036C6 7.50036 0 10.0004 0 11.5004Z'/%3e%3c/svg%3e")}:where(:root:has(.modal-open,.modal[open],.modal:target,.modal-toggle:checked,.drawer:not(.drawer-open)>.drawer-toggle:checked)){scrollbar-gutter:stable;background-image:linear-gradient(var(--color-base-100),var(--color-base-100));--root-bg:color-mix(in srgb,var(--color-base-100),oklch(0% 0 0) 40%)}@supports (color:color-mix(in lab, red, red)){:where(:root:has(.modal-open,.modal[open],.modal:target,.modal-toggle:checked,.drawer:not(.drawer-open)>.drawer-toggle:checked)){--root-bg:color-mix(in srgb,var(--color-base-100),oklch(0% 0 0) 40%)}}}@layer components;@layer utilities{.modal{pointer-events:none;visibility:hidden;width:100%;max-width:none;height:100%;max-height:none;color:inherit;transition:transform .3s ease-out,visibility .3s allow-discrete,background-color .3s ease-out,opacity .1s ease-out;overscroll-behavior:contain;z-index:999;background-color:#0000;place-items:center;margin:0;padding:0;display:grid;position:fixed;inset:0;overflow:hidden}.modal::backdrop{display:none}.modal.modal-open,.modal[open],.modal:target{pointer-events:auto;visibility:visible;opacity:1;background-color:oklch(0% 0 0/.4);transition:transform .3s ease-out,background-color .3s ease-out,opacity .1s ease-out}:is(.modal.modal-open,.modal[open],.modal:target) .modal-box{opacity:1;translate:0;scale:1}@starting-style{.modal.modal-open,.modal[open],.modal:target{visibility:hidden;opacity:0}}.drawer-side{pointer-events:none;visibility:hidden;overscroll-behavior:contain;opacity:0;width:100%;transition:opacity .2s ease-out .1s allow-discrete,visibility .3s ease-out .1s allow-discrete;inset-inline-start:0;grid-template-rows:repeat(1,minmax(0,1fr));grid-template-columns:repeat(1,minmax(0,1fr));grid-row-start:1;grid-column-start:1;place-items:flex-start start;height:100dvh;display:grid;position:fixed;top:0;overflow:hidden}.drawer-side>.drawer-overlay{cursor:pointer;background-color:oklch(0% 0 0/.4);place-self:stretch stretch;position:sticky;top:0}.drawer-side>*{grid-row-start:1;grid-column-start:1}.drawer-side>:not(.drawer-overlay){will-change:transform;transition:translate .3s ease-out;translate:-100%}[dir=rtl] :is(.drawer-side>:not(.drawer-overlay)){translate:100%}.drawer-toggle{appearance:none;opacity:0;width:0;height:0;position:fixed}.drawer-toggle:checked~.drawer-side{pointer-events:auto;visibility:visible;opacity:1;overflow-y:auto}.drawer-toggle:checked~.drawer-side>:not(.drawer-overlay){translate:0%}.drawer-toggle:focus-visible~.drawer-content label.drawer-button{outline-offset:2px;outline:2px solid}.tooltip{--tt-bg:var(--color-neutral);--tt-off:calc(100% + .5rem);--tt-tail:calc(100% + 1px + .25rem);display:inline-block;position:relative}.tooltip>:where(.tooltip-content),.tooltip[data-tip]:before{border-radius:var(--radius-field);text-align:center;white-space:normal;max-width:20rem;color:var(--color-neutral-content);opacity:0;background-color:var(--tt-bg);pointer-events:none;z-index:1;--tw-content:attr(data-tip);content:var(--tw-content);width:max-content;padding-block:.25rem;padding-inline:.5rem;font-size:.875rem;line-height:1.25em;transition:opacity .2s cubic-bezier(.4,0,.2,1) 75ms,transform .2s cubic-bezier(.4,0,.2,1) 75ms;position:absolute}.tooltip:after{opacity:0;background-color:var(--tt-bg);content:"";pointer-events:none;--mask-tooltip:url("data:image/svg+xml,%3Csvg width='10' height='4' viewBox='0 0 8 4' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.500009 1C3.5 1 3.00001 4 5.00001 4C7 4 6.5 1 9.5 1C10 1 10 0.499897 10 0H0C-1.99338e-08 0.5 0 1 0.500009 1Z' fill='black'/%3E%3C/svg%3E%0A");width:.625rem;height:.25rem;-webkit-mask-position:-1px 0;mask-position:-1px 0;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-image:var(--mask-tooltip);-webkit-mask-image:var(--mask-tooltip);mask-image:var(--mask-tooltip);transition:opacity .2s cubic-bezier(.4,0,.2,1) 75ms,transform .2s cubic-bezier(.4,0,.2,1) 75ms;display:block;position:absolute}:is(.tooltip.tooltip-open,.tooltip[data-tip]:hover,.tooltip:hover,.tooltip:has(:focus-visible))>.tooltip-content,:is(.tooltip.tooltip-open,.tooltip[data-tip]:hover,.tooltip:hover,.tooltip:has(:focus-visible))[data-tip]:before,:is(.tooltip.tooltip-open,.tooltip[data-tip]:hover,.tooltip:hover,.tooltip:has(:focus-visible)):after{opacity:1;--tt-pos:0rem;transition:opacity .2s cubic-bezier(.4,0,.2,1),transform .2s cubic-bezier(.4,0,.2,1)}.tooltip>.tooltip-content,.tooltip[data-tip]:before{transform:translateX(-50%)translateY(var(--tt-pos,.25rem));inset:auto auto var(--tt-off)50%}.tooltip:after{transform:translateX(-50%)translateY(var(--tt-pos,.25rem));inset:auto auto var(--tt-tail)50%}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;text-wrap:balance;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:color-mix(in srgb,var(--color-base-content)10%,transparent);color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@supports (color:color-mix(in lab, red, red)){:is(.menu :where(li:not(.menu-title,.disabled)>:not(ul,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible){background-color:color-mix(in oklab,var(--color-base-content)10%,transparent)}}@media (forced-colors:active){:is(.menu :where(li:not(.menu-title,.disabled)>:not(ul,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:color-mix(in srgb,var(--color-base-content)10%,transparent);--tw-outline-style:none;outline-style:none;box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab,var(--color-base-content)10%,transparent)}}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto,calc(var(--noise)*100%);background-image:none,var(--fx-noise);outline-style:none}@media (forced-colors:active){:is(.menu :where(li)>:not(ul,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active){outline-offset:2px;outline:2px solid #0000}}:is(.menu :where(li)>:not(ul,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth)*3px)-2px var(--menu-active-bg)}.menu :where(li).menu-disabled{pointer-events:none;color:color-mix(in srgb,var(--color-base-content)20%,transparent)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled{color:color-mix(in oklab,var(--color-base-content)20%,transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.floating-label{display:block;position:relative}.floating-label input{display:block}.floating-label input::placeholder,.floating-label textarea::placeholder{transition:top .1s ease-out,translate .1s ease-out,scale .1s ease-out,opacity .1s ease-out}.floating-label>span{z-index:1;background-color:var(--color-base-100);opacity:0;inset-inline-start:.75rem;top:calc(var(--size-field,.25rem)*10/2);pointer-events:none;border-radius:2px;padding-inline:.25rem;font-size:.875rem;line-height:1;transition:top .1s ease-out,translate .1s ease-out,scale .1s ease-out,opacity .1s ease-out;position:absolute;translate:0 -50%}:is(.floating-label:focus-within,.floating-label:not(:has(input:placeholder-shown,textarea:placeholder-shown))) ::placeholder{opacity:0;pointer-events:auto;top:0;translate:-12.5% calc(-50% - .125em);scale:.75}:is(.floating-label:focus-within,.floating-label:not(:has(input:placeholder-shown,textarea:placeholder-shown)))>span{opacity:1;pointer-events:auto;top:0;translate:-12.5% calc(-50% - .125em);scale:.75}.floating-label:has(:disabled,[disabled])>span{opacity:0}.floating-label:has(.input-xs,.select-xs,.textarea-xs) span{top:calc(var(--size-field,.25rem)*6/2);font-size:.6875rem}.floating-label:has(.input-sm,.select-sm,.textarea-sm) span{top:calc(var(--size-field,.25rem)*8/2);font-size:.75rem}.floating-label:has(.input-md,.select-md,.textarea-md) span{top:calc(var(--size-field,.25rem)*10/2);font-size:.875rem}.floating-label:has(.input-lg,.select-lg,.textarea-lg) span{top:calc(var(--size-field,.25rem)*12/2);font-size:1.125rem}.floating-label:has(.input-xl,.select-xl,.textarea-xl) span{top:calc(var(--size-field,.25rem)*14/2);font-size:1.375rem}.btn{cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;padding-inline:var(--btn-p);color:var(--btn-fg);--tw-prose-links:var(--btn-fg);height:var(--size);font-size:var(--fontsize,.875rem);outline-color:var(--btn-color,var(--color-base-content));background-color:var(--btn-bg);background-size:auto,calc(var(--noise)*100%);background-image:none,var(--btn-noise);border-width:var(--border);border-style:solid;border-color:var(--btn-border);text-shadow:0 .5px oklch(100% 0 0/calc(var(--depth)*.15));box-shadow:0 .5px 0 .5px oklch(100% 0 0/calc(var(--depth)*6%))inset,var(--btn-shadow);--size:calc(var(--size-field,.25rem)*10);--btn-bg:var(--btn-color,var(--color-base-200));--btn-fg:var(--color-base-content);--btn-p:1rem;--btn-border:var(--btn-bg);--btn-shadow:0 3px 2px -2px var(--btn-bg),0 4px 3px -2px var(--btn-bg);--btn-noise:var(--fx-noise);border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}:where(.btn){width:unset}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab,var(--btn-bg),#000 calc(var(--depth)*5%));--btn-shadow:0 3px 2px -2px color-mix(in oklab,var(--btn-bg)calc(var(--depth)*30%),#0000),0 4px 3px -2px color-mix(in oklab,var(--btn-bg)calc(var(--depth)*30%),#0000)}}.prose .btn{text-decoration-line:none}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab,var(--btn-color,var(--color-base-200)),#000 7%)}}}.btn:focus-visible{outline-width:2px;outline-style:solid}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));--btn-shadow:0 0 0 0 oklch(0% 0 0/0),0 0 0 0 oklch(0% 0 0/0);translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab,var(--btn-color,var(--color-base-200)),#000 5%);--btn-border:color-mix(in oklab,var(--btn-color,var(--color-base-200)),#000 7%)}}.btn:is(:disabled,[disabled],.btn-disabled){pointer-events:none;--btn-border:#0000;--btn-noise:none;--btn-fg:color-mix(in srgb,var(--color-base-content)20%,#0000)}.btn:is(:disabled,[disabled],.btn-disabled):not(.btn-link,.btn-ghost){background-color:color-mix(in srgb,var(--color-base-content)10%,transparent);box-shadow:none}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],.btn-disabled):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab,var(--color-base-content)10%,transparent)}.btn:is(:disabled,[disabled],.btn-disabled){--btn-fg:color-mix(in oklch,var(--color-base-content)20%,#0000)}}@media (hover:hover){.btn:is(:disabled,[disabled],.btn-disabled):hover{pointer-events:none;background-color:color-mix(in srgb,var(--color-neutral)20%,transparent);--btn-border:#0000;--btn-fg:color-mix(in srgb,var(--color-base-content)20%,#0000)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],.btn-disabled):hover{background-color:color-mix(in oklab,var(--color-neutral)20%,transparent);--btn-fg:color-mix(in oklch,var(--color-base-content)20%,#0000)}}}.btn:is(input[type=checkbox],input[type=radio]){appearance:none}.btn:is(input[type=checkbox],input[type=radio]):after{content:attr(aria-label)}.btn:where(input:checked:not(.filter .btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);isolation:isolate}.loading{pointer-events:none;aspect-ratio:1;vertical-align:middle;width:calc(var(--size-selector,.25rem)*6);background-color:currentColor;display:inline-block;-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:100%;mask-size:100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.validator:user-valid{--input-color:var(--color-success)}.validator:user-valid:focus{--input-color:var(--color-success)}.validator:user-valid:checked{--input-color:var(--color-success)}.validator:user-valid[aria-checked=true]{--input-color:var(--color-success)}.validator:user-valid:focus-within{--input-color:var(--color-success)}.validator:has(:user-valid){--input-color:var(--color-success)}.validator:has(:user-valid):focus{--input-color:var(--color-success)}.validator:has(:user-valid):checked{--input-color:var(--color-success)}.validator:has(:user-valid)[aria-checked=true]{--input-color:var(--color-success)}.validator:has(:user-valid):focus-within{--input-color:var(--color-success)}.validator:user-invalid{--input-color:var(--color-error)}.validator:user-invalid:focus{--input-color:var(--color-error)}.validator:user-invalid:checked{--input-color:var(--color-error)}.validator:user-invalid[aria-checked=true]{--input-color:var(--color-error)}.validator:user-invalid:focus-within{--input-color:var(--color-error)}.validator:user-invalid~.validator-hint{visibility:visible;color:var(--color-error);display:block}.validator:has(:user-invalid){--input-color:var(--color-error)}.validator:has(:user-invalid):focus{--input-color:var(--color-error)}.validator:has(:user-invalid):checked{--input-color:var(--color-error)}.validator:has(:user-invalid)[aria-checked=true]{--input-color:var(--color-error)}.validator:has(:user-invalid):focus-within{--input-color:var(--color-error)}.validator:has(:user-invalid)~.validator-hint{visibility:visible;color:var(--color-error);display:block}.validator~.validator-hint{visibility:hidden}.visible{visibility:visible}.list{flex-direction:column;font-size:.875rem;display:flex}.list :where(.list-row){--list-grid-cols:minmax(0,auto)1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}.list :where(.list-row):has(.list-col-grow:first-child){--list-grid-cols:1fr}.list :where(.list-row):has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0,auto)1fr}.list :where(.list-row):has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0,auto)minmax(0,auto)1fr}.list :where(.list-row):has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0,auto)minmax(0,auto)minmax(0,auto)1fr}.list :where(.list-row):has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0,auto)minmax(0,auto)minmax(0,auto)minmax(0,auto)1fr}.list :where(.list-row):has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0,auto)minmax(0,auto)minmax(0,auto)minmax(0,auto)minmax(0,auto)1fr}.list :where(.list-row) :not(.list-col-wrap){grid-row-start:1}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border)solid;inset-inline:var(--radius-box);border-color:color-mix(in srgb,var(--color-base-content)5%,transparent);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab,var(--color-base-content)5%,transparent)}}.toggle{border:var(--border)solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p),var(--radius-selector-max)) + min(var(--border),var(--radius-selector-max)));padding:var(--toggle-p);--input-color:color-mix(in srgb,var(--color-base-content)50%,#0000);--toggle-p:.1875rem;--size:calc(var(--size-selector,.25rem)*6);width:calc((var(--size)*2) - (var(--border) + var(--toggle-p))*2);height:var(--size);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;transition:color .3s,grid-template-columns .2s;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab,currentColor calc(var(--depth)*10%),#0000)inset;--input-color:color-mix(in oklab,var(--color-base-content)50%,#0000)}}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:none}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:none}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);height:100%;box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1))inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1))inset,0 1px currentColor;background-color:currentColor;background-size:auto,calc(var(--noise)*100%);background-image:none,var(--fx-noise);grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1))inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1))inset,0 1px color-mix(in oklab,currentColor calc(var(--depth)*10%),#0000)}}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px*-1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border)solid currentColor;background-color:#0000}.input{cursor:text;border:var(--border)solid #0000;appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;width:clamp(3rem,20rem,100%);height:var(--size);border-color:var(--input-color);box-shadow:0 1px var(--input-color)inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1))inset;--size:calc(var(--size-field,.25rem)*10);--input-color:color-mix(in srgb,var(--color-base-content)20%,#0000);border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;font-size:.875rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1))inset;--input-color:color-mix(in oklab,var(--color-base-content)20%,#0000)}}.input:where(input){display:inline-flex}.input :where(input){appearance:none;background-color:#0000;border:none;width:100%;height:100%;display:inline-flex}.input :where(input):focus,.input :where(input):focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.input :where(input):focus,.input :where(input):focus-within){outline-offset:2px;outline:2px solid #0000}}.input :where(input[type=date]){display:inline-block}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color);outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@supports (color:color-mix(in lab, red, red)){:is(.input:focus,.input:focus-within){box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:color-mix(in srgb,var(--color-base-content)40%,transparent);box-shadow:none}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled])){color:color-mix(in oklab,var(--color-base-content)40%,transparent)}}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]))::placeholder{color:color-mix(in srgb,var(--color-base-content)20%,transparent)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab,var(--color-base-content)20%,transparent)}}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.input::-webkit-date-and-time-value{text-align:inherit}.input[type=number]::-webkit-inner-spin-button{margin-block:-.75rem;margin-inline-end:-.75rem}.input::-webkit-calendar-picker-indicator{position:absolute;inset-inline-end:.75em}.table{border-radius:var(--radius-box);text-align:left;width:100%;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:color-mix(in srgb,var(--color-base-content)60%,transparent);font-size:.875rem;font-weight:600}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab,var(--color-base-content)60%,transparent)}}.table :where(tfoot){border-top:var(--border)solid color-mix(in srgb,var(--color-base-content)5%,#0000)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot){border-top:var(--border)solid color-mix(in oklch,var(--color-base-content)5%,#0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr,tbody tr:not(:last-child)){border-bottom:var(--border)solid color-mix(in srgb,var(--color-base-content)5%,#0000)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr,tbody tr:not(:last-child)){border-bottom:var(--border)solid color-mix(in oklch,var(--color-base-content)5%,#0000)}}.chat-bubble{border-radius:var(--radius-field);background-color:var(--color-base-300);width:fit-content;color:var(--color-base-content);grid-row-end:3;min-width:2.5rem;max-width:90%;min-height:2rem;padding-block:.5rem;padding-inline:1rem;display:block;position:relative}.chat-bubble:before{background-color:inherit;content:"";width:.75rem;height:.75rem;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-image:var(--mask-chat);-webkit-mask-image:var(--mask-chat);mask-image:var(--mask-chat);position:absolute;bottom:0;-webkit-mask-position:0 -1px;mask-position:0 -1px;-webkit-mask-size:13px;mask-size:13px}.card{border-radius:var(--radius-box);outline-offset:2px;outline:0 solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.card:focus{outline-offset:2px;outline:2px solid #0000}}.card:focus-visible{outline-color:currentColor}.card :where(figure:first-child){border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card :where(figure:last-child){border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card:where(.card-border){border:var(--border)solid var(--color-base-200)}.card:where(.card-dash){border:var(--border)dashed var(--color-base-200)}.card.image-full{display:grid}.card.image-full>*{grid-row-start:1;grid-column-start:1}.card.image-full>.card-body{color:var(--color-neutral-content);position:relative}.card.image-full :where(figure){border-radius:inherit;overflow:hidden}.card.image-full>figure img{object-fit:cover;filter:brightness(28%);height:100%}.card figure{justify-content:center;align-items:center;display:flex}.card:has(>input:is(input[type=checkbox],input[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card:has(>:checked){outline:2px solid}.swap{cursor:pointer;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;place-content:center;display:inline-grid;position:relative}.swap input{appearance:none;border:none}.swap>*{grid-row-start:1;grid-column-start:1;transition-property:transform,rotate,opacity;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}.swap .swap-on,.swap .swap-indeterminate,.swap input:indeterminate~.swap-on,.swap input:is(:checked,:indeterminate)~.swap-off{opacity:0}.swap input:checked~.swap-on,.swap input:indeterminate~.swap-indeterminate{opacity:1}.select{border:var(--border)solid #0000;appearance:none;background-color:var(--color-base-100);vertical-align:middle;width:clamp(3rem,20rem,100%);height:var(--size);text-overflow:ellipsis;box-shadow:0 1px var(--input-color)inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1))inset;border-color:var(--input-color);--input-color:color-mix(in srgb,var(--color-base-content)20%,#0000);--size:calc(var(--size-field,.25rem)*10);background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-shrink:1;align-items:center;gap:.375rem;padding-inline:1rem 1.75rem;font-size:.875rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1))inset;--input-color:color-mix(in oklab,var(--color-base-content)20%,#0000)}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}.select select{appearance:none;background:inherit;border-radius:inherit;border-style:none;width:calc(100% + 2.75rem);height:calc(100% - 2px);margin-inline:-1rem -1.75rem;padding-inline:1rem 1.75rem}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select select:focus,.select select:focus-within){outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}.select:focus,.select:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color);outline:2px solid var(--input-color);outline-offset:2px}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within){box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:color-mix(in srgb,var(--color-base-content)40%,transparent)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled])){color:color-mix(in oklab,var(--color-base-content)40%,transparent)}}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]))::placeholder{color:color-mix(in srgb,var(--color-base-content)20%,transparent)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab,var(--color-base-content)20%,transparent)}}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}.menu-horizontal{flex-direction:row;display:inline-flex}.menu-horizontal>li:not(.menu-title)>details>ul{margin-inline-start:0;margin-top:1rem;padding-block:.5rem;padding-inline-end:.5rem;position:absolute}.menu-horizontal>li>details>ul:before{content:none}:where(.menu-horizontal>li:not(.menu-title)>details>ul){border-radius:var(--radius-box);background-color:var(--color-base-100);box-shadow:0 1px 3px oklch(0% 0 0/.1),0 1px 2px -1px oklch(0% 0 0/.1)}.checkbox{border:var(--border)solid var(--input-color,color-mix(in srgb,var(--color-base-content)20%,#0000));cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0/calc(var(--depth)*.1))inset,0 0 #0000 inset,0 0 #0000;--size:calc(var(--size-selector,.25rem)*6);width:var(--size);height:var(--size);background-size:auto,calc(var(--noise)*100%);background-image:none,var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;position:relative}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border)solid var(--input-color,color-mix(in oklab,var(--color-base-content)20%,#0000))}}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0/calc(var(--depth)*.1))inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1))inset,0 1px oklch(0% 0 0/calc(var(--depth)*.1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:none}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:none}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:none}.checkbox:disabled{cursor:not-allowed;opacity:.2}.drawer{grid-auto-columns:max-content auto;width:100%;display:grid;position:relative}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-y-0{inset-block:calc(var(--spacing)*0)}.chat-end{grid-template-columns:1fr auto;place-items:end}.chat-end .chat-header,.chat-end .chat-footer{grid-column-start:1}.chat-end .chat-image{grid-column-start:2}.chat-end .chat-bubble{border-end-end-radius:0;grid-column-start:1}.chat-end .chat-bubble:before{inset-inline-start:100%;transform:rotateY(180deg)}[dir=rtl] :is(.chat-end .chat-bubble):before{transform:rotateY(0)}.chat-start{grid-template-columns:auto 1fr;place-items:start}.chat-start .chat-header,.chat-start .chat-footer{grid-column-start:2}.chat-start .chat-image{grid-column-start:1}.chat-start .chat-bubble{border-end-start-radius:0;grid-column-start:2}.chat-start .chat-bubble:before{inset-inline-start:-.75rem;transform:rotateY(0)}[dir=rtl] :is(.chat-start .chat-bubble):before{transform:rotateY(180deg)}.\!top-2\.5{top:calc(var(--spacing)*2.5)!important}.top-0{top:calc(var(--spacing)*0)}.top-1{top:calc(var(--spacing)*1)}.top-6{top:calc(var(--spacing)*6)}.top-9{top:calc(var(--spacing)*9)}.right-0{right:calc(var(--spacing)*0)}.right-0\.5{right:calc(var(--spacing)*.5)}.bottom-0{bottom:calc(var(--spacing)*0)}.\!left-3{left:calc(var(--spacing)*3)!important}.left-0{left:calc(var(--spacing)*0)}.file-input{cursor:pointer;cursor:pointer;border:var(--border)solid #0000;appearance:none;background-color:var(--color-base-100);vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;width:clamp(3rem,20rem,100%);height:var(--size);border-color:var(--input-color);box-shadow:0 1px var(--input-color)inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1))inset;--size:calc(var(--size-field,.25rem)*10);--input-color:color-mix(in srgb,var(--color-base-content)20%,#0000);border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));align-items:center;padding-inline-end:.75rem;font-size:.875rem;line-height:2;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.file-input{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1))inset;--input-color:color-mix(in oklab,var(--color-base-content)20%,#0000)}}.file-input::file-selector-button{cursor:pointer;webkit-user-select:none;-webkit-user-select:none;user-select:none;height:calc(100% + var(--border)*2);margin-inline-end:1rem;margin-block:calc(var(--border)*-1);color:var(--btn-fg);border-width:var(--border);border-style:solid;border-color:var(--btn-border);background-color:var(--btn-bg);background-size:calc(var(--noise)*100%);background-image:var(--btn-noise);text-shadow:0 .5px oklch(1 0 0/calc(var(--depth)*.15));box-shadow:0 .5px 0 .5px white inset,var(--btn-shadow);--size:calc(var(--size-field,.25rem)*10);--btn-bg:var(--btn-color,var(--color-base-200));--btn-fg:var(--color-base-content);--btn-border:var(--btn-bg);--btn-shadow:0 3px 2px -2px var(--btn-bg),0 4px 3px -2px var(--btn-bg);--btn-noise:var(--fx-noise);border-start-start-radius:calc(var(--join-ss,var(--radius-field) - var(--border)));border-end-start-radius:calc(var(--join-es,var(--radius-field) - var(--border)));margin-inline-start:calc(var(--border)*-1);padding-inline:1rem;font-size:.875rem;font-weight:600}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{box-shadow:0 .5px 0 .5px color-mix(in oklab,color-mix(in oklab,white 30%,var(--btn-bg))calc(var(--depth)*20%),#0000)inset,var(--btn-shadow);--btn-border:color-mix(in oklab,var(--btn-bg),#000 5%);--btn-shadow:0 3px 2px -2px color-mix(in oklab,var(--btn-bg)30%,#0000),0 4px 3px -2px color-mix(in oklab,var(--btn-bg)30%,#0000)}}.file-input:focus{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color);outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@supports (color:color-mix(in lab, red, red)){.file-input:focus{box-shadow:0 1px color-mix(in oklab,var(--input-color)10%,#0000)}}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);box-shadow:none;color:color-mix(in srgb,var(--color-base-content)20%,#0000)}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:color-mix(in srgb,var(--color-base-content)20%,transparent)}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab,var(--color-base-content)20%,transparent)}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled])){color:color-mix(in oklch,var(--color-base-content)20%,#0000)}}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);--btn-border:#0000;--btn-noise:none;--btn-fg:color-mix(in srgb,var(--color-base-content)20%,#0000)}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{--btn-fg:color-mix(in oklch,var(--color-base-content)20%,#0000)}}.textarea{border:var(--border)solid #0000;appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;border-color:var(--input-color);width:clamp(3rem,20rem,100%);min-height:5rem;box-shadow:0 1px var(--input-color)inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1))inset;--input-color:color-mix(in srgb,var(--color-base-content)20%,#0000);flex-shrink:1;padding-block:.5rem;padding-inline:.75rem;font-size:.875rem}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1))inset;--input-color:color-mix(in oklab,var(--color-base-content)20%,#0000)}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.textarea textarea:focus,.textarea textarea:focus-within){outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color);outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@supports (color:color-mix(in lab, red, red)){:is(.textarea:focus,.textarea:focus-within){box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:color-mix(in srgb,var(--color-base-content)40%,transparent);box-shadow:none}@supports (color:color-mix(in lab, red, red)){:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])){color:color-mix(in oklab,var(--color-base-content)40%,transparent)}}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder{color:color-mix(in srgb,var(--color-base-content)20%,transparent)}@supports (color:color-mix(in lab, red, red)){:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab,var(--color-base-content)20%,transparent)}}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.modal-backdrop{color:#0000;z-index:-1;grid-row-start:1;grid-column-start:1;place-self:stretch stretch;display:grid}.modal-backdrop button{cursor:pointer}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-\[9999\]{z-index:9999}.modal-box{background-color:var(--color-base-100);border-top-left-radius:var(--modal-tl,var(--radius-box));border-top-right-radius:var(--modal-tr,var(--radius-box));border-bottom-left-radius:var(--modal-bl,var(--radius-box));border-bottom-right-radius:var(--modal-br,var(--radius-box));opacity:0;overscroll-behavior:contain;grid-row-start:1;grid-column-start:1;width:91.6667%;max-width:32rem;max-height:100vh;padding:1.5rem;transition:translate .3s ease-out,scale .3s ease-out,opacity .2s ease-out 50ms,box-shadow .3s ease-out;overflow-y:auto;scale:95%;box-shadow:0 25px 50px -12px oklch(0% 0 0/.25)}.drawer-content{grid-row-start:1;grid-column-start:2;min-width:0}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-desc{white-space:nowrap;color:color-mix(in srgb,var(--color-base-content)60%,transparent);grid-column-start:1;font-size:.75rem}@supports (color:color-mix(in lab, red, red)){.stat-desc{color:color-mix(in oklab,var(--color-base-content)60%,transparent)}}.stat-title{white-space:nowrap;color:color-mix(in srgb,var(--color-base-content)60%,transparent);grid-column-start:1;font-size:.75rem}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab,var(--color-base-content)60%,transparent)}}.list-col-wrap{grid-row-start:2}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);flex-direction:row;align-self:stretch;align-items:center;display:flex}.divider:before,.divider:after{content:"";background-color:color-mix(in srgb,var(--color-base-content)10%,transparent);flex-grow:1;width:100%;height:.125rem}@supports (color:color-mix(in lab, red, red)){:is(){background-color:color-mix(in oklab,var(--color-base-content)10%,transparent)}}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.mx-auto{margin-inline:auto}.input-md{--size:calc(var(--size-field,.25rem)*10);font-size:.875rem}.input-md[type=number]::-webkit-inner-spin-button{margin-block:-.75rem;margin-inline-end:-.75rem}.input-sm{--size:calc(var(--size-field,.25rem)*8);font-size:.75rem}.input-sm[type=number]::-webkit-inner-spin-button{margin-block:-.5rem;margin-inline-end:-.75rem}.my-2{margin-block:calc(var(--spacing)*2)}.my-4{margin-block:calc(var(--spacing)*4)}.my-10{margin-block:calc(var(--spacing)*10)}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab,currentColor 60%,transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border)solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border)solid color-mix(in oklab,currentColor 10%,#0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border)solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border)solid color-mix(in oklab,currentColor 10%,#0000)}}.prose{color:var(--tw-prose-body);--tw-prose-body:oklch(37.3% .034 259.733);--tw-prose-headings:oklch(21% .034 264.665);--tw-prose-lead:oklch(44.6% .03 256.802);--tw-prose-links:oklch(21% .034 264.665);--tw-prose-bold:oklch(21% .034 264.665);--tw-prose-counters:oklch(55.1% .027 264.364);--tw-prose-bullets:oklch(87.2% .01 258.338);--tw-prose-hr:oklch(92.8% .006 264.531);--tw-prose-quotes:oklch(21% .034 264.665);--tw-prose-quote-borders:oklch(92.8% .006 264.531);--tw-prose-captions:oklch(55.1% .027 264.364);--tw-prose-kbd:oklch(21% .034 264.665);--tw-prose-kbd-shadows:NaN NaN NaN;--tw-prose-code:oklch(21% .034 264.665);--tw-prose-pre-code:oklch(92.8% .006 264.531);--tw-prose-pre-bg:oklch(27.8% .033 256.848);--tw-prose-th-borders:oklch(87.2% .01 258.338);--tw-prose-td-borders:oklch(92.8% .006 264.531);--tw-prose-invert-body:oklch(87.2% .01 258.338);--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:oklch(70.7% .022 261.325);--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:oklch(70.7% .022 261.325);--tw-prose-invert-bullets:oklch(44.6% .03 256.802);--tw-prose-invert-hr:oklch(37.3% .034 259.733);--tw-prose-invert-quotes:oklch(96.7% .003 264.542);--tw-prose-invert-quote-borders:oklch(37.3% .034 259.733);--tw-prose-invert-captions:oklch(70.7% .022 261.325);--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:255 255 255;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:oklch(87.2% .01 258.338);--tw-prose-invert-pre-bg:#00000080;--tw-prose-invert-th-borders:oklch(44.6% .03 256.802);--tw-prose-invert-td-borders:oklch(37.3% .034 259.733);max-width:90ch;font-size:1rem;line-height:1.75}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);margin-top:1.2em;margin-bottom:1.2em;font-size:1.25em;line-height:1.6}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);font-weight:500;text-decoration:underline}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em;list-style-type:decimal}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em;list-style-type:disc}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-counters);font-weight:400}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.25em;font-weight:600}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em;font-style:italic;font-weight:500}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:0;margin-bottom:.888889em;font-size:2.25em;font-weight:800;line-height:1.11111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:900}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:2em;margin-bottom:1em;font-size:1.5em;font-weight:700;line-height:1.33333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:800}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.6em;margin-bottom:.6em;font-size:1.25em;font-weight:600;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.5em;margin-bottom:.5em;font-weight:600;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em;display:block}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows)/10%),0 3px 0 rgb(var(--tw-prose-kbd-shadows)/10%);padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;border-radius:.3125rem;padding-inline-start:.375em;font-family:inherit;font-size:.875em;font-weight:500}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-size:.875em;font-weight:600}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before,.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);padding-top:.857143em;padding-inline-end:1.14286em;padding-bottom:.857143em;border-radius:.375rem;margin-top:1.71429em;margin-bottom:1.71429em;padding-inline-start:1.14286em;font-size:.875em;font-weight:400;line-height:1.71429;overflow-x:auto}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit;background-color:#0000;border-width:0;border-radius:0;padding:0}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before,.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){table-layout:auto;width:100%;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.71429}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);vertical-align:bottom;padding-inline-end:.571429em;padding-bottom:.571429em;padding-inline-start:.571429em;font-weight:600}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);margin-top:.857143em;font-size:.875em;line-height:1.42857}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.571429em;padding-inline-end:.571429em;padding-bottom:.571429em;padding-inline-start:.571429em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.modal-action{justify-content:flex-end;gap:.5rem;margin-top:1.5rem;display:flex}.validator-hint{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));margin-top:.5rem}.\!mt-1{margin-top:calc(var(--spacing)*1)!important}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-6{margin-top:calc(var(--spacing)*6)}.mt-8{margin-top:calc(var(--spacing)*8)}.mt-10{margin-top:calc(var(--spacing)*10)}.mt-14{margin-top:calc(var(--spacing)*14)}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-2{margin-right:calc(var(--spacing)*2)}.fieldset-legend{color:var(--color-base-content);justify-content:space-between;align-items:center;gap:.5rem;margin-bottom:-.25rem;padding-block:.5rem;font-weight:600;display:flex}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.mb-10{margin-bottom:calc(var(--spacing)*10)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border)solid var(--badge-color,var(--color-base-200));width:fit-content;padding-inline:calc(.25rem*3 - var(--border));background-size:auto,calc(var(--noise)*100%);background-image:none,var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem)*6);height:var(--size);justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.badge.badge-outline{--badge-fg:var(--badge-color);--badge-bg:#0000;background-image:none}.badge.badge-dash{--badge-fg:var(--badge-color);--badge-bg:#0000;background-image:none;border-style:dashed}.badge.badge-soft{color:var(--badge-color,var(--color-base-content));background-color:var(--badge-color,var(--color-base-content));border-color:var(--badge-color,var(--color-base-content));background-image:none}@supports (color:color-mix(in lab, red, red)){.badge.badge-soft{background-color:color-mix(in oklab,var(--badge-color,var(--color-base-content))8%,var(--color-base-100));border-color:color-mix(in oklab,var(--badge-color,var(--color-base-content))10%,var(--color-base-100))}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border)dashed currentColor;border-block-end:none}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border)dashed color-mix(in oklab,currentColor 10%,#0000)}}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.card-body :where(p){flex-grow:1}.fieldset{grid-template-columns:1fr;grid-auto-rows:max-content;gap:.375rem;padding-block:.25rem;font-size:.75rem;display:grid}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.chat{column-gap:.75rem;padding-block:.25rem;display:grid}:root .prose{--tw-prose-body:color-mix(in srgb,var(--color-base-content)80%,#0000);--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:color-mix(in srgb,var(--color-base-content)50%,#0000);--tw-prose-hr:color-mix(in srgb,var(--color-base-content)20%,#0000);--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:color-mix(in srgb,var(--color-base-content)20%,#0000);--tw-prose-captions:color-mix(in srgb,var(--color-base-content)50%,#0000);--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:color-mix(in srgb,var(--color-base-content)50%,#0000);--tw-prose-td-borders:color-mix(in srgb,var(--color-base-content)20%,#0000);--tw-prose-kbd:color-mix(in srgb,var(--color-base-content)80%,#0000)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab,var(--color-base-content)80%,#0000);--tw-prose-bullets:color-mix(in oklab,var(--color-base-content)50%,#0000);--tw-prose-hr:color-mix(in oklab,var(--color-base-content)20%,#0000);--tw-prose-quote-borders:color-mix(in oklab,var(--color-base-content)20%,#0000);--tw-prose-captions:color-mix(in oklab,var(--color-base-content)50%,#0000);--tw-prose-th-borders:color-mix(in oklab,var(--color-base-content)50%,#0000);--tw-prose-td-borders:color-mix(in oklab,var(--color-base-content)20%,#0000);--tw-prose-kbd:color-mix(in oklab,var(--color-base-content)80%,#0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border)solid var(--color-base-300);font-weight:inherit;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.table{display:table}.btn-square{width:var(--size);height:var(--size);padding-inline:0}.size-5{width:calc(var(--spacing)*5);height:calc(var(--spacing)*5)}.size-6{width:calc(var(--spacing)*6);height:calc(var(--spacing)*6)}.size-10{width:calc(var(--spacing)*10);height:calc(var(--spacing)*10)}.h-3{height:calc(var(--spacing)*3)}.h-5{height:calc(var(--spacing)*5)}.h-24{height:calc(var(--spacing)*24)}.h-32{height:calc(var(--spacing)*32)}.h-96{height:calc(var(--spacing)*96)}.h-\[calc\(100vh-170px\)\]{height:calc(100vh - 170px)}.h-full{height:100%}.h-screen{height:100vh}.max-h-\[95\%\]{max-height:95%}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-\[95\%\]{min-height:95%}.min-h-\[100dvh\]{min-height:100dvh}.min-h-\[200px\]{min-height:200px}.loading-sm{width:calc(var(--size-selector,.25rem)*5)}.loading-xs{width:calc(var(--size-selector,.25rem)*4)}.w-3{width:calc(var(--spacing)*3)}.w-5{width:calc(var(--spacing)*5)}.w-11\/12{width:91.6667%}.w-40{width:calc(var(--spacing)*40)}.w-64{width:calc(var(--spacing)*64)}.w-72{width:calc(var(--spacing)*72)}.w-full{width:100%}.\!max-w-none{max-width:none!important}.max-w-3xl{max-width:var(--container-3xl)}.max-w-\[20ch\]{max-width:20ch}.max-w-\[90ch\]{max-width:90ch}.max-w-\[160px\]{max-width:160px}.max-w-full{max-width:100%}.max-w-xs{max-width:var(--container-xs)}.min-w-72{min-width:calc(var(--spacing)*72)}.flex-1{flex:1}.flex-none{flex:none}.flex-shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.scale-75{--tw-scale-x:75%;--tw-scale-y:75%;--tw-scale-z:75%;scale:var(--tw-scale-x)var(--tw-scale-y)}.swap-rotate .swap-on,.swap-rotate input:indeterminate~.swap-on{rotate:45deg}.swap-rotate input:is(:checked,:indeterminate)~.swap-on,.swap-rotate.swap-active .swap-on{rotate:none}.swap-rotate input:is(:checked,:indeterminate)~.swap-off,.swap-rotate.swap-active .swap-off{rotate:-45deg}.transform{transform:var(--tw-rotate-x)var(--tw-rotate-y)var(--tw-rotate-z)var(--tw-skew-x)var(--tw-skew-y)}.animate-pulse{animation:var(--animate-pulse)}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.stats-vertical{grid-auto-flow:row;overflow-y:auto}.stats-vertical .stat:not(:last-child){border-inline-end:none;border-block-end:var(--border)dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stats-vertical .stat:not(:last-child){border-block-end:var(--border)dashed color-mix(in oklab,currentColor 10%,#0000)}}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-start{justify-content:flex-start}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*4)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-x-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-clip{overflow:clip}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.rounded-md{border-radius:var(--radius-md)}.rounded-t-md{border-top-left-radius:var(--radius-md);border-top-right-radius:var(--radius-md)}.rounded-t-none{border-top-left-radius:0;border-top-right-radius:0}.rounded-b-none{border-bottom-right-radius:0;border-bottom-left-radius:0}.border{border-style:var(--tw-border-style);border-width:1px}.textarea-ghost{box-shadow:none;background-color:#0000;border-color:#0000}.textarea-ghost:focus,.textarea-ghost:focus-within{background-color:var(--color-base-100);color:var(--color-base-content);box-shadow:none;border-color:#0000}.border-base-content\/5{border-color:color-mix(in srgb,var(--color-base-content)5%,transparent)}@supports (color:color-mix(in lab, red, red)){.border-base-content\/5{border-color:color-mix(in oklab,var(--color-base-content)5%,transparent)}}.border-neutral-700{border-color:var(--color-neutral-700)}.bg-accent{background-color:var(--color-accent)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.bg-neutral-800{background-color:var(--color-neutral-800)}.bg-secondary{background-color:var(--color-secondary)}.bg-white{background-color:var(--color-white)}.bg-linear-to-r{--tw-gradient-position:to right;background-image:linear-gradient(var(--tw-gradient-stops))}@supports (background-image:linear-gradient(in lab, red, red)){.bg-linear-to-r{--tw-gradient-position:to right in oklab}}.from-primary{--tw-gradient-from:var(--color-primary);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-secondary{--tw-gradient-to:var(--color-secondary);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.loading-dots{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='4' cy='12' r='3'%3E%3Canimate attributeName='cy' values='12;6;12;12' keyTimes='0;0.286;0.571;1' dur='1.05s' repeatCount='indefinite' keySplines='.33,0,.66,.33;.33,.66,.66,1'/%3E%3C/circle%3E%3Ccircle cx='12' cy='12' r='3'%3E%3Canimate attributeName='cy' values='12;6;12;12' keyTimes='0;0.286;0.571;1' dur='1.05s' repeatCount='indefinite' keySplines='.33,0,.66,.33;.33,.66,.66,1' begin='0.1s'/%3E%3C/circle%3E%3Ccircle cx='20' cy='12' r='3'%3E%3Canimate attributeName='cy' values='12;6;12;12' keyTimes='0;0.286;0.571;1' dur='1.05s' repeatCount='indefinite' keySplines='.33,0,.66,.33;.33,.66,.66,1' begin='0.2s'/%3E%3C/circle%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='4' cy='12' r='3'%3E%3Canimate attributeName='cy' values='12;6;12;12' keyTimes='0;0.286;0.571;1' dur='1.05s' repeatCount='indefinite' keySplines='.33,0,.66,.33;.33,.66,.66,1'/%3E%3C/circle%3E%3Ccircle cx='12' cy='12' r='3'%3E%3Canimate attributeName='cy' values='12;6;12;12' keyTimes='0;0.286;0.571;1' dur='1.05s' repeatCount='indefinite' keySplines='.33,0,.66,.33;.33,.66,.66,1' begin='0.1s'/%3E%3C/circle%3E%3Ccircle cx='20' cy='12' r='3'%3E%3Canimate attributeName='cy' values='12;6;12;12' keyTimes='0;0.286;0.571;1' dur='1.05s' repeatCount='indefinite' keySplines='.33,0,.66,.33;.33,.66,.66,1' begin='0.2s'/%3E%3C/circle%3E%3C/svg%3E")}.loading-spinner{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E")}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.fill-current{fill:currentColor}.p-0{padding:calc(var(--spacing)*0)}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.menu-title{color:color-mix(in srgb,var(--color-base-content)40%,transparent);padding-block:.5rem;padding-inline:.75rem;font-size:.875rem;font-weight:600}@supports (color:color-mix(in lab, red, red)){.menu-title{color:color-mix(in oklab,var(--color-base-content)40%,transparent)}}.badge-xs{--size:calc(var(--size-selector,.25rem)*4);padding-inline:calc(.25rem*2 - var(--border));font-size:.625rem}.container{padding-inline:10px}@media (min-width:40rem){.container{padding-inline:2rem}}@media (min-width:64rem){.container{padding-inline:4rem}}@media (min-width:80rem){.container{padding-inline:5rem}}@media (min-width:96rem){.container{padding-inline:6rem}}.px-1{padding-inline:calc(var(--spacing)*1)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-2{padding-block:calc(var(--spacing)*2)}.py-4{padding-block:calc(var(--spacing)*4)}.pt-10{padding-top:calc(var(--spacing)*10)}.pr-3{padding-right:calc(var(--spacing)*3)}.pr-8{padding-right:calc(var(--spacing)*8)}.pr-12{padding-right:calc(var(--spacing)*12)}.pb-0{padding-bottom:calc(var(--spacing)*0)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.pl-3{padding-left:calc(var(--spacing)*3)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.link-primary{color:var(--color-primary)}@media (hover:hover){.link-primary:hover{color:color-mix(in srgb,var(--color-primary)80%,#000)}@supports (color:color-mix(in lab, red, red)){.link-primary:hover{color:color-mix(in oklab,var(--color-primary)80%,#000)}}}.text-accent{color:var(--color-accent)}.text-accent-content{color:var(--color-accent-content)}.text-base-content{color:var(--color-base-content)}.text-base-content\/60{color:color-mix(in srgb,var(--color-base-content)60%,transparent)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/60{color:color-mix(in oklab,var(--color-base-content)60%,transparent)}}.text-blue-500{color:var(--color-blue-500)}.text-error{color:var(--color-error)}.text-gray-500{color:var(--color-gray-500)}.text-primary{color:var(--color-primary)}.text-primary-content{color:var(--color-primary-content)}.text-secondary{color:var(--color-secondary)}.text-secondary-content{color:var(--color-secondary-content)}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.opacity-60{opacity:.6}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.btn-ghost:not(.btn-active,:hover,:active:focus,:focus-visible){--btn-shadow:"";--btn-bg:#0000;--btn-border:#0000;--btn-noise:none}.btn-ghost:not(.btn-active,:hover,:active:focus,:focus-visible):not(:disabled,[disabled],.btn-disabled){--btn-fg:currentColor;outline-color:currentColor}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.btn-outline:not(.btn-active,:hover,:active:focus,:focus-visible,:disabled,[disabled],.btn-disabled,:checked){--btn-shadow:"";--btn-bg:#0000;--btn-fg:var(--btn-color);--btn-border:var(--btn-color);--btn-noise:none}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem)*8)}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem)*6)}.badge-accent{--badge-color:var(--color-accent);--badge-fg:var(--color-accent-content)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-primary{--badge-color:var(--color-primary);--badge-fg:var(--color-primary-content)}.badge-secondary{--badge-color:var(--color-secondary);--badge-fg:var(--color-secondary-content)}.btn-accent{--btn-color:var(--color-accent);--btn-fg:var(--color-accent-content)}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content)}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}.btn-secondary{--btn-color:var(--color-secondary);--btn-fg:var(--color-secondary-content)}@media (hover:hover){.hover\:cursor-pointer:hover{cursor:pointer}.hover\:text-blue-700:hover{color:var(--color-blue-700)}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:bg-base-200:focus{background-color:var(--color-base-200)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}@media (min-width:40rem){.sm\:mt-0{margin-top:calc(var(--spacing)*0)}.sm\:mt-4{margin-top:calc(var(--spacing)*4)}.sm\:h-\[calc\(100vh-190px\)\]{height:calc(100vh - 190px)}.sm\:max-w-md{max-width:var(--container-md)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:rounded-b-2xl{border-bottom-right-radius:var(--radius-2xl);border-bottom-left-radius:var(--radius-2xl)}.sm\:px-0{padding-inline:calc(var(--spacing)*0)}.sm\:pb-4{padding-bottom:calc(var(--spacing)*4)}.sm\:text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:64rem){.lg\:drawer-open>.drawer-side{overflow-y:auto}.lg\:drawer-open>.drawer-toggle{display:none}.lg\:drawer-open>.drawer-toggle~.drawer-side{pointer-events:auto;visibility:visible;overscroll-behavior:auto;opacity:1;width:auto;display:block;position:sticky}.lg\:drawer-open>.drawer-toggle~.drawer-side>.drawer-overlay{cursor:default;background-color:#0000}.lg\:drawer-open>.drawer-toggle~.drawer-side>:not(.drawer-overlay),[dir=rtl] :is(.lg\:drawer-open>.drawer-toggle~.drawer-side>:not(.drawer-overlay)){translate:0%}.lg\:drawer-open>.drawer-toggle:checked~.drawer-side{pointer-events:auto;visibility:visible}.lg\:hidden{display:none}.lg\:stats-horizontal{grid-auto-flow:column;overflow-x:auto}.lg\:stats-horizontal .stat:not(:last-child){border-inline-end:var(--border)dashed currentColor;border-block-end:none}@supports (color:color-mix(in lab, red, red)){.lg\:stats-horizontal .stat:not(:last-child){border-inline-end:var(--border)dashed color-mix(in oklab,currentColor 10%,#0000)}}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width:80rem){.xl\:hidden{display:none}}.\[\&\:before\]\:opacity-60:before{opacity:.6}.\[\&\:before\]\:content-\[\'Content\:_\'\]:before{--tw-content:"Content: ";content:var(--tw-content)}.\[\&\:before\]\:content-\[\'Instructions\:_\'\]:before{--tw-content:"Instructions: ";content:var(--tw-content)}.\[\&\:before\]\:content-\[\'Status\:_\'\]:before{--tw-content:"Status: ";content:var(--tw-content)}}@view-transition{navigation:auto}@font-face{font-family:Satoshi;src:url(fonts/Satoshi-Variable.woff2)format("woff2"),url(fonts/Satoshi-Variable.woff)format("woff"),url(fonts/Satoshi-Variable.ttf)format("truetype");font-weight:300 900;font-style:normal;font-display:swap}@font-face{font-family:Satoshi;src:url(fonts/Satoshi-VariableItalic.woff2)format("woff2"),url(fonts/Satoshi-VariableItalic.woff)format("woff"),url(fonts/Satoshi-VariableItalic.ttf)format("truetype");font-weight:300 900;font-style:italic;font-display:swap}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes dropdown{0%{opacity:0}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false;initial-value:rotateX(0)}@property --tw-rotate-y{syntax:"*";inherits:false;initial-value:rotateY(0)}@property --tw-rotate-z{syntax:"*";inherits:false;initial-value:rotateZ(0)}@property --tw-skew-x{syntax:"*";inherits:false;initial-value:skewX(0)}@property --tw-skew-y{syntax:"*";inherits:false;initial-value:skewY(0)}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes pulse{50%{opacity:.5}} \ No newline at end of file diff --git a/main/src/main.rs b/main/src/main.rs index 720073f..2871cc5 100644 --- a/main/src/main.rs +++ b/main/src/main.rs @@ -34,7 +34,7 @@ async fn main() -> Result<(), Box> { ); // Ensure db is initialized - db.ensure_initialized().await?; + db.apply_migrations().await?; let session_store = Arc::new(db.create_session_store().await?); let openai_client = Arc::new(async_openai::Client::with_config( diff --git a/main/src/server.rs b/main/src/server.rs index 33e6fba..de77dc5 100644 --- a/main/src/server.rs +++ b/main/src/server.rs @@ -32,7 +32,7 @@ async fn main() -> Result<(), Box> { ); // Ensure db is initialized - db.ensure_initialized().await?; + db.apply_migrations().await?; let session_store = Arc::new(db.create_session_store().await?); let openai_client = Arc::new(async_openai::Client::with_config( diff --git a/result b/result new file mode 120000 index 0000000..b86213c --- /dev/null +++ b/result @@ -0,0 +1 @@ +/nix/store/d4n7jbj3j7ql8v90kn4zlf4kj2zq81sy-minne \ No newline at end of file