From e007bdd4e77a30522299a083366a02d7079fb865 Mon Sep 17 00:00:00 2001 From: karim Date: Tue, 26 May 2026 11:52:03 +0200 Subject: [PATCH] Initial commit: Rapport Website (Hugo + Hextra) Co-Authored-By: Claude Opus 4.7 --- .gitignore | 6 + assets/css/custom.css | 756 +++++ content/_index.md | 107 + content/docs/_index.md | 35 + content/docs/arbeitsablauf.md | 129 + content/docs/changelog.md | 114 + content/docs/datenhaltung.md | 148 + content/docs/einrichtung.md | 111 + content/docs/entwicklung.md | 146 + content/docs/erste-schritte.md | 62 + content/docs/installation.md | 94 + content/docs/troubleshooting.md | 203 ++ content/docs/web-modus.md | 53 + content/downloads/_index.md | 99 + content/faq/_index.md | 65 + content/features/_index.md | 20 + content/features/auto-updater.md | 49 + content/features/mitarbeiter.md | 52 + content/features/projekte.md | 53 + content/features/protokolle.md | 44 + content/features/rechnungen.md | 44 + content/features/spesen.md | 40 + content/features/system-tray.md | 40 + content/features/zeiterfassung.md | 45 + content/lizenz/_index.md | 104 + content/server/_index.md | 227 ++ hugo.yaml | 103 + i18n/de.yaml | 1 + layouts/hextra-home.html | 10 + public/404.html | 1 + public/android-chrome-192x192.png | Bin 0 -> 7296 bytes public/android-chrome-512x512.png | Bin 0 -> 27677 bytes public/apple-touch-icon.png | Bin 0 -> 6521 bytes public/categories/index.html | 32 + public/categories/index.xml | 1 + public/css/compiled/main.css | 2 + ...7edfe7d6718ac312bc985e36fe9fb736850a27.css | 1 + public/css/custom.css | 756 +++++ public/css/variables.css | 26 + public/de.search-data.json | 1 + public/de.search.js | 490 ++++ ...a01887353cb627a1eff647e8683b01439c4dee5.js | 2 + public/docs/arbeitsablauf/index.html | 64 + public/docs/changelog/index.html | 68 + public/docs/datenhaltung/index.html | 76 + public/docs/einrichtung/index.html | 62 + public/docs/entwicklung/index.html | 165 ++ public/docs/erste-schritte/index.html | 44 + public/docs/index.html | 34 + public/docs/index.xml | 1465 ++++++++++ public/docs/installation/index.html | 83 + public/docs/troubleshooting/index.html | 93 + public/docs/web-modus/index.html | 99 + public/downloads/index.html | 55 + public/downloads/index.xml | 1 + public/faq/index.html | 43 + public/faq/index.xml | 1 + public/favicon-16x16.png | Bin 0 -> 340 bytes public/favicon-32x32.png | Bin 0 -> 753 bytes public/favicon.ico | Bin 0 -> 15406 bytes public/favicon.svg | 13 + public/features/auto-updater/index.html | 79 + public/features/index.html | 31 + public/features/index.xml | 349 +++ public/features/mitarbeiter/index.html | 67 + public/features/projekte/index.html | 60 + public/features/protokolle/index.html | 82 + public/features/rechnungen/index.html | 96 + public/features/spesen/index.html | 81 + public/features/system-tray/index.html | 83 + public/features/zeiterfassung/index.html | 61 + public/fonts/Krungthep.ttf | Bin 0 -> 190152 bytes public/images/logo-dark.svg | 3 + public/images/logo.svg | 3 + public/index.html | 41 + ...c75266ab6b93f569ac2fb4f3dc66882e0416f4c.js | 96 + ...b799f580c2d1654e703dd9357d568ac41b2547a.js | 2525 +++++++++++++++++ public/js/main-head.js | 22 + ...5ff17478453df5f350d34d830b3fc96fba21672.js | 1 + public/js/main.js | 1082 +++++++ ...0bc3e2b22faddb10ae258b4d77bdb1c45dfef9e.js | 6 + public/lizenz/index.html | 45 + public/lizenz/index.xml | 1 + public/robots.txt | 1 + public/server/index.html | 98 + public/server/index.xml | 1 + public/site.webmanifest | 20 + public/sitemap.xml | 1 + public/tags/index.html | 32 + public/tags/index.xml | 1 + static/fonts/Krungthep.ttf | Bin 0 -> 190152 bytes themes/hextra/.devcontainer/devcontainer.json | 17 + themes/hextra/.gitattributes | 3 + themes/hextra/.gitignore | 9 + themes/hextra/.prettierrc | 21 + themes/hextra/.vscode/settings.json | 4 + themes/hextra/AGENTS.md | 193 ++ themes/hextra/CLAUDE.md | 193 ++ themes/hextra/LICENSE | 21 + themes/hextra/README.fa.md | 49 + themes/hextra/README.md | 49 + themes/hextra/README.zh-cn.md | 46 + themes/hextra/assets/css/chroma/dark.css | 89 + themes/hextra/assets/css/chroma/light.css | 90 + themes/hextra/assets/css/compiled/main.css | 2 + .../hextra/assets/css/components/archives.css | 3 + themes/hextra/assets/css/components/badge.css | 3 + .../hextra/assets/css/components/banner.css | 12 + themes/hextra/assets/css/components/cards.css | 46 + .../assets/css/components/code-copy.css | 7 + .../css/components/hextra/feature-grid.css | 5 + .../hextra/assets/css/components/jupyter.css | 17 + .../hextra/assets/css/components/navbar.css | 50 + .../assets/css/components/scrollbar.css | 21 + .../hextra/assets/css/components/search.css | 38 + .../hextra/assets/css/components/sidebar.css | 21 + themes/hextra/assets/css/components/steps.css | 22 + themes/hextra/assets/css/components/toc.css | 4 + themes/hextra/assets/css/custom.css | 0 themes/hextra/assets/css/highlight.css | 53 + themes/hextra/assets/css/safelist.txt | 1 + themes/hextra/assets/css/styles.css | 91 + themes/hextra/assets/css/typography.css | 147 + themes/hextra/assets/css/variables.css | 35 + themes/hextra/assets/js/core/back-to-top.js | 26 + themes/hextra/assets/js/core/banner.js | 15 + themes/hextra/assets/js/core/code-copy.js | 91 + themes/hextra/assets/js/core/favicon.js | 22 + themes/hextra/assets/js/core/filetree.js | 15 + themes/hextra/assets/js/core/lang.js | 103 + themes/hextra/assets/js/core/menu.js | 84 + themes/hextra/assets/js/core/nav-menu.js | 126 + .../assets/js/core/page-context-menu.js | 172 ++ themes/hextra/assets/js/core/sidebar.js | 37 + themes/hextra/assets/js/core/switcher-menu.js | 52 + themes/hextra/assets/js/core/tabs.js | 102 + themes/hextra/assets/js/core/task-list.js | 15 + themes/hextra/assets/js/core/theme.js | 112 + themes/hextra/assets/js/core/toc-scroll.js | 97 + themes/hextra/assets/js/flexsearch.js | 492 ++++ themes/hextra/assets/js/head/banner.js | 6 + themes/hextra/assets/js/head/theme.js | 14 + themes/hextra/assets/json/search-data.json | 38 + themes/hextra/build.sh | 56 + themes/hextra/data/icons.yaml | 312 ++ themes/hextra/dev.toml | 35 + themes/hextra/docs/assets/example.ipynb | 224 ++ themes/hextra/docs/assets/images/space.jpg | Bin 0 -> 172460 bytes themes/hextra/docs/content/_index.fa.md | 76 + themes/hextra/docs/content/_index.ja.md | 76 + themes/hextra/docs/content/_index.md | 76 + themes/hextra/docs/content/_index.zh-cn.md | 76 + themes/hextra/docs/content/about/index.fa.md | 22 + themes/hextra/docs/content/about/index.ja.md | 20 + themes/hextra/docs/content/about/index.md | 20 + .../hextra/docs/content/about/index.zh-cn.md | 20 + .../hextra/docs/content/archives/_index.fa.md | 5 + .../hextra/docs/content/archives/_index.ja.md | 5 + themes/hextra/docs/content/archives/_index.md | 5 + .../docs/content/archives/_index.zh-cn.md | 5 + themes/hextra/docs/content/blog/_index.fa.md | 10 + themes/hextra/docs/content/blog/_index.ja.md | 10 + themes/hextra/docs/content/blog/_index.md | 10 + .../hextra/docs/content/blog/_index.zh-cn.md | 10 + .../hextra/docs/content/blog/markdown.fa.md | 160 ++ .../hextra/docs/content/blog/markdown.ja.md | 157 + themes/hextra/docs/content/blog/markdown.md | 157 + .../docs/content/blog/markdown.zh-cn.md | 157 + themes/hextra/docs/content/blog/v0.10.fa.md | 247 ++ themes/hextra/docs/content/blog/v0.10.ja.md | 247 ++ themes/hextra/docs/content/blog/v0.10.md | 247 ++ .../hextra/docs/content/blog/v0.10.zh-cn.md | 247 ++ themes/hextra/docs/content/blog/v0.11.fa.md | 128 + themes/hextra/docs/content/blog/v0.11.ja.md | 128 + themes/hextra/docs/content/blog/v0.11.md | 128 + .../hextra/docs/content/blog/v0.11.zh-cn.md | 129 + themes/hextra/docs/content/blog/v0.12.fa.md | 218 ++ themes/hextra/docs/content/blog/v0.12.ja.md | 218 ++ themes/hextra/docs/content/blog/v0.12.md | 218 ++ .../hextra/docs/content/blog/v0.12.zh-cn.md | 218 ++ themes/hextra/docs/content/docs/_index.fa.md | 42 + themes/hextra/docs/content/docs/_index.ja.md | 41 + themes/hextra/docs/content/docs/_index.md | 42 + .../hextra/docs/content/docs/_index.zh-cn.md | 41 + .../docs/content/docs/advanced/_index.fa.md | 17 + .../docs/content/docs/advanced/_index.ja.md | 17 + .../docs/content/docs/advanced/_index.md | 17 + .../content/docs/advanced/_index.zh-cn.md | 17 + .../docs/advanced/additional-pages.fa.md | 105 + .../docs/advanced/additional-pages.ja.md | 105 + .../content/docs/advanced/additional-pages.md | 105 + .../docs/advanced/additional-pages.zh-cn.md | 105 + .../docs/content/docs/advanced/comments.fa.md | 39 + .../docs/content/docs/advanced/comments.ja.md | 39 + .../docs/content/docs/advanced/comments.md | 39 + .../content/docs/advanced/comments.zh-cn.md | 39 + .../content/docs/advanced/customization.fa.md | 251 ++ .../content/docs/advanced/customization.ja.md | 251 ++ .../content/docs/advanced/customization.md | 251 ++ .../docs/advanced/customization.zh-cn.md | 251 ++ .../docs/advanced/multi-language.fa.md | 88 + .../docs/advanced/multi-language.ja.md | 88 + .../content/docs/advanced/multi-language.md | 88 + .../docs/advanced/multi-language.zh-cn.md | 88 + .../docs/content/docs/getting-started.fa.md | 204 ++ .../docs/content/docs/getting-started.ja.md | 204 ++ .../docs/content/docs/getting-started.md | 211 ++ .../content/docs/getting-started.zh-cn.md | 204 ++ .../docs/content/docs/guide/_index.fa.md | 23 + .../docs/content/docs/guide/_index.ja.md | 23 + .../hextra/docs/content/docs/guide/_index.md | 23 + .../docs/content/docs/guide/_index.zh-cn.md | 23 + .../content/docs/guide/configuration.fa.md | 629 ++++ .../content/docs/guide/configuration.ja.md | 635 +++++ .../docs/content/docs/guide/configuration.md | 780 +++++ .../content/docs/guide/configuration.zh-cn.md | 635 +++++ .../docs/content/docs/guide/deploy-site.fa.md | 164 ++ .../docs/content/docs/guide/deploy-site.ja.md | 163 ++ .../docs/content/docs/guide/deploy-site.md | 164 ++ .../content/docs/guide/deploy-site.zh-cn.md | 164 ++ .../docs/content/docs/guide/diagrams.fa.md | 53 + .../docs/content/docs/guide/diagrams.ja.md | 53 + .../docs/content/docs/guide/diagrams.md | 53 + .../docs/content/docs/guide/diagrams.zh-cn.md | 53 + .../docs/content/docs/guide/latex.fa.md | 146 + .../docs/content/docs/guide/latex.ja.md | 146 + .../hextra/docs/content/docs/guide/latex.md | 146 + .../docs/content/docs/guide/latex.zh-cn.md | 145 + .../docs/content/docs/guide/markdown.fa.md | 210 ++ .../docs/content/docs/guide/markdown.ja.md | 210 ++ .../docs/content/docs/guide/markdown.md | 210 ++ .../docs/content/docs/guide/markdown.zh-cn.md | 210 ++ .../content/docs/guide/organize-files.fa.md | 256 ++ .../content/docs/guide/organize-files.ja.md | 256 ++ .../docs/content/docs/guide/organize-files.md | 277 ++ .../docs/guide/organize-files.zh-cn.md | 256 ++ .../docs/guide/shortcodes/_index.fa.md | 30 + .../docs/guide/shortcodes/_index.ja.md | 30 + .../content/docs/guide/shortcodes/_index.md | 32 + .../docs/guide/shortcodes/_index.zh-cn.md | 30 + .../docs/guide/shortcodes/asciinema.fa.md | 121 + .../docs/guide/shortcodes/asciinema.ja.md | 121 + .../docs/guide/shortcodes/asciinema.md | 124 + .../docs/guide/shortcodes/asciinema.zh-cn.md | 121 + .../docs/guide/shortcodes/callout.fa.md | 149 + .../docs/guide/shortcodes/callout.ja.md | 149 + .../content/docs/guide/shortcodes/callout.md | 149 + .../docs/guide/shortcodes/callout.zh-cn.md | 149 + .../content/docs/guide/shortcodes/cards.fa.md | 116 + .../content/docs/guide/shortcodes/cards.ja.md | 116 + .../content/docs/guide/shortcodes/cards.md | 126 + .../docs/guide/shortcodes/cards.zh-cn.md | 116 + .../docs/guide/shortcodes/details.fa.md | 43 + .../docs/guide/shortcodes/details.ja.md | 43 + .../content/docs/guide/shortcodes/details.md | 43 + .../docs/guide/shortcodes/details.zh-cn.md | 43 + .../docs/guide/shortcodes/filetree.fa.md | 49 + .../docs/guide/shortcodes/filetree.ja.md | 49 + .../content/docs/guide/shortcodes/filetree.md | 49 + .../docs/guide/shortcodes/filetree.zh-cn.md | 49 + .../docs/guide/shortcodes/hextra.fa.md | 175 ++ .../docs/guide/shortcodes/hextra.ja.md | 175 ++ .../content/docs/guide/shortcodes/hextra.md | 175 ++ .../docs/guide/shortcodes/hextra.zh-cn.md | 175 ++ .../content/docs/guide/shortcodes/icon.fa.md | 74 + .../content/docs/guide/shortcodes/icon.ja.md | 74 + .../content/docs/guide/shortcodes/icon.md | 74 + .../docs/guide/shortcodes/icon.zh-cn.md | 74 + .../docs/guide/shortcodes/jupyter.fa.md | 79 + .../docs/guide/shortcodes/jupyter.ja.md | 79 + .../content/docs/guide/shortcodes/jupyter.md | 79 + .../docs/guide/shortcodes/jupyter.zh-cn.md | 79 + .../docs/guide/shortcodes/others.fa.md | 129 + .../docs/guide/shortcodes/others.ja.md | 129 + .../content/docs/guide/shortcodes/others.md | 129 + .../docs/guide/shortcodes/others.zh-cn.md | 129 + .../content/docs/guide/shortcodes/steps.fa.md | 47 + .../content/docs/guide/shortcodes/steps.ja.md | 47 + .../content/docs/guide/shortcodes/steps.md | 57 + .../docs/guide/shortcodes/steps.zh-cn.md | 47 + .../content/docs/guide/shortcodes/tabs.fa.md | 149 + .../content/docs/guide/shortcodes/tabs.ja.md | 149 + .../content/docs/guide/shortcodes/tabs.md | 155 + .../docs/guide/shortcodes/tabs.zh-cn.md | 149 + .../content/docs/guide/shortcodes/term.fa.md | 28 + .../content/docs/guide/shortcodes/term.ja.md | 28 + .../content/docs/guide/shortcodes/term.md | 29 + .../docs/guide/shortcodes/term.zh-cn.md | 28 + .../docs/guide/syntax-highlighting.fa.md | 114 + .../docs/guide/syntax-highlighting.ja.md | 114 + .../content/docs/guide/syntax-highlighting.md | 114 + .../docs/guide/syntax-highlighting.zh-cn.md | 111 + .../hextra/docs/content/glossary/_index.en.md | 4 + .../hextra/docs/content/glossary/_index.fa.md | 4 + .../hextra/docs/content/glossary/_index.ja.md | 4 + .../docs/content/glossary/_index.zh-cn.md | 4 + .../hextra/docs/content/showcase/index.fa.md | 35 + .../hextra/docs/content/showcase/index.ja.md | 146 + themes/hextra/docs/content/showcase/index.md | 146 + .../docs/content/showcase/index.zh-cn.md | 104 + themes/hextra/docs/data/en/termbase.yaml | 12 + themes/hextra/docs/data/fa/termbase.yaml | 12 + themes/hextra/docs/data/ja/termbase.yaml | 12 + themes/hextra/docs/data/zh-cn/termbase.yaml | 12 + themes/hextra/docs/go.mod | 5 + themes/hextra/docs/hugo.work | 3 + themes/hextra/docs/hugo.yaml | 292 ++ themes/hextra/docs/hugo_stats.json | 801 ++++++ themes/hextra/docs/i18n/fa.yaml | 9 + themes/hextra/docs/i18n/ja.yaml | 9 + themes/hextra/docs/i18n/zh-cn.yaml | 9 + .../layouts/_partials/custom/head-end.html | 6 + .../docs/layouts/_shortcodes/new-feature.html | 11 + themes/hextra/docs/static/casts/demo.cast | 19 + themes/hextra/docs/static/favicon-dark.svg | 13 + .../static/images/card-image-unprocessed.jpg | Bin 0 -> 191858 bytes .../hextra/docs/static/images/hextra-doc.webp | Bin 0 -> 205614 bytes .../docs/static/images/hextra-markdown.webp | Bin 0 -> 56908 bytes .../docs/static/images/hextra-search.webp | Bin 0 -> 49276 bytes themes/hextra/examples/README.md | 13 + themes/hextra/go.mod | 3 + themes/hextra/hugo.toml | 16 + themes/hextra/i18n/cs.yaml | 43 + themes/hextra/i18n/de.yaml | 43 + themes/hextra/i18n/en.yaml | 43 + themes/hextra/i18n/es.yaml | 43 + themes/hextra/i18n/fa.yaml | 43 + themes/hextra/i18n/fr.yaml | 43 + themes/hextra/i18n/he.yaml | 43 + themes/hextra/i18n/it.yaml | 43 + themes/hextra/i18n/ja.yaml | 43 + themes/hextra/i18n/ko.yaml | 43 + themes/hextra/i18n/nb.yaml | 43 + themes/hextra/i18n/nl.yaml | 43 + themes/hextra/i18n/nn.yaml | 43 + themes/hextra/i18n/pt.yaml | 43 + themes/hextra/i18n/ro.yaml | 43 + themes/hextra/i18n/ru.yaml | 43 + themes/hextra/i18n/sw.yaml | 43 + themes/hextra/i18n/uk.yaml | 43 + themes/hextra/i18n/vi.yaml | 43 + themes/hextra/i18n/zh-cn.yaml | 43 + themes/hextra/i18n/zh-tw.yaml | 43 + themes/hextra/images/screenshot.jpg | Bin 0 -> 179674 bytes themes/hextra/images/tn.jpg | Bin 0 -> 92825 bytes themes/hextra/layouts/404.html | 40 + .../_markup/render-blockquote-alert.html | 9 + .../_markup/render-blockquote-regular.html | 3 + .../_markup/render-codeblock-mermaid.html | 6 + .../layouts/_markup/render-codeblock.html | 13 + .../layouts/_markup/render-heading.html | 8 + .../hextra/layouts/_markup/render-image.html | 59 + .../hextra/layouts/_markup/render-link.html | 28 + .../layouts/_markup/render-passthrough.html | 20 + themes/hextra/layouts/_partials/banner.html | 19 + .../hextra/layouts/_partials/breadcrumb.html | 17 + .../components/analytics/analytics.html | 24 + .../components/analytics/goat-counter.html | 17 + .../analytics/google-analytics.html | 13 + .../components/analytics/matomo.html | 31 + .../_partials/components/analytics/umami.html | 57 + .../_partials/components/blog-pager.html | 39 + .../components/codeblock-copy-button.html | 17 + .../_partials/components/codeblock.html | 29 + .../_partials/components/comments.html | 11 + .../layouts/_partials/components/giscus.html | 89 + .../components/github-style-alert.html | 53 + .../_partials/components/last-updated.html | 20 + .../components/page-context-menu.html | 90 + .../layouts/_partials/components/pager.html | 53 + .../layouts/_partials/custom/banner.html | 0 .../layouts/_partials/custom/footer.html | 0 .../layouts/_partials/custom/head-end.html | 0 .../_partials/custom/navbar-title.html | 0 themes/hextra/layouts/_partials/favicons.html | 6 + themes/hextra/layouts/_partials/footer.html | 44 + .../layouts/_partials/google-analytics.html | 2 + themes/hextra/layouts/_partials/head.html | 81 + .../layouts/_partials/language-switch.html | 58 + .../hextra/layouts/_partials/navbar-link.html | 88 + .../layouts/_partials/navbar-title.html | 16 + themes/hextra/layouts/_partials/navbar.html | 59 + .../hextra/layouts/_partials/opengraph.html | 96 + themes/hextra/layouts/_partials/scripts.html | 20 + .../layouts/_partials/scripts/asciinema.html | 140 + .../layouts/_partials/scripts/core.html | 10 + .../layouts/_partials/scripts/katex.html | 92 + .../layouts/_partials/scripts/mathjax.html | 20 + .../_partials/scripts/medium-zoom.html | 85 + .../layouts/_partials/scripts/mermaid.html | 79 + .../layouts/_partials/scripts/search.html | 63 + themes/hextra/layouts/_partials/search.html | 30 + .../layouts/_partials/shortcodes/badge.html | 38 + .../layouts/_partials/shortcodes/callout.html | 28 + .../layouts/_partials/shortcodes/card.html | 70 + .../layouts/_partials/shortcodes/cards.html | 6 + .../layouts/_partials/shortcodes/tabs.html | 48 + themes/hextra/layouts/_partials/sidebar.html | 325 +++ themes/hextra/layouts/_partials/tags.html | 7 + .../layouts/_partials/theme-toggle.html | 83 + themes/hextra/layouts/_partials/toc.html | 91 + .../_partials/utils/extract-headings.html | 31 + .../layouts/_partials/utils/file-path.html | 21 + .../layouts/_partials/utils/format-date.html | 3 + .../layouts/_partials/utils/fragments.html | 93 + .../utils/hugo-compat/language-direction.html | 15 + .../utils/hugo-compat/language-label.html | 15 + .../utils/hugo-compat/language-locale.html | 15 + .../utils/hugo-compat/site-data.html | 14 + .../_partials/utils/hugo-compat/sites.html | 14 + .../hextra/layouts/_partials/utils/icon.html | 79 + .../layouts/_partials/utils/lang-link.html | 25 + .../_partials/utils/page-description.html | 11 + .../_partials/utils/page-width-override.html | 10 + .../layouts/_partials/utils/sort-pages.html | 32 + .../layouts/_partials/utils/template-url.html | 18 + .../hextra/layouts/_partials/utils/title.html | 19 + .../hextra/layouts/_shortcodes/asciinema.html | 88 + themes/hextra/layouts/_shortcodes/badge.html | 54 + .../hextra/layouts/_shortcodes/callout.html | 57 + themes/hextra/layouts/_shortcodes/card.html | 72 + themes/hextra/layouts/_shortcodes/cards.html | 11 + .../hextra/layouts/_shortcodes/details.html | 20 + .../_shortcodes/filetree/container.html | 13 + .../layouts/_shortcodes/filetree/file.html | 16 + .../layouts/_shortcodes/filetree/folder.html | 26 + .../_shortcodes/hextra/feature-card.html | 51 + .../_shortcodes/hextra/feature-grid.html | 21 + .../_shortcodes/hextra/hero-badge.html | 24 + .../_shortcodes/hextra/hero-button.html | 25 + .../_shortcodes/hextra/hero-container.html | 56 + .../_shortcodes/hextra/hero-headline.html | 16 + .../_shortcodes/hextra/hero-section.html | 20 + .../_shortcodes/hextra/hero-subtitle.html | 16 + themes/hextra/layouts/_shortcodes/icon.html | 20 + .../hextra/layouts/_shortcodes/include.html | 22 + .../hextra/layouts/_shortcodes/jupyter.html | 88 + themes/hextra/layouts/_shortcodes/pdf.html | 15 + themes/hextra/layouts/_shortcodes/steps.html | 9 + themes/hextra/layouts/_shortcodes/tab.html | 28 + themes/hextra/layouts/_shortcodes/tabs.html | 39 + themes/hextra/layouts/_shortcodes/term.html | 33 + themes/hextra/layouts/archives.html | 38 + themes/hextra/layouts/baseof.html | 16 + themes/hextra/layouts/blog/list.html | 39 + themes/hextra/layouts/blog/single.html | 55 + themes/hextra/layouts/docs/list.html | 25 + themes/hextra/layouts/docs/single.html | 25 + themes/hextra/layouts/glossary.html | 26 + themes/hextra/layouts/glossary.markdown.md | 3 + themes/hextra/layouts/hextra-home.html | 10 + themes/hextra/layouts/home.html | 14 + themes/hextra/layouts/list.html | 18 + themes/hextra/layouts/list.rss.xml | 43 + themes/hextra/layouts/llms.txt | 35 + themes/hextra/layouts/page.markdown.md | 2 + themes/hextra/layouts/section.markdown.md | 2 + themes/hextra/layouts/single.html | 19 + themes/hextra/layouts/taxonomy.html | 30 + themes/hextra/layouts/term.html | 33 + themes/hextra/layouts/wide.html | 12 + themes/hextra/layouts/wide.markdown.md | 3 + themes/hextra/netlify.toml | 12 + themes/hextra/package-lock.json | 1564 ++++++++++ themes/hextra/package.json | 20 + themes/hextra/playwright.config.ts | 21 + themes/hextra/postcss.config.mjs | 5 + .../hextra/static/android-chrome-192x192.png | Bin 0 -> 7296 bytes .../hextra/static/android-chrome-512x512.png | Bin 0 -> 27677 bytes themes/hextra/static/apple-touch-icon.png | Bin 0 -> 6521 bytes themes/hextra/static/favicon-16x16.png | Bin 0 -> 340 bytes themes/hextra/static/favicon-32x32.png | Bin 0 -> 753 bytes themes/hextra/static/favicon.ico | Bin 0 -> 15406 bytes themes/hextra/static/favicon.svg | 13 + themes/hextra/static/images/logo-dark.svg | 3 + themes/hextra/static/images/logo.svg | 3 + themes/hextra/static/site.webmanifest | 20 + themes/hextra/tests/accessibility.spec.ts | 103 + themes/hextra/tests/mobile-menu.spec.ts | 157 + themes/hextra/theme.toml | 16 + 480 files changed, 41697 insertions(+) create mode 100644 .gitignore create mode 100644 assets/css/custom.css create mode 100644 content/_index.md create mode 100644 content/docs/_index.md create mode 100644 content/docs/arbeitsablauf.md create mode 100644 content/docs/changelog.md create mode 100644 content/docs/datenhaltung.md create mode 100644 content/docs/einrichtung.md create mode 100644 content/docs/entwicklung.md create mode 100644 content/docs/erste-schritte.md create mode 100644 content/docs/installation.md create mode 100644 content/docs/troubleshooting.md create mode 100644 content/docs/web-modus.md create mode 100644 content/downloads/_index.md create mode 100644 content/faq/_index.md create mode 100644 content/features/_index.md create mode 100644 content/features/auto-updater.md create mode 100644 content/features/mitarbeiter.md create mode 100644 content/features/projekte.md create mode 100644 content/features/protokolle.md create mode 100644 content/features/rechnungen.md create mode 100644 content/features/spesen.md create mode 100644 content/features/system-tray.md create mode 100644 content/features/zeiterfassung.md create mode 100644 content/lizenz/_index.md create mode 100644 content/server/_index.md create mode 100644 hugo.yaml create mode 100644 i18n/de.yaml create mode 100644 layouts/hextra-home.html create mode 100644 public/404.html create mode 100644 public/android-chrome-192x192.png create mode 100644 public/android-chrome-512x512.png create mode 100644 public/apple-touch-icon.png create mode 100644 public/categories/index.html create mode 100644 public/categories/index.xml create mode 100644 public/css/compiled/main.css create mode 100644 public/css/compiled/main.min.3f70c37b0f2ce37d160ca0a36b7edfe7d6718ac312bc985e36fe9fb736850a27.css create mode 100644 public/css/custom.css create mode 100644 public/css/variables.css create mode 100644 public/de.search-data.json create mode 100644 public/de.search.js create mode 100644 public/de.search.min.e433191e26d61d7cc2f542972a01887353cb627a1eff647e8683b01439c4dee5.js create mode 100644 public/docs/arbeitsablauf/index.html create mode 100644 public/docs/changelog/index.html create mode 100644 public/docs/datenhaltung/index.html create mode 100644 public/docs/einrichtung/index.html create mode 100644 public/docs/entwicklung/index.html create mode 100644 public/docs/erste-schritte/index.html create mode 100644 public/docs/index.html create mode 100644 public/docs/index.xml create mode 100644 public/docs/installation/index.html create mode 100644 public/docs/troubleshooting/index.html create mode 100644 public/docs/web-modus/index.html create mode 100644 public/downloads/index.html create mode 100644 public/downloads/index.xml create mode 100644 public/faq/index.html create mode 100644 public/faq/index.xml create mode 100644 public/favicon-16x16.png create mode 100644 public/favicon-32x32.png create mode 100644 public/favicon.ico create mode 100644 public/favicon.svg create mode 100644 public/features/auto-updater/index.html create mode 100644 public/features/index.html create mode 100644 public/features/index.xml create mode 100644 public/features/mitarbeiter/index.html create mode 100644 public/features/projekte/index.html create mode 100644 public/features/protokolle/index.html create mode 100644 public/features/rechnungen/index.html create mode 100644 public/features/spesen/index.html create mode 100644 public/features/system-tray/index.html create mode 100644 public/features/zeiterfassung/index.html create mode 100644 public/fonts/Krungthep.ttf create mode 100644 public/images/logo-dark.svg create mode 100644 public/images/logo.svg create mode 100644 public/index.html create mode 100644 public/js/flexsearch.433e941a8a573ebb9931fc16fc75266ab6b93f569ac2fb4f3dc66882e0416f4c.js create mode 100644 public/js/flexsearch.9f5b5908f93ae86f1ecd4b043b799f580c2d1654e703dd9357d568ac41b2547a.js create mode 100644 public/js/main-head.js create mode 100644 public/js/main-head.min.2375b7265ea066ecb314226ce5ff17478453df5f350d34d830b3fc96fba21672.js create mode 100644 public/js/main.js create mode 100644 public/js/main.min.87785fd96557e898756188ba90bc3e2b22faddb10ae258b4d77bdb1c45dfef9e.js create mode 100644 public/lizenz/index.html create mode 100644 public/lizenz/index.xml create mode 100644 public/robots.txt create mode 100644 public/server/index.html create mode 100644 public/server/index.xml create mode 100644 public/site.webmanifest create mode 100644 public/sitemap.xml create mode 100644 public/tags/index.html create mode 100644 public/tags/index.xml create mode 100644 static/fonts/Krungthep.ttf create mode 100644 themes/hextra/.devcontainer/devcontainer.json create mode 100644 themes/hextra/.gitattributes create mode 100644 themes/hextra/.gitignore create mode 100644 themes/hextra/.prettierrc create mode 100644 themes/hextra/.vscode/settings.json create mode 100644 themes/hextra/AGENTS.md create mode 100644 themes/hextra/CLAUDE.md create mode 100644 themes/hextra/LICENSE create mode 100644 themes/hextra/README.fa.md create mode 100644 themes/hextra/README.md create mode 100644 themes/hextra/README.zh-cn.md create mode 100644 themes/hextra/assets/css/chroma/dark.css create mode 100644 themes/hextra/assets/css/chroma/light.css create mode 100644 themes/hextra/assets/css/compiled/main.css create mode 100644 themes/hextra/assets/css/components/archives.css create mode 100644 themes/hextra/assets/css/components/badge.css create mode 100644 themes/hextra/assets/css/components/banner.css create mode 100644 themes/hextra/assets/css/components/cards.css create mode 100644 themes/hextra/assets/css/components/code-copy.css create mode 100644 themes/hextra/assets/css/components/hextra/feature-grid.css create mode 100644 themes/hextra/assets/css/components/jupyter.css create mode 100644 themes/hextra/assets/css/components/navbar.css create mode 100644 themes/hextra/assets/css/components/scrollbar.css create mode 100644 themes/hextra/assets/css/components/search.css create mode 100644 themes/hextra/assets/css/components/sidebar.css create mode 100644 themes/hextra/assets/css/components/steps.css create mode 100644 themes/hextra/assets/css/components/toc.css create mode 100644 themes/hextra/assets/css/custom.css create mode 100644 themes/hextra/assets/css/highlight.css create mode 100644 themes/hextra/assets/css/safelist.txt create mode 100644 themes/hextra/assets/css/styles.css create mode 100644 themes/hextra/assets/css/typography.css create mode 100644 themes/hextra/assets/css/variables.css create mode 100644 themes/hextra/assets/js/core/back-to-top.js create mode 100644 themes/hextra/assets/js/core/banner.js create mode 100644 themes/hextra/assets/js/core/code-copy.js create mode 100644 themes/hextra/assets/js/core/favicon.js create mode 100644 themes/hextra/assets/js/core/filetree.js create mode 100644 themes/hextra/assets/js/core/lang.js create mode 100644 themes/hextra/assets/js/core/menu.js create mode 100644 themes/hextra/assets/js/core/nav-menu.js create mode 100644 themes/hextra/assets/js/core/page-context-menu.js create mode 100644 themes/hextra/assets/js/core/sidebar.js create mode 100644 themes/hextra/assets/js/core/switcher-menu.js create mode 100644 themes/hextra/assets/js/core/tabs.js create mode 100644 themes/hextra/assets/js/core/task-list.js create mode 100644 themes/hextra/assets/js/core/theme.js create mode 100644 themes/hextra/assets/js/core/toc-scroll.js create mode 100644 themes/hextra/assets/js/flexsearch.js create mode 100644 themes/hextra/assets/js/head/banner.js create mode 100644 themes/hextra/assets/js/head/theme.js create mode 100644 themes/hextra/assets/json/search-data.json create mode 100755 themes/hextra/build.sh create mode 100644 themes/hextra/data/icons.yaml create mode 100644 themes/hextra/dev.toml create mode 100644 themes/hextra/docs/assets/example.ipynb create mode 100644 themes/hextra/docs/assets/images/space.jpg create mode 100644 themes/hextra/docs/content/_index.fa.md create mode 100644 themes/hextra/docs/content/_index.ja.md create mode 100644 themes/hextra/docs/content/_index.md create mode 100644 themes/hextra/docs/content/_index.zh-cn.md create mode 100644 themes/hextra/docs/content/about/index.fa.md create mode 100644 themes/hextra/docs/content/about/index.ja.md create mode 100644 themes/hextra/docs/content/about/index.md create mode 100644 themes/hextra/docs/content/about/index.zh-cn.md create mode 100644 themes/hextra/docs/content/archives/_index.fa.md create mode 100644 themes/hextra/docs/content/archives/_index.ja.md create mode 100644 themes/hextra/docs/content/archives/_index.md create mode 100644 themes/hextra/docs/content/archives/_index.zh-cn.md create mode 100644 themes/hextra/docs/content/blog/_index.fa.md create mode 100644 themes/hextra/docs/content/blog/_index.ja.md create mode 100644 themes/hextra/docs/content/blog/_index.md create mode 100644 themes/hextra/docs/content/blog/_index.zh-cn.md create mode 100644 themes/hextra/docs/content/blog/markdown.fa.md create mode 100644 themes/hextra/docs/content/blog/markdown.ja.md create mode 100644 themes/hextra/docs/content/blog/markdown.md create mode 100644 themes/hextra/docs/content/blog/markdown.zh-cn.md create mode 100644 themes/hextra/docs/content/blog/v0.10.fa.md create mode 100644 themes/hextra/docs/content/blog/v0.10.ja.md create mode 100644 themes/hextra/docs/content/blog/v0.10.md create mode 100644 themes/hextra/docs/content/blog/v0.10.zh-cn.md create mode 100644 themes/hextra/docs/content/blog/v0.11.fa.md create mode 100644 themes/hextra/docs/content/blog/v0.11.ja.md create mode 100644 themes/hextra/docs/content/blog/v0.11.md create mode 100644 themes/hextra/docs/content/blog/v0.11.zh-cn.md create mode 100644 themes/hextra/docs/content/blog/v0.12.fa.md create mode 100644 themes/hextra/docs/content/blog/v0.12.ja.md create mode 100644 themes/hextra/docs/content/blog/v0.12.md create mode 100644 themes/hextra/docs/content/blog/v0.12.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/_index.fa.md create mode 100644 themes/hextra/docs/content/docs/_index.ja.md create mode 100644 themes/hextra/docs/content/docs/_index.md create mode 100644 themes/hextra/docs/content/docs/_index.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/advanced/_index.fa.md create mode 100644 themes/hextra/docs/content/docs/advanced/_index.ja.md create mode 100644 themes/hextra/docs/content/docs/advanced/_index.md create mode 100644 themes/hextra/docs/content/docs/advanced/_index.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/advanced/additional-pages.fa.md create mode 100644 themes/hextra/docs/content/docs/advanced/additional-pages.ja.md create mode 100644 themes/hextra/docs/content/docs/advanced/additional-pages.md create mode 100644 themes/hextra/docs/content/docs/advanced/additional-pages.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/advanced/comments.fa.md create mode 100644 themes/hextra/docs/content/docs/advanced/comments.ja.md create mode 100644 themes/hextra/docs/content/docs/advanced/comments.md create mode 100644 themes/hextra/docs/content/docs/advanced/comments.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/advanced/customization.fa.md create mode 100644 themes/hextra/docs/content/docs/advanced/customization.ja.md create mode 100644 themes/hextra/docs/content/docs/advanced/customization.md create mode 100644 themes/hextra/docs/content/docs/advanced/customization.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/advanced/multi-language.fa.md create mode 100644 themes/hextra/docs/content/docs/advanced/multi-language.ja.md create mode 100644 themes/hextra/docs/content/docs/advanced/multi-language.md create mode 100644 themes/hextra/docs/content/docs/advanced/multi-language.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/getting-started.fa.md create mode 100644 themes/hextra/docs/content/docs/getting-started.ja.md create mode 100644 themes/hextra/docs/content/docs/getting-started.md create mode 100644 themes/hextra/docs/content/docs/getting-started.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/_index.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/_index.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/_index.md create mode 100644 themes/hextra/docs/content/docs/guide/_index.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/configuration.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/configuration.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/configuration.md create mode 100644 themes/hextra/docs/content/docs/guide/configuration.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/deploy-site.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/deploy-site.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/deploy-site.md create mode 100644 themes/hextra/docs/content/docs/guide/deploy-site.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/diagrams.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/diagrams.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/diagrams.md create mode 100644 themes/hextra/docs/content/docs/guide/diagrams.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/latex.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/latex.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/latex.md create mode 100644 themes/hextra/docs/content/docs/guide/latex.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/markdown.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/markdown.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/markdown.md create mode 100644 themes/hextra/docs/content/docs/guide/markdown.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/organize-files.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/organize-files.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/organize-files.md create mode 100644 themes/hextra/docs/content/docs/guide/organize-files.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/_index.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/_index.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/_index.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/_index.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/asciinema.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/asciinema.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/asciinema.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/asciinema.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/callout.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/callout.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/callout.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/callout.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/cards.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/cards.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/cards.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/cards.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/details.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/details.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/details.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/details.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/filetree.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/filetree.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/filetree.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/filetree.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/hextra.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/hextra.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/hextra.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/hextra.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/icon.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/icon.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/icon.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/icon.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/jupyter.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/jupyter.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/jupyter.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/jupyter.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/others.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/others.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/others.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/others.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/steps.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/steps.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/steps.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/steps.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/tabs.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/tabs.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/tabs.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/tabs.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/term.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/term.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/term.md create mode 100644 themes/hextra/docs/content/docs/guide/shortcodes/term.zh-cn.md create mode 100644 themes/hextra/docs/content/docs/guide/syntax-highlighting.fa.md create mode 100644 themes/hextra/docs/content/docs/guide/syntax-highlighting.ja.md create mode 100644 themes/hextra/docs/content/docs/guide/syntax-highlighting.md create mode 100644 themes/hextra/docs/content/docs/guide/syntax-highlighting.zh-cn.md create mode 100644 themes/hextra/docs/content/glossary/_index.en.md create mode 100644 themes/hextra/docs/content/glossary/_index.fa.md create mode 100644 themes/hextra/docs/content/glossary/_index.ja.md create mode 100644 themes/hextra/docs/content/glossary/_index.zh-cn.md create mode 100644 themes/hextra/docs/content/showcase/index.fa.md create mode 100644 themes/hextra/docs/content/showcase/index.ja.md create mode 100644 themes/hextra/docs/content/showcase/index.md create mode 100644 themes/hextra/docs/content/showcase/index.zh-cn.md create mode 100644 themes/hextra/docs/data/en/termbase.yaml create mode 100644 themes/hextra/docs/data/fa/termbase.yaml create mode 100644 themes/hextra/docs/data/ja/termbase.yaml create mode 100644 themes/hextra/docs/data/zh-cn/termbase.yaml create mode 100644 themes/hextra/docs/go.mod create mode 100644 themes/hextra/docs/hugo.work create mode 100644 themes/hextra/docs/hugo.yaml create mode 100644 themes/hextra/docs/hugo_stats.json create mode 100644 themes/hextra/docs/i18n/fa.yaml create mode 100644 themes/hextra/docs/i18n/ja.yaml create mode 100644 themes/hextra/docs/i18n/zh-cn.yaml create mode 100644 themes/hextra/docs/layouts/_partials/custom/head-end.html create mode 100644 themes/hextra/docs/layouts/_shortcodes/new-feature.html create mode 100644 themes/hextra/docs/static/casts/demo.cast create mode 100644 themes/hextra/docs/static/favicon-dark.svg create mode 100644 themes/hextra/docs/static/images/card-image-unprocessed.jpg create mode 100644 themes/hextra/docs/static/images/hextra-doc.webp create mode 100644 themes/hextra/docs/static/images/hextra-markdown.webp create mode 100644 themes/hextra/docs/static/images/hextra-search.webp create mode 100644 themes/hextra/examples/README.md create mode 100644 themes/hextra/go.mod create mode 100644 themes/hextra/hugo.toml create mode 100644 themes/hextra/i18n/cs.yaml create mode 100644 themes/hextra/i18n/de.yaml create mode 100644 themes/hextra/i18n/en.yaml create mode 100644 themes/hextra/i18n/es.yaml create mode 100644 themes/hextra/i18n/fa.yaml create mode 100644 themes/hextra/i18n/fr.yaml create mode 100644 themes/hextra/i18n/he.yaml create mode 100644 themes/hextra/i18n/it.yaml create mode 100644 themes/hextra/i18n/ja.yaml create mode 100644 themes/hextra/i18n/ko.yaml create mode 100644 themes/hextra/i18n/nb.yaml create mode 100644 themes/hextra/i18n/nl.yaml create mode 100644 themes/hextra/i18n/nn.yaml create mode 100644 themes/hextra/i18n/pt.yaml create mode 100644 themes/hextra/i18n/ro.yaml create mode 100644 themes/hextra/i18n/ru.yaml create mode 100644 themes/hextra/i18n/sw.yaml create mode 100644 themes/hextra/i18n/uk.yaml create mode 100644 themes/hextra/i18n/vi.yaml create mode 100644 themes/hextra/i18n/zh-cn.yaml create mode 100644 themes/hextra/i18n/zh-tw.yaml create mode 100644 themes/hextra/images/screenshot.jpg create mode 100644 themes/hextra/images/tn.jpg create mode 100644 themes/hextra/layouts/404.html create mode 100644 themes/hextra/layouts/_markup/render-blockquote-alert.html create mode 100644 themes/hextra/layouts/_markup/render-blockquote-regular.html create mode 100644 themes/hextra/layouts/_markup/render-codeblock-mermaid.html create mode 100644 themes/hextra/layouts/_markup/render-codeblock.html create mode 100644 themes/hextra/layouts/_markup/render-heading.html create mode 100644 themes/hextra/layouts/_markup/render-image.html create mode 100644 themes/hextra/layouts/_markup/render-link.html create mode 100644 themes/hextra/layouts/_markup/render-passthrough.html create mode 100644 themes/hextra/layouts/_partials/banner.html create mode 100644 themes/hextra/layouts/_partials/breadcrumb.html create mode 100644 themes/hextra/layouts/_partials/components/analytics/analytics.html create mode 100644 themes/hextra/layouts/_partials/components/analytics/goat-counter.html create mode 100644 themes/hextra/layouts/_partials/components/analytics/google-analytics.html create mode 100644 themes/hextra/layouts/_partials/components/analytics/matomo.html create mode 100644 themes/hextra/layouts/_partials/components/analytics/umami.html create mode 100644 themes/hextra/layouts/_partials/components/blog-pager.html create mode 100644 themes/hextra/layouts/_partials/components/codeblock-copy-button.html create mode 100644 themes/hextra/layouts/_partials/components/codeblock.html create mode 100644 themes/hextra/layouts/_partials/components/comments.html create mode 100644 themes/hextra/layouts/_partials/components/giscus.html create mode 100644 themes/hextra/layouts/_partials/components/github-style-alert.html create mode 100644 themes/hextra/layouts/_partials/components/last-updated.html create mode 100644 themes/hextra/layouts/_partials/components/page-context-menu.html create mode 100644 themes/hextra/layouts/_partials/components/pager.html create mode 100644 themes/hextra/layouts/_partials/custom/banner.html create mode 100644 themes/hextra/layouts/_partials/custom/footer.html create mode 100644 themes/hextra/layouts/_partials/custom/head-end.html create mode 100644 themes/hextra/layouts/_partials/custom/navbar-title.html create mode 100644 themes/hextra/layouts/_partials/favicons.html create mode 100644 themes/hextra/layouts/_partials/footer.html create mode 100644 themes/hextra/layouts/_partials/google-analytics.html create mode 100644 themes/hextra/layouts/_partials/head.html create mode 100644 themes/hextra/layouts/_partials/language-switch.html create mode 100644 themes/hextra/layouts/_partials/navbar-link.html create mode 100644 themes/hextra/layouts/_partials/navbar-title.html create mode 100644 themes/hextra/layouts/_partials/navbar.html create mode 100644 themes/hextra/layouts/_partials/opengraph.html create mode 100644 themes/hextra/layouts/_partials/scripts.html create mode 100644 themes/hextra/layouts/_partials/scripts/asciinema.html create mode 100644 themes/hextra/layouts/_partials/scripts/core.html create mode 100644 themes/hextra/layouts/_partials/scripts/katex.html create mode 100644 themes/hextra/layouts/_partials/scripts/mathjax.html create mode 100644 themes/hextra/layouts/_partials/scripts/medium-zoom.html create mode 100644 themes/hextra/layouts/_partials/scripts/mermaid.html create mode 100644 themes/hextra/layouts/_partials/scripts/search.html create mode 100644 themes/hextra/layouts/_partials/search.html create mode 100644 themes/hextra/layouts/_partials/shortcodes/badge.html create mode 100644 themes/hextra/layouts/_partials/shortcodes/callout.html create mode 100644 themes/hextra/layouts/_partials/shortcodes/card.html create mode 100644 themes/hextra/layouts/_partials/shortcodes/cards.html create mode 100644 themes/hextra/layouts/_partials/shortcodes/tabs.html create mode 100644 themes/hextra/layouts/_partials/sidebar.html create mode 100644 themes/hextra/layouts/_partials/tags.html create mode 100644 themes/hextra/layouts/_partials/theme-toggle.html create mode 100644 themes/hextra/layouts/_partials/toc.html create mode 100644 themes/hextra/layouts/_partials/utils/extract-headings.html create mode 100644 themes/hextra/layouts/_partials/utils/file-path.html create mode 100644 themes/hextra/layouts/_partials/utils/format-date.html create mode 100644 themes/hextra/layouts/_partials/utils/fragments.html create mode 100644 themes/hextra/layouts/_partials/utils/hugo-compat/language-direction.html create mode 100644 themes/hextra/layouts/_partials/utils/hugo-compat/language-label.html create mode 100644 themes/hextra/layouts/_partials/utils/hugo-compat/language-locale.html create mode 100644 themes/hextra/layouts/_partials/utils/hugo-compat/site-data.html create mode 100644 themes/hextra/layouts/_partials/utils/hugo-compat/sites.html create mode 100644 themes/hextra/layouts/_partials/utils/icon.html create mode 100644 themes/hextra/layouts/_partials/utils/lang-link.html create mode 100644 themes/hextra/layouts/_partials/utils/page-description.html create mode 100644 themes/hextra/layouts/_partials/utils/page-width-override.html create mode 100644 themes/hextra/layouts/_partials/utils/sort-pages.html create mode 100644 themes/hextra/layouts/_partials/utils/template-url.html create mode 100644 themes/hextra/layouts/_partials/utils/title.html create mode 100644 themes/hextra/layouts/_shortcodes/asciinema.html create mode 100644 themes/hextra/layouts/_shortcodes/badge.html create mode 100644 themes/hextra/layouts/_shortcodes/callout.html create mode 100644 themes/hextra/layouts/_shortcodes/card.html create mode 100644 themes/hextra/layouts/_shortcodes/cards.html create mode 100644 themes/hextra/layouts/_shortcodes/details.html create mode 100644 themes/hextra/layouts/_shortcodes/filetree/container.html create mode 100644 themes/hextra/layouts/_shortcodes/filetree/file.html create mode 100644 themes/hextra/layouts/_shortcodes/filetree/folder.html create mode 100644 themes/hextra/layouts/_shortcodes/hextra/feature-card.html create mode 100644 themes/hextra/layouts/_shortcodes/hextra/feature-grid.html create mode 100644 themes/hextra/layouts/_shortcodes/hextra/hero-badge.html create mode 100644 themes/hextra/layouts/_shortcodes/hextra/hero-button.html create mode 100644 themes/hextra/layouts/_shortcodes/hextra/hero-container.html create mode 100644 themes/hextra/layouts/_shortcodes/hextra/hero-headline.html create mode 100644 themes/hextra/layouts/_shortcodes/hextra/hero-section.html create mode 100644 themes/hextra/layouts/_shortcodes/hextra/hero-subtitle.html create mode 100644 themes/hextra/layouts/_shortcodes/icon.html create mode 100644 themes/hextra/layouts/_shortcodes/include.html create mode 100644 themes/hextra/layouts/_shortcodes/jupyter.html create mode 100644 themes/hextra/layouts/_shortcodes/pdf.html create mode 100644 themes/hextra/layouts/_shortcodes/steps.html create mode 100644 themes/hextra/layouts/_shortcodes/tab.html create mode 100644 themes/hextra/layouts/_shortcodes/tabs.html create mode 100644 themes/hextra/layouts/_shortcodes/term.html create mode 100644 themes/hextra/layouts/archives.html create mode 100644 themes/hextra/layouts/baseof.html create mode 100644 themes/hextra/layouts/blog/list.html create mode 100644 themes/hextra/layouts/blog/single.html create mode 100644 themes/hextra/layouts/docs/list.html create mode 100644 themes/hextra/layouts/docs/single.html create mode 100644 themes/hextra/layouts/glossary.html create mode 100644 themes/hextra/layouts/glossary.markdown.md create mode 100644 themes/hextra/layouts/hextra-home.html create mode 100644 themes/hextra/layouts/home.html create mode 100644 themes/hextra/layouts/list.html create mode 100644 themes/hextra/layouts/list.rss.xml create mode 100644 themes/hextra/layouts/llms.txt create mode 100644 themes/hextra/layouts/page.markdown.md create mode 100644 themes/hextra/layouts/section.markdown.md create mode 100644 themes/hextra/layouts/single.html create mode 100644 themes/hextra/layouts/taxonomy.html create mode 100644 themes/hextra/layouts/term.html create mode 100644 themes/hextra/layouts/wide.html create mode 100644 themes/hextra/layouts/wide.markdown.md create mode 100644 themes/hextra/netlify.toml create mode 100644 themes/hextra/package-lock.json create mode 100644 themes/hextra/package.json create mode 100644 themes/hextra/playwright.config.ts create mode 100644 themes/hextra/postcss.config.mjs create mode 100644 themes/hextra/static/android-chrome-192x192.png create mode 100644 themes/hextra/static/android-chrome-512x512.png create mode 100644 themes/hextra/static/apple-touch-icon.png create mode 100644 themes/hextra/static/favicon-16x16.png create mode 100644 themes/hextra/static/favicon-32x32.png create mode 100644 themes/hextra/static/favicon.ico create mode 100644 themes/hextra/static/favicon.svg create mode 100644 themes/hextra/static/images/logo-dark.svg create mode 100644 themes/hextra/static/images/logo.svg create mode 100644 themes/hextra/static/site.webmanifest create mode 100644 themes/hextra/tests/accessibility.spec.ts create mode 100644 themes/hextra/tests/mobile-menu.spec.ts create mode 100644 themes/hextra/theme.toml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dbc592e --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Hugo +.hugo_build.lock +resources/_gen/ + +# macOS +.DS_Store diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..7f50625 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,756 @@ +/* ───────────────────────────────────────────────────────────── + RAPPORT — Theme-Overrides für Hextra + Warmes Sand/Tan auf hellem Grund (Architektur-Büro-Ästhetik) + ───────────────────────────────────────────────────────────── */ + +@import url('https://fonts.googleapis.com/css2?family=Archivo+Black&family=Inter:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,700;1,400&display=swap'); + +/* Krungthep — RAPPORT-Display-Font (Mac-System-Font lokal gebundelt) */ +@font-face { + font-family: 'Krungthep'; + src: url('/fonts/Krungthep.ttf') format('truetype'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +/* — Primary-HSL: Tan #b07848 — */ +:root { + --primary-hue: 26deg; + --primary-saturation: 42%; + --primary-lightness: 49%; + + /* RAPPORT-Palette (Light) */ + --rapport-bg: #f7f5f2; + --rapport-surface: #ffffff; + --rapport-surface2: #fbf9f5; + --rapport-dark: #1a1a18; + --rapport-dark2: #2d2d28; + --rapport-accent: #b07848; + --rapport-accent-2: #9a673b; + --rapport-accent-3: #7d5430; + --rapport-text: #1a1a18; + --rapport-text-2: #555550; + --rapport-text-3: #888880; + --rapport-text-4: #aaaaaa; + --rapport-border: #e8e3dc; + --rapport-border-2: #d8d2ca; +} + +.dark { + --primary-hue: 26deg; + --primary-saturation: 42%; + --primary-lightness: 56%; + --color-dark: var(--rapport-dark); +} + +/* — Body & Backgrounds — */ +body { + background: var(--rapport-bg); + color: var(--rapport-text); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; +} + +.dark body { + background: #181614; + color: #ece9e3; +} + +::selection { + background: rgba(176, 120, 72, 0.25); + color: var(--rapport-dark); +} + +.dark ::selection { + background: rgba(176, 120, 72, 0.35); + color: #fff; +} + +/* — Typografie — Headings serifig, Body monospaced — */ +.hextra-toc, +.content, +.prose { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; +} + +h1, h2, h3, h4, +.hextra-home h1, .hextra-home h2, .hextra-home h3, +.hx\:text-2xl, .hx\:text-3xl, .hx\:text-4xl, .hx\:text-5xl, .hx\:text-6xl { + font-family: 'Playfair Display', Georgia, serif !important; + font-weight: 700 !important; + letter-spacing: -0.01em; +} + +/* Navbar-Logo: RAPPORT in Krungthep/Archivo Black — nur navbar! */ +.hextra-navbar-title, +nav .hextra-navbar-title, +nav .hextra-max-navbar-width .hx\:font-bold { + font-family: Krungthep, 'Archivo Black', sans-serif !important; + letter-spacing: -0.02em !important; + font-weight: 900 !important; +} + +/* — Navbar mit hellem Hintergrund & dezenter Border — */ +.nav-container { + background: rgba(247, 245, 242, 0.85) !important; + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border-bottom: 1px solid var(--rapport-border) !important; +} + +.dark .nav-container { + background: rgba(24, 22, 20, 0.85) !important; + border-bottom: 1px solid #2d2926 !important; +} + +.nav-container-blur, +.dark .nav-container-blur { + background: transparent !important; +} + +/* — Sidebar — */ +aside.sidebar-container, +.sidebar-container { + background: var(--rapport-bg) !important; + border-right: 1px solid var(--rapport-border); +} + +.sidebar-container a { + color: var(--rapport-text-2); +} + +.sidebar-container a:hover { + color: var(--rapport-accent); + background: var(--rapport-surface); +} + +.sidebar-active-item, +.sidebar-container .sidebar-active-item { + background: rgba(176, 120, 72, 0.10) !important; + color: var(--rapport-accent) !important; + border-color: rgba(176, 120, 72, 0.20) !important; +} + +.dark aside.sidebar-container, +.dark .sidebar-container { + background: #181614 !important; + border-right: 1px solid #2d2926; +} + + +/* — Links — */ +a { + transition: color 0.15s; +} + +.content a, +.prose a { + color: var(--rapport-accent); + text-decoration: none; +} + +.content a:hover, +.prose a:hover { + color: var(--rapport-accent-2); + text-decoration: underline; +} + +/* Hextra-Card-Links (-Shortcode) sollen NIE unterstrichen werden */ +.hextra-card, +.hextra-card:hover, +.hextra-card:focus, +.hextra-card:active, +.hextra-card *, +.hextra-card:hover * { + text-decoration: none !important; +} + +/* Card-Hover-Effekt sauber: nur Border + leichte Schatten, kein Underline */ +a.hextra-card:hover, +a.hextra-card:focus { + outline: none; + text-decoration: none !important; +} + +/* — RAPPORT Hero-Buttons — eigene Pills, nicht von Hextra abhängig — */ +.rapport-hero-actions { + display: flex; + gap: 18px; + align-items: center; + justify-content: center; + flex-wrap: wrap; + margin-top: 8px; + margin-bottom: 8px; +} + +.rapport-btn { + display: inline-flex; + align-items: center; + gap: 9px; + border-radius: 999px; + padding: 14px 30px; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; + font-size: 12px; + font-weight: 500; + letter-spacing: 0.07em; + text-transform: uppercase; + text-decoration: none !important; + cursor: pointer; + transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease, border-color 0.18s ease; + user-select: none; + white-space: nowrap; +} + +/* Primary (Download) — Weiss mit Tiefe */ +.rapport-btn-primary { + background: #ffffff; + color: var(--rapport-text) !important; + border: 1px solid var(--rapport-border); + box-shadow: + 0 1px 0 rgba(255,255,255,0.9) inset, + 0 2px 4px rgba(0,0,0,0.04), + 0 8px 20px rgba(0,0,0,0.10), + 0 16px 40px rgba(0,0,0,0.08); +} + +.rapport-btn-primary:hover { + background: #ffffff; + border-color: var(--rapport-border-2); + color: var(--rapport-accent-2) !important; + transform: translateY(-2px); + box-shadow: + 0 1px 0 rgba(255,255,255,0.9) inset, + 0 4px 8px rgba(0,0,0,0.06), + 0 14px 28px rgba(0,0,0,0.12), + 0 24px 56px rgba(176,120,72,0.18); +} + +.rapport-btn-primary:active { + transform: translateY(0); + box-shadow: + 0 1px 0 rgba(255,255,255,0.9) inset, + 0 2px 4px rgba(0,0,0,0.06), + 0 4px 12px rgba(0,0,0,0.08); +} + +/* Secondary (Quellcode) — Outline */ +.rapport-btn-secondary { + background: transparent; + color: var(--rapport-text-2) !important; + border: 1.5px solid var(--rapport-border-2); + box-shadow: 0 2px 6px rgba(0,0,0,0.03); +} + +.rapport-btn-secondary:hover { + background: rgba(255,255,255,0.5); + border-color: var(--rapport-text-3); + color: var(--rapport-text) !important; + transform: translateY(-2px); + box-shadow: + 0 6px 14px rgba(0,0,0,0.06), + 0 12px 28px rgba(0,0,0,0.04); +} + +.rapport-btn-secondary:active { + transform: translateY(0); +} + +/* Dark-Mode-Varianten */ +.dark .rapport-btn-primary { + background: #2a2722; + color: #ece9e3 !important; + border-color: #3a3530; + box-shadow: + 0 1px 0 rgba(255,255,255,0.04) inset, + 0 2px 4px rgba(0,0,0,0.30), + 0 10px 24px rgba(0,0,0,0.45); +} + +.dark .rapport-btn-primary:hover { + background: #322e28; + color: var(--rapport-accent) !important; + box-shadow: + 0 1px 0 rgba(255,255,255,0.04) inset, + 0 4px 8px rgba(0,0,0,0.40), + 0 16px 32px rgba(0,0,0,0.50), + 0 24px 56px rgba(176,120,72,0.22); +} + +.dark .rapport-btn-secondary { + border-color: #3a3530; + color: #b8b2a8 !important; +} + +.dark .rapport-btn-secondary:hover { + background: rgba(255,255,255,0.04); + border-color: #6a6258; + color: #ece9e3 !important; +} + +/* — Hero-Badge — */ +.hextra-badge { + background: var(--rapport-surface) !important; + border: 1px solid var(--rapport-border) !important; + border-radius: 20px !important; + padding: 5px 14px !important; + font-size: 10px !important; + letter-spacing: 0.12em !important; + color: var(--rapport-text-4) !important; + text-transform: uppercase !important; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif !important; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.04); +} + +/* — Feature-Cards — */ +.hextra-feature-card { + background: var(--rapport-surface) !important; + border: 1px solid var(--rapport-border) !important; + border-radius: 14px !important; + transition: box-shadow 0.2s, transform 0.2s, border-color 0.2s !important; +} + +.hextra-feature-card:hover { + border-color: var(--rapport-border-2) !important; + transform: translateY(-2px); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.09), 0 2px 8px rgba(0, 0, 0, 0.05) !important; +} + +.hextra-feature-card h3 { + font-family: 'Playfair Display', serif !important; + color: var(--rapport-text) !important; + font-weight: 700 !important; +} + +.hextra-feature-card p { + color: var(--rapport-text-2) !important; + font-size: 12px !important; + line-height: 1.8 !important; +} + +.dark .hextra-feature-card { + background: #211e1a !important; + border-color: #2d2926 !important; +} + +.dark .hextra-feature-card h3 { + color: #ece9e3 !important; +} + +.dark .hextra-feature-card p { + color: #b8b2a8 !important; +} + +/* — Generic Cards (Hextra shortcode) — */ +.hextra-card { + background: var(--rapport-surface); + border: 1px solid var(--rapport-border); + border-radius: 12px; + transition: border-color 0.2s, transform 0.2s; +} + +.hextra-card:hover { + border-color: var(--rapport-accent-2); + transform: translateY(-1px); +} + +/* — Code-Blocks — */ +.hextra-code-block { + background: #ffffff !important; + border: 1px solid var(--rapport-border) !important; + border-radius: 12px !important; + overflow: hidden; +} + +.hextra-code-block pre, +.hextra-code-block .highlight, +.hextra-code-block .chroma { + background: transparent !important; + border: none !important; + border-radius: 0 !important; + margin: 0 !important; +} + +pre { + background: var(--rapport-surface2); + border: none; +} + +code, pre, pre code, kbd, samp, tt { + font-family: ui-monospace, 'SF Mono', Menlo, Monaco, Consolas, 'Liberation Mono', monospace !important; +} + +code { + color: var(--rapport-accent-3) !important; + background: rgba(176, 120, 72, 0.10) !important; + padding: 2px 6px; + border-radius: 4px; + font-size: 0.9em; +} + +pre code { + color: var(--rapport-text) !important; + background: transparent !important; + padding: 0; + font-size: 0.9em; +} + +.dark .hextra-code-block { + background: #1f1c19 !important; + border-color: #2d2926 !important; +} + +.dark .hextra-code-block pre, +.dark .hextra-code-block .highlight, +.dark .hextra-code-block .chroma { + background: transparent !important; + border: none !important; +} + +.dark code { + color: var(--rapport-accent) !important; + background: rgba(176, 120, 72, 0.15) !important; +} + +.dark pre code { + color: #ece9e3 !important; +} + +/* — Callouts — */ +.hextra-callout { + background: var(--rapport-surface) !important; + border-color: var(--rapport-border-2) !important; +} + +/* — Footer — */ +.hextra-footer, +footer { + background: var(--rapport-surface2); + border-top: 1px solid var(--rapport-border); + color: var(--rapport-text-3); +} + +.hextra-footer a { + color: var(--rapport-text-2); +} + +.hextra-footer a:hover { + color: var(--rapport-accent); +} + +.dark .hextra-footer, +.dark footer { + background: #15130f; + border-top: 1px solid #2d2926; + color: #888880; +} + +/* — TOC — */ +.hextra-toc a { + color: var(--rapport-text-3); +} + +.hextra-toc a:hover, +.hextra-toc .active { + color: var(--rapport-accent) !important; +} + +/* Hextra-Sticky-Bottom-Fades (TOC "Nach oben" + Sidebar-Footer) — */ +/* der hardcoded weisse Fade passt nicht zum cremefarbenen RAPPORT-Bg */ +.hextra-toc div:has(> #backToTop), +[data-toggle-animation] { + background: var(--rapport-bg) !important; + box-shadow: none !important; + border-top-color: var(--rapport-border) !important; +} + +.dark .hextra-toc div:has(> #backToTop), +.dark [data-toggle-animation] { + background: #181614 !important; + border-top-color: #2d2926 !important; + box-shadow: none !important; +} + +/* — Search — */ +.hextra-search-wrapper input { + background: var(--rapport-surface) !important; + border: 1px solid var(--rapport-border) !important; + color: var(--rapport-text) !important; +} + +.hextra-search-wrapper input:focus { + border-color: var(--rapport-accent-2) !important; +} + +/* — Tabellen — */ +table { + border-color: var(--rapport-border) !important; +} + +thead { + background: var(--rapport-surface) !important; +} + +th, td { + border-color: var(--rapport-border) !important; +} + +/* ───────────────────────────────────────────────────────────── + RAPPORT-LOGO-KARTE (Hero) — Dark Card auf hellem Grund + ───────────────────────────────────────────────────────────── */ +.rapport-logo-card { + background: var(--rapport-dark); + border: 1px solid var(--rapport-dark2); + border-radius: 999px; + padding: 28px 64px 26px; + display: inline-block; + box-shadow: 6px 0 20px rgba(0, 0, 0, 0.18), 0 6px 16px rgba(0, 0, 0, 0.12); + text-align: center; + margin: 0 auto 32px; +} + +.rapport-logo-text { + font-family: 'Krungthep', 'Archivo Black', sans-serif !important; + font-size: 48px; + letter-spacing: -0.02em; + color: #f0ede8; + line-height: 0.95; + font-weight: 400; +} + +.rapport-logo-sub { + font-size: 9px; + letter-spacing: 0.15em; + color: #f0ede8; + text-transform: uppercase; + margin-top: 8px; + font-weight: 500; +} + +/* — Hero-Meta-Pillen — */ +.rapport-meta { + display: flex; + gap: 6px; + align-items: center; + justify-content: center; + flex-wrap: wrap; + margin-top: 32px; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; +} + +.rapport-meta-item { + font-size: 10px; + letter-spacing: 0.08em; + color: var(--rapport-text-4); + text-transform: uppercase; + padding: 0 10px; + border-right: 1px solid var(--rapport-border); +} + +.rapport-meta-item:last-child { + border-right: none; +} + +/* — Status-Badges (in Arbeit / Geplant / Stabil / Neu) — */ +/* Einheitlich: weisse Pill mit dunklem Text — wie Download-Button */ +.rapport-status, +.rapport-status.active, +.rapport-status.planned, +.rapport-status.stable, +.rapport-status.new { + display: inline-block; + font-size: 10px; + letter-spacing: 0.12em; + text-transform: uppercase; + padding: 5px 14px; + border-radius: 999px; + margin-bottom: 12px; + font-weight: 600; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; + background: #ffffff; + color: var(--rapport-text); + border: 1px solid var(--rapport-border); + box-shadow: + 0 1px 0 rgba(255,255,255,0.9) inset, + 0 1px 2px rgba(0,0,0,0.04), + 0 3px 8px rgba(0,0,0,0.06); +} + +.dark .rapport-status, +.dark .rapport-status.active, +.dark .rapport-status.planned, +.dark .rapport-status.stable, +.dark .rapport-status.new { + background: #2a2722; + color: #ece9e3; + border-color: #3a3530; + box-shadow: + 0 1px 0 rgba(255,255,255,0.04) inset, + 0 1px 2px rgba(0,0,0,0.30), + 0 4px 10px rgba(0,0,0,0.35); +} + +/* — Stack-Bar (am Footer) — */ +.rapport-stack-bar { + padding: 20px 0; + border-top: 1px solid var(--rapport-border); + display: flex; + align-items: center; + gap: 20px; + flex-wrap: wrap; + margin-top: 32px; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; +} + +.rapport-stack-label { + font-size: 10px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--rapport-text-4); + flex-shrink: 0; +} + +.rapport-stack-items { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.rapport-stack-item { + font-size: 10px; + letter-spacing: 0.06em; + color: var(--rapport-text-3); + background: var(--rapport-surface); + border: 1px solid var(--rapport-border); + border-radius: 6px; + padding: 4px 10px; +} + +/* — Reduce default content max-width slightly for monospace lines — */ +.content article { + max-width: 100%; +} + +/* ───────────────────────────────────────────────────────────── + HOME-HERO — zentriertes Layout wie auf der alten Website + ───────────────────────────────────────────────────────────── */ + +/* Inhalt der home Page zentrieren */ +.hextra-home { + align-items: center !important; + text-align: center; + max-width: 100%; +} + +/* Subtitle zentriert, schmaler max-width, Playfair Display */ +.hextra-home > p, +.hextra-home .not-prose.hx\:text-xl { + font-family: 'Playfair Display', serif !important; + font-size: clamp(16px, 2.2vw, 24px) !important; + font-weight: 400 !important; + color: var(--rapport-text-3) !important; + max-width: 560px !important; + margin-left: auto !important; + margin-right: auto !important; + line-height: 1.55 !important; + text-align: center; +} + +/* Buttons immer in einer zentrierten Zeile */ +.hextra-home > div.hx\:flex, +.hextra-home > .hx\:flex { + justify-content: center; + align-items: center; +} + +/* Hero-Button matches RAPPORT design (dark on cream) */ +.hextra-home .hextra-hero-button, +a.hextra-hero-button { + background: var(--rapport-dark); + color: #f0ede8 !important; + border-radius: 20px; + padding: 13px 28px; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif !important; + font-size: 11px !important; + font-weight: 500 !important; + letter-spacing: 0.07em !important; + text-transform: uppercase !important; + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.20); + transition: all 0.18s; + display: inline-flex; + align-items: center; + gap: 9px; +} + +/* Feature-Grid linksbündig (Text in Cards) */ +.hextra-home .hextra-feature-grid, +.hextra-home .rapport-stack-bar { + text-align: left; + width: 100%; +} + +.hextra-home .hextra-feature-card * { + text-align: left; +} + +.rapport-stack-bar { + justify-content: flex-start; + text-align: left; +} + +/* Section-Heading & Eyebrow vor dem Feature-Grid zentrieren */ +.hextra-home h2, +.hextra-home > div > h2 { + text-align: center; + margin-left: auto; + margin-right: auto; +} + +/* RAPPORT-Logo bleibt zentriert */ +.hextra-home .rapport-logo-card { + margin-left: auto; + margin-right: auto; + display: block; +} + +/* Hero-Badge zentrieren */ +.hextra-home .hextra-badge, +.hextra-home > p:has(.hextra-badge) { + margin-left: auto; + margin-right: auto; + display: inline-flex !important; +} + +/* Hero-Background-Glow — sanfter Tan-Halo */ +body:has(.hextra-home)::before { + content: ""; + position: fixed; + top: 5%; + left: 50%; + transform: translateX(-50%); + width: 720px; + height: 720px; + background: radial-gradient(circle, rgba(176, 120, 72, 0.08) 0%, transparent 60%); + pointer-events: none; + z-index: 0; +} + +.dark body:has(.hextra-home)::before { + background: radial-gradient(circle, rgba(176, 120, 72, 0.12) 0%, transparent 60%); +} + +/* Navbar-Logo — RAPPORT-Schriftzug in Krungthep, etwas grösser */ +.hextra-navbar-title, +nav a[href="/"] span, +nav [class*="font-bold"] { + font-family: 'Krungthep', 'Archivo Black', sans-serif !important; + letter-spacing: -0.02em !important; + font-weight: 400 !important; + font-size: 23px !important; + line-height: 1 !important; +} + +/* RAPPORT-Meta-Pillen zentriert */ +.rapport-meta { + justify-content: center; + text-align: center; +} diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..3614f39 --- /dev/null +++ b/content/_index.md @@ -0,0 +1,107 @@ +--- +title: RAPPORT +layout: hextra-home +toc: false +--- + +{{< hextra/hero-badge >}} +
+ Pre-Release 0.8.3 · Aktiv in Entwicklung +{{< /hextra/hero-badge >}} + +
+
+
RAPPORT
+
Studio Administration
+
+
+ +
+{{< hextra/hero-subtitle >}} + Die Studio Management Software für Architekturbüros mit offenem Quellcode — Zeiterfassung, Rechnungen, Offerten, Projekte, Mitarbeiter und QR-Einzahlungsscheine in einer App. +{{< /hextra/hero-subtitle >}} +
+ + + +
+ AGPL-3.0 + Tauri + React + macOS (später Linux & Windows) + Lokal / Selfhosting +
+ +
+

ZIEL

+

Freie Studio Managementsoftware

+

Rapport wurde für eigene Zwecke konzipiert — aus dem Drang heraus, möglichst viel Open-Source-Software in einem Architekturbüro zu verwenden. Die Strukturen folgen der SIA 102 (Phasen, Honorar), die Daten bleiben lokal.

+
+ +{{< hextra/feature-grid >}} + + {{< hextra/feature-card + title="Zeiterfassung" + subtitle="Tages- und Wochenraster mit Drag & Drop. Auswertungen pro Mitarbeiter und Projekt. Ferienverwaltung mit Prorata und Jahresabschluss." + style="background: radial-gradient(ellipse at 50% 80%,rgba(176,120,72,0.10),hsla(0,0%,100%,0));" + >}} + + {{< hextra/feature-card + title="Rechnungen & Offerten" + subtitle="QR-Einzahlungsscheine, SIA-Phasen, Akonto-, Teil- und Schlussrechnungen. Offerten sind in Projekte und Rechnungen konvertierbar. PDF-Export." + style="background: radial-gradient(ellipse at 50% 80%,rgba(176,120,72,0.08),hsla(0,0%,100%,0));" + >}} + + {{< hextra/feature-card + title="Projekt- & Kundenverwaltung" + subtitle="Projekte nach SIA 102 mit Budget, Phasen und Beteiligten. Erstellung aus einer Offerte mit Verknüpfung zu Zeiterfassung und Rechnungen." + style="background: radial-gradient(ellipse at 50% 80%,rgba(176,120,72,0.12),hsla(0,0%,100%,0));" + >}} + + {{< hextra/feature-card + title="Mitarbeiter" + subtitle="Ferienverwaltung, interne Stunden / Absenzen und Lohnabrechnung. Jahresabschluss mit Überstundenausgleich." + >}} + + {{< hextra/feature-card + title="Spesen & Bürobudget" + subtitle="Spesenerfassung mit Belegupload. Jahresbudget mit Einnahmen und Ausgaben. Internes Rechnungswesen." + >}} + + {{< hextra/feature-card + title="Protokolle & Lieferscheine" + subtitle="Einfache Erstellung von Sitzungsprotokollen mit Beschlüssen und Aufgaben. Briefe und Lieferscheine im gleichen Erscheinungsbild." + >}} + + {{< hextra/feature-card + title="Auto-Updater" + subtitle="Rapport prüft beim Start automatisch auf neue Versionen und installiert Updates signiert über Tauri. Einzelne Versionen können übersprungen werden." + >}} + + {{< hextra/feature-card + title="System-Tray" + subtitle="Schnellzugriff über die Menüleiste mit Hide-on-Close. Beim Schliessen läuft Rapport im Hintergrund weiter, Cmd+Q beendet die App vollständig." + >}} + + {{< hextra/feature-card + title="Rapport Server" + subtitle="Selfhost-Stack für Studios mit mehreren Personen — Postgres, Auth, Realtime-Sync, Storage. Alles in einem Docker-Compose. Eigene Daten, eigene Domain, eigene Kontrolle." + icon="sparkles" + link="/server/" + style="background: radial-gradient(ellipse at 50% 80%,rgba(176,120,72,0.10),hsla(0,0%,100%,0));" + >}} + +{{< /hextra/feature-grid >}} + +
+ Aufgebaut auf +
+ Tauri 2 + React 19 + Vite + SIA 102 + AGPL-3.0 +
+
diff --git a/content/docs/_index.md b/content/docs/_index.md new file mode 100644 index 0000000..dbfbdae --- /dev/null +++ b/content/docs/_index.md @@ -0,0 +1,35 @@ +--- +title: Dokumentation +linkTitle: Dokumentation +weight: 1 +--- + +Vollständige Anleitung zu RAPPORT — von der Installation über den täglichen Arbeitsablauf bis zur Cloud-Variante und Eigen-Builds. + +## Erste Schritte + +{{< cards >}} + {{< card link="erste-schritte" title="Quick-Start" subtitle="In sechs Schritten von Null zur ersten Rechnung." >}} + {{< card link="installation" title="Installation" subtitle="macOS, Gatekeeper, Signatur, geplante Plattformen." >}} + {{< card link="einrichtung" title="Einrichtung" subtitle="Bürodaten, Mitarbeiter, Kunden, Projekte initial anlegen." >}} +{{< /cards >}} + +## Im Alltag + +{{< cards >}} + {{< card link="arbeitsablauf" title="Typischer Arbeitsablauf" subtitle="Kunde → Offerte → Projekt → Zeit → Rechnung." >}} + {{< card link="datenhaltung" title="Datenhaltung & Backup" subtitle="Wo die Daten liegen, wie du sie sicherst und wiederherstellst." >}} + {{< card link="troubleshooting" title="Troubleshooting" subtitle="App startet nicht, Daten weg, Update hängt." >}} +{{< /cards >}} + +## Für Fortgeschrittene + +{{< cards >}} + {{< card link="web-modus" title="Web-Modus (Multi-User)" subtitle="Rapport im Browser via Supabase — für Studios mit mehreren Nutzern." >}} + {{< card link="entwicklung" title="Entwicklung & Build" subtitle="Aus dem Quellcode kompilieren, beitragen, eigenes Release." >}} + {{< card link="changelog" title="Changelog" subtitle="Versionsgeschichte und Breaking Changes." >}} +{{< /cards >}} + +## Hilfe & Support + +Bei Bugs oder weiteren Fragen → [Issue auf Gitea](https://git.kgva.ch/karim/RAPPORT/issues). Siehe auch die [FAQ](../faq) für häufige Fragen. diff --git a/content/docs/arbeitsablauf.md b/content/docs/arbeitsablauf.md new file mode 100644 index 0000000..09ec076 --- /dev/null +++ b/content/docs/arbeitsablauf.md @@ -0,0 +1,129 @@ +--- +title: Typischer Arbeitsablauf +linkTitle: Arbeitsablauf +weight: 4 +toc: true +--- + +Vom Erstkontakt mit dem Kunden bis zur Schlussrechnung — der typische Weg eines Projekts durch Rapport. + +## Übersicht + +```text + Kunde → Offerte → Projekt → Zeit → Akonto → Schluss + anlegen erstellen (aus Offerte) erfassen -Rechnung -Rechnung + ↓ + QR-Schein +``` + +## 1 · Kunde anlegen + +**Kunden → Neu** — siehe [Einrichtung § 3](../einrichtung#3--kunden). + +Falls der Kunde später bestellt, lassen sich alle gesammelten Daten (Adresse, Honorartyp) direkt übernehmen. + +## 2 · Offerte erstellen + +**Offerten → Neu** + +| Feld | Inhalt | +|---|---| +| **Kunde** | aus Kundendatenbank wählen | +| **Bezeichnung** | Projekttitel (z. B. "Einfamilienhaus Müller, Bern") | +| **Honorartyp** | Stundensatz / SIA 102 / Pauschal | +| **Bauschätzwert** | bei SIA — Bruttowert in CHF | +| **Phasen** | bei SIA — anzuklickende Phasen | +| **Positionen** | bei Stundensatz/Pauschal — Position, Beschreibung, Betrag | + +Mit *PDF exportieren* — fertige Offerte für den Kunden. + +### Offerten-Status + +| Status | Bedeutung | +|---|---| +| **Entwurf** | noch nicht versandt | +| **Versandt** | beim Kunden, wartet auf Antwort | +| **Angenommen** | Kunde hat zugesagt — bereit zur Konvertierung | +| **Abgelehnt** | Kunde hat abgelehnt — Archiv | + +## 3 · Offerte → Projekt + +Bei Status **"Angenommen"**: Knopf **"In Projekt konvertieren"**. + +Das erzeugt: + +- Ein neues Projekt mit den Daten der Offerte (Kunde, Bezeichnung, Honorar, Phasen) +- Eine Verknüpfung zwischen Offerte und Projekt +- Die Offerte selbst bleibt im Archiv erhalten + +Siehe auch [Projekt-Feature](../../features/projekte). + +## 4 · Zeit erfassen + +**Zeit → Mitarbeiter wählen → Woche navigieren** + +- **Klick** auf einen Halbstunden-Slot → Eintrag erstellen +- **Drag** über mehrere Slots → längerer Eintrag +- **Projekt zuweisen** (Pflichtfeld) — aus aktiven Projekten +- **SIA-Phase** zuweisen (optional, für detaillierte Auswertung) + +### Spezialfälle + +- **Ferien** — eigener "Projekt"-Typ "Ferien", in der Auswertung separat +- **Krankheit/Absenz** — eigener "Projekt"-Typ, ebenfalls separat +- **Interne Stunden** — z. B. Verwaltung, Marketing, IT — eigene interne Projekte + +## 5 · Akonto-Rechnung + +Während der Projektlaufzeit — typisch nach jeder abgeschlossenen SIA-Phase oder monatlich. + +**Rechnungen → Neu → Akonto** + +| Feld | Inhalt | +|---|---| +| **Projekt** | aus aktiven Projekten | +| **Bezug** | "Akonto für Phase 31 — Vorprojekt" | +| **Betrag** | Stundensatz × Stunden, oder Phasenanteil × Bausumme | +| **Fälligkeit** | i. d. R. 30 Tage | + +Rapport zieht automatisch die geleisteten Stunden aus der Zeiterfassung — die kannst du als Basis nehmen oder überschreiben. + +PDF inkl. **QR-Einzahlungsschein** — siehe [Rechnungen-Feature](../../features/rechnungen). + +## 6 · Schlussrechnung + +Nach Projektabschluss — Differenz aus Gesamthonorar minus aller Akonto-Beträge. + +**Rechnungen → Neu → Schlussrechnung** + +Rapport rechnet die bisherigen Akonto-Rechnungen automatisch ab: + +```text +Gesamthonorar (SIA / Pauschal / Stundensatz) + − Akonto-Rechnung 1 + − Akonto-Rechnung 2 + − Akonto-Rechnung 3 + = Schlussrechnung +``` + +## 7 · Projektabschluss + +Im Projekt → Status auf **"Abgeschlossen"**. + +Das Projekt bleibt für historische Auswertungen sichtbar, taucht aber nicht mehr in der Zeiterfassungs-Auswahl auf. + +## Auswertungen + +Wöchentlich / monatlich / am Jahresende: + +- **Zeit → Auswertungen** — Stunden pro Mitarbeiter, pro Projekt, pro Phase +- **Rechnungen → Übersicht** — offene Beträge, bezahlt, Mahnungen +- **Buchhaltung → Erfolgsrechnung** — Einnahmen vs. Ausgaben +- **Mitarbeiter → Lohnabrechnung** — monatlich + +## Tipps aus dem Alltag + +- **Zeit jeden Tag erfassen** statt rückwirkend — sonst gehen Details verloren +- **Akonto regelmässig** statt einmal am Schluss — Liquidität +- **Backups** vor Jahresabschluss — siehe [Datenhaltung](../datenhaltung) +- **Briefbogen-Logo** in hoher Auflösung — sieht im PDF besser aus diff --git a/content/docs/changelog.md b/content/docs/changelog.md new file mode 100644 index 0000000..5f87a69 --- /dev/null +++ b/content/docs/changelog.md @@ -0,0 +1,114 @@ +--- +title: Changelog +linkTitle: Changelog +weight: 9 +toc: true +--- + +Versionsgeschichte von RAPPORT. Aktuelle Releases: [Gitea](https://git.kgva.ch/karim/RAPPORT/releases). + +## 0.8.3 — Aktuelle Version Aktuell + +Veröffentlicht am 2026-05-24. + +**Neu / Verbessert** + +- Diverse Verbesserungen und Bugfixes (Details werden im [Release auf Gitea](https://git.kgva.ch/karim/RAPPORT/releases/tag/0.8.3) gepflegt) + +**Bekannte Einschränkungen** + +- Builds sind Tauri-signiert, aber noch nicht Apple-notarisiert — siehe [Installation § Gatekeeper](../installation#3--erster-start-macos-gatekeeper) +- Linux- und Windows-Builds noch nicht verfügbar + +## 0.8.0–0.8.2 — Patch-Releases + +Patch-Reihe mit kleineren Verbesserungen und Bugfixes. Details siehe [Releases auf Gitea](https://git.kgva.ch/karim/RAPPORT/releases). + +## 0.7.0 — Auto-Updater & System-Tray + +**Neu** + +- **Auto-Updater** — Rapport prüft beim Start auf neue Versionen und installiert Updates signiert über Tauri. Einzelne Versionen können übersprungen werden. ([Doku](../../features/auto-updater)) +- **System-Tray** — Schnellzugriff über die Menüleiste mit Hide-on-Close. Beim Schliessen läuft Rapport im Hintergrund weiter, Cmd+Q beendet die App vollständig. ([Doku](../../features/system-tray)) +- **Quick-Open** der letzten 5 Projekte direkt aus dem Tray-Menü + +**Verbessert** + +- Schnellerer App-Start durch lazy-geladene Views +- Klarere Statusbadges in der Projekt-Übersicht + +## 0.6.x — Spesen & Buchhaltung + +**Neu** + +- Spesenerfassung mit Beleg-Upload (Base64 in localStorage) +- Jahresbudget mit Einnahmen-/Ausgaben-Übersicht +- Vereinfachte Erfolgsrechnung pro Geschäftsjahr + +**Verbessert** + +- Lohnabrechnung integriert Spesen-Erstattungen +- CSV-Export aus der Zeiterfassung + +## 0.5.x — Mitarbeiter & Lohn + +**Neu** + +- Mitarbeiterverwaltung mit Pensum, Stundensatz, Ferienanspruch +- Lohnabrechnung mit AHV/IV/EO, ALV, BVG, NBU +- Jahresabschluss mit Überstundenausgleich +- Ferien-Prorata bei Eintritt unter Jahr + +## 0.4.x — Projekte & SIA 102 + +**Neu** + +- Projektverwaltung nach SIA 102 +- Vorgeschlagene Phasen-Anteile am Gesamthonorar +- Bauschätzwert-basiertes Honorar + +**Verbessert** + +- Zeit-Auswertung pro SIA-Phase +- Akonto- und Schlussrechnungen mit automatischer Differenzberechnung + +## 0.3.x — Rechnungen & QR + +**Neu** + +- Rechnungsmodul mit QR-Einzahlungsschein (via `swissqrbill`) +- Akonto-, Teil- und Schlussrechnungen +- PDF-Export mit Bürobriefbogen +- Stundensatz-Rechnungen ziehen direkt aus der Zeiterfassung + +## 0.2.x — Zeiterfassung + +**Neu** + +- Wochenraster mit Halbstunden-Slots +- Drag & Drop zur Slot-Erfassung +- Projekt-Zuweisung pro Eintrag +- Auswertungen pro Mitarbeiter und Projekt + +## 0.1.x — Initial + +**Neu** + +- Erste Setup-Routine (Bürodaten, Mitarbeiter, Kunden) +- Briefe und Lieferscheine +- Tauri-2-Bundle für macOS + +--- + +## Roadmap + +Geplant — keine konkreten Termine: + +- **Linux-Build** (Tauri 2 unterstützt es, Bedarf nötig) +- **Windows-Build** (analog) +- **Cloud-Modus** Stable (Supabase) — derzeit in [Web-Modus](../web-modus) verfügbar, aber experimentell +- **Französische / Italienische Übersetzung** +- **PostgreSQL-Migration** aus localStorage (Knopf in der App) +- **Mobile App** (iOS Companion zur Zeiterfassung) — offen + +Wünsche oder Prioritäten → [Issue auf Gitea](https://git.kgva.ch/karim/RAPPORT/issues). diff --git a/content/docs/datenhaltung.md b/content/docs/datenhaltung.md new file mode 100644 index 0000000..c920ee3 --- /dev/null +++ b/content/docs/datenhaltung.md @@ -0,0 +1,148 @@ +--- +title: Datenhaltung & Backup +linkTitle: Datenhaltung +weight: 5 +toc: true +--- + +Wo Rapport seine Daten speichert, wie du sie sicherst und wiederherstellst. + +> **Diese Seite beschreibt die Desktop-App (Single-User).** Wer im Team arbeitet und Rapport gegen einen [Rapport Server](../../server/) betreibt, sichert stattdessen die Postgres-Datenbank — siehe [Rapport Server § Backup](../../server/#backup). + +## Speicherort (Desktop-App) + +Die Desktop-App speichert **alles lokal** — keine Cloud, kein Server. + +### macOS + +```text +~/Library/Application Support/com.rapport.app/ +``` + +Dort liegt eine einzelne **`localStorage`**-Datenbank des WebView, in der **alle** Rapport-Daten als JSON unter dem Key `studio_data_v1` gespeichert sind: + +- Bürodaten, Logo, IBAN +- Mitarbeiter, Kunden, Projekte, Offerten +- Zeit-Einträge, Rechnungen +- Spesen, Lohnabrechnungen, Protokolle +- App-Einstellungen + +> **Konsequenz:** Wer den Application-Support-Ordner kopiert, hat ein vollständiges Backup. Wer ihn löscht, verliert alle Daten. + +### Warum localStorage? + +In der Desktop-App ist Rapport eine **Single-User-Anwendung**. localStorage ist dafür: + +- **Schnell** — keine Datenbank-Roundtrips +- **Einfach** — keine Migration nötig, JSON-Schema im Code +- **Portabel** — eine Datei → ein Backup + +Für **Multi-User-Betrieb** existiert [Rapport Server](../../server/) — Postgres + Auth + Realtime in einem Docker-Compose. Wechsel zwischen Desktop- und Server-Modus erfolgt im Login-Bildschirm der App. + +## Backup-Strategien + +### A · Einfach (manuell) + +Den ganzen Ordner kopieren: + +```bash +cp -R "~/Library/Application Support/com.rapport.app" \ + "~/Documents/Rapport-Backup-$(date +%Y%m%d)" +``` + +→ Auf USB-Stick, externen Datenträger oder in die Cloud. + +### B · Time Machine + +Wenn Time Machine läuft, ist der Ordner automatisch dabei. Versionierung inbegriffen. + +> **Einschränkung:** Time Machine sichert nur lokal/USB. Für off-site-Backup separat sorgen. + +### C · Cron-Job (täglich automatisch) + +```bash +# In ~/Library/LaunchAgents/com.rapport.backup.plist hinterlegen +# oder als crontab-Eintrag: +0 22 * * * rsync -a "$HOME/Library/Application Support/com.rapport.app/" \ + "$HOME/Backups/rapport/$(date +\%Y\%m\%d)/" +``` + +### D · iCloud Drive (off-site) + +Application-Support liegt **nicht** automatisch in iCloud. Wer das will: + +```bash +# Symlink anlegen +mkdir -p "~/iCloud Drive/Rapport" +ln -s "~/Library/Application Support/com.rapport.app" "~/iCloud Drive/Rapport/data" +``` + +> **Achtung:** iCloud-Sync mit aktiver App kann zu **Race-Conditions** führen. Besser den Sync zeitversetzt (z. B. nachts via Cron). + +## Wiederherstellung + +### Vollständig (Rapport komplett tot) + +1. Rapport beenden (Cmd+Q, nicht nur Fenster zu) +2. Aktuellen Ordner umbenennen (falls noch da): + ```bash + mv "~/Library/Application Support/com.rapport.app" \ + "~/Library/Application Support/com.rapport.app.bak" + ``` +3. Backup-Ordner zurück kopieren: + ```bash + cp -R "~/Documents/Rapport-Backup-20260523" \ + "~/Library/Application Support/com.rapport.app" + ``` +4. Rapport starten + +### Selektiv (nur einzelne Daten) + +Da alle Daten in **einem JSON unter `studio_data_v1`** liegen, ist selektive Wiederherstellung **manuell**: + +1. Backup-`localStorage`-Datei öffnen (WebKit-Format → mit Tool wie [WebKit Storage Inspector] lesen, oder via Rapport DevTools) +2. Gewünschte Felder in die aktuelle Instanz übertragen + +In der Praxis: meistens lohnt sich die **vollständige** Wiederherstellung mehr. + +## Export-Funktionen (in der App) + +Aus Rapport selbst: + +| Was | Wo | Format | +|---|---|---| +| **Zeit-Auswertung** | Zeit → Export | CSV | +| **Rechnung** | Rechnung → PDF | PDF (inkl. QR) | +| **Offerte** | Offerte → PDF | PDF | +| **Lohnabrechnung** | Mitarbeiter → PDF | PDF | +| **Jahres-Buchhaltung** | Buchhaltung → Export | CSV | + +Die Exports sind für externe Verwendung (Buchhalter, Treuhänder, Archiv) gedacht — kein Full-Backup. + +## Schema-Migrationen + +Bei Updates kann sich das Datenformat ändern. Rapport hat einen **Migrations-Mechanismus**: beim Start prüft die App, ob das gespeicherte Format dem aktuellen entspricht, und migriert es automatisch. + +Code: [src/storage/migrations.js](https://git.kgva.ch/karim/RAPPORT/src/branch/main/APP/src/storage/migrations.js). + +> **Empfehlung:** Vor jedem grösseren Update ein Backup machen — Migrationen sind getestet, aber 100%-Sicherheit gibt es nicht. + +## Was wird **nicht** gespeichert? + +- **WebView-Cache** — `~/Library/Caches/com.rapport.app/` und `~/Library/WebKit/com.rapport.app/` sind sicher zu löschen (UI-Caches, regenerieren sich) +- **App-Updates** — werden bei Bedarf erneut runtergeladen +- **Logs** — `~/Library/Logs/com.rapport.app/` (geplant, derzeit nicht geschrieben) + +## Datenmenge + +Typische Grössen pro Bürojahr: + +| Inhalt | Grösse | +|---|---| +| Logo (PNG/SVG) | 50 KB – 1 MB | +| 1 Jahr Zeiterfassung (1 MA) | ~ 80 KB | +| 1 Jahr Zeiterfassung (5 MA) | ~ 400 KB | +| 50 Projekte mit je 5 Rechnungen | ~ 800 KB | +| **Total typisches Solo-Büro / Jahr** | **~ 1–2 MB** | + +localStorage hat Limits (i. d. R. ~10 MB pro Origin). Für Solo-Büros reicht das problemlos für viele Jahre. Wer das Limit erreicht oder im Team arbeitet → [Rapport Server](../../server/). diff --git a/content/docs/einrichtung.md b/content/docs/einrichtung.md new file mode 100644 index 0000000..c501c6c --- /dev/null +++ b/content/docs/einrichtung.md @@ -0,0 +1,111 @@ +--- +title: Einrichtung +linkTitle: Einrichtung +weight: 3 +toc: true +--- + +Nach der [Installation](../installation): Bürodaten, Mitarbeiter, Kunden und Projekte initial anlegen. + +## Reihenfolge + +Die Reihenfolge ist wichtig — jede Stufe baut auf der vorherigen auf: + +```text +1. Bürodaten → 2. Mitarbeiter → 3. Kunden → 4. Projekte + ▼ ▼ ▼ ▼ + Briefbogen, Zeiterfassung, Adressen, Zeiterfassung, + QR-Schein, Lohn Rechnungen Rechnungen + Login +``` + +## 1 · Bürodaten + +**Einstellungen → Bürodaten** + +| Feld | Beschreibung | Verwendung | +|---|---|---| +| **Name** | Bürobezeichnung | Briefbogen, Login-Screen | +| **Adresse** | Strasse, PLZ, Ort | Briefbogen, QR-Rechnung (Empfänger) | +| **Telefon, E-Mail** | Kontaktdaten | Briefbogen, Rechnung-Footer | +| **IBAN** | CH-IBAN (Format `CH XX XXXX …`) | **QR-Einzahlungsschein** — Pflicht | +| **Logo** | PNG/SVG-Upload | Briefbogen, Rechnung, Brief | +| **MwSt.-Nr.** | optional | Rechnung-Footer | + +> **Tipp:** Das Logo wird hochauflösend gespeichert (Base64 im localStorage). Bei sehr grossen Dateien (>1 MB) vorher in Vorschau verkleinern. + +## 2 · Mitarbeiter + +**Einstellungen → Mitarbeiter → Neu** + +| Feld | Beschreibung | +|---|---| +| **Name, Vorname** | wird in Zeiterfassung & Lohn verwendet | +| **Initialen** | Kürzel für Auswertungen (z. B. "KGE") | +| **Eintrittsdatum** | für **Prorata** der Ferien | +| **Pensum (%)** | 100 = Vollzeit | +| **Stundensatz (CHF)** | für Stundensatz-Rechnungen | +| **Ferienanspruch (Tage/Jahr)** | i. d. R. 25–30 | +| **Lohn (brutto, monatlich)** | optional, für Lohnabrechnung | + +Mindestens **ein Mitarbeiter** (z. B. der Inhaber selbst) muss angelegt sein, sonst lässt sich keine Zeit erfassen. + +### Sozialabzüge (optional) + +In Einstellungen → Lohn: + +| Abzug | Standardwert (CH) | +|---|---| +| AHV / IV / EO | 5,3 % | +| ALV | 1,1 % | +| BVG (Pensionskasse) | variabel — je Mitarbeiter | +| NBU | je nach Versicherung | + +Die Standardsätze sind hinterlegt, können aber überschrieben werden. + +## 3 · Kunden + +**Kunden → Neu** + +| Feld | Beschreibung | +|---|---| +| **Typ** | Privatperson / Firma | +| **Anrede, Name** | für Brief / Rechnung | +| **Adresse** | Strasse, PLZ, Ort, Land | +| **Ansprechperson** | bei Firmen | +| **Telefon, E-Mail** | Kontakt | +| **Honorartyp Default** | Stundensatz / SIA / Pauschal | +| **Stundensatz** | falls vom Bürostandard abweichend | +| **MwSt.-pflichtig** | ja/nein | + +## 4 · Projekte + +**Projekte → Neu** + +| Feld | Beschreibung | +|---|---| +| **Projekt-Nr.** | freie Form, oder generiert (`2026-001`) | +| **Bezeichnung** | Kurztitel | +| **Standort** | Adresse | +| **Kunde** | aus Kundendatenbank | +| **Bauschätzwert** | für SIA-Phasen-Honorar | +| **SIA-Phasen** | Vorprojekt / Bauprojekt / … — alle anwählbar | +| **Honorartyp** | Stundensatz / SIA 102 / Pauschal | +| **Status** | aktiv / pausiert / abgeschlossen | + +### SIA-102-Phasen + +Wenn als Honorartyp **SIA 102** gewählt ist, werden die Phasen-Anteile am Gesamthonorar vorgeschlagen — siehe [Projekt-Feature](../../features/projekte#sia-102). + +## Checkliste + +Nach diesen vier Schritten ist Rapport einsatzbereit: + +- [ ] Bürodaten inkl. IBAN erfasst +- [ ] Mindestens ein Mitarbeiter angelegt +- [ ] Erster Kunde angelegt +- [ ] Erstes Projekt angelegt +- [ ] Eine Test-Zeitbuchung erfasst — wird das Projekt korrekt zugewiesen? +- [ ] Eine Test-Rechnung erstellt — kommt der QR-Schein sauber raus? + +Wenn alles funktioniert: [Typischer Arbeitsablauf](../arbeitsablauf). diff --git a/content/docs/entwicklung.md b/content/docs/entwicklung.md new file mode 100644 index 0000000..6c4eace --- /dev/null +++ b/content/docs/entwicklung.md @@ -0,0 +1,146 @@ +--- +title: Entwicklung & Build +linkTitle: Entwicklung +weight: 7 +toc: true +--- + +Aus dem Quellcode kompilieren, beitragen, eigenes Release erzeugen. + +## Voraussetzungen + +| Tool | Version | +|---|---| +| **Node.js** | ≥ 20 (für Vite 8) | +| **npm** | ≥ 10 | +| **Rust toolchain** | ≥ 1.77.2 (via `rustup`) | +| **Plattform-Tools** | siehe [Tauri Prerequisites](https://v2.tauri.app/start/prerequisites/) | + +Plattform-spezifisch: + +- **macOS:** Xcode Command Line Tools (`xcode-select --install`) +- **Windows:** Microsoft C++ Build Tools + WebView2 +- **Linux:** `webkit2gtk-4.1`, `librsvg2-dev`, `libayatana-appindicator3-dev`, `build-essential` + +## Setup + +```bash +git clone https://git.kgva.ch/karim/RAPPORT.git +cd RAPPORT/APP +npm install +``` + +## Entwicklung + +### Web-Modus (HMR, schnellster Loop) + +```bash +npm run dev # http://localhost:3000 +``` + +- Hot-Module-Replacement +- Schnellster Iteration-Loop für UI-Arbeit +- Datenpersistierung: Browser-localStorage + +### Native Window (Tauri-Fenster mit Desktop-Integration) + +```bash +npx tauri dev +``` + +- Echtes Tauri-Fenster +- System-Tray, Updater, native APIs verfügbar +- Erster Start dauert lange (Rust-Compile) + +## Architektur in einem Absatz + +> RAPPORT ist eine monolithische SPA: ein React-Root in [App.jsx](https://git.kgva.ch/karim/RAPPORT/src/branch/main/APP/src/App.jsx) hält den **gesamten** App-State in einem `useState({...})`, persistiert ihn synchron in `localStorage` unter `studio_data_v1`, und übergibt ihn als Props an lazy-geladene Views. **Kein Routing-Framework**, **kein State-Library**, **kein TypeScript**, **kein CSS-Framework**. Der **Rust-Teil** ist 109 Zeilen und macht nur drei Dinge: System-Tray, Window-Hide-on-Close, Plugin-Registrierung (Updater, Process, Log). **Keine** `#[tauri::command]` — Frontend ↔ Backend kommuniziert nur über das Event `rapport:navigate` (Tray → Frontend). + +Detaillierte Karte: [ARCHITECTURE.md](https://git.kgva.ch/karim/RAPPORT/src/branch/main/APP/ARCHITECTURE.md). + +## Verzeichnis-Karte + +```text +APP/ +├── src/ React 19 (kein TS, nur .jsx) +│ ├── App.jsx Root: State, Navigation, Sidebar, Modals +│ ├── constants.js STORAGE_KEY, NAV_ITEMS, defaultData, SIA-Phasen +│ ├── utils.js Business-Logik: Kalkulation, QR-Bill, CSV, Lohn +│ ├── storage/adapter.js LocalStorageAdapter (Phase 1), SupabaseAdapter (Phase 2) +│ ├── storage/migrations.js Schema-Migrationen +│ ├── views/ 20 Top-Level-Screens, lazy-geladen +│ ├── components/UI.jsx StatusBadge, Modal, FormField, … +│ ├── components/Update* Auto-Update-UI +│ ├── print/ PrintComponents.jsx — alle Druckansichten +│ └── utils/updater.js @tauri-apps/plugin-updater Wrapper +│ +├── supabase/migrations/ Postgres-Schema für Cloud-Variante +│ +├── src-tauri/ Rust-Backend (Tauri 2.10.3) +│ ├── src/lib.rs ~103 Z. — Tray, Window-Events, Plugins +│ ├── tauri.conf.json Updater-URL, Public-Key, CSP, Bundle-Targets +│ └── capabilities/ Tauri Permissions +│ +├── scripts/release.sh Build + Sign + latest.json erzeugen +├── latest.json Updater-Manifest +└── deploy/ Docker-Compose für Web-Modus +``` + +## Konventionen + +- **JavaScript** statt TypeScript — bewusste Entscheidung für Solo-Dev-Velocity +- **Inline-Styles** statt CSS-Framework +- **kein Routing-Framework** — `view`-State in App.jsx triggert Komponente +- **JSON-Schema implizit** — definiert durch `defaultData` in `constants.js` +- **Migrationen** als reine Funktionen in [storage/migrations.js](https://git.kgva.ch/karim/RAPPORT/src/branch/main/APP/src/storage/migrations.js) + +## Build + +### Desktop (Tauri DMG) + +```bash +npm run build # erst Vite-Build (dist/) +npx tauri build # dann Tauri-Bundle (DMG) +``` + +Output: `src-tauri/target/release/bundle/dmg/Rapport__aarch64.dmg` + +### Web (statisches Bundle) + +```bash +npm run build # Output: dist/ (~500 KB) +``` + +Für Hosting auf einem Static-Server (z. B. Nginx, Caddy, GitHub Pages). Siehe [Web-Modus](../web-modus). + +## Release-Workflow + +```bash +# 1 · Version bumpen in package.json + Cargo.toml + tauri.conf.json +./scripts/release.sh 0.7.1 + +# 2 · Build mit Signatur +TAURI_SIGNING_PRIVATE_KEY=$(cat ~/.tauri/rapport-key) \ +TAURI_SIGNING_PRIVATE_KEY_PASSWORD=… \ +npx tauri build + +# 3 · latest.json aktualisieren mit neuer Signatur + +# 4 · DMG + latest.json auf Gitea Releases hochladen +``` + +## Beitragen + +[Issues & Pull Requests](https://git.kgva.ch/karim/RAPPORT) sind willkommen. Wertvoll sind: + +- **Bug-Reports** mit Reproduktionsschritten +- **Workflow-Verbesserungen** aus dem realen Büroalltag +- **Vorlagen** (Briefe, Protokolle, Lieferscheine) für andere Büros +- **Übersetzungen / Internationalisierung** (FR, IT) +- **Linux-/Windows-Builds** und plattformspezifische Fixes + +Vor grösseren Änderungen → Issue zum Diskutieren. + +## Lizenz + +**GNU AGPL-3.0-or-later** — eigene Builds erlaubt, Modifikationen müssen unter derselben Lizenz veröffentlicht werden, wenn die Software als Service angeboten wird. diff --git a/content/docs/erste-schritte.md b/content/docs/erste-schritte.md new file mode 100644 index 0000000..7ecbb37 --- /dev/null +++ b/content/docs/erste-schritte.md @@ -0,0 +1,62 @@ +--- +title: Erste Schritte +linkTitle: Erste Schritte +weight: 1 +toc: true +--- + +Von der Installation bis zur ersten Rechnung — in sechs Schritten. + +## 01 · Installation + +DMG von [Gitea Releases](https://git.kgva.ch/karim/RAPPORT/releases) herunterladen. Rapport in den **Programme-Ordner** ziehen. Beim ersten Start: *Systemeinstellungen → Datenschutz & Sicherheit* öffnen und Rapport zulassen. + +Die Pre-Release-Builds sind **signiert über Tauri**, aber (noch) nicht über die Apple-Notarisierung gegangen — daher der manuelle Freigabe-Schritt. + +## 02 · Einrichtung + +In den **Einstellungen** hinterlegen: + +- **Bürodaten** — Name, Adresse, IBAN, Logo +- **Mitarbeiter** — Namen, Pensum, Stundensatz, Ferienanspruch +- **Kunden** — Adresse, Ansprechperson, Honorartyp +- **Projekte** — SIA-102-Phasen, Budget, Beteiligte + +Danach ist die Zeiterfassung bereit. + +## 03 · Zeiterfassung + +Im Modul **Zeit**: + +1. Mitarbeiter wählen +2. Woche navigieren +3. Stunden per **Klick** oder **Drag** erfassen +4. Jedem Eintrag ein Projekt zuweisen + +Auswertungen pro Mitarbeiter und pro Projekt sind unter *Zeit → Auswertungen* abrufbar. Halbe Tage und Mehrfacheinträge pro Slot werden unterstützt. + +## 04 · Rechnungen + +Aus einer **Offerte** oder **direkt** erstellen: + +- SIA-Phasen, Stundensatz oder Pauschal wählen +- Akonto-, Teil- oder Schlussrechnung +- Mit *PDF exportieren* wird die fertige Rechnung inkl. **QR-Einzahlungsschein** generiert + +Offerten lassen sich nahtlos in Projekte und Rechnungen konvertieren. + +## 05 · Backup + +In der Desktop-App liegen alle Daten als **JSON** im Applikationsordner: + +```text +~/Library/Application Support/com.rapport.app/ +``` + +Für ein Backup reicht es, diesen Ordner zu kopieren — z. B. auf einen externen Datenträger oder in die Cloud. Empfohlen: regelmässig (wöchentlich oder via Time Machine). Im Server-Modus läuft das Backup über Postgres-Dumps — siehe [Rapport Server § Backup](../../server/#backup). + +## 06 · Probleme melden + +Ein [Issue auf Gitea](https://git.kgva.ch/karim/RAPPORT/issues) erstellen — mit kurzer Beschreibung, was passiert ist. **Screenshots helfen.** Bitte die Rapport-Version (links unten in der App) angeben. + +> **Tipp:** Wenn die App nicht mehr startet, hilft oft, den Cache-Ordner zu sichern und neu zu starten. Die JSON-Daten selbst bleiben unverändert. diff --git a/content/docs/installation.md b/content/docs/installation.md new file mode 100644 index 0000000..f5aa0b9 --- /dev/null +++ b/content/docs/installation.md @@ -0,0 +1,94 @@ +--- +title: Installation +linkTitle: Installation +weight: 2 +toc: true +--- + +Schritt-für-Schritt-Anleitung für die Installation der Desktop-App. + +## Voraussetzungen + +| Plattform | Status | Versionen | +|---|---|---| +| **macOS** Apple Silicon (M1 – M4) | ✅ Unterstützt | macOS 12+ | +| **macOS** Intel | ⚠ Build auf Anfrage | macOS 12+ | +| **Linux** | 🕐 Geplant | — | +| **Windows** | 🕐 Geplant | — | + +Eine Portierung auf Linux und Windows ist mit Tauri 2 möglich. [Issue erstellen](https://git.kgva.ch/karim/RAPPORT/issues/new), wenn du eine Plattform brauchst. + +## 1 · Download + +Aktueller Build: [Downloads-Seite](../../downloads) oder direkt [Releases auf Gitea](https://git.kgva.ch/karim/RAPPORT/releases). + +| Datei | Plattform | +|---|---| +| `RAPPORT__aarch64.dmg` | macOS Apple Silicon | +| `RAPPORT__x86_64.dmg` | macOS Intel (auf Anfrage) | + +## 2 · DMG öffnen & installieren + +1. DMG doppelklicken +2. Rapport.app in den **Applications**-Ordner ziehen +3. DMG auswerfen + +## 3 · Erster Start (macOS Gatekeeper) + +Beim ersten Start verweigert macOS den Start, weil die Pre-Release-Builds **Tauri-signiert**, aber (noch) **nicht Apple-notarisiert** sind. + +### Lösung + +1. **Systemeinstellungen → Datenschutz & Sicherheit** öffnen +2. Bis ganz nach unten scrollen — es erscheint: + > "Rapport" wurde blockiert, da es nicht von einem identifizierten Entwickler stammt. +3. Auf **"Trotzdem öffnen"** klicken +4. Bestätigen + +Ab dem zweiten Start läuft Rapport ohne Rückfragen. + +### Alternative (Terminal) + +Falls der GUI-Weg nicht funktioniert: + +```bash +xattr -d com.apple.quarantine /Applications/Rapport.app +``` + +Das entfernt das Quarantäne-Flag und macOS akzeptiert den Start. + +## 4 · Erstes Setup + +Beim ersten Start zeigt Rapport den **Setup-Bildschirm**. Hier werden die Stammdaten erfasst — siehe [Einrichtung](../einrichtung). + +## 5 · Update-Strategie + +Ab Version 0.7.0 prüft Rapport beim Start automatisch auf neue Versionen (siehe [Auto-Updater](../../features/auto-updater)). Updates können in den Einstellungen deaktiviert werden. + +Manuelle Updates: einfach das neuere DMG installieren — die Daten bleiben erhalten, da sie unabhängig von der App liegen (siehe [Datenhaltung](../datenhaltung)). + +## Deinstallation + +```bash +# App entfernen +rm -rf /Applications/Rapport.app + +# Daten entfernen (optional!) +rm -rf "~/Library/Application Support/com.rapport.app" + +# Caches +rm -rf "~/Library/Caches/com.rapport.app" +rm -rf "~/Library/WebKit/com.rapport.app" +``` + +> **Achtung:** Schritt 2 löscht **alle Rapport-Daten unwiederbringlich**. Vorher Backup machen — siehe [Datenhaltung](../datenhaltung). + +## Bekannte Probleme + +| Problem | Lösung | +|---|---| +| App lässt sich nicht öffnen trotz Freigabe | Terminal-Variante mit `xattr` | +| "Rapport is damaged" | DMG erneut von Gitea ziehen (Browser-Cache hat evtl. Müll) | +| Schwarzer Bildschirm beim Start | `~/Library/WebKit/com.rapport.app` löschen, neu starten | + +Mehr unter [Troubleshooting](../troubleshooting). diff --git a/content/docs/troubleshooting.md b/content/docs/troubleshooting.md new file mode 100644 index 0000000..4d843f8 --- /dev/null +++ b/content/docs/troubleshooting.md @@ -0,0 +1,203 @@ +--- +title: Troubleshooting +linkTitle: Troubleshooting +weight: 8 +toc: true +--- + +Typische Probleme und Lösungen. Wenn dein Problem nicht dabei ist → [Issue auf Gitea](https://git.kgva.ch/karim/RAPPORT/issues). + +## App startet nicht + +### "Rapport ist beschädigt" beim ersten Start + +**Ursache:** macOS Gatekeeper blockt unsignierte/nicht-notarisierte Apps. + +**Lösung:** siehe [Installation § 3](../installation#3--erster-start-macos-gatekeeper). Kurz: + +```bash +xattr -d com.apple.quarantine /Applications/Rapport.app +``` + +### App startet, zeigt aber schwarzen Bildschirm + +**Ursache:** WebView-Cache korrupt. + +**Lösung:** + +```bash +rm -rf "~/Library/Caches/com.rapport.app" +rm -rf "~/Library/WebKit/com.rapport.app" +``` + +App neu starten. Daten gehen dabei **nicht** verloren (liegen in `Application Support`, nicht im Cache). + +### App stürzt sofort beim Start ab + +**Ursache:** wahrscheinlich beschädigte JSON-Daten in `studio_data_v1`. + +**Diagnose:** + +```bash +# Daten ansehen (DevTools-Output) +open "~/Library/Application Support/com.rapport.app" +``` + +**Lösung:** + +1. Backup wiederherstellen (siehe [Datenhaltung](../datenhaltung#wiederherstellung)) +2. **Oder** als letzter Ausweg: Daten zurücksetzen + ```bash + mv "~/Library/Application Support/com.rapport.app" \ + "~/Library/Application Support/com.rapport.app.bad" + ``` + App neu starten → erstellt frische Daten. Anschliessend Setup-Screen. + +## Daten weg + +### Nach einem App-Update fehlen Einträge + +**Ursache:** mögliche fehlgeschlagene Migration. + +**Sofortmassnahme:** + +1. Rapport **beenden** (Cmd+Q, nicht nur Fenster zu) +2. **Aktuelles Datenverzeichnis sichern**: + ```bash + cp -R "~/Library/Application Support/com.rapport.app" \ + "~/Documents/Rapport-Notfall-$(date +%Y%m%d-%H%M)" + ``` +3. [Issue erstellen](https://git.kgva.ch/karim/RAPPORT/issues/new) mit: + - Version vor dem Update (falls bekannt) + - Version nach dem Update + - Was fehlt + - Optional: gesicherter Datenordner (via Pastebin oder verschlüsselt zugesandt) + +### localStorage voll + +**Symptom:** Rapport schreibt Fehler-Toast "Speicher voll" beim Sichern. + +**Ursache:** macOS WebView limitiert localStorage auf ~10 MB pro Origin. + +**Lösung:** + +- Sehr grosse Logos durch kleinere ersetzen (Bürodaten → Logo) +- Belege (Spesen) selektiv löschen oder als externe Datei archivieren +- Auf [Web-Modus](../web-modus) wechseln (Postgres ohne praktisches Limit) + +## Updates + +### Auto-Update findet nichts + +**Diagnose:** + +```bash +curl -s https://git.kgva.ch/karim/RAPPORT/raw/branch/main/APP/latest.json +``` + +→ sollte JSON liefern. Wenn nicht: Server-/Netzwerkproblem. + +### Update lädt, lässt sich aber nicht installieren + +**Ursache:** Signaturprüfung scheitert (Public-Key in App ≠ Signatur in `latest.json`). + +**Lösung:** Manuelles Update — DMG direkt von [Releases](https://git.kgva.ch/karim/RAPPORT/releases) laden und installieren. Daten bleiben erhalten. + +### "Diese Version überspringen" rückgängig machen + +In **Einstellungen → Updates** → *Übersprungene Versionen zurücksetzen*. Beim nächsten Start wird die Version wieder angeboten. + +## PDF / QR-Schein + +### QR-Schein hat falsche Daten + +**Diagnose-Checkliste:** + +- [ ] IBAN korrekt? (CH, 21 Zeichen, keine Leerzeichen) +- [ ] Empfänger-Adresse vollständig? (PLZ und Ort beide gefüllt) +- [ ] Schuldner-Adresse vollständig? +- [ ] Betrag > 0? +- [ ] Referenz nicht zu lang? (max 27 Zeichen) + +QR-Bibliothek: [`swissqrbill`](https://github.com/schoero/SwissQRBill) — bei merkwürdigen Fehlern dort nachschauen. + +### PDF-Export ist leer / weisses Blatt + +**Ursache:** Print-View hat keine Daten (möglicherweise Race-Condition beim Laden). + +**Lösung:** Rechnung schliessen, erneut öffnen, dann PDF. + +### PDF-Schrift sieht falsch aus + +**Ursache:** Web-Schrift nicht geladen, Fallback greift. + +**Lösung:** Vor dem Drucken warten, bis das Vorschau-Bild komplett geladen ist (3–5 Sek). + +## System-Tray + +### Tray-Icon erscheint nicht + +**Plattform-Hinweis:** Tray-Icons unter macOS sind bei extrem voller Menüleiste oder unter "Bartender"/"Hidden Bar" eventuell unsichtbar. + +**Diagnose:** + +```bash +ps aux | grep -i rapport +``` + +→ wenn Prozess läuft, aber kein Icon: in den Tray-Manager-Apps prüfen. + +**Konfiguration:** Einstellungen → System-Tray → *Tray-Icon ausblenden* (aus → Icon erzwingen). + +### Tray-Menü reagiert nicht / hängt + +**Lösung:** App via *Activity Monitor* hart beenden und neu starten. Daten gehen nicht verloren (alle Schreibungen sind synchron in localStorage). + +## Web-Modus + +### Login-Screen zeigt keine Server-URL + +**Ursache:** `.env.production` enthielt nicht den richtigen `VITE_SUPABASE_URL` zur Build-Zeit. + +**Lösung:** `.env.production` prüfen, dann `npm run build` neu, Container restart. + +### Realtime-Updates kommen nicht an + +**Ursache:** Websocket-Support fehlt im Reverse Proxy. + +**Lösung:** In Nginx Proxy Manager für `api.*` Websocket Support aktivieren. + +Siehe [Web-Modus § Troubleshooting](../web-modus#troubleshooting). + +## Debug-Informationen sammeln + +Bei einem Issue helfen folgende Infos: + +```bash +# Rapport-Version +defaults read /Applications/Rapport.app/Contents/Info.plist CFBundleShortVersionString + +# macOS-Version +sw_vers + +# Architektur +uname -m + +# Datenverzeichnis-Grösse +du -sh "~/Library/Application Support/com.rapport.app" + +# Cache-Verzeichnis-Grösse +du -sh "~/Library/Caches/com.rapport.app" +``` + +→ Bei Issue mit anhängen. + +## Wenn nichts hilft + +[Neues Issue auf Gitea](https://git.kgva.ch/karim/RAPPORT/issues/new) mit: + +- Was du gemacht hast +- Was passiert ist +- Was du erwartet hättest +- Screenshots (auch von DevTools-Konsole falls möglich) +- Rapport-Version und macOS-Version diff --git a/content/docs/web-modus.md b/content/docs/web-modus.md new file mode 100644 index 0000000..349d66b --- /dev/null +++ b/content/docs/web-modus.md @@ -0,0 +1,53 @@ +--- +title: Web-Modus (Multi-User) +linkTitle: Web-Modus +weight: 6 +toc: true +--- + +> **Hinweis:** Der frühere Supabase-basierte Web-Modus wurde durch **[Rapport Server](../../server/)** abgelöst — den vollständigen Selfhost-Stack mit eigenem Postgres, Auth, Realtime und Storage. Keine externe Cloud-Abhängigkeit mehr. +> +> Diese Seite bleibt als Referenz erhalten, der **empfohlene Weg** für Multi-User-Setups ist **[Rapport Server](../../server/)**. + +## Wann brauchst du das? + +| Anwendungsfall | Empfehlung | +|---|---| +| **Solo-Büro, ein Mac** | Desktop-App — siehe [Installation](../installation) | +| **2–5 Personen, gleicher Standort** | [Rapport Server](../../server/) auf einem Mac Mini im LAN | +| **Verteiltes Team / Home-Office** | [Rapport Server](../../server/) mit SSL + Reverse Proxy | +| **Hosted Backend (eigener VPS)** | [Rapport Server](../../server/) auf Linux-VPS | + +## Architektur (Kurzfassung) + +```text +┌────────────┐ HTTPS ┌──────────────┐ SQL ┌────────────┐ +│ Browser │ ──────────────│ nginx │ ─────────────│ Postgres │ +│ / Desktop │ │ (Frontend) │ │ + GoTrue │ +└────────────┘ └──────────────┘ │ + REST │ + │ + Realtime │ + │ + Storage │ + └────────────┘ +``` + +- **Frontend:** dieselbe React-App, aber Vite-Build statt Tauri (`npm run build`) +- **Backend:** Postgres-Stack ([Rapport Server](../../server/)) +- **Auth:** E-Mail / Passwort über GoTrue +- **Storage:** Belege, Logos in Object-Storage + +## Setup + +Alle Setup-Schritte (Repo klonen, `.env` erstellen, Migrations syncen, Docker-Compose starten, Reverse-Proxy konfigurieren) sind in **[Rapport Server](../../server/)** dokumentiert. + +## Migration Desktop → Cloud + +Wer mit der Desktop-App startet und später auf den Web-Modus wechseln möchte: + +- **Aktuell:** manueller Export aus Rapport (CSV/PDF) und manuelles Wiederanlegen im neuen Setup +- **Geplant:** *"localStorage → Postgres"*-Import-Knopf direkt in der App + +Status: [Issue auf Gitea](https://git.kgva.ch/karim/RAPPORT/issues). + +## Troubleshooting + +Siehe [Rapport Server § Troubleshooting](../../server/) oder [allgemeine Troubleshooting-Seite](../troubleshooting). diff --git a/content/downloads/_index.md b/content/downloads/_index.md new file mode 100644 index 0000000..df05579 --- /dev/null +++ b/content/downloads/_index.md @@ -0,0 +1,99 @@ +--- +title: Downloads +linkTitle: Downloads +weight: 3 +toc: true +--- + +Aktuelle Builds von Rapport. Quellcode und ältere Versionen auf [Gitea](https://git.kgva.ch/karim/RAPPORT/releases). + +Rapport besteht aus zwei Komponenten: + +| Komponente | Für wen | Aktuelle Version | +|---|---|---| +| **Desktop-App** | Solo-Büro, lokale Datenhaltung | 0.8.3 | +| **Rapport Server** | Team / Multi-User / Selfhost | 0.1.0 | + +--- + +## Desktop-App — Pre-Release 0.8.3 + +Aktuelle Version + +**Neuerungen** — siehe [Changelog](../docs/changelog#083) für Details. + +### macOS + +| Architektur | Download | +|---|---| +| **Apple Silicon (M1–M4)** | [RAPPORT_0.8.3_aarch64.dmg](https://git.kgva.ch/karim/RAPPORT/releases/download/0.8.3/RAPPORT%20PRE-RELEASE_0.8.3_aarch64.dmg) | +| **Intel (x86_64)** | [auf Anfrage](https://git.kgva.ch/karim/RAPPORT/issues/new) | + +> **Erstinstallation:** *Systemeinstellungen → Datenschutz & Sicherheit* öffnen und Rapport zulassen. Die Builds sind über Tauri signiert, aber (noch) nicht Apple-notarisiert. + +### Linux & Windows + +**Geplant.** Rapport basiert auf Tauri 2 — eine Portierung ist möglich, sobald der Bedarf besteht. [Issue erstellen](https://git.kgva.ch/karim/RAPPORT/issues/new), wenn du eine Plattform brauchst. + +--- + +## Rapport Server — 0.1.0 + +Erstes Release + +Selfhost-Backend für Multi-User-Setups — Postgres, Auth, Realtime-Sync, Storage. Details und Setup-Anleitung auf der [Server-Seite](../server). + +### Server-App (macOS) + +GUI zum Starten, Stoppen und Verwalten der Server-Instanz auf einem Mac (Mac Mini, Studio-Rechner). Bündelt den Docker-Compose-Stack. + +| Architektur | Download | +|---|---| +| **Apple Silicon (M1–M4)** | [RAPPORT_SERVER_0.1.0_aarch64.dmg](https://git.kgva.ch/karim/rapport-server/releases/download/0.1.0/RAPPORT%20SERVER_0.1.0_aarch64.dmg) | +| **Intel (x86_64)** | [auf Anfrage](https://git.kgva.ch/karim/rapport-server/issues/new) | + +> **Voraussetzung:** [Colima](https://github.com/abiosoft/colima) oder Docker Desktop muss installiert sein. Die App verwaltet den Container-Stack darüber. + +### Docker (Linux / VPS / Headless) + +Für Linux-Server, NAS oder VPS — der reine Docker-Compose-Stack ohne GUI. + +```bash +git clone https://git.kgva.ch/karim/rapport-server.git +cd rapport-server +git checkout 0.1.0 +cp .env.example .env +# .env editieren (POSTGRES_PASSWORD, JWT_SECRET, SITE_URL) +docker compose up -d +``` + +Vollständige Anleitung mit `.env`-Variablen, Reverse-Proxy und Backup → [Server-Seite](../server). + +Container-Images werden von Docker Hub / Gitea-Registry gezogen — `docker compose pull` aktualisiert auf die neuesten Tags. + +--- + +## Quellcode + +**Desktop-App** — Tauri / React: + +```bash +git clone https://git.kgva.ch/karim/RAPPORT.git +cd RAPPORT/APP +npm install +npm run tauri dev +``` + +Voraussetzungen: Node 20+, Rust (stable), Xcode-CLI (macOS). + +**Rapport Server**: + +```bash +git clone https://git.kgva.ch/karim/rapport-server.git +``` + +Beide Repos auf [Gitea](https://git.kgva.ch/karim) — Issues und PRs willkommen. + +## Auto-Update + +Seit 0.7.0 prüft die Desktop-App beim Start automatisch auf neue Versionen — siehe [Auto-Updater](../features/auto-updater). Für Rapport Server: `git pull && docker compose pull && docker compose up -d`. diff --git a/content/faq/_index.md b/content/faq/_index.md new file mode 100644 index 0000000..9a80a8d --- /dev/null +++ b/content/faq/_index.md @@ -0,0 +1,65 @@ +--- +title: FAQ +linkTitle: FAQ +weight: 4 +toc: true +--- + +Häufige Fragen zu RAPPORT. Bei Bugs oder weiteren Fragen → [Issue auf Gitea](https://git.kgva.ch/karim/RAPPORT/issues). + +## Für wen ist Rapport gedacht? + +Für **kleine Architekturbüros in der Schweiz**. Die Strukturen folgen der SIA 102 (Phasen, Honorar). Rapport wurde für den internen Gebrauch entwickelt und wird mit diesem Projekt öffentlich geteilt. + +## Ist Rapport kostenlos? + +**Ja, vollständig.** Quellcode unter GNU AGPL-3.0-or-later. Keine versteckten Kosten, keine Telemetrie. Die Daten bleiben lokal auf deinem Computer bzw. in deiner Instanz. Du hast die komplette Kontrolle über deine Daten. + +## Welche Systeme werden unterstützt? + +Aktuell nur **macOS** (Intel & Apple Silicon). Rapport basiert auf Tauri — eine Portierung auf **Linux** und **Windows** ist möglich, sobald der Bedarf seitens Community besteht. + +## Wo werden die Daten gespeichert? + +Rapport unterstützt **zwei Modi**, beide selbst-gehostet: + +- **Desktop-App (Single-User)** — alle Daten liegen lokal auf deinem Mac (`localStorage` im Applikationsordner). Kein Server nötig, kein Cloud-Account, keine Telemetrie. +- **Server-Modus (Multi-User)** — Daten in einer eigenen PostgreSQL-Datenbank über [Rapport Server](../server). Mehrere Personen, Realtime-Sync, eigene Domain. Wechsel direkt im Login-Bildschirm der App. + +In beiden Fällen bleibt die Kontrolle über die Daten bei dir. + +## Ist die Software stabil genug für den Betrieb? + +**Noch nicht.** Aktuell befindet sich Rapport in aktiver Entwicklung (Pre-Release **0.8.3**). Funktionen können sich ändern, Bugs sind möglich. Regelmässige Backups sind empfohlen. Testen und Feedback geben ist erwünscht. + +## Was ist mit dem QR-Einzahlungsschein? + +Rapport erzeugt **Schweizer QR-Rechnungen** nach Norm — direkt eingebettet in das PDF der Rechnung. IBAN, Bürodaten und Empfänger werden aus den Einstellungen bzw. den Kundendaten gezogen. Akonto-, Teil- und Schlussrechnungen werden unterstützt. + +## Wie funktioniert die Zeiterfassung? + +Tages- und Wochenraster mit **Drag & Drop**. Jeder Eintrag wird einem Projekt zugewiesen. Auswertungen pro Mitarbeiter und Projekt sind unter *Zeit* abrufbar. Ferienverwaltung mit Prorata und Jahresabschluss mit Überstundenausgleich. + +## Kann ich zum Projekt beitragen? + +**Ja.** Issues und Pull Requests sind willkommen auf [Gitea](https://git.kgva.ch/karim/RAPPORT). Rapport ist kein Framework — konkrete Verbesserungen für den **Büroalltag** sind am wertvollsten: + +- Bug-Reports mit Reproduktionsschritten +- Workflow-Verbesserungen aus dem realen Büroalltag +- Vorlagen (Briefe, Protokolle, Lieferscheine) für andere Büros +- Übersetzungen / Internationalisierung + +## Wie erhalte ich Hilfe bei einem Problem? + +Ein [Issue auf Gitea](https://git.kgva.ch/karim/RAPPORT/issues) ist der beste Weg. Bitte beschreibe, was du gemacht hast und was passiert ist. Screenshots helfen. Die Rapport-Version (links unten in der App) bitte angeben. + +| Kanal | Verwendung | +|--------------------|-------------------------------------------| +| **Gitea Issues** | Bugs, Feature-Wünsche, allgemeine Fragen | +| **gabrielevarano.ch** | Entwickler-Kontakt | + +## Warum Open Source? + +Bürowissen sollte nicht in proprietären Tools eingesperrt sein. Studio-Management-Workflows sind in der Branche gut etabliert — ein Tool, das die Schweizer Konventionen (SIA 102, QR-Rechnung) sauber umsetzt, gehört allen. + +**AGPL-3.0** stellt sicher, dass Verbesserungen wieder ins Projekt fliessen. diff --git a/content/features/_index.md b/content/features/_index.md new file mode 100644 index 0000000..e3e9a95 --- /dev/null +++ b/content/features/_index.md @@ -0,0 +1,20 @@ +--- +title: Features +linkTitle: Features +weight: 2 +--- + +Die Bausteine von RAPPORT — Studio-Management für Schweizer Architekturbüros. + +## Module + +{{< cards >}} + {{< card link="zeiterfassung" title="Zeiterfassung" subtitle="Tages- & Wochenraster mit Drag & Drop." >}} + {{< card link="rechnungen" title="Rechnungen & Offerten" subtitle="QR-Einzahlungsscheine, SIA-Phasen, Akonto." >}} + {{< card link="projekte" title="Projekt- & Kundenverwaltung" subtitle="SIA 102, Budget, Phasen, Beteiligte." >}} + {{< card link="mitarbeiter" title="Mitarbeiter" subtitle="Ferien, Absenzen, Lohnabrechnung." >}} + {{< card link="spesen" title="Spesen & Bürobudget" subtitle="Belegupload, Jahresbudget, Internes." >}} + {{< card link="protokolle" title="Protokolle & Lieferscheine" subtitle="Sitzungsprotokolle, Briefe, Lieferscheine." >}} + {{< card link="auto-updater" title="Auto-Updater" subtitle="Signierte Updates via Tauri." >}} + {{< card link="system-tray" title="System-Tray" subtitle="Hide-on-Close, Quick-Open." >}} +{{< /cards >}} diff --git a/content/features/auto-updater.md b/content/features/auto-updater.md new file mode 100644 index 0000000..f78a391 --- /dev/null +++ b/content/features/auto-updater.md @@ -0,0 +1,49 @@ +--- +title: Auto-Updater +linkTitle: Auto-Updater +weight: 7 +toc: true +--- + +Neu in 0.7.0 + +**Rapport prüft beim Start automatisch auf neue Versionen** und installiert Updates signiert über Tauri. Einzelne Versionen können übersprungen werden. + +## Funktionsweise + +Beim App-Start: + +1. Abfrage gegen `https://git.kgva.ch/karim/RAPPORT/releases/latest.json` +2. Versionsvergleich mit lokaler `version` im Tauri-Bundle +3. Bei neuer Version → Update-Dialog +4. Bei Bestätigung → Download + Signaturprüfung + Installation + Neustart + +## Sicherheit + +- Updates werden mit dem **Tauri-Updater-Schlüssel** signiert +- Manipulierte Downloads werden abgelehnt +- Quellcode und Build sind reproduzierbar (Gitea CI, geplant) + +## Optionen + +- **Update installieren** — Download & Neustart +- **Diese Version überspringen** — überspringt nur diese eine Version +- **Später erinnern** — beim nächsten Start erneut fragen + +Updates können in den Einstellungen komplett deaktiviert werden. + +## Latest-Endpoint + +```json +{ + "version": "0.8.3", + "notes": "Rapport 0.8.3", + "pub_date": "2026-05-24T00:00:00Z", + "platforms": { + "darwin-aarch64": { + "signature": "…", + "url": "https://git.kgva.ch/karim/RAPPORT/releases/download/0.8.3/RAPPORT%20PRE-RELEASE.app.tar.gz" + } + } +} +``` diff --git a/content/features/mitarbeiter.md b/content/features/mitarbeiter.md new file mode 100644 index 0000000..1feafde --- /dev/null +++ b/content/features/mitarbeiter.md @@ -0,0 +1,52 @@ +--- +title: Mitarbeiter +linkTitle: Mitarbeiter +weight: 4 +toc: true +--- + +In Arbeit + +**Ferienverwaltung, interne Stunden / Absenzen und Lohnabrechnung.** Jahresabschluss mit Überstundenausgleich. + +## Stammdaten + +Pro Mitarbeiter: + +- Name, Eintrittsdatum, Pensum (%) +- Stundensatz (intern, für Rechnungen) +- Ferienanspruch (Tage / Jahr) +- Lohn (monatlich, brutto) + +## Ferienverwaltung + +- **Prorata-Berechnung** bei Eintritt unter Jahr +- **Ferien-Saldo** in Tagen (live) +- **Halbtage** unterstützt +- **Übertrag** ins Folgejahr oder Auszahlung + +## Absenzen + +Krankheit, Militär, Mutterschaft, unbezahlter Urlaub — getrennt erfasst, mit Auswertung pro Mitarbeiter. + +## Lohnabrechnung + +Monatliche Abrechnung mit: + +- Grundlohn (basierend auf Pensum) +- Überstunden-Vergütung +- Spesen-Erstattung +- Sozialabzüge (AHV, ALV, Pensionskasse) + +PDF-Export pro Mitarbeiter. + +## Jahresabschluss + +- Ferien-Restguthaben übertragen oder auszahlen +- Überstunden ausgleichen oder vergüten +- Lohnausweis vorbereiten (Export) + +## Verwandte Module + +- [Zeiterfassung](../zeiterfassung) — Pensum-Soll vs. Stunden-Ist +- [Spesen](../spesen) — Spesen-Erstattung in der Lohnabrechnung diff --git a/content/features/projekte.md b/content/features/projekte.md new file mode 100644 index 0000000..28b4d08 --- /dev/null +++ b/content/features/projekte.md @@ -0,0 +1,53 @@ +--- +title: Projekt- & Kundenverwaltung +linkTitle: Projekte +weight: 3 +toc: true +--- + +In Arbeit + +**Projekte nach SIA 102** mit Budget, Phasen und Beteiligten. Erstellung aus einer Offerte mit Verknüpfung zu Zeiterfassung und Rechnungen. + +## Projektstruktur + +Jedes Projekt besitzt: + +- **Stammdaten** — Nummer, Bezeichnung, Standort, Bauschätzwert +- **Kunde** — verknüpft mit Kundendatenbank +- **Beteiligte** — Bauleitung, Fachplaner, Behörden +- **Phasen** — SIA 102 (Vorprojekt, Bauprojekt, Ausschreibung, …) +- **Budget** — Gesamthonorar, pro Phase aufgeteilt + +## SIA 102 + +Standard-Phasenverteilung wird vorgeschlagen, kann pro Projekt überschrieben werden. + +| Phase | Anteil (Standard) | +|---|---| +| 31 — Vorprojekt | 9 % | +| 32 — Bauprojekt | 21 % | +| 33 — Bewilligung | 3 % | +| 41 — Ausschreibung | 18 % | +| 51 — Ausführung | 38 % | +| 52 — Inbetriebnahme | 6 % | +| 53 — Abschluss | 5 % | + +## Kundendatenbank + +- Adresse, Ansprechperson, Telefon, E-Mail +- Honorartyp (Stundensatz / SIA / Pauschal) +- Verknüpfung zu allen Projekten und Rechnungen des Kunden + +## Auswertung + +Pro Projekt: + +- Geleistete Stunden vs. Budget +- Honorar-Saldo (verrechnet / Akonto / offen) +- Phasen-Fortschritt + +## Verwandte Module + +- [Rechnungen](../rechnungen) — Offerte → Projekt +- [Zeiterfassung](../zeiterfassung) — Stunden-Auswertung pro Phase diff --git a/content/features/protokolle.md b/content/features/protokolle.md new file mode 100644 index 0000000..8ed6414 --- /dev/null +++ b/content/features/protokolle.md @@ -0,0 +1,44 @@ +--- +title: Protokolle & Lieferscheine +linkTitle: Protokolle +weight: 6 +toc: true +--- + +In Arbeit + +**Einfache Erstellung von Sitzungsprotokollen** mit Beschlüssen und Aufgaben. Briefe und Lieferscheine im gleichen Erscheinungsbild. + +## Sitzungsprotokolle + +Pro Sitzung: + +- Datum, Ort, Teilnehmer (aus Beteiligten-Liste) +- **Traktanden** als nummerierte Liste +- Pro Traktandum: Beschluss, Aufgabe, Verantwortlich, Frist +- Anhänge + +PDF-Export mit Bürobriefbogen. + +## Briefe + +Brief-Editor mit: + +- Empfänger aus Kundendatenbank +- Bezugszeile, Anrede, Text, Grussformel +- Briefbogen-Vorlage mit Logo +- PDF-Export + +## Lieferscheine + +Pro Lieferung: + +- Empfänger, Datum, Bezug +- Positionen (Plan-Nummer, Bezeichnung, Anzahl, Massstab) +- Unterschriftenfeld + +Konsistentes Erscheinungsbild über alle Dokumenttypen — eine Briefbogen-Vorlage, mehrere Verwendungen. + +## Verwandte Module + +- [Projekte](../projekte) — Beteiligte als Empfänger diff --git a/content/features/rechnungen.md b/content/features/rechnungen.md new file mode 100644 index 0000000..9af4eef --- /dev/null +++ b/content/features/rechnungen.md @@ -0,0 +1,44 @@ +--- +title: Rechnungen & Offerten +linkTitle: Rechnungen +weight: 2 +toc: true +--- + +In Arbeit + +**QR-Einzahlungsscheine, SIA-Phasen, Akonto-, Teil- und Schlussrechnungen.** Offerten sind in Projekte und Rechnungen konvertierbar. PDF-Export. + +## Workflow + +1. **Offerte** erstellen — auf Basis SIA 102 oder pauschal +2. Kunde nimmt an → **konvertieren in Projekt + Rechnung** +3. **Akonto-Rechnungen** während der Projektlaufzeit +4. **Schlussrechnung** mit Differenz zum bisher Akonto-bezahlten + +## QR-Einzahlungsschein + +Schweizer **QR-Rechnung** nach Norm — direkt eingebettet in die PDF. + +Ausgelesen aus: + +- **Bürodaten** — IBAN, Empfänger-Adresse +- **Kundendaten** — Schuldner-Adresse +- **Rechnungs-Daten** — Betrag, Referenz, Zusatzinformation + +## Honorarmodelle + +| Modell | Berechnung | Verwendung | +|---|---|---| +| **Stundensatz** | Aus Zeiterfassung × Mitarbeiter-Stundensatz | Kleinaufträge, Beratung | +| **SIA-Phasen** | Bauschätzwert × Honorarsatz × Phasenanteil | Reguläre Architektur-Aufträge | +| **Pauschal** | Fester Betrag | Auf Wunsch des Kunden | + +## PDF-Export + +Druckfertige Rechnung inkl. QR-Schein. Layout aus dem Büro-Briefbogen (mit Logo). Mehrsprachig DE/FR/IT (geplant). + +## Verwandte Module + +- [Projekte](../projekte) — Honorarstruktur stammt aus dem Projekt +- [Zeiterfassung](../zeiterfassung) — Stundensatz-Rechnungen diff --git a/content/features/spesen.md b/content/features/spesen.md new file mode 100644 index 0000000..2eef481 --- /dev/null +++ b/content/features/spesen.md @@ -0,0 +1,40 @@ +--- +title: Spesen & Bürobudget +linkTitle: Spesen +weight: 5 +toc: true +--- + +In Arbeit + +**Spesenerfassung mit Belegupload.** Jahresbudget mit Einnahmen und Ausgaben. Internes Rechnungswesen. + +## Spesenerfassung + +Pro Mitarbeiter: + +- Datum, Betrag, Kategorie +- **Beleg-Upload** (PDF, JPG, PNG) +- Projekt-Zuordnung (optional) +- Status (offen / erstattet) + +Kategorien: Reise, Verpflegung, Material, Telefon, Sonstiges. + +## Jahresbudget + +Übersicht über: + +- **Einnahmen** — Rechnungsbeträge, sortiert nach Eingang +- **Ausgaben** — Spesen, Bürokosten, Löhne, Sozialabzüge +- **Saldo** pro Monat / Quartal / Jahr + +## Auswertung + +- Einnahmen pro Kunde / Projekt +- Ausgaben pro Kategorie / Mitarbeiter +- Erfolgsrechnung pro Geschäftsjahr (vereinfacht) + +## Verwandte Module + +- [Mitarbeiter](../mitarbeiter) — Spesen-Erstattung in der Lohnabrechnung +- [Rechnungen](../rechnungen) — Einnahmen-Quelle diff --git a/content/features/system-tray.md b/content/features/system-tray.md new file mode 100644 index 0000000..fb028ff --- /dev/null +++ b/content/features/system-tray.md @@ -0,0 +1,40 @@ +--- +title: System-Tray +linkTitle: System-Tray +weight: 8 +toc: true +--- + +Neu in 0.7.0 + +**Schnellzugriff über die Menüleiste mit Hide-on-Close.** Beim Schliessen läuft Rapport im Hintergrund weiter — Cmd+Q beendet die App vollständig. + +## Verhalten + +| Aktion | Verhalten | +|---|---| +| **Fenster schliessen** (⌘W oder rotes X) | App läuft im Tray weiter | +| **Cmd+Q** | App wird vollständig beendet | +| **Klick auf Tray-Icon** | Fenster nach vorne, oder zeigen | +| **Rechtsklick auf Tray-Icon** | Menü mit Schnellzugriffen | + +## Tray-Menü + +- **Rapport zeigen** — Fenster nach vorne +- **Neue Zeiterfassung** — direkt im Zeit-Modul +- **Neue Rechnung** — direkt im Rechnungs-Modul +- **Letzte Projekte** — Quick-Open der letzten 5 Projekte +- **Einstellungen** +- **Rapport beenden** + +## Konfiguration + +In den Einstellungen: + +- **Beim Systemstart starten** (Login-Item) — Standard: aus +- **Beim Schliessen beenden** statt ins Tray — Standard: aus +- **Tray-Icon ausblenden** — App läuft, aber kein Menüleisten-Icon + +## Verwandte Module + +- [Auto-Updater](../auto-updater) — prüft Updates im Hintergrund diff --git a/content/features/zeiterfassung.md b/content/features/zeiterfassung.md new file mode 100644 index 0000000..d500a00 --- /dev/null +++ b/content/features/zeiterfassung.md @@ -0,0 +1,45 @@ +--- +title: Zeiterfassung +linkTitle: Zeiterfassung +weight: 1 +toc: true +--- + +In Arbeit + +**Tages- und Wochenraster** mit Drag & Drop. Auswertungen pro Mitarbeiter und Projekt. Ferienverwaltung mit Prorata und Jahresabschluss. + +## Konzept + +Die Zeiterfassung ist das **Kernmodul** von RAPPORT — alle anderen Module (Rechnungen, Auswertungen, Lohnabrechnung) greifen auf die hier erfassten Stunden zu. + +## Eingabe + +- **Wochenraster** mit den 5 (oder 7) Arbeitstagen +- **Halbstunden-Slots** von 06:00 bis 22:00 +- **Klick** oder **Drag** über mehrere Slots +- Jeder Eintrag wird einem **Projekt** zugewiesen (Pflichtfeld) +- Mehrfacheinträge pro Slot möglich (z. B. parallele Telefonate) + +## Auswertungen + +Pro Mitarbeiter und pro Projekt: + +- Geleistete Stunden vs. Soll-Pensum +- Ferienanspruch / -saldo (mit Prorata bei Eintritt unter Jahr) +- Überstunden-Saldo +- Stundenaufschlüsselung nach SIA-Phase pro Projekt + +## Jahresabschluss + +Am Jahresende: + +- Ferien-Restguthaben übertragen oder auszahlen +- Überstunden ausgleichen oder vergüten +- Neues Jahr automatisch initialisieren + +## Verwandte Module + +- [Rechnungen](../rechnungen) — Stundensatz-Rechnungen ziehen direkt aus der Zeiterfassung +- [Projekte](../projekte) — Stunden-Auswertung pro SIA-Phase +- [Mitarbeiter](../mitarbeiter) — Pensum, Ferienanspruch diff --git a/content/lizenz/_index.md b/content/lizenz/_index.md new file mode 100644 index 0000000..e3ce40d --- /dev/null +++ b/content/lizenz/_index.md @@ -0,0 +1,104 @@ +--- +title: Lizenz & Mitwirkende +linkTitle: Lizenz +weight: 99 +toc: true +--- + +Lizenz von RAPPORT, Zugehörigkeit zu OpenBureau und Attribution der verwendeten Open-Source-Software. + +## RAPPORT + +**RAPPORT** — Studio Management Software für Architekturbüros. + +Quellcode: [git.kgva.ch/karim/RAPPORT](https://git.kgva.ch/karim/RAPPORT) +Autor: [Karim Gabriele Varano](https://gabrielevarano.ch) + +### Lizenz + +Lizenziert unter **GNU Affero General Public License v3.0 oder höher** ([AGPL-3.0-or-later](https://www.gnu.org/licenses/agpl-3.0.html)). + +> Dies bedeutet im Kern: +> +> - **Freie Nutzung** für jeden Zweck (privat, kommerziell, behördlich) +> - **Freier Zugang** zum Quellcode +> - **Freie Modifikation** und Weitergabe — unter derselben Lizenz +> - **Netzwerk-Nutzung gilt als Weitergabe** — wer eine modifizierte Version als Service anbietet, muss den Quellcode offenlegen + +Eigene Builds, eigene Forks, eigene Anpassungen — alles erlaubt, solange Modifikationen wieder unter AGPL-3.0 veröffentlicht werden. + +## OpenBureau + +RAPPORT ist Teil der **OpenBureau**-Initiative — einer Sammlung freier Software-Werkzeuge für den Architektur-Büro-Alltag. + +Die Idee: Bürowissen — Workflows, Konventionen, Vorlagen — sollte nicht in proprietären Tools eingesperrt sein. OpenBureau-Tools setzen die in der Branche etablierten Konventionen (SIA-Normen, QR-Rechnung, Plan-Verwaltung) sauber um und stehen unter freien Lizenzen zur Verfügung. + +| Tool | Zweck | Status | +|---|---|---| +| **RAPPORT** | Studio-Management Desktop-App (Zeit, Rechnungen, Projekte) | Pre-Release | +| **Rapport Server** | Selfhost-Stack (Postgres + Auth + Realtime + Storage) | Pre-Release | +| **DOSSIER** | Rhino-8-Plugin für Plan- und Dokumentationsausgabe | Pre-Release | + +Mehr unter [gabrielevarano.ch](https://gabrielevarano.ch). + +## Verwendete Open-Source-Software + +RAPPORT baut auf freier Software auf. Diese Liste nennt die wichtigsten Komponenten und ihre jeweiligen Lizenzen. + +### Frontend + +| Software | Verwendung | Lizenz | +|---|---|---| +| [React 19](https://react.dev/) | UI-Bibliothek | MIT | +| [Vite](https://vite.dev/) | Build-Tool, Dev-Server | MIT | +| [swissqrbill](https://github.com/schoero/SwissQRBill) | QR-Einzahlungsscheine | MIT | + +### Desktop-Wrapper + +| Software | Verwendung | Lizenz | +|---|---|---| +| [Tauri 2](https://v2.tauri.app/) | Cross-Platform Desktop-Wrapper | MIT / Apache-2.0 | +| [WebKit](https://webkit.org/) | WebView-Engine (macOS) | LGPL-2.1 / BSD | + +### Rapport Server (Selfhost-Stack) + +| Software | Verwendung | Lizenz | +|---|---|---| +| [PostgreSQL](https://www.postgresql.org/) | Datenbank | PostgreSQL-Lizenz | +| [GoTrue](https://github.com/supabase/gotrue) | Authentication-Service | MIT | +| [PostgREST](https://postgrest.org/) | REST-API über Postgres-Schema | MIT | +| [Realtime](https://github.com/supabase/realtime) | WebSocket-Sync | Apache-2.0 | +| [Storage](https://github.com/supabase/storage-api) | Object-Storage für Belege & Logos | Apache-2.0 | +| [Kong](https://konghq.com/) | API-Gateway | Apache-2.0 | +| [nginx](https://nginx.org/) | Frontend-Webserver | BSD-2-Clause | +| [Docker Compose](https://docs.docker.com/compose/) | Orchestrierung | Apache-2.0 | + +### Diese Website + +| Software | Verwendung | Lizenz | +|---|---|---| +| [Hugo](https://gohugo.io/) | Static-Site-Generator | Apache-2.0 | +| [Hextra](https://imfing.github.io/hextra/) | Hugo-Theme (von Xin) | MIT | +| [Inter](https://rsms.me/inter/) | UI-Schrift (Rasmus Andersson) | SIL OFL 1.1 | +| [Playfair Display](https://fonts.google.com/specimen/Playfair+Display) | Headings | SIL OFL 1.1 | + +Alle Logos und Marken-Namen gehören ihren jeweiligen Eigentümern. + +## Lizenz-Volltexte + +Die vollständigen Lizenztexte: + +- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html) — RAPPORT +- [MIT](https://opensource.org/licenses/MIT) — React, Vite, Tauri, Hextra, swissqrbill +- [Apache-2.0](https://opensource.org/licenses/Apache-2.0) — Tauri, Hugo, Supabase +- [SIL OFL 1.1](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) — Inter, Playfair Display + +## Mitwirkende + +RAPPORT wird derzeit als Ein-Personen-Projekt von [Karim Gabriele Varano](https://gabrielevarano.ch) entwickelt. Beiträge — Code, Issues, Übersetzungen, Vorlagen — sind willkommen. + +Wer beitragen möchte → [Gitea Issues](https://git.kgva.ch/karim/RAPPORT/issues). + +## Kontakt + +Fragen zur Lizenz, kommerzieller Einsatz, Spezialfälle: [Issue auf Gitea](https://git.kgva.ch/karim/RAPPORT/issues) oder via [gabrielevarano.ch](https://gabrielevarano.ch). diff --git a/content/server/_index.md b/content/server/_index.md new file mode 100644 index 0000000..e2af89b --- /dev/null +++ b/content/server/_index.md @@ -0,0 +1,227 @@ +--- +title: Rapport Server +linkTitle: Server +weight: 4 +toc: true +--- + +Self-Hosting + +**Rapport Server** — der vollständige Selfhost-Stack für Rapport. Eigene Daten, eigene Domain, eigener Server. Komplett Open-Source, Docker-Compose, AGPL-3.0. + +## Wann brauchst du Rapport Server? + +| Szenario | Lösung | +|---|---| +| **Ein Mensch, ein Mac** | Desktop-App reicht — [Installation](../docs/installation) | +| **Mehrere Personen im Studio** | Rapport Server auf einem Mac Mini oder Linux-Server | +| **Verteiltes Team, Home-Office, Mobile-Zugriff** | Rapport Server mit Reverse-Proxy + SSL | +| **Cloud-Hosting bei einem Anbieter** | Rapport Server auf VPS/Hetzner/etc. | + +> Die Desktop-App speichert lokal als JSON. Rapport Server bringt **Postgres + Multi-User + Realtime-Sync** — für alle, die zu zweit oder im Team arbeiten. + +## Architektur + +Rapport Server bündelt sechs Open-Source-Komponenten zu einem stimmigen Stack: + +```text +┌──────────────────────────────────────────────────────┐ +│ Browser / Desktop-App │ +└──────────────┬───────────────────────┬───────────────┘ + │ │ + ▼ ▼ + ┌──────────┐ ┌──────────┐ + │ nginx │ │ Kong │ ← API-Gateway + │ (app) │ │ │ + └──────────┘ └──────────┘ + │ + ┌───────────────────┼─────────────────────┐ + ▼ ▼ ▼ + ┌────────┐ ┌──────────┐ ┌─────────┐ + │ GoTrue │ │PostgREST │ │ Realtime│ + │ (Auth) │ │ (API) │ │ (WS) │ + └────────┘ └──────────┘ └─────────┘ + │ │ │ + └───────────────────┼─────────────────────┘ + ▼ + ┌─────────────────┐ + │ PostgreSQL │ + └─────────────────┘ + │ + ▼ + ┌─────────────────┐ + │ Storage │ ← Belege, Logos + └─────────────────┘ +``` + +| Komponente | Funktion | +|---|---| +| **PostgreSQL** | Datenbank — alle Rapport-Daten | +| **GoTrue** | Authentication — Email/Passwort, JWT-Tokens | +| **PostgREST** | REST-API direkt aus dem Postgres-Schema | +| **Realtime** | WebSocket-Sync für Live-Updates | +| **Storage** | Object-Storage für Belege & Logos | +| **Kong** | API-Gateway, routet alle Calls | +| **app** (nginx) | Liefert das Rapport-Frontend (React-Build) | + +Alles in **einem Docker-Compose**. Keine Cloud-Abhängigkeit, keine Telemetrie. + +## Voraussetzungen + +| OS | Container-Runtime | +|---|---| +| **Linux** (Ubuntu 22.04+, Debian 12+, …) | Docker Engine + Compose v2 | +| **macOS** (Mac Mini etc.) | [Colima](https://github.com/abiosoft/colima) + Docker CLI — voll OSS | + +> Auf macOS funktionieren auch OrbStack oder Docker Desktop, beide sind aber proprietär. **Colima** ist die OSS-Alternative und für Selfhost vollkommen ausreichend. + +**Plus:** ein DNS-Name für TLS (z. B. `rapport.studio.ch`), und optional ein Reverse-Proxy wie [Nginx Proxy Manager](https://nginxproxymanager.com/) oder [Caddy](https://caddyserver.com/). + +## Setup in 5 Schritten + +### 1 · Repo klonen + +```bash +git clone https://git.kgva.ch/karim/rapport-server.git +cd rapport-server +``` + +### 2 · `.env` erstellen + +```bash +cp .env.example .env +``` + +In `.env` müssen mindestens diese Werte ersetzt werden: + +| Variable | Was | +|---|---| +| `POSTGRES_PASSWORD` | DB-Passwort (≥ 32 Zeichen, zufällig) | +| `JWT_SECRET` | JWT-Signatur (≥ 32 Zeichen, zufällig) | +| `ANON_KEY` / `SERVICE_ROLE_KEY` | aus JWT-Secret abgeleitet | +| `SITE_URL` | Public-URL der Instanz (z. B. `https://app.rapport.studio.ch`) | + +Zufallswerte generieren: + +```bash +openssl rand -hex 32 # für POSTGRES_PASSWORD und JWT_SECRET +node scripts/generate-keys.mjs # ANON_KEY + SERVICE_ROLE_KEY aus JWT_SECRET +``` + +### 3 · Migrations holen + +Die SQL-Migrations stammen aus dem App-Repo. Einmal initial holen: + +```bash +./scripts/sync-migrations.sh +``` + +Bei späteren Rapport-Updates erneut ausführen, dann `docker compose down && docker compose up -d`. + +### 4 · Stack starten + +```bash +docker compose up -d +``` + +Erststart dauert ~1 Minute (Postgres initialisiert sich, Migrations laufen, Container starten). + +### 5 · Health-Check + +```bash +docker compose ps +``` + +Alle Container sollten `healthy` zeigen. Frontend ist dann erreichbar unter `http://localhost:8080`. + +## Reverse-Proxy + HTTPS + +Für Production-Setups mit eigener Domain — **Caddy** ist die einfachste Variante (config-as-code, Let's-Encrypt automatisch): + +```caddy +app.rapport.studio.ch { + reverse_proxy localhost:8080 +} + +api.rapport.studio.ch { + reverse_proxy localhost:8000 +} +``` + +In `.env` dann: + +```env +SITE_URL=https://app.rapport.studio.ch +API_EXTERNAL_URL=https://api.rapport.studio.ch +``` + +Alternativ über **Nginx Proxy Manager** mit Web-UI — siehe [Troubleshooting](../docs/troubleshooting). + +## Updates + +```bash +git pull +./scripts/sync-migrations.sh # falls neue SQL-Migrationen +docker compose pull # neueste Container-Versionen +docker compose up -d # neu starten +``` + +Daten bleiben erhalten — das Volume `postgres-data` wird nicht angetastet. + +## Backup + +### Postgres-Dump + +```bash +docker compose exec -T db pg_dumpall -U postgres > backup-$(date +%Y%m%d).sql +``` + +Empfohlen: per `cron` täglich + auf externe Disk / S3 / Backblaze sichern. + +### Storage (Belege, Logos) + +```bash +docker compose exec storage tar -czf - /var/lib/storage > storage-$(date +%Y%m%d).tar.gz +``` + +### Restore + +```bash +docker compose down +docker volume rm rapport-server_postgres-data +docker compose up -d db +sleep 10 +cat backup-YYYYMMDD.sql | docker compose exec -T db psql -U postgres +docker compose up -d +``` + +## Bezug Rapport ↔ Rapport Server + +| Komponente | Wo | Lizenz | +|---|---|---| +| **Rapport Desktop-App** | [git.kgva.ch/karim/RAPPORT](https://git.kgva.ch/karim/RAPPORT) | AGPL-3.0 | +| **Rapport Server** | [git.kgva.ch/karim/rapport-server](https://git.kgva.ch/karim/rapport-server) | AGPL-3.0 | + +Die Desktop-App kann **wahlweise** im Lokal-Modus (JSON, kein Server nötig) oder im Cloud-Modus (gegen Rapport Server) betrieben werden. Wechsel erfolgt im Login-Bildschirm der App. + +## Häufige Fragen + +### Brauche ich Supabase Cloud? + +**Nein.** Rapport Server ist die Selfhost-Variante derselben Komponenten (Postgres + GoTrue + PostgREST + Realtime + Storage), aber komplett auf eigener Infrastruktur. Kein Supabase-Account, keine externe Abhängigkeit. + +### Kann ich von der Desktop-App auf Rapport Server migrieren? + +Im Login der Desktop-App auf **Cloud** wechseln, Server-URL eingeben, Account erstellen, Daten manuell wieder anlegen (Bürodaten, Mitarbeiter, Kunden). Ein direkter localStorage → Postgres-Import ist [geplant](https://git.kgva.ch/karim/RAPPORT/issues). + +### Was kostet das? + +Nichts — Rapport Server ist Open-Source (AGPL-3.0). Du brauchst nur einen Server (Mac Mini, NAS, VPS) und ggf. eine Domain. + +### Wieviel Server-Resourcen? + +Ein Mac Mini M1 (8 GB RAM) reicht problemlos für ein 5-Personen-Studio. Linux-VPS mit 2 GB RAM + 2 CPU reicht auch. + +## Quellcode + +[git.kgva.ch/karim/rapport-server](https://git.kgva.ch/karim/rapport-server) — Issues, Pull Requests und Forks willkommen. diff --git a/hugo.yaml b/hugo.yaml new file mode 100644 index 0000000..b716a03 --- /dev/null +++ b/hugo.yaml @@ -0,0 +1,103 @@ +# RAPPORT — Hugo site configuration +baseURL: "https://rapport.gabrielevarano.ch/" +title: "RAPPORT" +theme: "hextra" + +enableRobotsTXT: true +hasCJKLanguage: false + +outputs: + home: [html] + page: [html] + section: [html, rss] + +defaultContentLanguage: de +languages: + de: + label: Deutsch + weight: 1 + title: RAPPORT + +module: + hugoVersion: + extended: true + min: "0.146.0" + +markup: + highlight: + noClasses: false + goldmark: + renderer: + unsafe: true + +enableInlineShortcodes: true + +menu: + main: + - identifier: documentation + name: Dokumentation + pageRef: /docs + weight: 1 + - identifier: features + name: Features + pageRef: /features + weight: 2 + - identifier: downloads + name: Downloads + pageRef: /downloads + weight: 3 + - identifier: server + name: Server + pageRef: /server + weight: 4 + - identifier: faq + name: FAQ + pageRef: /faq + weight: 5 + - name: Search + weight: 6 + params: + type: search + - name: Gitea + weight: 7 + url: "https://git.kgva.ch/karim/RAPPORT" + params: + icon: github + +params: + description: "Freie Studio Management Software für Architekturbüros — Zeiterfassung, Rechnungen, Projekte, QR-Einzahlungsscheine. Tauri + React. AGPL-3.0." + + externalLinkDecoration: true + + navbar: + displayTitle: true + displayLogo: false + width: normal + + theme: + default: light + displayToggle: true + + footer: + enable: true + displayCopyright: true + displayPoweredBy: false + width: normal + + page: + width: normal + + displayUpdatedDate: false + + search: + enable: true + type: flexsearch + flexsearch: + index: content + tokenize: forward + + editURL: + enable: false + + toc: + displayTags: false diff --git a/i18n/de.yaml b/i18n/de.yaml new file mode 100644 index 0000000..c7e4b44 --- /dev/null +++ b/i18n/de.yaml @@ -0,0 +1 @@ +copyright: '© 2026 [RAPPORT](https://git.kgva.ch/karim/RAPPORT) · [AGPL-3.0](/lizenz/) · Teil von [OpenBureau](/lizenz/#openbureau)' diff --git a/layouts/hextra-home.html b/layouts/hextra-home.html new file mode 100644 index 0000000..018ec86 --- /dev/null +++ b/layouts/hextra-home.html @@ -0,0 +1,10 @@ +{{ define "main" }} +
+ {{ partial "sidebar.html" (dict "context" . "disableSidebar" true) }} +
+
+ {{ .Content }} +
+
+
+{{ end }} diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..9b23949 --- /dev/null +++ b/public/404.html @@ -0,0 +1 @@ +

404

This page could not be found.

\ No newline at end of file diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..7f0493c7efdff87df4aa2b69907e5e139aa65d2d GIT binary patch literal 7296 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 aw1?3)!!yhudWBvfMZiGM6jh_+9P@twy3z#z literal 0 HcmV?d00001 diff --git a/public/android-chrome-512x512.png b/public/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..faea4c2fdfc9d9e28b87c2aab2c24bac43a74c8a GIT binary patch literal 27677 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 T^_}^rr2t^}jPF+HJdXJvBP!h= literal 0 HcmV?d00001 diff --git a/public/categories/index.html b/public/categories/index.html new file mode 100644 index 0000000..8871380 --- /dev/null +++ b/public/categories/index.html @@ -0,0 +1,32 @@ +Categories – RAPPORTZum Inhalt springen \ No newline at end of file diff --git a/public/categories/index.xml b/public/categories/index.xml new file mode 100644 index 0000000..ec51926 --- /dev/null +++ b/public/categories/index.xml @@ -0,0 +1 @@ +RAPPORT – Categorieshttps://rapport.gabrielevarano.ch/categories/Recent content in Categories on RAPPORTHugo -- gohugo.iode \ No newline at end of file diff --git a/public/css/compiled/main.css b/public/css/compiled/main.css new file mode 100644 index 0000000..a221a0b --- /dev/null +++ b/public/css/compiled/main.css @@ -0,0 +1,2 @@ +/*! tailwindcss v4.3.0 | 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-translate-x:0;--tw-translate-y:0;--tw-translate-z: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-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction: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-duration:initial;--tw-ease:initial;--tw-content:"";--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--hx-font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--hx-font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--hx-color-red-100:oklch(93.6% .032 17.717);--hx-color-red-200:oklch(88.5% .062 18.334);--hx-color-red-900:oklch(39.6% .141 25.723);--hx-color-orange-50:oklch(98% .016 73.684);--hx-color-orange-100:oklch(95.4% .038 75.164);--hx-color-orange-300:oklch(83.7% .128 66.29);--hx-color-orange-400:oklch(75% .183 55.934);--hx-color-orange-800:oklch(47% .157 37.304);--hx-color-amber-100:oklch(96.2% .059 95.617);--hx-color-amber-200:oklch(92.4% .12 95.746);--hx-color-amber-900:oklch(41.4% .112 45.904);--hx-color-yellow-50:oklch(98.7% .026 102.212);--hx-color-yellow-100:oklch(97.3% .071 103.193);--hx-color-yellow-200:oklch(94.5% .129 101.54);--hx-color-yellow-700:oklch(55.4% .135 66.442);--hx-color-yellow-900:oklch(42.1% .095 57.708);--hx-color-green-100:oklch(96.2% .044 156.743);--hx-color-green-200:oklch(92.5% .084 155.995);--hx-color-green-900:oklch(39.3% .095 152.535);--hx-color-blue-100:oklch(93.2% .032 255.585);--hx-color-blue-200:oklch(88.2% .059 254.128);--hx-color-blue-900:oklch(37.9% .146 265.522);--hx-color-indigo-100:oklch(93% .034 272.788);--hx-color-indigo-200:oklch(87% .065 274.039);--hx-color-indigo-900:oklch(35.9% .144 278.697);--hx-color-purple-100:oklch(94.6% .033 307.174);--hx-color-purple-200:oklch(90.2% .063 306.703);--hx-color-purple-900:oklch(38.1% .176 304.987);--hx-color-slate-50:oklch(98.4% .003 247.858);--hx-color-slate-100:oklch(96.8% .007 247.896);--hx-color-slate-900:oklch(20.8% .042 265.755);--hx-color-gray-50:oklch(98.5% .002 247.839);--hx-color-gray-100:oklch(96.7% .003 264.542);--hx-color-gray-200:oklch(92.8% .006 264.531);--hx-color-gray-300:oklch(87.2% .01 258.338);--hx-color-gray-400:oklch(70.7% .022 261.325);--hx-color-gray-500:oklch(55.1% .027 264.364);--hx-color-gray-600:oklch(44.6% .03 256.802);--hx-color-gray-700:oklch(37.3% .034 259.733);--hx-color-gray-800:oklch(27.8% .033 256.848);--hx-color-gray-900:oklch(21% .034 264.665);--hx-color-neutral-50:oklch(98.5% 0 0);--hx-color-neutral-200:oklch(92.2% 0 0);--hx-color-neutral-300:oklch(87% 0 0);--hx-color-neutral-400:oklch(70.8% 0 0);--hx-color-neutral-500:oklch(55.6% 0 0);--hx-color-neutral-600:oklch(43.9% 0 0);--hx-color-neutral-700:oklch(37.1% 0 0);--hx-color-neutral-800:oklch(26.9% 0 0);--hx-color-neutral-900:oklch(20.5% 0 0);--hx-color-black:#000;--hx-color-white:#fff;--hx-spacing:.25rem;--hx-breakpoint-xl:80rem;--hx-text-xs:.75rem;--hx-text-xs--line-height:calc(1 / .75);--hx-text-sm:.875rem;--hx-text-sm--line-height:calc(1.25 / .875);--hx-text-base:1rem;--hx-text-base--line-height:calc(1.5 / 1);--hx-text-lg:1.125rem;--hx-text-lg--line-height:calc(1.75 / 1.125);--hx-text-xl:1.25rem;--hx-text-xl--line-height:calc(1.75 / 1.25);--hx-text-2xl:1.5rem;--hx-text-2xl--line-height:calc(2 / 1.5);--hx-text-3xl:1.875rem;--hx-text-3xl--line-height:calc(2.25 / 1.875);--hx-text-4xl:2.25rem;--hx-text-4xl--line-height:calc(2.5 / 2.25);--hx-text-5xl:3rem;--hx-text-5xl--line-height:1;--hx-font-weight-normal:400;--hx-font-weight-medium:500;--hx-font-weight-semibold:600;--hx-font-weight-bold:700;--hx-font-weight-extrabold:800;--hx-tracking-tighter:-.05em;--hx-tracking-tight:-.025em;--hx-leading-tight:1.25;--hx-radius-xs:.125rem;--hx-radius-sm:.25rem;--hx-radius-md:.375rem;--hx-radius-lg:.5rem;--hx-radius-xl:.75rem;--hx-radius-3xl:1.5rem;--hx-ease-in:cubic-bezier(.4, 0, 1, 1);--hx-ease-out:cubic-bezier(0, 0, .2, 1);--hx-ease-in-out:cubic-bezier(.4, 0, .2, 1);--hx-blur-md:12px;--hx-default-transition-duration:.15s;--hx-default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--hx-default-font-family:var(--hx-font-sans);--hx-default-mono-font-family:var(--hx-font-mono);--hx-color-primary-100:hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 44));--hx-color-primary-200:hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 36));--hx-color-primary-300:hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 27));--hx-color-primary-400:hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 16));--hx-color-primary-500:hsl(var(--primary-hue) var(--primary-saturation) var(--primary-lightness));--hx-color-primary-600:hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 45));--hx-color-primary-700:hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 39));--hx-color-primary-800:hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 32));--hx-color-primary-900:hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 24));--hx-color-dark:#111}}@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(--hx-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(--hx-default-font-feature-settings,normal);font-variation-settings:var(--hx-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(--hx-default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--hx-default-mono-font-feature-settings,normal);font-variation-settings:var(--hx-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}::-webkit-calendar-picker-indicator{line-height:1}:-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}abbr:where([title]){cursor:help}:where(a,button,[role=tab],[role=menuitem],[role=menuitemradio],input,select,textarea,[tabindex="0"]):not([class*=hextra-focus-visible]):focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:var(--hx-color-primary-200);--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-offset-color:var(--hx-color-primary-300);--tw-outline-style:none;outline-style:none}:where(a,button,[role=tab],[role=menuitem],[role=menuitemradio],input,select,textarea,[tabindex="0"]):not([class*=hextra-focus-visible]):focus-visible:where(.dark,.dark *){--tw-ring-color:var(--hx-color-primary-800);--tw-ring-offset-color:var(--hx-color-primary-700)}}@layer components;@layer utilities{.hx\:pointer-events-none{pointer-events:none}.hx\:sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.hx\:absolute{position:absolute}.hx\:relative{position:relative}.hx\:sticky{position:sticky}.hx\:inset-x-0{inset-inline:calc(var(--hx-spacing) * 0)}.hx\:inset-y-0{inset-block:calc(var(--hx-spacing) * 0)}.hx\:top-0{top:calc(var(--hx-spacing) * 0)}.hx\:top-1\/2{top:50%}.hx\:top-8{top:calc(var(--hx-spacing) * 8)}.hx\:top-16{top:calc(var(--hx-spacing) * 16)}.hx\:top-\[40\%\]{top:40%}.hx\:top-full{top:100%}.hx\:right-0{right:calc(var(--hx-spacing) * 0)}.hx\:bottom-0{bottom:calc(var(--hx-spacing) * 0)}.hx\:left-0{left:calc(var(--hx-spacing) * 0)}.hx\:left-\[24px\]{left:24px}.hx\:left-\[36px\]{left:36px}.hx\:z-20{z-index:20}.hx\:z-\[-1\]{z-index:-1}.hx\:order-last{order:9999}.hx\:m-\[11px\]{margin:11px}.hx\:mx-1{margin-inline:calc(var(--hx-spacing) * 1)}.hx\:mx-4{margin-inline:calc(var(--hx-spacing) * 4)}.hx\:mx-auto{margin-inline:auto}.hx\:my-1{margin-block:calc(var(--hx-spacing) * 1)}.hx\:my-1\.5{margin-block:calc(var(--hx-spacing) * 1.5)}.hx\:my-2{margin-block:calc(var(--hx-spacing) * 2)}.hx\:-mt-20{margin-top:calc(var(--hx-spacing) * -20)}.hx\:mt-1{margin-top:calc(var(--hx-spacing) * 1)}.hx\:mt-1\.5{margin-top:calc(var(--hx-spacing) * 1.5)}.hx\:mt-2{margin-top:calc(var(--hx-spacing) * 2)}.hx\:mt-4{margin-top:calc(var(--hx-spacing) * 4)}.hx\:mt-5{margin-top:calc(var(--hx-spacing) * 5)}.hx\:mt-6{margin-top:calc(var(--hx-spacing) * 6)}.hx\:mt-8{margin-top:calc(var(--hx-spacing) * 8)}.hx\:mt-12{margin-top:calc(var(--hx-spacing) * 12)}.hx\:mt-16{margin-top:calc(var(--hx-spacing) * 16)}.hx\:mt-auto{margin-top:auto}.hx\:-mr-2{margin-right:calc(var(--hx-spacing) * -2)}.hx\:mr-1{margin-right:calc(var(--hx-spacing) * 1)}.hx\:mr-2{margin-right:calc(var(--hx-spacing) * 2)}.hx\:-mb-0\.5{margin-bottom:calc(var(--hx-spacing) * -.5)}.hx\:mb-0{margin-bottom:calc(var(--hx-spacing) * 0)}.hx\:mb-2{margin-bottom:calc(var(--hx-spacing) * 2)}.hx\:mb-4{margin-bottom:calc(var(--hx-spacing) * 4)}.hx\:mb-6{margin-bottom:calc(var(--hx-spacing) * 6)}.hx\:mb-8{margin-bottom:calc(var(--hx-spacing) * 8)}.hx\:mb-10{margin-bottom:calc(var(--hx-spacing) * 10)}.hx\:mb-12{margin-bottom:calc(var(--hx-spacing) * 12)}.hx\:mb-16{margin-bottom:calc(var(--hx-spacing) * 16)}.hx\:-ml-2{margin-left:calc(var(--hx-spacing) * -2)}.hx\:ml-2{margin-left:calc(var(--hx-spacing) * 2)}.hx\:ml-4{margin-left:calc(var(--hx-spacing) * 4)}.hx\:line-clamp-3{-webkit-line-clamp:3;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.hx\:block{display:block}.hx\:flex{display:flex}.hx\:grid{display:grid}.hx\:hidden{display:none}.hx\:inline{display:inline}.hx\:inline-block{display:inline-block}.hx\:inline-flex{display:inline-flex}.hx\:aspect-auto{aspect-ratio:auto}.hx\:size-4{width:calc(var(--hx-spacing) * 4);height:calc(var(--hx-spacing) * 4)}.hx\:h-0{height:calc(var(--hx-spacing) * 0)}.hx\:h-2{height:calc(var(--hx-spacing) * 2)}.hx\:h-3\.5{height:calc(var(--hx-spacing) * 3.5)}.hx\:h-4{height:calc(var(--hx-spacing) * 4)}.hx\:h-5{height:calc(var(--hx-spacing) * 5)}.hx\:h-7{height:calc(var(--hx-spacing) * 7)}.hx\:h-10{height:calc(var(--hx-spacing) * 10)}.hx\:h-16{height:calc(var(--hx-spacing) * 16)}.hx\:h-\[18px\]{height:18px}.hx\:h-full{height:100%}.hx\:h-px{height:1px}.hx\:max-h-\(--menu-height\){max-height:var(--menu-height)}.hx\:max-h-64{max-height:calc(var(--hx-spacing) * 64)}.hx\:max-h-\[calc\(100vh-var\(--navbar-height\)-env\(safe-area-inset-bottom\)\)\]{max-height:calc(100vh - var(--navbar-height) - env(safe-area-inset-bottom))}.hx\:max-h-\[min\(calc\(50vh-11rem-env\(safe-area-inset-bottom\)\)\,400px\)\]{max-height:min(calc(50vh - 11rem - env(safe-area-inset-bottom)), 400px)}.hx\:min-h-\[100px\]{min-height:100px}.hx\:min-h-\[calc\(100vh-var\(--navbar-height\)\)\]{min-height:calc(100vh - var(--navbar-height))}.hx\:w-2{width:calc(var(--hx-spacing) * 2)}.hx\:w-3\.5{width:calc(var(--hx-spacing) * 3.5)}.hx\:w-4{width:calc(var(--hx-spacing) * 4)}.hx\:w-10{width:calc(var(--hx-spacing) * 10)}.hx\:w-64{width:calc(var(--hx-spacing) * 64)}.hx\:w-\[110\%\]{width:110%}.hx\:w-\[180\%\]{width:180%}.hx\:w-full{width:100%}.hx\:w-max{width:max-content}.hx\:w-screen{width:100vw}.hx\:max-w-\[50\%\]{max-width:50%}.hx\:max-w-\[90rem\]{max-width:90rem}.hx\:max-w-\[min\(calc\(100vw-2rem\)\,calc\(100\%\+20rem\)\)\]{max-width:min(100vw - 2rem,100% + 20rem)}.hx\:max-w-full{max-width:100%}.hx\:max-w-none{max-width:none}.hx\:max-w-screen-xl{max-width:var(--hx-breakpoint-xl)}.hx\:min-w-0{min-width:calc(var(--hx-spacing) * 0)}.hx\:min-w-\[18px\]{min-width:18px}.hx\:min-w-\[24px\]{min-width:24px}.hx\:min-w-full{min-width:100%}.hx\:shrink-0{flex-shrink:0}.hx\:grow{flex-grow:1}.hx\:origin-center{transform-origin:50%}.hx\:-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.hx\:cursor-default{cursor:default}.hx\:cursor-pointer{cursor:pointer}.hx\:scroll-my-6{scroll-margin-block:calc(var(--hx-spacing) * 6)}.hx\:scroll-py-6{scroll-padding-block:calc(var(--hx-spacing) * 6)}.hx\:list-none{list-style-type:none}.hx\:appearance-none{appearance:none}.hx\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.hx\:flex-col{flex-direction:column}.hx\:flex-wrap{flex-wrap:wrap}.hx\:items-baseline{align-items:baseline}.hx\:items-center{align-items:center}.hx\:items-start{align-items:flex-start}.hx\:justify-between{justify-content:space-between}.hx\:justify-center{justify-content:center}.hx\:justify-end{justify-content:flex-end}.hx\:justify-start{justify-content:flex-start}.hx\:justify-items-start{justify-items:start}.hx\:gap-1{gap:calc(var(--hx-spacing) * 1)}.hx\:gap-1\.5{gap:calc(var(--hx-spacing) * 1.5)}.hx\:gap-2{gap:calc(var(--hx-spacing) * 2)}.hx\:gap-4{gap:calc(var(--hx-spacing) * 4)}.hx\:gap-x-1\.5{column-gap:calc(var(--hx-spacing) * 1.5)}.hx\:gap-x-2{column-gap:calc(var(--hx-spacing) * 2)}.hx\:gap-y-1{row-gap:calc(var(--hx-spacing) * 1)}.hx\:gap-y-2{row-gap:calc(var(--hx-spacing) * 2)}.hx\:overflow-auto{overflow:auto}.hx\:overflow-hidden{overflow:hidden}.hx\:overflow-x-auto{overflow-x:auto}.hx\:overflow-x-hidden{overflow-x:hidden}.hx\:overflow-y-auto{overflow-y:auto}.hx\:overflow-y-hidden{overflow-y:hidden}.hx\:overscroll-contain{overscroll-behavior:contain}.hx\:overscroll-x-contain{overscroll-behavior-x:contain}.hx\:rounded-3xl{border-radius:var(--hx-radius-3xl)}.hx\:rounded-full{border-radius:3.40282e38px}.hx\:rounded-lg{border-radius:var(--hx-radius-lg)}.hx\:rounded-md{border-radius:var(--hx-radius-md)}.hx\:rounded-sm{border-radius:var(--hx-radius-sm)}.hx\:rounded-xl{border-radius:var(--hx-radius-xl)}.hx\:rounded-xs{border-radius:var(--hx-radius-xs)}.hx\:rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.hx\:border{border-style:var(--tw-border-style);border-width:1px}.hx\:border-0{border-style:var(--tw-border-style);border-width:0}.hx\:border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.hx\:border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.hx\:border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.hx\:border-amber-200{border-color:var(--hx-color-amber-200)}.hx\:border-black\/5{border-color:var(--hx-color-black)}@supports (color:color-mix(in lab, red, red)){.hx\:border-black\/5{border-color:color-mix(in oklab, var(--hx-color-black) 5%, transparent)}}.hx\:border-blue-200{border-color:var(--hx-color-blue-200)}.hx\:border-gray-200{border-color:var(--hx-color-gray-200)}.hx\:border-gray-500{border-color:var(--hx-color-gray-500)}.hx\:border-green-200{border-color:var(--hx-color-green-200)}.hx\:border-indigo-200{border-color:var(--hx-color-indigo-200)}.hx\:border-orange-100{border-color:var(--hx-color-orange-100)}.hx\:border-purple-200{border-color:var(--hx-color-purple-200)}.hx\:border-red-200{border-color:var(--hx-color-red-200)}.hx\:border-transparent{border-color:#0000}.hx\:border-yellow-100{border-color:var(--hx-color-yellow-100)}.hx\:bg-amber-100{background-color:var(--hx-color-amber-100)}.hx\:bg-black\/\[\.05\]{background-color:var(--hx-color-black)}@supports (color:color-mix(in lab, red, red)){.hx\:bg-black\/\[\.05\]{background-color:color-mix(in oklab, var(--hx-color-black) 5%, transparent)}}.hx\:bg-blue-100{background-color:var(--hx-color-blue-100)}.hx\:bg-gray-100{background-color:var(--hx-color-gray-100)}.hx\:bg-gray-200{background-color:var(--hx-color-gray-200)}.hx\:bg-green-100{background-color:var(--hx-color-green-100)}.hx\:bg-indigo-100{background-color:var(--hx-color-indigo-100)}.hx\:bg-neutral-50{background-color:var(--hx-color-neutral-50)}.hx\:bg-neutral-900{background-color:var(--hx-color-neutral-900)}.hx\:bg-orange-50{background-color:var(--hx-color-orange-50)}.hx\:bg-primary-100{background-color:var(--hx-color-primary-100)}.hx\:bg-primary-400{background-color:var(--hx-color-primary-400)}.hx\:bg-primary-600{background-color:var(--hx-color-primary-600)}.hx\:bg-primary-700\/5{background-color:var(--hx-color-primary-700)}@supports (color:color-mix(in lab, red, red)){.hx\:bg-primary-700\/5{background-color:color-mix(in oklab, var(--hx-color-primary-700) 5%, transparent)}}.hx\:bg-purple-100{background-color:var(--hx-color-purple-100)}.hx\:bg-red-100{background-color:var(--hx-color-red-100)}.hx\:bg-transparent{background-color:#0000}.hx\:bg-white{background-color:var(--hx-color-white)}.hx\:bg-yellow-50{background-color:var(--hx-color-yellow-50)}.hx\:bg-gradient-to-r{--tw-gradient-position:to right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.hx\:from-gray-900{--tw-gradient-from:var(--hx-color-gray-900);--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))}.hx\:to-gray-600{--tw-gradient-to:var(--hx-color-gray-600);--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))}.hx\:bg-clip-text{-webkit-background-clip:text;background-clip:text}.hx\:p-0{padding:calc(var(--hx-spacing) * 0)}.hx\:p-0\.5{padding:calc(var(--hx-spacing) * .5)}.hx\:p-1{padding:calc(var(--hx-spacing) * 1)}.hx\:p-1\.5{padding:calc(var(--hx-spacing) * 1.5)}.hx\:p-2{padding:calc(var(--hx-spacing) * 2)}.hx\:p-4{padding:calc(var(--hx-spacing) * 4)}.hx\:p-6{padding:calc(var(--hx-spacing) * 6)}.hx\:px-1\.5{padding-inline:calc(var(--hx-spacing) * 1.5)}.hx\:px-2{padding-inline:calc(var(--hx-spacing) * 2)}.hx\:px-2\.5{padding-inline:calc(var(--hx-spacing) * 2.5)}.hx\:px-3{padding-inline:calc(var(--hx-spacing) * 3)}.hx\:px-4{padding-inline:calc(var(--hx-spacing) * 4)}.hx\:px-6{padding-inline:calc(var(--hx-spacing) * 6)}.hx\:px-8{padding-inline:calc(var(--hx-spacing) * 8)}.hx\:py-1{padding-block:calc(var(--hx-spacing) * 1)}.hx\:py-1\.5{padding-block:calc(var(--hx-spacing) * 1.5)}.hx\:py-2{padding-block:calc(var(--hx-spacing) * 2)}.hx\:py-2\.5{padding-block:calc(var(--hx-spacing) * 2.5)}.hx\:py-3{padding-block:calc(var(--hx-spacing) * 3)}.hx\:py-4{padding-block:calc(var(--hx-spacing) * 4)}.hx\:py-12{padding-block:calc(var(--hx-spacing) * 12)}.hx\:pt-4{padding-top:calc(var(--hx-spacing) * 4)}.hx\:pt-6{padding-top:calc(var(--hx-spacing) * 6)}.hx\:pt-8{padding-top:calc(var(--hx-spacing) * 8)}.hx\:pr-2{padding-right:calc(var(--hx-spacing) * 2)}.hx\:pr-4{padding-right:calc(var(--hx-spacing) * 4)}.hx\:pr-\[calc\(env\(safe-area-inset-right\)-1\.5rem\)\]{padding-right:calc(env(safe-area-inset-right) - 1.5rem)}.hx\:pr-\[max\(env\(safe-area-inset-left\)\,1\.5rem\)\]{padding-right:max(env(safe-area-inset-left), 1.5rem)}.hx\:pr-\[max\(env\(safe-area-inset-right\)\,1\.5rem\)\]{padding-right:max(env(safe-area-inset-right), 1.5rem)}.hx\:pb-8{padding-bottom:calc(var(--hx-spacing) * 8)}.hx\:pb-\[env\(safe-area-inset-bottom\)\]{padding-bottom:env(safe-area-inset-bottom)}.hx\:pb-px{padding-bottom:1px}.hx\:pl-6{padding-left:calc(var(--hx-spacing) * 6)}.hx\:pl-\[max\(env\(safe-area-inset-left\)\,1\.5rem\)\]{padding-left:max(env(safe-area-inset-left), 1.5rem)}.hx\:text-center{text-align:center}.hx\:text-left{text-align:left}.hx\:align-\[-2\.5px\]{vertical-align:-2.5px}.hx\:align-baseline{vertical-align:baseline}.hx\:align-middle{vertical-align:middle}.hx\:align-text-bottom{vertical-align:text-bottom}.hx\:font-mono{font-family:var(--hx-font-mono)}.hx\:text-2xl{font-size:var(--hx-text-2xl);line-height:var(--tw-leading,var(--hx-text-2xl--line-height))}.hx\:text-4xl{font-size:var(--hx-text-4xl);line-height:var(--tw-leading,var(--hx-text-4xl--line-height))}.hx\:text-base{font-size:var(--hx-text-base);line-height:var(--tw-leading,var(--hx-text-base--line-height))}.hx\:text-lg{font-size:var(--hx-text-lg);line-height:var(--tw-leading,var(--hx-text-lg--line-height))}.hx\:text-sm{font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height))}.hx\:text-xl{font-size:var(--hx-text-xl);line-height:var(--tw-leading,var(--hx-text-xl--line-height))}.hx\:text-xs{font-size:var(--hx-text-xs);line-height:var(--tw-leading,var(--hx-text-xs--line-height))}.hx\:text-\[\.65rem\]{font-size:.65rem}.hx\:text-\[10px\]{font-size:10px}.hx\:leading-5{--tw-leading:calc(var(--hx-spacing) * 5);line-height:calc(var(--hx-spacing) * 5)}.hx\:leading-6{--tw-leading:calc(var(--hx-spacing) * 6);line-height:calc(var(--hx-spacing) * 6)}.hx\:leading-7{--tw-leading:calc(var(--hx-spacing) * 7);line-height:calc(var(--hx-spacing) * 7)}.hx\:leading-none{--tw-leading:1;line-height:1}.hx\:leading-tight{--tw-leading:var(--hx-leading-tight);line-height:var(--hx-leading-tight)}.hx\:font-bold{--tw-font-weight:var(--hx-font-weight-bold);font-weight:var(--hx-font-weight-bold)}.hx\:font-extrabold{--tw-font-weight:var(--hx-font-weight-extrabold);font-weight:var(--hx-font-weight-extrabold)}.hx\:font-medium{--tw-font-weight:var(--hx-font-weight-medium);font-weight:var(--hx-font-weight-medium)}.hx\:font-normal{--tw-font-weight:var(--hx-font-weight-normal);font-weight:var(--hx-font-weight-normal)}.hx\:font-semibold{--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold)}.hx\:tracking-tight{--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight)}.hx\:tracking-tighter{--tw-tracking:var(--hx-tracking-tighter);letter-spacing:var(--hx-tracking-tighter)}.hx\:break-words,.hx\:wrap-break-word{overflow-wrap:break-word}.hx\:text-ellipsis{text-overflow:ellipsis}.hx\:whitespace-nowrap{white-space:nowrap}.hx\:text-\[color\:hsl\(var\(--primary-hue\)\,100\%\,50\%\)\]{color:hsl(var(--primary-hue),100%,50%)}.hx\:text-amber-900{color:var(--hx-color-amber-900)}.hx\:text-blue-900{color:var(--hx-color-blue-900)}.hx\:text-current{color:currentColor}.hx\:text-gray-100{color:var(--hx-color-gray-100)}.hx\:text-gray-500{color:var(--hx-color-gray-500)}.hx\:text-gray-600{color:var(--hx-color-gray-600)}.hx\:text-gray-700{color:var(--hx-color-gray-700)}.hx\:text-gray-800{color:var(--hx-color-gray-800)}.hx\:text-gray-900{color:var(--hx-color-gray-900)}.hx\:text-green-900{color:var(--hx-color-green-900)}.hx\:text-indigo-900{color:var(--hx-color-indigo-900)}.hx\:text-orange-800{color:var(--hx-color-orange-800)}.hx\:text-primary-800{color:var(--hx-color-primary-800)}.hx\:text-purple-900{color:var(--hx-color-purple-900)}.hx\:text-red-900{color:var(--hx-color-red-900)}.hx\:text-slate-50{color:var(--hx-color-slate-50)}.hx\:text-slate-900{color:var(--hx-color-slate-900)}.hx\:text-transparent{color:#0000}.hx\:text-white{color:var(--hx-color-white)}.hx\:text-yellow-900{color:var(--hx-color-yellow-900)}.hx\:capitalize{text-transform:capitalize}.hx\:tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.hx\:no-underline{text-decoration-line:none}.hx\:underline{text-decoration-line:underline}.hx\:decoration-from-font{text-decoration-thickness:from-font}.hx\:underline-offset-2{text-underline-offset:2px}.hx\:opacity-0{opacity:0}.hx\:opacity-50{opacity:.5}.hx\:opacity-80{opacity:.8}.hx\:shadow-\[0_-12px_16px_\#fff\]{--tw-shadow:0 -12px 16px var(--tw-shadow-color,#fff);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hx\:shadow-\[0_-12px_16px_white\]{--tw-shadow:0 -12px 16px var(--tw-shadow-color,white);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hx\:shadow-\[0_2px_4px_rgba\(0\,0\,0\,\.02\)\,0_1px_0_rgba\(0\,0\,0\,\.06\)\]{--tw-shadow:0 2px 4px var(--tw-shadow-color,#00000005), 0 1px 0 var(--tw-shadow-color,#0000000f);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hx\: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)}.hx\:shadow-sm{--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)}.hx\:shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px 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)}.hx\:shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hx\:hextra-focus-visible:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:var(--hx-color-primary-200);--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-offset-color:var(--hx-color-primary-300);--tw-outline-style:none;outline-style:none}.hx\:hextra-focus-visible:where(.dark,.dark *):focus-visible{--tw-ring-color:var(--hx-color-primary-800);--tw-ring-offset-color:var(--hx-color-primary-700)}.hx\:hextra-focus-visible-inset:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:var(--hx-color-primary-200);--tw-ring-offset-width:0px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;--tw-ring-inset:inset;outline-style:none}.hx\:hextra-focus-visible-inset:where(.dark,.dark *):focus-visible{--tw-ring-color:var(--hx-color-primary-800)}.hx\:shadow-gray-100{--tw-shadow-color:var(--hx-color-gray-100)}@supports (color:color-mix(in lab, red, red)){.hx\:shadow-gray-100{--tw-shadow-color:color-mix(in oklab, var(--hx-color-gray-100) var(--tw-shadow-alpha), transparent)}}.hx\: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,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:duration-75{--tw-duration:75ms;transition-duration:75ms}.hx\:duration-150{--tw-duration:.15s;transition-duration:.15s}.hx\:duration-200{--tw-duration:.2s;transition-duration:.2s}.hx\:ease-in{--tw-ease:var(--hx-ease-in);transition-timing-function:var(--hx-ease-in)}.hx\:ease-in-out{--tw-ease:var(--hx-ease-in-out);transition-timing-function:var(--hx-ease-in-out)}.hx\:outline-none{--tw-outline-style:none;outline-style:none}.hx\:select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.hx\:group-hover\:text-gray-900:is(:where(.hx\:group):hover *){color:var(--hx-color-gray-900)}.hx\:group-hover\:underline:is(:where(.hx\:group):hover *){text-decoration-line:underline}.hx\:group-hover\/code\:opacity-100:is(:where(.hx\:group\/code):hover *){opacity:1}}.hx\:group-data-\[active\=true\]\:text-primary-800:is(:where(.hx\:group)[data-active=true] *){color:var(--hx-color-primary-800)}.hx\:group-data-\[theme\=dark\]\:hidden:is(:where(.hx\:group)[data-theme=dark] *),.hx\:group-data-\[theme\=light\]\:hidden:is(:where(.hx\:group)[data-theme=light] *),.hx\:group-data-\[theme\=system\]\:hidden:is(:where(.hx\:group)[data-theme=system] *){display:none}.hx\:group-\[\.copied\]\/copybtn\:block:is(:where(.hx\:group\/copybtn).copied *){display:block}.hx\:group-\[\.copied\]\/copybtn\:hidden:is(:where(.hx\:group\/copybtn).copied *){display:none}.hx\:placeholder\:text-gray-500::placeholder{color:var(--hx-color-gray-500)}.hx\:before\:pointer-events-none:before{content:var(--tw-content);pointer-events:none}.hx\:before\:absolute:before{content:var(--tw-content);position:absolute}.hx\:before\:inset-0:before{content:var(--tw-content);inset:calc(var(--hx-spacing) * 0)}.hx\:before\:inset-y-1:before{content:var(--tw-content);inset-block:calc(var(--hx-spacing) * 1)}.hx\:before\:mr-1:before{content:var(--tw-content);margin-right:calc(var(--hx-spacing) * 1)}.hx\:before\:inline-block:before{content:var(--tw-content);display:inline-block}.hx\:before\:w-px:before{content:var(--tw-content);width:1px}.hx\:before\:bg-gray-200:before{content:var(--tw-content);background-color:var(--hx-color-gray-200)}.hx\:before\:opacity-25:before{content:var(--tw-content);opacity:.25}.hx\:before\:transition-transform:before{content:var(--tw-content);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:before\:content-\[\"\"\]:before{--tw-content:"";content:var(--tw-content)}.hx\:before\:content-\[\'\#\'\]:before{--tw-content:"#";content:var(--tw-content)}.hx\:before\:content-\[\'\'\]:before{--tw-content:"";content:var(--tw-content)}.hx\:before\:content-\[\\\"\\\"\]:before{--tw-content:\"\";content:var(--tw-content)}.hx\:group-open\:before\:rotate-90:is(:where(.hx\:group):is([open],:popover-open,:open) *):before{content:var(--tw-content);rotate:90deg}.hx\:first\:mt-0:first-child{margin-top:calc(var(--hx-spacing) * 0)}.hx\:last-of-type\:mb-0:last-of-type{margin-bottom:calc(var(--hx-spacing) * 0)}@media (hover:hover){.hx\:hover\:border-gray-200:hover{border-color:var(--hx-color-gray-200)}.hx\:hover\:border-gray-300:hover{border-color:var(--hx-color-gray-300)}.hx\:hover\:border-gray-400:hover{border-color:var(--hx-color-gray-400)}.hx\:hover\:border-gray-900:hover{border-color:var(--hx-color-gray-900)}.hx\:hover\:bg-gray-100:hover{background-color:var(--hx-color-gray-100)}.hx\:hover\:bg-gray-800\/5:hover{background-color:var(--hx-color-gray-800)}@supports (color:color-mix(in lab, red, red)){.hx\:hover\:bg-gray-800\/5:hover{background-color:color-mix(in oklab, var(--hx-color-gray-800) 5%, transparent)}}.hx\:hover\:bg-primary-700:hover{background-color:var(--hx-color-primary-700)}.hx\:hover\:bg-slate-50:hover{background-color:var(--hx-color-slate-50)}.hx\:hover\:text-black:hover{color:var(--hx-color-black)}.hx\:hover\:text-gray-700:hover{color:var(--hx-color-gray-700)}.hx\:hover\:text-gray-800:hover{color:var(--hx-color-gray-800)}.hx\:hover\:text-gray-900:hover{color:var(--hx-color-gray-900)}.hx\:hover\:text-primary-600:hover{color:var(--hx-color-primary-600)}.hx\:hover\:opacity-60:hover{opacity:.6}.hx\:hover\:opacity-75:hover{opacity:.75}.hx\:hover\:shadow-lg:hover{--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)}.hx\:hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px 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)}.hx\:hover\:shadow-gray-100:hover{--tw-shadow-color:var(--hx-color-gray-100)}@supports (color:color-mix(in lab, red, red)){.hx\:hover\:shadow-gray-100:hover{--tw-shadow-color:color-mix(in oklab, var(--hx-color-gray-100) var(--tw-shadow-alpha), transparent)}}}.hx\:focus-visible\:not-sr-only:focus-visible{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.hx\:focus-visible\:fixed:focus-visible{position:fixed}.hx\:focus-visible\:top-2:focus-visible{top:calc(var(--hx-spacing) * 2)}.hx\:focus-visible\:left-2:focus-visible{left:calc(var(--hx-spacing) * 2)}.hx\:focus-visible\:z-50:focus-visible{z-index:50}.hx\:focus-visible\:rounded-md:focus-visible{border-radius:var(--hx-radius-md)}.hx\:focus-visible\:bg-primary-500:focus-visible{background-color:var(--hx-color-primary-500)}.hx\:focus-visible\:bg-white:focus-visible{background-color:var(--hx-color-white)}.hx\:focus-visible\:px-4:focus-visible{padding-inline:calc(var(--hx-spacing) * 4)}.hx\:focus-visible\:py-2:focus-visible{padding-block:calc(var(--hx-spacing) * 2)}.hx\:focus-visible\:text-sm:focus-visible{font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height))}.hx\:focus-visible\:font-medium:focus-visible{--tw-font-weight:var(--hx-font-weight-medium);font-weight:var(--hx-font-weight-medium)}.hx\:focus-visible\:text-white:focus-visible{color:var(--hx-color-white)}.hx\:active\:bg-gray-400\/20:active{background-color:var(--hx-color-gray-400)}@supports (color:color-mix(in lab, red, red)){.hx\:active\:bg-gray-400\/20:active{background-color:color-mix(in oklab, var(--hx-color-gray-400) 20%, transparent)}}.hx\:active\:opacity-50:active{opacity:.5}.hx\:active\:shadow-sm:active{--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)}.hx\:active\:shadow-gray-200:active{--tw-shadow-color:var(--hx-color-gray-200)}@supports (color:color-mix(in lab, red, red)){.hx\:active\:shadow-gray-200:active{--tw-shadow-color:color-mix(in oklab, var(--hx-color-gray-200) var(--tw-shadow-alpha), transparent)}}.hx\:data-\[state\=closed\]\:hidden[data-state=closed],.hx\:data-\[state\=open\]\:hidden[data-state=open]{display:none}.hx\:data-\[state\=selected\]\:block[data-state=selected]{display:block}.hx\:data-\[state\=selected\]\:border-primary-500[data-state=selected]{border-color:var(--hx-color-primary-500)}.hx\:data-\[state\=selected\]\:text-primary-600[data-state=selected]{color:var(--hx-color-primary-600)}@media (prefers-contrast:more){.hx\:contrast-more\:border{border-style:var(--tw-border-style);border-width:1px}.hx\:contrast-more\:border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.hx\:contrast-more\:border-current{border-color:currentColor}.hx\:contrast-more\:border-gray-800{border-color:var(--hx-color-gray-800)}.hx\:contrast-more\:border-gray-900{border-color:var(--hx-color-gray-900)}.hx\:contrast-more\:border-neutral-400{border-color:var(--hx-color-neutral-400)}.hx\:contrast-more\:border-primary-500{border-color:var(--hx-color-primary-500)}.hx\:contrast-more\:border-transparent{border-color:#0000}.hx\:contrast-more\:font-bold{--tw-font-weight:var(--hx-font-weight-bold);font-weight:var(--hx-font-weight-bold)}.hx\:contrast-more\:text-current{color:currentColor}.hx\:contrast-more\:text-gray-700{color:var(--hx-color-gray-700)}.hx\:contrast-more\:text-gray-800{color:var(--hx-color-gray-800)}.hx\:contrast-more\:text-gray-900{color:var(--hx-color-gray-900)}.hx\:contrast-more\:underline{text-decoration-line:underline}.hx\:contrast-more\:shadow-\[0_0_0_1px_\#000\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,#000);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hx\:contrast-more\:shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}@media (hover:hover){.hx\:contrast-more\:hover\:border-gray-900:hover{border-color:var(--hx-color-gray-900)}}}@media not all and (min-width:80rem){.hx\:max-xl\:hidden{display:none}}@media not all and (min-width:64rem){.hx\:max-lg\:min-h-\[340px\]{min-height:340px}}@media not all and (min-width:48rem){.hx\:max-md\:sticky{position:sticky}.hx\:max-md\:hidden{display:none}.hx\:max-md\:min-h-\[340px\]{min-height:340px}.hx\:max-md\:\[transform\:translate3d\(0\,-100\%\,0\)\]{transform:translateY(-100%)}.hx\:max-md\:\[transform\:translate3d\(0\,0\,0\)\]{transform:translate(0)}}@media not all and (min-width:40rem){.hx\:max-sm\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}}@media (min-width:40rem){.hx\:sm\:right-0{right:calc(var(--hx-spacing) * 0)}.hx\:sm\:left-auto{left:auto}.hx\:sm\:block{display:block}.hx\:sm\:flex{display:flex}.hx\:sm\:w-\[110\%\]{width:110%}.hx\:sm\:flex-row{flex-direction:row}.hx\:sm\:items-center{align-items:center}.hx\:sm\:items-start{align-items:flex-start}.hx\:sm\:justify-between{justify-content:space-between}.hx\:sm\:text-xl{font-size:var(--hx-text-xl);line-height:var(--tw-leading,var(--hx-text-xl--line-height))}@media not all and (min-width:64rem){.hx\:sm\:max-lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@media (min-width:48rem){.hx\:md\:sticky{position:sticky}.hx\:md\:top-16{top:calc(var(--hx-spacing) * 16)}.hx\:md\:mr-0{margin-right:calc(var(--hx-spacing) * 0)}.hx\:md\:hidden{display:none}.hx\:md\:inline-block{display:inline-block}.hx\:md\:inline-flex{display:inline-flex}.hx\:md\:aspect-\[1\.1\/1\]{aspect-ratio:1.1}.hx\:md\:h-\[calc\(100vh-var\(--navbar-height\)-var\(--menu-height\)\)\]{height:calc(100vh - var(--navbar-height) - var(--menu-height))}.hx\:md\:max-h-\[min\(calc\(100vh-5rem-env\(safe-area-inset-bottom\)\)\,400px\)\]{max-height:min(calc(100vh - 5rem - env(safe-area-inset-bottom)), 400px)}.hx\:md\:w-64{width:calc(var(--hx-spacing) * 64)}.hx\:md\:shrink-0{flex-shrink:0}.hx\:md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.hx\:md\:justify-start{justify-content:flex-start}.hx\:md\:self-start{align-self:flex-start}.hx\:md\:overflow-auto{overflow:auto}.hx\:md\:px-12{padding-inline:calc(var(--hx-spacing) * 12)}.hx\:md\:pt-12{padding-top:calc(var(--hx-spacing) * 12)}.hx\:md\:text-3xl{font-size:var(--hx-text-3xl);line-height:var(--tw-leading,var(--hx-text-3xl--line-height))}.hx\:md\:text-5xl{font-size:var(--hx-text-5xl);line-height:var(--tw-leading,var(--hx-text-5xl--line-height))}.hx\:md\:text-lg{font-size:var(--hx-text-lg);line-height:var(--tw-leading,var(--hx-text-lg--line-height))}.hx\:md\:text-sm{font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height))}}@media (min-width:64rem){.hx\:lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width:80rem){.hx\:xl\:block{display:block}.hx\:xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.hx\:ltr\:right-1\.5:where(:dir(ltr),[dir=ltr],[dir=ltr] *){right:calc(var(--hx-spacing) * 1.5)}.hx\:ltr\:right-2:where(:dir(ltr),[dir=ltr],[dir=ltr] *){right:calc(var(--hx-spacing) * 2)}.hx\:ltr\:right-3:where(:dir(ltr),[dir=ltr],[dir=ltr] *){right:calc(var(--hx-spacing) * 3)}.hx\:ltr\:-mr-4:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-right:calc(var(--hx-spacing) * -4)}.hx\:ltr\:mr-auto:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-right:auto}.hx\:ltr\:ml-1:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:calc(var(--hx-spacing) * 1)}.hx\:ltr\:ml-3:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:calc(var(--hx-spacing) * 3)}.hx\:ltr\:ml-auto:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:auto}.hx\:ltr\:rotate-180:where(:dir(ltr),[dir=ltr],[dir=ltr] *){rotate:180deg}.hx\:ltr\:rounded-l-lg:where(:dir(ltr),[dir=ltr],[dir=ltr] *){border-top-left-radius:var(--hx-radius-lg);border-bottom-left-radius:var(--hx-radius-lg)}.hx\:ltr\:rounded-r-lg:where(:dir(ltr),[dir=ltr],[dir=ltr] *){border-top-right-radius:var(--hx-radius-lg);border-bottom-right-radius:var(--hx-radius-lg)}.hx\:ltr\:border-l:where(:dir(ltr),[dir=ltr],[dir=ltr] *){border-left-style:var(--tw-border-style);border-left-width:1px}.hx\:ltr\:pr-0:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-right:calc(var(--hx-spacing) * 0)}.hx\:ltr\:pr-2:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-right:calc(var(--hx-spacing) * 2)}.hx\:ltr\:pr-4:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-right:calc(var(--hx-spacing) * 4)}.hx\:ltr\:pr-8:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-right:calc(var(--hx-spacing) * 8)}.hx\:ltr\:pr-9:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-right:calc(var(--hx-spacing) * 9)}.hx\:ltr\:pl-3:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 3)}.hx\:ltr\:pl-4:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 4)}.hx\:ltr\:pl-5:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 5)}.hx\:ltr\:pl-6:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 6)}.hx\:ltr\:pl-8:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 8)}.hx\:ltr\:pl-12:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 12)}.hx\:ltr\:pl-16:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 16)}.hx\:ltr\:text-right:where(:dir(ltr),[dir=ltr],[dir=ltr] *){text-align:right}.hx\:ltr\:before\:left-0:where(:dir(ltr),[dir=ltr],[dir=ltr] *):before{content:var(--tw-content);left:calc(var(--hx-spacing) * 0)}@media (min-width:48rem){.hx\:ltr\:md\:left-auto:where(:dir(ltr),[dir=ltr],[dir=ltr] *){left:auto}}.hx\:rtl\:left-1\.5:where(:dir(rtl),[dir=rtl],[dir=rtl] *){left:calc(var(--hx-spacing) * 1.5)}.hx\:rtl\:left-2:where(:dir(rtl),[dir=rtl],[dir=rtl] *){left:calc(var(--hx-spacing) * 2)}.hx\:rtl\:left-3:where(:dir(rtl),[dir=rtl],[dir=rtl] *){left:calc(var(--hx-spacing) * 3)}.hx\:rtl\:mr-1:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:calc(var(--hx-spacing) * 1)}.hx\:rtl\:mr-3:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:calc(var(--hx-spacing) * 3)}.hx\:rtl\:mr-auto:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:auto}.hx\:rtl\:-ml-4:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-left:calc(var(--hx-spacing) * -4)}.hx\:rtl\:ml-auto:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-left:auto}.hx\:rtl\:-rotate-180:where(:dir(rtl),[dir=rtl],[dir=rtl] *){rotate:-180deg}.hx\:rtl\:rotate-270:where(:dir(rtl),[dir=rtl],[dir=rtl] *){rotate:270deg}.hx\:rtl\:rounded-l-lg:where(:dir(rtl),[dir=rtl],[dir=rtl] *){border-top-left-radius:var(--hx-radius-lg);border-bottom-left-radius:var(--hx-radius-lg)}.hx\:rtl\:rounded-r-lg:where(:dir(rtl),[dir=rtl],[dir=rtl] *){border-top-right-radius:var(--hx-radius-lg);border-bottom-right-radius:var(--hx-radius-lg)}.hx\:rtl\:border-r:where(:dir(rtl),[dir=rtl],[dir=rtl] *){border-right-style:var(--tw-border-style);border-right-width:1px}.hx\:rtl\:pr-3:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 3)}.hx\:rtl\:pr-4:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 4)}.hx\:rtl\:pr-5:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 5)}.hx\:rtl\:pr-6:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 6)}.hx\:rtl\:pr-8:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 8)}.hx\:rtl\:pr-12:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 12)}.hx\:rtl\:pr-16:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 16)}.hx\:rtl\:pl-0:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-left:calc(var(--hx-spacing) * 0)}.hx\:rtl\:pl-2:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-left:calc(var(--hx-spacing) * 2)}.hx\:rtl\:pl-4:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-left:calc(var(--hx-spacing) * 4)}.hx\:rtl\:pl-8:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-left:calc(var(--hx-spacing) * 8)}.hx\:rtl\:pl-9:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-left:calc(var(--hx-spacing) * 9)}.hx\:rtl\:text-left:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:left}.hx\:rtl\:before\:right-0:where(:dir(rtl),[dir=rtl],[dir=rtl] *):before{content:var(--tw-content);right:calc(var(--hx-spacing) * 0)}.hx\:rtl\:before\:rotate-180:where(:dir(rtl),[dir=rtl],[dir=rtl] *):before{content:var(--tw-content);rotate:180deg}@media (min-width:48rem){.hx\:rtl\:md\:right-auto:where(:dir(rtl),[dir=rtl],[dir=rtl] *){right:auto}}.hx\:dark\:block:where(.dark,.dark *){display:block}.hx\:dark\:hidden:where(.dark,.dark *){display:none}.hx\:dark\:border-amber-200\/30:where(.dark,.dark *){border-color:var(--hx-color-amber-200)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:border-amber-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-amber-200) 30%, transparent)}}.hx\:dark\:border-blue-200\/30:where(.dark,.dark *){border-color:var(--hx-color-blue-200)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:border-blue-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-blue-200) 30%, transparent)}}.hx\:dark\:border-gray-100\/20:where(.dark,.dark *){border-color:var(--hx-color-gray-100)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:border-gray-100\/20:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-gray-100) 20%, transparent)}}.hx\:dark\:border-gray-400:where(.dark,.dark *){border-color:var(--hx-color-gray-400)}.hx\:dark\:border-green-200\/30:where(.dark,.dark *){border-color:var(--hx-color-green-200)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:border-green-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-green-200) 30%, transparent)}}.hx\:dark\:border-indigo-200\/30:where(.dark,.dark *){border-color:var(--hx-color-indigo-200)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:border-indigo-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-indigo-200) 30%, transparent)}}.hx\:dark\:border-neutral-700:where(.dark,.dark *){border-color:var(--hx-color-neutral-700)}.hx\:dark\:border-neutral-800:where(.dark,.dark *){border-color:var(--hx-color-neutral-800)}.hx\:dark\:border-orange-400\/30:where(.dark,.dark *){border-color:var(--hx-color-orange-400)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:border-orange-400\/30:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-orange-400) 30%, transparent)}}.hx\:dark\:border-purple-200\/30:where(.dark,.dark *){border-color:var(--hx-color-purple-200)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:border-purple-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-purple-200) 30%, transparent)}}.hx\:dark\:border-red-200\/30:where(.dark,.dark *){border-color:var(--hx-color-red-200)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:border-red-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-red-200) 30%, transparent)}}.hx\:dark\:border-white\/10:where(.dark,.dark *){border-color:var(--hx-color-white)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:border-white\/10:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-white) 10%, transparent)}}.hx\:dark\:border-yellow-200\/30:where(.dark,.dark *){border-color:var(--hx-color-yellow-200)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:border-yellow-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-yellow-200) 30%, transparent)}}.hx\:dark\:bg-amber-900\/30:where(.dark,.dark *){background-color:var(--hx-color-amber-900)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-amber-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-amber-900) 30%, transparent)}}.hx\:dark\:bg-blue-900\/30:where(.dark,.dark *){background-color:var(--hx-color-blue-900)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-blue-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-blue-900) 30%, transparent)}}.hx\:dark\:bg-dark:where(.dark,.dark *),.hx\:dark\:bg-dark\/50:where(.dark,.dark *){background-color:var(--hx-color-dark)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-dark\/50:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-dark) 50%, transparent)}}.hx\:dark\:bg-gray-50\/10:where(.dark,.dark *){background-color:var(--hx-color-gray-50)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-gray-50\/10:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-gray-50) 10%, transparent)}}.hx\:dark\:bg-green-900\/30:where(.dark,.dark *){background-color:var(--hx-color-green-900)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-green-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-green-900) 30%, transparent)}}.hx\:dark\:bg-indigo-900\/30:where(.dark,.dark *){background-color:var(--hx-color-indigo-900)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-indigo-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-indigo-900) 30%, transparent)}}.hx\:dark\:bg-neutral-700:where(.dark,.dark *){background-color:var(--hx-color-neutral-700)}.hx\:dark\:bg-neutral-800:where(.dark,.dark *){background-color:var(--hx-color-neutral-800)}.hx\:dark\:bg-neutral-900:where(.dark,.dark *){background-color:var(--hx-color-neutral-900)}.hx\:dark\:bg-orange-400\/20:where(.dark,.dark *){background-color:var(--hx-color-orange-400)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-orange-400\/20:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-orange-400) 20%, transparent)}}.hx\:dark\:bg-primary-300\/10:where(.dark,.dark *){background-color:var(--hx-color-primary-300)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-primary-300\/10:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-primary-300) 10%, transparent)}}.hx\:dark\:bg-primary-400\/10:where(.dark,.dark *){background-color:var(--hx-color-primary-400)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-primary-400\/10:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-primary-400) 10%, transparent)}}.hx\:dark\:bg-primary-600:where(.dark,.dark *){background-color:var(--hx-color-primary-600)}.hx\:dark\:bg-purple-900\/30:where(.dark,.dark *){background-color:var(--hx-color-purple-900)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-purple-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-purple-900) 30%, transparent)}}.hx\:dark\:bg-red-900\/30:where(.dark,.dark *){background-color:var(--hx-color-red-900)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-red-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-red-900) 30%, transparent)}}.hx\:dark\:bg-yellow-700\/30:where(.dark,.dark *){background-color:var(--hx-color-yellow-700)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:bg-yellow-700\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-yellow-700) 30%, transparent)}}.hx\:dark\:from-gray-100:where(.dark,.dark *){--tw-gradient-from:var(--hx-color-gray-100);--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))}.hx\:dark\:to-gray-400:where(.dark,.dark *){--tw-gradient-to:var(--hx-color-gray-400);--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))}.hx\:dark\:text-amber-200:where(.dark,.dark *){color:var(--hx-color-amber-200)}.hx\:dark\:text-blue-200:where(.dark,.dark *){color:var(--hx-color-blue-200)}.hx\:dark\:text-gray-50:where(.dark,.dark *){color:var(--hx-color-gray-50)}.hx\:dark\:text-gray-100:where(.dark,.dark *){color:var(--hx-color-gray-100)}.hx\:dark\:text-gray-200:where(.dark,.dark *){color:var(--hx-color-gray-200)}.hx\:dark\:text-gray-300:where(.dark,.dark *){color:var(--hx-color-gray-300)}.hx\:dark\:text-gray-400:where(.dark,.dark *){color:var(--hx-color-gray-400)}.hx\:dark\:text-green-200:where(.dark,.dark *){color:var(--hx-color-green-200)}.hx\:dark\:text-indigo-200:where(.dark,.dark *){color:var(--hx-color-indigo-200)}.hx\:dark\:text-neutral-200:where(.dark,.dark *){color:var(--hx-color-neutral-200)}.hx\:dark\:text-neutral-400:where(.dark,.dark *){color:var(--hx-color-neutral-400)}.hx\:dark\:text-orange-300:where(.dark,.dark *){color:var(--hx-color-orange-300)}.hx\:dark\:text-primary-600:where(.dark,.dark *){color:var(--hx-color-primary-600)}.hx\:dark\:text-purple-200:where(.dark,.dark *){color:var(--hx-color-purple-200)}.hx\:dark\:text-red-200:where(.dark,.dark *){color:var(--hx-color-red-200)}.hx\:dark\:text-slate-100:where(.dark,.dark *){color:var(--hx-color-slate-100)}.hx\:dark\:text-white:where(.dark,.dark *){color:var(--hx-color-white)}.hx\:dark\:text-yellow-200:where(.dark,.dark *){color:var(--hx-color-yellow-200)}.hx\:dark\:opacity-80:where(.dark,.dark *){opacity:.8}.hx\:dark\:shadow-\[0_-1px_0_rgba\(255\,255\,255\,\.1\)_inset\]:where(.dark,.dark *){--tw-shadow:0 -1px 0 var(--tw-shadow-color,#ffffff1a) inset;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hx\:dark\:shadow-\[0_-12px_16px_\#111\]:where(.dark,.dark *){--tw-shadow:0 -12px 16px var(--tw-shadow-color,#111);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hx\:dark\:shadow-none:where(.dark,.dark *){--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}@media (hover:hover){.hx\:dark\:group-hover\:text-gray-50:where(.dark,.dark *):is(:where(.hx\:group):hover *){color:var(--hx-color-gray-50)}}.hx\:group-data-\[active\=true\]\:dark\:text-primary-600:is(:where(.hx\:group)[data-active=true] *):where(.dark,.dark *){color:var(--hx-color-primary-600)}.hx\:dark\:placeholder\:text-gray-400:where(.dark,.dark *)::placeholder{color:var(--hx-color-gray-400)}.hx\:dark\:before\:bg-neutral-800:where(.dark,.dark *):before{content:var(--tw-content);background-color:var(--hx-color-neutral-800)}.hx\:dark\:before\:invert:where(.dark,.dark *):before{content:var(--tw-content);--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (hover:hover){.hx\:dark\:hover\:border-gray-100:where(.dark,.dark *):hover{border-color:var(--hx-color-gray-100)}.hx\:dark\:hover\:border-gray-600:where(.dark,.dark *):hover{border-color:var(--hx-color-gray-600)}.hx\:dark\:hover\:border-neutral-500:where(.dark,.dark *):hover{border-color:var(--hx-color-neutral-500)}.hx\:dark\:hover\:border-neutral-700:where(.dark,.dark *):hover{border-color:var(--hx-color-neutral-700)}.hx\:dark\:hover\:border-neutral-800:where(.dark,.dark *):hover{border-color:var(--hx-color-neutral-800)}.hx\:dark\:hover\:bg-gray-100\/5:where(.dark,.dark *):hover{background-color:var(--hx-color-gray-100)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:hover\:bg-gray-100\/5:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--hx-color-gray-100) 5%, transparent)}}.hx\:dark\:hover\:bg-neutral-700:where(.dark,.dark *):hover{background-color:var(--hx-color-neutral-700)}.hx\:dark\:hover\:bg-neutral-800:where(.dark,.dark *):hover{background-color:var(--hx-color-neutral-800)}.hx\:dark\:hover\:bg-neutral-900:where(.dark,.dark *):hover{background-color:var(--hx-color-neutral-900)}.hx\:dark\:hover\:bg-primary-100\/5:where(.dark,.dark *):hover{background-color:var(--hx-color-primary-100)}@supports (color:color-mix(in lab, red, red)){.hx\:dark\:hover\:bg-primary-100\/5:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--hx-color-primary-100) 5%, transparent)}}.hx\:dark\:hover\:bg-primary-700:where(.dark,.dark *):hover{background-color:var(--hx-color-primary-700)}.hx\:dark\:hover\:text-gray-50:where(.dark,.dark *):hover{color:var(--hx-color-gray-50)}.hx\:dark\:hover\:text-gray-100:where(.dark,.dark *):hover{color:var(--hx-color-gray-100)}.hx\:dark\:hover\:text-gray-200:where(.dark,.dark *):hover{color:var(--hx-color-gray-200)}.hx\:dark\:hover\:text-gray-300:where(.dark,.dark *):hover{color:var(--hx-color-gray-300)}.hx\:dark\:hover\:text-neutral-50:where(.dark,.dark *):hover{color:var(--hx-color-neutral-50)}.hx\:dark\:hover\:text-primary-400:where(.dark,.dark *):hover{color:var(--hx-color-primary-400)}.hx\:dark\:hover\:text-white:where(.dark,.dark *):hover{color:var(--hx-color-white)}.hx\:dark\:hover\:shadow-none:where(.dark,.dark *):hover{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.hx\:dark\:focus-visible\:bg-dark:where(.dark,.dark *):focus-visible{background-color:var(--hx-color-dark)}.hx\:data-\[state\=selected\]\:dark\:border-primary-500[data-state=selected]:where(.dark,.dark *){border-color:var(--hx-color-primary-500)}.hx\:data-\[state\=selected\]\:dark\:text-primary-600[data-state=selected]:where(.dark,.dark *){color:var(--hx-color-primary-600)}@media (prefers-contrast:more){.hx\:contrast-more\:dark\:border-current:where(.dark,.dark *){border-color:currentColor}.hx\:contrast-more\:dark\:border-gray-50:where(.dark,.dark *){border-color:var(--hx-color-gray-50)}.hx\:contrast-more\:dark\:border-neutral-400:where(.dark,.dark *){border-color:var(--hx-color-neutral-400)}.hx\:contrast-more\:dark\:border-primary-500:where(.dark,.dark *){border-color:var(--hx-color-primary-500)}.hx\:dark\:contrast-more\:border-neutral-400:where(.dark,.dark *){border-color:var(--hx-color-neutral-400)}.hx\:contrast-more\:dark\:text-current:where(.dark,.dark *){color:currentColor}.hx\:contrast-more\:dark\:text-gray-50:where(.dark,.dark *){color:var(--hx-color-gray-50)}.hx\:contrast-more\:dark\:text-gray-100:where(.dark,.dark *){color:var(--hx-color-gray-100)}.hx\:contrast-more\:dark\:text-gray-300:where(.dark,.dark *){color:var(--hx-color-gray-300)}.hx\:contrast-more\:dark\:shadow-\[0_0_0_1px_\#fff\]:where(.dark,.dark *){--tw-shadow:0 0 0 1px var(--tw-shadow-color,#fff);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hx\:contrast-more\:dark\:shadow-none:where(.dark,.dark *){--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}@media (hover:hover){.hx\:contrast-more\:dark\:hover\:border-gray-50:where(.dark,.dark *):hover{border-color:var(--hx-color-gray-50)}}}@media print{.hx\:print\:\[display\:none\],.hx\:print\:hidden{display:none}.hx\:print\:bg-transparent{background-color:#0000}}}html{font-size:var(--hx-text-base);line-height:var(--tw-leading,var(--hx-text-base--line-height));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{background-color:var(--hx-color-white);width:100%}body:where(.dark,.dark *){background-color:var(--hx-color-dark);color:var(--hx-color-gray-100)}:root{--primary-hue:212deg;--primary-saturation:100%;--primary-lightness:50%;--navbar-height:4rem;--hextra-banner-height:2rem;--menu-height:3.75rem}.dark{--primary-hue:204deg;--primary-saturation:100%;--primary-lightness:50%}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.content :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 2);font-size:var(--hx-text-4xl);line-height:var(--tw-leading,var(--hx-text-4xl--line-height));--tw-font-weight:var(--hx-font-weight-bold);font-weight:var(--hx-font-weight-bold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}.content :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 10);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--hx-color-neutral-200)}@supports (color:color-mix(in lab, red, red)){.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:color-mix(in oklab, var(--hx-color-neutral-200) 70%, transparent)}}.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){padding-bottom:calc(var(--hx-spacing) * 1);font-size:var(--hx-text-3xl);line-height:var(--tw-leading,var(--hx-text-3xl--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}@media (prefers-contrast:more){.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--hx-color-neutral-400)}}.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-primary-100)}@supports (color:color-mix(in lab, red, red)){.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-primary-100) 10%, transparent)}}.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}@media (prefers-contrast:more){.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-neutral-400)}}.content :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 8);font-size:var(--hx-text-2xl);line-height:var(--tw-leading,var(--hx-text-2xl--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}.content :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}.content :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 8);font-size:var(--hx-text-xl);line-height:var(--tw-leading,var(--hx-text-xl--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}.content :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}.content :where(h5):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 8);font-size:var(--hx-text-lg);line-height:var(--tw-leading,var(--hx-text-lg--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}.content :where(h5):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}.content :where(h6):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 8);font-size:var(--hx-text-base);line-height:var(--tw-leading,var(--hx-text-base--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}.content :where(h6):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}.content :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 6);--tw-leading:calc(var(--hx-spacing) * 7);line-height:calc(var(--hx-spacing) * 7)}.content :where(p):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--hx-color-primary-600);text-decoration-line:underline;text-decoration-thickness:from-font}.content :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 6);border-color:var(--hx-color-gray-300);color:var(--hx-color-gray-700);font-style:italic}.content :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(ltr),[dir=ltr],[dir=ltr] *){border-left-style:var(--tw-border-style);padding-left:calc(var(--hx-spacing) * 6);border-left-width:2px}.content :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(rtl),[dir=rtl],[dir=rtl] *){border-right-style:var(--tw-border-style);padding-right:calc(var(--hx-spacing) * 6);border-right-width:2px}.content :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-gray-700);color:var(--hx-color-gray-400)}.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){margin-bottom:calc(var(--hx-spacing) * 4);border-radius:var(--hx-radius-xl);background-color:var(--hx-color-primary-700);overflow-x:auto}@supports (color:color-mix(in lab, red, red)){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){background-color:color-mix(in oklab, var(--hx-color-primary-700) 5%, transparent)}}.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){padding-block:calc(var(--hx-spacing) * 4);--tw-font-weight:var(--hx-font-weight-medium);font-size:.9em;font-weight:var(--hx-font-weight-medium);-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}@media (prefers-contrast:more){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-primary-900)}@supports (color:color-mix(in lab, red, red)){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){border-color:color-mix(in oklab, var(--hx-color-primary-900) 20%, transparent)}}.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){--tw-contrast:contrast(150%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}}.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:var(--hx-color-primary-300)}@supports (color:color-mix(in lab, red, red)){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-primary-300) 10%, transparent)}}@media (prefers-contrast:more){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-primary-100)}@supports (color:color-mix(in lab, red, red)){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-primary-100) 40%, transparent)}}}.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)){border-radius:var(--hx-radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-black)}@supports (color:color-mix(in lab, red, red)){.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)){border-color:color-mix(in oklab, var(--hx-color-black) 4%, transparent)}}.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)){background-color:var(--hx-color-black)}@supports (color:color-mix(in lab, red, red)){.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)){background-color:color-mix(in oklab, var(--hx-color-black) 3%, transparent)}}.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)){padding-inline:.25em;padding-block:calc(var(--hx-spacing) * .5);overflow-wrap:break-word;font-size:.9em}.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-white)}@supports (color:color-mix(in lab, red, red)){.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-white) 10%, transparent)}}.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:var(--hx-color-white)}@supports (color:color-mix(in lab, red, red)){.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-white) 10%, transparent)}}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)){margin-block:calc(var(--hx-spacing) * 6);border-collapse:collapse;width:100%;padding:calc(var(--hx-spacing) * 0);font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height));--tw-leading:calc(var(--hx-spacing) * 5);line-height:calc(var(--hx-spacing) * 5);display:block;overflow-x:auto}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) thead{background-color:var(--hx-color-gray-50)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) thead:where(.dark,.dark *){background-color:var(--hx-color-gray-600)}@supports (color:color-mix(in lab, red, red)){.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) thead:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-gray-600) 20%, transparent)}}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) tr{margin:calc(var(--hx-spacing) * 0);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--hx-color-gray-300);padding:calc(var(--hx-spacing) * 0)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) tr:where(.dark,.dark *){border-color:var(--hx-color-gray-600)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) th{margin:calc(var(--hx-spacing) * 0);border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-gray-300);padding:calc(var(--hx-spacing) * 2);--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) th:where(.dark,.dark *){border-color:var(--hx-color-gray-600)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) td{margin:calc(var(--hx-spacing) * 0);border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-gray-300);padding:calc(var(--hx-spacing) * 2)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) td:where(.dark,.dark *){border-color:var(--hx-color-gray-600)}.content :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 6);list-style-type:decimal}.content :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:calc(var(--hx-spacing) * 6)}.content :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:calc(var(--hx-spacing) * 6)}.content :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)) li{margin-block:calc(var(--hx-spacing) * 2)}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 6);list-style-type:disc}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:calc(var(--hx-spacing) * 6)}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:calc(var(--hx-spacing) * 6)}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)) li{margin-block:calc(var(--hx-spacing) * 2)}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)):has(li input[type=checkbox]){list-style-type:none}.content :where(ul,ol)>li>:where(ul,ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 0)}.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){border-radius:var(--hx-radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-black)}@supports (color:color-mix(in lab, red, red)){.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:color-mix(in oklab, var(--hx-color-black) 4%, transparent)}}.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:var(--hx-color-black)}@supports (color:color-mix(in lab, red, red)){.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:color-mix(in oklab, var(--hx-color-black) 3%, transparent)}}.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline:.25em;padding-block:calc(var(--hx-spacing) * .5);overflow-wrap:break-word;font-size:.9em}.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-white)}@supports (color:color-mix(in lab, red, red)){.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-white) 10%, transparent)}}.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:var(--hx-color-white)}@supports (color:color-mix(in lab, red, red)){.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-white) 10%, transparent)}}.content :where(pre.mermaid):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){background-color:#0000;border-radius:0}.content :where(pre.mermaid):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:#0000}.content :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-inline:auto;margin-block:calc(var(--hx-spacing) * 4);border-radius:var(--hx-radius-md)}.content :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)) figcaption{margin-top:calc(var(--hx-spacing) * 2);text-align:center;font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height));color:var(--hx-color-gray-500);display:block}.content :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)) figcaption:where(.dark,.dark *){color:var(--hx-color-gray-400)}.content :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)) dt{margin-top:calc(var(--hx-spacing) * 6);--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold)}.content :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)) dd{margin-block:calc(var(--hx-spacing) * 2);padding-inline-start:calc(var(--hx-spacing) * 6)}.content :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-block:calc(var(--hx-spacing) * 10);border-color:var(--hx-color-gray-200)}.content :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)):last-child{margin-bottom:calc(var(--hx-spacing) * 0)}.content :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-neutral-800)}.content .footnotes{margin-top:calc(var(--hx-spacing) * 12);font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height))}.content .footnotes hr{border-color:var(--hx-color-gray-200)}.content .footnotes hr:where(.dark,.dark *){border-color:var(--hx-color-neutral-800)}.content .subheading-anchor{opacity:0;transition-property:opacity;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.content .subheading-anchor:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:calc(var(--hx-spacing) * 1)}.content .subheading-anchor:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:calc(var(--hx-spacing) * 1)}span:target+:is(.content .subheading-anchor),:hover>:is(.content .subheading-anchor),.content .subheading-anchor:focus-visible{opacity:1}span+:is(.content .subheading-anchor),:hover>:is(.content .subheading-anchor){text-decoration-line:none!important}.content .subheading-anchor:after{content:var(--tw-content);color:var(--hx-color-gray-300)}.content .subheading-anchor:where(.dark,.dark *):after{content:var(--tw-content);color:var(--hx-color-neutral-700)}.content .subheading-anchor:after{padding-inline:calc(var(--hx-spacing) * 1);--tw-content:"#";content:var(--tw-content)}span:target+:is(){color:var(--hx-color-gray-400)}span:target+:is():where(.dark,.dark *){color:var(--hx-color-neutral-500)}article details>summary::-webkit-details-marker{display:none}article details>summary:before{vertical-align:-4px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='hx:h-5 hx:w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z' clip-rule='evenodd' /%3E%3C/svg%3E");width:1.2em;height:1.2em;padding:0 .6em}:lang(fa) ol{list-style-type:persian}.highlight .chroma .err{color:#a61717;background-color:#e3d2d2}.highlight .chroma .lnlinks{color:inherit;outline:none;text-decoration:none}.highlight .chroma .line{display:flex}.highlight .chroma .k,.highlight .chroma .kc,.highlight .chroma .kd,.highlight .chroma .kn,.highlight .chroma .kp,.highlight .chroma .kr{color:#000;font-weight:700}.highlight .chroma .kt{color:#458;font-weight:700}.highlight .chroma .na{color:teal}.highlight .chroma .nb{color:#0086b3}.highlight .chroma .bp{color:#999}.highlight .chroma .nc{color:#458;font-weight:700}.highlight .chroma .no{color:teal}.highlight .chroma .nd{color:#3c5d5d;font-weight:700}.highlight .chroma .ni{color:purple}.highlight .chroma .ne,.highlight .chroma .nf,.highlight .chroma .nl{color:#900;font-weight:700}.highlight .chroma .nn{color:#555}.highlight .chroma .nt{color:navy}.highlight .chroma .nv,.highlight .chroma .vc,.highlight .chroma .vg,.highlight .chroma .vi{color:teal}.highlight .chroma .s,.highlight .chroma .sa,.highlight .chroma .sb,.highlight .chroma .sc,.highlight .chroma .dl,.highlight .chroma .sd,.highlight .chroma .s2,.highlight .chroma .se,.highlight .chroma .sh,.highlight .chroma .si,.highlight .chroma .sx{color:#d14}.highlight .chroma .sr{color:#009926}.highlight .chroma .s1{color:#d14}.highlight .chroma .ss{color:#990073}.highlight .chroma .m,.highlight .chroma .mb,.highlight .chroma .mf,.highlight .chroma .mh,.highlight .chroma .mi,.highlight .chroma .il,.highlight .chroma .mo{color:#099}.highlight .chroma .o,.highlight .chroma .ow{color:#000;font-weight:700}.highlight .chroma .c,.highlight .chroma .ch,.highlight .chroma .cm,.highlight .chroma .c1{color:#998;font-style:italic}.highlight .chroma .cs,.highlight .chroma .cp,.highlight .chroma .cpf{color:#999;font-style:italic;font-weight:700}.highlight .chroma .gd{color:#000;background-color:#fdd}.highlight .chroma .ge{color:#000;font-style:italic}.highlight .chroma .gr{color:#a00}.highlight .chroma .gh{color:#999}.highlight .chroma .gi{color:#000;background-color:#dfd}.highlight .chroma .go{color:#888}.highlight .chroma .gp{color:#555}.highlight .chroma .gs{font-weight:700}.highlight .chroma .gu{color:#aaa}.highlight .chroma .gt{color:#a00}.highlight .chroma .gl{text-decoration:underline}.highlight .chroma .w{color:#bbb}.dark .highlight .chroma .err{color:#f85149}.dark .highlight .chroma .lnlinks{color:inherit;outline:none;text-decoration:none}.dark .highlight .chroma .line{display:flex}.dark .highlight .chroma .k{color:#ff7b72}.dark .highlight .chroma .kc{color:#79c0ff}.dark .highlight .chroma .kd,.dark .highlight .chroma .kn{color:#ff7b72}.dark .highlight .chroma .kp{color:#79c0ff}.dark .highlight .chroma .kr,.dark .highlight .chroma .kt{color:#ff7b72}.dark .highlight .chroma .nc{color:#f0883e;font-weight:700}.dark .highlight .chroma .no{color:#79c0ff;font-weight:700}.dark .highlight .chroma .nd{color:#d2a8ff;font-weight:700}.dark .highlight .chroma .ni{color:#ffa657}.dark .highlight .chroma .ne{color:#f0883e;font-weight:700}.dark .highlight .chroma .nf{color:#d2a8ff;font-weight:700}.dark .highlight .chroma .nl{color:#79c0ff;font-weight:700}.dark .highlight .chroma .nn{color:#ff7b72}.dark .highlight .chroma .py{color:#79c0ff}.dark .highlight .chroma .nt{color:#7ee787}.dark .highlight .chroma .nv{color:#79c0ff}.dark .highlight .chroma .l{color:#a5d6ff}.dark .highlight .chroma .ld{color:#79c0ff}.dark .highlight .chroma .s{color:#a5d6ff}.dark .highlight .chroma .sa{color:#79c0ff}.dark .highlight .chroma .sb,.dark .highlight .chroma .sc{color:#a5d6ff}.dark .highlight .chroma .dl{color:#79c0ff}.dark .highlight .chroma .sd,.dark .highlight .chroma .s2{color:#a5d6ff}.dark .highlight .chroma .se,.dark .highlight .chroma .sh{color:#79c0ff}.dark .highlight .chroma .si,.dark .highlight .chroma .sx{color:#a5d6ff}.dark .highlight .chroma .sr{color:#79c0ff}.dark .highlight .chroma .s1,.dark .highlight .chroma .ss,.dark .highlight .chroma .m,.dark .highlight .chroma .mb,.dark .highlight .chroma .mf,.dark .highlight .chroma .mh,.dark .highlight .chroma .mi,.dark .highlight .chroma .il,.dark .highlight .chroma .mo{color:#a5d6ff}.dark .highlight .chroma .o,.dark .highlight .chroma .ow{color:#ff7b72;font-weight:700}.dark .highlight .chroma .c,.dark .highlight .chroma .ch,.dark .highlight .chroma .cm,.dark .highlight .chroma .c1{color:#8b949e;font-style:italic}.dark .highlight .chroma .cs,.dark .highlight .chroma .cp,.dark .highlight .chroma .cpf{color:#8b949e;font-style:italic;font-weight:700}.dark .highlight .chroma .gd{color:#ffa198;background-color:#490202}.dark .highlight .chroma .ge{color:inherit;font-style:italic}.dark .highlight .chroma .gr{color:#ffa198}.dark .highlight .chroma .gh{color:#79c0ff;font-weight:700}.dark .highlight .chroma .gi{color:#56d364;background-color:#0f5323}.dark .highlight .chroma .go,.dark .highlight .chroma .gp{color:#8b949e}.dark .highlight .chroma .gs{font-weight:700}.dark .highlight .chroma .gu{color:#79c0ff}.dark .highlight .chroma .gt{color:#ff7b72}.dark .highlight .chroma .gl{text-decoration:underline}.dark .highlight .chroma .w{color:#6e7681}.hextra-code-block{--tw-leading:calc(var(--hx-spacing) * 5);font-size:.9em;line-height:calc(var(--hx-spacing) * 5)}.hextra-code-block pre{background-color:var(--hx-color-primary-700);overflow-x:auto}@supports (color:color-mix(in lab, red, red)){.hextra-code-block pre{background-color:color-mix(in oklab, var(--hx-color-primary-700) 5%, transparent)}}.hextra-code-block pre{--tw-font-weight:var(--hx-font-weight-medium);font-size:.9em;font-weight:var(--hx-font-weight-medium);-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}@media (prefers-contrast:more){.hextra-code-block pre{border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-primary-900)}@supports (color:color-mix(in lab, red, red)){.hextra-code-block pre{border-color:color-mix(in oklab, var(--hx-color-primary-900) 20%, transparent)}}.hextra-code-block pre{--tw-contrast:contrast(150%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}}.hextra-code-block pre:where(.dark,.dark *){background-color:var(--hx-color-primary-300)}@supports (color:color-mix(in lab, red, red)){.hextra-code-block pre:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-primary-300) 10%, transparent)}}@media (prefers-contrast:more){.hextra-code-block pre:where(.dark,.dark *){border-color:var(--hx-color-primary-100)}@supports (color:color-mix(in lab, red, red)){.hextra-code-block pre:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-primary-100) 40%, transparent)}}}.hextra-code-block .hextra-code-filename{top:calc(var(--hx-spacing) * 0);z-index:1;text-overflow:ellipsis;white-space:nowrap;border-top-left-radius:var(--hx-radius-xl);border-top-right-radius:var(--hx-radius-xl);background-color:var(--hx-color-primary-700);width:100%;position:absolute;overflow:hidden}@supports (color:color-mix(in lab, red, red)){.hextra-code-block .hextra-code-filename{background-color:color-mix(in oklab, var(--hx-color-primary-700) 5%, transparent)}}.hextra-code-block .hextra-code-filename{padding-inline:calc(var(--hx-spacing) * 4);padding-block:calc(var(--hx-spacing) * 2);font-size:var(--hx-text-xs);line-height:var(--tw-leading,var(--hx-text-xs--line-height));color:var(--hx-color-gray-700)}.hextra-code-block .hextra-code-filename:where(.dark,.dark *){background-color:var(--hx-color-primary-300)}@supports (color:color-mix(in lab, red, red)){.hextra-code-block .hextra-code-filename:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-primary-300) 10%, transparent)}}.hextra-code-block .hextra-code-filename:where(.dark,.dark *){color:var(--hx-color-gray-200)}.hextra-code-block .hextra-code-filename+pre:not(.lntable pre){padding-top:calc(var(--hx-spacing) * 12)}.hextra-code-block pre:not(.lntable pre){margin-bottom:calc(var(--hx-spacing) * 4);border-radius:var(--hx-radius-xl);padding-inline:calc(var(--hx-spacing) * 4);padding-block:calc(var(--hx-spacing) * 4)}.hextra-code-block div:nth-of-type(2) pre{padding-top:calc(var(--hx-spacing) * 12);padding-bottom:calc(var(--hx-spacing) * 4)}.chroma .lntable{margin:calc(var(--hx-spacing) * 0);border-radius:var(--hx-radius-xl);width:auto;display:block;overflow:auto}.chroma .lntable pre{padding-top:calc(var(--hx-spacing) * 4);padding-bottom:calc(var(--hx-spacing) * 4)}.chroma .ln,.chroma .lnt:not(.hl>.lnt),.chroma .hl:not(.line){min-width:2.6rem;padding-right:calc(var(--hx-spacing) * 4);padding-left:calc(var(--hx-spacing) * 4);color:var(--hx-color-neutral-600)}:is(.chroma .ln,.chroma .lnt:not(.hl>.lnt),.chroma .hl:not(.line)):where(.dark,.dark *){color:var(--hx-color-neutral-300)}.chroma .lntd{padding:calc(var(--hx-spacing) * 0);vertical-align:top}.chroma .lntd:last-of-type{width:100%}.chroma .hl{background-color:var(--hx-color-primary-800);width:100%;display:block}@supports (color:color-mix(in lab, red, red)){.chroma .hl{background-color:color-mix(in oklab, var(--hx-color-primary-800) 10%, transparent)}}.hextra-cards{grid-template-columns:repeat(auto-fill, minmax(max(250px, calc((100% - 1rem * 2) / var(--hextra-cards-grid-cols))), 1fr))}.hextra-card{position:relative}.hextra-card img{-webkit-user-select:none;user-select:none}.hextra-card:hover .hextra-card-icon svg{color:currentColor}.hextra-card .hextra-card-icon svg{color:#0003;width:1.5rem;transition:color .3s}.hextra-card p{margin-top:.5rem;position:relative}.dark .hextra-card .hextra-card-icon svg{color:#fff6}.dark .hextra-card:hover .hextra-card-icon svg{color:currentColor}.hextra-card-tag{z-index:10;position:absolute;top:5px}.hextra-card-tag:where(:dir(ltr)){right:5px}.hextra-card-tag:where(:dir(rtl)){left:5px}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker){counter-increment:step}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):where(:dir(ltr),[dir=ltr],[dir=ltr] *):before{content:var(--tw-content);margin-left:-41px}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):where(:dir(rtl),[dir=rtl],[dir=rtl] *):before{content:var(--tw-content);margin-right:-44px}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):before{content:var(--tw-content);background-color:var(--hx-color-gray-100)}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):where(.dark,.dark *):before{content:var(--tw-content);background-color:var(--hx-color-neutral-800)}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):before{content:var(--tw-content);border-style:var(--tw-border-style);content:var(--tw-content);border-width:4px;border-color:var(--hx-color-white)}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):where(.dark,.dark *):before{content:var(--tw-content);border-color:var(--hx-color-dark)}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):before{content:counter(step);text-align:center;text-indent:-1px;width:33px;height:33px;font-size:var(--hx-text-base);line-height:var(--tw-leading,var(--hx-text-base--line-height));--tw-font-weight:var(--hx-font-weight-normal);font-weight:var(--hx-font-weight-normal);color:var(--hx-color-neutral-400);border-radius:3.40282e38px;position:absolute}:lang(fa) .hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):before{content:counter(step, persian)}.hextra-search-wrapper li{margin-inline:calc(var(--hx-spacing) * 2.5);border-radius:var(--hx-radius-md);overflow-wrap:break-word;color:var(--hx-color-gray-800)}@media (prefers-contrast:more){.hextra-search-wrapper li{border-style:var(--tw-border-style);border-width:1px;border-color:#0000}}.hextra-search-wrapper li:where(.dark,.dark *){color:var(--hx-color-gray-300)}.hextra-search-wrapper li a{scroll-margin:calc(var(--hx-spacing) * 12);padding-inline:calc(var(--hx-spacing) * 2.5);padding-block:calc(var(--hx-spacing) * 2);display:block}.hextra-search-wrapper li a:focus-visible{--tw-outline-style:none;outline-style:none}.hextra-search-wrapper li .hextra-search-title{font-size:var(--hx-text-base);line-height:var(--tw-leading,var(--hx-text-base--line-height));--tw-leading:calc(var(--hx-spacing) * 5);line-height:calc(var(--hx-spacing) * 5);--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold)}.hextra-search-wrapper li .hextra-search-active{border-radius:var(--hx-radius-md);background-color:var(--hx-color-primary-500)}@supports (color:color-mix(in lab, red, red)){.hextra-search-wrapper li .hextra-search-active{background-color:color-mix(in oklab, var(--hx-color-primary-500) 10%, transparent)}}@media (prefers-contrast:more){.hextra-search-wrapper li .hextra-search-active{border-color:var(--hx-color-primary-500)}}.hextra-search-wrapper .hextra-search-no-result{padding:calc(var(--hx-spacing) * 8);text-align:center;font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height));color:var(--hx-color-gray-400);-webkit-user-select:none;user-select:none;display:block}.hextra-search-wrapper .hextra-search-prefix{margin-inline:calc(var(--hx-spacing) * 2.5);margin-top:calc(var(--hx-spacing) * 6);margin-bottom:calc(var(--hx-spacing) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--hx-color-black)}@supports (color:color-mix(in lab, red, red)){.hextra-search-wrapper .hextra-search-prefix{border-color:color-mix(in oklab, var(--hx-color-black) 10%, transparent)}}.hextra-search-wrapper .hextra-search-prefix{padding-inline:calc(var(--hx-spacing) * 2.5);padding-bottom:calc(var(--hx-spacing) * 1.5);font-size:var(--hx-text-xs);line-height:var(--tw-leading,var(--hx-text-xs--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);color:var(--hx-color-gray-500);text-transform:uppercase;-webkit-user-select:none;user-select:none}.hextra-search-wrapper .hextra-search-prefix:first-child{margin-top:calc(var(--hx-spacing) * 0)}@media (prefers-contrast:more){.hextra-search-wrapper .hextra-search-prefix{border-color:var(--hx-color-gray-600);color:var(--hx-color-gray-900)}}.hextra-search-wrapper .hextra-search-prefix:where(.dark,.dark *){border-color:var(--hx-color-white)}@supports (color:color-mix(in lab, red, red)){.hextra-search-wrapper .hextra-search-prefix:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-white) 20%, transparent)}}.hextra-search-wrapper .hextra-search-prefix:where(.dark,.dark *){color:var(--hx-color-gray-300)}@media (prefers-contrast:more){.hextra-search-wrapper .hextra-search-prefix:where(.dark,.dark *){border-color:var(--hx-color-gray-50);color:var(--hx-color-gray-50)}}.hextra-search-wrapper .hextra-search-excerpt{margin-top:calc(var(--hx-spacing) * 1);font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height));--tw-leading:1.35rem;text-overflow:ellipsis;color:var(--hx-color-gray-600);line-height:1.35rem;overflow:hidden}.hextra-search-wrapper .hextra-search-excerpt:where(.dark,.dark *){color:var(--hx-color-gray-400)}@media (prefers-contrast:more){.hextra-search-wrapper .hextra-search-excerpt:where(.dark,.dark *){color:var(--hx-color-gray-50)}}.hextra-search-wrapper .hextra-search-excerpt{line-clamp:1;-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box}.hextra-search-wrapper .hextra-search-match{color:var(--hx-color-primary-600)}@media (max-width:48rem){.hextra-sidebar-container{top:calc(var(--hx-spacing) * 0);bottom:calc(var(--hx-spacing) * 0);z-index:15;overscroll-behavior:contain;background-color:var(--hx-color-white);width:100%;padding-top:calc(var(--navbar-height) + var(--hextra-banner-height));position:fixed}.hextra-sidebar-container:where(.dark,.dark *){background-color:var(--hx-color-dark)}.hextra-sidebar-container{will-change:transform, opacity;contain:layout style;backface-visibility:hidden;transition:transform .4s cubic-bezier(.52,.16,.04,1)}}.hextra-sidebar-container li>.hextra-sidebar-children{height:calc(var(--hx-spacing) * 0)}.hextra-sidebar-container li.open>.hextra-sidebar-children{height:auto;padding-top:calc(var(--hx-spacing) * 1)}.hextra-sidebar-container li.open>.hextra-sidebar-item>.hextra-sidebar-collapsible-button>svg>path{rotate:90deg}.hextra-banner-hidden .hextra-banner{display:none}.hextra-banner :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:underline;text-decoration-thickness:from-font}.hextra-banner :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){--tw-leading:calc(var(--hx-spacing) * 7);line-height:calc(var(--hx-spacing) * 7)}.hextra-banner :where(p):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}nav .hextra-search-wrapper{display:none}@media (min-width:48rem){nav .hextra-search-wrapper{display:inline-block}}@supports ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){.hextra-nav-container-blur{background-color:var(--hx-color-white)}@supports (color:color-mix(in lab, red, red)){.hextra-nav-container-blur{background-color:color-mix(in oklab, var(--hx-color-white) 85%, transparent)}}.hextra-nav-container-blur{--tw-backdrop-blur:blur(var(--hx-blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.hextra-nav-container-blur:where(.dark,.dark *){background-color:var(--hx-color-dark)!important}@supports (color:color-mix(in lab, red, red)){.hextra-nav-container-blur:where(.dark,.dark *){background-color:color-mix(in oklab, var(--hx-color-dark) 80%, transparent)!important}}}.hextra-hamburger-menu svg g{transform-origin:50%;transition-property:all;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration));--tw-duration:.1s;--tw-ease:var(--hx-ease-out);transition-duration:.1s;transition-timing-function:var(--hx-ease-out)}.hextra-hamburger-menu svg path{opacity:1;transition-property:all;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration));--tw-duration:.1s;--tw-ease:var(--hx-ease-out);transition-duration:.1s;transition-delay:.1s;transition-timing-function:var(--hx-ease-out)}.hextra-hamburger-menu svg.open path{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration));--tw-duration:.1s;--tw-ease:var(--hx-ease-out);transition-duration:.1s;transition-delay:0s;transition-timing-function:var(--hx-ease-out)}.hextra-hamburger-menu svg.open g{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration));--tw-duration:.1s;--tw-ease:var(--hx-ease-out);transition-duration:.1s;transition-delay:.1s;transition-timing-function:var(--hx-ease-out)}.hextra-hamburger-menu svg.open>path{opacity:0}.hextra-hamburger-menu svg.open>g:first-of-type{rotate:45deg}.hextra-hamburger-menu svg.open>g:first-of-type path{--tw-translate-y:calc(var(--hx-spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.hextra-hamburger-menu svg.open>g:nth-of-type(2){rotate:-45deg}.hextra-hamburger-menu svg.open>g:nth-of-type(2) path{--tw-translate-y:calc(var(--hx-spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.hextra-scrollbar,.hextra-scrollbar *{scrollbar-width:thin;scrollbar-color:oklch(55.55% 0 0/.4) transparent;scrollbar-gutter:stable}:is(.hextra-scrollbar,.hextra-scrollbar *)::-webkit-scrollbar{height:calc(var(--hx-spacing) * 3);width:calc(var(--hx-spacing) * 3)}:is(.hextra-scrollbar,.hextra-scrollbar *)::-webkit-scrollbar-track{background-color:#0000}:is(.hextra-scrollbar,.hextra-scrollbar *)::-webkit-scrollbar-thumb{border-radius:10px}:is(.hextra-scrollbar,.hextra-scrollbar *):hover::-webkit-scrollbar-thumb{background-color:var(--tw-shadow-color);--tw-shadow-color:var(--hx-color-neutral-500);background-clip:content-box;border:3px solid #0000}@supports (color:color-mix(in lab, red, red)){:is(.hextra-scrollbar,.hextra-scrollbar *):hover::-webkit-scrollbar-thumb{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--hx-color-neutral-500) 20%, transparent) var(--tw-shadow-alpha), transparent)}}@media (hover:hover){:is(.hextra-scrollbar,.hextra-scrollbar *):hover::-webkit-scrollbar-thumb:hover{--tw-shadow-color:var(--hx-color-neutral-500)}@supports (color:color-mix(in lab, red, red)){:is(.hextra-scrollbar,.hextra-scrollbar *):hover::-webkit-scrollbar-thumb:hover{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--hx-color-neutral-500) 40%, transparent) var(--tw-shadow-alpha), transparent)}}}@supports ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){.hextra-code-copy-btn{opacity:.85;--tw-backdrop-blur:blur(var(--hx-blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.hextra-code-copy-btn:where(.dark,.dark *){opacity:.8}}@media (min-width:1024px){.hextra-feature-grid{grid-template-columns:repeat(var(--hextra-feature-grid-cols), minmax(0, 1fr))}}.hextra-jupyter-code-cell{scrollbar-gutter:auto;margin-top:calc(var(--hx-spacing) * 6)}.hextra-jupyter-code-cell .hextra-jupyter-code-cell-outputs-container{font-size:var(--hx-text-xs);line-height:var(--tw-leading,var(--hx-text-xs--line-height));overflow:hidden}.hextra-jupyter-code-cell .hextra-jupyter-code-cell-outputs-container .hextra-jupyter-code-cell-outputs{max-height:50vh;overflow:auto}.hextra-jupyter-code-cell .hextra-jupyter-code-cell-outputs-container .hextra-jupyter-code-cell-outputs pre{max-width:100%;font-size:var(--hx-text-xs);line-height:var(--tw-leading,var(--hx-text-xs--line-height));overflow:auto}.hextra-badge{align-items:center;display:inline-flex}.hextra-toc a.hextra-toc-active{transition-property:all;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration));--tw-duration:.2s;transition-duration:.2s;color:var(--hx-color-gray-900)!important}.hextra-toc a.hextra-toc-active:where(.dark,.dark *){color:var(--hx-color-gray-50)!important}.hextra-archive-timeline{border-left-style:var(--tw-border-style);border-left-width:2px;border-color:var(--hx-color-black)}@supports (color:color-mix(in lab, red, red)){.hextra-archive-timeline{border-color:color-mix(in oklab, var(--hx-color-black) 15%, transparent)}}.hextra-archive-timeline:where(.dark,.dark *){border-color:var(--hx-color-white)}@supports (color:color-mix(in lab, red, red)){.hextra-archive-timeline:where(.dark,.dark *){border-color:color-mix(in oklab, var(--hx-color-white) 15%, transparent)}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{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-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{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-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false} diff --git a/public/css/compiled/main.min.3f70c37b0f2ce37d160ca0a36b7edfe7d6718ac312bc985e36fe9fb736850a27.css b/public/css/compiled/main.min.3f70c37b0f2ce37d160ca0a36b7edfe7d6718ac312bc985e36fe9fb736850a27.css new file mode 100644 index 0000000..bacabcf --- /dev/null +++ b/public/css/compiled/main.min.3f70c37b0f2ce37d160ca0a36b7edfe7d6718ac312bc985e36fe9fb736850a27.css @@ -0,0 +1 @@ +:root{--hextra-max-page-width:80rem;--hextra-max-content-width:72rem;--hextra-max-navbar-width:80rem;--hextra-max-footer-width:80rem}.hextra-max-page-width{max-width:var(--hextra-max-page-width)}.hextra-max-content-width{max-width:var(--hextra-max-content-width)}.hextra-max-navbar-width{max-width:var(--hextra-max-navbar-width)}.hextra-max-footer-width{max-width:var(--hextra-max-footer-width)}/*!tailwindcss v4.3.0 | 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-translate-x:0;--tw-translate-y:0;--tw-translate-z: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-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction: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-duration:initial;--tw-ease:initial;--tw-content:"";--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--hx-font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--hx-font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--hx-color-red-100:oklch(93.6% .032 17.717);--hx-color-red-200:oklch(88.5% .062 18.334);--hx-color-red-900:oklch(39.6% .141 25.723);--hx-color-orange-50:oklch(98% .016 73.684);--hx-color-orange-100:oklch(95.4% .038 75.164);--hx-color-orange-300:oklch(83.7% .128 66.29);--hx-color-orange-400:oklch(75% .183 55.934);--hx-color-orange-800:oklch(47% .157 37.304);--hx-color-amber-100:oklch(96.2% .059 95.617);--hx-color-amber-200:oklch(92.4% .12 95.746);--hx-color-amber-900:oklch(41.4% .112 45.904);--hx-color-yellow-50:oklch(98.7% .026 102.212);--hx-color-yellow-100:oklch(97.3% .071 103.193);--hx-color-yellow-200:oklch(94.5% .129 101.54);--hx-color-yellow-700:oklch(55.4% .135 66.442);--hx-color-yellow-900:oklch(42.1% .095 57.708);--hx-color-green-100:oklch(96.2% .044 156.743);--hx-color-green-200:oklch(92.5% .084 155.995);--hx-color-green-900:oklch(39.3% .095 152.535);--hx-color-blue-100:oklch(93.2% .032 255.585);--hx-color-blue-200:oklch(88.2% .059 254.128);--hx-color-blue-900:oklch(37.9% .146 265.522);--hx-color-indigo-100:oklch(93% .034 272.788);--hx-color-indigo-200:oklch(87% .065 274.039);--hx-color-indigo-900:oklch(35.9% .144 278.697);--hx-color-purple-100:oklch(94.6% .033 307.174);--hx-color-purple-200:oklch(90.2% .063 306.703);--hx-color-purple-900:oklch(38.1% .176 304.987);--hx-color-slate-50:oklch(98.4% .003 247.858);--hx-color-slate-100:oklch(96.8% .007 247.896);--hx-color-slate-900:oklch(20.8% .042 265.755);--hx-color-gray-50:oklch(98.5% .002 247.839);--hx-color-gray-100:oklch(96.7% .003 264.542);--hx-color-gray-200:oklch(92.8% .006 264.531);--hx-color-gray-300:oklch(87.2% .01 258.338);--hx-color-gray-400:oklch(70.7% .022 261.325);--hx-color-gray-500:oklch(55.1% .027 264.364);--hx-color-gray-600:oklch(44.6% .03 256.802);--hx-color-gray-700:oklch(37.3% .034 259.733);--hx-color-gray-800:oklch(27.8% .033 256.848);--hx-color-gray-900:oklch(21% .034 264.665);--hx-color-neutral-50:oklch(98.5% 0 0);--hx-color-neutral-200:oklch(92.2% 0 0);--hx-color-neutral-300:oklch(87% 0 0);--hx-color-neutral-400:oklch(70.8% 0 0);--hx-color-neutral-500:oklch(55.6% 0 0);--hx-color-neutral-600:oklch(43.9% 0 0);--hx-color-neutral-700:oklch(37.1% 0 0);--hx-color-neutral-800:oklch(26.9% 0 0);--hx-color-neutral-900:oklch(20.5% 0 0);--hx-color-black:#000;--hx-color-white:#fff;--hx-spacing:.25rem;--hx-breakpoint-xl:80rem;--hx-text-xs:.75rem;--hx-text-xs--line-height:calc(1 / .75);--hx-text-sm:.875rem;--hx-text-sm--line-height:calc(1.25 / .875);--hx-text-base:1rem;--hx-text-base--line-height:calc(1.5 / 1);--hx-text-lg:1.125rem;--hx-text-lg--line-height:calc(1.75 / 1.125);--hx-text-xl:1.25rem;--hx-text-xl--line-height:calc(1.75 / 1.25);--hx-text-2xl:1.5rem;--hx-text-2xl--line-height:calc(2 / 1.5);--hx-text-3xl:1.875rem;--hx-text-3xl--line-height:calc(2.25 / 1.875);--hx-text-4xl:2.25rem;--hx-text-4xl--line-height:calc(2.5 / 2.25);--hx-text-5xl:3rem;--hx-text-5xl--line-height:1;--hx-font-weight-normal:400;--hx-font-weight-medium:500;--hx-font-weight-semibold:600;--hx-font-weight-bold:700;--hx-font-weight-extrabold:800;--hx-tracking-tighter:-.05em;--hx-tracking-tight:-.025em;--hx-leading-tight:1.25;--hx-radius-xs:.125rem;--hx-radius-sm:.25rem;--hx-radius-md:.375rem;--hx-radius-lg:.5rem;--hx-radius-xl:.75rem;--hx-radius-3xl:1.5rem;--hx-ease-in:cubic-bezier(.4, 0, 1, 1);--hx-ease-out:cubic-bezier(0, 0, .2, 1);--hx-ease-in-out:cubic-bezier(.4, 0, .2, 1);--hx-blur-md:12px;--hx-default-transition-duration:.15s;--hx-default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--hx-default-font-family:var(--hx-font-sans);--hx-default-mono-font-family:var(--hx-font-mono);--hx-color-primary-100:hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 44));--hx-color-primary-200:hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 36));--hx-color-primary-300:hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 27));--hx-color-primary-400:hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 16));--hx-color-primary-500:hsl(var(--primary-hue) var(--primary-saturation) var(--primary-lightness));--hx-color-primary-600:hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 45));--hx-color-primary-700:hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 39));--hx-color-primary-800:hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 32));--hx-color-primary-900:hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 24));--hx-color-dark:#111}}@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(--hx-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(--hx-default-font-feature-settings,normal);font-variation-settings:var(--hx-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(--hx-default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--hx-default-mono-font-feature-settings,normal);font-variation-settings:var(--hx-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}::-webkit-calendar-picker-indicator{line-height:1}:-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}abbr:where([title]){cursor:help}:where(a,button,[role=tab],[role=menuitem],[role=menuitemradio],input,select,textarea,[tabindex="0"]):not([class*=hextra-focus-visible]):focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--hx-color-primary-200);--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-offset-color:var(--hx-color-primary-300);--tw-outline-style:none;outline-style:none}:where(a,button,[role=tab],[role=menuitem],[role=menuitemradio],input,select,textarea,[tabindex="0"]):not([class*=hextra-focus-visible]):focus-visible:where(.dark,.dark *){--tw-ring-color:var(--hx-color-primary-800);--tw-ring-offset-color:var(--hx-color-primary-700)}}@layer components;@layer utilities{.hx\:pointer-events-none{pointer-events:none}.hx\:sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.hx\:absolute{position:absolute}.hx\:relative{position:relative}.hx\:sticky{position:sticky}.hx\:inset-x-0{inset-inline:calc(var(--hx-spacing) * 0)}.hx\:inset-y-0{inset-block:calc(var(--hx-spacing) * 0)}.hx\:top-0{top:calc(var(--hx-spacing) * 0)}.hx\:top-1\/2{top:50%}.hx\:top-8{top:calc(var(--hx-spacing) * 8)}.hx\:top-16{top:calc(var(--hx-spacing) * 16)}.hx\:top-\[40\%\]{top:40%}.hx\:top-full{top:100%}.hx\:right-0{right:calc(var(--hx-spacing) * 0)}.hx\:bottom-0{bottom:calc(var(--hx-spacing) * 0)}.hx\:left-0{left:calc(var(--hx-spacing) * 0)}.hx\:left-\[24px\]{left:24px}.hx\:left-\[36px\]{left:36px}.hx\:z-20{z-index:20}.hx\:z-\[-1\]{z-index:-1}.hx\:order-last{order:9999}.hx\:m-\[11px\]{margin:11px}.hx\:mx-1{margin-inline:calc(var(--hx-spacing) * 1)}.hx\:mx-4{margin-inline:calc(var(--hx-spacing) * 4)}.hx\:mx-auto{margin-inline:auto}.hx\:my-1{margin-block:calc(var(--hx-spacing) * 1)}.hx\:my-1\.5{margin-block:calc(var(--hx-spacing) * 1.5)}.hx\:my-2{margin-block:calc(var(--hx-spacing) * 2)}.hx\:-mt-20{margin-top:calc(var(--hx-spacing) * -20)}.hx\:mt-1{margin-top:calc(var(--hx-spacing) * 1)}.hx\:mt-1\.5{margin-top:calc(var(--hx-spacing) * 1.5)}.hx\:mt-2{margin-top:calc(var(--hx-spacing) * 2)}.hx\:mt-4{margin-top:calc(var(--hx-spacing) * 4)}.hx\:mt-5{margin-top:calc(var(--hx-spacing) * 5)}.hx\:mt-6{margin-top:calc(var(--hx-spacing) * 6)}.hx\:mt-8{margin-top:calc(var(--hx-spacing) * 8)}.hx\:mt-12{margin-top:calc(var(--hx-spacing) * 12)}.hx\:mt-16{margin-top:calc(var(--hx-spacing) * 16)}.hx\:mt-auto{margin-top:auto}.hx\:-mr-2{margin-right:calc(var(--hx-spacing) * -2)}.hx\:mr-1{margin-right:calc(var(--hx-spacing) * 1)}.hx\:mr-2{margin-right:calc(var(--hx-spacing) * 2)}.hx\:-mb-0\.5{margin-bottom:calc(var(--hx-spacing) * -.5)}.hx\:mb-0{margin-bottom:calc(var(--hx-spacing) * 0)}.hx\:mb-2{margin-bottom:calc(var(--hx-spacing) * 2)}.hx\:mb-4{margin-bottom:calc(var(--hx-spacing) * 4)}.hx\:mb-6{margin-bottom:calc(var(--hx-spacing) * 6)}.hx\:mb-8{margin-bottom:calc(var(--hx-spacing) * 8)}.hx\:mb-10{margin-bottom:calc(var(--hx-spacing) * 10)}.hx\:mb-12{margin-bottom:calc(var(--hx-spacing) * 12)}.hx\:mb-16{margin-bottom:calc(var(--hx-spacing) * 16)}.hx\:-ml-2{margin-left:calc(var(--hx-spacing) * -2)}.hx\:ml-2{margin-left:calc(var(--hx-spacing) * 2)}.hx\:ml-4{margin-left:calc(var(--hx-spacing) * 4)}.hx\:line-clamp-3{-webkit-line-clamp:3;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.hx\:block{display:block}.hx\:flex{display:flex}.hx\:grid{display:grid}.hx\:hidden{display:none}.hx\:inline{display:inline}.hx\:inline-block{display:inline-block}.hx\:inline-flex{display:inline-flex}.hx\:aspect-auto{aspect-ratio:auto}.hx\:size-4{width:calc(var(--hx-spacing) * 4);height:calc(var(--hx-spacing) * 4)}.hx\:h-0{height:calc(var(--hx-spacing) * 0)}.hx\:h-2{height:calc(var(--hx-spacing) * 2)}.hx\:h-3\.5{height:calc(var(--hx-spacing) * 3.5)}.hx\:h-4{height:calc(var(--hx-spacing) * 4)}.hx\:h-5{height:calc(var(--hx-spacing) * 5)}.hx\:h-7{height:calc(var(--hx-spacing) * 7)}.hx\:h-10{height:calc(var(--hx-spacing) * 10)}.hx\:h-16{height:calc(var(--hx-spacing) * 16)}.hx\:h-\[18px\]{height:18px}.hx\:h-full{height:100%}.hx\:h-px{height:1px}.hx\:max-h-\(--menu-height\){max-height:var(--menu-height)}.hx\:max-h-64{max-height:calc(var(--hx-spacing) * 64)}.hx\:max-h-\[calc\(100vh-var\(--navbar-height\)-env\(safe-area-inset-bottom\)\)\]{max-height:calc(100vh - var(--navbar-height) - env(safe-area-inset-bottom))}.hx\:max-h-\[min\(calc\(50vh-11rem-env\(safe-area-inset-bottom\)\)\,400px\)\]{max-height:min(calc(50vh - 11rem - env(safe-area-inset-bottom)),400px)}.hx\:min-h-\[100px\]{min-height:100px}.hx\:min-h-\[calc\(100vh-var\(--navbar-height\)\)\]{min-height:calc(100vh - var(--navbar-height))}.hx\:w-2{width:calc(var(--hx-spacing) * 2)}.hx\:w-3\.5{width:calc(var(--hx-spacing) * 3.5)}.hx\:w-4{width:calc(var(--hx-spacing) * 4)}.hx\:w-10{width:calc(var(--hx-spacing) * 10)}.hx\:w-64{width:calc(var(--hx-spacing) * 64)}.hx\:w-\[110\%\]{width:110%}.hx\:w-\[180\%\]{width:180%}.hx\:w-full{width:100%}.hx\:w-max{width:max-content}.hx\:w-screen{width:100vw}.hx\:max-w-\[50\%\]{max-width:50%}.hx\:max-w-\[90rem\]{max-width:90rem}.hx\:max-w-\[min\(calc\(100vw-2rem\)\,calc\(100\%\+20rem\)\)\]{max-width:min(100vw - 2rem,100% + 20rem)}.hx\:max-w-full{max-width:100%}.hx\:max-w-none{max-width:none}.hx\:max-w-screen-xl{max-width:var(--hx-breakpoint-xl)}.hx\:min-w-0{min-width:calc(var(--hx-spacing) * 0)}.hx\:min-w-\[18px\]{min-width:18px}.hx\:min-w-\[24px\]{min-width:24px}.hx\:min-w-full{min-width:100%}.hx\:shrink-0{flex-shrink:0}.hx\:grow{flex-grow:1}.hx\:origin-center{transform-origin:50%}.hx\:-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x)var(--tw-translate-y)}.hx\:cursor-default{cursor:default}.hx\:cursor-pointer{cursor:pointer}.hx\:scroll-my-6{scroll-margin-block:calc(var(--hx-spacing) * 6)}.hx\:scroll-py-6{scroll-padding-block:calc(var(--hx-spacing) * 6)}.hx\:list-none{list-style-type:none}.hx\:appearance-none{appearance:none}.hx\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.hx\:flex-col{flex-direction:column}.hx\:flex-wrap{flex-wrap:wrap}.hx\:items-baseline{align-items:baseline}.hx\:items-center{align-items:center}.hx\:items-start{align-items:flex-start}.hx\:justify-between{justify-content:space-between}.hx\:justify-center{justify-content:center}.hx\:justify-end{justify-content:flex-end}.hx\:justify-start{justify-content:flex-start}.hx\:justify-items-start{justify-items:start}.hx\:gap-1{gap:calc(var(--hx-spacing) * 1)}.hx\:gap-1\.5{gap:calc(var(--hx-spacing) * 1.5)}.hx\:gap-2{gap:calc(var(--hx-spacing) * 2)}.hx\:gap-4{gap:calc(var(--hx-spacing) * 4)}.hx\:gap-x-1\.5{column-gap:calc(var(--hx-spacing) * 1.5)}.hx\:gap-x-2{column-gap:calc(var(--hx-spacing) * 2)}.hx\:gap-y-1{row-gap:calc(var(--hx-spacing) * 1)}.hx\:gap-y-2{row-gap:calc(var(--hx-spacing) * 2)}.hx\:overflow-auto{overflow:auto}.hx\:overflow-hidden{overflow:hidden}.hx\:overflow-x-auto{overflow-x:auto}.hx\:overflow-x-hidden{overflow-x:hidden}.hx\:overflow-y-auto{overflow-y:auto}.hx\:overflow-y-hidden{overflow-y:hidden}.hx\:overscroll-contain{overscroll-behavior:contain}.hx\:overscroll-x-contain{overscroll-behavior-x:contain}.hx\:rounded-3xl{border-radius:var(--hx-radius-3xl)}.hx\:rounded-full{border-radius:340282e33px}.hx\:rounded-lg{border-radius:var(--hx-radius-lg)}.hx\:rounded-md{border-radius:var(--hx-radius-md)}.hx\:rounded-sm{border-radius:var(--hx-radius-sm)}.hx\:rounded-xl{border-radius:var(--hx-radius-xl)}.hx\:rounded-xs{border-radius:var(--hx-radius-xs)}.hx\:rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.hx\:border{border-style:var(--tw-border-style);border-width:1px}.hx\:border-0{border-style:var(--tw-border-style);border-width:0}.hx\:border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.hx\:border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.hx\:border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.hx\:border-amber-200{border-color:var(--hx-color-amber-200)}.hx\:border-black\/5{border-color:var(--hx-color-black)}@supports(color:color-mix(in lab,red,red)){.hx\:border-black\/5{border-color:color-mix(in oklab,var(--hx-color-black) 5%,transparent)}}.hx\:border-blue-200{border-color:var(--hx-color-blue-200)}.hx\:border-gray-200{border-color:var(--hx-color-gray-200)}.hx\:border-gray-500{border-color:var(--hx-color-gray-500)}.hx\:border-green-200{border-color:var(--hx-color-green-200)}.hx\:border-indigo-200{border-color:var(--hx-color-indigo-200)}.hx\:border-orange-100{border-color:var(--hx-color-orange-100)}.hx\:border-purple-200{border-color:var(--hx-color-purple-200)}.hx\:border-red-200{border-color:var(--hx-color-red-200)}.hx\:border-transparent{border-color:#0000}.hx\:border-yellow-100{border-color:var(--hx-color-yellow-100)}.hx\:bg-amber-100{background-color:var(--hx-color-amber-100)}.hx\:bg-black\/\[\.05\]{background-color:var(--hx-color-black)}@supports(color:color-mix(in lab,red,red)){.hx\:bg-black\/\[\.05\]{background-color:color-mix(in oklab,var(--hx-color-black) 5%,transparent)}}.hx\:bg-blue-100{background-color:var(--hx-color-blue-100)}.hx\:bg-gray-100{background-color:var(--hx-color-gray-100)}.hx\:bg-gray-200{background-color:var(--hx-color-gray-200)}.hx\:bg-green-100{background-color:var(--hx-color-green-100)}.hx\:bg-indigo-100{background-color:var(--hx-color-indigo-100)}.hx\:bg-neutral-50{background-color:var(--hx-color-neutral-50)}.hx\:bg-neutral-900{background-color:var(--hx-color-neutral-900)}.hx\:bg-orange-50{background-color:var(--hx-color-orange-50)}.hx\:bg-primary-100{background-color:var(--hx-color-primary-100)}.hx\:bg-primary-400{background-color:var(--hx-color-primary-400)}.hx\:bg-primary-600{background-color:var(--hx-color-primary-600)}.hx\:bg-primary-700\/5{background-color:var(--hx-color-primary-700)}@supports(color:color-mix(in lab,red,red)){.hx\:bg-primary-700\/5{background-color:color-mix(in oklab,var(--hx-color-primary-700) 5%,transparent)}}.hx\:bg-purple-100{background-color:var(--hx-color-purple-100)}.hx\:bg-red-100{background-color:var(--hx-color-red-100)}.hx\:bg-transparent{background-color:#0000}.hx\:bg-white{background-color:var(--hx-color-white)}.hx\:bg-yellow-50{background-color:var(--hx-color-yellow-50)}.hx\:bg-gradient-to-r{--tw-gradient-position:to right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.hx\:from-gray-900{--tw-gradient-from:var(--hx-color-gray-900);--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))}.hx\:to-gray-600{--tw-gradient-to:var(--hx-color-gray-600);--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))}.hx\:bg-clip-text{-webkit-background-clip:text;background-clip:text}.hx\:p-0{padding:calc(var(--hx-spacing) * 0)}.hx\:p-0\.5{padding:calc(var(--hx-spacing) * .5)}.hx\:p-1{padding:calc(var(--hx-spacing) * 1)}.hx\:p-1\.5{padding:calc(var(--hx-spacing) * 1.5)}.hx\:p-2{padding:calc(var(--hx-spacing) * 2)}.hx\:p-4{padding:calc(var(--hx-spacing) * 4)}.hx\:p-6{padding:calc(var(--hx-spacing) * 6)}.hx\:px-1\.5{padding-inline:calc(var(--hx-spacing) * 1.5)}.hx\:px-2{padding-inline:calc(var(--hx-spacing) * 2)}.hx\:px-2\.5{padding-inline:calc(var(--hx-spacing) * 2.5)}.hx\:px-3{padding-inline:calc(var(--hx-spacing) * 3)}.hx\:px-4{padding-inline:calc(var(--hx-spacing) * 4)}.hx\:px-6{padding-inline:calc(var(--hx-spacing) * 6)}.hx\:px-8{padding-inline:calc(var(--hx-spacing) * 8)}.hx\:py-1{padding-block:calc(var(--hx-spacing) * 1)}.hx\:py-1\.5{padding-block:calc(var(--hx-spacing) * 1.5)}.hx\:py-2{padding-block:calc(var(--hx-spacing) * 2)}.hx\:py-2\.5{padding-block:calc(var(--hx-spacing) * 2.5)}.hx\:py-3{padding-block:calc(var(--hx-spacing) * 3)}.hx\:py-4{padding-block:calc(var(--hx-spacing) * 4)}.hx\:py-12{padding-block:calc(var(--hx-spacing) * 12)}.hx\:pt-4{padding-top:calc(var(--hx-spacing) * 4)}.hx\:pt-6{padding-top:calc(var(--hx-spacing) * 6)}.hx\:pt-8{padding-top:calc(var(--hx-spacing) * 8)}.hx\:pr-2{padding-right:calc(var(--hx-spacing) * 2)}.hx\:pr-4{padding-right:calc(var(--hx-spacing) * 4)}.hx\:pr-\[calc\(env\(safe-area-inset-right\)-1\.5rem\)\]{padding-right:calc(env(safe-area-inset-right) - 1.5rem)}.hx\:pr-\[max\(env\(safe-area-inset-left\)\,1\.5rem\)\]{padding-right:max(env(safe-area-inset-left),1.5rem)}.hx\:pr-\[max\(env\(safe-area-inset-right\)\,1\.5rem\)\]{padding-right:max(env(safe-area-inset-right),1.5rem)}.hx\:pb-8{padding-bottom:calc(var(--hx-spacing) * 8)}.hx\:pb-\[env\(safe-area-inset-bottom\)\]{padding-bottom:env(safe-area-inset-bottom)}.hx\:pb-px{padding-bottom:1px}.hx\:pl-6{padding-left:calc(var(--hx-spacing) * 6)}.hx\:pl-\[max\(env\(safe-area-inset-left\)\,1\.5rem\)\]{padding-left:max(env(safe-area-inset-left),1.5rem)}.hx\:text-center{text-align:center}.hx\:text-left{text-align:left}.hx\:align-\[-2\.5px\]{vertical-align:-2.5px}.hx\:align-baseline{vertical-align:baseline}.hx\:align-middle{vertical-align:middle}.hx\:align-text-bottom{vertical-align:text-bottom}.hx\:font-mono{font-family:var(--hx-font-mono)}.hx\:text-2xl{font-size:var(--hx-text-2xl);line-height:var(--tw-leading,var(--hx-text-2xl--line-height))}.hx\:text-4xl{font-size:var(--hx-text-4xl);line-height:var(--tw-leading,var(--hx-text-4xl--line-height))}.hx\:text-base{font-size:var(--hx-text-base);line-height:var(--tw-leading,var(--hx-text-base--line-height))}.hx\:text-lg{font-size:var(--hx-text-lg);line-height:var(--tw-leading,var(--hx-text-lg--line-height))}.hx\:text-sm{font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height))}.hx\:text-xl{font-size:var(--hx-text-xl);line-height:var(--tw-leading,var(--hx-text-xl--line-height))}.hx\:text-xs{font-size:var(--hx-text-xs);line-height:var(--tw-leading,var(--hx-text-xs--line-height))}.hx\:text-\[\.65rem\]{font-size:.65rem}.hx\:text-\[10px\]{font-size:10px}.hx\:leading-5{--tw-leading:calc(var(--hx-spacing) * 5);line-height:calc(var(--hx-spacing) * 5)}.hx\:leading-6{--tw-leading:calc(var(--hx-spacing) * 6);line-height:calc(var(--hx-spacing) * 6)}.hx\:leading-7{--tw-leading:calc(var(--hx-spacing) * 7);line-height:calc(var(--hx-spacing) * 7)}.hx\:leading-none{--tw-leading:1;line-height:1}.hx\:leading-tight{--tw-leading:var(--hx-leading-tight);line-height:var(--hx-leading-tight)}.hx\:font-bold{--tw-font-weight:var(--hx-font-weight-bold);font-weight:var(--hx-font-weight-bold)}.hx\:font-extrabold{--tw-font-weight:var(--hx-font-weight-extrabold);font-weight:var(--hx-font-weight-extrabold)}.hx\:font-medium{--tw-font-weight:var(--hx-font-weight-medium);font-weight:var(--hx-font-weight-medium)}.hx\:font-normal{--tw-font-weight:var(--hx-font-weight-normal);font-weight:var(--hx-font-weight-normal)}.hx\:font-semibold{--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold)}.hx\:tracking-tight{--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight)}.hx\:tracking-tighter{--tw-tracking:var(--hx-tracking-tighter);letter-spacing:var(--hx-tracking-tighter)}.hx\:break-words,.hx\:wrap-break-word{overflow-wrap:break-word}.hx\:text-ellipsis{text-overflow:ellipsis}.hx\:whitespace-nowrap{white-space:nowrap}.hx\:text-\[color\:hsl\(var\(--primary-hue\)\,100\%\,50\%\)\]{color:hsl(var(--primary-hue),100%,50%)}.hx\:text-amber-900{color:var(--hx-color-amber-900)}.hx\:text-blue-900{color:var(--hx-color-blue-900)}.hx\:text-current{color:currentColor}.hx\:text-gray-100{color:var(--hx-color-gray-100)}.hx\:text-gray-500{color:var(--hx-color-gray-500)}.hx\:text-gray-600{color:var(--hx-color-gray-600)}.hx\:text-gray-700{color:var(--hx-color-gray-700)}.hx\:text-gray-800{color:var(--hx-color-gray-800)}.hx\:text-gray-900{color:var(--hx-color-gray-900)}.hx\:text-green-900{color:var(--hx-color-green-900)}.hx\:text-indigo-900{color:var(--hx-color-indigo-900)}.hx\:text-orange-800{color:var(--hx-color-orange-800)}.hx\:text-primary-800{color:var(--hx-color-primary-800)}.hx\:text-purple-900{color:var(--hx-color-purple-900)}.hx\:text-red-900{color:var(--hx-color-red-900)}.hx\:text-slate-50{color:var(--hx-color-slate-50)}.hx\:text-slate-900{color:var(--hx-color-slate-900)}.hx\:text-transparent{color:#0000}.hx\:text-white{color:var(--hx-color-white)}.hx\:text-yellow-900{color:var(--hx-color-yellow-900)}.hx\:capitalize{text-transform:capitalize}.hx\:tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.hx\:no-underline{text-decoration-line:none}.hx\:underline{text-decoration-line:underline}.hx\:decoration-from-font{text-decoration-thickness:from-font}.hx\:underline-offset-2{text-underline-offset:2px}.hx\:opacity-0{opacity:0}.hx\:opacity-50{opacity:.5}.hx\:opacity-80{opacity:.8}.hx\:shadow-\[0_-12px_16px_\#fff\]{--tw-shadow:0 -12px 16px var(--tw-shadow-color,#fff);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hx\:shadow-\[0_-12px_16px_white\]{--tw-shadow:0 -12px 16px var(--tw-shadow-color,white);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hx\:shadow-\[0_2px_4px_rgba\(0\,0\,0\,\.02\)\,0_1px_0_rgba\(0\,0\,0\,\.06\)\]{--tw-shadow:0 2px 4px var(--tw-shadow-color,#00000005), 0 1px 0 var(--tw-shadow-color,#0000000f);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hx\: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)}.hx\:shadow-sm{--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)}.hx\:shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px 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)}.hx\:shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hx\:hextra-focus-visible:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--hx-color-primary-200);--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-offset-color:var(--hx-color-primary-300);--tw-outline-style:none;outline-style:none}.hx\:hextra-focus-visible:where(.dark,.dark *):focus-visible{--tw-ring-color:var(--hx-color-primary-800);--tw-ring-offset-color:var(--hx-color-primary-700)}.hx\:hextra-focus-visible-inset:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--hx-color-primary-200);--tw-ring-offset-width:0px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;--tw-ring-inset:inset;outline-style:none}.hx\:hextra-focus-visible-inset:where(.dark,.dark *):focus-visible{--tw-ring-color:var(--hx-color-primary-800)}.hx\:shadow-gray-100{--tw-shadow-color:var(--hx-color-gray-100)}@supports(color:color-mix(in lab,red,red)){.hx\:shadow-gray-100{--tw-shadow-color:color-mix(in oklab, var(--hx-color-gray-100) var(--tw-shadow-alpha), transparent)}}.hx\: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,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:duration-75{--tw-duration:75ms;transition-duration:75ms}.hx\:duration-150{--tw-duration:.15s;transition-duration:.15s}.hx\:duration-200{--tw-duration:.2s;transition-duration:.2s}.hx\:ease-in{--tw-ease:var(--hx-ease-in);transition-timing-function:var(--hx-ease-in)}.hx\:ease-in-out{--tw-ease:var(--hx-ease-in-out);transition-timing-function:var(--hx-ease-in-out)}.hx\:outline-none{--tw-outline-style:none;outline-style:none}.hx\:select-none{-webkit-user-select:none;user-select:none}@media(hover:hover){.hx\:group-hover\:text-gray-900:is(:where(.hx\:group):hover *){color:var(--hx-color-gray-900)}.hx\:group-hover\:underline:is(:where(.hx\:group):hover *){text-decoration-line:underline}.hx\:group-hover\/code\:opacity-100:is(:where(.hx\:group\/code):hover *){opacity:1}}.hx\:group-data-\[active\=true\]\:text-primary-800:is(:where(.hx\:group)[data-active=true] *){color:var(--hx-color-primary-800)}.hx\:group-data-\[theme\=dark\]\:hidden:is(:where(.hx\:group)[data-theme=dark] *),.hx\:group-data-\[theme\=light\]\:hidden:is(:where(.hx\:group)[data-theme=light] *),.hx\:group-data-\[theme\=system\]\:hidden:is(:where(.hx\:group)[data-theme=system] *){display:none}.hx\:group-\[\.copied\]\/copybtn\:block:is(:where(.hx\:group\/copybtn).copied *){display:block}.hx\:group-\[\.copied\]\/copybtn\:hidden:is(:where(.hx\:group\/copybtn).copied *){display:none}.hx\:placeholder\:text-gray-500::placeholder{color:var(--hx-color-gray-500)}.hx\:before\:pointer-events-none:before{content:var(--tw-content);pointer-events:none}.hx\:before\:absolute:before{content:var(--tw-content);position:absolute}.hx\:before\:inset-0:before{content:var(--tw-content);inset:calc(var(--hx-spacing) * 0)}.hx\:before\:inset-y-1:before{content:var(--tw-content);inset-block:calc(var(--hx-spacing) * 1)}.hx\:before\:mr-1:before{content:var(--tw-content);margin-right:calc(var(--hx-spacing) * 1)}.hx\:before\:inline-block:before{content:var(--tw-content);display:inline-block}.hx\:before\:w-px:before{content:var(--tw-content);width:1px}.hx\:before\:bg-gray-200:before{content:var(--tw-content);background-color:var(--hx-color-gray-200)}.hx\:before\:opacity-25:before{content:var(--tw-content);opacity:.25}.hx\:before\:transition-transform:before{content:var(--tw-content);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.hx\:before\:content-\[\"\"\]:before{--tw-content:"";content:var(--tw-content)}.hx\:before\:content-\[\'\#\'\]:before{--tw-content:"#";content:var(--tw-content)}.hx\:before\:content-\[\'\'\]:before{--tw-content:"";content:var(--tw-content)}.hx\:before\:content-\[\\\"\\\"\]:before{--tw-content:\"\";content:var(--tw-content)}.hx\:group-open\:before\:rotate-90:is(:where(.hx\:group):is([open],:popover-open,:open) *):before{content:var(--tw-content);rotate:90deg}.hx\:first\:mt-0:first-child{margin-top:calc(var(--hx-spacing) * 0)}.hx\:last-of-type\:mb-0:last-of-type{margin-bottom:calc(var(--hx-spacing) * 0)}@media(hover:hover){.hx\:hover\:border-gray-200:hover{border-color:var(--hx-color-gray-200)}.hx\:hover\:border-gray-300:hover{border-color:var(--hx-color-gray-300)}.hx\:hover\:border-gray-400:hover{border-color:var(--hx-color-gray-400)}.hx\:hover\:border-gray-900:hover{border-color:var(--hx-color-gray-900)}.hx\:hover\:bg-gray-100:hover{background-color:var(--hx-color-gray-100)}.hx\:hover\:bg-gray-800\/5:hover{background-color:var(--hx-color-gray-800)}@supports(color:color-mix(in lab,red,red)){.hx\:hover\:bg-gray-800\/5:hover{background-color:color-mix(in oklab,var(--hx-color-gray-800) 5%,transparent)}}.hx\:hover\:bg-primary-700:hover{background-color:var(--hx-color-primary-700)}.hx\:hover\:bg-slate-50:hover{background-color:var(--hx-color-slate-50)}.hx\:hover\:text-black:hover{color:var(--hx-color-black)}.hx\:hover\:text-gray-700:hover{color:var(--hx-color-gray-700)}.hx\:hover\:text-gray-800:hover{color:var(--hx-color-gray-800)}.hx\:hover\:text-gray-900:hover{color:var(--hx-color-gray-900)}.hx\:hover\:text-primary-600:hover{color:var(--hx-color-primary-600)}.hx\:hover\:opacity-60:hover{opacity:.6}.hx\:hover\:opacity-75:hover{opacity:.75}.hx\:hover\:shadow-lg:hover{--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)}.hx\:hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px 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)}.hx\:hover\:shadow-gray-100:hover{--tw-shadow-color:var(--hx-color-gray-100)}@supports(color:color-mix(in lab,red,red)){.hx\:hover\:shadow-gray-100:hover{--tw-shadow-color:color-mix(in oklab, var(--hx-color-gray-100) var(--tw-shadow-alpha), transparent)}}}.hx\:focus-visible\:not-sr-only:focus-visible{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.hx\:focus-visible\:fixed:focus-visible{position:fixed}.hx\:focus-visible\:top-2:focus-visible{top:calc(var(--hx-spacing) * 2)}.hx\:focus-visible\:left-2:focus-visible{left:calc(var(--hx-spacing) * 2)}.hx\:focus-visible\:z-50:focus-visible{z-index:50}.hx\:focus-visible\:rounded-md:focus-visible{border-radius:var(--hx-radius-md)}.hx\:focus-visible\:bg-primary-500:focus-visible{background-color:var(--hx-color-primary-500)}.hx\:focus-visible\:bg-white:focus-visible{background-color:var(--hx-color-white)}.hx\:focus-visible\:px-4:focus-visible{padding-inline:calc(var(--hx-spacing) * 4)}.hx\:focus-visible\:py-2:focus-visible{padding-block:calc(var(--hx-spacing) * 2)}.hx\:focus-visible\:text-sm:focus-visible{font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height))}.hx\:focus-visible\:font-medium:focus-visible{--tw-font-weight:var(--hx-font-weight-medium);font-weight:var(--hx-font-weight-medium)}.hx\:focus-visible\:text-white:focus-visible{color:var(--hx-color-white)}.hx\:active\:bg-gray-400\/20:active{background-color:var(--hx-color-gray-400)}@supports(color:color-mix(in lab,red,red)){.hx\:active\:bg-gray-400\/20:active{background-color:color-mix(in oklab,var(--hx-color-gray-400) 20%,transparent)}}.hx\:active\:opacity-50:active{opacity:.5}.hx\:active\:shadow-sm:active{--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)}.hx\:active\:shadow-gray-200:active{--tw-shadow-color:var(--hx-color-gray-200)}@supports(color:color-mix(in lab,red,red)){.hx\:active\:shadow-gray-200:active{--tw-shadow-color:color-mix(in oklab, var(--hx-color-gray-200) var(--tw-shadow-alpha), transparent)}}.hx\:data-\[state\=closed\]\:hidden[data-state=closed],.hx\:data-\[state\=open\]\:hidden[data-state=open]{display:none}.hx\:data-\[state\=selected\]\:block[data-state=selected]{display:block}.hx\:data-\[state\=selected\]\:border-primary-500[data-state=selected]{border-color:var(--hx-color-primary-500)}.hx\:data-\[state\=selected\]\:text-primary-600[data-state=selected]{color:var(--hx-color-primary-600)}@media(prefers-contrast:more){.hx\:contrast-more\:border{border-style:var(--tw-border-style);border-width:1px}.hx\:contrast-more\:border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.hx\:contrast-more\:border-current{border-color:initial}.hx\:contrast-more\:border-gray-800{border-color:var(--hx-color-gray-800)}.hx\:contrast-more\:border-gray-900{border-color:var(--hx-color-gray-900)}.hx\:contrast-more\:border-neutral-400{border-color:var(--hx-color-neutral-400)}.hx\:contrast-more\:border-primary-500{border-color:var(--hx-color-primary-500)}.hx\:contrast-more\:border-transparent{border-color:#0000}.hx\:contrast-more\:font-bold{--tw-font-weight:var(--hx-font-weight-bold);font-weight:var(--hx-font-weight-bold)}.hx\:contrast-more\:text-current{color:currentColor}.hx\:contrast-more\:text-gray-700{color:var(--hx-color-gray-700)}.hx\:contrast-more\:text-gray-800{color:var(--hx-color-gray-800)}.hx\:contrast-more\:text-gray-900{color:var(--hx-color-gray-900)}.hx\:contrast-more\:underline{text-decoration-line:underline}.hx\:contrast-more\:shadow-\[0_0_0_1px_\#000\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,#000);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hx\:contrast-more\:shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.hx\:contrast-more\:hover\:border-gray-900:hover{border-color:var(--hx-color-gray-900)}}}@media not all and (min-width:80rem){.hx\:max-xl\:hidden{display:none}}@media not all and (min-width:64rem){.hx\:max-lg\:min-h-\[340px\]{min-height:340px}}@media not all and (min-width:48rem){.hx\:max-md\:sticky{position:sticky}.hx\:max-md\:hidden{display:none}.hx\:max-md\:min-h-\[340px\]{min-height:340px}.hx\:max-md\:\[transform\:translate3d\(0\,-100\%\,0\)\]{transform:translateY(-100%)}.hx\:max-md\:\[transform\:translate3d\(0\,0\,0\)\]{transform:translate(0)}}@media not all and (min-width:40rem){.hx\:max-sm\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}}@media(min-width:40rem){.hx\:sm\:right-0{right:calc(var(--hx-spacing) * 0)}.hx\:sm\:left-auto{left:auto}.hx\:sm\:block{display:block}.hx\:sm\:flex{display:flex}.hx\:sm\:w-\[110\%\]{width:110%}.hx\:sm\:flex-row{flex-direction:row}.hx\:sm\:items-center{align-items:center}.hx\:sm\:items-start{align-items:flex-start}.hx\:sm\:justify-between{justify-content:space-between}.hx\:sm\:text-xl{font-size:var(--hx-text-xl);line-height:var(--tw-leading,var(--hx-text-xl--line-height))}@media not all and (min-width:64rem){.hx\:sm\:max-lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@media(min-width:48rem){.hx\:md\:sticky{position:sticky}.hx\:md\:top-16{top:calc(var(--hx-spacing) * 16)}.hx\:md\:mr-0{margin-right:calc(var(--hx-spacing) * 0)}.hx\:md\:hidden{display:none}.hx\:md\:inline-block{display:inline-block}.hx\:md\:inline-flex{display:inline-flex}.hx\:md\:aspect-\[1\.1\/1\]{aspect-ratio:1.1}.hx\:md\:h-\[calc\(100vh-var\(--navbar-height\)-var\(--menu-height\)\)\]{height:calc(100vh - var(--navbar-height) - var(--menu-height))}.hx\:md\:max-h-\[min\(calc\(100vh-5rem-env\(safe-area-inset-bottom\)\)\,400px\)\]{max-height:min(calc(100vh - 5rem - env(safe-area-inset-bottom)),400px)}.hx\:md\:w-64{width:calc(var(--hx-spacing) * 64)}.hx\:md\:shrink-0{flex-shrink:0}.hx\:md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.hx\:md\:justify-start{justify-content:flex-start}.hx\:md\:self-start{align-self:flex-start}.hx\:md\:overflow-auto{overflow:auto}.hx\:md\:px-12{padding-inline:calc(var(--hx-spacing) * 12)}.hx\:md\:pt-12{padding-top:calc(var(--hx-spacing) * 12)}.hx\:md\:text-3xl{font-size:var(--hx-text-3xl);line-height:var(--tw-leading,var(--hx-text-3xl--line-height))}.hx\:md\:text-5xl{font-size:var(--hx-text-5xl);line-height:var(--tw-leading,var(--hx-text-5xl--line-height))}.hx\:md\:text-lg{font-size:var(--hx-text-lg);line-height:var(--tw-leading,var(--hx-text-lg--line-height))}.hx\:md\:text-sm{font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height))}}@media(min-width:64rem){.hx\:lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(min-width:80rem){.hx\:xl\:block{display:block}.hx\:xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.hx\:ltr\:right-1\.5:where(:dir(ltr),[dir=ltr],[dir=ltr] *){right:calc(var(--hx-spacing) * 1.5)}.hx\:ltr\:right-2:where(:dir(ltr),[dir=ltr],[dir=ltr] *){right:calc(var(--hx-spacing) * 2)}.hx\:ltr\:right-3:where(:dir(ltr),[dir=ltr],[dir=ltr] *){right:calc(var(--hx-spacing) * 3)}.hx\:ltr\:-mr-4:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-right:calc(var(--hx-spacing) * -4)}.hx\:ltr\:mr-auto:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-right:auto}.hx\:ltr\:ml-1:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:calc(var(--hx-spacing) * 1)}.hx\:ltr\:ml-3:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:calc(var(--hx-spacing) * 3)}.hx\:ltr\:ml-auto:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:auto}.hx\:ltr\:rotate-180:where(:dir(ltr),[dir=ltr],[dir=ltr] *){rotate:180deg}.hx\:ltr\:rounded-l-lg:where(:dir(ltr),[dir=ltr],[dir=ltr] *){border-top-left-radius:var(--hx-radius-lg);border-bottom-left-radius:var(--hx-radius-lg)}.hx\:ltr\:rounded-r-lg:where(:dir(ltr),[dir=ltr],[dir=ltr] *){border-top-right-radius:var(--hx-radius-lg);border-bottom-right-radius:var(--hx-radius-lg)}.hx\:ltr\:border-l:where(:dir(ltr),[dir=ltr],[dir=ltr] *){border-left-style:var(--tw-border-style);border-left-width:1px}.hx\:ltr\:pr-0:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-right:calc(var(--hx-spacing) * 0)}.hx\:ltr\:pr-2:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-right:calc(var(--hx-spacing) * 2)}.hx\:ltr\:pr-4:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-right:calc(var(--hx-spacing) * 4)}.hx\:ltr\:pr-8:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-right:calc(var(--hx-spacing) * 8)}.hx\:ltr\:pr-9:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-right:calc(var(--hx-spacing) * 9)}.hx\:ltr\:pl-3:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 3)}.hx\:ltr\:pl-4:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 4)}.hx\:ltr\:pl-5:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 5)}.hx\:ltr\:pl-6:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 6)}.hx\:ltr\:pl-8:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 8)}.hx\:ltr\:pl-12:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 12)}.hx\:ltr\:pl-16:where(:dir(ltr),[dir=ltr],[dir=ltr] *){padding-left:calc(var(--hx-spacing) * 16)}.hx\:ltr\:text-right:where(:dir(ltr),[dir=ltr],[dir=ltr] *){text-align:right}.hx\:ltr\:before\:left-0:where(:dir(ltr),[dir=ltr],[dir=ltr] *):before{content:var(--tw-content);left:calc(var(--hx-spacing) * 0)}@media(min-width:48rem){.hx\:ltr\:md\:left-auto:where(:dir(ltr),[dir=ltr],[dir=ltr] *){left:auto}}.hx\:rtl\:left-1\.5:where(:dir(rtl),[dir=rtl],[dir=rtl] *){left:calc(var(--hx-spacing) * 1.5)}.hx\:rtl\:left-2:where(:dir(rtl),[dir=rtl],[dir=rtl] *){left:calc(var(--hx-spacing) * 2)}.hx\:rtl\:left-3:where(:dir(rtl),[dir=rtl],[dir=rtl] *){left:calc(var(--hx-spacing) * 3)}.hx\:rtl\:mr-1:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:calc(var(--hx-spacing) * 1)}.hx\:rtl\:mr-3:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:calc(var(--hx-spacing) * 3)}.hx\:rtl\:mr-auto:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:auto}.hx\:rtl\:-ml-4:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-left:calc(var(--hx-spacing) * -4)}.hx\:rtl\:ml-auto:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-left:auto}.hx\:rtl\:-rotate-180:where(:dir(rtl),[dir=rtl],[dir=rtl] *){rotate:-180deg}.hx\:rtl\:rotate-270:where(:dir(rtl),[dir=rtl],[dir=rtl] *){rotate:270deg}.hx\:rtl\:rounded-l-lg:where(:dir(rtl),[dir=rtl],[dir=rtl] *){border-top-left-radius:var(--hx-radius-lg);border-bottom-left-radius:var(--hx-radius-lg)}.hx\:rtl\:rounded-r-lg:where(:dir(rtl),[dir=rtl],[dir=rtl] *){border-top-right-radius:var(--hx-radius-lg);border-bottom-right-radius:var(--hx-radius-lg)}.hx\:rtl\:border-r:where(:dir(rtl),[dir=rtl],[dir=rtl] *){border-right-style:var(--tw-border-style);border-right-width:1px}.hx\:rtl\:pr-3:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 3)}.hx\:rtl\:pr-4:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 4)}.hx\:rtl\:pr-5:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 5)}.hx\:rtl\:pr-6:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 6)}.hx\:rtl\:pr-8:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 8)}.hx\:rtl\:pr-12:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 12)}.hx\:rtl\:pr-16:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-right:calc(var(--hx-spacing) * 16)}.hx\:rtl\:pl-0:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-left:calc(var(--hx-spacing) * 0)}.hx\:rtl\:pl-2:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-left:calc(var(--hx-spacing) * 2)}.hx\:rtl\:pl-4:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-left:calc(var(--hx-spacing) * 4)}.hx\:rtl\:pl-8:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-left:calc(var(--hx-spacing) * 8)}.hx\:rtl\:pl-9:where(:dir(rtl),[dir=rtl],[dir=rtl] *){padding-left:calc(var(--hx-spacing) * 9)}.hx\:rtl\:text-left:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:left}.hx\:rtl\:before\:right-0:where(:dir(rtl),[dir=rtl],[dir=rtl] *):before{content:var(--tw-content);right:calc(var(--hx-spacing) * 0)}.hx\:rtl\:before\:rotate-180:where(:dir(rtl),[dir=rtl],[dir=rtl] *):before{content:var(--tw-content);rotate:180deg}@media(min-width:48rem){.hx\:rtl\:md\:right-auto:where(:dir(rtl),[dir=rtl],[dir=rtl] *){right:auto}}.hx\:dark\:block:where(.dark,.dark *){display:block}.hx\:dark\:hidden:where(.dark,.dark *){display:none}.hx\:dark\:border-amber-200\/30:where(.dark,.dark *){border-color:var(--hx-color-amber-200)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:border-amber-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-amber-200) 30%,transparent)}}.hx\:dark\:border-blue-200\/30:where(.dark,.dark *){border-color:var(--hx-color-blue-200)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:border-blue-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-blue-200) 30%,transparent)}}.hx\:dark\:border-gray-100\/20:where(.dark,.dark *){border-color:var(--hx-color-gray-100)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:border-gray-100\/20:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-gray-100) 20%,transparent)}}.hx\:dark\:border-gray-400:where(.dark,.dark *){border-color:var(--hx-color-gray-400)}.hx\:dark\:border-green-200\/30:where(.dark,.dark *){border-color:var(--hx-color-green-200)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:border-green-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-green-200) 30%,transparent)}}.hx\:dark\:border-indigo-200\/30:where(.dark,.dark *){border-color:var(--hx-color-indigo-200)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:border-indigo-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-indigo-200) 30%,transparent)}}.hx\:dark\:border-neutral-700:where(.dark,.dark *){border-color:var(--hx-color-neutral-700)}.hx\:dark\:border-neutral-800:where(.dark,.dark *){border-color:var(--hx-color-neutral-800)}.hx\:dark\:border-orange-400\/30:where(.dark,.dark *){border-color:var(--hx-color-orange-400)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:border-orange-400\/30:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-orange-400) 30%,transparent)}}.hx\:dark\:border-purple-200\/30:where(.dark,.dark *){border-color:var(--hx-color-purple-200)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:border-purple-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-purple-200) 30%,transparent)}}.hx\:dark\:border-red-200\/30:where(.dark,.dark *){border-color:var(--hx-color-red-200)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:border-red-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-red-200) 30%,transparent)}}.hx\:dark\:border-white\/10:where(.dark,.dark *){border-color:var(--hx-color-white)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:border-white\/10:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-white) 10%,transparent)}}.hx\:dark\:border-yellow-200\/30:where(.dark,.dark *){border-color:var(--hx-color-yellow-200)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:border-yellow-200\/30:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-yellow-200) 30%,transparent)}}.hx\:dark\:bg-amber-900\/30:where(.dark,.dark *){background-color:var(--hx-color-amber-900)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-amber-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-amber-900) 30%,transparent)}}.hx\:dark\:bg-blue-900\/30:where(.dark,.dark *){background-color:var(--hx-color-blue-900)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-blue-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-blue-900) 30%,transparent)}}.hx\:dark\:bg-dark:where(.dark,.dark *),.hx\:dark\:bg-dark\/50:where(.dark,.dark *){background-color:var(--hx-color-dark)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-dark\/50:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-dark) 50%,transparent)}}.hx\:dark\:bg-gray-50\/10:where(.dark,.dark *){background-color:var(--hx-color-gray-50)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-gray-50\/10:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-gray-50) 10%,transparent)}}.hx\:dark\:bg-green-900\/30:where(.dark,.dark *){background-color:var(--hx-color-green-900)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-green-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-green-900) 30%,transparent)}}.hx\:dark\:bg-indigo-900\/30:where(.dark,.dark *){background-color:var(--hx-color-indigo-900)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-indigo-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-indigo-900) 30%,transparent)}}.hx\:dark\:bg-neutral-700:where(.dark,.dark *){background-color:var(--hx-color-neutral-700)}.hx\:dark\:bg-neutral-800:where(.dark,.dark *){background-color:var(--hx-color-neutral-800)}.hx\:dark\:bg-neutral-900:where(.dark,.dark *){background-color:var(--hx-color-neutral-900)}.hx\:dark\:bg-orange-400\/20:where(.dark,.dark *){background-color:var(--hx-color-orange-400)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-orange-400\/20:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-orange-400) 20%,transparent)}}.hx\:dark\:bg-primary-300\/10:where(.dark,.dark *){background-color:var(--hx-color-primary-300)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-primary-300\/10:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-primary-300) 10%,transparent)}}.hx\:dark\:bg-primary-400\/10:where(.dark,.dark *){background-color:var(--hx-color-primary-400)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-primary-400\/10:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-primary-400) 10%,transparent)}}.hx\:dark\:bg-primary-600:where(.dark,.dark *){background-color:var(--hx-color-primary-600)}.hx\:dark\:bg-purple-900\/30:where(.dark,.dark *){background-color:var(--hx-color-purple-900)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-purple-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-purple-900) 30%,transparent)}}.hx\:dark\:bg-red-900\/30:where(.dark,.dark *){background-color:var(--hx-color-red-900)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-red-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-red-900) 30%,transparent)}}.hx\:dark\:bg-yellow-700\/30:where(.dark,.dark *){background-color:var(--hx-color-yellow-700)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:bg-yellow-700\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-yellow-700) 30%,transparent)}}.hx\:dark\:from-gray-100:where(.dark,.dark *){--tw-gradient-from:var(--hx-color-gray-100);--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))}.hx\:dark\:to-gray-400:where(.dark,.dark *){--tw-gradient-to:var(--hx-color-gray-400);--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))}.hx\:dark\:text-amber-200:where(.dark,.dark *){color:var(--hx-color-amber-200)}.hx\:dark\:text-blue-200:where(.dark,.dark *){color:var(--hx-color-blue-200)}.hx\:dark\:text-gray-50:where(.dark,.dark *){color:var(--hx-color-gray-50)}.hx\:dark\:text-gray-100:where(.dark,.dark *){color:var(--hx-color-gray-100)}.hx\:dark\:text-gray-200:where(.dark,.dark *){color:var(--hx-color-gray-200)}.hx\:dark\:text-gray-300:where(.dark,.dark *){color:var(--hx-color-gray-300)}.hx\:dark\:text-gray-400:where(.dark,.dark *){color:var(--hx-color-gray-400)}.hx\:dark\:text-green-200:where(.dark,.dark *){color:var(--hx-color-green-200)}.hx\:dark\:text-indigo-200:where(.dark,.dark *){color:var(--hx-color-indigo-200)}.hx\:dark\:text-neutral-200:where(.dark,.dark *){color:var(--hx-color-neutral-200)}.hx\:dark\:text-neutral-400:where(.dark,.dark *){color:var(--hx-color-neutral-400)}.hx\:dark\:text-orange-300:where(.dark,.dark *){color:var(--hx-color-orange-300)}.hx\:dark\:text-primary-600:where(.dark,.dark *){color:var(--hx-color-primary-600)}.hx\:dark\:text-purple-200:where(.dark,.dark *){color:var(--hx-color-purple-200)}.hx\:dark\:text-red-200:where(.dark,.dark *){color:var(--hx-color-red-200)}.hx\:dark\:text-slate-100:where(.dark,.dark *){color:var(--hx-color-slate-100)}.hx\:dark\:text-white:where(.dark,.dark *){color:var(--hx-color-white)}.hx\:dark\:text-yellow-200:where(.dark,.dark *){color:var(--hx-color-yellow-200)}.hx\:dark\:opacity-80:where(.dark,.dark *){opacity:.8}.hx\:dark\:shadow-\[0_-1px_0_rgba\(255\,255\,255\,\.1\)_inset\]:where(.dark,.dark *){--tw-shadow:0 -1px 0 var(--tw-shadow-color,#ffffff1a) inset;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hx\:dark\:shadow-\[0_-12px_16px_\#111\]:where(.dark,.dark *){--tw-shadow:0 -12px 16px var(--tw-shadow-color,#111);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hx\:dark\:shadow-none:where(.dark,.dark *){--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.hx\:dark\:group-hover\:text-gray-50:where(.dark,.dark *):is(:where(.hx\:group):hover *){color:var(--hx-color-gray-50)}}.hx\:group-data-\[active\=true\]\:dark\:text-primary-600:is(:where(.hx\:group)[data-active=true] *):where(.dark,.dark *){color:var(--hx-color-primary-600)}.hx\:dark\:placeholder\:text-gray-400:where(.dark,.dark *)::placeholder{color:var(--hx-color-gray-400)}.hx\:dark\:before\:bg-neutral-800:where(.dark,.dark *):before{content:var(--tw-content);background-color:var(--hx-color-neutral-800)}.hx\:dark\:before\:invert:where(.dark,.dark *):before{content:var(--tw-content);--tw-invert:invert(100%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}@media(hover:hover){.hx\:dark\:hover\:border-gray-100:where(.dark,.dark *):hover{border-color:var(--hx-color-gray-100)}.hx\:dark\:hover\:border-gray-600:where(.dark,.dark *):hover{border-color:var(--hx-color-gray-600)}.hx\:dark\:hover\:border-neutral-500:where(.dark,.dark *):hover{border-color:var(--hx-color-neutral-500)}.hx\:dark\:hover\:border-neutral-700:where(.dark,.dark *):hover{border-color:var(--hx-color-neutral-700)}.hx\:dark\:hover\:border-neutral-800:where(.dark,.dark *):hover{border-color:var(--hx-color-neutral-800)}.hx\:dark\:hover\:bg-gray-100\/5:where(.dark,.dark *):hover{background-color:var(--hx-color-gray-100)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:hover\:bg-gray-100\/5:where(.dark,.dark *):hover{background-color:color-mix(in oklab,var(--hx-color-gray-100) 5%,transparent)}}.hx\:dark\:hover\:bg-neutral-700:where(.dark,.dark *):hover{background-color:var(--hx-color-neutral-700)}.hx\:dark\:hover\:bg-neutral-800:where(.dark,.dark *):hover{background-color:var(--hx-color-neutral-800)}.hx\:dark\:hover\:bg-neutral-900:where(.dark,.dark *):hover{background-color:var(--hx-color-neutral-900)}.hx\:dark\:hover\:bg-primary-100\/5:where(.dark,.dark *):hover{background-color:var(--hx-color-primary-100)}@supports(color:color-mix(in lab,red,red)){.hx\:dark\:hover\:bg-primary-100\/5:where(.dark,.dark *):hover{background-color:color-mix(in oklab,var(--hx-color-primary-100) 5%,transparent)}}.hx\:dark\:hover\:bg-primary-700:where(.dark,.dark *):hover{background-color:var(--hx-color-primary-700)}.hx\:dark\:hover\:text-gray-50:where(.dark,.dark *):hover{color:var(--hx-color-gray-50)}.hx\:dark\:hover\:text-gray-100:where(.dark,.dark *):hover{color:var(--hx-color-gray-100)}.hx\:dark\:hover\:text-gray-200:where(.dark,.dark *):hover{color:var(--hx-color-gray-200)}.hx\:dark\:hover\:text-gray-300:where(.dark,.dark *):hover{color:var(--hx-color-gray-300)}.hx\:dark\:hover\:text-neutral-50:where(.dark,.dark *):hover{color:var(--hx-color-neutral-50)}.hx\:dark\:hover\:text-primary-400:where(.dark,.dark *):hover{color:var(--hx-color-primary-400)}.hx\:dark\:hover\:text-white:where(.dark,.dark *):hover{color:var(--hx-color-white)}.hx\:dark\:hover\:shadow-none:where(.dark,.dark *):hover{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.hx\:dark\:focus-visible\:bg-dark:where(.dark,.dark *):focus-visible{background-color:var(--hx-color-dark)}.hx\:data-\[state\=selected\]\:dark\:border-primary-500[data-state=selected]:where(.dark,.dark *){border-color:var(--hx-color-primary-500)}.hx\:data-\[state\=selected\]\:dark\:text-primary-600[data-state=selected]:where(.dark,.dark *){color:var(--hx-color-primary-600)}@media(prefers-contrast:more){.hx\:contrast-more\:dark\:border-current:where(.dark,.dark *){border-color:initial}.hx\:contrast-more\:dark\:border-gray-50:where(.dark,.dark *){border-color:var(--hx-color-gray-50)}.hx\:contrast-more\:dark\:border-neutral-400:where(.dark,.dark *){border-color:var(--hx-color-neutral-400)}.hx\:contrast-more\:dark\:border-primary-500:where(.dark,.dark *){border-color:var(--hx-color-primary-500)}.hx\:dark\:contrast-more\:border-neutral-400:where(.dark,.dark *){border-color:var(--hx-color-neutral-400)}.hx\:contrast-more\:dark\:text-current:where(.dark,.dark *){color:currentColor}.hx\:contrast-more\:dark\:text-gray-50:where(.dark,.dark *){color:var(--hx-color-gray-50)}.hx\:contrast-more\:dark\:text-gray-100:where(.dark,.dark *){color:var(--hx-color-gray-100)}.hx\:contrast-more\:dark\:text-gray-300:where(.dark,.dark *){color:var(--hx-color-gray-300)}.hx\:contrast-more\:dark\:shadow-\[0_0_0_1px_\#fff\]:where(.dark,.dark *){--tw-shadow:0 0 0 1px var(--tw-shadow-color,#fff);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hx\:contrast-more\:dark\:shadow-none:where(.dark,.dark *){--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.hx\:contrast-more\:dark\:hover\:border-gray-50:where(.dark,.dark *):hover{border-color:var(--hx-color-gray-50)}}}@media print{.hx\:print\:\[display\:none\],.hx\:print\:hidden{display:none}.hx\:print\:bg-transparent{background-color:#0000}}}html{font-size:var(--hx-text-base);line-height:var(--tw-leading,var(--hx-text-base--line-height));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{background-color:var(--hx-color-white);width:100%}body:where(.dark,.dark *){background-color:var(--hx-color-dark);color:var(--hx-color-gray-100)}:root{--primary-hue:212deg;--primary-saturation:100%;--primary-lightness:50%;--navbar-height:4rem;--hextra-banner-height:2rem;--menu-height:3.75rem}.dark{--primary-hue:204deg;--primary-saturation:100%;--primary-lightness:50%}@media(prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.content :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 2);font-size:var(--hx-text-4xl);line-height:var(--tw-leading,var(--hx-text-4xl--line-height));--tw-font-weight:var(--hx-font-weight-bold);font-weight:var(--hx-font-weight-bold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}.content :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 10);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--hx-color-neutral-200)}@supports(color:color-mix(in lab,red,red)){.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:color-mix(in oklab,var(--hx-color-neutral-200) 70%,transparent)}}.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){padding-bottom:calc(var(--hx-spacing) * 1);font-size:var(--hx-text-3xl);line-height:var(--tw-leading,var(--hx-text-3xl--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}@media(prefers-contrast:more){.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--hx-color-neutral-400)}}.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-primary-100)}@supports(color:color-mix(in lab,red,red)){.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-primary-100) 10%,transparent)}}.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}@media(prefers-contrast:more){.content :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-neutral-400)}}.content :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 8);font-size:var(--hx-text-2xl);line-height:var(--tw-leading,var(--hx-text-2xl--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}.content :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}.content :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 8);font-size:var(--hx-text-xl);line-height:var(--tw-leading,var(--hx-text-xl--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}.content :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}.content :where(h5):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 8);font-size:var(--hx-text-lg);line-height:var(--tw-leading,var(--hx-text-lg--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}.content :where(h5):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}.content :where(h6):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 8);font-size:var(--hx-text-base);line-height:var(--tw-leading,var(--hx-text-base--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);--tw-tracking:var(--hx-tracking-tight);letter-spacing:var(--hx-tracking-tight);color:var(--hx-color-slate-900)}.content :where(h6):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){color:var(--hx-color-slate-100)}.content :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 6);--tw-leading:calc(var(--hx-spacing) * 7);line-height:calc(var(--hx-spacing) * 7)}.content :where(p):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--hx-color-primary-600);text-decoration-line:underline;text-decoration-thickness:from-font}.content :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 6);border-color:var(--hx-color-gray-300);color:var(--hx-color-gray-700);font-style:italic}.content :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(ltr),[dir=ltr],[dir=ltr] *){border-left-style:var(--tw-border-style);padding-left:calc(var(--hx-spacing) * 6);border-left-width:2px}.content :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(rtl),[dir=rtl],[dir=rtl] *){border-right-style:var(--tw-border-style);padding-right:calc(var(--hx-spacing) * 6);border-right-width:2px}.content :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-gray-700);color:var(--hx-color-gray-400)}.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){margin-bottom:calc(var(--hx-spacing) * 4);border-radius:var(--hx-radius-xl);background-color:var(--hx-color-primary-700);overflow-x:auto}@supports(color:color-mix(in lab,red,red)){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){background-color:color-mix(in oklab,var(--hx-color-primary-700) 5%,transparent)}}.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){padding-block:calc(var(--hx-spacing) * 4);--tw-font-weight:var(--hx-font-weight-medium);font-size:.9em;font-weight:var(--hx-font-weight-medium);-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}@media(prefers-contrast:more){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-primary-900)}@supports(color:color-mix(in lab,red,red)){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){border-color:color-mix(in oklab,var(--hx-color-primary-900) 20%,transparent)}}.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){--tw-contrast:contrast(150%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}}.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:var(--hx-color-primary-300)}@supports(color:color-mix(in lab,red,red)){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-primary-300) 10%,transparent)}}@media(prefers-contrast:more){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-primary-100)}@supports(color:color-mix(in lab,red,red)){.content :where(pre):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-primary-100) 40%,transparent)}}}.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)){border-radius:var(--hx-radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-black)}@supports(color:color-mix(in lab,red,red)){.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)){border-color:color-mix(in oklab,var(--hx-color-black) 4%,transparent)}}.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)){background-color:var(--hx-color-black)}@supports(color:color-mix(in lab,red,red)){.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)){background-color:color-mix(in oklab,var(--hx-color-black) 3%,transparent)}}.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)){padding-inline:.25em;padding-block:calc(var(--hx-spacing) * .5);overflow-wrap:break-word;font-size:.9em}.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-white)}@supports(color:color-mix(in lab,red,red)){.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-white) 10%,transparent)}}.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:var(--hx-color-white)}@supports(color:color-mix(in lab,red,red)){.content :where(code):not(:where(.hextra-code-block code,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-white) 10%,transparent)}}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)){margin-block:calc(var(--hx-spacing) * 6);border-collapse:collapse;width:100%;padding:calc(var(--hx-spacing) * 0);font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height));--tw-leading:calc(var(--hx-spacing) * 5);line-height:calc(var(--hx-spacing) * 5);display:block;overflow-x:auto}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) thead{background-color:var(--hx-color-gray-50)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) thead:where(.dark,.dark *){background-color:var(--hx-color-gray-600)}@supports(color:color-mix(in lab,red,red)){.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) thead:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-gray-600) 20%,transparent)}}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) tr{margin:calc(var(--hx-spacing) * 0);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--hx-color-gray-300);padding:calc(var(--hx-spacing) * 0)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) tr:where(.dark,.dark *){border-color:var(--hx-color-gray-600)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) th{margin:calc(var(--hx-spacing) * 0);border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-gray-300);padding:calc(var(--hx-spacing) * 2);--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) th:where(.dark,.dark *){border-color:var(--hx-color-gray-600)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) td{margin:calc(var(--hx-spacing) * 0);border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-gray-300);padding:calc(var(--hx-spacing) * 2)}.content :where(table):not(:where(.hextra-code-block table,[class~=not-prose],[class~=not-prose] *)) td:where(.dark,.dark *){border-color:var(--hx-color-gray-600)}.content :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 6);list-style-type:decimal}.content :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:calc(var(--hx-spacing) * 6)}.content :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:calc(var(--hx-spacing) * 6)}.content :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)) li{margin-block:calc(var(--hx-spacing) * 2)}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 6);list-style-type:disc}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:calc(var(--hx-spacing) * 6)}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)):where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:calc(var(--hx-spacing) * 6)}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)) li{margin-block:calc(var(--hx-spacing) * 2)}.content :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)):has(li input[type=checkbox]){list-style-type:none}.content :where(ul,ol)>li>:where(ul,ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:calc(var(--hx-spacing) * 0)}.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){border-radius:var(--hx-radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-black)}@supports(color:color-mix(in lab,red,red)){.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:color-mix(in oklab,var(--hx-color-black) 4%,transparent)}}.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:var(--hx-color-black)}@supports(color:color-mix(in lab,red,red)){.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:color-mix(in oklab,var(--hx-color-black) 3%,transparent)}}.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline:.25em;padding-block:calc(var(--hx-spacing) * .5);overflow-wrap:break-word;font-size:.9em}.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-white)}@supports(color:color-mix(in lab,red,red)){.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-white) 10%,transparent)}}.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:var(--hx-color-white)}@supports(color:color-mix(in lab,red,red)){.content :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-white) 10%,transparent)}}.content :where(pre.mermaid):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)){background-color:#0000;border-radius:0}.content :where(pre.mermaid):not(:where(.hextra-code-block pre,[class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){background-color:#0000}.content :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-inline:auto;margin-block:calc(var(--hx-spacing) * 4);border-radius:var(--hx-radius-md)}.content :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)) figcaption{margin-top:calc(var(--hx-spacing) * 2);text-align:center;font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height));color:var(--hx-color-gray-500);display:block}.content :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)) figcaption:where(.dark,.dark *){color:var(--hx-color-gray-400)}.content :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)) dt{margin-top:calc(var(--hx-spacing) * 6);--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold)}.content :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)) dd{margin-block:calc(var(--hx-spacing) * 2);padding-inline-start:calc(var(--hx-spacing) * 6)}.content :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-block:calc(var(--hx-spacing) * 10);border-color:var(--hx-color-gray-200)}.content :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}.content :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)):last-child{margin-bottom:calc(var(--hx-spacing) * 0)}.content :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)):where(.dark,.dark *){border-color:var(--hx-color-neutral-800)}.content .footnotes{margin-top:calc(var(--hx-spacing) * 12);font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height))}.content .footnotes hr{border-color:var(--hx-color-gray-200)}.content .footnotes hr:where(.dark,.dark *){border-color:var(--hx-color-neutral-800)}.content .subheading-anchor{opacity:0;transition-property:opacity;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration))}.content .subheading-anchor:where(:dir(ltr),[dir=ltr],[dir=ltr] *){margin-left:calc(var(--hx-spacing) * 1)}.content .subheading-anchor:where(:dir(rtl),[dir=rtl],[dir=rtl] *){margin-right:calc(var(--hx-spacing) * 1)}span:target+:is(.content .subheading-anchor),:hover>:is(.content .subheading-anchor),.content .subheading-anchor:focus-visible{opacity:1}span+:is(.content .subheading-anchor),:hover>:is(.content .subheading-anchor){text-decoration-line:none!important}.content .subheading-anchor:after{content:var(--tw-content);color:var(--hx-color-gray-300)}.content .subheading-anchor:where(.dark,.dark *):after{content:var(--tw-content);color:var(--hx-color-neutral-700)}.content .subheading-anchor:after{padding-inline:calc(var(--hx-spacing) * 1);--tw-content:"#";content:var(--tw-content)}span:target+:is(){color:var(--hx-color-gray-400)}span:target+:is():where(.dark,.dark *){color:var(--hx-color-neutral-500)}article details>summary::-webkit-details-marker{display:none}article details>summary:before{vertical-align:-4px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='hx:h-5 hx:w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z' clip-rule='evenodd' /%3E%3C/svg%3E");width:1.2em;height:1.2em;padding:0 .6em}:lang(fa) ol{list-style-type:persian}.highlight .chroma .err{color:#a61717;background-color:#e3d2d2}.highlight .chroma .lnlinks{color:inherit;outline:none;text-decoration:none}.highlight .chroma .line{display:flex}.highlight .chroma .k,.highlight .chroma .kc,.highlight .chroma .kd,.highlight .chroma .kn,.highlight .chroma .kp,.highlight .chroma .kr{color:#000;font-weight:700}.highlight .chroma .kt{color:#458;font-weight:700}.highlight .chroma .na{color:teal}.highlight .chroma .nb{color:#0086b3}.highlight .chroma .bp{color:#999}.highlight .chroma .nc{color:#458;font-weight:700}.highlight .chroma .no{color:teal}.highlight .chroma .nd{color:#3c5d5d;font-weight:700}.highlight .chroma .ni{color:purple}.highlight .chroma .ne,.highlight .chroma .nf,.highlight .chroma .nl{color:#900;font-weight:700}.highlight .chroma .nn{color:#555}.highlight .chroma .nt{color:navy}.highlight .chroma .nv,.highlight .chroma .vc,.highlight .chroma .vg,.highlight .chroma .vi{color:teal}.highlight .chroma .s,.highlight .chroma .sa,.highlight .chroma .sb,.highlight .chroma .sc,.highlight .chroma .dl,.highlight .chroma .sd,.highlight .chroma .s2,.highlight .chroma .se,.highlight .chroma .sh,.highlight .chroma .si,.highlight .chroma .sx{color:#d14}.highlight .chroma .sr{color:#009926}.highlight .chroma .s1{color:#d14}.highlight .chroma .ss{color:#990073}.highlight .chroma .m,.highlight .chroma .mb,.highlight .chroma .mf,.highlight .chroma .mh,.highlight .chroma .mi,.highlight .chroma .il,.highlight .chroma .mo{color:#099}.highlight .chroma .o,.highlight .chroma .ow{color:#000;font-weight:700}.highlight .chroma .c,.highlight .chroma .ch,.highlight .chroma .cm,.highlight .chroma .c1{color:#998;font-style:italic}.highlight .chroma .cs,.highlight .chroma .cp,.highlight .chroma .cpf{color:#999;font-style:italic;font-weight:700}.highlight .chroma .gd{color:#000;background-color:#fdd}.highlight .chroma .ge{color:#000;font-style:italic}.highlight .chroma .gr{color:#a00}.highlight .chroma .gh{color:#999}.highlight .chroma .gi{color:#000;background-color:#dfd}.highlight .chroma .go{color:#888}.highlight .chroma .gp{color:#555}.highlight .chroma .gs{font-weight:700}.highlight .chroma .gu{color:#aaa}.highlight .chroma .gt{color:#a00}.highlight .chroma .gl{text-decoration:underline}.highlight .chroma .w{color:#bbb}.dark .highlight .chroma .err{color:#f85149}.dark .highlight .chroma .lnlinks{color:inherit;outline:none;text-decoration:none}.dark .highlight .chroma .line{display:flex}.dark .highlight .chroma .k{color:#ff7b72}.dark .highlight .chroma .kc{color:#79c0ff}.dark .highlight .chroma .kd,.dark .highlight .chroma .kn{color:#ff7b72}.dark .highlight .chroma .kp{color:#79c0ff}.dark .highlight .chroma .kr,.dark .highlight .chroma .kt{color:#ff7b72}.dark .highlight .chroma .nc{color:#f0883e;font-weight:700}.dark .highlight .chroma .no{color:#79c0ff;font-weight:700}.dark .highlight .chroma .nd{color:#d2a8ff;font-weight:700}.dark .highlight .chroma .ni{color:#ffa657}.dark .highlight .chroma .ne{color:#f0883e;font-weight:700}.dark .highlight .chroma .nf{color:#d2a8ff;font-weight:700}.dark .highlight .chroma .nl{color:#79c0ff;font-weight:700}.dark .highlight .chroma .nn{color:#ff7b72}.dark .highlight .chroma .py{color:#79c0ff}.dark .highlight .chroma .nt{color:#7ee787}.dark .highlight .chroma .nv{color:#79c0ff}.dark .highlight .chroma .l{color:#a5d6ff}.dark .highlight .chroma .ld{color:#79c0ff}.dark .highlight .chroma .s{color:#a5d6ff}.dark .highlight .chroma .sa{color:#79c0ff}.dark .highlight .chroma .sb,.dark .highlight .chroma .sc{color:#a5d6ff}.dark .highlight .chroma .dl{color:#79c0ff}.dark .highlight .chroma .sd,.dark .highlight .chroma .s2{color:#a5d6ff}.dark .highlight .chroma .se,.dark .highlight .chroma .sh{color:#79c0ff}.dark .highlight .chroma .si,.dark .highlight .chroma .sx{color:#a5d6ff}.dark .highlight .chroma .sr{color:#79c0ff}.dark .highlight .chroma .s1,.dark .highlight .chroma .ss,.dark .highlight .chroma .m,.dark .highlight .chroma .mb,.dark .highlight .chroma .mf,.dark .highlight .chroma .mh,.dark .highlight .chroma .mi,.dark .highlight .chroma .il,.dark .highlight .chroma .mo{color:#a5d6ff}.dark .highlight .chroma .o,.dark .highlight .chroma .ow{color:#ff7b72;font-weight:700}.dark .highlight .chroma .c,.dark .highlight .chroma .ch,.dark .highlight .chroma .cm,.dark .highlight .chroma .c1{color:#8b949e;font-style:italic}.dark .highlight .chroma .cs,.dark .highlight .chroma .cp,.dark .highlight .chroma .cpf{color:#8b949e;font-style:italic;font-weight:700}.dark .highlight .chroma .gd{color:#ffa198;background-color:#490202}.dark .highlight .chroma .ge{color:inherit;font-style:italic}.dark .highlight .chroma .gr{color:#ffa198}.dark .highlight .chroma .gh{color:#79c0ff;font-weight:700}.dark .highlight .chroma .gi{color:#56d364;background-color:#0f5323}.dark .highlight .chroma .go,.dark .highlight .chroma .gp{color:#8b949e}.dark .highlight .chroma .gs{font-weight:700}.dark .highlight .chroma .gu{color:#79c0ff}.dark .highlight .chroma .gt{color:#ff7b72}.dark .highlight .chroma .gl{text-decoration:underline}.dark .highlight .chroma .w{color:#6e7681}.hextra-code-block{--tw-leading:calc(var(--hx-spacing) * 5);font-size:.9em;line-height:calc(var(--hx-spacing) * 5)}.hextra-code-block pre{background-color:var(--hx-color-primary-700);overflow-x:auto}@supports(color:color-mix(in lab,red,red)){.hextra-code-block pre{background-color:color-mix(in oklab,var(--hx-color-primary-700) 5%,transparent)}}.hextra-code-block pre{--tw-font-weight:var(--hx-font-weight-medium);font-size:.9em;font-weight:var(--hx-font-weight-medium);-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}@media(prefers-contrast:more){.hextra-code-block pre{border-style:var(--tw-border-style);border-width:1px;border-color:var(--hx-color-primary-900)}@supports(color:color-mix(in lab,red,red)){.hextra-code-block pre{border-color:color-mix(in oklab,var(--hx-color-primary-900) 20%,transparent)}}.hextra-code-block pre{--tw-contrast:contrast(150%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}}.hextra-code-block pre:where(.dark,.dark *){background-color:var(--hx-color-primary-300)}@supports(color:color-mix(in lab,red,red)){.hextra-code-block pre:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-primary-300) 10%,transparent)}}@media(prefers-contrast:more){.hextra-code-block pre:where(.dark,.dark *){border-color:var(--hx-color-primary-100)}@supports(color:color-mix(in lab,red,red)){.hextra-code-block pre:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-primary-100) 40%,transparent)}}}.hextra-code-block .hextra-code-filename{top:calc(var(--hx-spacing) * 0);z-index:1;text-overflow:ellipsis;white-space:nowrap;border-top-left-radius:var(--hx-radius-xl);border-top-right-radius:var(--hx-radius-xl);background-color:var(--hx-color-primary-700);width:100%;position:absolute;overflow:hidden}@supports(color:color-mix(in lab,red,red)){.hextra-code-block .hextra-code-filename{background-color:color-mix(in oklab,var(--hx-color-primary-700) 5%,transparent)}}.hextra-code-block .hextra-code-filename{padding-inline:calc(var(--hx-spacing) * 4);padding-block:calc(var(--hx-spacing) * 2);font-size:var(--hx-text-xs);line-height:var(--tw-leading,var(--hx-text-xs--line-height));color:var(--hx-color-gray-700)}.hextra-code-block .hextra-code-filename:where(.dark,.dark *){background-color:var(--hx-color-primary-300)}@supports(color:color-mix(in lab,red,red)){.hextra-code-block .hextra-code-filename:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-primary-300) 10%,transparent)}}.hextra-code-block .hextra-code-filename:where(.dark,.dark *){color:var(--hx-color-gray-200)}.hextra-code-block .hextra-code-filename+pre:not(.lntable pre){padding-top:calc(var(--hx-spacing) * 12)}.hextra-code-block pre:not(.lntable pre){margin-bottom:calc(var(--hx-spacing) * 4);border-radius:var(--hx-radius-xl);padding-inline:calc(var(--hx-spacing) * 4);padding-block:calc(var(--hx-spacing) * 4)}.hextra-code-block div:nth-of-type(2) pre{padding-top:calc(var(--hx-spacing) * 12);padding-bottom:calc(var(--hx-spacing) * 4)}.chroma .lntable{margin:calc(var(--hx-spacing) * 0);border-radius:var(--hx-radius-xl);width:auto;display:block;overflow:auto}.chroma .lntable pre{padding-top:calc(var(--hx-spacing) * 4);padding-bottom:calc(var(--hx-spacing) * 4)}.chroma .ln,.chroma .lnt:not(.hl>.lnt),.chroma .hl:not(.line){min-width:2.6rem;padding-right:calc(var(--hx-spacing) * 4);padding-left:calc(var(--hx-spacing) * 4);color:var(--hx-color-neutral-600)}:is(.chroma .ln,.chroma .lnt:not(.hl>.lnt),.chroma .hl:not(.line)):where(.dark,.dark *){color:var(--hx-color-neutral-300)}.chroma .lntd{padding:calc(var(--hx-spacing) * 0);vertical-align:top}.chroma .lntd:last-of-type{width:100%}.chroma .hl{background-color:var(--hx-color-primary-800);width:100%;display:block}@supports(color:color-mix(in lab,red,red)){.chroma .hl{background-color:color-mix(in oklab,var(--hx-color-primary-800) 10%,transparent)}}.hextra-cards{grid-template-columns:repeat(auto-fill,minmax(max(250px,calc((100% - 1rem * 2)/var(--hextra-cards-grid-cols))),1fr))}.hextra-card{position:relative}.hextra-card img{-webkit-user-select:none;user-select:none}.hextra-card:hover .hextra-card-icon svg{color:currentColor}.hextra-card .hextra-card-icon svg{color:#0003;width:1.5rem;transition:color .3s}.hextra-card p{margin-top:.5rem;position:relative}.dark .hextra-card .hextra-card-icon svg{color:#fff6}.dark .hextra-card:hover .hextra-card-icon svg{color:currentColor}.hextra-card-tag{z-index:10;position:absolute;top:5px}.hextra-card-tag:where(:dir(ltr)){right:5px}.hextra-card-tag:where(:dir(rtl)){left:5px}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker){counter-increment:step}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):where(:dir(ltr),[dir=ltr],[dir=ltr] *):before{content:var(--tw-content);margin-left:-41px}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):where(:dir(rtl),[dir=rtl],[dir=rtl] *):before{content:var(--tw-content);margin-right:-44px}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):before{content:var(--tw-content);background-color:var(--hx-color-gray-100)}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):where(.dark,.dark *):before{content:var(--tw-content);background-color:var(--hx-color-neutral-800)}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):before{content:var(--tw-content);border-style:var(--tw-border-style);content:var(--tw-content);border-width:4px;border-color:var(--hx-color-white)}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):where(.dark,.dark *):before{content:var(--tw-content);border-color:var(--hx-color-dark)}.hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):before{content:counter(step);text-align:center;text-indent:-1px;width:33px;height:33px;font-size:var(--hx-text-base);line-height:var(--tw-leading,var(--hx-text-base--line-height));--tw-font-weight:var(--hx-font-weight-normal);font-weight:var(--hx-font-weight-normal);color:var(--hx-color-neutral-400);border-radius:340282e33px;position:absolute}:lang(fa) .hextra-steps :where(h2,h3,h4,h5,h6):not(.no-step-marker):before{content:counter(step,persian)}.hextra-search-wrapper li{margin-inline:calc(var(--hx-spacing) * 2.5);border-radius:var(--hx-radius-md);overflow-wrap:break-word;color:var(--hx-color-gray-800)}@media(prefers-contrast:more){.hextra-search-wrapper li{border-style:var(--tw-border-style);border-width:1px;border-color:#0000}}.hextra-search-wrapper li:where(.dark,.dark *){color:var(--hx-color-gray-300)}.hextra-search-wrapper li a{scroll-margin:calc(var(--hx-spacing) * 12);padding-inline:calc(var(--hx-spacing) * 2.5);padding-block:calc(var(--hx-spacing) * 2);display:block}.hextra-search-wrapper li a:focus-visible{--tw-outline-style:none;outline-style:none}.hextra-search-wrapper li .hextra-search-title{font-size:var(--hx-text-base);line-height:var(--tw-leading,var(--hx-text-base--line-height));--tw-leading:calc(var(--hx-spacing) * 5);line-height:calc(var(--hx-spacing) * 5);--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold)}.hextra-search-wrapper li .hextra-search-active{border-radius:var(--hx-radius-md);background-color:var(--hx-color-primary-500)}@supports(color:color-mix(in lab,red,red)){.hextra-search-wrapper li .hextra-search-active{background-color:color-mix(in oklab,var(--hx-color-primary-500) 10%,transparent)}}@media(prefers-contrast:more){.hextra-search-wrapper li .hextra-search-active{border-color:var(--hx-color-primary-500)}}.hextra-search-wrapper .hextra-search-no-result{padding:calc(var(--hx-spacing) * 8);text-align:center;font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height));color:var(--hx-color-gray-400);-webkit-user-select:none;user-select:none;display:block}.hextra-search-wrapper .hextra-search-prefix{margin-inline:calc(var(--hx-spacing) * 2.5);margin-top:calc(var(--hx-spacing) * 6);margin-bottom:calc(var(--hx-spacing) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--hx-color-black)}@supports(color:color-mix(in lab,red,red)){.hextra-search-wrapper .hextra-search-prefix{border-color:color-mix(in oklab,var(--hx-color-black) 10%,transparent)}}.hextra-search-wrapper .hextra-search-prefix{padding-inline:calc(var(--hx-spacing) * 2.5);padding-bottom:calc(var(--hx-spacing) * 1.5);font-size:var(--hx-text-xs);line-height:var(--tw-leading,var(--hx-text-xs--line-height));--tw-font-weight:var(--hx-font-weight-semibold);font-weight:var(--hx-font-weight-semibold);color:var(--hx-color-gray-500);text-transform:uppercase;-webkit-user-select:none;user-select:none}.hextra-search-wrapper .hextra-search-prefix:first-child{margin-top:calc(var(--hx-spacing) * 0)}@media(prefers-contrast:more){.hextra-search-wrapper .hextra-search-prefix{border-color:var(--hx-color-gray-600);color:var(--hx-color-gray-900)}}.hextra-search-wrapper .hextra-search-prefix:where(.dark,.dark *){border-color:var(--hx-color-white)}@supports(color:color-mix(in lab,red,red)){.hextra-search-wrapper .hextra-search-prefix:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-white) 20%,transparent)}}.hextra-search-wrapper .hextra-search-prefix:where(.dark,.dark *){color:var(--hx-color-gray-300)}@media(prefers-contrast:more){.hextra-search-wrapper .hextra-search-prefix:where(.dark,.dark *){border-color:var(--hx-color-gray-50);color:var(--hx-color-gray-50)}}.hextra-search-wrapper .hextra-search-excerpt{margin-top:calc(var(--hx-spacing) * 1);font-size:var(--hx-text-sm);line-height:var(--tw-leading,var(--hx-text-sm--line-height));--tw-leading:1.35rem;text-overflow:ellipsis;color:var(--hx-color-gray-600);line-height:1.35rem;overflow:hidden}.hextra-search-wrapper .hextra-search-excerpt:where(.dark,.dark *){color:var(--hx-color-gray-400)}@media(prefers-contrast:more){.hextra-search-wrapper .hextra-search-excerpt:where(.dark,.dark *){color:var(--hx-color-gray-50)}}.hextra-search-wrapper .hextra-search-excerpt{line-clamp:1;-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box}.hextra-search-wrapper .hextra-search-match{color:var(--hx-color-primary-600)}@media(max-width:48rem){.hextra-sidebar-container{top:calc(var(--hx-spacing) * 0);bottom:calc(var(--hx-spacing) * 0);z-index:15;overscroll-behavior:contain;background-color:var(--hx-color-white);width:100%;padding-top:calc(var(--navbar-height) + var(--hextra-banner-height));position:fixed}.hextra-sidebar-container:where(.dark,.dark *){background-color:var(--hx-color-dark)}.hextra-sidebar-container{will-change:transform,opacity;contain:layout style;backface-visibility:hidden;transition:transform .4s cubic-bezier(.52,.16,.04,1)}}.hextra-sidebar-container li>.hextra-sidebar-children{height:calc(var(--hx-spacing) * 0)}.hextra-sidebar-container li.open>.hextra-sidebar-children{height:auto;padding-top:calc(var(--hx-spacing) * 1)}.hextra-sidebar-container li.open>.hextra-sidebar-item>.hextra-sidebar-collapsible-button>svg>path{rotate:90deg}.hextra-banner-hidden .hextra-banner{display:none}.hextra-banner :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:underline;text-decoration-thickness:from-font}.hextra-banner :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){--tw-leading:calc(var(--hx-spacing) * 7);line-height:calc(var(--hx-spacing) * 7)}.hextra-banner :where(p):not(:where([class~=not-prose],[class~=not-prose] *)):first-child{margin-top:calc(var(--hx-spacing) * 0)}nav .hextra-search-wrapper{display:none}@media(min-width:48rem){nav .hextra-search-wrapper{display:inline-block}}@supports((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){.hextra-nav-container-blur{background-color:var(--hx-color-white)}@supports(color:color-mix(in lab,red,red)){.hextra-nav-container-blur{background-color:color-mix(in oklab,var(--hx-color-white) 85%,transparent)}}.hextra-nav-container-blur{--tw-backdrop-blur:blur(var(--hx-blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.hextra-nav-container-blur:where(.dark,.dark *){background-color:var(--hx-color-dark)!important}@supports(color:color-mix(in lab,red,red)){.hextra-nav-container-blur:where(.dark,.dark *){background-color:color-mix(in oklab,var(--hx-color-dark) 80%,transparent)!important}}}.hextra-hamburger-menu svg g{transform-origin:50%;transition-property:all;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration));--tw-duration:.1s;--tw-ease:var(--hx-ease-out);transition-duration:.1s;transition-timing-function:var(--hx-ease-out)}.hextra-hamburger-menu svg path{opacity:1;transition-property:all;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration));--tw-duration:.1s;--tw-ease:var(--hx-ease-out);transition-duration:.1s;transition-delay:.1s;transition-timing-function:var(--hx-ease-out)}.hextra-hamburger-menu svg.open path{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration));--tw-duration:.1s;--tw-ease:var(--hx-ease-out);transition-duration:.1s;transition-delay:0s;transition-timing-function:var(--hx-ease-out)}.hextra-hamburger-menu svg.open g{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration));--tw-duration:.1s;--tw-ease:var(--hx-ease-out);transition-duration:.1s;transition-delay:.1s;transition-timing-function:var(--hx-ease-out)}.hextra-hamburger-menu svg.open>path{opacity:0}.hextra-hamburger-menu svg.open>g:first-of-type{rotate:45deg}.hextra-hamburger-menu svg.open>g:first-of-type path{--tw-translate-y:calc(var(--hx-spacing) * 1);translate:var(--tw-translate-x)var(--tw-translate-y)}.hextra-hamburger-menu svg.open>g:nth-of-type(2){rotate:-45deg}.hextra-hamburger-menu svg.open>g:nth-of-type(2) path{--tw-translate-y:calc(var(--hx-spacing) * -1);translate:var(--tw-translate-x)var(--tw-translate-y)}.hextra-scrollbar,.hextra-scrollbar *{scrollbar-width:thin;scrollbar-color:oklch(55.55% 0 0/.4)transparent;scrollbar-gutter:stable}:is(.hextra-scrollbar,.hextra-scrollbar *)::-webkit-scrollbar{height:calc(var(--hx-spacing) * 3);width:calc(var(--hx-spacing) * 3)}:is(.hextra-scrollbar,.hextra-scrollbar *)::-webkit-scrollbar-track{background-color:#0000}:is(.hextra-scrollbar,.hextra-scrollbar *)::-webkit-scrollbar-thumb{border-radius:10px}:is(.hextra-scrollbar,.hextra-scrollbar *):hover::-webkit-scrollbar-thumb{background-color:var(--tw-shadow-color);--tw-shadow-color:var(--hx-color-neutral-500);background-clip:content-box;border:3px solid #0000}@supports(color:color-mix(in lab,red,red)){:is(.hextra-scrollbar,.hextra-scrollbar *):hover::-webkit-scrollbar-thumb{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--hx-color-neutral-500) 20%, transparent) var(--tw-shadow-alpha), transparent)}}@media(hover:hover){:is(.hextra-scrollbar,.hextra-scrollbar *):hover::-webkit-scrollbar-thumb:hover{--tw-shadow-color:var(--hx-color-neutral-500)}@supports(color:color-mix(in lab,red,red)){:is(.hextra-scrollbar,.hextra-scrollbar *):hover::-webkit-scrollbar-thumb:hover{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--hx-color-neutral-500) 40%, transparent) var(--tw-shadow-alpha), transparent)}}}@supports((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){.hextra-code-copy-btn{opacity:.85;--tw-backdrop-blur:blur(var(--hx-blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.hextra-code-copy-btn:where(.dark,.dark *){opacity:.8}}@media(min-width:1024px){.hextra-feature-grid{grid-template-columns:repeat(var(--hextra-feature-grid-cols),minmax(0,1fr))}}.hextra-jupyter-code-cell{scrollbar-gutter:auto;margin-top:calc(var(--hx-spacing) * 6)}.hextra-jupyter-code-cell .hextra-jupyter-code-cell-outputs-container{font-size:var(--hx-text-xs);line-height:var(--tw-leading,var(--hx-text-xs--line-height));overflow:hidden}.hextra-jupyter-code-cell .hextra-jupyter-code-cell-outputs-container .hextra-jupyter-code-cell-outputs{max-height:50vh;overflow:auto}.hextra-jupyter-code-cell .hextra-jupyter-code-cell-outputs-container .hextra-jupyter-code-cell-outputs pre{max-width:100%;font-size:var(--hx-text-xs);line-height:var(--tw-leading,var(--hx-text-xs--line-height));overflow:auto}.hextra-badge{align-items:center;display:inline-flex}.hextra-toc a.hextra-toc-active{transition-property:all;transition-timing-function:var(--tw-ease,var(--hx-default-transition-timing-function));transition-duration:var(--tw-duration,var(--hx-default-transition-duration));--tw-duration:.2s;transition-duration:.2s;color:var(--hx-color-gray-900)!important}.hextra-toc a.hextra-toc-active:where(.dark,.dark *){color:var(--hx-color-gray-50)!important}.hextra-archive-timeline{border-left-style:var(--tw-border-style);border-left-width:2px;border-color:var(--hx-color-black)}@supports(color:color-mix(in lab,red,red)){.hextra-archive-timeline{border-color:color-mix(in oklab,var(--hx-color-black) 15%,transparent)}}.hextra-archive-timeline:where(.dark,.dark *){border-color:var(--hx-color-white)}@supports(color:color-mix(in lab,red,red)){.hextra-archive-timeline:where(.dark,.dark *){border-color:color-mix(in oklab,var(--hx-color-white) 15%,transparent)}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{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-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{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-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@import 'https://fonts.googleapis.com/css2?family=Archivo+Black&family=Inter:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,700;1,400&display=swap';@font-face{font-family:krungthep;src:url(/fonts/Krungthep.ttf)format('truetype');font-weight:400;font-style:normal;font-display:swap}:root{--primary-hue:26deg;--primary-saturation:42%;--primary-lightness:49%;--rapport-bg:#f7f5f2;--rapport-surface:#ffffff;--rapport-surface2:#fbf9f5;--rapport-dark:#1a1a18;--rapport-dark2:#2d2d28;--rapport-accent:#b07848;--rapport-accent-2:#9a673b;--rapport-accent-3:#7d5430;--rapport-text:#1a1a18;--rapport-text-2:#555550;--rapport-text-3:#888880;--rapport-text-4:#aaaaaa;--rapport-border:#e8e3dc;--rapport-border-2:#d8d2ca}.dark{--primary-hue:26deg;--primary-saturation:42%;--primary-lightness:56%;--color-dark:var(--rapport-dark)}body{background:var(--rapport-bg);color:var(--rapport-text);font-family:inter,-apple-system,BlinkMacSystemFont,helvetica neue,sans-serif}.dark body{background:#181614;color:#ece9e3}::selection{background:rgba(176,120,72,.25);color:var(--rapport-dark)}.dark ::selection{background:rgba(176,120,72,.35);color:#fff}.hextra-toc,.content,.prose{font-family:inter,-apple-system,BlinkMacSystemFont,helvetica neue,sans-serif}h1,h2,h3,h4,.hextra-home h1,.hextra-home h2,.hextra-home h3,.hx\:text-2xl,.hx\:text-3xl,.hx\:text-4xl,.hx\:text-5xl,.hx\:text-6xl{font-family:playfair display,Georgia,serif!important;font-weight:700!important;letter-spacing:-.01em}.hextra-navbar-title,nav .hextra-navbar-title,nav .hextra-max-navbar-width .hx\:font-bold{font-family:Krungthep,archivo black,sans-serif!important;letter-spacing:-.02em!important;font-weight:900!important}.nav-container{background:rgba(247,245,242,.85)!important;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--rapport-border)!important}.dark .nav-container{background:rgba(24,22,20,.85)!important;border-bottom:1px solid #2d2926!important}.nav-container-blur,.dark .nav-container-blur{background:0 0!important}aside.sidebar-container,.sidebar-container{background:var(--rapport-bg)!important;border-right:1px solid var(--rapport-border)}.sidebar-container a{color:var(--rapport-text-2)}.sidebar-container a:hover{color:var(--rapport-accent);background:var(--rapport-surface)}.sidebar-active-item,.sidebar-container .sidebar-active-item{background:rgba(176,120,72,.1)!important;color:var(--rapport-accent)!important;border-color:rgba(176,120,72,.2)!important}.dark aside.sidebar-container,.dark .sidebar-container{background:#181614!important;border-right:1px solid #2d2926}a{transition:color .15s}.content a,.prose a{color:var(--rapport-accent);text-decoration:none}.content a:hover,.prose a:hover{color:var(--rapport-accent-2);text-decoration:underline}.hextra-card,.hextra-card:hover,.hextra-card:focus,.hextra-card:active,.hextra-card *,.hextra-card:hover *{text-decoration:none!important}a.hextra-card:hover,a.hextra-card:focus{outline:none;text-decoration:none!important}.rapport-hero-actions{display:flex;gap:18px;align-items:center;justify-content:center;flex-wrap:wrap;margin-top:8px;margin-bottom:8px}.rapport-btn{display:inline-flex;align-items:center;gap:9px;border-radius:999px;padding:14px 30px;font-family:inter,-apple-system,BlinkMacSystemFont,helvetica neue,sans-serif;font-size:12px;font-weight:500;letter-spacing:.07em;text-transform:uppercase;text-decoration:none!important;cursor:pointer;transition:transform .18s ease,box-shadow .18s ease,background .18s ease,border-color .18s ease;user-select:none;white-space:nowrap}.rapport-btn-primary{background:#fff;color:var(--rapport-text)!important;border:1px solid var(--rapport-border);box-shadow:0 1px rgba(255,255,255,.9)inset,0 2px 4px rgba(0,0,0,4%),0 8px 20px rgba(0,0,0,.1),0 16px 40px rgba(0,0,0,8%)}.rapport-btn-primary:hover{background:#fff;border-color:var(--rapport-border-2);color:var(--rapport-accent-2)!important;transform:translateY(-2px);box-shadow:0 1px rgba(255,255,255,.9)inset,0 4px 8px rgba(0,0,0,6%),0 14px 28px rgba(0,0,0,.12),0 24px 56px rgba(176,120,72,.18)}.rapport-btn-primary:active{transform:translateY(0);box-shadow:0 1px rgba(255,255,255,.9)inset,0 2px 4px rgba(0,0,0,6%),0 4px 12px rgba(0,0,0,8%)}.rapport-btn-secondary{background:0 0;color:var(--rapport-text-2)!important;border:1.5px solid var(--rapport-border-2);box-shadow:0 2px 6px rgba(0,0,0,3%)}.rapport-btn-secondary:hover{background:rgba(255,255,255,.5);border-color:var(--rapport-text-3);color:var(--rapport-text)!important;transform:translateY(-2px);box-shadow:0 6px 14px rgba(0,0,0,6%),0 12px 28px rgba(0,0,0,4%)}.rapport-btn-secondary:active{transform:translateY(0)}.dark .rapport-btn-primary{background:#2a2722;color:#ece9e3!important;border-color:#3a3530;box-shadow:0 1px rgba(255,255,255,4%)inset,0 2px 4px rgba(0,0,0,.3),0 10px 24px rgba(0,0,0,.45)}.dark .rapport-btn-primary:hover{background:#322e28;color:var(--rapport-accent)!important;box-shadow:0 1px rgba(255,255,255,4%)inset,0 4px 8px rgba(0,0,0,.4),0 16px 32px rgba(0,0,0,.5),0 24px 56px rgba(176,120,72,.22)}.dark .rapport-btn-secondary{border-color:#3a3530;color:#b8b2a8!important}.dark .rapport-btn-secondary:hover{background:rgba(255,255,255,4%);border-color:#6a6258;color:#ece9e3!important}.hextra-badge{background:var(--rapport-surface)!important;border:1px solid var(--rapport-border)!important;border-radius:20px!important;padding:5px 14px!important;font-size:10px!important;letter-spacing:.12em!important;color:var(--rapport-text-4)!important;text-transform:uppercase!important;font-family:inter,-apple-system,BlinkMacSystemFont,helvetica neue,sans-serif!important;box-shadow:0 2px 8px rgba(0,0,0,6%),0 1px 3px rgba(0,0,0,4%)}.hextra-feature-card{background:var(--rapport-surface)!important;border:1px solid var(--rapport-border)!important;border-radius:14px!important;transition:box-shadow .2s,transform .2s,border-color .2s!important}.hextra-feature-card:hover{border-color:var(--rapport-border-2)!important;transform:translateY(-2px);box-shadow:0 8px 32px rgba(0,0,0,9%),0 2px 8px rgba(0,0,0,5%)!important}.hextra-feature-card h3{font-family:playfair display,serif!important;color:var(--rapport-text)!important;font-weight:700!important}.hextra-feature-card p{color:var(--rapport-text-2)!important;font-size:12px!important;line-height:1.8!important}.dark .hextra-feature-card{background:#211e1a!important;border-color:#2d2926!important}.dark .hextra-feature-card h3{color:#ece9e3!important}.dark .hextra-feature-card p{color:#b8b2a8!important}.hextra-card{background:var(--rapport-surface);border:1px solid var(--rapport-border);border-radius:12px;transition:border-color .2s,transform .2s}.hextra-card:hover{border-color:var(--rapport-accent-2);transform:translateY(-1px)}.hextra-code-block{background:#fff!important;border:1px solid var(--rapport-border)!important;border-radius:12px!important;overflow:hidden}.hextra-code-block pre,.hextra-code-block .highlight,.hextra-code-block .chroma{background:0 0!important;border:none!important;border-radius:0!important;margin:0!important}pre{background:var(--rapport-surface2);border:none}code,pre,pre code,kbd,samp,tt{font-family:ui-monospace,sf mono,Menlo,Monaco,Consolas,liberation mono,monospace!important}code{color:var(--rapport-accent-3)!important;background:rgba(176,120,72,.1)!important;padding:2px 6px;border-radius:4px;font-size:.9em}pre code{color:var(--rapport-text)!important;background:0 0!important;padding:0;font-size:.9em}.dark .hextra-code-block{background:#1f1c19!important;border-color:#2d2926!important}.dark .hextra-code-block pre,.dark .hextra-code-block .highlight,.dark .hextra-code-block .chroma{background:0 0!important;border:none!important}.dark code{color:var(--rapport-accent)!important;background:rgba(176,120,72,.15)!important}.dark pre code{color:#ece9e3!important}.hextra-callout{background:var(--rapport-surface)!important;border-color:var(--rapport-border-2)!important}.hextra-footer,footer{background:var(--rapport-surface2);border-top:1px solid var(--rapport-border);color:var(--rapport-text-3)}.hextra-footer a{color:var(--rapport-text-2)}.hextra-footer a:hover{color:var(--rapport-accent)}.dark .hextra-footer,.dark footer{background:#15130f;border-top:1px solid #2d2926;color:#888880}.hextra-toc a{color:var(--rapport-text-3)}.hextra-toc a:hover,.hextra-toc .active{color:var(--rapport-accent)!important}.hextra-toc div:has(>#backToTop),[data-toggle-animation]{background:var(--rapport-bg)!important;box-shadow:none!important;border-top-color:var(--rapport-border)!important}.dark .hextra-toc div:has(>#backToTop),.dark [data-toggle-animation]{background:#181614!important;border-top-color:#2d2926!important;box-shadow:none!important}.hextra-search-wrapper input{background:var(--rapport-surface)!important;border:1px solid var(--rapport-border)!important;color:var(--rapport-text)!important}.hextra-search-wrapper input:focus{border-color:var(--rapport-accent-2)!important}table{border-color:var(--rapport-border)!important}thead{background:var(--rapport-surface)!important}th,td{border-color:var(--rapport-border)!important}.rapport-logo-card{background:var(--rapport-dark);border:1px solid var(--rapport-dark2);border-radius:999px;padding:28px 64px 26px;display:inline-block;box-shadow:6px 0 20px rgba(0,0,0,.18),0 6px 16px rgba(0,0,0,.12);text-align:center;margin:0 auto 32px}.rapport-logo-text{font-family:krungthep,archivo black,sans-serif!important;font-size:48px;letter-spacing:-.02em;color:#f0ede8;line-height:.95;font-weight:400}.rapport-logo-sub{font-size:9px;letter-spacing:.15em;color:#f0ede8;text-transform:uppercase;margin-top:8px;font-weight:500}.rapport-meta{display:flex;gap:6px;align-items:center;justify-content:center;flex-wrap:wrap;margin-top:32px;font-family:inter,-apple-system,BlinkMacSystemFont,helvetica neue,sans-serif}.rapport-meta-item{font-size:10px;letter-spacing:.08em;color:var(--rapport-text-4);text-transform:uppercase;padding:0 10px;border-right:1px solid var(--rapport-border)}.rapport-meta-item:last-child{border-right:none}.rapport-status,.rapport-status.active,.rapport-status.planned,.rapport-status.stable,.rapport-status.new{display:inline-block;font-size:10px;letter-spacing:.12em;text-transform:uppercase;padding:5px 14px;border-radius:999px;margin-bottom:12px;font-weight:600;font-family:inter,-apple-system,BlinkMacSystemFont,helvetica neue,sans-serif;background:#fff;color:var(--rapport-text);border:1px solid var(--rapport-border);box-shadow:0 1px rgba(255,255,255,.9)inset,0 1px 2px rgba(0,0,0,4%),0 3px 8px rgba(0,0,0,6%)}.dark .rapport-status,.dark .rapport-status.active,.dark .rapport-status.planned,.dark .rapport-status.stable,.dark .rapport-status.new{background:#2a2722;color:#ece9e3;border-color:#3a3530;box-shadow:0 1px rgba(255,255,255,4%)inset,0 1px 2px rgba(0,0,0,.3),0 4px 10px rgba(0,0,0,.35)}.rapport-stack-bar{padding:20px 0;border-top:1px solid var(--rapport-border);display:flex;align-items:center;gap:20px;flex-wrap:wrap;margin-top:32px;font-family:inter,-apple-system,BlinkMacSystemFont,helvetica neue,sans-serif}.rapport-stack-label{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--rapport-text-4);flex-shrink:0}.rapport-stack-items{display:flex;gap:8px;flex-wrap:wrap}.rapport-stack-item{font-size:10px;letter-spacing:.06em;color:var(--rapport-text-3);background:var(--rapport-surface);border:1px solid var(--rapport-border);border-radius:6px;padding:4px 10px}.content article{max-width:100%}.hextra-home{align-items:center!important;text-align:center;max-width:100%}.hextra-home>p,.hextra-home .not-prose.hx\:text-xl{font-family:playfair display,serif!important;font-size:clamp(16px,2.2vw,24px)!important;font-weight:400!important;color:var(--rapport-text-3)!important;max-width:560px!important;margin-left:auto!important;margin-right:auto!important;line-height:1.55!important;text-align:center}.hextra-home>div.hx\:flex,.hextra-home>.hx\:flex{justify-content:center;align-items:center}.hextra-home .hextra-hero-button,a.hextra-hero-button{background:var(--rapport-dark);color:#f0ede8!important;border-radius:20px;padding:13px 28px;font-family:inter,-apple-system,BlinkMacSystemFont,helvetica neue,sans-serif!important;font-size:11px!important;font-weight:500!important;letter-spacing:.07em!important;text-transform:uppercase!important;box-shadow:0 4px 14px rgba(0,0,0,.2);transition:all .18s;display:inline-flex;align-items:center;gap:9px}.hextra-home .hextra-feature-grid,.hextra-home .rapport-stack-bar{text-align:left;width:100%}.hextra-home .hextra-feature-card *{text-align:left}.rapport-stack-bar{justify-content:flex-start;text-align:left}.hextra-home h2,.hextra-home>div>h2{text-align:center;margin-left:auto;margin-right:auto}.hextra-home .rapport-logo-card{margin-left:auto;margin-right:auto;display:block}.hextra-home .hextra-badge,.hextra-home>p:has(.hextra-badge){margin-left:auto;margin-right:auto;display:inline-flex!important}body:has(.hextra-home)::before{content:"";position:fixed;top:5%;left:50%;transform:translateX(-50%);width:720px;height:720px;background:radial-gradient(circle,rgba(176,120,72,8%) 0%,transparent 60%);pointer-events:none;z-index:0}.dark body:has(.hextra-home)::before{background:radial-gradient(circle,rgba(176,120,72,.12) 0%,transparent 60%)}.hextra-navbar-title,nav a[href="/"] span,nav [class*=font-bold]{font-family:krungthep,archivo black,sans-serif!important;letter-spacing:-.02em!important;font-weight:400!important;font-size:23px!important;line-height:1!important}.rapport-meta{justify-content:center;text-align:center} \ No newline at end of file diff --git a/public/css/custom.css b/public/css/custom.css new file mode 100644 index 0000000..7f50625 --- /dev/null +++ b/public/css/custom.css @@ -0,0 +1,756 @@ +/* ───────────────────────────────────────────────────────────── + RAPPORT — Theme-Overrides für Hextra + Warmes Sand/Tan auf hellem Grund (Architektur-Büro-Ästhetik) + ───────────────────────────────────────────────────────────── */ + +@import url('https://fonts.googleapis.com/css2?family=Archivo+Black&family=Inter:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,700;1,400&display=swap'); + +/* Krungthep — RAPPORT-Display-Font (Mac-System-Font lokal gebundelt) */ +@font-face { + font-family: 'Krungthep'; + src: url('/fonts/Krungthep.ttf') format('truetype'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +/* — Primary-HSL: Tan #b07848 — */ +:root { + --primary-hue: 26deg; + --primary-saturation: 42%; + --primary-lightness: 49%; + + /* RAPPORT-Palette (Light) */ + --rapport-bg: #f7f5f2; + --rapport-surface: #ffffff; + --rapport-surface2: #fbf9f5; + --rapport-dark: #1a1a18; + --rapport-dark2: #2d2d28; + --rapport-accent: #b07848; + --rapport-accent-2: #9a673b; + --rapport-accent-3: #7d5430; + --rapport-text: #1a1a18; + --rapport-text-2: #555550; + --rapport-text-3: #888880; + --rapport-text-4: #aaaaaa; + --rapport-border: #e8e3dc; + --rapport-border-2: #d8d2ca; +} + +.dark { + --primary-hue: 26deg; + --primary-saturation: 42%; + --primary-lightness: 56%; + --color-dark: var(--rapport-dark); +} + +/* — Body & Backgrounds — */ +body { + background: var(--rapport-bg); + color: var(--rapport-text); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; +} + +.dark body { + background: #181614; + color: #ece9e3; +} + +::selection { + background: rgba(176, 120, 72, 0.25); + color: var(--rapport-dark); +} + +.dark ::selection { + background: rgba(176, 120, 72, 0.35); + color: #fff; +} + +/* — Typografie — Headings serifig, Body monospaced — */ +.hextra-toc, +.content, +.prose { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; +} + +h1, h2, h3, h4, +.hextra-home h1, .hextra-home h2, .hextra-home h3, +.hx\:text-2xl, .hx\:text-3xl, .hx\:text-4xl, .hx\:text-5xl, .hx\:text-6xl { + font-family: 'Playfair Display', Georgia, serif !important; + font-weight: 700 !important; + letter-spacing: -0.01em; +} + +/* Navbar-Logo: RAPPORT in Krungthep/Archivo Black — nur navbar! */ +.hextra-navbar-title, +nav .hextra-navbar-title, +nav .hextra-max-navbar-width .hx\:font-bold { + font-family: Krungthep, 'Archivo Black', sans-serif !important; + letter-spacing: -0.02em !important; + font-weight: 900 !important; +} + +/* — Navbar mit hellem Hintergrund & dezenter Border — */ +.nav-container { + background: rgba(247, 245, 242, 0.85) !important; + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border-bottom: 1px solid var(--rapport-border) !important; +} + +.dark .nav-container { + background: rgba(24, 22, 20, 0.85) !important; + border-bottom: 1px solid #2d2926 !important; +} + +.nav-container-blur, +.dark .nav-container-blur { + background: transparent !important; +} + +/* — Sidebar — */ +aside.sidebar-container, +.sidebar-container { + background: var(--rapport-bg) !important; + border-right: 1px solid var(--rapport-border); +} + +.sidebar-container a { + color: var(--rapport-text-2); +} + +.sidebar-container a:hover { + color: var(--rapport-accent); + background: var(--rapport-surface); +} + +.sidebar-active-item, +.sidebar-container .sidebar-active-item { + background: rgba(176, 120, 72, 0.10) !important; + color: var(--rapport-accent) !important; + border-color: rgba(176, 120, 72, 0.20) !important; +} + +.dark aside.sidebar-container, +.dark .sidebar-container { + background: #181614 !important; + border-right: 1px solid #2d2926; +} + + +/* — Links — */ +a { + transition: color 0.15s; +} + +.content a, +.prose a { + color: var(--rapport-accent); + text-decoration: none; +} + +.content a:hover, +.prose a:hover { + color: var(--rapport-accent-2); + text-decoration: underline; +} + +/* Hextra-Card-Links (-Shortcode) sollen NIE unterstrichen werden */ +.hextra-card, +.hextra-card:hover, +.hextra-card:focus, +.hextra-card:active, +.hextra-card *, +.hextra-card:hover * { + text-decoration: none !important; +} + +/* Card-Hover-Effekt sauber: nur Border + leichte Schatten, kein Underline */ +a.hextra-card:hover, +a.hextra-card:focus { + outline: none; + text-decoration: none !important; +} + +/* — RAPPORT Hero-Buttons — eigene Pills, nicht von Hextra abhängig — */ +.rapport-hero-actions { + display: flex; + gap: 18px; + align-items: center; + justify-content: center; + flex-wrap: wrap; + margin-top: 8px; + margin-bottom: 8px; +} + +.rapport-btn { + display: inline-flex; + align-items: center; + gap: 9px; + border-radius: 999px; + padding: 14px 30px; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; + font-size: 12px; + font-weight: 500; + letter-spacing: 0.07em; + text-transform: uppercase; + text-decoration: none !important; + cursor: pointer; + transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease, border-color 0.18s ease; + user-select: none; + white-space: nowrap; +} + +/* Primary (Download) — Weiss mit Tiefe */ +.rapport-btn-primary { + background: #ffffff; + color: var(--rapport-text) !important; + border: 1px solid var(--rapport-border); + box-shadow: + 0 1px 0 rgba(255,255,255,0.9) inset, + 0 2px 4px rgba(0,0,0,0.04), + 0 8px 20px rgba(0,0,0,0.10), + 0 16px 40px rgba(0,0,0,0.08); +} + +.rapport-btn-primary:hover { + background: #ffffff; + border-color: var(--rapport-border-2); + color: var(--rapport-accent-2) !important; + transform: translateY(-2px); + box-shadow: + 0 1px 0 rgba(255,255,255,0.9) inset, + 0 4px 8px rgba(0,0,0,0.06), + 0 14px 28px rgba(0,0,0,0.12), + 0 24px 56px rgba(176,120,72,0.18); +} + +.rapport-btn-primary:active { + transform: translateY(0); + box-shadow: + 0 1px 0 rgba(255,255,255,0.9) inset, + 0 2px 4px rgba(0,0,0,0.06), + 0 4px 12px rgba(0,0,0,0.08); +} + +/* Secondary (Quellcode) — Outline */ +.rapport-btn-secondary { + background: transparent; + color: var(--rapport-text-2) !important; + border: 1.5px solid var(--rapport-border-2); + box-shadow: 0 2px 6px rgba(0,0,0,0.03); +} + +.rapport-btn-secondary:hover { + background: rgba(255,255,255,0.5); + border-color: var(--rapport-text-3); + color: var(--rapport-text) !important; + transform: translateY(-2px); + box-shadow: + 0 6px 14px rgba(0,0,0,0.06), + 0 12px 28px rgba(0,0,0,0.04); +} + +.rapport-btn-secondary:active { + transform: translateY(0); +} + +/* Dark-Mode-Varianten */ +.dark .rapport-btn-primary { + background: #2a2722; + color: #ece9e3 !important; + border-color: #3a3530; + box-shadow: + 0 1px 0 rgba(255,255,255,0.04) inset, + 0 2px 4px rgba(0,0,0,0.30), + 0 10px 24px rgba(0,0,0,0.45); +} + +.dark .rapport-btn-primary:hover { + background: #322e28; + color: var(--rapport-accent) !important; + box-shadow: + 0 1px 0 rgba(255,255,255,0.04) inset, + 0 4px 8px rgba(0,0,0,0.40), + 0 16px 32px rgba(0,0,0,0.50), + 0 24px 56px rgba(176,120,72,0.22); +} + +.dark .rapport-btn-secondary { + border-color: #3a3530; + color: #b8b2a8 !important; +} + +.dark .rapport-btn-secondary:hover { + background: rgba(255,255,255,0.04); + border-color: #6a6258; + color: #ece9e3 !important; +} + +/* — Hero-Badge — */ +.hextra-badge { + background: var(--rapport-surface) !important; + border: 1px solid var(--rapport-border) !important; + border-radius: 20px !important; + padding: 5px 14px !important; + font-size: 10px !important; + letter-spacing: 0.12em !important; + color: var(--rapport-text-4) !important; + text-transform: uppercase !important; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif !important; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.04); +} + +/* — Feature-Cards — */ +.hextra-feature-card { + background: var(--rapport-surface) !important; + border: 1px solid var(--rapport-border) !important; + border-radius: 14px !important; + transition: box-shadow 0.2s, transform 0.2s, border-color 0.2s !important; +} + +.hextra-feature-card:hover { + border-color: var(--rapport-border-2) !important; + transform: translateY(-2px); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.09), 0 2px 8px rgba(0, 0, 0, 0.05) !important; +} + +.hextra-feature-card h3 { + font-family: 'Playfair Display', serif !important; + color: var(--rapport-text) !important; + font-weight: 700 !important; +} + +.hextra-feature-card p { + color: var(--rapport-text-2) !important; + font-size: 12px !important; + line-height: 1.8 !important; +} + +.dark .hextra-feature-card { + background: #211e1a !important; + border-color: #2d2926 !important; +} + +.dark .hextra-feature-card h3 { + color: #ece9e3 !important; +} + +.dark .hextra-feature-card p { + color: #b8b2a8 !important; +} + +/* — Generic Cards (Hextra shortcode) — */ +.hextra-card { + background: var(--rapport-surface); + border: 1px solid var(--rapport-border); + border-radius: 12px; + transition: border-color 0.2s, transform 0.2s; +} + +.hextra-card:hover { + border-color: var(--rapport-accent-2); + transform: translateY(-1px); +} + +/* — Code-Blocks — */ +.hextra-code-block { + background: #ffffff !important; + border: 1px solid var(--rapport-border) !important; + border-radius: 12px !important; + overflow: hidden; +} + +.hextra-code-block pre, +.hextra-code-block .highlight, +.hextra-code-block .chroma { + background: transparent !important; + border: none !important; + border-radius: 0 !important; + margin: 0 !important; +} + +pre { + background: var(--rapport-surface2); + border: none; +} + +code, pre, pre code, kbd, samp, tt { + font-family: ui-monospace, 'SF Mono', Menlo, Monaco, Consolas, 'Liberation Mono', monospace !important; +} + +code { + color: var(--rapport-accent-3) !important; + background: rgba(176, 120, 72, 0.10) !important; + padding: 2px 6px; + border-radius: 4px; + font-size: 0.9em; +} + +pre code { + color: var(--rapport-text) !important; + background: transparent !important; + padding: 0; + font-size: 0.9em; +} + +.dark .hextra-code-block { + background: #1f1c19 !important; + border-color: #2d2926 !important; +} + +.dark .hextra-code-block pre, +.dark .hextra-code-block .highlight, +.dark .hextra-code-block .chroma { + background: transparent !important; + border: none !important; +} + +.dark code { + color: var(--rapport-accent) !important; + background: rgba(176, 120, 72, 0.15) !important; +} + +.dark pre code { + color: #ece9e3 !important; +} + +/* — Callouts — */ +.hextra-callout { + background: var(--rapport-surface) !important; + border-color: var(--rapport-border-2) !important; +} + +/* — Footer — */ +.hextra-footer, +footer { + background: var(--rapport-surface2); + border-top: 1px solid var(--rapport-border); + color: var(--rapport-text-3); +} + +.hextra-footer a { + color: var(--rapport-text-2); +} + +.hextra-footer a:hover { + color: var(--rapport-accent); +} + +.dark .hextra-footer, +.dark footer { + background: #15130f; + border-top: 1px solid #2d2926; + color: #888880; +} + +/* — TOC — */ +.hextra-toc a { + color: var(--rapport-text-3); +} + +.hextra-toc a:hover, +.hextra-toc .active { + color: var(--rapport-accent) !important; +} + +/* Hextra-Sticky-Bottom-Fades (TOC "Nach oben" + Sidebar-Footer) — */ +/* der hardcoded weisse Fade passt nicht zum cremefarbenen RAPPORT-Bg */ +.hextra-toc div:has(> #backToTop), +[data-toggle-animation] { + background: var(--rapport-bg) !important; + box-shadow: none !important; + border-top-color: var(--rapport-border) !important; +} + +.dark .hextra-toc div:has(> #backToTop), +.dark [data-toggle-animation] { + background: #181614 !important; + border-top-color: #2d2926 !important; + box-shadow: none !important; +} + +/* — Search — */ +.hextra-search-wrapper input { + background: var(--rapport-surface) !important; + border: 1px solid var(--rapport-border) !important; + color: var(--rapport-text) !important; +} + +.hextra-search-wrapper input:focus { + border-color: var(--rapport-accent-2) !important; +} + +/* — Tabellen — */ +table { + border-color: var(--rapport-border) !important; +} + +thead { + background: var(--rapport-surface) !important; +} + +th, td { + border-color: var(--rapport-border) !important; +} + +/* ───────────────────────────────────────────────────────────── + RAPPORT-LOGO-KARTE (Hero) — Dark Card auf hellem Grund + ───────────────────────────────────────────────────────────── */ +.rapport-logo-card { + background: var(--rapport-dark); + border: 1px solid var(--rapport-dark2); + border-radius: 999px; + padding: 28px 64px 26px; + display: inline-block; + box-shadow: 6px 0 20px rgba(0, 0, 0, 0.18), 0 6px 16px rgba(0, 0, 0, 0.12); + text-align: center; + margin: 0 auto 32px; +} + +.rapport-logo-text { + font-family: 'Krungthep', 'Archivo Black', sans-serif !important; + font-size: 48px; + letter-spacing: -0.02em; + color: #f0ede8; + line-height: 0.95; + font-weight: 400; +} + +.rapport-logo-sub { + font-size: 9px; + letter-spacing: 0.15em; + color: #f0ede8; + text-transform: uppercase; + margin-top: 8px; + font-weight: 500; +} + +/* — Hero-Meta-Pillen — */ +.rapport-meta { + display: flex; + gap: 6px; + align-items: center; + justify-content: center; + flex-wrap: wrap; + margin-top: 32px; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; +} + +.rapport-meta-item { + font-size: 10px; + letter-spacing: 0.08em; + color: var(--rapport-text-4); + text-transform: uppercase; + padding: 0 10px; + border-right: 1px solid var(--rapport-border); +} + +.rapport-meta-item:last-child { + border-right: none; +} + +/* — Status-Badges (in Arbeit / Geplant / Stabil / Neu) — */ +/* Einheitlich: weisse Pill mit dunklem Text — wie Download-Button */ +.rapport-status, +.rapport-status.active, +.rapport-status.planned, +.rapport-status.stable, +.rapport-status.new { + display: inline-block; + font-size: 10px; + letter-spacing: 0.12em; + text-transform: uppercase; + padding: 5px 14px; + border-radius: 999px; + margin-bottom: 12px; + font-weight: 600; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; + background: #ffffff; + color: var(--rapport-text); + border: 1px solid var(--rapport-border); + box-shadow: + 0 1px 0 rgba(255,255,255,0.9) inset, + 0 1px 2px rgba(0,0,0,0.04), + 0 3px 8px rgba(0,0,0,0.06); +} + +.dark .rapport-status, +.dark .rapport-status.active, +.dark .rapport-status.planned, +.dark .rapport-status.stable, +.dark .rapport-status.new { + background: #2a2722; + color: #ece9e3; + border-color: #3a3530; + box-shadow: + 0 1px 0 rgba(255,255,255,0.04) inset, + 0 1px 2px rgba(0,0,0,0.30), + 0 4px 10px rgba(0,0,0,0.35); +} + +/* — Stack-Bar (am Footer) — */ +.rapport-stack-bar { + padding: 20px 0; + border-top: 1px solid var(--rapport-border); + display: flex; + align-items: center; + gap: 20px; + flex-wrap: wrap; + margin-top: 32px; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; +} + +.rapport-stack-label { + font-size: 10px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--rapport-text-4); + flex-shrink: 0; +} + +.rapport-stack-items { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.rapport-stack-item { + font-size: 10px; + letter-spacing: 0.06em; + color: var(--rapport-text-3); + background: var(--rapport-surface); + border: 1px solid var(--rapport-border); + border-radius: 6px; + padding: 4px 10px; +} + +/* — Reduce default content max-width slightly for monospace lines — */ +.content article { + max-width: 100%; +} + +/* ───────────────────────────────────────────────────────────── + HOME-HERO — zentriertes Layout wie auf der alten Website + ───────────────────────────────────────────────────────────── */ + +/* Inhalt der home Page zentrieren */ +.hextra-home { + align-items: center !important; + text-align: center; + max-width: 100%; +} + +/* Subtitle zentriert, schmaler max-width, Playfair Display */ +.hextra-home > p, +.hextra-home .not-prose.hx\:text-xl { + font-family: 'Playfair Display', serif !important; + font-size: clamp(16px, 2.2vw, 24px) !important; + font-weight: 400 !important; + color: var(--rapport-text-3) !important; + max-width: 560px !important; + margin-left: auto !important; + margin-right: auto !important; + line-height: 1.55 !important; + text-align: center; +} + +/* Buttons immer in einer zentrierten Zeile */ +.hextra-home > div.hx\:flex, +.hextra-home > .hx\:flex { + justify-content: center; + align-items: center; +} + +/* Hero-Button matches RAPPORT design (dark on cream) */ +.hextra-home .hextra-hero-button, +a.hextra-hero-button { + background: var(--rapport-dark); + color: #f0ede8 !important; + border-radius: 20px; + padding: 13px 28px; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif !important; + font-size: 11px !important; + font-weight: 500 !important; + letter-spacing: 0.07em !important; + text-transform: uppercase !important; + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.20); + transition: all 0.18s; + display: inline-flex; + align-items: center; + gap: 9px; +} + +/* Feature-Grid linksbündig (Text in Cards) */ +.hextra-home .hextra-feature-grid, +.hextra-home .rapport-stack-bar { + text-align: left; + width: 100%; +} + +.hextra-home .hextra-feature-card * { + text-align: left; +} + +.rapport-stack-bar { + justify-content: flex-start; + text-align: left; +} + +/* Section-Heading & Eyebrow vor dem Feature-Grid zentrieren */ +.hextra-home h2, +.hextra-home > div > h2 { + text-align: center; + margin-left: auto; + margin-right: auto; +} + +/* RAPPORT-Logo bleibt zentriert */ +.hextra-home .rapport-logo-card { + margin-left: auto; + margin-right: auto; + display: block; +} + +/* Hero-Badge zentrieren */ +.hextra-home .hextra-badge, +.hextra-home > p:has(.hextra-badge) { + margin-left: auto; + margin-right: auto; + display: inline-flex !important; +} + +/* Hero-Background-Glow — sanfter Tan-Halo */ +body:has(.hextra-home)::before { + content: ""; + position: fixed; + top: 5%; + left: 50%; + transform: translateX(-50%); + width: 720px; + height: 720px; + background: radial-gradient(circle, rgba(176, 120, 72, 0.08) 0%, transparent 60%); + pointer-events: none; + z-index: 0; +} + +.dark body:has(.hextra-home)::before { + background: radial-gradient(circle, rgba(176, 120, 72, 0.12) 0%, transparent 60%); +} + +/* Navbar-Logo — RAPPORT-Schriftzug in Krungthep, etwas grösser */ +.hextra-navbar-title, +nav a[href="/"] span, +nav [class*="font-bold"] { + font-family: 'Krungthep', 'Archivo Black', sans-serif !important; + letter-spacing: -0.02em !important; + font-weight: 400 !important; + font-size: 23px !important; + line-height: 1 !important; +} + +/* RAPPORT-Meta-Pillen zentriert */ +.rapport-meta { + justify-content: center; + text-align: center; +} diff --git a/public/css/variables.css b/public/css/variables.css new file mode 100644 index 0000000..f4f9ef7 --- /dev/null +++ b/public/css/variables.css @@ -0,0 +1,26 @@ +/* Hugo template to derive CSS variables from site and page parameters */ + +/* Do not remove the following comment. It is used by Hugo to render CSS variables.*/ + +:root { + --hextra-max-page-width: 80rem; + --hextra-max-content-width: 72rem; + --hextra-max-navbar-width: 80rem; + --hextra-max-footer-width: 80rem; +} + +.hextra-max-page-width { + max-width: var(--hextra-max-page-width); +} + +.hextra-max-content-width { + max-width: var(--hextra-max-content-width); +} + +.hextra-max-navbar-width { + max-width: var(--hextra-max-navbar-width); +} + +.hextra-max-footer-width { + max-width: var(--hextra-max-footer-width); +} diff --git a/public/de.search-data.json b/public/de.search-data.json new file mode 100644 index 0000000..98a9343 --- /dev/null +++ b/public/de.search-data.json @@ -0,0 +1 @@ +{"/docs/":{"data":{"":"Vollständige Anleitung zu RAPPORT — von der Installation über den täglichen Arbeitsablauf bis zur Cloud-Variante und Eigen-Builds.","erste-schritte#Erste Schritte":"Quick-StartIn sechs Schritten von Null zur ersten Rechnung. InstallationmacOS, Gatekeeper, Signatur, geplante Plattformen. EinrichtungBürodaten, Mitarbeiter, Kunden, Projekte initial anlegen.","für-fortgeschrittene#Für Fortgeschrittene":"Web-Modus (Multi-User)Rapport im Browser via Supabase — für Studios mit mehreren Nutzern. Entwicklung \u0026 BuildAus dem Quellcode kompilieren, beitragen, eigenes Release. ChangelogVersionsgeschichte und Breaking Changes.","hilfe--support#Hilfe \u0026amp; Support":"Bei Bugs oder weiteren Fragen → Issue auf Gitea . Siehe auch die FAQ für häufige Fragen.","im-alltag#Im Alltag":"Typischer ArbeitsablaufKunde → Offerte → Projekt → Zeit → Rechnung. Datenhaltung \u0026 BackupWo die Daten liegen, wie du sie sicherst und wiederherstellst. TroubleshootingApp startet nicht, Daten weg, Update hängt."},"title":"Dokumentation"},"/docs/arbeitsablauf/":{"data":{"":"Vom Erstkontakt mit dem Kunden bis zur Schlussrechnung — der typische Weg eines Projekts durch Rapport.","1--kunde-anlegen#1 · Kunde anlegen":"Kunden → Neu — siehe Einrichtung § 3.\nFalls der Kunde später bestellt, lassen sich alle gesammelten Daten (Adresse, Honorartyp) direkt übernehmen.","2--offerte-erstellen#2 · Offerte erstellen":"Offerten → Neu\nFeld Inhalt Kunde aus Kundendatenbank wählen Bezeichnung Projekttitel (z. B. “Einfamilienhaus Müller, Bern”) Honorartyp Stundensatz / SIA 102 / Pauschal Bauschätzwert bei SIA — Bruttowert in CHF Phasen bei SIA — anzuklickende Phasen Positionen bei Stundensatz/Pauschal — Position, Beschreibung, Betrag Mit PDF exportieren — fertige Offerte für den Kunden.","3--offerte--projekt#3 · Offerte → Projekt":"Bei Status “Angenommen”: Knopf “In Projekt konvertieren”.\nDas erzeugt:\nEin neues Projekt mit den Daten der Offerte (Kunde, Bezeichnung, Honorar, Phasen) Eine Verknüpfung zwischen Offerte und Projekt Die Offerte selbst bleibt im Archiv erhalten Siehe auch Projekt-Feature.","4--zeit-erfassen#4 · Zeit erfassen":"Zeit → Mitarbeiter wählen → Woche navigieren\nKlick auf einen Halbstunden-Slot → Eintrag erstellen Drag über mehrere Slots → längerer Eintrag Projekt zuweisen (Pflichtfeld) — aus aktiven Projekten SIA-Phase zuweisen (optional, für detaillierte Auswertung)","5--akonto-rechnung#5 · Akonto-Rechnung":"Während der Projektlaufzeit — typisch nach jeder abgeschlossenen SIA-Phase oder monatlich.\nRechnungen → Neu → Akonto\nFeld Inhalt Projekt aus aktiven Projekten Bezug “Akonto für Phase 31 — Vorprojekt” Betrag Stundensatz × Stunden, oder Phasenanteil × Bausumme Fälligkeit i. d. R. 30 Tage Rapport zieht automatisch die geleisteten Stunden aus der Zeiterfassung — die kannst du als Basis nehmen oder überschreiben.\nPDF inkl. QR-Einzahlungsschein — siehe Rechnungen-Feature.","6--schlussrechnung#6 · Schlussrechnung":"Nach Projektabschluss — Differenz aus Gesamthonorar minus aller Akonto-Beträge.\nRechnungen → Neu → Schlussrechnung\nRapport rechnet die bisherigen Akonto-Rechnungen automatisch ab:\nGesamthonorar (SIA / Pauschal / Stundensatz) − Akonto-Rechnung 1 − Akonto-Rechnung 2 − Akonto-Rechnung 3 = Schlussrechnung","7--projektabschluss#7 · Projektabschluss":"Im Projekt → Status auf “Abgeschlossen”.\nDas Projekt bleibt für historische Auswertungen sichtbar, taucht aber nicht mehr in der Zeiterfassungs-Auswahl auf.","auswertungen#Auswertungen":"Wöchentlich / monatlich / am Jahresende:\nZeit → Auswertungen — Stunden pro Mitarbeiter, pro Projekt, pro Phase Rechnungen → Übersicht — offene Beträge, bezahlt, Mahnungen Buchhaltung → Erfolgsrechnung — Einnahmen vs. Ausgaben Mitarbeiter → Lohnabrechnung — monatlich","offerten-status#Offerten-Status":"Status Bedeutung Entwurf noch nicht versandt Versandt beim Kunden, wartet auf Antwort Angenommen Kunde hat zugesagt — bereit zur Konvertierung Abgelehnt Kunde hat abgelehnt — Archiv","spezialfälle#Spezialfälle":"Ferien — eigener “Projekt”-Typ “Ferien”, in der Auswertung separat Krankheit/Absenz — eigener “Projekt”-Typ, ebenfalls separat Interne Stunden — z. B. Verwaltung, Marketing, IT — eigene interne Projekte","tipps-aus-dem-alltag#Tipps aus dem Alltag":"Zeit jeden Tag erfassen statt rückwirkend — sonst gehen Details verloren Akonto regelmässig statt einmal am Schluss — Liquidität Backups vor Jahresabschluss — siehe Datenhaltung Briefbogen-Logo in hoher Auflösung — sieht im PDF besser aus","übersicht#Übersicht":"Kunde → Offerte → Projekt → Zeit → Akonto → Schluss anlegen erstellen (aus Offerte) erfassen -Rechnung -Rechnung ↓ QR-Schein"},"title":"Arbeitsablauf"},"/docs/changelog/":{"data":{"":"Versionsgeschichte von RAPPORT. Aktuelle Releases: Gitea .","01x--initial#0.1.x — Initial":"Neu\nErste Setup-Routine (Bürodaten, Mitarbeiter, Kunden) Briefe und Lieferscheine Tauri-2-Bundle für macOS","02x--zeiterfassung#0.2.x — Zeiterfassung":"Neu\nWochenraster mit Halbstunden-Slots Drag \u0026 Drop zur Slot-Erfassung Projekt-Zuweisung pro Eintrag Auswertungen pro Mitarbeiter und Projekt","03x--rechnungen--qr#0.3.x — Rechnungen \u0026amp; QR":"Neu\nRechnungsmodul mit QR-Einzahlungsschein (via swissqrbill) Akonto-, Teil- und Schlussrechnungen PDF-Export mit Bürobriefbogen Stundensatz-Rechnungen ziehen direkt aus der Zeiterfassung","04x--projekte--sia-102#0.4.x — Projekte \u0026amp; SIA 102":"Neu\nProjektverwaltung nach SIA 102 Vorgeschlagene Phasen-Anteile am Gesamthonorar Bauschätzwert-basiertes Honorar Verbessert\nZeit-Auswertung pro SIA-Phase Akonto- und Schlussrechnungen mit automatischer Differenzberechnung","05x--mitarbeiter--lohn#0.5.x — Mitarbeiter \u0026amp; Lohn":"Neu\nMitarbeiterverwaltung mit Pensum, Stundensatz, Ferienanspruch Lohnabrechnung mit AHV/IV/EO, ALV, BVG, NBU Jahresabschluss mit Überstundenausgleich Ferien-Prorata bei Eintritt unter Jahr","06x--spesen--buchhaltung#0.6.x — Spesen \u0026amp; Buchhaltung":"Neu\nSpesenerfassung mit Beleg-Upload (Base64 in localStorage) Jahresbudget mit Einnahmen-/Ausgaben-Übersicht Vereinfachte Erfolgsrechnung pro Geschäftsjahr Verbessert\nLohnabrechnung integriert Spesen-Erstattungen CSV-Export aus der Zeiterfassung","070--auto-updater--system-tray#0.7.0 — Auto-Updater \u0026amp; System-Tray":"Neu\nAuto-Updater — Rapport prüft beim Start auf neue Versionen und installiert Updates signiert über Tauri. Einzelne Versionen können übersprungen werden. (Doku) System-Tray — Schnellzugriff über die Menüleiste mit Hide-on-Close. Beim Schliessen läuft Rapport im Hintergrund weiter, Cmd+Q beendet die App vollständig. (Doku) Quick-Open der letzten 5 Projekte direkt aus dem Tray-Menü Verbessert\nSchnellerer App-Start durch lazy-geladene Views Klarere Statusbadges in der Projekt-Übersicht","080082--patch-releases#0.8.0–0.8.2 — Patch-Releases":"Patch-Reihe mit kleineren Verbesserungen und Bugfixes. Details siehe Releases auf Gitea .","083--aktuelle-version-aktuell#0.8.3 — Aktuelle Version \u003cspan class=\"rapport-status new\"\u003eAktuell\u003c/span\u003e":"Veröffentlicht am 2026-05-24.\nNeu / Verbessert\nDiverse Verbesserungen und Bugfixes (Details werden im Release auf Gitea gepflegt) Bekannte Einschränkungen\nBuilds sind Tauri-signiert, aber noch nicht Apple-notarisiert — siehe Installation § Gatekeeper Linux- und Windows-Builds noch nicht verfügbar","roadmap#Roadmap":"Geplant — keine konkreten Termine:\nLinux-Build (Tauri 2 unterstützt es, Bedarf nötig) Windows-Build (analog) Cloud-Modus Stable (Supabase) — derzeit in Web-Modus verfügbar, aber experimentell Französische / Italienische Übersetzung PostgreSQL-Migration aus localStorage (Knopf in der App) Mobile App (iOS Companion zur Zeiterfassung) — offen Wünsche oder Prioritäten → Issue auf Gitea ."},"title":"Changelog"},"/docs/datenhaltung/":{"data":{"":"Wo Rapport seine Daten speichert, wie du sie sicherst und wiederherstellst.\nDiese Seite beschreibt die Desktop-App (Single-User). Wer im Team arbeitet und Rapport gegen einen Rapport Server betreibt, sichert stattdessen die Postgres-Datenbank — siehe Rapport Server § Backup.","a--einfach-manuell#A · Einfach (manuell)":"Den ganzen Ordner kopieren:\ncp -R \"~/Library/Application Support/com.rapport.app\" \\ \"~/Documents/Rapport-Backup-$(date +%Y%m%d)\" → Auf USB-Stick, externen Datenträger oder in die Cloud.","b--time-machine#B · Time Machine":"Wenn Time Machine läuft, ist der Ordner automatisch dabei. Versionierung inbegriffen.\nEinschränkung: Time Machine sichert nur lokal/USB. Für off-site-Backup separat sorgen.","backup-strategien#Backup-Strategien":"","c--cron-job-täglich-automatisch#C · Cron-Job (täglich automatisch)":"# In ~/Library/LaunchAgents/com.rapport.backup.plist hinterlegen # oder als crontab-Eintrag: 0 22 * * * rsync -a \"$HOME/Library/Application Support/com.rapport.app/\" \\ \"$HOME/Backups/rapport/$(date +\\%Y\\%m\\%d)/\"","d--icloud-drive-off-site#D · iCloud Drive (off-site)":"Application-Support liegt nicht automatisch in iCloud. Wer das will:\n# Symlink anlegen mkdir -p \"~/iCloud Drive/Rapport\" ln -s \"~/Library/Application Support/com.rapport.app\" \"~/iCloud Drive/Rapport/data\" Achtung: iCloud-Sync mit aktiver App kann zu Race-Conditions führen. Besser den Sync zeitversetzt (z. B. nachts via Cron).","datenmenge#Datenmenge":"Typische Grössen pro Bürojahr:\nInhalt Grösse Logo (PNG/SVG) 50 KB – 1 MB 1 Jahr Zeiterfassung (1 MA) ~ 80 KB 1 Jahr Zeiterfassung (5 MA) ~ 400 KB 50 Projekte mit je 5 Rechnungen ~ 800 KB Total typisches Solo-Büro / Jahr ~ 1–2 MB localStorage hat Limits (i. d. R. ~10 MB pro Origin). Für Solo-Büros reicht das problemlos für viele Jahre. Wer das Limit erreicht oder im Team arbeitet → Rapport Server.","export-funktionen-in-der-app#Export-Funktionen (in der App)":"Aus Rapport selbst:\nWas Wo Format Zeit-Auswertung Zeit → Export CSV Rechnung Rechnung → PDF PDF (inkl. QR) Offerte Offerte → PDF PDF Lohnabrechnung Mitarbeiter → PDF PDF Jahres-Buchhaltung Buchhaltung → Export CSV Die Exports sind für externe Verwendung (Buchhalter, Treuhänder, Archiv) gedacht — kein Full-Backup.","macos#macOS":"~/Library/Application Support/com.rapport.app/ Dort liegt eine einzelne localStorage-Datenbank des WebView, in der alle Rapport-Daten als JSON unter dem Key studio_data_v1 gespeichert sind:\nBürodaten, Logo, IBAN Mitarbeiter, Kunden, Projekte, Offerten Zeit-Einträge, Rechnungen Spesen, Lohnabrechnungen, Protokolle App-Einstellungen Konsequenz: Wer den Application-Support-Ordner kopiert, hat ein vollständiges Backup. Wer ihn löscht, verliert alle Daten.","schema-migrationen#Schema-Migrationen":"Bei Updates kann sich das Datenformat ändern. Rapport hat einen Migrations-Mechanismus: beim Start prüft die App, ob das gespeicherte Format dem aktuellen entspricht, und migriert es automatisch.\nCode: src/storage/migrations.js .\nEmpfehlung: Vor jedem grösseren Update ein Backup machen — Migrationen sind getestet, aber 100%-Sicherheit gibt es nicht.","selektiv-nur-einzelne-daten#Selektiv (nur einzelne Daten)":"Da alle Daten in einem JSON unter studio_data_v1 liegen, ist selektive Wiederherstellung manuell:\nBackup-localStorage-Datei öffnen (WebKit-Format → mit Tool wie [WebKit Storage Inspector] lesen, oder via Rapport DevTools) Gewünschte Felder in die aktuelle Instanz übertragen In der Praxis: meistens lohnt sich die vollständige Wiederherstellung mehr.","speicherort-desktop-app#Speicherort (Desktop-App)":"Die Desktop-App speichert alles lokal — keine Cloud, kein Server.","vollständig-rapport-komplett-tot#Vollständig (Rapport komplett tot)":"Rapport beenden (Cmd+Q, nicht nur Fenster zu) Aktuellen Ordner umbenennen (falls noch da): mv \"~/Library/Application Support/com.rapport.app\" \\ \"~/Library/Application Support/com.rapport.app.bak\" Backup-Ordner zurück kopieren: cp -R \"~/Documents/Rapport-Backup-20260523\" \\ \"~/Library/Application Support/com.rapport.app\" Rapport starten","warum-localstorage#Warum localStorage?":"In der Desktop-App ist Rapport eine Single-User-Anwendung. localStorage ist dafür:\nSchnell — keine Datenbank-Roundtrips Einfach — keine Migration nötig, JSON-Schema im Code Portabel — eine Datei → ein Backup Für Multi-User-Betrieb existiert Rapport Server — Postgres + Auth + Realtime in einem Docker-Compose. Wechsel zwischen Desktop- und Server-Modus erfolgt im Login-Bildschirm der App.","was-wird-nicht-gespeichert#Was wird \u003cstrong\u003enicht\u003c/strong\u003e gespeichert?":"WebView-Cache — ~/Library/Caches/com.rapport.app/ und ~/Library/WebKit/com.rapport.app/ sind sicher zu löschen (UI-Caches, regenerieren sich) App-Updates — werden bei Bedarf erneut runtergeladen Logs — ~/Library/Logs/com.rapport.app/ (geplant, derzeit nicht geschrieben)","wiederherstellung#Wiederherstellung":""},"title":"Datenhaltung"},"/docs/einrichtung/":{"data":{"":"Nach der Installation: Bürodaten, Mitarbeiter, Kunden und Projekte initial anlegen.","1--bürodaten#1 · Bürodaten":"Einstellungen → Bürodaten\nFeld Beschreibung Verwendung Name Bürobezeichnung Briefbogen, Login-Screen Adresse Strasse, PLZ, Ort Briefbogen, QR-Rechnung (Empfänger) Telefon, E-Mail Kontaktdaten Briefbogen, Rechnung-Footer IBAN CH-IBAN (Format CH XX XXXX …) QR-Einzahlungsschein — Pflicht Logo PNG/SVG-Upload Briefbogen, Rechnung, Brief MwSt.-Nr. optional Rechnung-Footer Tipp: Das Logo wird hochauflösend gespeichert (Base64 im localStorage). Bei sehr grossen Dateien (\u003e1 MB) vorher in Vorschau verkleinern.","2--mitarbeiter#2 · Mitarbeiter":"Einstellungen → Mitarbeiter → Neu\nFeld Beschreibung Name, Vorname wird in Zeiterfassung \u0026 Lohn verwendet Initialen Kürzel für Auswertungen (z. B. “KGE”) Eintrittsdatum für Prorata der Ferien Pensum (%) 100 = Vollzeit Stundensatz (CHF) für Stundensatz-Rechnungen Ferienanspruch (Tage/Jahr) i. d. R. 25–30 Lohn (brutto, monatlich) optional, für Lohnabrechnung Mindestens ein Mitarbeiter (z. B. der Inhaber selbst) muss angelegt sein, sonst lässt sich keine Zeit erfassen.","3--kunden#3 · Kunden":"Kunden → Neu\nFeld Beschreibung Typ Privatperson / Firma Anrede, Name für Brief / Rechnung Adresse Strasse, PLZ, Ort, Land Ansprechperson bei Firmen Telefon, E-Mail Kontakt Honorartyp Default Stundensatz / SIA / Pauschal Stundensatz falls vom Bürostandard abweichend MwSt.-pflichtig ja/nein","4--projekte#4 · Projekte":"Projekte → Neu\nFeld Beschreibung Projekt-Nr. freie Form, oder generiert (2026-001) Bezeichnung Kurztitel Standort Adresse Kunde aus Kundendatenbank Bauschätzwert für SIA-Phasen-Honorar SIA-Phasen Vorprojekt / Bauprojekt / … — alle anwählbar Honorartyp Stundensatz / SIA 102 / Pauschal Status aktiv / pausiert / abgeschlossen","checkliste#Checkliste":"Nach diesen vier Schritten ist Rapport einsatzbereit:\nBürodaten inkl. IBAN erfasst Mindestens ein Mitarbeiter angelegt Erster Kunde angelegt Erstes Projekt angelegt Eine Test-Zeitbuchung erfasst — wird das Projekt korrekt zugewiesen? Eine Test-Rechnung erstellt — kommt der QR-Schein sauber raus? Wenn alles funktioniert: Typischer Arbeitsablauf.","reihenfolge#Reihenfolge":"Die Reihenfolge ist wichtig — jede Stufe baut auf der vorherigen auf:\n1. Bürodaten → 2. Mitarbeiter → 3. Kunden → 4. Projekte ▼ ▼ ▼ ▼ Briefbogen, Zeiterfassung, Adressen, Zeiterfassung, QR-Schein, Lohn Rechnungen Rechnungen Login","sia-102-phasen#SIA-102-Phasen":"Wenn als Honorartyp SIA 102 gewählt ist, werden die Phasen-Anteile am Gesamthonorar vorgeschlagen — siehe Projekt-Feature.","sozialabzüge-optional#Sozialabzüge (optional)":"In Einstellungen → Lohn:\nAbzug Standardwert (CH) AHV / IV / EO 5,3 % ALV 1,1 % BVG (Pensionskasse) variabel — je Mitarbeiter NBU je nach Versicherung Die Standardsätze sind hinterlegt, können aber überschrieben werden."},"title":"Einrichtung"},"/docs/entwicklung/":{"data":{"":"Aus dem Quellcode kompilieren, beitragen, eigenes Release erzeugen.","architektur-in-einem-absatz#Architektur in einem Absatz":"RAPPORT ist eine monolithische SPA: ein React-Root in App.jsx hält den gesamten App-State in einem useState({...}), persistiert ihn synchron in localStorage unter studio_data_v1, und übergibt ihn als Props an lazy-geladene Views. Kein Routing-Framework, kein State-Library, kein TypeScript, kein CSS-Framework. Der Rust-Teil ist 109 Zeilen und macht nur drei Dinge: System-Tray, Window-Hide-on-Close, Plugin-Registrierung (Updater, Process, Log). Keine #[tauri::command] — Frontend ↔ Backend kommuniziert nur über das Event rapport:navigate (Tray → Frontend).\nDetaillierte Karte: ARCHITECTURE.md .","beitragen#Beitragen":"Issues \u0026 Pull Requests sind willkommen. Wertvoll sind:\nBug-Reports mit Reproduktionsschritten Workflow-Verbesserungen aus dem realen Büroalltag Vorlagen (Briefe, Protokolle, Lieferscheine) für andere Büros Übersetzungen / Internationalisierung (FR, IT) Linux-/Windows-Builds und plattformspezifische Fixes Vor grösseren Änderungen → Issue zum Diskutieren.","build#Build":"","desktop-tauri-dmg#Desktop (Tauri DMG)":"npm run build # erst Vite-Build (dist/) npx tauri build # dann Tauri-Bundle (DMG) Output: src-tauri/target/release/bundle/dmg/Rapport__aarch64.dmg","entwicklung#Entwicklung":"","konventionen#Konventionen":"JavaScript statt TypeScript — bewusste Entscheidung für Solo-Dev-Velocity Inline-Styles statt CSS-Framework kein Routing-Framework — view-State in App.jsx triggert Komponente JSON-Schema implizit — definiert durch defaultData in constants.js Migrationen als reine Funktionen in storage/migrations.js","lizenz#Lizenz":"GNU AGPL-3.0-or-later — eigene Builds erlaubt, Modifikationen müssen unter derselben Lizenz veröffentlicht werden, wenn die Software als Service angeboten wird.","native-window-tauri-fenster-mit-desktop-integration#Native Window (Tauri-Fenster mit Desktop-Integration)":"npx tauri dev Echtes Tauri-Fenster System-Tray, Updater, native APIs verfügbar Erster Start dauert lange (Rust-Compile)","release-workflow#Release-Workflow":"# 1 · Version bumpen in package.json + Cargo.toml + tauri.conf.json ./scripts/release.sh 0.7.1 # 2 · Build mit Signatur TAURI_SIGNING_PRIVATE_KEY=$(cat ~/.tauri/rapport-key) \\ TAURI_SIGNING_PRIVATE_KEY_PASSWORD=… \\ npx tauri build # 3 · latest.json aktualisieren mit neuer Signatur # 4 · DMG + latest.json auf Gitea Releases hochladen","setup#Setup":"git clone https://git.kgva.ch/karim/RAPPORT.git cd RAPPORT/APP npm install","verzeichnis-karte#Verzeichnis-Karte":"APP/ ├── src/ React 19 (kein TS, nur .jsx) │ ├── App.jsx Root: State, Navigation, Sidebar, Modals │ ├── constants.js STORAGE_KEY, NAV_ITEMS, defaultData, SIA-Phasen │ ├── utils.js Business-Logik: Kalkulation, QR-Bill, CSV, Lohn │ ├── storage/adapter.js LocalStorageAdapter (Phase 1), SupabaseAdapter (Phase 2) │ ├── storage/migrations.js Schema-Migrationen │ ├── views/ 20 Top-Level-Screens, lazy-geladen │ ├── components/UI.jsx StatusBadge, Modal, FormField, … │ ├── components/Update* Auto-Update-UI │ ├── print/ PrintComponents.jsx — alle Druckansichten │ └── utils/updater.js @tauri-apps/plugin-updater Wrapper │ ├── supabase/migrations/ Postgres-Schema für Cloud-Variante │ ├── src-tauri/ Rust-Backend (Tauri 2.10.3) │ ├── src/lib.rs ~103 Z. — Tray, Window-Events, Plugins │ ├── tauri.conf.json Updater-URL, Public-Key, CSP, Bundle-Targets │ └── capabilities/ Tauri Permissions │ ├── scripts/release.sh Build + Sign + latest.json erzeugen ├── latest.json Updater-Manifest └── deploy/ Docker-Compose für Web-Modus","voraussetzungen#Voraussetzungen":"Tool Version Node.js ≥ 20 (für Vite 8) npm ≥ 10 Rust toolchain ≥ 1.77.2 (via rustup) Plattform-Tools siehe Tauri Prerequisites Plattform-spezifisch:\nmacOS: Xcode Command Line Tools (xcode-select --install) Windows: Microsoft C++ Build Tools + WebView2 Linux: webkit2gtk-4.1, librsvg2-dev, libayatana-appindicator3-dev, build-essential","web-modus-hmr-schnellster-loop#Web-Modus (HMR, schnellster Loop)":"npm run dev # http://localhost:3000 Hot-Module-Replacement Schnellster Iteration-Loop für UI-Arbeit Datenpersistierung: Browser-localStorage","web-statisches-bundle#Web (statisches Bundle)":"npm run build # Output: dist/ (~500 KB) Für Hosting auf einem Static-Server (z. B. Nginx, Caddy, GitHub Pages). Siehe Web-Modus."},"title":"Entwicklung"},"/docs/erste-schritte/":{"data":{"":"Von der Installation bis zur ersten Rechnung — in sechs Schritten.","01--installation#01 · Installation":"DMG von Gitea Releases herunterladen. Rapport in den Programme-Ordner ziehen. Beim ersten Start: Systemeinstellungen → Datenschutz \u0026 Sicherheit öffnen und Rapport zulassen.\nDie Pre-Release-Builds sind signiert über Tauri, aber (noch) nicht über die Apple-Notarisierung gegangen — daher der manuelle Freigabe-Schritt.","02--einrichtung#02 · Einrichtung":"In den Einstellungen hinterlegen:\nBürodaten — Name, Adresse, IBAN, Logo Mitarbeiter — Namen, Pensum, Stundensatz, Ferienanspruch Kunden — Adresse, Ansprechperson, Honorartyp Projekte — SIA-102-Phasen, Budget, Beteiligte Danach ist die Zeiterfassung bereit.","03--zeiterfassung#03 · Zeiterfassung":"Im Modul Zeit:\nMitarbeiter wählen Woche navigieren Stunden per Klick oder Drag erfassen Jedem Eintrag ein Projekt zuweisen Auswertungen pro Mitarbeiter und pro Projekt sind unter Zeit → Auswertungen abrufbar. Halbe Tage und Mehrfacheinträge pro Slot werden unterstützt.","04--rechnungen#04 · Rechnungen":"Aus einer Offerte oder direkt erstellen:\nSIA-Phasen, Stundensatz oder Pauschal wählen Akonto-, Teil- oder Schlussrechnung Mit PDF exportieren wird die fertige Rechnung inkl. QR-Einzahlungsschein generiert Offerten lassen sich nahtlos in Projekte und Rechnungen konvertieren.","05--backup#05 · Backup":"In der Desktop-App liegen alle Daten als JSON im Applikationsordner:\n~/Library/Application Support/com.rapport.app/ Für ein Backup reicht es, diesen Ordner zu kopieren — z. B. auf einen externen Datenträger oder in die Cloud. Empfohlen: regelmässig (wöchentlich oder via Time Machine). Im Server-Modus läuft das Backup über Postgres-Dumps — siehe Rapport Server § Backup.","06--probleme-melden#06 · Probleme melden":"Ein Issue auf Gitea erstellen — mit kurzer Beschreibung, was passiert ist. Screenshots helfen. Bitte die Rapport-Version (links unten in der App) angeben.\nTipp: Wenn die App nicht mehr startet, hilft oft, den Cache-Ordner zu sichern und neu zu starten. Die JSON-Daten selbst bleiben unverändert."},"title":"Erste Schritte"},"/docs/installation/":{"data":{"":"Schritt-für-Schritt-Anleitung für die Installation der Desktop-App.","1--download#1 · Download":"Aktueller Build: Downloads-Seite oder direkt Releases auf Gitea .\nDatei Plattform RAPPORT__aarch64.dmg macOS Apple Silicon RAPPORT__x86_64.dmg macOS Intel (auf Anfrage)","2--dmg-öffnen--installieren#2 · DMG öffnen \u0026amp; installieren":"DMG doppelklicken Rapport.app in den Applications-Ordner ziehen DMG auswerfen","3--erster-start-macos-gatekeeper#3 · Erster Start (macOS Gatekeeper)":"Beim ersten Start verweigert macOS den Start, weil die Pre-Release-Builds Tauri-signiert, aber (noch) nicht Apple-notarisiert sind.","4--erstes-setup#4 · Erstes Setup":"Beim ersten Start zeigt Rapport den Setup-Bildschirm. Hier werden die Stammdaten erfasst — siehe Einrichtung.","5--update-strategie#5 · Update-Strategie":"Ab Version 0.7.0 prüft Rapport beim Start automatisch auf neue Versionen (siehe Auto-Updater). Updates können in den Einstellungen deaktiviert werden.\nManuelle Updates: einfach das neuere DMG installieren — die Daten bleiben erhalten, da sie unabhängig von der App liegen (siehe Datenhaltung).","alternative-terminal#Alternative (Terminal)":"Falls der GUI-Weg nicht funktioniert:\nxattr -d com.apple.quarantine /Applications/Rapport.app Das entfernt das Quarantäne-Flag und macOS akzeptiert den Start.","bekannte-probleme#Bekannte Probleme":"Problem Lösung App lässt sich nicht öffnen trotz Freigabe Terminal-Variante mit xattr “Rapport is damaged” DMG erneut von Gitea ziehen (Browser-Cache hat evtl. Müll) Schwarzer Bildschirm beim Start ~/Library/WebKit/com.rapport.app löschen, neu starten Mehr unter Troubleshooting.","deinstallation#Deinstallation":"# App entfernen rm -rf /Applications/Rapport.app # Daten entfernen (optional!) rm -rf \"~/Library/Application Support/com.rapport.app\" # Caches rm -rf \"~/Library/Caches/com.rapport.app\" rm -rf \"~/Library/WebKit/com.rapport.app\" Achtung: Schritt 2 löscht alle Rapport-Daten unwiederbringlich. Vorher Backup machen — siehe Datenhaltung.","lösung#Lösung":"Systemeinstellungen → Datenschutz \u0026 Sicherheit öffnen Bis ganz nach unten scrollen — es erscheint: “Rapport” wurde blockiert, da es nicht von einem identifizierten Entwickler stammt.\nAuf “Trotzdem öffnen” klicken Bestätigen Ab dem zweiten Start läuft Rapport ohne Rückfragen.","voraussetzungen#Voraussetzungen":"Plattform Status Versionen macOS Apple Silicon (M1 – M4) ✅ Unterstützt macOS 12+ macOS Intel ⚠ Build auf Anfrage macOS 12+ Linux 🕐 Geplant — Windows 🕐 Geplant — Eine Portierung auf Linux und Windows ist mit Tauri 2 möglich. Issue erstellen , wenn du eine Plattform brauchst."},"title":"Installation"},"/docs/troubleshooting/":{"data":{"":"Typische Probleme und Lösungen. Wenn dein Problem nicht dabei ist → Issue auf Gitea .","app-startet-nicht#App startet nicht":"","app-startet-zeigt-aber-schwarzen-bildschirm#App startet, zeigt aber schwarzen Bildschirm":"Ursache: WebView-Cache korrupt.\nLösung:\nrm -rf \"~/Library/Caches/com.rapport.app\" rm -rf \"~/Library/WebKit/com.rapport.app\" App neu starten. Daten gehen dabei nicht verloren (liegen in Application Support, nicht im Cache).","app-stürzt-sofort-beim-start-ab#App stürzt sofort beim Start ab":"Ursache: wahrscheinlich beschädigte JSON-Daten in studio_data_v1.\nDiagnose:\n# Daten ansehen (DevTools-Output) open \"~/Library/Application Support/com.rapport.app\" Lösung:\nBackup wiederherstellen (siehe Datenhaltung) Oder als letzter Ausweg: Daten zurücksetzen mv \"~/Library/Application Support/com.rapport.app\" \\ \"~/Library/Application Support/com.rapport.app.bad\" App neu starten → erstellt frische Daten. Anschliessend Setup-Screen.","auto-update-findet-nichts#Auto-Update findet nichts":"Diagnose:\ncurl -s https://git.kgva.ch/karim/RAPPORT/raw/branch/main/APP/latest.json → sollte JSON liefern. Wenn nicht: Server-/Netzwerkproblem.","daten-weg#Daten weg":"","debug-informationen-sammeln#Debug-Informationen sammeln":"Bei einem Issue helfen folgende Infos:\n# Rapport-Version defaults read /Applications/Rapport.app/Contents/Info.plist CFBundleShortVersionString # macOS-Version sw_vers # Architektur uname -m # Datenverzeichnis-Grösse du -sh \"~/Library/Application Support/com.rapport.app\" # Cache-Verzeichnis-Grösse du -sh \"~/Library/Caches/com.rapport.app\" → Bei Issue mit anhängen.","diese-version-überspringen-rückgängig-machen#\u0026ldquo;Diese Version überspringen\u0026rdquo; rückgängig machen":"In Einstellungen → Updates → Übersprungene Versionen zurücksetzen. Beim nächsten Start wird die Version wieder angeboten.","localstorage-voll#localStorage voll":"Symptom: Rapport schreibt Fehler-Toast “Speicher voll” beim Sichern.\nUrsache: macOS WebView limitiert localStorage auf ~10 MB pro Origin.\nLösung:\nSehr grosse Logos durch kleinere ersetzen (Bürodaten → Logo) Belege (Spesen) selektiv löschen oder als externe Datei archivieren Auf Web-Modus wechseln (Postgres ohne praktisches Limit)","login-screen-zeigt-keine-server-url#Login-Screen zeigt keine Server-URL":"Ursache: .env.production enthielt nicht den richtigen VITE_SUPABASE_URL zur Build-Zeit.\nLösung: .env.production prüfen, dann npm run build neu, Container restart.","nach-einem-app-update-fehlen-einträge#Nach einem App-Update fehlen Einträge":"Ursache: mögliche fehlgeschlagene Migration.\nSofortmassnahme:\nRapport beenden (Cmd+Q, nicht nur Fenster zu) Aktuelles Datenverzeichnis sichern: cp -R \"~/Library/Application Support/com.rapport.app\" \\ \"~/Documents/Rapport-Notfall-$(date +%Y%m%d-%H%M)\" Issue erstellen mit: Version vor dem Update (falls bekannt) Version nach dem Update Was fehlt Optional: gesicherter Datenordner (via Pastebin oder verschlüsselt zugesandt)","pdf--qr-schein#PDF / QR-Schein":"","pdf-export-ist-leer--weisses-blatt#PDF-Export ist leer / weisses Blatt":"Ursache: Print-View hat keine Daten (möglicherweise Race-Condition beim Laden).\nLösung: Rechnung schliessen, erneut öffnen, dann PDF.","pdf-schrift-sieht-falsch-aus#PDF-Schrift sieht falsch aus":"Ursache: Web-Schrift nicht geladen, Fallback greift.\nLösung: Vor dem Drucken warten, bis das Vorschau-Bild komplett geladen ist (3–5 Sek).","qr-schein-hat-falsche-daten#QR-Schein hat falsche Daten":"Diagnose-Checkliste:\nIBAN korrekt? (CH, 21 Zeichen, keine Leerzeichen) Empfänger-Adresse vollständig? (PLZ und Ort beide gefüllt) Schuldner-Adresse vollständig? Betrag \u003e 0? Referenz nicht zu lang? (max 27 Zeichen) QR-Bibliothek: swissqrbill — bei merkwürdigen Fehlern dort nachschauen.","rapport-ist-beschädigt-beim-ersten-start#\u0026ldquo;Rapport ist beschädigt\u0026rdquo; beim ersten Start":"Ursache: macOS Gatekeeper blockt unsignierte/nicht-notarisierte Apps.\nLösung: siehe Installation § 3. Kurz:\nxattr -d com.apple.quarantine /Applications/Rapport.app","realtime-updates-kommen-nicht-an#Realtime-Updates kommen nicht an":"Ursache: Websocket-Support fehlt im Reverse Proxy.\nLösung: In Nginx Proxy Manager für api.* Websocket Support aktivieren.\nSiehe Web-Modus § Troubleshooting.","system-tray#System-Tray":"","tray-icon-erscheint-nicht#Tray-Icon erscheint nicht":"Plattform-Hinweis: Tray-Icons unter macOS sind bei extrem voller Menüleiste oder unter “Bartender”/“Hidden Bar” eventuell unsichtbar.\nDiagnose:\nps aux | grep -i rapport → wenn Prozess läuft, aber kein Icon: in den Tray-Manager-Apps prüfen.\nKonfiguration: Einstellungen → System-Tray → Tray-Icon ausblenden (aus → Icon erzwingen).","tray-menü-reagiert-nicht--hängt#Tray-Menü reagiert nicht / hängt":"Lösung: App via Activity Monitor hart beenden und neu starten. Daten gehen nicht verloren (alle Schreibungen sind synchron in localStorage).","update-lädt-lässt-sich-aber-nicht-installieren#Update lädt, lässt sich aber nicht installieren":"Ursache: Signaturprüfung scheitert (Public-Key in App ≠ Signatur in latest.json).\nLösung: Manuelles Update — DMG direkt von Releases laden und installieren. Daten bleiben erhalten.","updates#Updates":"","web-modus#Web-Modus":"","wenn-nichts-hilft#Wenn nichts hilft":"Neues Issue auf Gitea mit:\nWas du gemacht hast Was passiert ist Was du erwartet hättest Screenshots (auch von DevTools-Konsole falls möglich) Rapport-Version und macOS-Version"},"title":"Troubleshooting"},"/docs/web-modus/":{"data":{"":"Hinweis: Der frühere Supabase-basierte Web-Modus wurde durch Rapport Server abgelöst — den vollständigen Selfhost-Stack mit eigenem Postgres, Auth, Realtime und Storage. Keine externe Cloud-Abhängigkeit mehr.\nDiese Seite bleibt als Referenz erhalten, der empfohlene Weg für Multi-User-Setups ist Rapport Server.","architektur-kurzfassung#Architektur (Kurzfassung)":"┌────────────┐ HTTPS ┌──────────────┐ SQL ┌────────────┐ │ Browser │ ──────────────│ nginx │ ─────────────│ Postgres │ │ / Desktop │ │ (Frontend) │ │ + GoTrue │ └────────────┘ └──────────────┘ │ + REST │ │ + Realtime │ │ + Storage │ └────────────┘ Frontend: dieselbe React-App, aber Vite-Build statt Tauri (npm run build) Backend: Postgres-Stack (Rapport Server) Auth: E-Mail / Passwort über GoTrue Storage: Belege, Logos in Object-Storage","migration-desktop--cloud#Migration Desktop → Cloud":"Wer mit der Desktop-App startet und später auf den Web-Modus wechseln möchte:\nAktuell: manueller Export aus Rapport (CSV/PDF) und manuelles Wiederanlegen im neuen Setup Geplant: “localStorage → Postgres”-Import-Knopf direkt in der App Status: Issue auf Gitea .","setup#Setup":"Alle Setup-Schritte (Repo klonen, .env erstellen, Migrations syncen, Docker-Compose starten, Reverse-Proxy konfigurieren) sind in Rapport Server dokumentiert.","troubleshooting#Troubleshooting":"Siehe Rapport Server § Troubleshooting oder allgemeine Troubleshooting-Seite.","wann-brauchst-du-das#Wann brauchst du das?":"Anwendungsfall Empfehlung Solo-Büro, ein Mac Desktop-App — siehe Installation 2–5 Personen, gleicher Standort Rapport Server auf einem Mac Mini im LAN Verteiltes Team / Home-Office Rapport Server mit SSL + Reverse Proxy Hosted Backend (eigener VPS) Rapport Server auf Linux-VPS"},"title":"Web-Modus"},"/downloads/":{"data":{"":"Aktuelle Builds von Rapport. Quellcode und ältere Versionen auf Gitea .\nRapport besteht aus zwei Komponenten:\nKomponente Für wen Aktuelle Version Desktop-App Solo-Büro, lokale Datenhaltung 0.8.3 Rapport Server Team / Multi-User / Selfhost 0.1.0","auto-update#Auto-Update":"Seit 0.7.0 prüft die Desktop-App beim Start automatisch auf neue Versionen — siehe Auto-Updater. Für Rapport Server: git pull \u0026\u0026 docker compose pull \u0026\u0026 docker compose up -d.","desktop-app--pre-release-083#Desktop-App — Pre-Release 0.8.3":"Aktuelle Version\nNeuerungen — siehe Changelog für Details.","docker-linux--vps--headless#Docker (Linux / VPS / Headless)":"Für Linux-Server, NAS oder VPS — der reine Docker-Compose-Stack ohne GUI.\ngit clone https://git.kgva.ch/karim/rapport-server.git cd rapport-server git checkout 0.1.0 cp .env.example .env # .env editieren (POSTGRES_PASSWORD, JWT_SECRET, SITE_URL) docker compose up -d Vollständige Anleitung mit .env-Variablen, Reverse-Proxy und Backup → Server-Seite.\nContainer-Images werden von Docker Hub / Gitea-Registry gezogen — docker compose pull aktualisiert auf die neuesten Tags.","linux--windows#Linux \u0026amp; Windows":"Geplant. Rapport basiert auf Tauri 2 — eine Portierung ist möglich, sobald der Bedarf besteht. Issue erstellen , wenn du eine Plattform brauchst.","macos#macOS":"Architektur Download Apple Silicon (M1–M4) RAPPORT_0.8.3_aarch64.dmg Intel (x86_64) auf Anfrage Erstinstallation: Systemeinstellungen → Datenschutz \u0026 Sicherheit öffnen und Rapport zulassen. Die Builds sind über Tauri signiert, aber (noch) nicht Apple-notarisiert.","quellcode#Quellcode":"Desktop-App — Tauri / React:\ngit clone https://git.kgva.ch/karim/RAPPORT.git cd RAPPORT/APP npm install npm run tauri dev Voraussetzungen: Node 20+, Rust (stable), Xcode-CLI (macOS).\nRapport Server:\ngit clone https://git.kgva.ch/karim/rapport-server.git Beide Repos auf Gitea — Issues und PRs willkommen.","rapport-server--010#Rapport Server — 0.1.0":"Erstes Release\nSelfhost-Backend für Multi-User-Setups — Postgres, Auth, Realtime-Sync, Storage. Details und Setup-Anleitung auf der Server-Seite.","server-app-macos#Server-App (macOS)":"GUI zum Starten, Stoppen und Verwalten der Server-Instanz auf einem Mac (Mac Mini, Studio-Rechner). Bündelt den Docker-Compose-Stack.\nArchitektur Download Apple Silicon (M1–M4) RAPPORT_SERVER_0.1.0_aarch64.dmg Intel (x86_64) auf Anfrage Voraussetzung: Colima oder Docker Desktop muss installiert sein. Die App verwaltet den Container-Stack darüber."},"title":"Downloads"},"/faq/":{"data":{"":"Häufige Fragen zu RAPPORT. Bei Bugs oder weiteren Fragen → Issue auf Gitea .","für-wen-ist-rapport-gedacht#Für wen ist Rapport gedacht?":"Für kleine Architekturbüros in der Schweiz. Die Strukturen folgen der SIA 102 (Phasen, Honorar). Rapport wurde für den internen Gebrauch entwickelt und wird mit diesem Projekt öffentlich geteilt.","ist-die-software-stabil-genug-für-den-betrieb#Ist die Software stabil genug für den Betrieb?":"Noch nicht. Aktuell befindet sich Rapport in aktiver Entwicklung (Pre-Release 0.8.3). Funktionen können sich ändern, Bugs sind möglich. Regelmässige Backups sind empfohlen. Testen und Feedback geben ist erwünscht.","ist-rapport-kostenlos#Ist Rapport kostenlos?":"Ja, vollständig. Quellcode unter GNU AGPL-3.0-or-later. Keine versteckten Kosten, keine Telemetrie. Die Daten bleiben lokal auf deinem Computer bzw. in deiner Instanz. Du hast die komplette Kontrolle über deine Daten.","kann-ich-zum-projekt-beitragen#Kann ich zum Projekt beitragen?":"Ja. Issues und Pull Requests sind willkommen auf Gitea . Rapport ist kein Framework — konkrete Verbesserungen für den Büroalltag sind am wertvollsten:\nBug-Reports mit Reproduktionsschritten Workflow-Verbesserungen aus dem realen Büroalltag Vorlagen (Briefe, Protokolle, Lieferscheine) für andere Büros Übersetzungen / Internationalisierung","warum-open-source#Warum Open Source?":"Bürowissen sollte nicht in proprietären Tools eingesperrt sein. Studio-Management-Workflows sind in der Branche gut etabliert — ein Tool, das die Schweizer Konventionen (SIA 102, QR-Rechnung) sauber umsetzt, gehört allen.\nAGPL-3.0 stellt sicher, dass Verbesserungen wieder ins Projekt fliessen.","was-ist-mit-dem-qr-einzahlungsschein#Was ist mit dem QR-Einzahlungsschein?":"Rapport erzeugt Schweizer QR-Rechnungen nach Norm — direkt eingebettet in das PDF der Rechnung. IBAN, Bürodaten und Empfänger werden aus den Einstellungen bzw. den Kundendaten gezogen. Akonto-, Teil- und Schlussrechnungen werden unterstützt.","welche-systeme-werden-unterstützt#Welche Systeme werden unterstützt?":"Aktuell nur macOS (Intel \u0026 Apple Silicon). Rapport basiert auf Tauri — eine Portierung auf Linux und Windows ist möglich, sobald der Bedarf seitens Community besteht.","wie-erhalte-ich-hilfe-bei-einem-problem#Wie erhalte ich Hilfe bei einem Problem?":"Ein Issue auf Gitea ist der beste Weg. Bitte beschreibe, was du gemacht hast und was passiert ist. Screenshots helfen. Die Rapport-Version (links unten in der App) bitte angeben.\nKanal Verwendung Gitea Issues Bugs, Feature-Wünsche, allgemeine Fragen gabrielevarano.ch Entwickler-Kontakt","wie-funktioniert-die-zeiterfassung#Wie funktioniert die Zeiterfassung?":"Tages- und Wochenraster mit Drag \u0026 Drop. Jeder Eintrag wird einem Projekt zugewiesen. Auswertungen pro Mitarbeiter und Projekt sind unter Zeit abrufbar. Ferienverwaltung mit Prorata und Jahresabschluss mit Überstundenausgleich.","wo-werden-die-daten-gespeichert#Wo werden die Daten gespeichert?":"Rapport unterstützt zwei Modi, beide selbst-gehostet:\nDesktop-App (Single-User) — alle Daten liegen lokal auf deinem Mac (localStorage im Applikationsordner). Kein Server nötig, kein Cloud-Account, keine Telemetrie. Server-Modus (Multi-User) — Daten in einer eigenen PostgreSQL-Datenbank über Rapport Server. Mehrere Personen, Realtime-Sync, eigene Domain. Wechsel direkt im Login-Bildschirm der App. In beiden Fällen bleibt die Kontrolle über die Daten bei dir."},"title":"FAQ"},"/features/":{"data":{"":"Die Bausteine von RAPPORT — Studio-Management für Schweizer Architekturbüros.","module#Module":"ZeiterfassungTages- \u0026 Wochenraster mit Drag \u0026 Drop. Rechnungen \u0026 OffertenQR-Einzahlungsscheine, SIA-Phasen, Akonto. Projekt- \u0026 KundenverwaltungSIA 102, Budget, Phasen, Beteiligte. MitarbeiterFerien, Absenzen, Lohnabrechnung. Spesen \u0026 BürobudgetBelegupload, Jahresbudget, Internes. Protokolle \u0026 LieferscheineSitzungsprotokolle, Briefe, Lieferscheine. Auto-UpdaterSignierte Updates via Tauri. System-TrayHide-on-Close, Quick-Open."},"title":"Features"},"/features/auto-updater/":{"data":{"":"Neu in 0.7.0\nRapport prüft beim Start automatisch auf neue Versionen und installiert Updates signiert über Tauri. Einzelne Versionen können übersprungen werden.","funktionsweise#Funktionsweise":"Beim App-Start:\nAbfrage gegen https://git.kgva.ch/karim/RAPPORT/releases/latest.json Versionsvergleich mit lokaler version im Tauri-Bundle Bei neuer Version → Update-Dialog Bei Bestätigung → Download + Signaturprüfung + Installation + Neustart","latest-endpoint#Latest-Endpoint":"{ \"version\": \"0.8.3\", \"notes\": \"Rapport 0.8.3\", \"pub_date\": \"2026-05-24T00:00:00Z\", \"platforms\": { \"darwin-aarch64\": { \"signature\": \"…\", \"url\": \"https://git.kgva.ch/karim/RAPPORT/releases/download/0.8.3/RAPPORT%20PRE-RELEASE.app.tar.gz\" } } }","optionen#Optionen":"Update installieren — Download \u0026 Neustart Diese Version überspringen — überspringt nur diese eine Version Später erinnern — beim nächsten Start erneut fragen Updates können in den Einstellungen komplett deaktiviert werden.","sicherheit#Sicherheit":"Updates werden mit dem Tauri-Updater-Schlüssel signiert Manipulierte Downloads werden abgelehnt Quellcode und Build sind reproduzierbar (Gitea CI, geplant)"},"title":"Auto-Updater"},"/features/mitarbeiter/":{"data":{"":"In Arbeit\nFerienverwaltung, interne Stunden / Absenzen und Lohnabrechnung. Jahresabschluss mit Überstundenausgleich.","absenzen#Absenzen":"Krankheit, Militär, Mutterschaft, unbezahlter Urlaub — getrennt erfasst, mit Auswertung pro Mitarbeiter.","ferienverwaltung#Ferienverwaltung":"Prorata-Berechnung bei Eintritt unter Jahr Ferien-Saldo in Tagen (live) Halbtage unterstützt Übertrag ins Folgejahr oder Auszahlung","jahresabschluss#Jahresabschluss":"Ferien-Restguthaben übertragen oder auszahlen Überstunden ausgleichen oder vergüten Lohnausweis vorbereiten (Export)","lohnabrechnung#Lohnabrechnung":"Monatliche Abrechnung mit:\nGrundlohn (basierend auf Pensum) Überstunden-Vergütung Spesen-Erstattung Sozialabzüge (AHV, ALV, Pensionskasse) PDF-Export pro Mitarbeiter.","stammdaten#Stammdaten":"Pro Mitarbeiter:\nName, Eintrittsdatum, Pensum (%) Stundensatz (intern, für Rechnungen) Ferienanspruch (Tage / Jahr) Lohn (monatlich, brutto)","verwandte-module#Verwandte Module":"Zeiterfassung — Pensum-Soll vs. Stunden-Ist Spesen — Spesen-Erstattung in der Lohnabrechnung"},"title":"Mitarbeiter"},"/features/projekte/":{"data":{"":"In Arbeit\nProjekte nach SIA 102 mit Budget, Phasen und Beteiligten. Erstellung aus einer Offerte mit Verknüpfung zu Zeiterfassung und Rechnungen.","auswertung#Auswertung":"Pro Projekt:\nGeleistete Stunden vs. Budget Honorar-Saldo (verrechnet / Akonto / offen) Phasen-Fortschritt","kundendatenbank#Kundendatenbank":"Adresse, Ansprechperson, Telefon, E-Mail Honorartyp (Stundensatz / SIA / Pauschal) Verknüpfung zu allen Projekten und Rechnungen des Kunden","projektstruktur#Projektstruktur":"Jedes Projekt besitzt:\nStammdaten — Nummer, Bezeichnung, Standort, Bauschätzwert Kunde — verknüpft mit Kundendatenbank Beteiligte — Bauleitung, Fachplaner, Behörden Phasen — SIA 102 (Vorprojekt, Bauprojekt, Ausschreibung, …) Budget — Gesamthonorar, pro Phase aufgeteilt","sia-102#SIA 102":"Standard-Phasenverteilung wird vorgeschlagen, kann pro Projekt überschrieben werden.\nPhase Anteil (Standard) 31 — Vorprojekt 9 % 32 — Bauprojekt 21 % 33 — Bewilligung 3 % 41 — Ausschreibung 18 % 51 — Ausführung 38 % 52 — Inbetriebnahme 6 % 53 — Abschluss 5 %","verwandte-module#Verwandte Module":"Rechnungen — Offerte → Projekt Zeiterfassung — Stunden-Auswertung pro Phase"},"title":"Projekte"},"/features/protokolle/":{"data":{"":"In Arbeit\nEinfache Erstellung von Sitzungsprotokollen mit Beschlüssen und Aufgaben. Briefe und Lieferscheine im gleichen Erscheinungsbild.","briefe#Briefe":"Brief-Editor mit:\nEmpfänger aus Kundendatenbank Bezugszeile, Anrede, Text, Grussformel Briefbogen-Vorlage mit Logo PDF-Export","lieferscheine#Lieferscheine":"Pro Lieferung:\nEmpfänger, Datum, Bezug Positionen (Plan-Nummer, Bezeichnung, Anzahl, Massstab) Unterschriftenfeld Konsistentes Erscheinungsbild über alle Dokumenttypen — eine Briefbogen-Vorlage, mehrere Verwendungen.","sitzungsprotokolle#Sitzungsprotokolle":"Pro Sitzung:\nDatum, Ort, Teilnehmer (aus Beteiligten-Liste) Traktanden als nummerierte Liste Pro Traktandum: Beschluss, Aufgabe, Verantwortlich, Frist Anhänge PDF-Export mit Bürobriefbogen.","verwandte-module#Verwandte Module":"Projekte — Beteiligte als Empfänger"},"title":"Protokolle"},"/features/rechnungen/":{"data":{"":"In Arbeit\nQR-Einzahlungsscheine, SIA-Phasen, Akonto-, Teil- und Schlussrechnungen. Offerten sind in Projekte und Rechnungen konvertierbar. PDF-Export.","honorarmodelle#Honorarmodelle":"Modell Berechnung Verwendung Stundensatz Aus Zeiterfassung × Mitarbeiter-Stundensatz Kleinaufträge, Beratung SIA-Phasen Bauschätzwert × Honorarsatz × Phasenanteil Reguläre Architektur-Aufträge Pauschal Fester Betrag Auf Wunsch des Kunden","pdf-export#PDF-Export":"Druckfertige Rechnung inkl. QR-Schein. Layout aus dem Büro-Briefbogen (mit Logo). Mehrsprachig DE/FR/IT (geplant).","qr-einzahlungsschein#QR-Einzahlungsschein":"Schweizer QR-Rechnung nach Norm — direkt eingebettet in die PDF.\nAusgelesen aus:\nBürodaten — IBAN, Empfänger-Adresse Kundendaten — Schuldner-Adresse Rechnungs-Daten — Betrag, Referenz, Zusatzinformation","verwandte-module#Verwandte Module":"Projekte — Honorarstruktur stammt aus dem Projekt Zeiterfassung — Stundensatz-Rechnungen","workflow#Workflow":"Offerte erstellen — auf Basis SIA 102 oder pauschal Kunde nimmt an → konvertieren in Projekt + Rechnung Akonto-Rechnungen während der Projektlaufzeit Schlussrechnung mit Differenz zum bisher Akonto-bezahlten"},"title":"Rechnungen"},"/features/spesen/":{"data":{"":"In Arbeit\nSpesenerfassung mit Belegupload. Jahresbudget mit Einnahmen und Ausgaben. Internes Rechnungswesen.","auswertung#Auswertung":"Einnahmen pro Kunde / Projekt Ausgaben pro Kategorie / Mitarbeiter Erfolgsrechnung pro Geschäftsjahr (vereinfacht)","jahresbudget#Jahresbudget":"Übersicht über:\nEinnahmen — Rechnungsbeträge, sortiert nach Eingang Ausgaben — Spesen, Bürokosten, Löhne, Sozialabzüge Saldo pro Monat / Quartal / Jahr","spesenerfassung#Spesenerfassung":"Pro Mitarbeiter:\nDatum, Betrag, Kategorie Beleg-Upload (PDF, JPG, PNG) Projekt-Zuordnung (optional) Status (offen / erstattet) Kategorien: Reise, Verpflegung, Material, Telefon, Sonstiges.","verwandte-module#Verwandte Module":"Mitarbeiter — Spesen-Erstattung in der Lohnabrechnung Rechnungen — Einnahmen-Quelle"},"title":"Spesen"},"/features/system-tray/":{"data":{"":"Neu in 0.7.0\nSchnellzugriff über die Menüleiste mit Hide-on-Close. Beim Schliessen läuft Rapport im Hintergrund weiter — Cmd+Q beendet die App vollständig.","konfiguration#Konfiguration":"In den Einstellungen:\nBeim Systemstart starten (Login-Item) — Standard: aus Beim Schliessen beenden statt ins Tray — Standard: aus Tray-Icon ausblenden — App läuft, aber kein Menüleisten-Icon","tray-menü#Tray-Menü":"Rapport zeigen — Fenster nach vorne Neue Zeiterfassung — direkt im Zeit-Modul Neue Rechnung — direkt im Rechnungs-Modul Letzte Projekte — Quick-Open der letzten 5 Projekte Einstellungen Rapport beenden","verhalten#Verhalten":"Aktion Verhalten Fenster schliessen (⌘W oder rotes X) App läuft im Tray weiter Cmd+Q App wird vollständig beendet Klick auf Tray-Icon Fenster nach vorne, oder zeigen Rechtsklick auf Tray-Icon Menü mit Schnellzugriffen","verwandte-module#Verwandte Module":"Auto-Updater — prüft Updates im Hintergrund"},"title":"System-Tray"},"/features/zeiterfassung/":{"data":{"":"In Arbeit\nTages- und Wochenraster mit Drag \u0026 Drop. Auswertungen pro Mitarbeiter und Projekt. Ferienverwaltung mit Prorata und Jahresabschluss.","auswertungen#Auswertungen":"Pro Mitarbeiter und pro Projekt:\nGeleistete Stunden vs. Soll-Pensum Ferienanspruch / -saldo (mit Prorata bei Eintritt unter Jahr) Überstunden-Saldo Stundenaufschlüsselung nach SIA-Phase pro Projekt","eingabe#Eingabe":"Wochenraster mit den 5 (oder 7) Arbeitstagen Halbstunden-Slots von 06:00 bis 22:00 Klick oder Drag über mehrere Slots Jeder Eintrag wird einem Projekt zugewiesen (Pflichtfeld) Mehrfacheinträge pro Slot möglich (z. B. parallele Telefonate)","jahresabschluss#Jahresabschluss":"Am Jahresende:\nFerien-Restguthaben übertragen oder auszahlen Überstunden ausgleichen oder vergüten Neues Jahr automatisch initialisieren","konzept#Konzept":"Die Zeiterfassung ist das Kernmodul von RAPPORT — alle anderen Module (Rechnungen, Auswertungen, Lohnabrechnung) greifen auf die hier erfassten Stunden zu.","verwandte-module#Verwandte Module":"Rechnungen — Stundensatz-Rechnungen ziehen direkt aus der Zeiterfassung Projekte — Stunden-Auswertung pro SIA-Phase Mitarbeiter — Pensum, Ferienanspruch"},"title":"Zeiterfassung"},"/glossary":{"data":{},"title":"Glossary"},"/lizenz/":{"data":{"":"Lizenz von RAPPORT, Zugehörigkeit zu OpenBureau und Attribution der verwendeten Open-Source-Software.","desktop-wrapper#Desktop-Wrapper":"Software Verwendung Lizenz Tauri 2 Cross-Platform Desktop-Wrapper MIT / Apache-2.0 WebKit WebView-Engine (macOS) LGPL-2.1 / BSD","diese-website#Diese Website":"Software Verwendung Lizenz Hugo Static-Site-Generator Apache-2.0 Hextra Hugo-Theme (von Xin) MIT Inter UI-Schrift (Rasmus Andersson) SIL OFL 1.1 Playfair Display Headings SIL OFL 1.1 Alle Logos und Marken-Namen gehören ihren jeweiligen Eigentümern.","frontend#Frontend":"Software Verwendung Lizenz React 19 UI-Bibliothek MIT Vite Build-Tool, Dev-Server MIT swissqrbill QR-Einzahlungsscheine MIT","kontakt#Kontakt":"Fragen zur Lizenz, kommerzieller Einsatz, Spezialfälle: Issue auf Gitea oder via gabrielevarano.ch .","lizenz#Lizenz":"Lizenziert unter GNU Affero General Public License v3.0 oder höher (AGPL-3.0-or-later ).\nDies bedeutet im Kern:\nFreie Nutzung für jeden Zweck (privat, kommerziell, behördlich) Freier Zugang zum Quellcode Freie Modifikation und Weitergabe — unter derselben Lizenz Netzwerk-Nutzung gilt als Weitergabe — wer eine modifizierte Version als Service anbietet, muss den Quellcode offenlegen Eigene Builds, eigene Forks, eigene Anpassungen — alles erlaubt, solange Modifikationen wieder unter AGPL-3.0 veröffentlicht werden.","lizenz-volltexte#Lizenz-Volltexte":"Die vollständigen Lizenztexte:\nAGPL-3.0 — RAPPORT MIT — React, Vite, Tauri, Hextra, swissqrbill Apache-2.0 — Tauri, Hugo, Supabase SIL OFL 1.1 — Inter, Playfair Display","mitwirkende#Mitwirkende":"RAPPORT wird derzeit als Ein-Personen-Projekt von Karim Gabriele Varano entwickelt. Beiträge — Code, Issues, Übersetzungen, Vorlagen — sind willkommen.\nWer beitragen möchte → Gitea Issues .","openbureau#OpenBureau":"RAPPORT ist Teil der OpenBureau-Initiative — einer Sammlung freier Software-Werkzeuge für den Architektur-Büro-Alltag.\nDie Idee: Bürowissen — Workflows, Konventionen, Vorlagen — sollte nicht in proprietären Tools eingesperrt sein. OpenBureau-Tools setzen die in der Branche etablierten Konventionen (SIA-Normen, QR-Rechnung, Plan-Verwaltung) sauber um und stehen unter freien Lizenzen zur Verfügung.\nTool Zweck Status RAPPORT Studio-Management Desktop-App (Zeit, Rechnungen, Projekte) Pre-Release Rapport Server Selfhost-Stack (Postgres + Auth + Realtime + Storage) Pre-Release DOSSIER Rhino-8-Plugin für Plan- und Dokumentationsausgabe Pre-Release Mehr unter gabrielevarano.ch .","rapport#RAPPORT":"RAPPORT — Studio Management Software für Architekturbüros.\nQuellcode: git.kgva.ch/karim/RAPPORT Autor: Karim Gabriele Varano","rapport-server-selfhost-stack#Rapport Server (Selfhost-Stack)":"Software Verwendung Lizenz PostgreSQL Datenbank PostgreSQL-Lizenz GoTrue Authentication-Service MIT PostgREST REST-API über Postgres-Schema MIT Realtime WebSocket-Sync Apache-2.0 Storage Object-Storage für Belege \u0026 Logos Apache-2.0 Kong API-Gateway Apache-2.0 nginx Frontend-Webserver BSD-2-Clause Docker Compose Orchestrierung Apache-2.0","verwendete-open-source-software#Verwendete Open-Source-Software":"RAPPORT baut auf freier Software auf. Diese Liste nennt die wichtigsten Komponenten und ihre jeweiligen Lizenzen."},"title":"Lizenz"},"/server/":{"data":{"":"Self-Hosting\nRapport Server — der vollständige Selfhost-Stack für Rapport. Eigene Daten, eigene Domain, eigener Server. Komplett Open-Source, Docker-Compose, AGPL-3.0.","1--repo-klonen#1 · Repo klonen":"git clone https://git.kgva.ch/karim/rapport-server.git cd rapport-server","2--env-erstellen#2 · \u003ccode\u003e.env\u003c/code\u003e erstellen":"cp .env.example .env In .env müssen mindestens diese Werte ersetzt werden:\nVariable Was POSTGRES_PASSWORD DB-Passwort (≥ 32 Zeichen, zufällig) JWT_SECRET JWT-Signatur (≥ 32 Zeichen, zufällig) ANON_KEY / SERVICE_ROLE_KEY aus JWT-Secret abgeleitet SITE_URL Public-URL der Instanz (z. B. https://app.rapport.studio.ch) Zufallswerte generieren:\nopenssl rand -hex 32 # für POSTGRES_PASSWORD und JWT_SECRET node scripts/generate-keys.mjs # ANON_KEY + SERVICE_ROLE_KEY aus JWT_SECRET","3--migrations-holen#3 · Migrations holen":"Die SQL-Migrations stammen aus dem App-Repo. Einmal initial holen:\n./scripts/sync-migrations.sh Bei späteren Rapport-Updates erneut ausführen, dann docker compose down \u0026\u0026 docker compose up -d.","4--stack-starten#4 · Stack starten":"docker compose up -d Erststart dauert ~1 Minute (Postgres initialisiert sich, Migrations laufen, Container starten).","5--health-check#5 · Health-Check":"docker compose ps Alle Container sollten healthy zeigen. Frontend ist dann erreichbar unter http://localhost:8080.","architektur#Architektur":"Rapport Server bündelt sechs Open-Source-Komponenten zu einem stimmigen Stack:\n┌──────────────────────────────────────────────────────┐ │ Browser / Desktop-App │ └──────────────┬───────────────────────┬───────────────┘ │ │ ▼ ▼ ┌──────────┐ ┌──────────┐ │ nginx │ │ Kong │ ← API-Gateway │ (app) │ │ │ └──────────┘ └──────────┘ │ ┌───────────────────┼─────────────────────┐ ▼ ▼ ▼ ┌────────┐ ┌──────────┐ ┌─────────┐ │ GoTrue │ │PostgREST │ │ Realtime│ │ (Auth) │ │ (API) │ │ (WS) │ └────────┘ └──────────┘ └─────────┘ │ │ │ └───────────────────┼─────────────────────┘ ▼ ┌─────────────────┐ │ PostgreSQL │ └─────────────────┘ │ ▼ ┌─────────────────┐ │ Storage │ ← Belege, Logos └─────────────────┘ Komponente Funktion PostgreSQL Datenbank — alle Rapport-Daten GoTrue Authentication — Email/Passwort, JWT-Tokens PostgREST REST-API direkt aus dem Postgres-Schema Realtime WebSocket-Sync für Live-Updates Storage Object-Storage für Belege \u0026 Logos Kong API-Gateway, routet alle Calls app (nginx) Liefert das Rapport-Frontend (React-Build) Alles in einem Docker-Compose. Keine Cloud-Abhängigkeit, keine Telemetrie.","backup#Backup":"","bezug-rapport--rapport-server#Bezug Rapport ↔ Rapport Server":"Komponente Wo Lizenz Rapport Desktop-App git.kgva.ch/karim/RAPPORT AGPL-3.0 Rapport Server git.kgva.ch/karim/rapport-server AGPL-3.0 Die Desktop-App kann wahlweise im Lokal-Modus (JSON, kein Server nötig) oder im Cloud-Modus (gegen Rapport Server) betrieben werden. Wechsel erfolgt im Login-Bildschirm der App.","brauche-ich-supabase-cloud#Brauche ich Supabase Cloud?":"Nein. Rapport Server ist die Selfhost-Variante derselben Komponenten (Postgres + GoTrue + PostgREST + Realtime + Storage), aber komplett auf eigener Infrastruktur. Kein Supabase-Account, keine externe Abhängigkeit.","häufige-fragen#Häufige Fragen":"","kann-ich-von-der-desktop-app-auf-rapport-server-migrieren#Kann ich von der Desktop-App auf Rapport Server migrieren?":"Im Login der Desktop-App auf Cloud wechseln, Server-URL eingeben, Account erstellen, Daten manuell wieder anlegen (Bürodaten, Mitarbeiter, Kunden). Ein direkter localStorage → Postgres-Import ist geplant .","postgres-dump#Postgres-Dump":"docker compose exec -T db pg_dumpall -U postgres \u003e backup-$(date +%Y%m%d).sql Empfohlen: per cron täglich + auf externe Disk / S3 / Backblaze sichern.","quellcode#Quellcode":"git.kgva.ch/karim/rapport-server — Issues, Pull Requests und Forks willkommen.","restore#Restore":"docker compose down docker volume rm rapport-server_postgres-data docker compose up -d db sleep 10 cat backup-YYYYMMDD.sql | docker compose exec -T db psql -U postgres docker compose up -d","reverse-proxy--https#Reverse-Proxy + HTTPS":"Für Production-Setups mit eigener Domain — Caddy ist die einfachste Variante (config-as-code, Let’s-Encrypt automatisch):\napp.rapport.studio.ch { reverse_proxy localhost:8080 } api.rapport.studio.ch { reverse_proxy localhost:8000 } In .env dann:\nSITE_URL=https://app.rapport.studio.ch API_EXTERNAL_URL=https://api.rapport.studio.ch Alternativ über Nginx Proxy Manager mit Web-UI — siehe Troubleshooting.","setup-in-5-schritten#Setup in 5 Schritten":"","storage-belege-logos#Storage (Belege, Logos)":"docker compose exec storage tar -czf - /var/lib/storage \u003e storage-$(date +%Y%m%d).tar.gz","updates#Updates":"git pull ./scripts/sync-migrations.sh # falls neue SQL-Migrationen docker compose pull # neueste Container-Versionen docker compose up -d # neu starten Daten bleiben erhalten — das Volume postgres-data wird nicht angetastet.","voraussetzungen#Voraussetzungen":"OS Container-Runtime Linux (Ubuntu 22.04+, Debian 12+, …) Docker Engine + Compose v2 macOS (Mac Mini etc.) Colima + Docker CLI — voll OSS Auf macOS funktionieren auch OrbStack oder Docker Desktop, beide sind aber proprietär. Colima ist die OSS-Alternative und für Selfhost vollkommen ausreichend.\nPlus: ein DNS-Name für TLS (z. B. rapport.studio.ch), und optional ein Reverse-Proxy wie Nginx Proxy Manager oder Caddy .","wann-brauchst-du-rapport-server#Wann brauchst du Rapport Server?":"Szenario Lösung Ein Mensch, ein Mac Desktop-App reicht — Installation Mehrere Personen im Studio Rapport Server auf einem Mac Mini oder Linux-Server Verteiltes Team, Home-Office, Mobile-Zugriff Rapport Server mit Reverse-Proxy + SSL Cloud-Hosting bei einem Anbieter Rapport Server auf VPS/Hetzner/etc. Die Desktop-App speichert lokal als JSON. Rapport Server bringt Postgres + Multi-User + Realtime-Sync — für alle, die zu zweit oder im Team arbeiten.","was-kostet-das#Was kostet das?":"Nichts — Rapport Server ist Open-Source (AGPL-3.0). Du brauchst nur einen Server (Mac Mini, NAS, VPS) und ggf. eine Domain.","wieviel-server-resourcen#Wieviel Server-Resourcen?":"Ein Mac Mini M1 (8 GB RAM) reicht problemlos für ein 5-Personen-Studio. Linux-VPS mit 2 GB RAM + 2 CPU reicht auch."},"title":"Server"}} \ No newline at end of file diff --git a/public/de.search.js b/public/de.search.js new file mode 100644 index 0000000..4907b47 --- /dev/null +++ b/public/de.search.js @@ -0,0 +1,490 @@ +// Search functionality using FlexSearch. + +// Change shortcut key to cmd+k on Mac, iPad or iPhone. +document.addEventListener("DOMContentLoaded", function () { + if (/iPad|iPhone|Macintosh/.test(navigator.userAgent)) { + // select the kbd element under the .hextra-search-wrapper class + const keys = document.querySelectorAll(".hextra-search-wrapper kbd"); + keys.forEach(key => { + key.innerHTML = 'K'; + }); + } +}); + +// Render the search data as JSON. +// +// +// +// + +(function () { + const searchDataURL = '/de.search-data.json'; + const resultsFoundTemplate = '%d Ergebnisse gefunden'; + + const inputElements = document.querySelectorAll('.hextra-search-input'); + for (const el of inputElements) { + el.addEventListener('focus', init); + el.addEventListener('keyup', search); + el.addEventListener('keydown', handleKeyDown); + el.addEventListener('input', handleInputChange); + } + + const shortcutElements = document.querySelectorAll('.hextra-search-wrapper kbd'); + + function setShortcutElementsOpacity(opacity) { + shortcutElements.forEach(el => { + el.style.opacity = opacity; + }); + } + + function handleInputChange(e) { + const opacity = e.target.value.length > 0 ? 0 : 100; + setShortcutElementsOpacity(opacity); + } + + // Get the search wrapper, input, and results elements. + function getActiveSearchElement() { + const inputs = Array.from(document.querySelectorAll('.hextra-search-wrapper')).filter(el => el.clientHeight > 0); + if (inputs.length === 1) { + return { + wrapper: inputs[0], + inputElement: inputs[0].querySelector('.hextra-search-input'), + resultsElement: inputs[0].querySelector('.hextra-search-results') + }; + } + return undefined; + } + + const INPUTS = ['input', 'select', 'button', 'textarea'] + + // Focus the search input when pressing ctrl+k/cmd+k or /. + document.addEventListener('keydown', function (e) { + const { inputElement } = getActiveSearchElement(); + if (!inputElement) return; + + const activeElement = document.activeElement; + const tagName = activeElement && activeElement.tagName; + if ( + inputElement === activeElement || + !tagName || + INPUTS.includes(tagName) || + (activeElement && activeElement.isContentEditable)) + return; + + if ( + e.key === '/' || + (e.key === 'k' && + (e.metaKey /* for Mac */ || /* for non-Mac */ e.ctrlKey)) + ) { + e.preventDefault(); + inputElement.focus(); + } else if (e.key === 'Escape' && inputElement.value) { + inputElement.blur(); + } + }); + + // Dismiss the search results when clicking outside the search box. + document.addEventListener('mousedown', function (e) { + const { inputElement, resultsElement } = getActiveSearchElement(); + if (!inputElement || !resultsElement) return; + if ( + e.target !== inputElement && + e.target !== resultsElement && + !resultsElement.contains(e.target) + ) { + setShortcutElementsOpacity(100); + hideSearchResults(); + } + }); + + // Get the currently active result and its index. + function getActiveResult() { + const { resultsElement } = getActiveSearchElement(); + if (!resultsElement) return { result: undefined, index: -1 }; + + const result = resultsElement.querySelector('.hextra-search-active'); + if (!result) return { result: undefined, index: -1 }; + + const index = parseInt(result.dataset.index, 10); + return { result, index }; + } + + // Set the active result by index. + function setActiveResult(index) { + const { resultsElement } = getActiveSearchElement(); + if (!resultsElement) return; + + const { result: activeResult } = getActiveResult(); + activeResult && activeResult.classList.remove('hextra-search-active'); + const result = resultsElement.querySelector(`[data-index="${index}"]`); + if (result) { + result.classList.add('hextra-search-active'); + result.focus(); + } + } + + // Get the number of search results from the DOM. + function getResultsLength() { + const { resultsElement } = getActiveSearchElement(); + if (!resultsElement) return 0; + return resultsElement.dataset.count; + } + + // Finish the search by hiding the results and clearing the input. + function finishSearch() { + const { inputElement } = getActiveSearchElement(); + if (!inputElement) return; + hideSearchResults(); + inputElement.value = ''; + inputElement.blur(); + } + + function hideSearchResults() { + const { resultsElement } = getActiveSearchElement(); + if (!resultsElement) return; + resultsElement.classList.add('hx:hidden'); + } + + // Handle keyboard events. + function handleKeyDown(e) { + const { inputElement } = getActiveSearchElement(); + if (!inputElement) return; + + const resultsLength = getResultsLength(); + const { result: activeResult, index: activeIndex } = getActiveResult(); + + switch (e.key) { + case 'ArrowUp': + e.preventDefault(); + if (activeIndex > 0) setActiveResult(activeIndex - 1); + break; + case 'ArrowDown': + e.preventDefault(); + if (activeIndex + 1 < resultsLength) setActiveResult(activeIndex + 1); + break; + case 'Enter': + e.preventDefault(); + if (activeResult) { + activeResult.click(); + } + finishSearch(); + case 'Escape': + e.preventDefault(); + hideSearchResults(); + // Clear the input when pressing escape + inputElement.value = ''; + inputElement.dispatchEvent(new Event('input')); + // Remove focus from the input + inputElement.blur(); + break; + } + } + + // Initializes the search. + function init(e) { + e.target.removeEventListener('focus', init); + if (!(window.pageIndex && window.sectionIndex)) { + preloadIndex(); + } + } + + /** + * Preloads the search index by fetching data and adding it to the FlexSearch index. + * @returns {Promise} A promise that resolves when the index is preloaded. + */ + async function preloadIndex() { + const tokenize = 'forward'; + + // https://github.com/TryGhost/Ghost/pull/21148 + const regex = new RegExp( + `[\u{4E00}-\u{9FFF}\u{3040}-\u{309F}\u{30A0}-\u{30FF}\u{AC00}-\u{D7A3}\u{3400}-\u{4DBF}\u{20000}-\u{2A6DF}\u{2A700}-\u{2B73F}\u{2B740}-\u{2B81F}\u{2B820}-\u{2CEAF}\u{2CEB0}-\u{2EBEF}\u{30000}-\u{3134F}\u{31350}-\u{323AF}\u{2EBF0}-\u{2EE5F}\u{F900}-\u{FAFF}\u{2F800}-\u{2FA1F}]|[0-9A-Za-zа-я\u00C0-\u017F\u0400-\u04FF\u0600-\u06FF\u0980-\u09FF\u1E00-\u1EFF\u0590-\u05FF]+`, + 'mug' + ); + const encode = (str) => { return ('' + str).toLowerCase().match(regex) ?? []; } + + window.pageIndex = new FlexSearch.Document({ + tokenize, + encode, + cache: 100, + document: { + id: 'id', + store: ['title', 'crumb'], + index: "content" + } + }); + + window.sectionIndex = new FlexSearch.Document({ + tokenize, + encode, + cache: 100, + document: { + id: 'id', + store: ['title', 'content', 'url', 'display', 'crumb'], + index: "content", + tag: [{ + field: "pageId" + }] + } + }); + + const resp = await fetch(searchDataURL); + const data = await resp.json(); + let pageId = 0; + for (const route in data) { + let pageContent = ''; + ++pageId; + const urlParts = route.split('/').filter(x => x != "" && !x.startsWith('#')); + + let crumb = ''; + let searchUrl = '/'; + for (let i = 0; i < urlParts.length; i++) { + const urlPart = urlParts[i]; + searchUrl += urlPart + '/' + + const crumbData = data[searchUrl]; + if (!crumbData) { + console.debug('Excluded page', searchUrl, '- will not be included for search result breadcrumb for', route); + continue; + } + + let title = data[searchUrl].title; + if (title == "_index") { + title = urlPart.split("-").map(x => x).join(" "); + } + crumb += title; + + if (i < urlParts.length - 1) { + crumb += ' > '; + } + } + + for (const heading in data[route].data) { + const [hash, text] = heading.split('#'); + const url = route.trimEnd('/') + (hash ? '#' + hash : ''); + const title = text || data[route].title; + + const content = data[route].data[heading] || ''; + const paragraphs = content.split('\n').filter(Boolean); + + sectionIndex.add({ + id: url, + url, + title, + crumb, + pageId: `page_${pageId}`, + content: title, + ...(paragraphs[0] && { display: paragraphs[0] }) + }); + + for (let i = 0; i < paragraphs.length; i++) { + sectionIndex.add({ + id: `${url}_${i}`, + url, + title, + crumb, + pageId: `page_${pageId}`, + content: paragraphs[i] + }); + } + + pageContent += ` ${title} ${content}`; + } + + window.pageIndex.add({ + id: pageId, + title: data[route].title, + crumb, + content: pageContent + }); + + } + } + + /** + * Performs a search based on the provided query and displays the results. + * @param {Event} e - The event object. + */ + function search(e) { + const query = e.target.value; + if (!e.target.value) { + hideSearchResults(); + return; + } + + const { resultsElement } = getActiveSearchElement(); + while (resultsElement.firstChild) { + resultsElement.removeChild(resultsElement.firstChild); + } + resultsElement.classList.remove('hx:hidden'); + + // Configurable search limits with sensible defaults + const maxPageResults = parseInt('20', 10); + const maxSectionResults = parseInt('10', 10); + const pageResults = window.pageIndex.search(query, maxPageResults, { enrich: true, suggest: true })[0]?.result || []; + + const results = []; + const pageTitleMatches = {}; + + for (let i = 0; i < pageResults.length; i++) { + const result = pageResults[i]; + pageTitleMatches[i] = 0; + + const sectionResults = window.sectionIndex.search(query, + { enrich: true, suggest: true, tag: { 'pageId': `page_${result.id}` } })[0]?.result || []; + let isFirstItemOfPage = true + const occurred = {} + + const nResults = Math.min(sectionResults.length, maxSectionResults); + for (let j = 0; j < nResults; j++) { + const { doc } = sectionResults[j] + const isMatchingTitle = doc.display !== undefined + if (isMatchingTitle) { + pageTitleMatches[i]++ + } + const { url, title } = doc + const content = doc.display || doc.content + + if (occurred[url + '@' + content]) continue + occurred[url + '@' + content] = true + results.push({ + _page_rk: i, + _section_rk: j, + route: url, + prefix: isFirstItemOfPage ? result.doc.crumb : undefined, + children: { title, content } + }) + isFirstItemOfPage = false + } + } + const sortedResults = results + .sort((a, b) => { + // Sort by number of matches in the title. + if (a._page_rk === b._page_rk) { + return a._section_rk - b._section_rk + } + if (pageTitleMatches[a._page_rk] !== pageTitleMatches[b._page_rk]) { + return pageTitleMatches[b._page_rk] - pageTitleMatches[a._page_rk] + } + return a._page_rk - b._page_rk + }) + .map(res => ({ + id: `${res._page_rk}_${res._section_rk}`, + route: res.route, + prefix: res.prefix, + children: res.children + })); + displayResults(sortedResults, query); + } + + /** + * Displays the search results on the page. + * + * @param {Array} results - The array of search results. + * @param {string} query - The search query. + */ + function displayResults(results, query) { + const { resultsElement } = getActiveSearchElement(); + if (!resultsElement) return; + + if (!results.length) { + resultsElement.innerHTML = `Keine Ergebnisse gefunden.`; + // Announce no results to screen readers + const wrapper = resultsElement.closest('.hextra-search-wrapper'); + const statusEl = wrapper ? wrapper.querySelector('.hextra-search-status') : null; + if (statusEl) { + statusEl.textContent = 'Keine Ergebnisse gefunden.'; + } + return; + } + + // Append text with highlighted matches using safe text nodes. + function appendHighlightedText(container, text, query) { + if (!text) return; + if (!query) { + container.textContent = text; + return; + } + + const escapedQuery = query.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&'); + if (!escapedQuery) { + container.textContent = text; + return; + } + + const regex = new RegExp(escapedQuery, 'gi'); + let lastIndex = 0; + let match; + while ((match = regex.exec(text)) !== null) { + if (match.index > lastIndex) { + container.appendChild(document.createTextNode(text.slice(lastIndex, match.index))); + } + const span = document.createElement('span'); + span.className = 'hextra-search-match'; + span.textContent = match[0]; + container.appendChild(span); + lastIndex = match.index + match[0].length; + } + if (lastIndex < text.length) { + container.appendChild(document.createTextNode(text.slice(lastIndex))); + } + } + + function handleMouseMove(e) { + const target = e.target.closest('a'); + if (target) { + const active = resultsElement.querySelector('a.hextra-search-active'); + if (active) { + active.classList.remove('hextra-search-active'); + } + target.classList.add('hextra-search-active'); + } + } + + const fragment = document.createDocumentFragment(); + for (let i = 0; i < results.length; i++) { + const result = results[i]; + if (result.prefix) { + const prefix = document.createElement('div'); + prefix.className = 'hextra-search-prefix'; + prefix.textContent = result.prefix; + fragment.appendChild(prefix); + } + const li = document.createElement('li'); + const link = document.createElement('a'); + link.dataset.index = i; + link.href = result.route; + if (i === 0) { + link.classList.add('hextra-search-active'); + } + + const title = document.createElement('div'); + title.className = 'hextra-search-title'; + appendHighlightedText(title, result.children.title, query); + link.appendChild(title); + + if (result.children.content) { + const excerpt = document.createElement('div'); + excerpt.className = 'hextra-search-excerpt'; + appendHighlightedText(excerpt, result.children.content, query); + link.appendChild(excerpt); + } + + li.appendChild(link); + li.addEventListener('mousemove', handleMouseMove); + li.addEventListener('keydown', handleKeyDown); + link.addEventListener('click', finishSearch); + fragment.appendChild(li); + } + resultsElement.appendChild(fragment); + resultsElement.dataset.count = results.length; + + // Announce results count to screen readers + const wrapper = resultsElement.closest('.hextra-search-wrapper'); + const statusEl = wrapper ? wrapper.querySelector('.hextra-search-status') : null; + if (statusEl) { + statusEl.textContent = results.length > 0 + ? resultsFoundTemplate.replace('%d', results.length.toString()) + : 'Keine Ergebnisse gefunden.'; + } + } +})(); diff --git a/public/de.search.min.e433191e26d61d7cc2f542972a01887353cb627a1eff647e8683b01439c4dee5.js b/public/de.search.min.e433191e26d61d7cc2f542972a01887353cb627a1eff647e8683b01439c4dee5.js new file mode 100644 index 0000000..4c2e224 --- /dev/null +++ b/public/de.search.min.e433191e26d61d7cc2f542972a01887353cb627a1eff647e8683b01439c4dee5.js @@ -0,0 +1,2 @@ +document.addEventListener("DOMContentLoaded",function(){if(/iPad|iPhone|Macintosh/.test(navigator.userAgent)){const e=document.querySelectorAll(".hextra-search-wrapper kbd");e.forEach(e=>{e.innerHTML='K'})}}),function(){const f="/de.search-data.json",h="%d Ergebnisse gefunden",c=document.querySelectorAll(".hextra-search-input");for(const e of c)e.addEventListener("focus",o),e.addEventListener("keyup",g),e.addEventListener("keydown",i),e.addEventListener("input",m);const u=document.querySelectorAll(".hextra-search-wrapper kbd");function n(e){u.forEach(t=>{t.style.opacity=e})}function m(e){const t=e.target.value.length>0?0:100;n(t)}function e(){const e=Array.from(document.querySelectorAll(".hextra-search-wrapper")).filter(e=>e.clientHeight>0);return e.length===1?{wrapper:e[0],inputElement:e[0].querySelector(".hextra-search-input"),resultsElement:e[0].querySelector(".hextra-search-results")}:0[0]}const l=["input","select","button","textarea"];document.addEventListener("keydown",function(t){const{inputElement:n}=e();if(!n)return;const s=document.activeElement,o=s&&s.tagName;if(n===s||!o||l.includes(o)||s&&s.isContentEditable)return;t.key==="/"||t.key==="k"&&(t.metaKey||t.ctrlKey)?(t.preventDefault(),n.focus()):t.key==="Escape"&&n.value&&n.blur()}),document.addEventListener("mousedown",function(s){const{inputElement:i,resultsElement:o}=e();if(!i||!o)return;s.target!==i&&s.target!==o&&!o.contains(s.target)&&(n(100),t())});function s(){const{resultsElement:n}=e();if(!n)return{result:0[0],index:-1};const t=n.querySelector(".hextra-search-active");if(!t)return{result:0[0],index:-1};const s=parseInt(t.dataset.index,10);return{result:t,index:s}}function r(t){const{resultsElement:o}=e();if(!o)return;const{result:i}=s();i&&i.classList.remove("hextra-search-active");const n=o.querySelector(`[data-index="${t}"]`);n&&(n.classList.add("hextra-search-active"),n.focus())}function d(){const{resultsElement:t}=e();return t?t.dataset.count:0}function a(){const{inputElement:n}=e();if(!n)return;t(),n.value="",n.blur()}function t(){const{resultsElement:t}=e();if(!t)return;t.classList.add("hx:hidden")}function i(n){const{inputElement:o}=e();if(!o)return;const{result:c,index:i}=s(),l=d();switch(n.key){case"ArrowUp":n.preventDefault(),i>0&&r(i-1);break;case"ArrowDown":n.preventDefault(),i+1(""+e).toLowerCase().match(o)??[];window.pageIndex=new FlexSearch.Document({tokenize:n,encode:s,cache:100,document:{id:"id",store:["title","crumb"],index:"content"}}),window.sectionIndex=new FlexSearch.Document({tokenize:n,encode:s,cache:100,document:{id:"id",store:["title","content","url","display","crumb"],index:"content",tag:[{field:"pageId"}]}});const i=await fetch(f),e=await i.json();let t=0;for(const n in e){let a="";++t;const i=n.split("/").filter(e=>e!=""&&!e.startsWith("#"));let s="",o="/";for(let t=0;te).join(" ")),s+=a,t ")}for(const c in e[n].data){const[l,u]=c.split("#"),o=n.trimEnd("/")+(l?"#"+l:""),i=u||e[n].title,d=e[n].data[c]||"",r=d.split(` +`).filter(Boolean);sectionIndex.add({id:o,url:o,title:i,crumb:s,pageId:`page_${t}`,content:i,...r[0]&&{display:r[0]}});for(let e=0;ee._page_rk===t._page_rk?e._section_rk-t._section_rk:s[e._page_rk]!==s[t._page_rk]?s[t._page_rk]-s[e._page_rk]:e._page_rk-t._page_rk).map(e=>({id:`${e._page_rk}_${e._section_rk}`,route:e.route,prefix:e.prefix,children:e.children}));v(d,i)}function v(t,n){const{resultsElement:s}=e();if(!s)return;if(!t.length){s.innerHTML=`Keine Ergebnisse gefunden.`;const e=s.closest(".hextra-search-wrapper"),t=e?e.querySelector(".hextra-search-status"):null;t&&(t.textContent="Keine Ergebnisse gefunden.");return}function r(e,t,n){if(!t)return;if(!n){e.textContent=t;return}const i=n.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&");if(!i){e.textContent=t;return}const a=new RegExp(i,"gi");let o=0,s;for(;(s=a.exec(t))!==null;){s.index>o&&e.appendChild(document.createTextNode(t.slice(o,s.index)));const n=document.createElement("span");n.className="hextra-search-match",n.textContent=s[0],e.appendChild(n),o=s.index+s[0].length}o0?h.replace("%d",t.length.toString()):"Keine Ergebnisse gefunden.")}}() \ No newline at end of file diff --git a/public/docs/arbeitsablauf/index.html b/public/docs/arbeitsablauf/index.html new file mode 100644 index 0000000..3ceb869 --- /dev/null +++ b/public/docs/arbeitsablauf/index.html @@ -0,0 +1,64 @@ +Typischer Arbeitsablauf – RAPPORTZum Inhalt springen
Arbeitsablauf

Typischer Arbeitsablauf

Vom Erstkontakt mit dem Kunden bis zur Schlussrechnung — der typische Weg eines Projekts durch Rapport.

Übersicht +

  Kunde     →     Offerte    →    Projekt    →    Zeit   →    Akonto   →    Schluss
+ anlegen        erstellen       (aus Offerte)   erfassen    -Rechnung      -Rechnung
+                                                                  ↓
+                                                              QR-Schein

1 · Kunde anlegen +

Kunden → Neu — siehe Einrichtung § 3.

Falls der Kunde später bestellt, lassen sich alle gesammelten Daten (Adresse, Honorartyp) direkt übernehmen.

2 · Offerte erstellen +

Offerten → Neu

FeldInhalt
Kundeaus Kundendatenbank wählen
BezeichnungProjekttitel (z. B. “Einfamilienhaus Müller, Bern”)
HonorartypStundensatz / SIA 102 / Pauschal
Bauschätzwertbei SIA — Bruttowert in CHF
Phasenbei SIA — anzuklickende Phasen
Positionenbei Stundensatz/Pauschal — Position, Beschreibung, Betrag

Mit PDF exportieren — fertige Offerte für den Kunden.

Offerten-Status +

StatusBedeutung
Entwurfnoch nicht versandt
Versandtbeim Kunden, wartet auf Antwort
AngenommenKunde hat zugesagt — bereit zur Konvertierung
AbgelehntKunde hat abgelehnt — Archiv

3 · Offerte → Projekt +

Bei Status “Angenommen”: Knopf “In Projekt konvertieren”.

Das erzeugt:

  • Ein neues Projekt mit den Daten der Offerte (Kunde, Bezeichnung, Honorar, Phasen)
  • Eine Verknüpfung zwischen Offerte und Projekt
  • Die Offerte selbst bleibt im Archiv erhalten

Siehe auch Projekt-Feature.

4 · Zeit erfassen +

Zeit → Mitarbeiter wählen → Woche navigieren

  • Klick auf einen Halbstunden-Slot → Eintrag erstellen
  • Drag über mehrere Slots → längerer Eintrag
  • Projekt zuweisen (Pflichtfeld) — aus aktiven Projekten
  • SIA-Phase zuweisen (optional, für detaillierte Auswertung)

Spezialfälle +

  • Ferien — eigener “Projekt”-Typ “Ferien”, in der Auswertung separat
  • Krankheit/Absenz — eigener “Projekt”-Typ, ebenfalls separat
  • Interne Stunden — z. B. Verwaltung, Marketing, IT — eigene interne Projekte

5 · Akonto-Rechnung +

Während der Projektlaufzeit — typisch nach jeder abgeschlossenen SIA-Phase oder monatlich.

Rechnungen → Neu → Akonto

FeldInhalt
Projektaus aktiven Projekten
Bezug“Akonto für Phase 31 — Vorprojekt”
BetragStundensatz × Stunden, oder Phasenanteil × Bausumme
Fälligkeiti. d. R. 30 Tage

Rapport zieht automatisch die geleisteten Stunden aus der Zeiterfassung — die kannst du als Basis nehmen oder überschreiben.

PDF inkl. QR-Einzahlungsschein — siehe Rechnungen-Feature.

6 · Schlussrechnung +

Nach Projektabschluss — Differenz aus Gesamthonorar minus aller Akonto-Beträge.

Rechnungen → Neu → Schlussrechnung

Rapport rechnet die bisherigen Akonto-Rechnungen automatisch ab:

Gesamthonorar (SIA / Pauschal / Stundensatz)
+  −  Akonto-Rechnung 1
+  −  Akonto-Rechnung 2
+  −  Akonto-Rechnung 3
+  =  Schlussrechnung

7 · Projektabschluss +

Im Projekt → Status auf “Abgeschlossen”.

Das Projekt bleibt für historische Auswertungen sichtbar, taucht aber nicht mehr in der Zeiterfassungs-Auswahl auf.

Auswertungen +

Wöchentlich / monatlich / am Jahresende:

  • Zeit → Auswertungen — Stunden pro Mitarbeiter, pro Projekt, pro Phase
  • Rechnungen → Übersicht — offene Beträge, bezahlt, Mahnungen
  • Buchhaltung → Erfolgsrechnung — Einnahmen vs. Ausgaben
  • Mitarbeiter → Lohnabrechnung — monatlich

Tipps aus dem Alltag +

  • Zeit jeden Tag erfassen statt rückwirkend — sonst gehen Details verloren
  • Akonto regelmässig statt einmal am Schluss — Liquidität
  • Backups vor Jahresabschluss — siehe Datenhaltung
  • Briefbogen-Logo in hoher Auflösung — sieht im PDF besser aus
\ No newline at end of file diff --git a/public/docs/changelog/index.html b/public/docs/changelog/index.html new file mode 100644 index 0000000..968aa16 --- /dev/null +++ b/public/docs/changelog/index.html @@ -0,0 +1,68 @@ +Changelog – RAPPORTZum Inhalt springen
Changelog

Changelog

Versionsgeschichte von RAPPORT. Aktuelle Releases: Gitea.

0.8.3 — Aktuelle Version Aktuell +

Veröffentlicht am 2026-05-24.

Neu / Verbessert

Bekannte Einschränkungen

  • Builds sind Tauri-signiert, aber noch nicht Apple-notarisiert — siehe Installation § Gatekeeper
  • Linux- und Windows-Builds noch nicht verfügbar

0.8.0–0.8.2 — Patch-Releases +

Patch-Reihe mit kleineren Verbesserungen und Bugfixes. Details siehe Releases auf Gitea.

0.7.0 — Auto-Updater & System-Tray +

Neu

  • Auto-Updater — Rapport prüft beim Start auf neue Versionen und installiert Updates signiert über Tauri. Einzelne Versionen können übersprungen werden. (Doku)
  • System-Tray — Schnellzugriff über die Menüleiste mit Hide-on-Close. Beim Schliessen läuft Rapport im Hintergrund weiter, Cmd+Q beendet die App vollständig. (Doku)
  • Quick-Open der letzten 5 Projekte direkt aus dem Tray-Menü

Verbessert

  • Schnellerer App-Start durch lazy-geladene Views
  • Klarere Statusbadges in der Projekt-Übersicht

0.6.x — Spesen & Buchhaltung +

Neu

  • Spesenerfassung mit Beleg-Upload (Base64 in localStorage)
  • Jahresbudget mit Einnahmen-/Ausgaben-Übersicht
  • Vereinfachte Erfolgsrechnung pro Geschäftsjahr

Verbessert

  • Lohnabrechnung integriert Spesen-Erstattungen
  • CSV-Export aus der Zeiterfassung

0.5.x — Mitarbeiter & Lohn +

Neu

  • Mitarbeiterverwaltung mit Pensum, Stundensatz, Ferienanspruch
  • Lohnabrechnung mit AHV/IV/EO, ALV, BVG, NBU
  • Jahresabschluss mit Überstundenausgleich
  • Ferien-Prorata bei Eintritt unter Jahr

0.4.x — Projekte & SIA 102 +

Neu

  • Projektverwaltung nach SIA 102
  • Vorgeschlagene Phasen-Anteile am Gesamthonorar
  • Bauschätzwert-basiertes Honorar

Verbessert

  • Zeit-Auswertung pro SIA-Phase
  • Akonto- und Schlussrechnungen mit automatischer Differenzberechnung

0.3.x — Rechnungen & QR +

Neu

  • Rechnungsmodul mit QR-Einzahlungsschein (via swissqrbill)
  • Akonto-, Teil- und Schlussrechnungen
  • PDF-Export mit Bürobriefbogen
  • Stundensatz-Rechnungen ziehen direkt aus der Zeiterfassung

0.2.x — Zeiterfassung +

Neu

  • Wochenraster mit Halbstunden-Slots
  • Drag & Drop zur Slot-Erfassung
  • Projekt-Zuweisung pro Eintrag
  • Auswertungen pro Mitarbeiter und Projekt

0.1.x — Initial +

Neu

  • Erste Setup-Routine (Bürodaten, Mitarbeiter, Kunden)
  • Briefe und Lieferscheine
  • Tauri-2-Bundle für macOS

Roadmap +

Geplant — keine konkreten Termine:

  • Linux-Build (Tauri 2 unterstützt es, Bedarf nötig)
  • Windows-Build (analog)
  • Cloud-Modus Stable (Supabase) — derzeit in Web-Modus verfügbar, aber experimentell
  • Französische / Italienische Übersetzung
  • PostgreSQL-Migration aus localStorage (Knopf in der App)
  • Mobile App (iOS Companion zur Zeiterfassung) — offen

Wünsche oder Prioritäten → Issue auf Gitea.

\ No newline at end of file diff --git a/public/docs/datenhaltung/index.html b/public/docs/datenhaltung/index.html new file mode 100644 index 0000000..7e399c8 --- /dev/null +++ b/public/docs/datenhaltung/index.html @@ -0,0 +1,76 @@ +Datenhaltung & Backup – RAPPORTZum Inhalt springen
Datenhaltung

Datenhaltung & Backup

Wo Rapport seine Daten speichert, wie du sie sicherst und wiederherstellst.

Diese Seite beschreibt die Desktop-App (Single-User). Wer im Team arbeitet und Rapport gegen einen Rapport Server betreibt, sichert stattdessen die Postgres-Datenbank — siehe Rapport Server § Backup.

Speicherort (Desktop-App) +

Die Desktop-App speichert alles lokal — keine Cloud, kein Server.

macOS +

~/Library/Application Support/com.rapport.app/

Dort liegt eine einzelne localStorage-Datenbank des WebView, in der alle Rapport-Daten als JSON unter dem Key studio_data_v1 gespeichert sind:

  • Bürodaten, Logo, IBAN
  • Mitarbeiter, Kunden, Projekte, Offerten
  • Zeit-Einträge, Rechnungen
  • Spesen, Lohnabrechnungen, Protokolle
  • App-Einstellungen

Konsequenz: Wer den Application-Support-Ordner kopiert, hat ein vollständiges Backup. Wer ihn löscht, verliert alle Daten.

Warum localStorage? +

In der Desktop-App ist Rapport eine Single-User-Anwendung. localStorage ist dafür:

  • Schnell — keine Datenbank-Roundtrips
  • Einfach — keine Migration nötig, JSON-Schema im Code
  • Portabel — eine Datei → ein Backup

Für Multi-User-Betrieb existiert Rapport Server — Postgres + Auth + Realtime in einem Docker-Compose. Wechsel zwischen Desktop- und Server-Modus erfolgt im Login-Bildschirm der App.

Backup-Strategien +

A · Einfach (manuell) +

Den ganzen Ordner kopieren:

cp -R "~/Library/Application Support/com.rapport.app" \
+      "~/Documents/Rapport-Backup-$(date +%Y%m%d)"

→ Auf USB-Stick, externen Datenträger oder in die Cloud.

B · Time Machine +

Wenn Time Machine läuft, ist der Ordner automatisch dabei. Versionierung inbegriffen.

Einschränkung: Time Machine sichert nur lokal/USB. Für off-site-Backup separat sorgen.

C · Cron-Job (täglich automatisch) +

# In ~/Library/LaunchAgents/com.rapport.backup.plist hinterlegen
+# oder als crontab-Eintrag:
+0 22 * * * rsync -a "$HOME/Library/Application Support/com.rapport.app/" \
+                    "$HOME/Backups/rapport/$(date +\%Y\%m\%d)/"

D · iCloud Drive (off-site) +

Application-Support liegt nicht automatisch in iCloud. Wer das will:

# Symlink anlegen
+mkdir -p "~/iCloud Drive/Rapport"
+ln -s "~/Library/Application Support/com.rapport.app" "~/iCloud Drive/Rapport/data"

Achtung: iCloud-Sync mit aktiver App kann zu Race-Conditions führen. Besser den Sync zeitversetzt (z. B. nachts via Cron).

Wiederherstellung +

Vollständig (Rapport komplett tot) +

  1. Rapport beenden (Cmd+Q, nicht nur Fenster zu)
  2. Aktuellen Ordner umbenennen (falls noch da):
    mv "~/Library/Application Support/com.rapport.app" \
    +   "~/Library/Application Support/com.rapport.app.bak"
  3. Backup-Ordner zurück kopieren:
    cp -R "~/Documents/Rapport-Backup-20260523" \
    +      "~/Library/Application Support/com.rapport.app"
  4. Rapport starten

Selektiv (nur einzelne Daten) +

Da alle Daten in einem JSON unter studio_data_v1 liegen, ist selektive Wiederherstellung manuell:

  1. Backup-localStorage-Datei öffnen (WebKit-Format → mit Tool wie [WebKit Storage Inspector] lesen, oder via Rapport DevTools)
  2. Gewünschte Felder in die aktuelle Instanz übertragen

In der Praxis: meistens lohnt sich die vollständige Wiederherstellung mehr.

Export-Funktionen (in der App) +

Aus Rapport selbst:

WasWoFormat
Zeit-AuswertungZeit → ExportCSV
RechnungRechnung → PDFPDF (inkl. QR)
OfferteOfferte → PDFPDF
LohnabrechnungMitarbeiter → PDFPDF
Jahres-BuchhaltungBuchhaltung → ExportCSV

Die Exports sind für externe Verwendung (Buchhalter, Treuhänder, Archiv) gedacht — kein Full-Backup.

Schema-Migrationen +

Bei Updates kann sich das Datenformat ändern. Rapport hat einen Migrations-Mechanismus: beim Start prüft die App, ob das gespeicherte Format dem aktuellen entspricht, und migriert es automatisch.

Code: src/storage/migrations.js.

Empfehlung: Vor jedem grösseren Update ein Backup machen — Migrationen sind getestet, aber 100%-Sicherheit gibt es nicht.

Was wird nicht gespeichert? +

  • WebView-Cache~/Library/Caches/com.rapport.app/ und ~/Library/WebKit/com.rapport.app/ sind sicher zu löschen (UI-Caches, regenerieren sich)
  • App-Updates — werden bei Bedarf erneut runtergeladen
  • Logs~/Library/Logs/com.rapport.app/ (geplant, derzeit nicht geschrieben)

Datenmenge +

Typische Grössen pro Bürojahr:

InhaltGrösse
Logo (PNG/SVG)50 KB – 1 MB
1 Jahr Zeiterfassung (1 MA)~ 80 KB
1 Jahr Zeiterfassung (5 MA)~ 400 KB
50 Projekte mit je 5 Rechnungen~ 800 KB
Total typisches Solo-Büro / Jahr~ 1–2 MB

localStorage hat Limits (i. d. R. ~10 MB pro Origin). Für Solo-Büros reicht das problemlos für viele Jahre. Wer das Limit erreicht oder im Team arbeitet → Rapport Server.

\ No newline at end of file diff --git a/public/docs/einrichtung/index.html b/public/docs/einrichtung/index.html new file mode 100644 index 0000000..606f8da --- /dev/null +++ b/public/docs/einrichtung/index.html @@ -0,0 +1,62 @@ +Einrichtung – RAPPORTZum Inhalt springen
Einrichtung

Einrichtung

Nach der Installation: Bürodaten, Mitarbeiter, Kunden und Projekte initial anlegen.

Reihenfolge +

Die Reihenfolge ist wichtig — jede Stufe baut auf der vorherigen auf:

1. Bürodaten   →   2. Mitarbeiter   →   3. Kunden   →   4. Projekte
+   ▼                  ▼                   ▼                ▼
+   Briefbogen,        Zeiterfassung,      Adressen,        Zeiterfassung,
+   QR-Schein,         Lohn               Rechnungen        Rechnungen
+   Login

1 · Bürodaten +

Einstellungen → Bürodaten

FeldBeschreibungVerwendung
NameBürobezeichnungBriefbogen, Login-Screen
AdresseStrasse, PLZ, OrtBriefbogen, QR-Rechnung (Empfänger)
Telefon, E-MailKontaktdatenBriefbogen, Rechnung-Footer
IBANCH-IBAN (Format CH XX XXXX …)QR-Einzahlungsschein — Pflicht
LogoPNG/SVG-UploadBriefbogen, Rechnung, Brief
MwSt.-Nr.optionalRechnung-Footer

Tipp: Das Logo wird hochauflösend gespeichert (Base64 im localStorage). Bei sehr grossen Dateien (>1 MB) vorher in Vorschau verkleinern.

2 · Mitarbeiter +

Einstellungen → Mitarbeiter → Neu

FeldBeschreibung
Name, Vornamewird in Zeiterfassung & Lohn verwendet
InitialenKürzel für Auswertungen (z. B. “KGE”)
Eintrittsdatumfür Prorata der Ferien
Pensum (%)100 = Vollzeit
Stundensatz (CHF)für Stundensatz-Rechnungen
Ferienanspruch (Tage/Jahr)i. d. R. 25–30
Lohn (brutto, monatlich)optional, für Lohnabrechnung

Mindestens ein Mitarbeiter (z. B. der Inhaber selbst) muss angelegt sein, sonst lässt sich keine Zeit erfassen.

Sozialabzüge (optional) +

In Einstellungen → Lohn:

AbzugStandardwert (CH)
AHV / IV / EO5,3 %
ALV1,1 %
BVG (Pensionskasse)variabel — je Mitarbeiter
NBUje nach Versicherung

Die Standardsätze sind hinterlegt, können aber überschrieben werden.

3 · Kunden +

Kunden → Neu

FeldBeschreibung
TypPrivatperson / Firma
Anrede, Namefür Brief / Rechnung
AdresseStrasse, PLZ, Ort, Land
Ansprechpersonbei Firmen
Telefon, E-MailKontakt
Honorartyp DefaultStundensatz / SIA / Pauschal
Stundensatzfalls vom Bürostandard abweichend
MwSt.-pflichtigja/nein

4 · Projekte +

Projekte → Neu

FeldBeschreibung
Projekt-Nr.freie Form, oder generiert (2026-001)
BezeichnungKurztitel
StandortAdresse
Kundeaus Kundendatenbank
Bauschätzwertfür SIA-Phasen-Honorar
SIA-PhasenVorprojekt / Bauprojekt / … — alle anwählbar
HonorartypStundensatz / SIA 102 / Pauschal
Statusaktiv / pausiert / abgeschlossen

SIA-102-Phasen +

Wenn als Honorartyp SIA 102 gewählt ist, werden die Phasen-Anteile am Gesamthonorar vorgeschlagen — siehe Projekt-Feature.

Checkliste +

Nach diesen vier Schritten ist Rapport einsatzbereit:

  • Bürodaten inkl. IBAN erfasst
  • Mindestens ein Mitarbeiter angelegt
  • Erster Kunde angelegt
  • Erstes Projekt angelegt
  • Eine Test-Zeitbuchung erfasst — wird das Projekt korrekt zugewiesen?
  • Eine Test-Rechnung erstellt — kommt der QR-Schein sauber raus?

Wenn alles funktioniert: Typischer Arbeitsablauf.

\ No newline at end of file diff --git a/public/docs/entwicklung/index.html b/public/docs/entwicklung/index.html new file mode 100644 index 0000000..4ed8fda --- /dev/null +++ b/public/docs/entwicklung/index.html @@ -0,0 +1,165 @@ +Entwicklung & Build – RAPPORTZum Inhalt springen
Entwicklung

Entwicklung & Build

Aus dem Quellcode kompilieren, beitragen, eigenes Release erzeugen.

Voraussetzungen +

ToolVersion
Node.js≥ 20 (für Vite 8)
npm≥ 10
Rust toolchain≥ 1.77.2 (via rustup)
Plattform-Toolssiehe Tauri Prerequisites

Plattform-spezifisch:

  • macOS: Xcode Command Line Tools (xcode-select --install)
  • Windows: Microsoft C++ Build Tools + WebView2
  • Linux: webkit2gtk-4.1, librsvg2-dev, libayatana-appindicator3-dev, build-essential

Setup +

git clone https://git.kgva.ch/karim/RAPPORT.git
+cd RAPPORT/APP
+npm install

Entwicklung +

Web-Modus (HMR, schnellster Loop) +

npm run dev          # http://localhost:3000
  • Hot-Module-Replacement
  • Schnellster Iteration-Loop für UI-Arbeit
  • Datenpersistierung: Browser-localStorage

Native Window (Tauri-Fenster mit Desktop-Integration) +

npx tauri dev
  • Echtes Tauri-Fenster
  • System-Tray, Updater, native APIs verfügbar
  • Erster Start dauert lange (Rust-Compile)

Architektur in einem Absatz +

RAPPORT ist eine monolithische SPA: ein React-Root in App.jsx hält den gesamten App-State in einem useState({...}), persistiert ihn synchron in localStorage unter studio_data_v1, und übergibt ihn als Props an lazy-geladene Views. Kein Routing-Framework, kein State-Library, kein TypeScript, kein CSS-Framework. Der Rust-Teil ist 109 Zeilen und macht nur drei Dinge: System-Tray, Window-Hide-on-Close, Plugin-Registrierung (Updater, Process, Log). Keine #[tauri::command] — Frontend ↔ Backend kommuniziert nur über das Event rapport:navigate (Tray → Frontend).

Detaillierte Karte: ARCHITECTURE.md.

Verzeichnis-Karte +

APP/
+├── src/                      React 19 (kein TS, nur .jsx)
+│   ├── App.jsx               Root: State, Navigation, Sidebar, Modals
+│   ├── constants.js          STORAGE_KEY, NAV_ITEMS, defaultData, SIA-Phasen
+│   ├── utils.js              Business-Logik: Kalkulation, QR-Bill, CSV, Lohn
+│   ├── storage/adapter.js    LocalStorageAdapter (Phase 1), SupabaseAdapter (Phase 2)
+│   ├── storage/migrations.js Schema-Migrationen
+│   ├── views/                20 Top-Level-Screens, lazy-geladen
+│   ├── components/UI.jsx     StatusBadge, Modal, FormField, …
+│   ├── components/Update*    Auto-Update-UI
+│   ├── print/                PrintComponents.jsx — alle Druckansichten
+│   └── utils/updater.js      @tauri-apps/plugin-updater Wrapper
+│
+├── supabase/migrations/      Postgres-Schema für Cloud-Variante
+│
+├── src-tauri/                Rust-Backend (Tauri 2.10.3)
+│   ├── src/lib.rs            ~103 Z. — Tray, Window-Events, Plugins
+│   ├── tauri.conf.json       Updater-URL, Public-Key, CSP, Bundle-Targets
+│   └── capabilities/         Tauri Permissions
+│
+├── scripts/release.sh        Build + Sign + latest.json erzeugen
+├── latest.json               Updater-Manifest
+└── deploy/                   Docker-Compose für Web-Modus

Konventionen +

  • JavaScript statt TypeScript — bewusste Entscheidung für Solo-Dev-Velocity
  • Inline-Styles statt CSS-Framework
  • kein Routing-Frameworkview-State in App.jsx triggert Komponente
  • JSON-Schema implizit — definiert durch defaultData in constants.js
  • Migrationen als reine Funktionen in storage/migrations.js

Build +

Desktop (Tauri DMG) +

npm run build         # erst Vite-Build (dist/)
+npx tauri build       # dann Tauri-Bundle (DMG)

Output: src-tauri/target/release/bundle/dmg/Rapport_<version>_aarch64.dmg

Web (statisches Bundle) +

npm run build         # Output: dist/ (~500 KB)

Für Hosting auf einem Static-Server (z. B. Nginx, Caddy, GitHub Pages). Siehe Web-Modus.

Release-Workflow +

# 1 · Version bumpen in package.json + Cargo.toml + tauri.conf.json
+./scripts/release.sh 0.7.1
+
+# 2 · Build mit Signatur
+TAURI_SIGNING_PRIVATE_KEY=$(cat ~/.tauri/rapport-key) \
+TAURI_SIGNING_PRIVATE_KEY_PASSWORD=\
+npx tauri build
+
+# 3 · latest.json aktualisieren mit neuer Signatur
+
+# 4 · DMG + latest.json auf Gitea Releases hochladen

Beitragen +

Issues & Pull Requests sind willkommen. Wertvoll sind:

  • Bug-Reports mit Reproduktionsschritten
  • Workflow-Verbesserungen aus dem realen Büroalltag
  • Vorlagen (Briefe, Protokolle, Lieferscheine) für andere Büros
  • Übersetzungen / Internationalisierung (FR, IT)
  • Linux-/Windows-Builds und plattformspezifische Fixes

Vor grösseren Änderungen → Issue zum Diskutieren.

Lizenz +

GNU AGPL-3.0-or-later — eigene Builds erlaubt, Modifikationen müssen unter derselben Lizenz veröffentlicht werden, wenn die Software als Service angeboten wird.

\ No newline at end of file diff --git a/public/docs/erste-schritte/index.html b/public/docs/erste-schritte/index.html new file mode 100644 index 0000000..2a4114e --- /dev/null +++ b/public/docs/erste-schritte/index.html @@ -0,0 +1,44 @@ +Erste Schritte – RAPPORTZum Inhalt springen
Erste Schritte

Erste Schritte

Von der Installation bis zur ersten Rechnung — in sechs Schritten.

01 · Installation +

DMG von Gitea Releases herunterladen. Rapport in den Programme-Ordner ziehen. Beim ersten Start: Systemeinstellungen → Datenschutz & Sicherheit öffnen und Rapport zulassen.

Die Pre-Release-Builds sind signiert über Tauri, aber (noch) nicht über die Apple-Notarisierung gegangen — daher der manuelle Freigabe-Schritt.

02 · Einrichtung +

In den Einstellungen hinterlegen:

  • Bürodaten — Name, Adresse, IBAN, Logo
  • Mitarbeiter — Namen, Pensum, Stundensatz, Ferienanspruch
  • Kunden — Adresse, Ansprechperson, Honorartyp
  • Projekte — SIA-102-Phasen, Budget, Beteiligte

Danach ist die Zeiterfassung bereit.

03 · Zeiterfassung +

Im Modul Zeit:

  1. Mitarbeiter wählen
  2. Woche navigieren
  3. Stunden per Klick oder Drag erfassen
  4. Jedem Eintrag ein Projekt zuweisen

Auswertungen pro Mitarbeiter und pro Projekt sind unter Zeit → Auswertungen abrufbar. Halbe Tage und Mehrfacheinträge pro Slot werden unterstützt.

04 · Rechnungen +

Aus einer Offerte oder direkt erstellen:

  • SIA-Phasen, Stundensatz oder Pauschal wählen
  • Akonto-, Teil- oder Schlussrechnung
  • Mit PDF exportieren wird die fertige Rechnung inkl. QR-Einzahlungsschein generiert

Offerten lassen sich nahtlos in Projekte und Rechnungen konvertieren.

05 · Backup +

In der Desktop-App liegen alle Daten als JSON im Applikationsordner:

~/Library/Application Support/com.rapport.app/

Für ein Backup reicht es, diesen Ordner zu kopieren — z. B. auf einen externen Datenträger oder in die Cloud. Empfohlen: regelmässig (wöchentlich oder via Time Machine). Im Server-Modus läuft das Backup über Postgres-Dumps — siehe Rapport Server § Backup.

06 · Probleme melden +

Ein Issue auf Gitea erstellen — mit kurzer Beschreibung, was passiert ist. Screenshots helfen. Bitte die Rapport-Version (links unten in der App) angeben.

Tipp: Wenn die App nicht mehr startet, hilft oft, den Cache-Ordner zu sichern und neu zu starten. Die JSON-Daten selbst bleiben unverändert.

\ No newline at end of file diff --git a/public/docs/index.html b/public/docs/index.html new file mode 100644 index 0000000..24953f6 --- /dev/null +++ b/public/docs/index.html @@ -0,0 +1,34 @@ +Dokumentation – RAPPORTZum Inhalt springen \ No newline at end of file diff --git a/public/docs/index.xml b/public/docs/index.xml new file mode 100644 index 0000000..01c79dc --- /dev/null +++ b/public/docs/index.xml @@ -0,0 +1,1465 @@ +RAPPORT – Dokumentationhttps://rapport.gabrielevarano.ch/docs/Recent content in Dokumentation on RAPPORTHugo -- gohugo.iodeErste Schrittehttps://rapport.gabrielevarano.ch/docs/erste-schritte/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/docs/erste-schritte/ +<p>Von der Installation bis zur ersten Rechnung — in sechs Schritten.</p> +<h2>01 · Installation<span class="hx:absolute hx:-mt-20" id="01--installation"></span> +<a href="#01--installation" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>DMG von <a href="https://git.kgva.ch/karim/RAPPORT/releases"target="_blank" rel="noopener">Gitea Releases<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a> herunterladen. Rapport in den <strong>Programme-Ordner</strong> ziehen. Beim ersten Start: <em>Systemeinstellungen → Datenschutz &amp; Sicherheit</em> öffnen und Rapport zulassen.</p> +<p>Die Pre-Release-Builds sind <strong>signiert über Tauri</strong>, aber (noch) nicht über die Apple-Notarisierung gegangen — daher der manuelle Freigabe-Schritt.</p> +<h2>02 · Einrichtung<span class="hx:absolute hx:-mt-20" id="02--einrichtung"></span> +<a href="#02--einrichtung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>In den <strong>Einstellungen</strong> hinterlegen:</p> +<ul> +<li><strong>Bürodaten</strong> — Name, Adresse, IBAN, Logo</li> +<li><strong>Mitarbeiter</strong> — Namen, Pensum, Stundensatz, Ferienanspruch</li> +<li><strong>Kunden</strong> — Adresse, Ansprechperson, Honorartyp</li> +<li><strong>Projekte</strong> — SIA-102-Phasen, Budget, Beteiligte</li> +</ul> +<p>Danach ist die Zeiterfassung bereit.</p> +<h2>03 · Zeiterfassung<span class="hx:absolute hx:-mt-20" id="03--zeiterfassung"></span> +<a href="#03--zeiterfassung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Im Modul <strong>Zeit</strong>:</p> +<ol> +<li>Mitarbeiter wählen</li> +<li>Woche navigieren</li> +<li>Stunden per <strong>Klick</strong> oder <strong>Drag</strong> erfassen</li> +<li>Jedem Eintrag ein Projekt zuweisen</li> +</ol> +<p>Auswertungen pro Mitarbeiter und pro Projekt sind unter <em>Zeit → Auswertungen</em> abrufbar. Halbe Tage und Mehrfacheinträge pro Slot werden unterstützt.</p> +<h2>04 · Rechnungen<span class="hx:absolute hx:-mt-20" id="04--rechnungen"></span> +<a href="#04--rechnungen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Aus einer <strong>Offerte</strong> oder <strong>direkt</strong> erstellen:</p> +<ul> +<li>SIA-Phasen, Stundensatz oder Pauschal wählen</li> +<li>Akonto-, Teil- oder Schlussrechnung</li> +<li>Mit <em>PDF exportieren</em> wird die fertige Rechnung inkl. <strong>QR-Einzahlungsschein</strong> generiert</li> +</ul> +<p>Offerten lassen sich nahtlos in Projekte und Rechnungen konvertieren.</p> +<h2>05 · Backup<span class="hx:absolute hx:-mt-20" id="05--backup"></span> +<a href="#05--backup" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>In der Desktop-App liegen alle Daten als <strong>JSON</strong> im Applikationsordner:</p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">~/Library/Application Support/com.rapport.app/</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<p>Für ein Backup reicht es, diesen Ordner zu kopieren — z. B. auf einen externen Datenträger oder in die Cloud. Empfohlen: regelmässig (wöchentlich oder via Time Machine). Im Server-Modus läuft das Backup über Postgres-Dumps — siehe <a href="../../server/#backup">Rapport Server § Backup</a>.</p> +<h2>06 · Probleme melden<span class="hx:absolute hx:-mt-20" id="06--probleme-melden"></span> +<a href="#06--probleme-melden" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Ein <a href="https://git.kgva.ch/karim/RAPPORT/issues"target="_blank" rel="noopener">Issue auf Gitea<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a> erstellen — mit kurzer Beschreibung, was passiert ist. <strong>Screenshots helfen.</strong> Bitte die Rapport-Version (links unten in der App) angeben.</p> +<blockquote> +<p><strong>Tipp:</strong> Wenn die App nicht mehr startet, hilft oft, den Cache-Ordner zu sichern und neu zu starten. Die JSON-Daten selbst bleiben unverändert.</p> +</blockquote>Installationhttps://rapport.gabrielevarano.ch/docs/installation/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/docs/installation/ +<p>Schritt-für-Schritt-Anleitung für die Installation der Desktop-App.</p> +<h2>Voraussetzungen<span class="hx:absolute hx:-mt-20" id="voraussetzungen"></span> +<a href="#voraussetzungen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><table> +<thead> +<tr> +<th>Plattform</th> +<th>Status</th> +<th>Versionen</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>macOS</strong> Apple Silicon (M1 – M4)</td> +<td>✅ Unterstützt</td> +<td>macOS 12+</td> +</tr> +<tr> +<td><strong>macOS</strong> Intel</td> +<td>⚠ Build auf Anfrage</td> +<td>macOS 12+</td> +</tr> +<tr> +<td><strong>Linux</strong></td> +<td>🕐 Geplant</td> +<td>—</td> +</tr> +<tr> +<td><strong>Windows</strong></td> +<td>🕐 Geplant</td> +<td>—</td> +</tr> +</tbody> +</table> +<p>Eine Portierung auf Linux und Windows ist mit Tauri 2 möglich. <a href="https://git.kgva.ch/karim/RAPPORT/issues/new"target="_blank" rel="noopener">Issue erstellen<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a>, wenn du eine Plattform brauchst.</p> +<h2>1 · Download<span class="hx:absolute hx:-mt-20" id="1--download"></span> +<a href="#1--download" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Aktueller Build: <a href="../../downloads">Downloads-Seite</a> oder direkt <a href="https://git.kgva.ch/karim/RAPPORT/releases"target="_blank" rel="noopener">Releases auf Gitea<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a>.</p> +<table> +<thead> +<tr> +<th>Datei</th> +<th>Plattform</th> +</tr> +</thead> +<tbody> +<tr> +<td><code>RAPPORT_&lt;version&gt;_aarch64.dmg</code></td> +<td>macOS Apple Silicon</td> +</tr> +<tr> +<td><code>RAPPORT_&lt;version&gt;_x86_64.dmg</code></td> +<td>macOS Intel (auf Anfrage)</td> +</tr> +</tbody> +</table> +<h2>2 · DMG öffnen &amp; installieren<span class="hx:absolute hx:-mt-20" id="2--dmg-öffnen--installieren"></span> +<a href="#2--dmg-%c3%b6ffnen--installieren" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ol> +<li>DMG doppelklicken</li> +<li>Rapport.app in den <strong>Applications</strong>-Ordner ziehen</li> +<li>DMG auswerfen</li> +</ol> +<h2>3 · Erster Start (macOS Gatekeeper)<span class="hx:absolute hx:-mt-20" id="3--erster-start-macos-gatekeeper"></span> +<a href="#3--erster-start-macos-gatekeeper" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Beim ersten Start verweigert macOS den Start, weil die Pre-Release-Builds <strong>Tauri-signiert</strong>, aber (noch) <strong>nicht Apple-notarisiert</strong> sind.</p> +<h3>Lösung<span class="hx:absolute hx:-mt-20" id="lösung"></span> +<a href="#l%c3%b6sung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><ol> +<li><strong>Systemeinstellungen → Datenschutz &amp; Sicherheit</strong> öffnen</li> +<li>Bis ganz nach unten scrollen — es erscheint: +<blockquote> +<p>&ldquo;Rapport&rdquo; wurde blockiert, da es nicht von einem identifizierten Entwickler stammt.</p> +</blockquote> +</li> +<li>Auf <strong>&ldquo;Trotzdem öffnen&rdquo;</strong> klicken</li> +<li>Bestätigen</li> +</ol> +<p>Ab dem zweiten Start läuft Rapport ohne Rückfragen.</p> +<h3>Alternative (Terminal)<span class="hx:absolute hx:-mt-20" id="alternative-terminal"></span> +<a href="#alternative-terminal" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p>Falls der GUI-Weg nicht funktioniert:</p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">xattr -d com.apple.quarantine /Applications/Rapport.app</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<p>Das entfernt das Quarantäne-Flag und macOS akzeptiert den Start.</p> +<h2>4 · Erstes Setup<span class="hx:absolute hx:-mt-20" id="4--erstes-setup"></span> +<a href="#4--erstes-setup" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Beim ersten Start zeigt Rapport den <strong>Setup-Bildschirm</strong>. Hier werden die Stammdaten erfasst — siehe <a href="../einrichtung">Einrichtung</a>.</p> +<h2>5 · Update-Strategie<span class="hx:absolute hx:-mt-20" id="5--update-strategie"></span> +<a href="#5--update-strategie" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Ab Version 0.7.0 prüft Rapport beim Start automatisch auf neue Versionen (siehe <a href="../../features/auto-updater">Auto-Updater</a>). Updates können in den Einstellungen deaktiviert werden.</p> +<p>Manuelle Updates: einfach das neuere DMG installieren — die Daten bleiben erhalten, da sie unabhängig von der App liegen (siehe <a href="../datenhaltung">Datenhaltung</a>).</p> +<h2>Deinstallation<span class="hx:absolute hx:-mt-20" id="deinstallation"></span> +<a href="#deinstallation" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># App entfernen</span> +</span></span><span class="line"><span class="cl">rm -rf /Applications/Rapport.app +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># Daten entfernen (optional!)</span> +</span></span><span class="line"><span class="cl">rm -rf <span class="s2">&#34;~/Library/Application Support/com.rapport.app&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># Caches</span> +</span></span><span class="line"><span class="cl">rm -rf <span class="s2">&#34;~/Library/Caches/com.rapport.app&#34;</span> +</span></span><span class="line"><span class="cl">rm -rf <span class="s2">&#34;~/Library/WebKit/com.rapport.app&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<blockquote> +<p><strong>Achtung:</strong> Schritt 2 löscht <strong>alle Rapport-Daten unwiederbringlich</strong>. Vorher Backup machen — siehe <a href="../datenhaltung">Datenhaltung</a>.</p> +</blockquote> +<h2>Bekannte Probleme<span class="hx:absolute hx:-mt-20" id="bekannte-probleme"></span> +<a href="#bekannte-probleme" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><table> +<thead> +<tr> +<th>Problem</th> +<th>Lösung</th> +</tr> +</thead> +<tbody> +<tr> +<td>App lässt sich nicht öffnen trotz Freigabe</td> +<td>Terminal-Variante mit <code>xattr</code></td> +</tr> +<tr> +<td>&ldquo;Rapport is damaged&rdquo;</td> +<td>DMG erneut von Gitea ziehen (Browser-Cache hat evtl. Müll)</td> +</tr> +<tr> +<td>Schwarzer Bildschirm beim Start</td> +<td><code>~/Library/WebKit/com.rapport.app</code> löschen, neu starten</td> +</tr> +</tbody> +</table> +<p>Mehr unter <a href="../troubleshooting">Troubleshooting</a>.</p>Einrichtunghttps://rapport.gabrielevarano.ch/docs/einrichtung/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/docs/einrichtung/ +<p>Nach der <a href="../installation">Installation</a>: Bürodaten, Mitarbeiter, Kunden und Projekte initial anlegen.</p> +<h2>Reihenfolge<span class="hx:absolute hx:-mt-20" id="reihenfolge"></span> +<a href="#reihenfolge" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Die Reihenfolge ist wichtig — jede Stufe baut auf der vorherigen auf:</p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">1. Bürodaten → 2. Mitarbeiter → 3. Kunden → 4. Projekte +</span></span><span class="line"><span class="cl"> ▼ ▼ ▼ ▼ +</span></span><span class="line"><span class="cl"> Briefbogen, Zeiterfassung, Adressen, Zeiterfassung, +</span></span><span class="line"><span class="cl"> QR-Schein, Lohn Rechnungen Rechnungen +</span></span><span class="line"><span class="cl"> Login</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<h2>1 · Bürodaten<span class="hx:absolute hx:-mt-20" id="1--bürodaten"></span> +<a href="#1--b%c3%bcrodaten" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Einstellungen → Bürodaten</strong></p> +<table> +<thead> +<tr> +<th>Feld</th> +<th>Beschreibung</th> +<th>Verwendung</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Name</strong></td> +<td>Bürobezeichnung</td> +<td>Briefbogen, Login-Screen</td> +</tr> +<tr> +<td><strong>Adresse</strong></td> +<td>Strasse, PLZ, Ort</td> +<td>Briefbogen, QR-Rechnung (Empfänger)</td> +</tr> +<tr> +<td><strong>Telefon, E-Mail</strong></td> +<td>Kontaktdaten</td> +<td>Briefbogen, Rechnung-Footer</td> +</tr> +<tr> +<td><strong>IBAN</strong></td> +<td>CH-IBAN (Format <code>CH XX XXXX …</code>)</td> +<td><strong>QR-Einzahlungsschein</strong> — Pflicht</td> +</tr> +<tr> +<td><strong>Logo</strong></td> +<td>PNG/SVG-Upload</td> +<td>Briefbogen, Rechnung, Brief</td> +</tr> +<tr> +<td><strong>MwSt.-Nr.</strong></td> +<td>optional</td> +<td>Rechnung-Footer</td> +</tr> +</tbody> +</table> +<blockquote> +<p><strong>Tipp:</strong> Das Logo wird hochauflösend gespeichert (Base64 im localStorage). Bei sehr grossen Dateien (&gt;1 MB) vorher in Vorschau verkleinern.</p> +</blockquote> +<h2>2 · Mitarbeiter<span class="hx:absolute hx:-mt-20" id="2--mitarbeiter"></span> +<a href="#2--mitarbeiter" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Einstellungen → Mitarbeiter → Neu</strong></p> +<table> +<thead> +<tr> +<th>Feld</th> +<th>Beschreibung</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Name, Vorname</strong></td> +<td>wird in Zeiterfassung &amp; Lohn verwendet</td> +</tr> +<tr> +<td><strong>Initialen</strong></td> +<td>Kürzel für Auswertungen (z. B. &ldquo;KGE&rdquo;)</td> +</tr> +<tr> +<td><strong>Eintrittsdatum</strong></td> +<td>für <strong>Prorata</strong> der Ferien</td> +</tr> +<tr> +<td><strong>Pensum (%)</strong></td> +<td>100 = Vollzeit</td> +</tr> +<tr> +<td><strong>Stundensatz (CHF)</strong></td> +<td>für Stundensatz-Rechnungen</td> +</tr> +<tr> +<td><strong>Ferienanspruch (Tage/Jahr)</strong></td> +<td>i. d. R. 25–30</td> +</tr> +<tr> +<td><strong>Lohn (brutto, monatlich)</strong></td> +<td>optional, für Lohnabrechnung</td> +</tr> +</tbody> +</table> +<p>Mindestens <strong>ein Mitarbeiter</strong> (z. B. der Inhaber selbst) muss angelegt sein, sonst lässt sich keine Zeit erfassen.</p> +<h3>Sozialabzüge (optional)<span class="hx:absolute hx:-mt-20" id="sozialabzüge-optional"></span> +<a href="#sozialabz%c3%bcge-optional" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p>In Einstellungen → Lohn:</p> +<table> +<thead> +<tr> +<th>Abzug</th> +<th>Standardwert (CH)</th> +</tr> +</thead> +<tbody> +<tr> +<td>AHV / IV / EO</td> +<td>5,3 %</td> +</tr> +<tr> +<td>ALV</td> +<td>1,1 %</td> +</tr> +<tr> +<td>BVG (Pensionskasse)</td> +<td>variabel — je Mitarbeiter</td> +</tr> +<tr> +<td>NBU</td> +<td>je nach Versicherung</td> +</tr> +</tbody> +</table> +<p>Die Standardsätze sind hinterlegt, können aber überschrieben werden.</p> +<h2>3 · Kunden<span class="hx:absolute hx:-mt-20" id="3--kunden"></span> +<a href="#3--kunden" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Kunden → Neu</strong></p> +<table> +<thead> +<tr> +<th>Feld</th> +<th>Beschreibung</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Typ</strong></td> +<td>Privatperson / Firma</td> +</tr> +<tr> +<td><strong>Anrede, Name</strong></td> +<td>für Brief / Rechnung</td> +</tr> +<tr> +<td><strong>Adresse</strong></td> +<td>Strasse, PLZ, Ort, Land</td> +</tr> +<tr> +<td><strong>Ansprechperson</strong></td> +<td>bei Firmen</td> +</tr> +<tr> +<td><strong>Telefon, E-Mail</strong></td> +<td>Kontakt</td> +</tr> +<tr> +<td><strong>Honorartyp Default</strong></td> +<td>Stundensatz / SIA / Pauschal</td> +</tr> +<tr> +<td><strong>Stundensatz</strong></td> +<td>falls vom Bürostandard abweichend</td> +</tr> +<tr> +<td><strong>MwSt.-pflichtig</strong></td> +<td>ja/nein</td> +</tr> +</tbody> +</table> +<h2>4 · Projekte<span class="hx:absolute hx:-mt-20" id="4--projekte"></span> +<a href="#4--projekte" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Projekte → Neu</strong></p> +<table> +<thead> +<tr> +<th>Feld</th> +<th>Beschreibung</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Projekt-Nr.</strong></td> +<td>freie Form, oder generiert (<code>2026-001</code>)</td> +</tr> +<tr> +<td><strong>Bezeichnung</strong></td> +<td>Kurztitel</td> +</tr> +<tr> +<td><strong>Standort</strong></td> +<td>Adresse</td> +</tr> +<tr> +<td><strong>Kunde</strong></td> +<td>aus Kundendatenbank</td> +</tr> +<tr> +<td><strong>Bauschätzwert</strong></td> +<td>für SIA-Phasen-Honorar</td> +</tr> +<tr> +<td><strong>SIA-Phasen</strong></td> +<td>Vorprojekt / Bauprojekt / … — alle anwählbar</td> +</tr> +<tr> +<td><strong>Honorartyp</strong></td> +<td>Stundensatz / SIA 102 / Pauschal</td> +</tr> +<tr> +<td><strong>Status</strong></td> +<td>aktiv / pausiert / abgeschlossen</td> +</tr> +</tbody> +</table> +<h3>SIA-102-Phasen<span class="hx:absolute hx:-mt-20" id="sia-102-phasen"></span> +<a href="#sia-102-phasen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p>Wenn als Honorartyp <strong>SIA 102</strong> gewählt ist, werden die Phasen-Anteile am Gesamthonorar vorgeschlagen — siehe <a href="../../features/projekte#sia-102">Projekt-Feature</a>.</p> +<h2>Checkliste<span class="hx:absolute hx:-mt-20" id="checkliste"></span> +<a href="#checkliste" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Nach diesen vier Schritten ist Rapport einsatzbereit:</p> +<ul> +<li><input disabled="" type="checkbox"> Bürodaten inkl. IBAN erfasst</li> +<li><input disabled="" type="checkbox"> Mindestens ein Mitarbeiter angelegt</li> +<li><input disabled="" type="checkbox"> Erster Kunde angelegt</li> +<li><input disabled="" type="checkbox"> Erstes Projekt angelegt</li> +<li><input disabled="" type="checkbox"> Eine Test-Zeitbuchung erfasst — wird das Projekt korrekt zugewiesen?</li> +<li><input disabled="" type="checkbox"> Eine Test-Rechnung erstellt — kommt der QR-Schein sauber raus?</li> +</ul> +<p>Wenn alles funktioniert: <a href="../arbeitsablauf">Typischer Arbeitsablauf</a>.</p>Typischer Arbeitsablaufhttps://rapport.gabrielevarano.ch/docs/arbeitsablauf/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/docs/arbeitsablauf/ +<p>Vom Erstkontakt mit dem Kunden bis zur Schlussrechnung — der typische Weg eines Projekts durch Rapport.</p> +<h2>Übersicht<span class="hx:absolute hx:-mt-20" id="übersicht"></span> +<a href="#%c3%bcbersicht" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl"> Kunde → Offerte → Projekt → Zeit → Akonto → Schluss +</span></span><span class="line"><span class="cl"> anlegen erstellen (aus Offerte) erfassen -Rechnung -Rechnung +</span></span><span class="line"><span class="cl"> ↓ +</span></span><span class="line"><span class="cl"> QR-Schein</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<h2>1 · Kunde anlegen<span class="hx:absolute hx:-mt-20" id="1--kunde-anlegen"></span> +<a href="#1--kunde-anlegen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Kunden → Neu</strong> — siehe <a href="../einrichtung#3--kunden">Einrichtung § 3</a>.</p> +<p>Falls der Kunde später bestellt, lassen sich alle gesammelten Daten (Adresse, Honorartyp) direkt übernehmen.</p> +<h2>2 · Offerte erstellen<span class="hx:absolute hx:-mt-20" id="2--offerte-erstellen"></span> +<a href="#2--offerte-erstellen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Offerten → Neu</strong></p> +<table> +<thead> +<tr> +<th>Feld</th> +<th>Inhalt</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Kunde</strong></td> +<td>aus Kundendatenbank wählen</td> +</tr> +<tr> +<td><strong>Bezeichnung</strong></td> +<td>Projekttitel (z. B. &ldquo;Einfamilienhaus Müller, Bern&rdquo;)</td> +</tr> +<tr> +<td><strong>Honorartyp</strong></td> +<td>Stundensatz / SIA 102 / Pauschal</td> +</tr> +<tr> +<td><strong>Bauschätzwert</strong></td> +<td>bei SIA — Bruttowert in CHF</td> +</tr> +<tr> +<td><strong>Phasen</strong></td> +<td>bei SIA — anzuklickende Phasen</td> +</tr> +<tr> +<td><strong>Positionen</strong></td> +<td>bei Stundensatz/Pauschal — Position, Beschreibung, Betrag</td> +</tr> +</tbody> +</table> +<p>Mit <em>PDF exportieren</em> — fertige Offerte für den Kunden.</p> +<h3>Offerten-Status<span class="hx:absolute hx:-mt-20" id="offerten-status"></span> +<a href="#offerten-status" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><table> +<thead> +<tr> +<th>Status</th> +<th>Bedeutung</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Entwurf</strong></td> +<td>noch nicht versandt</td> +</tr> +<tr> +<td><strong>Versandt</strong></td> +<td>beim Kunden, wartet auf Antwort</td> +</tr> +<tr> +<td><strong>Angenommen</strong></td> +<td>Kunde hat zugesagt — bereit zur Konvertierung</td> +</tr> +<tr> +<td><strong>Abgelehnt</strong></td> +<td>Kunde hat abgelehnt — Archiv</td> +</tr> +</tbody> +</table> +<h2>3 · Offerte → Projekt<span class="hx:absolute hx:-mt-20" id="3--offerte--projekt"></span> +<a href="#3--offerte--projekt" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Bei Status <strong>&ldquo;Angenommen&rdquo;</strong>: Knopf <strong>&ldquo;In Projekt konvertieren&rdquo;</strong>.</p> +<p>Das erzeugt:</p> +<ul> +<li>Ein neues Projekt mit den Daten der Offerte (Kunde, Bezeichnung, Honorar, Phasen)</li> +<li>Eine Verknüpfung zwischen Offerte und Projekt</li> +<li>Die Offerte selbst bleibt im Archiv erhalten</li> +</ul> +<p>Siehe auch <a href="../../features/projekte">Projekt-Feature</a>.</p> +<h2>4 · Zeit erfassen<span class="hx:absolute hx:-mt-20" id="4--zeit-erfassen"></span> +<a href="#4--zeit-erfassen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Zeit → Mitarbeiter wählen → Woche navigieren</strong></p> +<ul> +<li><strong>Klick</strong> auf einen Halbstunden-Slot → Eintrag erstellen</li> +<li><strong>Drag</strong> über mehrere Slots → längerer Eintrag</li> +<li><strong>Projekt zuweisen</strong> (Pflichtfeld) — aus aktiven Projekten</li> +<li><strong>SIA-Phase</strong> zuweisen (optional, für detaillierte Auswertung)</li> +</ul> +<h3>Spezialfälle<span class="hx:absolute hx:-mt-20" id="spezialfälle"></span> +<a href="#spezialf%c3%a4lle" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><ul> +<li><strong>Ferien</strong> — eigener &ldquo;Projekt&rdquo;-Typ &ldquo;Ferien&rdquo;, in der Auswertung separat</li> +<li><strong>Krankheit/Absenz</strong> — eigener &ldquo;Projekt&rdquo;-Typ, ebenfalls separat</li> +<li><strong>Interne Stunden</strong> — z. B. Verwaltung, Marketing, IT — eigene interne Projekte</li> +</ul> +<h2>5 · Akonto-Rechnung<span class="hx:absolute hx:-mt-20" id="5--akonto-rechnung"></span> +<a href="#5--akonto-rechnung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Während der Projektlaufzeit — typisch nach jeder abgeschlossenen SIA-Phase oder monatlich.</p> +<p><strong>Rechnungen → Neu → Akonto</strong></p> +<table> +<thead> +<tr> +<th>Feld</th> +<th>Inhalt</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Projekt</strong></td> +<td>aus aktiven Projekten</td> +</tr> +<tr> +<td><strong>Bezug</strong></td> +<td>&ldquo;Akonto für Phase 31 — Vorprojekt&rdquo;</td> +</tr> +<tr> +<td><strong>Betrag</strong></td> +<td>Stundensatz × Stunden, oder Phasenanteil × Bausumme</td> +</tr> +<tr> +<td><strong>Fälligkeit</strong></td> +<td>i. d. R. 30 Tage</td> +</tr> +</tbody> +</table> +<p>Rapport zieht automatisch die geleisteten Stunden aus der Zeiterfassung — die kannst du als Basis nehmen oder überschreiben.</p> +<p>PDF inkl. <strong>QR-Einzahlungsschein</strong> — siehe <a href="../../features/rechnungen">Rechnungen-Feature</a>.</p> +<h2>6 · Schlussrechnung<span class="hx:absolute hx:-mt-20" id="6--schlussrechnung"></span> +<a href="#6--schlussrechnung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Nach Projektabschluss — Differenz aus Gesamthonorar minus aller Akonto-Beträge.</p> +<p><strong>Rechnungen → Neu → Schlussrechnung</strong></p> +<p>Rapport rechnet die bisherigen Akonto-Rechnungen automatisch ab:</p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Gesamthonorar (SIA / Pauschal / Stundensatz) +</span></span><span class="line"><span class="cl"> − Akonto-Rechnung 1 +</span></span><span class="line"><span class="cl"> − Akonto-Rechnung 2 +</span></span><span class="line"><span class="cl"> − Akonto-Rechnung 3 +</span></span><span class="line"><span class="cl"> = Schlussrechnung</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<h2>7 · Projektabschluss<span class="hx:absolute hx:-mt-20" id="7--projektabschluss"></span> +<a href="#7--projektabschluss" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Im Projekt → Status auf <strong>&ldquo;Abgeschlossen&rdquo;</strong>.</p> +<p>Das Projekt bleibt für historische Auswertungen sichtbar, taucht aber nicht mehr in der Zeiterfassungs-Auswahl auf.</p> +<h2>Auswertungen<span class="hx:absolute hx:-mt-20" id="auswertungen"></span> +<a href="#auswertungen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Wöchentlich / monatlich / am Jahresende:</p> +<ul> +<li><strong>Zeit → Auswertungen</strong> — Stunden pro Mitarbeiter, pro Projekt, pro Phase</li> +<li><strong>Rechnungen → Übersicht</strong> — offene Beträge, bezahlt, Mahnungen</li> +<li><strong>Buchhaltung → Erfolgsrechnung</strong> — Einnahmen vs. Ausgaben</li> +<li><strong>Mitarbeiter → Lohnabrechnung</strong> — monatlich</li> +</ul> +<h2>Tipps aus dem Alltag<span class="hx:absolute hx:-mt-20" id="tipps-aus-dem-alltag"></span> +<a href="#tipps-aus-dem-alltag" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><strong>Zeit jeden Tag erfassen</strong> statt rückwirkend — sonst gehen Details verloren</li> +<li><strong>Akonto regelmässig</strong> statt einmal am Schluss — Liquidität</li> +<li><strong>Backups</strong> vor Jahresabschluss — siehe <a href="../datenhaltung">Datenhaltung</a></li> +<li><strong>Briefbogen-Logo</strong> in hoher Auflösung — sieht im PDF besser aus</li> +</ul>Datenhaltung & Backuphttps://rapport.gabrielevarano.ch/docs/datenhaltung/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/docs/datenhaltung/ +<p>Wo Rapport seine Daten speichert, wie du sie sicherst und wiederherstellst.</p> +<blockquote> +<p><strong>Diese Seite beschreibt die Desktop-App (Single-User).</strong> Wer im Team arbeitet und Rapport gegen einen <a href="../../server/">Rapport Server</a> betreibt, sichert stattdessen die Postgres-Datenbank — siehe <a href="../../server/#backup">Rapport Server § Backup</a>.</p> +</blockquote> +<h2>Speicherort (Desktop-App)<span class="hx:absolute hx:-mt-20" id="speicherort-desktop-app"></span> +<a href="#speicherort-desktop-app" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Die Desktop-App speichert <strong>alles lokal</strong> — keine Cloud, kein Server.</p> +<h3>macOS<span class="hx:absolute hx:-mt-20" id="macos"></span> +<a href="#macos" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">~/Library/Application Support/com.rapport.app/</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<p>Dort liegt eine einzelne <strong><code>localStorage</code></strong>-Datenbank des WebView, in der <strong>alle</strong> Rapport-Daten als JSON unter dem Key <code>studio_data_v1</code> gespeichert sind:</p> +<ul> +<li>Bürodaten, Logo, IBAN</li> +<li>Mitarbeiter, Kunden, Projekte, Offerten</li> +<li>Zeit-Einträge, Rechnungen</li> +<li>Spesen, Lohnabrechnungen, Protokolle</li> +<li>App-Einstellungen</li> +</ul> +<blockquote> +<p><strong>Konsequenz:</strong> Wer den Application-Support-Ordner kopiert, hat ein vollständiges Backup. Wer ihn löscht, verliert alle Daten.</p> +</blockquote> +<h3>Warum localStorage?<span class="hx:absolute hx:-mt-20" id="warum-localstorage"></span> +<a href="#warum-localstorage" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p>In der Desktop-App ist Rapport eine <strong>Single-User-Anwendung</strong>. localStorage ist dafür:</p> +<ul> +<li><strong>Schnell</strong> — keine Datenbank-Roundtrips</li> +<li><strong>Einfach</strong> — keine Migration nötig, JSON-Schema im Code</li> +<li><strong>Portabel</strong> — eine Datei → ein Backup</li> +</ul> +<p>Für <strong>Multi-User-Betrieb</strong> existiert <a href="../../server/">Rapport Server</a> — Postgres + Auth + Realtime in einem Docker-Compose. Wechsel zwischen Desktop- und Server-Modus erfolgt im Login-Bildschirm der App.</p> +<h2>Backup-Strategien<span class="hx:absolute hx:-mt-20" id="backup-strategien"></span> +<a href="#backup-strategien" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><h3>A · Einfach (manuell)<span class="hx:absolute hx:-mt-20" id="a--einfach-manuell"></span> +<a href="#a--einfach-manuell" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p>Den ganzen Ordner kopieren:</p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cp -R <span class="s2">&#34;~/Library/Application Support/com.rapport.app&#34;</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"> <span class="s2">&#34;~/Documents/Rapport-Backup-</span><span class="k">$(</span>date +%Y%m%d<span class="k">)</span><span class="s2">&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<p>→ Auf USB-Stick, externen Datenträger oder in die Cloud.</p> +<h3>B · Time Machine<span class="hx:absolute hx:-mt-20" id="b--time-machine"></span> +<a href="#b--time-machine" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p>Wenn Time Machine läuft, ist der Ordner automatisch dabei. Versionierung inbegriffen.</p> +<blockquote> +<p><strong>Einschränkung:</strong> Time Machine sichert nur lokal/USB. Für off-site-Backup separat sorgen.</p> +</blockquote> +<h3>C · Cron-Job (täglich automatisch)<span class="hx:absolute hx:-mt-20" id="c--cron-job-täglich-automatisch"></span> +<a href="#c--cron-job-t%c3%a4glich-automatisch" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># In ~/Library/LaunchAgents/com.rapport.backup.plist hinterlegen</span> +</span></span><span class="line"><span class="cl"><span class="c1"># oder als crontab-Eintrag:</span> +</span></span><span class="line"><span class="cl"><span class="m">0</span> <span class="m">22</span> * * * rsync -a <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/Library/Application Support/com.rapport.app/&#34;</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/Backups/rapport/</span><span class="k">$(</span>date +<span class="se">\%</span>Y<span class="se">\%</span>m<span class="se">\%</span>d<span class="k">)</span><span class="s2">/&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<h3>D · iCloud Drive (off-site)<span class="hx:absolute hx:-mt-20" id="d--icloud-drive-off-site"></span> +<a href="#d--icloud-drive-off-site" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p>Application-Support liegt <strong>nicht</strong> automatisch in iCloud. Wer das will:</p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Symlink anlegen</span> +</span></span><span class="line"><span class="cl">mkdir -p <span class="s2">&#34;~/iCloud Drive/Rapport&#34;</span> +</span></span><span class="line"><span class="cl">ln -s <span class="s2">&#34;~/Library/Application Support/com.rapport.app&#34;</span> <span class="s2">&#34;~/iCloud Drive/Rapport/data&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<blockquote> +<p><strong>Achtung:</strong> iCloud-Sync mit aktiver App kann zu <strong>Race-Conditions</strong> führen. Besser den Sync zeitversetzt (z. B. nachts via Cron).</p> +</blockquote> +<h2>Wiederherstellung<span class="hx:absolute hx:-mt-20" id="wiederherstellung"></span> +<a href="#wiederherstellung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><h3>Vollständig (Rapport komplett tot)<span class="hx:absolute hx:-mt-20" id="vollständig-rapport-komplett-tot"></span> +<a href="#vollst%c3%a4ndig-rapport-komplett-tot" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><ol> +<li>Rapport beenden (Cmd+Q, nicht nur Fenster zu)</li> +<li>Aktuellen Ordner umbenennen (falls noch da): +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mv <span class="s2">&#34;~/Library/Application Support/com.rapport.app&#34;</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"> <span class="s2">&#34;~/Library/Application Support/com.rapport.app.bak&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +</li> +<li>Backup-Ordner zurück kopieren: +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cp -R <span class="s2">&#34;~/Documents/Rapport-Backup-20260523&#34;</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"> <span class="s2">&#34;~/Library/Application Support/com.rapport.app&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +</li> +<li>Rapport starten</li> +</ol> +<h3>Selektiv (nur einzelne Daten)<span class="hx:absolute hx:-mt-20" id="selektiv-nur-einzelne-daten"></span> +<a href="#selektiv-nur-einzelne-daten" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p>Da alle Daten in <strong>einem JSON unter <code>studio_data_v1</code></strong> liegen, ist selektive Wiederherstellung <strong>manuell</strong>:</p> +<ol> +<li>Backup-<code>localStorage</code>-Datei öffnen (WebKit-Format → mit Tool wie [WebKit Storage Inspector] lesen, oder via Rapport DevTools)</li> +<li>Gewünschte Felder in die aktuelle Instanz übertragen</li> +</ol> +<p>In der Praxis: meistens lohnt sich die <strong>vollständige</strong> Wiederherstellung mehr.</p> +<h2>Export-Funktionen (in der App)<span class="hx:absolute hx:-mt-20" id="export-funktionen-in-der-app"></span> +<a href="#export-funktionen-in-der-app" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Aus Rapport selbst:</p> +<table> +<thead> +<tr> +<th>Was</th> +<th>Wo</th> +<th>Format</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Zeit-Auswertung</strong></td> +<td>Zeit → Export</td> +<td>CSV</td> +</tr> +<tr> +<td><strong>Rechnung</strong></td> +<td>Rechnung → PDF</td> +<td>PDF (inkl. QR)</td> +</tr> +<tr> +<td><strong>Offerte</strong></td> +<td>Offerte → PDF</td> +<td>PDF</td> +</tr> +<tr> +<td><strong>Lohnabrechnung</strong></td> +<td>Mitarbeiter → PDF</td> +<td>PDF</td> +</tr> +<tr> +<td><strong>Jahres-Buchhaltung</strong></td> +<td>Buchhaltung → Export</td> +<td>CSV</td> +</tr> +</tbody> +</table> +<p>Die Exports sind für externe Verwendung (Buchhalter, Treuhänder, Archiv) gedacht — kein Full-Backup.</p> +<h2>Schema-Migrationen<span class="hx:absolute hx:-mt-20" id="schema-migrationen"></span> +<a href="#schema-migrationen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Bei Updates kann sich das Datenformat ändern. Rapport hat einen <strong>Migrations-Mechanismus</strong>: beim Start prüft die App, ob das gespeicherte Format dem aktuellen entspricht, und migriert es automatisch.</p> +<p>Code: <a href="https://git.kgva.ch/karim/RAPPORT/src/branch/main/APP/src/storage/migrations.js"target="_blank" rel="noopener">src/storage/migrations.js<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a>.</p> +<blockquote> +<p><strong>Empfehlung:</strong> Vor jedem grösseren Update ein Backup machen — Migrationen sind getestet, aber 100%-Sicherheit gibt es nicht.</p> +</blockquote> +<h2>Was wird <strong>nicht</strong> gespeichert?<span class="hx:absolute hx:-mt-20" id="was-wird-nicht-gespeichert"></span> +<a href="#was-wird-nicht-gespeichert" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><strong>WebView-Cache</strong> — <code>~/Library/Caches/com.rapport.app/</code> und <code>~/Library/WebKit/com.rapport.app/</code> sind sicher zu löschen (UI-Caches, regenerieren sich)</li> +<li><strong>App-Updates</strong> — werden bei Bedarf erneut runtergeladen</li> +<li><strong>Logs</strong> — <code>~/Library/Logs/com.rapport.app/</code> (geplant, derzeit nicht geschrieben)</li> +</ul> +<h2>Datenmenge<span class="hx:absolute hx:-mt-20" id="datenmenge"></span> +<a href="#datenmenge" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Typische Grössen pro Bürojahr:</p> +<table> +<thead> +<tr> +<th>Inhalt</th> +<th>Grösse</th> +</tr> +</thead> +<tbody> +<tr> +<td>Logo (PNG/SVG)</td> +<td>50 KB – 1 MB</td> +</tr> +<tr> +<td>1 Jahr Zeiterfassung (1 MA)</td> +<td>~ 80 KB</td> +</tr> +<tr> +<td>1 Jahr Zeiterfassung (5 MA)</td> +<td>~ 400 KB</td> +</tr> +<tr> +<td>50 Projekte mit je 5 Rechnungen</td> +<td>~ 800 KB</td> +</tr> +<tr> +<td><strong>Total typisches Solo-Büro / Jahr</strong></td> +<td><strong>~ 1–2 MB</strong></td> +</tr> +</tbody> +</table> +<p>localStorage hat Limits (i. d. R. ~10 MB pro Origin). Für Solo-Büros reicht das problemlos für viele Jahre. Wer das Limit erreicht oder im Team arbeitet → <a href="../../server/">Rapport Server</a>.</p>Web-Modus (Multi-User)https://rapport.gabrielevarano.ch/docs/web-modus/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/docs/web-modus/ +<blockquote> +<p><strong>Hinweis:</strong> Der frühere Supabase-basierte Web-Modus wurde durch <strong><a href="../../server/">Rapport Server</a></strong> abgelöst — den vollständigen Selfhost-Stack mit eigenem Postgres, Auth, Realtime und Storage. Keine externe Cloud-Abhängigkeit mehr.</p> +<p>Diese Seite bleibt als Referenz erhalten, der <strong>empfohlene Weg</strong> für Multi-User-Setups ist <strong><a href="../../server/">Rapport Server</a></strong>.</p> +</blockquote> +<h2>Wann brauchst du das?<span class="hx:absolute hx:-mt-20" id="wann-brauchst-du-das"></span> +<a href="#wann-brauchst-du-das" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><table> +<thead> +<tr> +<th>Anwendungsfall</th> +<th>Empfehlung</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Solo-Büro, ein Mac</strong></td> +<td>Desktop-App — siehe <a href="../installation">Installation</a></td> +</tr> +<tr> +<td><strong>2–5 Personen, gleicher Standort</strong></td> +<td><a href="../../server/">Rapport Server</a> auf einem Mac Mini im LAN</td> +</tr> +<tr> +<td><strong>Verteiltes Team / Home-Office</strong></td> +<td><a href="../../server/">Rapport Server</a> mit SSL + Reverse Proxy</td> +</tr> +<tr> +<td><strong>Hosted Backend (eigener VPS)</strong></td> +<td><a href="../../server/">Rapport Server</a> auf Linux-VPS</td> +</tr> +</tbody> +</table> +<h2>Architektur (Kurzfassung)<span class="hx:absolute hx:-mt-20" id="architektur-kurzfassung"></span> +<a href="#architektur-kurzfassung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">┌────────────┐ HTTPS ┌──────────────┐ SQL ┌────────────┐ +</span></span><span class="line"><span class="cl">│ Browser │ ──────────────│ nginx │ ─────────────│ Postgres │ +</span></span><span class="line"><span class="cl">│ / Desktop │ │ (Frontend) │ │ + GoTrue │ +</span></span><span class="line"><span class="cl">└────────────┘ └──────────────┘ │ + REST │ +</span></span><span class="line"><span class="cl"> │ + Realtime │ +</span></span><span class="line"><span class="cl"> │ + Storage │ +</span></span><span class="line"><span class="cl"> └────────────┘</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<ul> +<li><strong>Frontend:</strong> dieselbe React-App, aber Vite-Build statt Tauri (<code>npm run build</code>)</li> +<li><strong>Backend:</strong> Postgres-Stack (<a href="../../server/">Rapport Server</a>)</li> +<li><strong>Auth:</strong> E-Mail / Passwort über GoTrue</li> +<li><strong>Storage:</strong> Belege, Logos in Object-Storage</li> +</ul> +<h2>Setup<span class="hx:absolute hx:-mt-20" id="setup"></span> +<a href="#setup" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Alle Setup-Schritte (Repo klonen, <code>.env</code> erstellen, Migrations syncen, Docker-Compose starten, Reverse-Proxy konfigurieren) sind in <strong><a href="../../server/">Rapport Server</a></strong> dokumentiert.</p> +<h2>Migration Desktop → Cloud<span class="hx:absolute hx:-mt-20" id="migration-desktop--cloud"></span> +<a href="#migration-desktop--cloud" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Wer mit der Desktop-App startet und später auf den Web-Modus wechseln möchte:</p> +<ul> +<li><strong>Aktuell:</strong> manueller Export aus Rapport (CSV/PDF) und manuelles Wiederanlegen im neuen Setup</li> +<li><strong>Geplant:</strong> <em>&ldquo;localStorage → Postgres&rdquo;</em>-Import-Knopf direkt in der App</li> +</ul> +<p>Status: <a href="https://git.kgva.ch/karim/RAPPORT/issues"target="_blank" rel="noopener">Issue auf Gitea<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a>.</p> +<h2>Troubleshooting<span class="hx:absolute hx:-mt-20" id="troubleshooting"></span> +<a href="#troubleshooting" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Siehe <a href="../../server/">Rapport Server § Troubleshooting</a> oder <a href="../troubleshooting">allgemeine Troubleshooting-Seite</a>.</p>Entwicklung & Buildhttps://rapport.gabrielevarano.ch/docs/entwicklung/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/docs/entwicklung/ +<p>Aus dem Quellcode kompilieren, beitragen, eigenes Release erzeugen.</p> +<h2>Voraussetzungen<span class="hx:absolute hx:-mt-20" id="voraussetzungen"></span> +<a href="#voraussetzungen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><table> +<thead> +<tr> +<th>Tool</th> +<th>Version</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Node.js</strong></td> +<td>≥ 20 (für Vite 8)</td> +</tr> +<tr> +<td><strong>npm</strong></td> +<td>≥ 10</td> +</tr> +<tr> +<td><strong>Rust toolchain</strong></td> +<td>≥ 1.77.2 (via <code>rustup</code>)</td> +</tr> +<tr> +<td><strong>Plattform-Tools</strong></td> +<td>siehe <a href="https://v2.tauri.app/start/prerequisites/"target="_blank" rel="noopener">Tauri Prerequisites<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a></td> +</tr> +</tbody> +</table> +<p>Plattform-spezifisch:</p> +<ul> +<li><strong>macOS:</strong> Xcode Command Line Tools (<code>xcode-select --install</code>)</li> +<li><strong>Windows:</strong> Microsoft C++ Build Tools + WebView2</li> +<li><strong>Linux:</strong> <code>webkit2gtk-4.1</code>, <code>librsvg2-dev</code>, <code>libayatana-appindicator3-dev</code>, <code>build-essential</code></li> +</ul> +<h2>Setup<span class="hx:absolute hx:-mt-20" id="setup"></span> +<a href="#setup" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://git.kgva.ch/karim/RAPPORT.git +</span></span><span class="line"><span class="cl"><span class="nb">cd</span> RAPPORT/APP +</span></span><span class="line"><span class="cl">npm install</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<h2>Entwicklung<span class="hx:absolute hx:-mt-20" id="entwicklung"></span> +<a href="#entwicklung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><h3>Web-Modus (HMR, schnellster Loop)<span class="hx:absolute hx:-mt-20" id="web-modus-hmr-schnellster-loop"></span> +<a href="#web-modus-hmr-schnellster-loop" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm run dev <span class="c1"># http://localhost:3000</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<ul> +<li>Hot-Module-Replacement</li> +<li>Schnellster Iteration-Loop für UI-Arbeit</li> +<li>Datenpersistierung: Browser-localStorage</li> +</ul> +<h3>Native Window (Tauri-Fenster mit Desktop-Integration)<span class="hx:absolute hx:-mt-20" id="native-window-tauri-fenster-mit-desktop-integration"></span> +<a href="#native-window-tauri-fenster-mit-desktop-integration" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npx tauri dev</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<ul> +<li>Echtes Tauri-Fenster</li> +<li>System-Tray, Updater, native APIs verfügbar</li> +<li>Erster Start dauert lange (Rust-Compile)</li> +</ul> +<h2>Architektur in einem Absatz<span class="hx:absolute hx:-mt-20" id="architektur-in-einem-absatz"></span> +<a href="#architektur-in-einem-absatz" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><blockquote> +<p>RAPPORT ist eine monolithische SPA: ein React-Root in <a href="https://git.kgva.ch/karim/RAPPORT/src/branch/main/APP/src/App.jsx"target="_blank" rel="noopener">App.jsx<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a> hält den <strong>gesamten</strong> App-State in einem <code>useState({...})</code>, persistiert ihn synchron in <code>localStorage</code> unter <code>studio_data_v1</code>, und übergibt ihn als Props an lazy-geladene Views. <strong>Kein Routing-Framework</strong>, <strong>kein State-Library</strong>, <strong>kein TypeScript</strong>, <strong>kein CSS-Framework</strong>. Der <strong>Rust-Teil</strong> ist 109 Zeilen und macht nur drei Dinge: System-Tray, Window-Hide-on-Close, Plugin-Registrierung (Updater, Process, Log). <strong>Keine</strong> <code>#[tauri::command]</code> — Frontend ↔ Backend kommuniziert nur über das Event <code>rapport:navigate</code> (Tray → Frontend).</p> +</blockquote> +<p>Detaillierte Karte: <a href="https://git.kgva.ch/karim/RAPPORT/src/branch/main/APP/ARCHITECTURE.md"target="_blank" rel="noopener">ARCHITECTURE.md<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a>.</p> +<h2>Verzeichnis-Karte<span class="hx:absolute hx:-mt-20" id="verzeichnis-karte"></span> +<a href="#verzeichnis-karte" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">APP/ +</span></span><span class="line"><span class="cl">├── src/ React 19 (kein TS, nur .jsx) +</span></span><span class="line"><span class="cl">│ ├── App.jsx Root: State, Navigation, Sidebar, Modals +</span></span><span class="line"><span class="cl">│ ├── constants.js STORAGE_KEY, NAV_ITEMS, defaultData, SIA-Phasen +</span></span><span class="line"><span class="cl">│ ├── utils.js Business-Logik: Kalkulation, QR-Bill, CSV, Lohn +</span></span><span class="line"><span class="cl">│ ├── storage/adapter.js LocalStorageAdapter (Phase 1), SupabaseAdapter (Phase 2) +</span></span><span class="line"><span class="cl">│ ├── storage/migrations.js Schema-Migrationen +</span></span><span class="line"><span class="cl">│ ├── views/ 20 Top-Level-Screens, lazy-geladen +</span></span><span class="line"><span class="cl">│ ├── components/UI.jsx StatusBadge, Modal, FormField, … +</span></span><span class="line"><span class="cl">│ ├── components/Update* Auto-Update-UI +</span></span><span class="line"><span class="cl">│ ├── print/ PrintComponents.jsx — alle Druckansichten +</span></span><span class="line"><span class="cl">│ └── utils/updater.js @tauri-apps/plugin-updater Wrapper +</span></span><span class="line"><span class="cl">│ +</span></span><span class="line"><span class="cl">├── supabase/migrations/ Postgres-Schema für Cloud-Variante +</span></span><span class="line"><span class="cl">│ +</span></span><span class="line"><span class="cl">├── src-tauri/ Rust-Backend (Tauri 2.10.3) +</span></span><span class="line"><span class="cl">│ ├── src/lib.rs ~103 Z. — Tray, Window-Events, Plugins +</span></span><span class="line"><span class="cl">│ ├── tauri.conf.json Updater-URL, Public-Key, CSP, Bundle-Targets +</span></span><span class="line"><span class="cl">│ └── capabilities/ Tauri Permissions +</span></span><span class="line"><span class="cl">│ +</span></span><span class="line"><span class="cl">├── scripts/release.sh Build + Sign + latest.json erzeugen +</span></span><span class="line"><span class="cl">├── latest.json Updater-Manifest +</span></span><span class="line"><span class="cl">└── deploy/ Docker-Compose für Web-Modus</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<h2>Konventionen<span class="hx:absolute hx:-mt-20" id="konventionen"></span> +<a href="#konventionen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><strong>JavaScript</strong> statt TypeScript — bewusste Entscheidung für Solo-Dev-Velocity</li> +<li><strong>Inline-Styles</strong> statt CSS-Framework</li> +<li><strong>kein Routing-Framework</strong> — <code>view</code>-State in App.jsx triggert Komponente</li> +<li><strong>JSON-Schema implizit</strong> — definiert durch <code>defaultData</code> in <code>constants.js</code></li> +<li><strong>Migrationen</strong> als reine Funktionen in <a href="https://git.kgva.ch/karim/RAPPORT/src/branch/main/APP/src/storage/migrations.js"target="_blank" rel="noopener">storage/migrations.js<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a></li> +</ul> +<h2>Build<span class="hx:absolute hx:-mt-20" id="build"></span> +<a href="#build" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><h3>Desktop (Tauri DMG)<span class="hx:absolute hx:-mt-20" id="desktop-tauri-dmg"></span> +<a href="#desktop-tauri-dmg" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm run build <span class="c1"># erst Vite-Build (dist/)</span> +</span></span><span class="line"><span class="cl">npx tauri build <span class="c1"># dann Tauri-Bundle (DMG)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<p>Output: <code>src-tauri/target/release/bundle/dmg/Rapport_&lt;version&gt;_aarch64.dmg</code></p> +<h3>Web (statisches Bundle)<span class="hx:absolute hx:-mt-20" id="web-statisches-bundle"></span> +<a href="#web-statisches-bundle" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm run build <span class="c1"># Output: dist/ (~500 KB)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<p>Für Hosting auf einem Static-Server (z. B. Nginx, Caddy, GitHub Pages). Siehe <a href="../web-modus">Web-Modus</a>.</p> +<h2>Release-Workflow<span class="hx:absolute hx:-mt-20" id="release-workflow"></span> +<a href="#release-workflow" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1 · Version bumpen in package.json + Cargo.toml + tauri.conf.json</span> +</span></span><span class="line"><span class="cl">./scripts/release.sh 0.7.1 +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 2 · Build mit Signatur</span> +</span></span><span class="line"><span class="cl"><span class="nv">TAURI_SIGNING_PRIVATE_KEY</span><span class="o">=</span><span class="k">$(</span>cat ~/.tauri/rapport-key<span class="k">)</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"><span class="nv">TAURI_SIGNING_PRIVATE_KEY_PASSWORD</span><span class="o">=</span>… <span class="se">\ +</span></span></span><span class="line"><span class="cl">npx tauri build +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 3 · latest.json aktualisieren mit neuer Signatur</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># 4 · DMG + latest.json auf Gitea Releases hochladen</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<h2>Beitragen<span class="hx:absolute hx:-mt-20" id="beitragen"></span> +<a href="#beitragen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><a href="https://git.kgva.ch/karim/RAPPORT"target="_blank" rel="noopener">Issues &amp; Pull Requests<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a> sind willkommen. Wertvoll sind:</p> +<ul> +<li><strong>Bug-Reports</strong> mit Reproduktionsschritten</li> +<li><strong>Workflow-Verbesserungen</strong> aus dem realen Büroalltag</li> +<li><strong>Vorlagen</strong> (Briefe, Protokolle, Lieferscheine) für andere Büros</li> +<li><strong>Übersetzungen / Internationalisierung</strong> (FR, IT)</li> +<li><strong>Linux-/Windows-Builds</strong> und plattformspezifische Fixes</li> +</ul> +<p>Vor grösseren Änderungen → Issue zum Diskutieren.</p> +<h2>Lizenz<span class="hx:absolute hx:-mt-20" id="lizenz"></span> +<a href="#lizenz" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>GNU AGPL-3.0-or-later</strong> — eigene Builds erlaubt, Modifikationen müssen unter derselben Lizenz veröffentlicht werden, wenn die Software als Service angeboten wird.</p>Troubleshootinghttps://rapport.gabrielevarano.ch/docs/troubleshooting/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/docs/troubleshooting/ +<p>Typische Probleme und Lösungen. Wenn dein Problem nicht dabei ist → <a href="https://git.kgva.ch/karim/RAPPORT/issues"target="_blank" rel="noopener">Issue auf Gitea<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a>.</p> +<h2>App startet nicht<span class="hx:absolute hx:-mt-20" id="app-startet-nicht"></span> +<a href="#app-startet-nicht" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><h3>&ldquo;Rapport ist beschädigt&rdquo; beim ersten Start<span class="hx:absolute hx:-mt-20" id="rapport-ist-beschädigt-beim-ersten-start"></span> +<a href="#rapport-ist-besch%c3%a4digt-beim-ersten-start" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Ursache:</strong> macOS Gatekeeper blockt unsignierte/nicht-notarisierte Apps.</p> +<p><strong>Lösung:</strong> siehe <a href="../installation#3--erster-start-macos-gatekeeper">Installation § 3</a>. Kurz:</p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">xattr -d com.apple.quarantine /Applications/Rapport.app</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<h3>App startet, zeigt aber schwarzen Bildschirm<span class="hx:absolute hx:-mt-20" id="app-startet-zeigt-aber-schwarzen-bildschirm"></span> +<a href="#app-startet-zeigt-aber-schwarzen-bildschirm" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Ursache:</strong> WebView-Cache korrupt.</p> +<p><strong>Lösung:</strong></p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rm -rf <span class="s2">&#34;~/Library/Caches/com.rapport.app&#34;</span> +</span></span><span class="line"><span class="cl">rm -rf <span class="s2">&#34;~/Library/WebKit/com.rapport.app&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<p>App neu starten. Daten gehen dabei <strong>nicht</strong> verloren (liegen in <code>Application Support</code>, nicht im Cache).</p> +<h3>App stürzt sofort beim Start ab<span class="hx:absolute hx:-mt-20" id="app-stürzt-sofort-beim-start-ab"></span> +<a href="#app-st%c3%bcrzt-sofort-beim-start-ab" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Ursache:</strong> wahrscheinlich beschädigte JSON-Daten in <code>studio_data_v1</code>.</p> +<p><strong>Diagnose:</strong></p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Daten ansehen (DevTools-Output)</span> +</span></span><span class="line"><span class="cl">open <span class="s2">&#34;~/Library/Application Support/com.rapport.app&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<p><strong>Lösung:</strong></p> +<ol> +<li>Backup wiederherstellen (siehe <a href="../datenhaltung#wiederherstellung">Datenhaltung</a>)</li> +<li><strong>Oder</strong> als letzter Ausweg: Daten zurücksetzen +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mv <span class="s2">&#34;~/Library/Application Support/com.rapport.app&#34;</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"> <span class="s2">&#34;~/Library/Application Support/com.rapport.app.bad&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +App neu starten → erstellt frische Daten. Anschliessend Setup-Screen.</li> +</ol> +<h2>Daten weg<span class="hx:absolute hx:-mt-20" id="daten-weg"></span> +<a href="#daten-weg" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><h3>Nach einem App-Update fehlen Einträge<span class="hx:absolute hx:-mt-20" id="nach-einem-app-update-fehlen-einträge"></span> +<a href="#nach-einem-app-update-fehlen-eintr%c3%a4ge" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Ursache:</strong> mögliche fehlgeschlagene Migration.</p> +<p><strong>Sofortmassnahme:</strong></p> +<ol> +<li>Rapport <strong>beenden</strong> (Cmd+Q, nicht nur Fenster zu)</li> +<li><strong>Aktuelles Datenverzeichnis sichern</strong>: +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cp -R <span class="s2">&#34;~/Library/Application Support/com.rapport.app&#34;</span> <span class="se">\ +</span></span></span><span class="line"><span class="cl"> <span class="s2">&#34;~/Documents/Rapport-Notfall-</span><span class="k">$(</span>date +%Y%m%d-%H%M<span class="k">)</span><span class="s2">&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +</li> +<li><a href="https://git.kgva.ch/karim/RAPPORT/issues/new"target="_blank" rel="noopener">Issue erstellen<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a> mit: +<ul> +<li>Version vor dem Update (falls bekannt)</li> +<li>Version nach dem Update</li> +<li>Was fehlt</li> +<li>Optional: gesicherter Datenordner (via Pastebin oder verschlüsselt zugesandt)</li> +</ul> +</li> +</ol> +<h3>localStorage voll<span class="hx:absolute hx:-mt-20" id="localstorage-voll"></span> +<a href="#localstorage-voll" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Symptom:</strong> Rapport schreibt Fehler-Toast &ldquo;Speicher voll&rdquo; beim Sichern.</p> +<p><strong>Ursache:</strong> macOS WebView limitiert localStorage auf ~10 MB pro Origin.</p> +<p><strong>Lösung:</strong></p> +<ul> +<li>Sehr grosse Logos durch kleinere ersetzen (Bürodaten → Logo)</li> +<li>Belege (Spesen) selektiv löschen oder als externe Datei archivieren</li> +<li>Auf <a href="../web-modus">Web-Modus</a> wechseln (Postgres ohne praktisches Limit)</li> +</ul> +<h2>Updates<span class="hx:absolute hx:-mt-20" id="updates"></span> +<a href="#updates" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><h3>Auto-Update findet nichts<span class="hx:absolute hx:-mt-20" id="auto-update-findet-nichts"></span> +<a href="#auto-update-findet-nichts" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Diagnose:</strong></p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s https://git.kgva.ch/karim/RAPPORT/raw/branch/main/APP/latest.json</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<p>→ sollte JSON liefern. Wenn nicht: Server-/Netzwerkproblem.</p> +<h3>Update lädt, lässt sich aber nicht installieren<span class="hx:absolute hx:-mt-20" id="update-lädt-lässt-sich-aber-nicht-installieren"></span> +<a href="#update-l%c3%a4dt-l%c3%a4sst-sich-aber-nicht-installieren" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Ursache:</strong> Signaturprüfung scheitert (Public-Key in App ≠ Signatur in <code>latest.json</code>).</p> +<p><strong>Lösung:</strong> Manuelles Update — DMG direkt von <a href="https://git.kgva.ch/karim/RAPPORT/releases"target="_blank" rel="noopener">Releases<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a> laden und installieren. Daten bleiben erhalten.</p> +<h3>&ldquo;Diese Version überspringen&rdquo; rückgängig machen<span class="hx:absolute hx:-mt-20" id="diese-version-überspringen-rückgängig-machen"></span> +<a href="#diese-version-%c3%bcberspringen-r%c3%bcckg%c3%a4ngig-machen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p>In <strong>Einstellungen → Updates</strong> → <em>Übersprungene Versionen zurücksetzen</em>. Beim nächsten Start wird die Version wieder angeboten.</p> +<h2>PDF / QR-Schein<span class="hx:absolute hx:-mt-20" id="pdf--qr-schein"></span> +<a href="#pdf--qr-schein" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><h3>QR-Schein hat falsche Daten<span class="hx:absolute hx:-mt-20" id="qr-schein-hat-falsche-daten"></span> +<a href="#qr-schein-hat-falsche-daten" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Diagnose-Checkliste:</strong></p> +<ul> +<li><input disabled="" type="checkbox"> IBAN korrekt? (CH, 21 Zeichen, keine Leerzeichen)</li> +<li><input disabled="" type="checkbox"> Empfänger-Adresse vollständig? (PLZ und Ort beide gefüllt)</li> +<li><input disabled="" type="checkbox"> Schuldner-Adresse vollständig?</li> +<li><input disabled="" type="checkbox"> Betrag &gt; 0?</li> +<li><input disabled="" type="checkbox"> Referenz nicht zu lang? (max 27 Zeichen)</li> +</ul> +<p>QR-Bibliothek: <a href="https://github.com/schoero/SwissQRBill"target="_blank" rel="noopener"><code>swissqrbill</code><svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a> — bei merkwürdigen Fehlern dort nachschauen.</p> +<h3>PDF-Export ist leer / weisses Blatt<span class="hx:absolute hx:-mt-20" id="pdf-export-ist-leer--weisses-blatt"></span> +<a href="#pdf-export-ist-leer--weisses-blatt" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Ursache:</strong> Print-View hat keine Daten (möglicherweise Race-Condition beim Laden).</p> +<p><strong>Lösung:</strong> Rechnung schliessen, erneut öffnen, dann PDF.</p> +<h3>PDF-Schrift sieht falsch aus<span class="hx:absolute hx:-mt-20" id="pdf-schrift-sieht-falsch-aus"></span> +<a href="#pdf-schrift-sieht-falsch-aus" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Ursache:</strong> Web-Schrift nicht geladen, Fallback greift.</p> +<p><strong>Lösung:</strong> Vor dem Drucken warten, bis das Vorschau-Bild komplett geladen ist (3–5 Sek).</p> +<h2>System-Tray<span class="hx:absolute hx:-mt-20" id="system-tray"></span> +<a href="#system-tray" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><h3>Tray-Icon erscheint nicht<span class="hx:absolute hx:-mt-20" id="tray-icon-erscheint-nicht"></span> +<a href="#tray-icon-erscheint-nicht" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Plattform-Hinweis:</strong> Tray-Icons unter macOS sind bei extrem voller Menüleiste oder unter &ldquo;Bartender&rdquo;/&ldquo;Hidden Bar&rdquo; eventuell unsichtbar.</p> +<p><strong>Diagnose:</strong></p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ps aux <span class="p">|</span> grep -i rapport</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<p>→ wenn Prozess läuft, aber kein Icon: in den Tray-Manager-Apps prüfen.</p> +<p><strong>Konfiguration:</strong> Einstellungen → System-Tray → <em>Tray-Icon ausblenden</em> (aus → Icon erzwingen).</p> +<h3>Tray-Menü reagiert nicht / hängt<span class="hx:absolute hx:-mt-20" id="tray-menü-reagiert-nicht--hängt"></span> +<a href="#tray-men%c3%bc-reagiert-nicht--h%c3%a4ngt" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Lösung:</strong> App via <em>Activity Monitor</em> hart beenden und neu starten. Daten gehen nicht verloren (alle Schreibungen sind synchron in localStorage).</p> +<h2>Web-Modus<span class="hx:absolute hx:-mt-20" id="web-modus"></span> +<a href="#web-modus" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><h3>Login-Screen zeigt keine Server-URL<span class="hx:absolute hx:-mt-20" id="login-screen-zeigt-keine-server-url"></span> +<a href="#login-screen-zeigt-keine-server-url" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Ursache:</strong> <code>.env.production</code> enthielt nicht den richtigen <code>VITE_SUPABASE_URL</code> zur Build-Zeit.</p> +<p><strong>Lösung:</strong> <code>.env.production</code> prüfen, dann <code>npm run build</code> neu, Container restart.</p> +<h3>Realtime-Updates kommen nicht an<span class="hx:absolute hx:-mt-20" id="realtime-updates-kommen-nicht-an"></span> +<a href="#realtime-updates-kommen-nicht-an" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h3><p><strong>Ursache:</strong> Websocket-Support fehlt im Reverse Proxy.</p> +<p><strong>Lösung:</strong> In Nginx Proxy Manager für <code>api.*</code> Websocket Support aktivieren.</p> +<p>Siehe <a href="../web-modus#troubleshooting">Web-Modus § Troubleshooting</a>.</p> +<h2>Debug-Informationen sammeln<span class="hx:absolute hx:-mt-20" id="debug-informationen-sammeln"></span> +<a href="#debug-informationen-sammeln" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Bei einem Issue helfen folgende Infos:</p> +<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Rapport-Version</span> +</span></span><span class="line"><span class="cl">defaults <span class="nb">read</span> /Applications/Rapport.app/Contents/Info.plist CFBundleShortVersionString +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># macOS-Version</span> +</span></span><span class="line"><span class="cl">sw_vers +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># Architektur</span> +</span></span><span class="line"><span class="cl">uname -m +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># Datenverzeichnis-Grösse</span> +</span></span><span class="line"><span class="cl">du -sh <span class="s2">&#34;~/Library/Application Support/com.rapport.app&#34;</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="c1"># Cache-Verzeichnis-Grösse</span> +</span></span><span class="line"><span class="cl">du -sh <span class="s2">&#34;~/Library/Caches/com.rapport.app&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div> +<p>→ Bei Issue mit anhängen.</p> +<h2>Wenn nichts hilft<span class="hx:absolute hx:-mt-20" id="wenn-nichts-hilft"></span> +<a href="#wenn-nichts-hilft" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><a href="https://git.kgva.ch/karim/RAPPORT/issues/new"target="_blank" rel="noopener">Neues Issue auf Gitea<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a> mit:</p> +<ul> +<li>Was du gemacht hast</li> +<li>Was passiert ist</li> +<li>Was du erwartet hättest</li> +<li>Screenshots (auch von DevTools-Konsole falls möglich)</li> +<li>Rapport-Version und macOS-Version</li> +</ul>Changeloghttps://rapport.gabrielevarano.ch/docs/changelog/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/docs/changelog/ +<p>Versionsgeschichte von RAPPORT. Aktuelle Releases: <a href="https://git.kgva.ch/karim/RAPPORT/releases"target="_blank" rel="noopener">Gitea<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a>.</p> +<h2>0.8.3 — Aktuelle Version <span class="rapport-status new">Aktuell</span><span class="hx:absolute hx:-mt-20" id="083--aktuelle-version-aktuell"></span> +<a href="#083--aktuelle-version-aktuell" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Veröffentlicht am 2026-05-24.</p> +<p><strong>Neu / Verbessert</strong></p> +<ul> +<li>Diverse Verbesserungen und Bugfixes (Details werden im <a href="https://git.kgva.ch/karim/RAPPORT/releases/tag/0.8.3"target="_blank" rel="noopener">Release auf Gitea<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a> gepflegt)</li> +</ul> +<p><strong>Bekannte Einschränkungen</strong></p> +<ul> +<li>Builds sind Tauri-signiert, aber noch nicht Apple-notarisiert — siehe <a href="../installation#3--erster-start-macos-gatekeeper">Installation § Gatekeeper</a></li> +<li>Linux- und Windows-Builds noch nicht verfügbar</li> +</ul> +<h2>0.8.0–0.8.2 — Patch-Releases<span class="hx:absolute hx:-mt-20" id="080082--patch-releases"></span> +<a href="#080082--patch-releases" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Patch-Reihe mit kleineren Verbesserungen und Bugfixes. Details siehe <a href="https://git.kgva.ch/karim/RAPPORT/releases"target="_blank" rel="noopener">Releases auf Gitea<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a>.</p> +<h2>0.7.0 — Auto-Updater &amp; System-Tray<span class="hx:absolute hx:-mt-20" id="070--auto-updater--system-tray"></span> +<a href="#070--auto-updater--system-tray" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Neu</strong></p> +<ul> +<li><strong>Auto-Updater</strong> — Rapport prüft beim Start auf neue Versionen und installiert Updates signiert über Tauri. Einzelne Versionen können übersprungen werden. (<a href="../../features/auto-updater">Doku</a>)</li> +<li><strong>System-Tray</strong> — Schnellzugriff über die Menüleiste mit Hide-on-Close. Beim Schliessen läuft Rapport im Hintergrund weiter, Cmd+Q beendet die App vollständig. (<a href="../../features/system-tray">Doku</a>)</li> +<li><strong>Quick-Open</strong> der letzten 5 Projekte direkt aus dem Tray-Menü</li> +</ul> +<p><strong>Verbessert</strong></p> +<ul> +<li>Schnellerer App-Start durch lazy-geladene Views</li> +<li>Klarere Statusbadges in der Projekt-Übersicht</li> +</ul> +<h2>0.6.x — Spesen &amp; Buchhaltung<span class="hx:absolute hx:-mt-20" id="06x--spesen--buchhaltung"></span> +<a href="#06x--spesen--buchhaltung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Neu</strong></p> +<ul> +<li>Spesenerfassung mit Beleg-Upload (Base64 in localStorage)</li> +<li>Jahresbudget mit Einnahmen-/Ausgaben-Übersicht</li> +<li>Vereinfachte Erfolgsrechnung pro Geschäftsjahr</li> +</ul> +<p><strong>Verbessert</strong></p> +<ul> +<li>Lohnabrechnung integriert Spesen-Erstattungen</li> +<li>CSV-Export aus der Zeiterfassung</li> +</ul> +<h2>0.5.x — Mitarbeiter &amp; Lohn<span class="hx:absolute hx:-mt-20" id="05x--mitarbeiter--lohn"></span> +<a href="#05x--mitarbeiter--lohn" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Neu</strong></p> +<ul> +<li>Mitarbeiterverwaltung mit Pensum, Stundensatz, Ferienanspruch</li> +<li>Lohnabrechnung mit AHV/IV/EO, ALV, BVG, NBU</li> +<li>Jahresabschluss mit Überstundenausgleich</li> +<li>Ferien-Prorata bei Eintritt unter Jahr</li> +</ul> +<h2>0.4.x — Projekte &amp; SIA 102<span class="hx:absolute hx:-mt-20" id="04x--projekte--sia-102"></span> +<a href="#04x--projekte--sia-102" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Neu</strong></p> +<ul> +<li>Projektverwaltung nach SIA 102</li> +<li>Vorgeschlagene Phasen-Anteile am Gesamthonorar</li> +<li>Bauschätzwert-basiertes Honorar</li> +</ul> +<p><strong>Verbessert</strong></p> +<ul> +<li>Zeit-Auswertung pro SIA-Phase</li> +<li>Akonto- und Schlussrechnungen mit automatischer Differenzberechnung</li> +</ul> +<h2>0.3.x — Rechnungen &amp; QR<span class="hx:absolute hx:-mt-20" id="03x--rechnungen--qr"></span> +<a href="#03x--rechnungen--qr" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Neu</strong></p> +<ul> +<li>Rechnungsmodul mit QR-Einzahlungsschein (via <code>swissqrbill</code>)</li> +<li>Akonto-, Teil- und Schlussrechnungen</li> +<li>PDF-Export mit Bürobriefbogen</li> +<li>Stundensatz-Rechnungen ziehen direkt aus der Zeiterfassung</li> +</ul> +<h2>0.2.x — Zeiterfassung<span class="hx:absolute hx:-mt-20" id="02x--zeiterfassung"></span> +<a href="#02x--zeiterfassung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Neu</strong></p> +<ul> +<li>Wochenraster mit Halbstunden-Slots</li> +<li>Drag &amp; Drop zur Slot-Erfassung</li> +<li>Projekt-Zuweisung pro Eintrag</li> +<li>Auswertungen pro Mitarbeiter und Projekt</li> +</ul> +<h2>0.1.x — Initial<span class="hx:absolute hx:-mt-20" id="01x--initial"></span> +<a href="#01x--initial" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p><strong>Neu</strong></p> +<ul> +<li>Erste Setup-Routine (Bürodaten, Mitarbeiter, Kunden)</li> +<li>Briefe und Lieferscheine</li> +<li>Tauri-2-Bundle für macOS</li> +</ul> +<hr> +<h2>Roadmap<span class="hx:absolute hx:-mt-20" id="roadmap"></span> +<a href="#roadmap" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Geplant — keine konkreten Termine:</p> +<ul> +<li><strong>Linux-Build</strong> (Tauri 2 unterstützt es, Bedarf nötig)</li> +<li><strong>Windows-Build</strong> (analog)</li> +<li><strong>Cloud-Modus</strong> Stable (Supabase) — derzeit in <a href="../web-modus">Web-Modus</a> verfügbar, aber experimentell</li> +<li><strong>Französische / Italienische Übersetzung</strong></li> +<li><strong>PostgreSQL-Migration</strong> aus localStorage (Knopf in der App)</li> +<li><strong>Mobile App</strong> (iOS Companion zur Zeiterfassung) — offen</li> +</ul> +<p>Wünsche oder Prioritäten → <a href="https://git.kgva.ch/karim/RAPPORT/issues"target="_blank" rel="noopener">Issue auf Gitea<svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/> +</svg></a>.</p> \ No newline at end of file diff --git a/public/docs/installation/index.html b/public/docs/installation/index.html new file mode 100644 index 0000000..89081fe --- /dev/null +++ b/public/docs/installation/index.html @@ -0,0 +1,83 @@ +Installation – RAPPORTZum Inhalt springen
Installation

Installation

Schritt-für-Schritt-Anleitung für die Installation der Desktop-App.

Voraussetzungen +

PlattformStatusVersionen
macOS Apple Silicon (M1 – M4)✅ UnterstütztmacOS 12+
macOS Intel⚠ Build auf AnfragemacOS 12+
Linux🕐 Geplant
Windows🕐 Geplant

Eine Portierung auf Linux und Windows ist mit Tauri 2 möglich. Issue erstellen, wenn du eine Plattform brauchst.

1 · Download +

Aktueller Build: Downloads-Seite oder direkt Releases auf Gitea.

DateiPlattform
RAPPORT_<version>_aarch64.dmgmacOS Apple Silicon
RAPPORT_<version>_x86_64.dmgmacOS Intel (auf Anfrage)

2 · DMG öffnen & installieren +

  1. DMG doppelklicken
  2. Rapport.app in den Applications-Ordner ziehen
  3. DMG auswerfen

3 · Erster Start (macOS Gatekeeper) +

Beim ersten Start verweigert macOS den Start, weil die Pre-Release-Builds Tauri-signiert, aber (noch) nicht Apple-notarisiert sind.

Lösung +

  1. Systemeinstellungen → Datenschutz & Sicherheit öffnen
  2. Bis ganz nach unten scrollen — es erscheint:

    “Rapport” wurde blockiert, da es nicht von einem identifizierten Entwickler stammt.

  3. Auf “Trotzdem öffnen” klicken
  4. Bestätigen

Ab dem zweiten Start läuft Rapport ohne Rückfragen.

Alternative (Terminal) +

Falls der GUI-Weg nicht funktioniert:

xattr -d com.apple.quarantine /Applications/Rapport.app

Das entfernt das Quarantäne-Flag und macOS akzeptiert den Start.

4 · Erstes Setup +

Beim ersten Start zeigt Rapport den Setup-Bildschirm. Hier werden die Stammdaten erfasst — siehe Einrichtung.

5 · Update-Strategie +

Ab Version 0.7.0 prüft Rapport beim Start automatisch auf neue Versionen (siehe Auto-Updater). Updates können in den Einstellungen deaktiviert werden.

Manuelle Updates: einfach das neuere DMG installieren — die Daten bleiben erhalten, da sie unabhängig von der App liegen (siehe Datenhaltung).

Deinstallation +

# App entfernen
+rm -rf /Applications/Rapport.app
+
+# Daten entfernen (optional!)
+rm -rf "~/Library/Application Support/com.rapport.app"
+
+# Caches
+rm -rf "~/Library/Caches/com.rapport.app"
+rm -rf "~/Library/WebKit/com.rapport.app"

Achtung: Schritt 2 löscht alle Rapport-Daten unwiederbringlich. Vorher Backup machen — siehe Datenhaltung.

Bekannte Probleme +

ProblemLösung
App lässt sich nicht öffnen trotz FreigabeTerminal-Variante mit xattr
“Rapport is damaged”DMG erneut von Gitea ziehen (Browser-Cache hat evtl. Müll)
Schwarzer Bildschirm beim Start~/Library/WebKit/com.rapport.app löschen, neu starten

Mehr unter Troubleshooting.

\ No newline at end of file diff --git a/public/docs/troubleshooting/index.html b/public/docs/troubleshooting/index.html new file mode 100644 index 0000000..8eb3103 --- /dev/null +++ b/public/docs/troubleshooting/index.html @@ -0,0 +1,93 @@ +Troubleshooting – RAPPORTZum Inhalt springen
Troubleshooting

Troubleshooting

Typische Probleme und Lösungen. Wenn dein Problem nicht dabei ist → Issue auf Gitea.

App startet nicht +

“Rapport ist beschädigt” beim ersten Start +

Ursache: macOS Gatekeeper blockt unsignierte/nicht-notarisierte Apps.

Lösung: siehe Installation § 3. Kurz:

xattr -d com.apple.quarantine /Applications/Rapport.app

App startet, zeigt aber schwarzen Bildschirm +

Ursache: WebView-Cache korrupt.

Lösung:

rm -rf "~/Library/Caches/com.rapport.app"
+rm -rf "~/Library/WebKit/com.rapport.app"

App neu starten. Daten gehen dabei nicht verloren (liegen in Application Support, nicht im Cache).

App stürzt sofort beim Start ab +

Ursache: wahrscheinlich beschädigte JSON-Daten in studio_data_v1.

Diagnose:

# Daten ansehen (DevTools-Output)
+open "~/Library/Application Support/com.rapport.app"

Lösung:

  1. Backup wiederherstellen (siehe Datenhaltung)
  2. Oder als letzter Ausweg: Daten zurücksetzen
    mv "~/Library/Application Support/com.rapport.app" \
    +   "~/Library/Application Support/com.rapport.app.bad"
    App neu starten → erstellt frische Daten. Anschliessend Setup-Screen.

Daten weg +

Nach einem App-Update fehlen Einträge +

Ursache: mögliche fehlgeschlagene Migration.

Sofortmassnahme:

  1. Rapport beenden (Cmd+Q, nicht nur Fenster zu)
  2. Aktuelles Datenverzeichnis sichern:
    cp -R "~/Library/Application Support/com.rapport.app" \
    +      "~/Documents/Rapport-Notfall-$(date +%Y%m%d-%H%M)"
  3. Issue erstellen mit:
    • Version vor dem Update (falls bekannt)
    • Version nach dem Update
    • Was fehlt
    • Optional: gesicherter Datenordner (via Pastebin oder verschlüsselt zugesandt)

localStorage voll +

Symptom: Rapport schreibt Fehler-Toast “Speicher voll” beim Sichern.

Ursache: macOS WebView limitiert localStorage auf ~10 MB pro Origin.

Lösung:

  • Sehr grosse Logos durch kleinere ersetzen (Bürodaten → Logo)
  • Belege (Spesen) selektiv löschen oder als externe Datei archivieren
  • Auf Web-Modus wechseln (Postgres ohne praktisches Limit)

Updates +

Auto-Update findet nichts +

Diagnose:

curl -s https://git.kgva.ch/karim/RAPPORT/raw/branch/main/APP/latest.json

→ sollte JSON liefern. Wenn nicht: Server-/Netzwerkproblem.

Update lädt, lässt sich aber nicht installieren +

Ursache: Signaturprüfung scheitert (Public-Key in App ≠ Signatur in latest.json).

Lösung: Manuelles Update — DMG direkt von Releases laden und installieren. Daten bleiben erhalten.

“Diese Version überspringen” rückgängig machen +

In Einstellungen → UpdatesÜbersprungene Versionen zurücksetzen. Beim nächsten Start wird die Version wieder angeboten.

PDF / QR-Schein +

QR-Schein hat falsche Daten +

Diagnose-Checkliste:

  • IBAN korrekt? (CH, 21 Zeichen, keine Leerzeichen)
  • Empfänger-Adresse vollständig? (PLZ und Ort beide gefüllt)
  • Schuldner-Adresse vollständig?
  • Betrag > 0?
  • Referenz nicht zu lang? (max 27 Zeichen)

QR-Bibliothek: swissqrbill — bei merkwürdigen Fehlern dort nachschauen.

PDF-Export ist leer / weisses Blatt +

Ursache: Print-View hat keine Daten (möglicherweise Race-Condition beim Laden).

Lösung: Rechnung schliessen, erneut öffnen, dann PDF.

PDF-Schrift sieht falsch aus +

Ursache: Web-Schrift nicht geladen, Fallback greift.

Lösung: Vor dem Drucken warten, bis das Vorschau-Bild komplett geladen ist (3–5 Sek).

System-Tray +

Tray-Icon erscheint nicht +

Plattform-Hinweis: Tray-Icons unter macOS sind bei extrem voller Menüleiste oder unter “Bartender”/“Hidden Bar” eventuell unsichtbar.

Diagnose:

ps aux | grep -i rapport

→ wenn Prozess läuft, aber kein Icon: in den Tray-Manager-Apps prüfen.

Konfiguration: Einstellungen → System-Tray → Tray-Icon ausblenden (aus → Icon erzwingen).

Tray-Menü reagiert nicht / hängt +

Lösung: App via Activity Monitor hart beenden und neu starten. Daten gehen nicht verloren (alle Schreibungen sind synchron in localStorage).

Web-Modus +

Login-Screen zeigt keine Server-URL +

Ursache: .env.production enthielt nicht den richtigen VITE_SUPABASE_URL zur Build-Zeit.

Lösung: .env.production prüfen, dann npm run build neu, Container restart.

Realtime-Updates kommen nicht an +

Ursache: Websocket-Support fehlt im Reverse Proxy.

Lösung: In Nginx Proxy Manager für api.* Websocket Support aktivieren.

Siehe Web-Modus § Troubleshooting.

Debug-Informationen sammeln +

Bei einem Issue helfen folgende Infos:

# Rapport-Version
+defaults read /Applications/Rapport.app/Contents/Info.plist CFBundleShortVersionString
+
+# macOS-Version
+sw_vers
+
+# Architektur
+uname -m
+
+# Datenverzeichnis-Grösse
+du -sh "~/Library/Application Support/com.rapport.app"
+
+# Cache-Verzeichnis-Grösse
+du -sh "~/Library/Caches/com.rapport.app"

→ Bei Issue mit anhängen.

Wenn nichts hilft +

Neues Issue auf Gitea mit:

  • Was du gemacht hast
  • Was passiert ist
  • Was du erwartet hättest
  • Screenshots (auch von DevTools-Konsole falls möglich)
  • Rapport-Version und macOS-Version
\ No newline at end of file diff --git a/public/docs/web-modus/index.html b/public/docs/web-modus/index.html new file mode 100644 index 0000000..5159f24 --- /dev/null +++ b/public/docs/web-modus/index.html @@ -0,0 +1,99 @@ +Web-Modus (Multi-User) – RAPPORTZum Inhalt springen
Web-Modus

Web-Modus (Multi-User)

Hinweis: Der frühere Supabase-basierte Web-Modus wurde durch Rapport Server abgelöst — den vollständigen Selfhost-Stack mit eigenem Postgres, Auth, Realtime und Storage. Keine externe Cloud-Abhängigkeit mehr.

Diese Seite bleibt als Referenz erhalten, der empfohlene Weg für Multi-User-Setups ist Rapport Server.

Wann brauchst du das? +

AnwendungsfallEmpfehlung
Solo-Büro, ein MacDesktop-App — siehe Installation
2–5 Personen, gleicher StandortRapport Server auf einem Mac Mini im LAN
Verteiltes Team / Home-OfficeRapport Server mit SSL + Reverse Proxy
Hosted Backend (eigener VPS)Rapport Server auf Linux-VPS

Architektur (Kurzfassung) +

┌────────────┐     HTTPS     ┌──────────────┐     SQL      ┌────────────┐
+│  Browser   │ ──────────────│  nginx       │ ─────────────│  Postgres  │
+│  / Desktop │               │  (Frontend)  │              │ + GoTrue   │
+└────────────┘               └──────────────┘              │ + REST     │
+                                                           │ + Realtime │
+                                                           │ + Storage  │
+                                                           └────────────┘
  • Frontend: dieselbe React-App, aber Vite-Build statt Tauri (npm run build)
  • Backend: Postgres-Stack (Rapport Server)
  • Auth: E-Mail / Passwort über GoTrue
  • Storage: Belege, Logos in Object-Storage

Setup +

Alle Setup-Schritte (Repo klonen, .env erstellen, Migrations syncen, Docker-Compose starten, Reverse-Proxy konfigurieren) sind in Rapport Server dokumentiert.

Migration Desktop → Cloud +

Wer mit der Desktop-App startet und später auf den Web-Modus wechseln möchte:

  • Aktuell: manueller Export aus Rapport (CSV/PDF) und manuelles Wiederanlegen im neuen Setup
  • Geplant: “localStorage → Postgres”-Import-Knopf direkt in der App

Status: Issue auf Gitea.

Troubleshooting +

Siehe Rapport Server § Troubleshooting oder allgemeine Troubleshooting-Seite.

\ No newline at end of file diff --git a/public/downloads/index.html b/public/downloads/index.html new file mode 100644 index 0000000..4947920 --- /dev/null +++ b/public/downloads/index.html @@ -0,0 +1,55 @@ +Downloads – RAPPORTZum Inhalt springen

Downloads

Aktuelle Builds von Rapport. Quellcode und ältere Versionen auf Gitea.

Rapport besteht aus zwei Komponenten:

KomponenteFür wenAktuelle Version
Desktop-AppSolo-Büro, lokale Datenhaltung0.8.3
Rapport ServerTeam / Multi-User / Selfhost0.1.0

Desktop-App — Pre-Release 0.8.3 +

Aktuelle Version

Neuerungen — siehe Changelog für Details.

macOS +

ArchitekturDownload
Apple Silicon (M1–M4)RAPPORT_0.8.3_aarch64.dmg
Intel (x86_64)auf Anfrage

Erstinstallation: Systemeinstellungen → Datenschutz & Sicherheit öffnen und Rapport zulassen. Die Builds sind über Tauri signiert, aber (noch) nicht Apple-notarisiert.

Linux & Windows +

Geplant. Rapport basiert auf Tauri 2 — eine Portierung ist möglich, sobald der Bedarf besteht. Issue erstellen, wenn du eine Plattform brauchst.


Rapport Server — 0.1.0 +

Erstes Release

Selfhost-Backend für Multi-User-Setups — Postgres, Auth, Realtime-Sync, Storage. Details und Setup-Anleitung auf der Server-Seite.

Server-App (macOS) +

GUI zum Starten, Stoppen und Verwalten der Server-Instanz auf einem Mac (Mac Mini, Studio-Rechner). Bündelt den Docker-Compose-Stack.

ArchitekturDownload
Apple Silicon (M1–M4)RAPPORT_SERVER_0.1.0_aarch64.dmg
Intel (x86_64)auf Anfrage

Voraussetzung: Colima oder Docker Desktop muss installiert sein. Die App verwaltet den Container-Stack darüber.

Docker (Linux / VPS / Headless) +

Für Linux-Server, NAS oder VPS — der reine Docker-Compose-Stack ohne GUI.

git clone https://git.kgva.ch/karim/rapport-server.git
+cd rapport-server
+git checkout 0.1.0
+cp .env.example .env
+# .env editieren (POSTGRES_PASSWORD, JWT_SECRET, SITE_URL)
+docker compose up -d

Vollständige Anleitung mit .env-Variablen, Reverse-Proxy und Backup → Server-Seite.

Container-Images werden von Docker Hub / Gitea-Registry gezogen — docker compose pull aktualisiert auf die neuesten Tags.


Quellcode +

Desktop-App — Tauri / React:

git clone https://git.kgva.ch/karim/RAPPORT.git
+cd RAPPORT/APP
+npm install
+npm run tauri dev

Voraussetzungen: Node 20+, Rust (stable), Xcode-CLI (macOS).

Rapport Server:

git clone https://git.kgva.ch/karim/rapport-server.git

Beide Repos auf Gitea — Issues und PRs willkommen.

Auto-Update +

Seit 0.7.0 prüft die Desktop-App beim Start automatisch auf neue Versionen — siehe Auto-Updater. Für Rapport Server: git pull && docker compose pull && docker compose up -d.

\ No newline at end of file diff --git a/public/downloads/index.xml b/public/downloads/index.xml new file mode 100644 index 0000000..c6e5332 --- /dev/null +++ b/public/downloads/index.xml @@ -0,0 +1 @@ +RAPPORT – Downloadshttps://rapport.gabrielevarano.ch/downloads/Recent content in Downloads on RAPPORTHugo -- gohugo.iode \ No newline at end of file diff --git a/public/faq/index.html b/public/faq/index.html new file mode 100644 index 0000000..be8711e --- /dev/null +++ b/public/faq/index.html @@ -0,0 +1,43 @@ +FAQ – RAPPORTZum Inhalt springen

FAQ

Häufige Fragen zu RAPPORT. Bei Bugs oder weiteren Fragen → Issue auf Gitea.

Für wen ist Rapport gedacht? +

Für kleine Architekturbüros in der Schweiz. Die Strukturen folgen der SIA 102 (Phasen, Honorar). Rapport wurde für den internen Gebrauch entwickelt und wird mit diesem Projekt öffentlich geteilt.

Ist Rapport kostenlos? +

Ja, vollständig. Quellcode unter GNU AGPL-3.0-or-later. Keine versteckten Kosten, keine Telemetrie. Die Daten bleiben lokal auf deinem Computer bzw. in deiner Instanz. Du hast die komplette Kontrolle über deine Daten.

Welche Systeme werden unterstützt? +

Aktuell nur macOS (Intel & Apple Silicon). Rapport basiert auf Tauri — eine Portierung auf Linux und Windows ist möglich, sobald der Bedarf seitens Community besteht.

Wo werden die Daten gespeichert? +

Rapport unterstützt zwei Modi, beide selbst-gehostet:

  • Desktop-App (Single-User) — alle Daten liegen lokal auf deinem Mac (localStorage im Applikationsordner). Kein Server nötig, kein Cloud-Account, keine Telemetrie.
  • Server-Modus (Multi-User) — Daten in einer eigenen PostgreSQL-Datenbank über Rapport Server. Mehrere Personen, Realtime-Sync, eigene Domain. Wechsel direkt im Login-Bildschirm der App.

In beiden Fällen bleibt die Kontrolle über die Daten bei dir.

Ist die Software stabil genug für den Betrieb? +

Noch nicht. Aktuell befindet sich Rapport in aktiver Entwicklung (Pre-Release 0.8.3). Funktionen können sich ändern, Bugs sind möglich. Regelmässige Backups sind empfohlen. Testen und Feedback geben ist erwünscht.

Was ist mit dem QR-Einzahlungsschein? +

Rapport erzeugt Schweizer QR-Rechnungen nach Norm — direkt eingebettet in das PDF der Rechnung. IBAN, Bürodaten und Empfänger werden aus den Einstellungen bzw. den Kundendaten gezogen. Akonto-, Teil- und Schlussrechnungen werden unterstützt.

Wie funktioniert die Zeiterfassung? +

Tages- und Wochenraster mit Drag & Drop. Jeder Eintrag wird einem Projekt zugewiesen. Auswertungen pro Mitarbeiter und Projekt sind unter Zeit abrufbar. Ferienverwaltung mit Prorata und Jahresabschluss mit Überstundenausgleich.

Kann ich zum Projekt beitragen? +

Ja. Issues und Pull Requests sind willkommen auf Gitea. Rapport ist kein Framework — konkrete Verbesserungen für den Büroalltag sind am wertvollsten:

  • Bug-Reports mit Reproduktionsschritten
  • Workflow-Verbesserungen aus dem realen Büroalltag
  • Vorlagen (Briefe, Protokolle, Lieferscheine) für andere Büros
  • Übersetzungen / Internationalisierung

Wie erhalte ich Hilfe bei einem Problem? +

Ein Issue auf Gitea ist der beste Weg. Bitte beschreibe, was du gemacht hast und was passiert ist. Screenshots helfen. Die Rapport-Version (links unten in der App) bitte angeben.

KanalVerwendung
Gitea IssuesBugs, Feature-Wünsche, allgemeine Fragen
gabrielevarano.chEntwickler-Kontakt

Warum Open Source? +

Bürowissen sollte nicht in proprietären Tools eingesperrt sein. Studio-Management-Workflows sind in der Branche gut etabliert — ein Tool, das die Schweizer Konventionen (SIA 102, QR-Rechnung) sauber umsetzt, gehört allen.

AGPL-3.0 stellt sicher, dass Verbesserungen wieder ins Projekt fliessen.

\ No newline at end of file diff --git a/public/faq/index.xml b/public/faq/index.xml new file mode 100644 index 0000000..af5d9ac --- /dev/null +++ b/public/faq/index.xml @@ -0,0 +1 @@ +RAPPORT – FAQhttps://rapport.gabrielevarano.ch/faq/Recent content in FAQ on RAPPORTHugo -- gohugo.iode \ No newline at end of file diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..0f2dd2b2eb092162cc3cb22473ae565ddf2db9c7 GIT binary patch literal 340 zcmV-a0jvIrP)Px$4oO5oR5(wi(?2VQVHC&l&%^2k$bi`8-++`7#UP56mmo8oO4ltUkH`0000 literal 0 HcmV?d00001 diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..5c1aea58495d1cfb77aac38b4f5ce0ae14ce6a6f GIT binary patch literal 753 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 + + diff --git a/public/features/auto-updater/index.html b/public/features/auto-updater/index.html new file mode 100644 index 0000000..b1eeb2d --- /dev/null +++ b/public/features/auto-updater/index.html @@ -0,0 +1,79 @@ +Auto-Updater – RAPPORTZum Inhalt springen

Auto-Updater

Neu in 0.7.0

Rapport prüft beim Start automatisch auf neue Versionen und installiert Updates signiert über Tauri. Einzelne Versionen können übersprungen werden.

Funktionsweise +

Beim App-Start:

  1. Abfrage gegen https://git.kgva.ch/karim/RAPPORT/releases/latest.json
  2. Versionsvergleich mit lokaler version im Tauri-Bundle
  3. Bei neuer Version → Update-Dialog
  4. Bei Bestätigung → Download + Signaturprüfung + Installation + Neustart

Sicherheit +

  • Updates werden mit dem Tauri-Updater-Schlüssel signiert
  • Manipulierte Downloads werden abgelehnt
  • Quellcode und Build sind reproduzierbar (Gitea CI, geplant)

Optionen +

  • Update installieren — Download & Neustart
  • Diese Version überspringen — überspringt nur diese eine Version
  • Später erinnern — beim nächsten Start erneut fragen

Updates können in den Einstellungen komplett deaktiviert werden.

Latest-Endpoint +

{
+  "version": "0.8.3",
+  "notes": "Rapport 0.8.3",
+  "pub_date": "2026-05-24T00:00:00Z",
+  "platforms": {
+    "darwin-aarch64": {
+      "signature": "…",
+      "url": "https://git.kgva.ch/karim/RAPPORT/releases/download/0.8.3/RAPPORT%20PRE-RELEASE.app.tar.gz"
+    }
+  }
+}
\ No newline at end of file diff --git a/public/features/index.html b/public/features/index.html new file mode 100644 index 0000000..d76060a --- /dev/null +++ b/public/features/index.html @@ -0,0 +1,31 @@ +Features – RAPPORTZum Inhalt springen \ No newline at end of file diff --git a/public/features/index.xml b/public/features/index.xml new file mode 100644 index 0000000..aafa709 --- /dev/null +++ b/public/features/index.xml @@ -0,0 +1,349 @@ +RAPPORT – Featureshttps://rapport.gabrielevarano.ch/features/Recent content in Features on RAPPORTHugo -- gohugo.iodeZeiterfassunghttps://rapport.gabrielevarano.ch/features/zeiterfassung/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/features/zeiterfassung/ +<p><span class="rapport-status active">In Arbeit</span></p> +<p><strong>Tages- und Wochenraster</strong> mit Drag &amp; Drop. Auswertungen pro Mitarbeiter und Projekt. Ferienverwaltung mit Prorata und Jahresabschluss.</p> +<h2>Konzept<span class="hx:absolute hx:-mt-20" id="konzept"></span> +<a href="#konzept" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Die Zeiterfassung ist das <strong>Kernmodul</strong> von RAPPORT — alle anderen Module (Rechnungen, Auswertungen, Lohnabrechnung) greifen auf die hier erfassten Stunden zu.</p> +<h2>Eingabe<span class="hx:absolute hx:-mt-20" id="eingabe"></span> +<a href="#eingabe" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><strong>Wochenraster</strong> mit den 5 (oder 7) Arbeitstagen</li> +<li><strong>Halbstunden-Slots</strong> von 06:00 bis 22:00</li> +<li><strong>Klick</strong> oder <strong>Drag</strong> über mehrere Slots</li> +<li>Jeder Eintrag wird einem <strong>Projekt</strong> zugewiesen (Pflichtfeld)</li> +<li>Mehrfacheinträge pro Slot möglich (z. B. parallele Telefonate)</li> +</ul> +<h2>Auswertungen<span class="hx:absolute hx:-mt-20" id="auswertungen"></span> +<a href="#auswertungen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Pro Mitarbeiter und pro Projekt:</p> +<ul> +<li>Geleistete Stunden vs. Soll-Pensum</li> +<li>Ferienanspruch / -saldo (mit Prorata bei Eintritt unter Jahr)</li> +<li>Überstunden-Saldo</li> +<li>Stundenaufschlüsselung nach SIA-Phase pro Projekt</li> +</ul> +<h2>Jahresabschluss<span class="hx:absolute hx:-mt-20" id="jahresabschluss"></span> +<a href="#jahresabschluss" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Am Jahresende:</p> +<ul> +<li>Ferien-Restguthaben übertragen oder auszahlen</li> +<li>Überstunden ausgleichen oder vergüten</li> +<li>Neues Jahr automatisch initialisieren</li> +</ul> +<h2>Verwandte Module<span class="hx:absolute hx:-mt-20" id="verwandte-module"></span> +<a href="#verwandte-module" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><a href="../rechnungen">Rechnungen</a> — Stundensatz-Rechnungen ziehen direkt aus der Zeiterfassung</li> +<li><a href="../projekte">Projekte</a> — Stunden-Auswertung pro SIA-Phase</li> +<li><a href="../mitarbeiter">Mitarbeiter</a> — Pensum, Ferienanspruch</li> +</ul>Rechnungen & Offertenhttps://rapport.gabrielevarano.ch/features/rechnungen/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/features/rechnungen/ +<p><span class="rapport-status active">In Arbeit</span></p> +<p><strong>QR-Einzahlungsscheine, SIA-Phasen, Akonto-, Teil- und Schlussrechnungen.</strong> Offerten sind in Projekte und Rechnungen konvertierbar. PDF-Export.</p> +<h2>Workflow<span class="hx:absolute hx:-mt-20" id="workflow"></span> +<a href="#workflow" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ol> +<li><strong>Offerte</strong> erstellen — auf Basis SIA 102 oder pauschal</li> +<li>Kunde nimmt an → <strong>konvertieren in Projekt + Rechnung</strong></li> +<li><strong>Akonto-Rechnungen</strong> während der Projektlaufzeit</li> +<li><strong>Schlussrechnung</strong> mit Differenz zum bisher Akonto-bezahlten</li> +</ol> +<h2>QR-Einzahlungsschein<span class="hx:absolute hx:-mt-20" id="qr-einzahlungsschein"></span> +<a href="#qr-einzahlungsschein" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Schweizer <strong>QR-Rechnung</strong> nach Norm — direkt eingebettet in die PDF.</p> +<p>Ausgelesen aus:</p> +<ul> +<li><strong>Bürodaten</strong> — IBAN, Empfänger-Adresse</li> +<li><strong>Kundendaten</strong> — Schuldner-Adresse</li> +<li><strong>Rechnungs-Daten</strong> — Betrag, Referenz, Zusatzinformation</li> +</ul> +<h2>Honorarmodelle<span class="hx:absolute hx:-mt-20" id="honorarmodelle"></span> +<a href="#honorarmodelle" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><table> +<thead> +<tr> +<th>Modell</th> +<th>Berechnung</th> +<th>Verwendung</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Stundensatz</strong></td> +<td>Aus Zeiterfassung × Mitarbeiter-Stundensatz</td> +<td>Kleinaufträge, Beratung</td> +</tr> +<tr> +<td><strong>SIA-Phasen</strong></td> +<td>Bauschätzwert × Honorarsatz × Phasenanteil</td> +<td>Reguläre Architektur-Aufträge</td> +</tr> +<tr> +<td><strong>Pauschal</strong></td> +<td>Fester Betrag</td> +<td>Auf Wunsch des Kunden</td> +</tr> +</tbody> +</table> +<h2>PDF-Export<span class="hx:absolute hx:-mt-20" id="pdf-export"></span> +<a href="#pdf-export" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Druckfertige Rechnung inkl. QR-Schein. Layout aus dem Büro-Briefbogen (mit Logo). Mehrsprachig DE/FR/IT (geplant).</p> +<h2>Verwandte Module<span class="hx:absolute hx:-mt-20" id="verwandte-module"></span> +<a href="#verwandte-module" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><a href="../projekte">Projekte</a> — Honorarstruktur stammt aus dem Projekt</li> +<li><a href="../zeiterfassung">Zeiterfassung</a> — Stundensatz-Rechnungen</li> +</ul>Projekt- & Kundenverwaltunghttps://rapport.gabrielevarano.ch/features/projekte/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/features/projekte/ +<p><span class="rapport-status active">In Arbeit</span></p> +<p><strong>Projekte nach SIA 102</strong> mit Budget, Phasen und Beteiligten. Erstellung aus einer Offerte mit Verknüpfung zu Zeiterfassung und Rechnungen.</p> +<h2>Projektstruktur<span class="hx:absolute hx:-mt-20" id="projektstruktur"></span> +<a href="#projektstruktur" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Jedes Projekt besitzt:</p> +<ul> +<li><strong>Stammdaten</strong> — Nummer, Bezeichnung, Standort, Bauschätzwert</li> +<li><strong>Kunde</strong> — verknüpft mit Kundendatenbank</li> +<li><strong>Beteiligte</strong> — Bauleitung, Fachplaner, Behörden</li> +<li><strong>Phasen</strong> — SIA 102 (Vorprojekt, Bauprojekt, Ausschreibung, …)</li> +<li><strong>Budget</strong> — Gesamthonorar, pro Phase aufgeteilt</li> +</ul> +<h2>SIA 102<span class="hx:absolute hx:-mt-20" id="sia-102"></span> +<a href="#sia-102" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Standard-Phasenverteilung wird vorgeschlagen, kann pro Projekt überschrieben werden.</p> +<table> +<thead> +<tr> +<th>Phase</th> +<th>Anteil (Standard)</th> +</tr> +</thead> +<tbody> +<tr> +<td>31 — Vorprojekt</td> +<td>9 %</td> +</tr> +<tr> +<td>32 — Bauprojekt</td> +<td>21 %</td> +</tr> +<tr> +<td>33 — Bewilligung</td> +<td>3 %</td> +</tr> +<tr> +<td>41 — Ausschreibung</td> +<td>18 %</td> +</tr> +<tr> +<td>51 — Ausführung</td> +<td>38 %</td> +</tr> +<tr> +<td>52 — Inbetriebnahme</td> +<td>6 %</td> +</tr> +<tr> +<td>53 — Abschluss</td> +<td>5 %</td> +</tr> +</tbody> +</table> +<h2>Kundendatenbank<span class="hx:absolute hx:-mt-20" id="kundendatenbank"></span> +<a href="#kundendatenbank" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li>Adresse, Ansprechperson, Telefon, E-Mail</li> +<li>Honorartyp (Stundensatz / SIA / Pauschal)</li> +<li>Verknüpfung zu allen Projekten und Rechnungen des Kunden</li> +</ul> +<h2>Auswertung<span class="hx:absolute hx:-mt-20" id="auswertung"></span> +<a href="#auswertung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Pro Projekt:</p> +<ul> +<li>Geleistete Stunden vs. Budget</li> +<li>Honorar-Saldo (verrechnet / Akonto / offen)</li> +<li>Phasen-Fortschritt</li> +</ul> +<h2>Verwandte Module<span class="hx:absolute hx:-mt-20" id="verwandte-module"></span> +<a href="#verwandte-module" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><a href="../rechnungen">Rechnungen</a> — Offerte → Projekt</li> +<li><a href="../zeiterfassung">Zeiterfassung</a> — Stunden-Auswertung pro Phase</li> +</ul>Mitarbeiterhttps://rapport.gabrielevarano.ch/features/mitarbeiter/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/features/mitarbeiter/ +<p><span class="rapport-status active">In Arbeit</span></p> +<p><strong>Ferienverwaltung, interne Stunden / Absenzen und Lohnabrechnung.</strong> Jahresabschluss mit Überstundenausgleich.</p> +<h2>Stammdaten<span class="hx:absolute hx:-mt-20" id="stammdaten"></span> +<a href="#stammdaten" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Pro Mitarbeiter:</p> +<ul> +<li>Name, Eintrittsdatum, Pensum (%)</li> +<li>Stundensatz (intern, für Rechnungen)</li> +<li>Ferienanspruch (Tage / Jahr)</li> +<li>Lohn (monatlich, brutto)</li> +</ul> +<h2>Ferienverwaltung<span class="hx:absolute hx:-mt-20" id="ferienverwaltung"></span> +<a href="#ferienverwaltung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><strong>Prorata-Berechnung</strong> bei Eintritt unter Jahr</li> +<li><strong>Ferien-Saldo</strong> in Tagen (live)</li> +<li><strong>Halbtage</strong> unterstützt</li> +<li><strong>Übertrag</strong> ins Folgejahr oder Auszahlung</li> +</ul> +<h2>Absenzen<span class="hx:absolute hx:-mt-20" id="absenzen"></span> +<a href="#absenzen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Krankheit, Militär, Mutterschaft, unbezahlter Urlaub — getrennt erfasst, mit Auswertung pro Mitarbeiter.</p> +<h2>Lohnabrechnung<span class="hx:absolute hx:-mt-20" id="lohnabrechnung"></span> +<a href="#lohnabrechnung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Monatliche Abrechnung mit:</p> +<ul> +<li>Grundlohn (basierend auf Pensum)</li> +<li>Überstunden-Vergütung</li> +<li>Spesen-Erstattung</li> +<li>Sozialabzüge (AHV, ALV, Pensionskasse)</li> +</ul> +<p>PDF-Export pro Mitarbeiter.</p> +<h2>Jahresabschluss<span class="hx:absolute hx:-mt-20" id="jahresabschluss"></span> +<a href="#jahresabschluss" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li>Ferien-Restguthaben übertragen oder auszahlen</li> +<li>Überstunden ausgleichen oder vergüten</li> +<li>Lohnausweis vorbereiten (Export)</li> +</ul> +<h2>Verwandte Module<span class="hx:absolute hx:-mt-20" id="verwandte-module"></span> +<a href="#verwandte-module" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><a href="../zeiterfassung">Zeiterfassung</a> — Pensum-Soll vs. Stunden-Ist</li> +<li><a href="../spesen">Spesen</a> — Spesen-Erstattung in der Lohnabrechnung</li> +</ul>Spesen & Bürobudgethttps://rapport.gabrielevarano.ch/features/spesen/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/features/spesen/ +<p><span class="rapport-status active">In Arbeit</span></p> +<p><strong>Spesenerfassung mit Belegupload.</strong> Jahresbudget mit Einnahmen und Ausgaben. Internes Rechnungswesen.</p> +<h2>Spesenerfassung<span class="hx:absolute hx:-mt-20" id="spesenerfassung"></span> +<a href="#spesenerfassung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Pro Mitarbeiter:</p> +<ul> +<li>Datum, Betrag, Kategorie</li> +<li><strong>Beleg-Upload</strong> (PDF, JPG, PNG)</li> +<li>Projekt-Zuordnung (optional)</li> +<li>Status (offen / erstattet)</li> +</ul> +<p>Kategorien: Reise, Verpflegung, Material, Telefon, Sonstiges.</p> +<h2>Jahresbudget<span class="hx:absolute hx:-mt-20" id="jahresbudget"></span> +<a href="#jahresbudget" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Übersicht über:</p> +<ul> +<li><strong>Einnahmen</strong> — Rechnungsbeträge, sortiert nach Eingang</li> +<li><strong>Ausgaben</strong> — Spesen, Bürokosten, Löhne, Sozialabzüge</li> +<li><strong>Saldo</strong> pro Monat / Quartal / Jahr</li> +</ul> +<h2>Auswertung<span class="hx:absolute hx:-mt-20" id="auswertung"></span> +<a href="#auswertung" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li>Einnahmen pro Kunde / Projekt</li> +<li>Ausgaben pro Kategorie / Mitarbeiter</li> +<li>Erfolgsrechnung pro Geschäftsjahr (vereinfacht)</li> +</ul> +<h2>Verwandte Module<span class="hx:absolute hx:-mt-20" id="verwandte-module"></span> +<a href="#verwandte-module" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><a href="../mitarbeiter">Mitarbeiter</a> — Spesen-Erstattung in der Lohnabrechnung</li> +<li><a href="../rechnungen">Rechnungen</a> — Einnahmen-Quelle</li> +</ul>Protokolle & Lieferscheinehttps://rapport.gabrielevarano.ch/features/protokolle/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/features/protokolle/ +<p><span class="rapport-status active">In Arbeit</span></p> +<p><strong>Einfache Erstellung von Sitzungsprotokollen</strong> mit Beschlüssen und Aufgaben. Briefe und Lieferscheine im gleichen Erscheinungsbild.</p> +<h2>Sitzungsprotokolle<span class="hx:absolute hx:-mt-20" id="sitzungsprotokolle"></span> +<a href="#sitzungsprotokolle" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Pro Sitzung:</p> +<ul> +<li>Datum, Ort, Teilnehmer (aus Beteiligten-Liste)</li> +<li><strong>Traktanden</strong> als nummerierte Liste</li> +<li>Pro Traktandum: Beschluss, Aufgabe, Verantwortlich, Frist</li> +<li>Anhänge</li> +</ul> +<p>PDF-Export mit Bürobriefbogen.</p> +<h2>Briefe<span class="hx:absolute hx:-mt-20" id="briefe"></span> +<a href="#briefe" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Brief-Editor mit:</p> +<ul> +<li>Empfänger aus Kundendatenbank</li> +<li>Bezugszeile, Anrede, Text, Grussformel</li> +<li>Briefbogen-Vorlage mit Logo</li> +<li>PDF-Export</li> +</ul> +<h2>Lieferscheine<span class="hx:absolute hx:-mt-20" id="lieferscheine"></span> +<a href="#lieferscheine" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Pro Lieferung:</p> +<ul> +<li>Empfänger, Datum, Bezug</li> +<li>Positionen (Plan-Nummer, Bezeichnung, Anzahl, Massstab)</li> +<li>Unterschriftenfeld</li> +</ul> +<p>Konsistentes Erscheinungsbild über alle Dokumenttypen — eine Briefbogen-Vorlage, mehrere Verwendungen.</p> +<h2>Verwandte Module<span class="hx:absolute hx:-mt-20" id="verwandte-module"></span> +<a href="#verwandte-module" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><a href="../projekte">Projekte</a> — Beteiligte als Empfänger</li> +</ul>Auto-Updaterhttps://rapport.gabrielevarano.ch/features/auto-updater/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/features/auto-updater/ +<p><span class="rapport-status new">Neu in 0.7.0</span></p> +<p><strong>Rapport prüft beim Start automatisch auf neue Versionen</strong> und installiert Updates signiert über Tauri. Einzelne Versionen können übersprungen werden.</p> +<h2>Funktionsweise<span class="hx:absolute hx:-mt-20" id="funktionsweise"></span> +<a href="#funktionsweise" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>Beim App-Start:</p> +<ol> +<li>Abfrage gegen <code>https://git.kgva.ch/karim/RAPPORT/releases/latest.json</code></li> +<li>Versionsvergleich mit lokaler <code>version</code> im Tauri-Bundle</li> +<li>Bei neuer Version → Update-Dialog</li> +<li>Bei Bestätigung → Download + Signaturprüfung + Installation + Neustart</li> +</ol> +<h2>Sicherheit<span class="hx:absolute hx:-mt-20" id="sicherheit"></span> +<a href="#sicherheit" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li>Updates werden mit dem <strong>Tauri-Updater-Schlüssel</strong> signiert</li> +<li>Manipulierte Downloads werden abgelehnt</li> +<li>Quellcode und Build sind reproduzierbar (Gitea CI, geplant)</li> +</ul> +<h2>Optionen<span class="hx:absolute hx:-mt-20" id="optionen"></span> +<a href="#optionen" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><strong>Update installieren</strong> — Download &amp; Neustart</li> +<li><strong>Diese Version überspringen</strong> — überspringt nur diese eine Version</li> +<li><strong>Später erinnern</strong> — beim nächsten Start erneut fragen</li> +</ul> +<p>Updates können in den Einstellungen komplett deaktiviert werden.</p> +<h2>Latest-Endpoint<span class="hx:absolute hx:-mt-20" id="latest-endpoint"></span> +<a href="#latest-endpoint" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"> +<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.8.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;notes&#34;</span><span class="p">:</span> <span class="s2">&#34;Rapport 0.8.3&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;pub_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2026-05-24T00:00:00Z&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;platforms&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;darwin-aarch64&#34;</span><span class="p">:</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;signature&#34;</span><span class="p">:</span> <span class="s2">&#34;…&#34;</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://git.kgva.ch/karim/RAPPORT/releases/download/0.8.3/RAPPORT%20PRE-RELEASE.app.tar.gz&#34;</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"> +<button +class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50" +title="Code kopieren" +aria-label="Code kopieren" +data-copied-label="Kopiert!" +> +<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div> +<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div> +</button> +</div> +</div>System-Trayhttps://rapport.gabrielevarano.ch/features/system-tray/Mon, 01 Jan 0001 00:00:00 +0000https://rapport.gabrielevarano.ch/features/system-tray/ +<p><span class="rapport-status new">Neu in 0.7.0</span></p> +<p><strong>Schnellzugriff über die Menüleiste mit Hide-on-Close.</strong> Beim Schliessen läuft Rapport im Hintergrund weiter — Cmd+Q beendet die App vollständig.</p> +<h2>Verhalten<span class="hx:absolute hx:-mt-20" id="verhalten"></span> +<a href="#verhalten" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><table> +<thead> +<tr> +<th>Aktion</th> +<th>Verhalten</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>Fenster schliessen</strong> (⌘W oder rotes X)</td> +<td>App läuft im Tray weiter</td> +</tr> +<tr> +<td><strong>Cmd+Q</strong></td> +<td>App wird vollständig beendet</td> +</tr> +<tr> +<td><strong>Klick auf Tray-Icon</strong></td> +<td>Fenster nach vorne, oder zeigen</td> +</tr> +<tr> +<td><strong>Rechtsklick auf Tray-Icon</strong></td> +<td>Menü mit Schnellzugriffen</td> +</tr> +</tbody> +</table> +<h2>Tray-Menü<span class="hx:absolute hx:-mt-20" id="tray-menü"></span> +<a href="#tray-men%c3%bc" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><strong>Rapport zeigen</strong> — Fenster nach vorne</li> +<li><strong>Neue Zeiterfassung</strong> — direkt im Zeit-Modul</li> +<li><strong>Neue Rechnung</strong> — direkt im Rechnungs-Modul</li> +<li><strong>Letzte Projekte</strong> — Quick-Open der letzten 5 Projekte</li> +<li><strong>Einstellungen</strong></li> +<li><strong>Rapport beenden</strong></li> +</ul> +<h2>Konfiguration<span class="hx:absolute hx:-mt-20" id="konfiguration"></span> +<a href="#konfiguration" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><p>In den Einstellungen:</p> +<ul> +<li><strong>Beim Systemstart starten</strong> (Login-Item) — Standard: aus</li> +<li><strong>Beim Schliessen beenden</strong> statt ins Tray — Standard: aus</li> +<li><strong>Tray-Icon ausblenden</strong> — App läuft, aber kein Menüleisten-Icon</li> +</ul> +<h2>Verwandte Module<span class="hx:absolute hx:-mt-20" id="verwandte-module"></span> +<a href="#verwandte-module" class="subheading-anchor" aria-label="Permalink für diesen Abschnitt"></a></h2><ul> +<li><a href="../auto-updater">Auto-Updater</a> — prüft Updates im Hintergrund</li> +</ul> \ No newline at end of file diff --git a/public/features/mitarbeiter/index.html b/public/features/mitarbeiter/index.html new file mode 100644 index 0000000..a6011e4 --- /dev/null +++ b/public/features/mitarbeiter/index.html @@ -0,0 +1,67 @@ +Mitarbeiter – RAPPORTZum Inhalt springen

Mitarbeiter

In Arbeit

Ferienverwaltung, interne Stunden / Absenzen und Lohnabrechnung. Jahresabschluss mit Überstundenausgleich.

Stammdaten +

Pro Mitarbeiter:

  • Name, Eintrittsdatum, Pensum (%)
  • Stundensatz (intern, für Rechnungen)
  • Ferienanspruch (Tage / Jahr)
  • Lohn (monatlich, brutto)

Ferienverwaltung +

  • Prorata-Berechnung bei Eintritt unter Jahr
  • Ferien-Saldo in Tagen (live)
  • Halbtage unterstützt
  • Übertrag ins Folgejahr oder Auszahlung

Absenzen +

Krankheit, Militär, Mutterschaft, unbezahlter Urlaub — getrennt erfasst, mit Auswertung pro Mitarbeiter.

Lohnabrechnung +

Monatliche Abrechnung mit:

  • Grundlohn (basierend auf Pensum)
  • Überstunden-Vergütung
  • Spesen-Erstattung
  • Sozialabzüge (AHV, ALV, Pensionskasse)

PDF-Export pro Mitarbeiter.

Jahresabschluss +

  • Ferien-Restguthaben übertragen oder auszahlen
  • Überstunden ausgleichen oder vergüten
  • Lohnausweis vorbereiten (Export)

Verwandte Module +

  • Zeiterfassung — Pensum-Soll vs. Stunden-Ist
  • Spesen — Spesen-Erstattung in der Lohnabrechnung
\ No newline at end of file diff --git a/public/features/projekte/index.html b/public/features/projekte/index.html new file mode 100644 index 0000000..d575cb4 --- /dev/null +++ b/public/features/projekte/index.html @@ -0,0 +1,60 @@ +Projekt- & Kundenverwaltung – RAPPORTZum Inhalt springen

Projekt- & Kundenverwaltung

In Arbeit

Projekte nach SIA 102 mit Budget, Phasen und Beteiligten. Erstellung aus einer Offerte mit Verknüpfung zu Zeiterfassung und Rechnungen.

Projektstruktur +

Jedes Projekt besitzt:

  • Stammdaten — Nummer, Bezeichnung, Standort, Bauschätzwert
  • Kunde — verknüpft mit Kundendatenbank
  • Beteiligte — Bauleitung, Fachplaner, Behörden
  • Phasen — SIA 102 (Vorprojekt, Bauprojekt, Ausschreibung, …)
  • Budget — Gesamthonorar, pro Phase aufgeteilt

SIA 102 +

Standard-Phasenverteilung wird vorgeschlagen, kann pro Projekt überschrieben werden.

PhaseAnteil (Standard)
31 — Vorprojekt9 %
32 — Bauprojekt21 %
33 — Bewilligung3 %
41 — Ausschreibung18 %
51 — Ausführung38 %
52 — Inbetriebnahme6 %
53 — Abschluss5 %

Kundendatenbank +

  • Adresse, Ansprechperson, Telefon, E-Mail
  • Honorartyp (Stundensatz / SIA / Pauschal)
  • Verknüpfung zu allen Projekten und Rechnungen des Kunden

Auswertung +

Pro Projekt:

  • Geleistete Stunden vs. Budget
  • Honorar-Saldo (verrechnet / Akonto / offen)
  • Phasen-Fortschritt

Verwandte Module +

\ No newline at end of file diff --git a/public/features/protokolle/index.html b/public/features/protokolle/index.html new file mode 100644 index 0000000..0d63a75 --- /dev/null +++ b/public/features/protokolle/index.html @@ -0,0 +1,82 @@ +Protokolle & Lieferscheine – RAPPORTZum Inhalt springen

Protokolle & Lieferscheine

In Arbeit

Einfache Erstellung von Sitzungsprotokollen mit Beschlüssen und Aufgaben. Briefe und Lieferscheine im gleichen Erscheinungsbild.

Sitzungsprotokolle +

Pro Sitzung:

  • Datum, Ort, Teilnehmer (aus Beteiligten-Liste)
  • Traktanden als nummerierte Liste
  • Pro Traktandum: Beschluss, Aufgabe, Verantwortlich, Frist
  • Anhänge

PDF-Export mit Bürobriefbogen.

Briefe +

Brief-Editor mit:

  • Empfänger aus Kundendatenbank
  • Bezugszeile, Anrede, Text, Grussformel
  • Briefbogen-Vorlage mit Logo
  • PDF-Export

Lieferscheine +

Pro Lieferung:

  • Empfänger, Datum, Bezug
  • Positionen (Plan-Nummer, Bezeichnung, Anzahl, Massstab)
  • Unterschriftenfeld

Konsistentes Erscheinungsbild über alle Dokumenttypen — eine Briefbogen-Vorlage, mehrere Verwendungen.

Verwandte Module +

\ No newline at end of file diff --git a/public/features/rechnungen/index.html b/public/features/rechnungen/index.html new file mode 100644 index 0000000..5a2051d --- /dev/null +++ b/public/features/rechnungen/index.html @@ -0,0 +1,96 @@ +Rechnungen & Offerten – RAPPORTZum Inhalt springen

Rechnungen & Offerten

In Arbeit

QR-Einzahlungsscheine, SIA-Phasen, Akonto-, Teil- und Schlussrechnungen. Offerten sind in Projekte und Rechnungen konvertierbar. PDF-Export.

Workflow +

  1. Offerte erstellen — auf Basis SIA 102 oder pauschal
  2. Kunde nimmt an → konvertieren in Projekt + Rechnung
  3. Akonto-Rechnungen während der Projektlaufzeit
  4. Schlussrechnung mit Differenz zum bisher Akonto-bezahlten

QR-Einzahlungsschein +

Schweizer QR-Rechnung nach Norm — direkt eingebettet in die PDF.

Ausgelesen aus:

  • Bürodaten — IBAN, Empfänger-Adresse
  • Kundendaten — Schuldner-Adresse
  • Rechnungs-Daten — Betrag, Referenz, Zusatzinformation

Honorarmodelle +

ModellBerechnungVerwendung
StundensatzAus Zeiterfassung × Mitarbeiter-StundensatzKleinaufträge, Beratung
SIA-PhasenBauschätzwert × Honorarsatz × PhasenanteilReguläre Architektur-Aufträge
PauschalFester BetragAuf Wunsch des Kunden

PDF-Export +

Druckfertige Rechnung inkl. QR-Schein. Layout aus dem Büro-Briefbogen (mit Logo). Mehrsprachig DE/FR/IT (geplant).

Verwandte Module +

\ No newline at end of file diff --git a/public/features/spesen/index.html b/public/features/spesen/index.html new file mode 100644 index 0000000..1905c0e --- /dev/null +++ b/public/features/spesen/index.html @@ -0,0 +1,81 @@ +Spesen & Bürobudget – RAPPORTZum Inhalt springen

Spesen & Bürobudget

In Arbeit

Spesenerfassung mit Belegupload. Jahresbudget mit Einnahmen und Ausgaben. Internes Rechnungswesen.

Spesenerfassung +

Pro Mitarbeiter:

  • Datum, Betrag, Kategorie
  • Beleg-Upload (PDF, JPG, PNG)
  • Projekt-Zuordnung (optional)
  • Status (offen / erstattet)

Kategorien: Reise, Verpflegung, Material, Telefon, Sonstiges.

Jahresbudget +

Übersicht über:

  • Einnahmen — Rechnungsbeträge, sortiert nach Eingang
  • Ausgaben — Spesen, Bürokosten, Löhne, Sozialabzüge
  • Saldo pro Monat / Quartal / Jahr

Auswertung +

  • Einnahmen pro Kunde / Projekt
  • Ausgaben pro Kategorie / Mitarbeiter
  • Erfolgsrechnung pro Geschäftsjahr (vereinfacht)

Verwandte Module +

\ No newline at end of file diff --git a/public/features/system-tray/index.html b/public/features/system-tray/index.html new file mode 100644 index 0000000..42df1d4 --- /dev/null +++ b/public/features/system-tray/index.html @@ -0,0 +1,83 @@ +System-Tray – RAPPORTZum Inhalt springen

System-Tray

Neu in 0.7.0

Schnellzugriff über die Menüleiste mit Hide-on-Close. Beim Schliessen läuft Rapport im Hintergrund weiter — Cmd+Q beendet die App vollständig.

Verhalten +

AktionVerhalten
Fenster schliessen (⌘W oder rotes X)App läuft im Tray weiter
Cmd+QApp wird vollständig beendet
Klick auf Tray-IconFenster nach vorne, oder zeigen
Rechtsklick auf Tray-IconMenü mit Schnellzugriffen

Tray-Menü +

  • Rapport zeigen — Fenster nach vorne
  • Neue Zeiterfassung — direkt im Zeit-Modul
  • Neue Rechnung — direkt im Rechnungs-Modul
  • Letzte Projekte — Quick-Open der letzten 5 Projekte
  • Einstellungen
  • Rapport beenden

Konfiguration +

In den Einstellungen:

  • Beim Systemstart starten (Login-Item) — Standard: aus
  • Beim Schliessen beenden statt ins Tray — Standard: aus
  • Tray-Icon ausblenden — App läuft, aber kein Menüleisten-Icon

Verwandte Module +

\ No newline at end of file diff --git a/public/features/zeiterfassung/index.html b/public/features/zeiterfassung/index.html new file mode 100644 index 0000000..7e0b8ed --- /dev/null +++ b/public/features/zeiterfassung/index.html @@ -0,0 +1,61 @@ +Zeiterfassung – RAPPORTZum Inhalt springen

Zeiterfassung

In Arbeit

Tages- und Wochenraster mit Drag & Drop. Auswertungen pro Mitarbeiter und Projekt. Ferienverwaltung mit Prorata und Jahresabschluss.

Konzept +

Die Zeiterfassung ist das Kernmodul von RAPPORT — alle anderen Module (Rechnungen, Auswertungen, Lohnabrechnung) greifen auf die hier erfassten Stunden zu.

Eingabe +

  • Wochenraster mit den 5 (oder 7) Arbeitstagen
  • Halbstunden-Slots von 06:00 bis 22:00
  • Klick oder Drag über mehrere Slots
  • Jeder Eintrag wird einem Projekt zugewiesen (Pflichtfeld)
  • Mehrfacheinträge pro Slot möglich (z. B. parallele Telefonate)

Auswertungen +

Pro Mitarbeiter und pro Projekt:

  • Geleistete Stunden vs. Soll-Pensum
  • Ferienanspruch / -saldo (mit Prorata bei Eintritt unter Jahr)
  • Überstunden-Saldo
  • Stundenaufschlüsselung nach SIA-Phase pro Projekt

Jahresabschluss +

Am Jahresende:

  • Ferien-Restguthaben übertragen oder auszahlen
  • Überstunden ausgleichen oder vergüten
  • Neues Jahr automatisch initialisieren

Verwandte Module +

  • Rechnungen — Stundensatz-Rechnungen ziehen direkt aus der Zeiterfassung
  • Projekte — Stunden-Auswertung pro SIA-Phase
  • Mitarbeiter — Pensum, Ferienanspruch
\ No newline at end of file diff --git a/public/fonts/Krungthep.ttf b/public/fonts/Krungthep.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9415853ce42198e9dad5b2d6eb1bb62a693141a9 GIT binary patch literal 190152 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 R>-g6n9rr*}uBi`X{6FjT!4d!f literal 0 HcmV?d00001 diff --git a/public/images/logo-dark.svg b/public/images/logo-dark.svg new file mode 100644 index 0000000..2857264 --- /dev/null +++ b/public/images/logo-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/logo.svg b/public/images/logo.svg new file mode 100644 index 0000000..1ed7daf --- /dev/null +++ b/public/images/logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..6b669da --- /dev/null +++ b/public/index.html @@ -0,0 +1,41 @@ +RAPPORTZum Inhalt springen

    Pre-Release 0.8.3 · Aktiv in Entwicklung
    RAPPORT
    Studio Administration

    Die Studio Management Software für Architekturbüros mit offenem Quellcode — Zeiterfassung, Rechnungen, Offerten, Projekte, Mitarbeiter und QR-Einzahlungsscheine in einer App.

    AGPL-3.0 +Tauri + React +macOS (später Linux & Windows) +Lokal / Selfhosting

    ZIEL

    Freie Studio Managementsoftware

    Rapport wurde für eigene Zwecke konzipiert — aus dem Drang heraus, möglichst viel Open-Source-Software in einem Architekturbüro zu verwenden. Die Strukturen folgen der SIA 102 (Phasen, Honorar), die Daten bleiben lokal.

    Aufgebaut auf
    Tauri 2 +React 19 +Vite +SIA 102 +AGPL-3.0
    \ No newline at end of file diff --git a/public/js/flexsearch.433e941a8a573ebb9931fc16fc75266ab6b93f569ac2fb4f3dc66882e0416f4c.js b/public/js/flexsearch.433e941a8a573ebb9931fc16fc75266ab6b93f569ac2fb4f3dc66882e0416f4c.js new file mode 100644 index 0000000..970b757 --- /dev/null +++ b/public/js/flexsearch.433e941a8a573ebb9931fc16fc75266ab6b93f569ac2fb4f3dc66882e0416f4c.js @@ -0,0 +1,96 @@ +/**! + * FlexSearch.js v0.8.143 (Bundle) + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ +(function _f(self){'use strict';if(typeof module!=='undefined')self=module;else if(typeof process !== 'undefined')self=process;self._factory=_f;var t;function z(a,c,b){const e=typeof b,d=typeof a;if("undefined"!==e){if("undefined"!==d){if(b){if("function"===d&&e===d)return function(h){return a(b(h))};c=a.constructor;if(c===b.constructor){if(c===Array)return b.concat(a);if(c===Map){var f=new Map(b);for(var g of a)f.set(g[0],g[1]);return f}if(c===Set){g=new Set(b);for(f of a.values())g.add(f);return g}}}return a}return b}return"undefined"===d?c:a}function B(){return Object.create(null)}function E(a){return"string"===typeof a} +function I(a){return"object"===typeof a}function aa(a){const c=[];for(const b of a.keys())c.push(b);return c}function ba(a,c){if(E(c))a=a[c];else for(let b=0;a&&b"a1a".split(b).length; +this.numeric=z(a.numeric,e)}else{try{this.split=z(this.split,da)}catch(d){this.split=/\s+/}this.numeric=z(a.numeric,z(this.numeric,!0))}this.prepare=z(a.prepare,null,this.prepare);this.finalize=z(a.finalize,null,this.finalize);b=a.filter;this.filter="function"===typeof b?b:z(b&&new Set(b),null,this.filter);this.dedupe=z(a.dedupe,!1,this.dedupe);this.matcher=z((b=a.matcher)&&new Map(b),null,this.matcher);this.mapper=z((b=a.mapper)&&new Map(b),null,this.mapper);this.stemmer=z((b=a.stemmer)&&new Map(b), +null,this.stemmer);this.replacer=z(a.replacer,null,this.replacer);this.minlength=z(a.minlength,1,this.minlength);this.maxlength=z(a.maxlength,0,this.maxlength);this.rtl=z(a.rtl,!1,this.rtl);if(this.cache=b=z(a.cache,!0,this.cache))this.H=null,this.S="number"===typeof b?b:2E5,this.B=new Map,this.G=new Map,this.L=this.K=128;this.h="";this.M=null;this.A="";this.N=null;if(this.matcher)for(const d of this.matcher.keys())this.h+=(this.h?"|":"")+d;if(this.stemmer)for(const d of this.stemmer.keys())this.A+= +(this.A?"|":"")+d;return this};t.addStemmer=function(a,c){this.stemmer||(this.stemmer=new Map);this.stemmer.set(a,c);this.A+=(this.A?"|":"")+a;this.N=null;this.cache&&K(this);return this};t.addFilter=function(a){"function"===typeof a?this.filter=a:(this.filter||(this.filter=new Set),this.filter.add(a));this.cache&&K(this);return this}; +t.addMapper=function(a,c){if("object"===typeof a)return this.addReplacer(a,c);if(1a.length&&(this.dedupe||this.mapper))return this.addMapper(a,c);this.matcher||(this.matcher=new Map);this.matcher.set(a,c);this.h+=(this.h?"|":"")+a;this.M=null;this.cache&&K(this);return this}; +t.addReplacer=function(a,c){if("string"===typeof a)return this.addMatcher(a,c);this.replacer||(this.replacer=[]);this.replacer.push(a,c);this.cache&&K(this);return this}; +t.encode=function(a){if(this.cache&&a.length<=this.K)if(this.H){if(this.B.has(a))return this.B.get(a)}else this.H=setTimeout(K,50,this);this.normalize&&("function"===typeof this.normalize?a=this.normalize(a):a=ia?a.normalize("NFKD").replace(ia,"").toLowerCase():a.toLowerCase());this.prepare&&(a=this.prepare(a));this.numeric&&3this.stemmer.get(k)),d!==g&&this.filter&& +g.length>=this.minlength&&("function"===typeof this.filter?!this.filter(g):this.filter.has(g))&&(g=""));if(g&&(this.mapper||this.dedupe&&1this.matcher.get(k)));if(g&&this.replacer)for(d=0;g&&dthis.S&&(this.G.clear(),this.L=this.L/1.1|0));g&&b.push(g)}this.finalize&&(b=this.finalize(b)||b);this.cache&&a.length<=this.K&&(this.B.set(a,b),this.B.size>this.S&&(this.B.clear(),this.K=this.K/1.1|0));return b};function K(a){a.H=null;a.B.clear();a.G.clear()};let M,ja;async function ka(a){a=a.data;var c=a.task;const b=a.id;let e=a.args;switch(c){case "init":ja=a.options||{};(c=a.factory)?(Function("return "+c)()(self),M=new self.FlexSearch.Index(ja),delete self.FlexSearch):M=new N(ja);postMessage({id:b});break;default:let d;"export"===c&&(e[1]?(e[0]=ja.export,e[2]=0,e[3]=1):e=null);"import"===c?e[0]&&(a=await ja.import.call(M,e[0]),M.import(e[0],a)):(d=e&&M[c].apply(M,e))&&d.then&&(d=await d);postMessage("search"===c?{id:b,msg:d}:{id:b})}};function la(a){ma.call(a,"add");ma.call(a,"append");ma.call(a,"search");ma.call(a,"update");ma.call(a,"remove")}let na,oa,pa;function qa(){na=pa=0} +function ma(a){this[a+"Async"]=function(){const c=arguments;var b=c[c.length-1];let e;"function"===typeof b&&(e=b,delete c[c.length-1]);na?pa||(pa=Date.now()-oa>=this.priority*this.priority*3):(na=setTimeout(qa,0),oa=Date.now());if(pa){const f=this;return new Promise(g=>{setTimeout(function(){g(f[a+"Async"].apply(f,c))},0)})}const d=this[a].apply(this,c);b=d.then?d:new Promise(f=>f(d));e&&b.then(e);return b}};let O=0; +function P(a={}){function c(g){function h(k){k=k.data||k;const l=k.id,n=l&&d.h[l];n&&(n(k.msg),delete d.h[l])}this.worker=g;this.h=B();if(this.worker){e?this.worker.on("message",h):this.worker.onmessage=h;if(a.config)return new Promise(function(k){d.h[++O]=function(){k(d);1E9=g.length)c-=g.length;else{c=g[e?"splice":"slice"](c,b);const h=c.length;if(h&&(d=d.length?d.concat(c):c,b-=h,e&&(a.length-=h),!b))break;c=0}return d} +function R(a){if(!this||this.constructor!==R)return new R(a);this.index=a?[a]:[];this.length=a?a.length:0;const c=this;return new Proxy([],{get(b,e){if("length"===e)return c.length;if("push"===e)return function(d){c.index[c.index.length-1].push(d);c.length++};if("pop"===e)return function(){if(c.length)return c.length--,c.index[c.index.length-1].pop()};if("indexOf"===e)return function(d){let f=0;for(let g=0,h,k;gb||e?k.slice(e,b+e):k;else{if(ab||e)k=k.slice(e,b+e)}else{d=[];for(let m= +0,q;me)e-=q.length;else{if(q.length>b||e)q=q.slice(e,b+e),b-=q.length,e&&(e-=q.length);d.push(q);if(!b)break}k=1c?c?a.slice(b,b+c):a.slice(b):a,e?V.call(this,a):a;let d=[];for(let f=0,g,h;f=h){b-=h;continue}bc&&(g=g.slice(0,c),h=c);if(!d.length&&h>=c)return e?V.call(this,g):g;d.push(g);c-=h;if(!c)break}d=1a.length?this.result=a[0]:(this.result=Ga(a,b,e,!1,this.h),e=0));return f?this.resolve(b,e,d):this};W.prototype.and=function(){let a=this.result.length,c,b,e,d;if(!a){const f=arguments[0];f&&(a=!!f.suggest,d=f.resolve,c=f.limit,b=f.offset,e=f.enrich&&d)}if(a){const {O:f,P:g,limit:h,offset:k,enrich:l,resolve:n,suggest:m}=Ja(this,"and",arguments);return La.call(this,f,g,h,k,l,n,m)}return d?this.resolve(c,b,e):this}; +function La(a,c,b,e,d,f,g){if(c.length){const h=this;return Promise.all(c).then(function(k){a=[];for(let l=0,n;la.length)this.result=a[0];else{if(c=ca(a))return this.result=Fa(a,c,b,e,g,this.h,f),f?d?V.call(this.index,this.result):this.result:this;this.result=[]}else g||(this.result=a);return f?this.resolve(b,e,d):this};W.prototype.xor=function(){const {O:a,P:c,limit:b,offset:e,enrich:d,resolve:f,suggest:g}=Ja(this,"xor",arguments);return Ma.call(this,a,c,b,e,d,f,g)}; +function Ma(a,c,b,e,d,f,g){if(c.length){const h=this;return Promise.all(c).then(function(k){a=[];for(let l=0,n;la.length)this.result=a[0];else return this.result=Na.call(this,a,b,e,f,this.h),f?d?V.call(this.index,this.result):this.result:this;else g||(this.result=a);return f?this.resolve(b,e,d):this} +function Na(a,c,b,e,d){const f=[],g=B();let h=0;for(let k=0,l;kb||e)a=a.slice(e,e+b);d&&(a=V.call(this,a));return a}}function V(a){if(!this||!this.store)return a;const c=Array(a.length);for(let b=0,e;bthis.limit&&this.cache.delete(this.cache.keys().next().value)}; +X.prototype.get=function(a){const c=this.cache.get(a);c&&this.h!==a&&(this.cache.delete(a),this.cache.set(this.h=a,c));return c};X.prototype.remove=function(a){for(const c of this.cache){const b=c[0];c[1].includes(a)&&this.cache.delete(b)}};X.prototype.clear=function(){this.cache.clear();this.h=""};const Wa={normalize:!1,numeric:!1,split:/\s+/};const Xa={normalize:!0};const Ya={normalize:!0,dedupe:!0};const Za=new Map([["b","p"],["v","f"],["w","f"],["z","s"],["x","s"],["d","t"],["n","m"],["c","k"],["g","k"],["j","k"],["q","k"],["i","e"],["y","e"],["u","o"]]);const $a=new Map([["ae","a"],["oe","o"],["sh","s"],["kh","k"],["th","t"],["ph","f"],["pf","f"]]),ab=[/([^aeo])h(.)/g,"$1$2",/([aeo])h([^aeo]|$)/g,"$1$2",/(.)\1+/g,"$1"];const bb={a:"",e:"",i:"",o:"",u:"",y:"",b:1,f:1,p:1,v:1,c:2,g:2,j:2,k:2,q:2,s:2,x:2,z:2,"\u00df":2,d:3,t:3,l:4,m:5,n:5,r:6};var cb={X:Wa,W:Xa,Y:Ya,LatinBalance:{normalize:!0,dedupe:!0,mapper:Za},LatinAdvanced:{normalize:!0,dedupe:!0,mapper:Za,matcher:$a,replacer:ab},LatinExtra:{normalize:!0,dedupe:!0,mapper:Za,replacer:ab.concat([/(?!^)[aeo]/g,""]),matcher:$a},LatinSoundex:{normalize:!0,dedupe:!1,include:{letter:!0},finalize:function(a){for(let b=0;bu;f--){g=r.substring(u,f);v=this.rtl?d-1-u:u;var h=this.score?this.score(c,r,p,g,v):eb(q,e,p,d,v); +fb(this,n,g,h,a,b)}break}case "bidirectional":case "reverse":if(1g?0:1), +e,p,h-1,k-1),v=this.bidirectional&&r>f;fb(this,l,v?f:r,u,a,b,v?r:f)}}}}this.fastupdate||this.reg.add(a)}else c=""}this.db&&(c||this.commit_task.push({del:a}),this.T&&gb(this));return this}; +function fb(a,c,b,e,d,f,g){let h=g?a.ctx:a.map,k;if(!c[b]||g&&!(k=c[b])[g])if(g?(c=k||(c[b]=B()),c[g]=1,(k=h.get(g))?h=k:h.set(g,h=new Map)):c[b]=1,(k=h.get(b))?h=k:h.set(b,h=k=[]),h=h[e]||(h[e]=[]),!f||!h.includes(d)){if(h.length===2**31-1){c=new R(h);if(a.fastupdate)for(let l of a.reg.values())l.includes(h)&&(l[l.indexOf(h)]=c);k[e]=h=c}h.push(d);a.fastupdate&&((e=a.reg.get(d))?e.push(h):a.reg.set(d,[h]))}} +function eb(a,c,b,e,d){return b&&1b)&&(k=b,b=c,c=k);if(a.db)return a.db.get(c,b,e,d,f,g,h);a=b?(a=a.ctx.get(b))&&a.get(c):a.map.get(c);return a};N.prototype.remove=function(a,c){const b=this.reg.size&&(this.fastupdate?this.reg.get(a):this.reg.has(a));if(b){if(this.fastupdate)for(let e=0,d;ed.length)d.pop();else{const f=d.indexOf(a);f===b.length-1?d.pop():d.splice(f,1)}}else lb(this.map,a),this.depth&&lb(this.ctx,a);c||this.reg.delete(a)}this.db&&(this.commit_task.push({del:a}),this.T&&gb(this));this.cache&&this.cache.remove(a);return this}; +function lb(a,c){let b=0;if(a.constructor===Array)for(let e=0,d,f;eb.add(a,c)):this.add(a,c)}; +function mb(a){let c=0;if(a.constructor===Array)for(let b=0,e;b=n.length){e-=n.length;continue}const m=b?e+Math.min(n.length-e,b):n.length;for(let q=e;q=f.length)return[];if(!c&&!b)return f;f=f.slice(b,b+c);return e?d.enrich(f):f})}; +t.enrich=function(a){"object"!==typeof a&&(a=[a]);const c=this.db.transaction("reg","readonly").objectStore("reg"),b=[];for(let e=0;e{a.onsuccess=a.oncomplete=function(){c&&c(this.result);c=null;b(this.result)};a.onerror=a.onblocked=e;a=null})};const rb={Index:N,Charset:cb,Encoder:J,Document:U,Worker:P,Resolver:W,IndexedDB:pb,Language:{}},tb="undefined"!==typeof self?self:"undefined"!==typeof global?global:self;let ub;(ub=tb.define)&&ub.amd?ub([],function(){return rb}):"object"===typeof tb.exports?tb.exports=rb:tb.FlexSearch=rb;}(this||self)); diff --git a/public/js/flexsearch.9f5b5908f93ae86f1ecd4b043b799f580c2d1654e703dd9357d568ac41b2547a.js b/public/js/flexsearch.9f5b5908f93ae86f1ecd4b043b799f580c2d1654e703dd9357d568ac41b2547a.js new file mode 100644 index 0000000..82ae0dd --- /dev/null +++ b/public/js/flexsearch.9f5b5908f93ae86f1ecd4b043b799f580c2d1654e703dd9357d568ac41b2547a.js @@ -0,0 +1,2525 @@ +/**! + * FlexSearch.js v0.8.143 (Bundle/Debug) + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ +(function _f(self){'use strict';if(typeof module!=='undefined')self=module;else if(typeof process !== 'undefined')self=process;self._factory=_f; +var t; +function z(a, c, b) { + const e = typeof b, d = typeof a; + if ("undefined" !== e) { + if ("undefined" !== d) { + if (b) { + if ("function" === d && e === d) { + return function(k) { + return a(b(k)); + }; + } + c = a.constructor; + if (c === b.constructor) { + if (c === Array) { + return b.concat(a); + } + if (c === Map) { + var f = new Map(b); + for (var g of a) { + f.set(g[0], g[1]); + } + return f; + } + if (c === Set) { + g = new Set(b); + for (f of a.values()) { + g.add(f); + } + return g; + } + } + } + return a; + } + return b; + } + return "undefined" === d ? c : a; +} +function B() { + return Object.create(null); +} +function E(a) { + return "string" === typeof a; +} +function I(a) { + return "object" === typeof a; +} +function aa(a) { + const c = []; + for (const b of a.keys()) { + c.push(b); + } + return c; +} +function ba(a, c) { + if (E(c)) { + a = a[c]; + } else { + for (let b = 0; a && b < c.length; b++) { + a = a[c[b]]; + } + } + return a; +} +function ca(a) { + let c = 0; + for (let b = 0, e; b < a.length; b++) { + (e = a[b]) && c < e.length && (c = e.length); + } + return c; +} +;const da = /[^\p{L}\p{N}]+/u, ea = /(\d{3})/g, fa = /(\D)(\d{3})/g, ha = /(\d{3})(\D)/g, ia = /[\u0300-\u036f]/g; +function ja(a = {}) { + if (!this || this.constructor !== ja) { + return new ja(...arguments); + } + if (arguments.length) { + for (a = 0; a < arguments.length; a++) { + this.assign(arguments[a]); + } + } else { + this.assign(a); + } +} +t = ja.prototype; +t.assign = function(a) { + this.normalize = z(a.normalize, !0, this.normalize); + let c = a.include, b = c || a.exclude || a.split, e; + if (b || "" === b) { + if ("object" === typeof b && b.constructor !== RegExp) { + let d = ""; + e = !c; + c || (d += "\\p{Z}"); + b.letter && (d += "\\p{L}"); + b.number && (d += "\\p{N}", e = !!c); + b.symbol && (d += "\\p{S}"); + b.punctuation && (d += "\\p{P}"); + b.control && (d += "\\p{C}"); + if (b = b.char) { + d += "object" === typeof b ? b.join("") : b; + } + try { + this.split = new RegExp("[" + (c ? "^" : "") + d + "]+", "u"); + } catch (f) { + console.error("Your split configuration:", b, "is not supported on this platform. It falls back to using simple whitespace splitter instead: /s+/."), this.split = /\s+/; + } + } else { + this.split = b, e = !1 === b || 2 > "a1a".split(b).length; + } + this.numeric = z(a.numeric, e); + } else { + try { + this.split = z(this.split, da); + } catch (d) { + console.warn("This platform does not support unicode regex. It falls back to using simple whitespace splitter instead: /s+/."), this.split = /\s+/; + } + this.numeric = z(a.numeric, z(this.numeric, !0)); + } + this.prepare = z(a.prepare, null, this.prepare); + this.finalize = z(a.finalize, null, this.finalize); + b = a.filter; + this.filter = "function" === typeof b ? b : z(b && new Set(b), null, this.filter); + this.dedupe = z(a.dedupe, !1, this.dedupe); + this.matcher = z((b = a.matcher) && new Map(b), null, this.matcher); + this.mapper = z((b = a.mapper) && new Map(b), null, this.mapper); + this.stemmer = z((b = a.stemmer) && new Map(b), null, this.stemmer); + this.replacer = z(a.replacer, null, this.replacer); + this.minlength = z(a.minlength, 1, this.minlength); + this.maxlength = z(a.maxlength, 0, this.maxlength); + this.rtl = z(a.rtl, !1, this.rtl); + if (this.cache = b = z(a.cache, !0, this.cache)) { + this.H = null, this.S = "number" === typeof b ? b : 2e5, this.B = new Map(), this.G = new Map(), this.L = this.K = 128; + } + this.h = ""; + this.M = null; + this.A = ""; + this.N = null; + if (this.matcher) { + for (const d of this.matcher.keys()) { + this.h += (this.h ? "|" : "") + d; + } + } + if (this.stemmer) { + for (const d of this.stemmer.keys()) { + this.A += (this.A ? "|" : "") + d; + } + } + return this; +}; +t.addStemmer = function(a, c) { + this.stemmer || (this.stemmer = new Map()); + this.stemmer.set(a, c); + this.A += (this.A ? "|" : "") + a; + this.N = null; + this.cache && J(this); + return this; +}; +t.addFilter = function(a) { + "function" === typeof a ? this.filter = a : (this.filter || (this.filter = new Set()), this.filter.add(a)); + this.cache && J(this); + return this; +}; +t.addMapper = function(a, c) { + if ("object" === typeof a) { + return this.addReplacer(a, c); + } + if (1 < a.length) { + return this.addMatcher(a, c); + } + this.mapper || (this.mapper = new Map()); + this.mapper.set(a, c); + this.cache && J(this); + return this; +}; +t.addMatcher = function(a, c) { + if ("object" === typeof a) { + return this.addReplacer(a, c); + } + if (2 > a.length && (this.dedupe || this.mapper)) { + return this.addMapper(a, c); + } + this.matcher || (this.matcher = new Map()); + this.matcher.set(a, c); + this.h += (this.h ? "|" : "") + a; + this.M = null; + this.cache && J(this); + return this; +}; +t.addReplacer = function(a, c) { + if ("string" === typeof a) { + return this.addMatcher(a, c); + } + this.replacer || (this.replacer = []); + this.replacer.push(a, c); + this.cache && J(this); + return this; +}; +t.encode = function(a) { + if (this.cache && a.length <= this.K) { + if (this.H) { + if (this.B.has(a)) { + return this.B.get(a); + } + } else { + this.H = setTimeout(J, 50, this); + } + } + this.normalize && ("function" === typeof this.normalize ? a = this.normalize(a) : a = ia ? a.normalize("NFKD").replace(ia, "").toLowerCase() : a.toLowerCase()); + this.prepare && (a = this.prepare(a)); + this.numeric && 3 < a.length && (a = a.replace(fa, "$1 $2").replace(ha, "$1 $2").replace(ea, "$1 ")); + const c = !(this.dedupe || this.mapper || this.filter || this.matcher || this.stemmer || this.replacer); + let b = [], e = this.split || "" === this.split ? a.split(this.split) : a; + for (let f = 0, g, k; f < e.length; f++) { + if ((g = k = e[f]) && !(g.length < this.minlength)) { + if (c) { + b.push(g); + } else { + if (!this.filter || ("function" === typeof this.filter ? this.filter(g) : !this.filter.has(g))) { + if (this.cache && g.length <= this.L) { + if (this.H) { + var d = this.G.get(g); + if (d || "" === d) { + d && b.push(d); + continue; + } + } else { + this.H = setTimeout(J, 50, this); + } + } + this.stemmer && 2 < g.length && (this.N || (this.N = new RegExp("(?!^)(" + this.A + ")$")), d = g, g = g.replace(this.N, h => this.stemmer.get(h)), d !== g && this.filter && g.length >= this.minlength && ("function" === typeof this.filter ? !this.filter(g) : this.filter.has(g)) && (g = "")); + if (g && (this.mapper || this.dedupe && 1 < g.length)) { + d = ""; + for (let h = 0, l = "", n, m; h < g.length; h++) { + n = g.charAt(h), n === l && this.dedupe || ((m = this.mapper && this.mapper.get(n)) || "" === m ? m === l && this.dedupe || !(l = m) || (d += m) : d += l = n); + } + g = d; + } + this.matcher && 1 < g.length && (this.M || (this.M = new RegExp("(" + this.h + ")", "g")), g = g.replace(this.M, h => this.matcher.get(h))); + if (g && this.replacer) { + for (d = 0; g && d < this.replacer.length; d += 2) { + g = g.replace(this.replacer[d], this.replacer[d + 1]); + } + } + this.cache && k.length <= this.L && (this.G.set(k, g), this.G.size > this.S && (this.G.clear(), this.L = this.L / 1.1 | 0)); + g && b.push(g); + } + } + } + } + this.finalize && (b = this.finalize(b) || b); + this.cache && a.length <= this.K && (this.B.set(a, b), this.B.size > this.S && (this.B.clear(), this.K = this.K / 1.1 | 0)); + return b; +}; +function J(a) { + a.H = null; + a.B.clear(); + a.G.clear(); +} +;let K, M; +async function ka(a) { + a = a.data; + var c = a.task; + const b = a.id; + let e = a.args; + switch(c) { + case "init": + M = a.options || {}; + (c = a.factory) ? (Function("return " + c)()(self), K = new self.FlexSearch.Index(M), delete self.FlexSearch) : K = new N(M); + postMessage({id:b}); + break; + default: + let d; + if ("export" === c) { + if (!M.export || "function" !== typeof M.export) { + throw Error('Either no extern configuration provided for the Worker-Index or no method was defined on the config property "export".'); + } + e[1] ? (e[0] = M.export, e[2] = 0, e[3] = 1) : e = null; + } + if ("import" === c) { + if (!M.import || "function" !== typeof M.import) { + throw Error('Either no extern configuration provided for the Worker-Index or no method was defined on the config property "import".'); + } + e[0] && (a = await M.import.call(K, e[0]), K.import(e[0], a)); + } else { + (d = e && K[c].apply(K, e)) && d.then && (d = await d); + } + postMessage("search" === c ? {id:b, msg:d} : {id:b}); + } +} +;function la(a) { + ma.call(a, "add"); + ma.call(a, "append"); + ma.call(a, "search"); + ma.call(a, "update"); + ma.call(a, "remove"); +} +let na, oa, pa; +function qa() { + na = pa = 0; +} +function ma(a) { + this[a + "Async"] = function() { + const c = arguments; + var b = c[c.length - 1]; + let e; + "function" === typeof b && (e = b, delete c[c.length - 1]); + na ? pa || (pa = Date.now() - oa >= this.priority * this.priority * 3) : (na = setTimeout(qa, 0), oa = Date.now()); + if (pa) { + const f = this; + return new Promise(g => { + setTimeout(function() { + g(f[a + "Async"].apply(f, c)); + }, 0); + }); + } + const d = this[a].apply(this, c); + b = d.then ? d : new Promise(f => f(d)); + e && b.then(e); + return b; + }; +} +;let O = 0; +function P(a = {}) { + function c(g) { + function k(h) { + h = h.data || h; + const l = h.id, n = l && d.h[l]; + n && (n(h.msg), delete d.h[l]); + } + this.worker = g; + this.h = B(); + if (this.worker) { + e ? this.worker.on("message", k) : this.worker.onmessage = k; + if (a.config) { + return new Promise(function(h) { + d.h[++O] = function() { + h(d); + 1e9 < O && (O = 0); + }; + d.worker.postMessage({id:O, task:"init", factory:b, options:a}); + }); + } + this.worker.postMessage({task:"init", factory:b, options:a}); + this.priority = a.priority || 4; + return this; + } + } + if (!this || this.constructor !== P) { + return new P(a); + } + let b = "undefined" !== typeof self ? self._factory : "undefined" !== typeof window ? window._factory : null; + b && (b = b.toString()); + const e = "undefined" === typeof window, d = this, f = ra(b, e, a.worker); + return f.then ? f.then(function(g) { + return c.call(d, g); + }) : c.call(this, f); +} +Q("add"); +Q("append"); +Q("search"); +Q("update"); +Q("remove"); +Q("clear"); +Q("export"); +Q("import"); +la(P.prototype); +function Q(a) { + P.prototype[a] = function() { + const c = this, b = [].slice.call(arguments); + var e = b[b.length - 1]; + let d; + "function" === typeof e && (d = e, b.pop()); + e = new Promise(function(f) { + "export" === a && "function" === typeof b[0] && (b[0] = null); + c.h[++O] = f; + c.worker.postMessage({task:a, id:O, args:b}); + }); + return d ? (e.then(d), this) : e; + }; +} +function ra(a, c, b) { + return c ? "undefined" !== typeof module ? new(require("worker_threads")["Worker"])(__dirname+"/node/node.js") : import("worker_threads").then(function(worker){return new worker["Worker"]((1,eval)("import.meta.dirname")+"/node/node.mjs")}) : a ? new window.Worker(URL.createObjectURL(new Blob(["onmessage=" + ka.toString()], {type:"text/javascript"}))) : new window.Worker("string" === typeof b ? b : (0,eval)("import.meta.url").replace("/worker.js", "/worker/worker.js").replace("flexsearch.bundle.module.min.js", + "module/worker/worker.js"), {type:"module"}); +} +;function sa(a, c = 0) { + let b = [], e = []; + c && (c = 250000 / c * 5000 | 0); + for (const d of a.entries()) { + e.push(d), e.length === c && (b.push(e), e = []); + } + e.length && b.push(e); + return b; +} +function ta(a, c) { + c || (c = new Map()); + for (let b = 0, e; b < a.length; b++) { + e = a[b], c.set(e[0], e[1]); + } + return c; +} +function ua(a, c = 0) { + let b = [], e = []; + c && (c = 250000 / c * 1000 | 0); + for (const d of a.entries()) { + e.push([d[0], sa(d[1])[0]]), e.length === c && (b.push(e), e = []); + } + e.length && b.push(e); + return b; +} +function va(a, c) { + c || (c = new Map()); + for (let b = 0, e, d; b < a.length; b++) { + e = a[b], d = c.get(e[0]), c.set(e[0], ta(e[1], d)); + } + return c; +} +function wa(a) { + let c = [], b = []; + for (const e of a.keys()) { + b.push(e), 250000 === b.length && (c.push(b), b = []); + } + b.length && c.push(b); + return c; +} +function xa(a, c) { + c || (c = new Set()); + for (let b = 0; b < a.length; b++) { + c.add(a[b]); + } + return c; +} +function ya(a, c, b, e, d, f, g = 0) { + const k = e && e.constructor === Array; + var h = k ? e.shift() : e; + if (!h) { + return this.export(a, c, d, f + 1); + } + if ((h = a((c ? c + "." : "") + (g + 1) + "." + b, JSON.stringify(h))) && h.then) { + const l = this; + return h.then(function() { + return ya.call(l, a, c, b, k ? e : null, d, f, g + 1); + }); + } + return ya.call(this, a, c, b, k ? e : null, d, f, g + 1); +} +function za(a, c) { + let b = ""; + for (const e of a.entries()) { + a = e[0]; + const d = e[1]; + let f = ""; + for (let g = 0, k; g < d.length; g++) { + k = d[g] || [""]; + let h = ""; + for (let l = 0; l < k.length; l++) { + h += (h ? "," : "") + ("string" === c ? '"' + k[l] + '"' : k[l]); + } + h = "[" + h + "]"; + f += (f ? "," : "") + h; + } + f = '["' + a + '",[' + f + "]]"; + b += (b ? "," : "") + f; + } + return b; +} +;function Aa(a, c, b, e) { + let d = []; + for (let f = 0, g; f < a.index.length; f++) { + if (g = a.index[f], c >= g.length) { + c -= g.length; + } else { + c = g[e ? "splice" : "slice"](c, b); + const k = c.length; + if (k && (d = d.length ? d.concat(c) : c, b -= k, e && (a.length -= k), !b)) { + break; + } + c = 0; + } + } + return d; +} +function R(a) { + if (!this || this.constructor !== R) { + return new R(a); + } + this.index = a ? [a] : []; + this.length = a ? a.length : 0; + const c = this; + return new Proxy([], {get(b, e) { + if ("length" === e) { + return c.length; + } + if ("push" === e) { + return function(d) { + c.index[c.index.length - 1].push(d); + c.length++; + }; + } + if ("pop" === e) { + return function() { + if (c.length) { + return c.length--, c.index[c.index.length - 1].pop(); + } + }; + } + if ("indexOf" === e) { + return function(d) { + let f = 0; + for (let g = 0, k, h; g < c.index.length; g++) { + k = c.index[g]; + h = k.indexOf(d); + if (0 <= h) { + return f + h; + } + f += k.length; + } + return -1; + }; + } + if ("includes" === e) { + return function(d) { + for (let f = 0; f < c.index.length; f++) { + if (c.index[f].includes(d)) { + return !0; + } + } + return !1; + }; + } + if ("slice" === e) { + return function(d, f) { + return Aa(c, d || 0, f || c.length, !1); + }; + } + if ("splice" === e) { + return function(d, f) { + return Aa(c, d || 0, f || c.length, !0); + }; + } + if ("constructor" === e) { + return Array; + } + if ("symbol" !== typeof e) { + return (b = c.index[e / 2 ** 31 | 0]) && b[e]; + } + }, set(b, e, d) { + b = e / 2 ** 31 | 0; + (c.index[b] || (c.index[b] = []))[e] = d; + c.length++; + return !0; + }}); +} +R.prototype.clear = function() { + this.index.length = 0; +}; +R.prototype.destroy = function() { + this.proxy = this.index = null; +}; +R.prototype.push = function() { +}; +function S(a = 8) { + if (!this || this.constructor !== S) { + return new S(a); + } + this.index = B(); + this.h = []; + this.size = 0; + 32 < a ? (this.B = Ba, this.A = BigInt(a)) : (this.B = Ca, this.A = a); +} +S.prototype.get = function(a) { + const c = this.index[this.B(a)]; + return c && c.get(a); +}; +S.prototype.set = function(a, c) { + var b = this.B(a); + let e = this.index[b]; + e ? (b = e.size, e.set(a, c), (b -= e.size) && this.size++) : (this.index[b] = e = new Map([[a, c]]), this.h.push(e), this.size++); +}; +function T(a = 8) { + if (!this || this.constructor !== T) { + return new T(a); + } + this.index = B(); + this.h = []; + this.size = 0; + 32 < a ? (this.B = Ba, this.A = BigInt(a)) : (this.B = Ca, this.A = a); +} +T.prototype.add = function(a) { + var c = this.B(a); + let b = this.index[c]; + b ? (c = b.size, b.add(a), (c -= b.size) && this.size++) : (this.index[c] = b = new Set([a]), this.h.push(b), this.size++); +}; +t = S.prototype; +t.has = T.prototype.has = function(a) { + const c = this.index[this.B(a)]; + return c && c.has(a); +}; +t.delete = T.prototype.delete = function(a) { + const c = this.index[this.B(a)]; + c && c.delete(a) && this.size--; +}; +t.clear = T.prototype.clear = function() { + this.index = B(); + this.h = []; + this.size = 0; +}; +t.values = T.prototype.values = function*() { + for (let a = 0; a < this.h.length; a++) { + for (let c of this.h[a].values()) { + yield c; + } + } +}; +t.keys = T.prototype.keys = function*() { + for (let a = 0; a < this.h.length; a++) { + for (let c of this.h[a].keys()) { + yield c; + } + } +}; +t.entries = T.prototype.entries = function*() { + for (let a = 0; a < this.h.length; a++) { + for (let c of this.h[a].entries()) { + yield c; + } + } +}; +function Ca(a) { + let c = 2 ** this.A - 1; + if ("number" == typeof a) { + return a & c; + } + let b = 0, e = this.A + 1; + for (let d = 0; d < a.length; d++) { + b = (b * e ^ a.charCodeAt(d)) & c; + } + return 32 === this.A ? b + 2 ** 31 : b; +} +function Ba(a) { + let c = BigInt(2) ** this.A - BigInt(1); + var b = typeof a; + if ("bigint" === b) { + return a & c; + } + if ("number" === b) { + return BigInt(a) & c; + } + b = BigInt(0); + let e = this.A + BigInt(1); + for (let d = 0; d < a.length; d++) { + b = (b * e ^ BigInt(a.charCodeAt(d))) & c; + } + return b; +} +;U.prototype.add = function(a, c, b) { + I(a) && (c = a, a = ba(c, this.key)); + if (c && (a || 0 === a)) { + if (!b && this.reg.has(a)) { + return this.update(a, c); + } + for (let k = 0, h; k < this.field.length; k++) { + h = this.D[k]; + var e = this.index.get(this.field[k]); + if ("function" === typeof h) { + var d = h(c); + d && e.add(a, d, !1, !0); + } else { + if (d = h.I, !d || d(c)) { + h.constructor === String ? h = ["" + h] : E(h) && (h = [h]), Da(c, h, this.J, 0, e, a, h[0], b); + } + } + } + if (this.tag) { + for (e = 0; e < this.F.length; e++) { + var f = this.F[e], g = this.R[e]; + d = this.tag.get(g); + let k = B(); + if ("function" === typeof f) { + if (f = f(c), !f) { + continue; + } + } else { + const h = f.I; + if (h && !h(c)) { + continue; + } + f.constructor === String && (f = "" + f); + f = ba(c, f); + } + if (d && f) { + E(f) && (f = [f]); + for (let h = 0, l, n; h < f.length; h++) { + if (l = f[h], !k[l] && (k[l] = 1, (g = d.get(l)) ? n = g : d.set(l, n = []), !b || !n.includes(a))) { + if (n.length === 2 ** 31 - 1) { + g = new R(n); + if (this.fastupdate) { + for (let m of this.reg.values()) { + m.includes(n) && (m[m.indexOf(n)] = g); + } + } + d.set(l, n = g); + } + n.push(a); + this.fastupdate && ((g = this.reg.get(a)) ? g.push(n) : this.reg.set(a, [n])); + } + } + } else { + d || console.warn("Tag '" + g + "' was not found"); + } + } + } + if (this.store && (!b || !this.store.has(a))) { + let k; + if (this.C) { + k = B(); + for (let h = 0, l; h < this.C.length; h++) { + l = this.C[h]; + if ((b = l.I) && !b(c)) { + continue; + } + let n; + if ("function" === typeof l) { + n = l(c); + if (!n) { + continue; + } + l = [l.V]; + } else if (E(l) || l.constructor === String) { + k[l] = c[l]; + continue; + } + Ea(c, k, l, 0, l[0], n); + } + } + this.store.set(a, k || c); + } + this.worker && (this.fastupdate || this.reg.add(a)); + } + return this; +}; +function Ea(a, c, b, e, d, f) { + a = a[d]; + if (e === b.length - 1) { + c[d] = f || a; + } else if (a) { + if (a.constructor === Array) { + for (c = c[d] = Array(a.length), d = 0; d < a.length; d++) { + Ea(a, c, b, e, d); + } + } else { + c = c[d] || (c[d] = B()), d = b[++e], Ea(a, c, b, e, d); + } + } +} +function Da(a, c, b, e, d, f, g, k) { + if (a = a[g]) { + if (e === c.length - 1) { + if (a.constructor === Array) { + if (b[e]) { + for (c = 0; c < a.length; c++) { + d.add(f, a[c], !0, !0); + } + return; + } + a = a.join(" "); + } + d.add(f, a, k, !0); + } else { + if (a.constructor === Array) { + for (g = 0; g < a.length; g++) { + Da(a, c, b, e, d, f, g, k); + } + } else { + g = c[++e], Da(a, c, b, e, d, f, g, k); + } + } + } else { + d.db && d.remove(f); + } +} +;function Fa(a, c, b, e, d, f, g) { + const k = a.length; + let h = [], l, n; + l = B(); + for (let m = 0, q, p, r, u; m < c; m++) { + for (let v = 0; v < k; v++) { + if (r = a[v], m < r.length && (q = r[m])) { + for (let w = 0; w < q.length; w++) { + p = q[w]; + (n = l[p]) ? l[p]++ : (n = 0, l[p] = 1); + u = h[n] || (h[n] = []); + if (!g) { + let y = m + (v || !d ? 0 : f || 0); + u = u[y] || (u[y] = []); + } + u.push(p); + if (g && b && n === k - 1 && u.length - e === b) { + return u; + } + } + } + } + } + if (a = h.length) { + if (d) { + h = 1 < h.length ? Ga(h, b, e, g, f) : (h = h[0]).length > b || e ? h.slice(e, b + e) : h; + } else { + if (a < k) { + return []; + } + h = h[a - 1]; + if (b || e) { + if (g) { + if (h.length > b || e) { + h = h.slice(e, b + e); + } + } else { + d = []; + for (let m = 0, q; m < h.length; m++) { + if (q = h[m], q.length > e) { + e -= q.length; + } else { + if (q.length > b || e) { + q = q.slice(e, b + e), b -= q.length, e && (e -= q.length); + } + d.push(q); + if (!b) { + break; + } + } + } + h = 1 < d.length ? [].concat.apply([], d) : d[0]; + } + } + } + } + return h; +} +function Ga(a, c, b, e, d) { + const f = [], g = B(); + let k; + var h = a.length; + let l; + if (e) { + for (d = h - 1; 0 <= d; d--) { + if (l = (e = a[d]) && e.length) { + for (h = 0; h < l; h++) { + if (k = e[h], !g[k]) { + if (g[k] = 1, b) { + b--; + } else { + if (f.push(k), f.length === c) { + return f; + } + } + } + } + } + } + } else { + for (let n = h - 1, m, q = 0; 0 <= n; n--) { + m = a[n]; + for (let p = 0; p < m.length; p++) { + if (l = (e = m[p]) && e.length) { + for (let r = 0; r < l; r++) { + if (k = e[r], !g[k]) { + if (g[k] = 1, b) { + b--; + } else { + let u = (p + (n < h - 1 ? d || 0 : 0)) / (n + 1) | 0; + (f[u] || (f[u] = [])).push(k); + if (++q === c) { + return f; + } + } + } + } + } + } + } + } + return f; +} +function Ha(a, c, b) { + const e = B(), d = []; + for (let f = 0, g; f < c.length; f++) { + g = c[f]; + for (let k = 0; k < g.length; k++) { + e[g[k]] = 1; + } + } + if (b) { + for (let f = 0, g; f < a.length; f++) { + g = a[f], e[g] && (d.push(g), e[g] = 0); + } + } else { + for (let f = 0, g, k; f < a.result.length; f++) { + for (g = a.result[f], c = 0; c < g.length; c++) { + k = g[c], e[k] && ((d[f] || (d[f] = [])).push(k), e[k] = 0); + } + } + } + return d; +} +;function Ia(a, c, b, e) { + if (!a.length) { + return a; + } + if (1 === a.length) { + return a = a[0], a = b || a.length > c ? c ? a.slice(b, b + c) : a.slice(b) : a, e ? V.call(this, a) : a; + } + let d = []; + for (let f = 0, g, k; f < a.length; f++) { + if ((g = a[f]) && (k = g.length)) { + if (b) { + if (b >= k) { + b -= k; + continue; + } + b < k && (g = c ? g.slice(b, b + c) : g.slice(b), k = g.length, b = 0); + } + k > c && (g = g.slice(0, c), k = c); + if (!d.length && k >= c) { + return e ? V.call(this, g) : g; + } + d.push(g); + c -= k; + if (!c) { + break; + } + } + } + d = 1 < d.length ? [].concat.apply([], d) : d[0]; + return e ? V.call(this, d) : d; +} +;function Ja(a, c, b) { + var e = b[0]; + if (e.then) { + return Promise.all(b).then(function(n) { + return a[c].apply(a, n); + }); + } + if (e[0] && e[0].index) { + return a[c].apply(a, e); + } + e = []; + let d = [], f = 0, g = 0, k, h, l; + for (let n = 0, m; n < b.length; n++) { + if (m = b[n]) { + let q; + if (m.constructor === W) { + q = m.result; + } else if (m.constructor === Array) { + q = m; + } else { + if (f = m.limit || 0, g = m.offset || 0, l = m.suggest, h = m.resolve, k = m.enrich && h, m.index) { + m.resolve = !1, m.enrich = !1, q = m.index.search(m).result, m.resolve = h, m.enrich = k; + } else if (m.and) { + q = a.and(m.and); + } else if (m.or) { + q = a.or(m.or); + } else if (m.xor) { + q = a.xor(m.xor); + } else if (m.not) { + q = a.not(m.not); + } else { + continue; + } + } + if (q.then) { + d.push(q); + } else if (q.length) { + e[n] = q; + } else if (!l && ("and" === c || "xor" === c)) { + e = []; + break; + } + } + } + return {O:e, P:d, limit:f, offset:g, enrich:k, resolve:h, suggest:l}; +} +;W.prototype.or = function() { + const {O:a, P:c, limit:b, offset:e, enrich:d, resolve:f} = Ja(this, "or", arguments); + return Ka.call(this, a, c, b, e, d, f); +}; +function Ka(a, c, b, e, d, f) { + if (c.length) { + const g = this; + return Promise.all(c).then(function(k) { + a = []; + for (let h = 0, l; h < k.length; h++) { + (l = k[h]).length && (a[h] = l); + } + return Ka.call(g, a, [], b, e, d, f); + }); + } + a.length && (this.result.length && a.push(this.result), 2 > a.length ? this.result = a[0] : (this.result = Ga(a, b, e, !1, this.h), e = 0)); + return f ? this.resolve(b, e, d) : this; +} +;W.prototype.and = function() { + let a = this.result.length, c, b, e, d; + if (!a) { + const f = arguments[0]; + f && (a = !!f.suggest, d = f.resolve, c = f.limit, b = f.offset, e = f.enrich && d); + } + if (a) { + const {O:f, P:g, limit:k, offset:h, enrich:l, resolve:n, suggest:m} = Ja(this, "and", arguments); + return La.call(this, f, g, k, h, l, n, m); + } + return d ? this.resolve(c, b, e) : this; +}; +function La(a, c, b, e, d, f, g) { + if (c.length) { + const k = this; + return Promise.all(c).then(function(h) { + a = []; + for (let l = 0, n; l < h.length; l++) { + (n = h[l]).length && (a[l] = n); + } + return La.call(k, a, [], b, e, d, f, g); + }); + } + if (a.length) { + if (this.result.length && a.unshift(this.result), 2 > a.length) { + this.result = a[0]; + } else { + if (c = ca(a)) { + return this.result = Fa(a, c, b, e, g, this.h, f), f ? d ? V.call(this.index, this.result) : this.result : this; + } + this.result = []; + } + } else { + g || (this.result = a); + } + return f ? this.resolve(b, e, d) : this; +} +;W.prototype.xor = function() { + const {O:a, P:c, limit:b, offset:e, enrich:d, resolve:f, suggest:g} = Ja(this, "xor", arguments); + return Ma.call(this, a, c, b, e, d, f, g); +}; +function Ma(a, c, b, e, d, f, g) { + if (c.length) { + const k = this; + return Promise.all(c).then(function(h) { + a = []; + for (let l = 0, n; l < h.length; l++) { + (n = h[l]).length && (a[l] = n); + } + return Ma.call(k, a, [], b, e, d, f, g); + }); + } + if (a.length) { + if (this.result.length && a.unshift(this.result), 2 > a.length) { + this.result = a[0]; + } else { + return this.result = Na.call(this, a, b, e, f, this.h), f ? d ? V.call(this.index, this.result) : this.result : this; + } + } else { + g || (this.result = a); + } + return f ? this.resolve(b, e, d) : this; +} +function Na(a, c, b, e, d) { + const f = [], g = B(); + let k = 0; + for (let h = 0, l; h < a.length; h++) { + if (l = a[h]) { + k < l.length && (k = l.length); + for (let n = 0, m; n < l.length; n++) { + if (m = l[n]) { + for (let q = 0, p; q < m.length; q++) { + p = m[q], g[p] = g[p] ? 2 : 1; + } + } + } + } + } + for (let h = 0, l, n = 0; h < k; h++) { + for (let m = 0, q; m < a.length; m++) { + if (q = a[m]) { + if (l = q[h]) { + for (let p = 0, r; p < l.length; p++) { + if (r = l[p], 1 === g[r]) { + if (b) { + b--; + } else { + if (e) { + if (f.push(r), f.length === c) { + return f; + } + } else { + const u = h + (m ? d : 0); + f[u] || (f[u] = []); + f[u].push(r); + if (++n === c) { + return f; + } + } + } + } + } + } + } + } + } + return f; +} +;W.prototype.not = function() { + const {O:a, P:c, limit:b, offset:e, enrich:d, resolve:f, suggest:g} = Ja(this, "not", arguments); + return Oa.call(this, a, c, b, e, d, f, g); +}; +function Oa(a, c, b, e, d, f, g) { + if (c.length) { + const k = this; + return Promise.all(c).then(function(h) { + a = []; + for (let l = 0, n; l < h.length; l++) { + (n = h[l]).length && (a[l] = n); + } + return Oa.call(k, a, [], b, e, d, f, g); + }); + } + if (a.length && this.result.length) { + this.result = Pa.call(this, a, b, e, f); + } else if (f) { + return this.resolve(b, e, d); + } + return f ? d ? V.call(this.index, this.result) : this.result : this; +} +function Pa(a, c, b, e) { + const d = []; + a = new Set(a.flat().flat()); + for (let f = 0, g, k = 0; f < this.result.length; f++) { + if (g = this.result[f]) { + for (let h = 0, l; h < g.length; h++) { + if (l = g[h], !a.has(l)) { + if (b) { + b--; + } else { + if (e) { + if (d.push(l), d.length === c) { + return d; + } + } else { + if (d[f] || (d[f] = []), d[f].push(l), ++k === c) { + return d; + } + } + } + } + } + } + } + return d; +} +;function W(a) { + if (!this || this.constructor !== W) { + return new W(a); + } + if (a && a.index) { + return a.resolve = !1, this.index = a.index, this.h = a.boost || 0, this.result = a.index.search(a).result, this; + } + this.index = null; + this.result = a || []; + this.h = 0; +} +W.prototype.limit = function(a) { + if (this.result.length) { + const c = []; + for (let b = 0, e; b < this.result.length; b++) { + if (e = this.result[b]) { + if (e.length <= a) { + if (c[b] = e, a -= e.length, !a) { + break; + } + } else { + c[b] = e.slice(0, a); + break; + } + } + } + this.result = c; + } + return this; +}; +W.prototype.offset = function(a) { + if (this.result.length) { + const c = []; + for (let b = 0, e; b < this.result.length; b++) { + if (e = this.result[b]) { + e.length <= a ? a -= e.length : (c[b] = e.slice(a), a = 0); + } + } + this.result = c; + } + return this; +}; +W.prototype.boost = function(a) { + this.h += a; + return this; +}; +W.prototype.resolve = function(a, c, b) { + const e = this.result, d = this.index; + this.result = this.index = null; + return e.length ? ("object" === typeof a && (b = a.enrich, c = a.offset, a = a.limit), Ia.call(d, e, a || 100, c, b)) : e; +}; +B(); +U.prototype.search = function(a, c, b, e) { + b || (!c && I(a) ? (b = a, a = "") : I(c) && (b = c, c = 0)); + let d = []; + var f = [], g; + let k; + let h, l; + let n = 0; + var m = !0; + let q; + if (b) { + b.constructor === Array && (b = {index:b}); + a = b.query || a; + var p = b.pluck; + k = b.merge; + h = p || b.field || (h = b.index) && (h.index ? null : h); + l = this.tag && b.tag; + var r = b.suggest; + m = !1 !== b.resolve; + if (!m && !p) { + if (h = h || this.field) { + E(h) ? p = h : (h.constructor === Array && 1 === h.length && (h = h[0]), p = h.field || h.index); + } + if (!p) { + throw Error("Apply resolver on document search requires either the option 'pluck' to be set or just select a single field name in your query."); + } + } + this.store && b.enrich && !m && console.warn("Enrich results can only be done on a final resolver task or when calling .resolve({ enrich: true })"); + q = (g = this.store && b.enrich && m) && b.highlight; + c = b.limit || c; + var u = b.offset || 0; + c || (c = 100); + if (l && (!this.db || !e)) { + l.constructor !== Array && (l = [l]); + var v = []; + for (let A = 0, x; A < l.length; A++) { + x = l[A]; + if (E(x)) { + throw Error("A tag option can't be a string, instead it needs a { field: tag } format."); + } + if (x.field && x.tag) { + var w = x.tag; + if (w.constructor === Array) { + for (var y = 0; y < w.length; y++) { + v.push(x.field, w[y]); + } + } else { + v.push(x.field, w); + } + } else { + w = Object.keys(x); + for (let D = 0, H, C; D < w.length; D++) { + if (H = w[D], C = x[H], C.constructor === Array) { + for (y = 0; y < C.length; y++) { + v.push(H, C[y]); + } + } else { + v.push(H, C); + } + } + } + } + if (!v.length) { + throw Error("Your tag definition within the search options is probably wrong. No valid tags found."); + } + l = v; + if (!a) { + m = []; + if (v.length) { + for (f = 0; f < v.length; f += 2) { + if (this.db) { + p = this.index.get(v[f]); + if (!p) { + console.warn("Tag '" + v[f] + ":" + v[f + 1] + "' will be skipped because there is no field '" + v[f] + "'."); + continue; + } + m.push(p = p.db.tag(v[f + 1], c, u, g)); + } else { + p = Qa.call(this, v[f], v[f + 1], c, u, g); + } + d.push({field:v[f], tag:v[f + 1], result:p}); + } + } + return m.length ? Promise.all(m).then(function(A) { + for (let x = 0; x < A.length; x++) { + d[x].result = A[x]; + } + return d; + }) : d; + } + } + h && h.constructor !== Array && (h = [h]); + } + h || (h = this.field); + v = !e && (this.worker || this.db) && []; + let F; + for (let A = 0, x, D, H; A < h.length; A++) { + D = h[A]; + if (this.db && this.tag && !this.D[A]) { + continue; + } + let C; + E(D) || (C = D, D = C.field, a = C.query || a, c = C.limit || c, u = C.offset || u, r = C.suggest || r, g = this.store && (C.enrich || g)); + if (e) { + x = e[A]; + } else { + if (w = C || b, y = this.index.get(D), l && (this.db && (w.tag = l, F = y.db.support_tag_search, w.field = h), F || (w.enrich = !1)), v) { + v[A] = y.search(a, c, w); + w && g && (w.enrich = g); + continue; + } else { + x = y.search(a, c, w), w && g && (w.enrich = g); + } + } + H = x && (m ? x.length : x.result.length); + if (l && H) { + w = []; + y = 0; + if (this.db && e) { + if (!F) { + for (let G = h.length; G < e.length; G++) { + let L = e[G]; + if (L && L.length) { + y++, w.push(L); + } else if (!r) { + return m ? d : new W(d); + } + } + } + } else { + for (let G = 0, L, tb; G < l.length; G += 2) { + L = this.tag.get(l[G]); + if (!L) { + if (console.warn("Tag '" + l[G] + ":" + l[G + 1] + "' will be skipped because there is no field '" + l[G] + "'."), r) { + continue; + } else { + return m ? d : new W(d); + } + } + if (tb = (L = L && L.get(l[G + 1])) && L.length) { + y++, w.push(L); + } else if (!r) { + return m ? d : new W(d); + } + } + } + if (y) { + x = Ha(x, w, m); + H = x.length; + if (!H && !r) { + return m ? x : new W(x); + } + y--; + } + } + if (H) { + f[n] = D, d.push(x), n++; + } else if (1 === h.length) { + return m ? d : new W(d); + } + } + if (v) { + if (this.db && l && l.length && !F) { + for (g = 0; g < l.length; g += 2) { + f = this.index.get(l[g]); + if (!f) { + if (console.warn("Tag '" + l[g] + ":" + l[g + 1] + "' was not found because there is no field '" + l[g] + "'."), r) { + continue; + } else { + return m ? d : new W(d); + } + } + v.push(f.db.tag(l[g + 1], c, u, !1)); + } + } + const A = this; + return Promise.all(v).then(function(x) { + return x.length ? A.search(a, c, b, x) : x; + }); + } + if (!n) { + return m ? d : new W(d); + } + if (p && (!g || !this.store)) { + return d[0]; + } + v = []; + for (u = 0; u < f.length; u++) { + r = d[u]; + g && r.length && "undefined" === typeof r[0].doc && (this.db ? v.push(r = this.index.get(this.field[0]).db.enrich(r)) : r = V.call(this, r)); + if (p) { + return m ? r : new W(r); + } + d[u] = {field:f[u], result:r}; + } + if (g && this.db && v.length) { + const A = this; + return Promise.all(v).then(function(x) { + for (let D = 0; D < x.length; D++) { + d[D].result = x[D]; + } + return k ? Ra(d, c) : q ? Sa(d, a, A.index, A.field, A.D, q) : d; + }); + } + return k ? Ra(d, c) : q ? Sa(d, a, this.index, this.field, this.D, q) : d; +}; +function Sa(a, c, b, e, d, f) { + let g, k, h; + for (let n = 0, m, q, p, r; n < a.length; n++) { + let u = a[n].result; + m = a[n].field; + p = b.get(m); + q = p.encoder; + h = p.tokenize; + r = d[e.indexOf(m)]; + q !== g && (g = q, k = g.encode(c)); + for (let v = 0; v < u.length; v++) { + let w = ""; + var l = ba(u[v].doc, r); + let y = g.encode(l); + l = l.split(g.split); + for (let F = 0, A, x; F < y.length; F++) { + A = y[F]; + x = l[F]; + if (!A || !x) { + continue; + } + let D; + for (let H = 0, C; H < k.length; H++) { + if (C = k[H], "strict" === h) { + if (A === C) { + w += (w ? " " : "") + f.replace("$1", x); + D = !0; + break; + } + } else { + const G = A.indexOf(C); + if (-1 < G) { + w += (w ? " " : "") + x.substring(0, G) + f.replace("$1", x.substring(G, C.length)) + x.substring(G + C.length); + D = !0; + break; + } + } + } + D || (w += (w ? " " : "") + l[F]); + } + u[v].highlight = w; + } + } + return a; +} +function Ra(a, c) { + const b = [], e = B(); + for (let d = 0, f, g; d < a.length; d++) { + f = a[d]; + g = f.result; + for (let k = 0, h, l, n; k < g.length; k++) { + if (l = g[k], "object" !== typeof l && (l = {id:l}), h = l.id, n = e[h]) { + n.push(f.field); + } else { + if (b.length === c) { + return b; + } + l.field = e[h] = [f.field]; + b.push(l); + } + } + } + return b; +} +function Qa(a, c, b, e, d) { + let f = this.tag.get(a); + if (!f) { + return console.warn("Tag '" + a + "' was not found"), []; + } + if ((a = (f = f && f.get(c)) && f.length - e) && 0 < a) { + if (a > b || e) { + f = f.slice(e, e + b); + } + d && (f = V.call(this, f)); + return f; + } +} +function V(a) { + if (!this || !this.store) { + return a; + } + const c = Array(a.length); + for (let b = 0, e; b < a.length; b++) { + e = a[b], c[b] = {id:e, doc:this.store.get(e)}; + } + return c; +} +;function U(a) { + if (!this || this.constructor !== U) { + return new U(a); + } + const c = a.document || a.doc || a; + let b, e; + this.D = []; + this.field = []; + this.J = []; + this.key = (b = c.key || c.id) && Ta(b, this.J) || "id"; + (e = a.keystore || 0) && (this.keystore = e); + this.fastupdate = !!a.fastupdate; + this.reg = !this.fastupdate || a.worker || a.db ? e ? new T(e) : new Set() : e ? new S(e) : new Map(); + this.C = (b = c.store || null) && b && !0 !== b && []; + this.store = b && (e ? new S(e) : new Map()); + this.cache = (b = a.cache || null) && new X(b); + a.cache = !1; + this.worker = a.worker; + this.priority = a.priority || 4; + this.index = Ua.call(this, a, c); + this.tag = null; + if (b = c.tag) { + if ("string" === typeof b && (b = [b]), b.length) { + this.tag = new Map(); + this.F = []; + this.R = []; + for (let d = 0, f, g; d < b.length; d++) { + f = b[d]; + g = f.field || f; + if (!g) { + throw Error("The tag field from the document descriptor is undefined."); + } + f.custom ? this.F[d] = f.custom : (this.F[d] = Ta(g, this.J), f.filter && ("string" === typeof this.F[d] && (this.F[d] = new String(this.F[d])), this.F[d].I = f.filter)); + this.R[d] = g; + this.tag.set(g, new Map()); + } + } + } + if (this.worker) { + this.fastupdate = !1; + a = []; + for (const d of this.index.values()) { + d.then && a.push(d); + } + if (a.length) { + const d = this; + return Promise.all(a).then(function(f) { + let g = 0; + for (const k of d.index.entries()) { + const h = k[0]; + k[1].then && d.index.set(h, f[g++]); + } + return d; + }); + } + } else { + a.db && (this.fastupdate = !1, this.mount(a.db)); + } +} +t = U.prototype; +t.mount = function(a) { + if (this.worker) { + throw Error("You can't use Worker-Indexes on a persistent model. That would be useless, since each of the persistent model acts like Worker-Index by default (Master/Slave)."); + } + let c = this.field; + if (this.tag) { + for (let d = 0, f; d < this.R.length; d++) { + f = this.R[d]; + var b = void 0; + this.index.set(f, b = new N({}, this.reg)); + c === this.field && (c = c.slice(0)); + c.push(f); + b.tag = this.tag.get(f); + } + } + b = []; + const e = {db:a.db, type:a.type, fastupdate:a.fastupdate}; + for (let d = 0, f, g; d < c.length; d++) { + e.field = g = c[d]; + f = this.index.get(g); + const k = new a.constructor(a.id, e); + k.id = a.id; + b[d] = k.mount(f); + f.document = !0; + d ? f.bypass = !0 : f.store = this.store; + } + this.db = !0; + return Promise.all(b); +}; +t.commit = async function(a, c) { + const b = []; + for (const e of this.index.values()) { + b.push(e.commit(a, c)); + } + await Promise.all(b); + this.reg.clear(); +}; +t.destroy = function() { + const a = []; + for (const c of this.index.values()) { + a.push(c.destroy()); + } + return Promise.all(a); +}; +function Ua(a, c) { + const b = new Map(); + let e = c.index || c.field || c; + E(e) && (e = [e]); + for (let d = 0, f, g; d < e.length; d++) { + f = e[d]; + E(f) || (g = f, f = f.field); + g = I(g) ? Object.assign({}, a, g) : a; + if (this.worker) { + const k = new P(g); + b.set(f, k); + } + this.worker || b.set(f, new N(g, this.reg)); + g.custom ? this.D[d] = g.custom : (this.D[d] = Ta(f, this.J), g.filter && ("string" === typeof this.D[d] && (this.D[d] = new String(this.D[d])), this.D[d].I = g.filter)); + this.field[d] = f; + } + if (this.C) { + a = c.store; + E(a) && (a = [a]); + for (let d = 0, f, g; d < a.length; d++) { + f = a[d], g = f.field || f, f.custom ? (this.C[d] = f.custom, f.custom.V = g) : (this.C[d] = Ta(g, this.J), f.filter && ("string" === typeof this.C[d] && (this.C[d] = new String(this.C[d])), this.C[d].I = f.filter)); + } + } + return b; +} +function Ta(a, c) { + const b = a.split(":"); + let e = 0; + for (let d = 0; d < b.length; d++) { + a = b[d], "]" === a[a.length - 1] && (a = a.substring(0, a.length - 2)) && (c[e] = !0), a && (b[e++] = a); + } + e < b.length && (b.length = e); + return 1 < e ? b : b[0]; +} +t.append = function(a, c) { + return this.add(a, c, !0); +}; +t.update = function(a, c) { + return this.remove(a).add(a, c); +}; +t.remove = function(a) { + I(a) && (a = ba(a, this.key)); + for (var c of this.index.values()) { + c.remove(a, !0); + } + if (this.reg.has(a)) { + if (this.tag && !this.fastupdate) { + for (let b of this.tag.values()) { + for (let e of b) { + c = e[0]; + const d = e[1], f = d.indexOf(a); + -1 < f && (1 < d.length ? d.splice(f, 1) : b.delete(c)); + } + } + } + this.store && this.store.delete(a); + this.reg.delete(a); + } + this.cache && this.cache.remove(a); + return this; +}; +t.clear = function() { + const a = []; + for (const c of this.index.values()) { + const b = c.clear(); + b.then && a.push(b); + } + if (this.tag) { + for (const c of this.tag.values()) { + c.clear(); + } + } + this.store && this.store.clear(); + this.cache && this.cache.clear(); + return a.length ? Promise.all(a) : this; +}; +t.contain = function(a) { + return this.db ? this.index.get(this.field[0]).db.has(a) : this.reg.has(a); +}; +t.cleanup = function() { + for (const a of this.index.values()) { + a.cleanup(); + } + return this; +}; +t.get = function(a) { + return this.db ? this.index.get(this.field[0]).db.enrich(a).then(function(c) { + return c[0] && c[0].doc; + }) : this.store.get(a); +}; +t.set = function(a, c) { + this.store.set(a, c); + return this; +}; +t.searchCache = Va; +t.export = function(a, c, b = 0, e = 0) { + if (b < this.field.length) { + const g = this.field[b]; + if ((c = this.index.get(g).export(a, g, b, e = 1)) && c.then) { + const k = this; + return c.then(function() { + return k.export(a, g, b + 1); + }); + } + return this.export(a, g, b + 1); + } + let d, f; + switch(e) { + case 0: + d = "reg"; + f = wa(this.reg); + c = null; + break; + case 1: + d = "tag"; + f = this.tag && ua(this.tag, this.reg.size); + c = null; + break; + case 2: + d = "doc"; + f = this.store && sa(this.store); + c = null; + break; + default: + return; + } + return ya.call(this, a, c, d, f, b, e); +}; +t.import = function(a, c) { + var b = a.split("."); + "json" === b[b.length - 1] && b.pop(); + const e = 2 < b.length ? b[0] : ""; + b = 2 < b.length ? b[2] : b[1]; + if (this.worker && e) { + return this.index.get(e).import(a); + } + if (c) { + "string" === typeof c && (c = JSON.parse(c)); + if (e) { + return this.index.get(e).import(b, c); + } + switch(b) { + case "reg": + this.fastupdate = !1; + this.reg = xa(c, this.reg); + for (let d = 0, f; d < this.field.length; d++) { + f = this.index.get(this.field[d]), f.fastupdate = !1, f.reg = this.reg; + } + if (this.worker) { + c = []; + for (const d of this.index.values()) { + c.push(d.import(a)); + } + return Promise.all(c); + } + break; + case "tag": + this.tag = va(c, this.tag); + break; + case "doc": + this.store = ta(c, this.store); + } + } +}; +la(U.prototype); +function Va(a, c, b) { + a = ("object" === typeof a ? "" + a.query : a).toLowerCase(); + this.cache || (this.cache = new X()); + let e = this.cache.get(a); + if (!e) { + e = this.search(a, c, b); + if (e.then) { + const d = this; + e.then(function(f) { + d.cache.set(a, f); + return f; + }); + } + this.cache.set(a, e); + } + return e; +} +function X(a) { + this.limit = a && !0 !== a ? a : 1000; + this.cache = new Map(); + this.h = ""; +} +X.prototype.set = function(a, c) { + this.cache.set(this.h = a, c); + this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value); +}; +X.prototype.get = function(a) { + const c = this.cache.get(a); + c && this.h !== a && (this.cache.delete(a), this.cache.set(this.h = a, c)); + return c; +}; +X.prototype.remove = function(a) { + for (const c of this.cache) { + const b = c[0]; + c[1].includes(a) && this.cache.delete(b); + } +}; +X.prototype.clear = function() { + this.cache.clear(); + this.h = ""; +}; +const Wa = {normalize:!1, numeric:!1, split:/\s+/}; +const Xa = {normalize:!0}; +const Ya = {normalize:!0, dedupe:!0}; +const Za = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); +const $a = new Map([["ae", "a"], ["oe", "o"], ["sh", "s"], ["kh", "k"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), ab = [/([^aeo])h(.)/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2", /(.)\1+/g, "$1"]; +const bb = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; +var cb = {X:Wa, W:Xa, Y:Ya, LatinBalance:{normalize:!0, dedupe:!0, mapper:Za}, LatinAdvanced:{normalize:!0, dedupe:!0, mapper:Za, matcher:$a, replacer:ab}, LatinExtra:{normalize:!0, dedupe:!0, mapper:Za, replacer:ab.concat([/(?!^)[aeo]/g, ""]), matcher:$a}, LatinSoundex:{normalize:!0, dedupe:!1, include:{letter:!0}, finalize:function(a) { + for (let b = 0; b < a.length; b++) { + var c = a[b]; + let e = c.charAt(0), d = bb[e]; + for (let f = 1, g; f < c.length && (g = c.charAt(f), "h" === g || "w" === g || !(g = bb[g]) || g === d || (e += g, d = g, 4 !== e.length)); f++) { + } + a[b] = e; + } +}}, LatinExact:Wa, LatinDefault:Xa, LatinSimple:Ya}; +const db = {memory:{resolution:1}, performance:{resolution:3, fastupdate:!0, context:{depth:1, resolution:1}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:3}}}; +N.prototype.add = function(a, c, b, e) { + if (c && (a || 0 === a)) { + if (!e && !b && this.reg.has(a)) { + return this.update(a, c); + } + c = this.encoder.encode(c); + if (e = c.length) { + const l = B(), n = B(), m = this.depth, q = this.resolution; + for (let p = 0; p < e; p++) { + let r = c[this.rtl ? e - 1 - p : p]; + var d = r.length; + if (d && (m || !n[r])) { + var f = this.score ? this.score(c, r, p, null, 0) : eb(q, e, p), g = ""; + switch(this.tokenize) { + case "full": + if (2 < d) { + for (let u = 0, v; u < d; u++) { + for (f = d; f > u; f--) { + g = r.substring(u, f); + v = this.rtl ? d - 1 - u : u; + var k = this.score ? this.score(c, r, p, g, v) : eb(q, e, p, d, v); + fb(this, n, g, k, a, b); + } + } + break; + } + case "bidirectional": + case "reverse": + if (1 < d) { + for (k = d - 1; 0 < k; k--) { + g = r[this.rtl ? d - 1 - k : k] + g; + var h = this.score ? this.score(c, r, p, g, k) : eb(q, e, p, d, k); + fb(this, n, g, h, a, b); + } + g = ""; + } + case "forward": + if (1 < d) { + for (k = 0; k < d; k++) { + g += r[this.rtl ? d - 1 - k : k], fb(this, n, g, f, a, b); + } + break; + } + default: + if (fb(this, n, r, f, a, b), m && 1 < e && p < e - 1) { + for (d = B(), g = this.U, f = r, k = Math.min(m + 1, this.rtl ? p + 1 : e - p), d[f] = 1, h = 1; h < k; h++) { + if ((r = c[this.rtl ? e - 1 - p - h : p + h]) && !d[r]) { + d[r] = 1; + const u = this.score ? this.score(c, f, p, r, h - 1) : eb(g + (e / 2 > g ? 0 : 1), e, p, k - 1, h - 1), v = this.bidirectional && r > f; + fb(this, l, v ? f : r, u, a, b, v ? r : f); + } + } + } + } + } + } + this.fastupdate || this.reg.add(a); + } else { + c = ""; + } + } + this.db && (c || this.commit_task.push({del:a}), this.T && gb(this)); + return this; +}; +function fb(a, c, b, e, d, f, g) { + let k = g ? a.ctx : a.map, h; + if (!c[b] || g && !(h = c[b])[g]) { + if (g ? (c = h || (c[b] = B()), c[g] = 1, (h = k.get(g)) ? k = h : k.set(g, k = new Map())) : c[b] = 1, (h = k.get(b)) ? k = h : k.set(b, k = h = []), k = k[e] || (k[e] = []), !f || !k.includes(d)) { + if (k.length === 2 ** 31 - 1) { + c = new R(k); + if (a.fastupdate) { + for (let l of a.reg.values()) { + l.includes(k) && (l[l.indexOf(k)] = c); + } + } + h[e] = k = c; + } + k.push(d); + a.fastupdate && ((e = a.reg.get(d)) ? e.push(k) : a.reg.set(d, [k])); + } + } +} +function eb(a, c, b, e, d) { + return b && 1 < a ? c + (e || 0) <= a ? b + (d || 0) : (a - 1) / (c + (e || 0)) * (b + (d || 0)) + 1 | 0 : 0; +} +;N.prototype.search = function(a, c, b) { + b || (!c && I(a) ? (b = a, a = "") : I(c) && (b = c, c = 0)); + let e = [], d, f, g, k = 0, h, l, n, m, q; + b ? (a = b.query || a, c = b.limit || c, k = b.offset || 0, f = b.context, g = b.suggest, q = (h = !1 !== b.resolve) && b.enrich, n = b.boost, m = b.resolution, l = this.db && b.tag) : h = this.resolve; + let p = this.encoder.encode(a); + d = p.length; + c = c || (h ? 100 : 0); + if (1 === d) { + return hb.call(this, p[0], "", c, k, h, q, l); + } + f = this.depth && !1 !== f; + if (2 === d && f && !g) { + return hb.call(this, p[0], p[1], c, k, h, q, l); + } + let r = B(), u = 0, v; + 1 < d && f && (v = p[0], u = 1); + m || 0 === m || (m = v ? this.U : this.resolution); + if (this.db) { + if (this.db.search && (a = this.db.search(this, p, c, k, g, h, q, l), !1 !== a)) { + return a; + } + const w = this; + return async function() { + for (let y, F; u < d; u++) { + if ((F = p[u]) && !r[F]) { + r[F] = 1; + y = await ib(w, F, v, 0, 0, !1, !1); + if (y = jb(y, e, g, m)) { + e = y; + break; + } + v && (g && y && e.length || (v = F)); + } + g && v && u === d - 1 && !e.length && (m = w.resolution, v = "", u = -1, r = B()); + } + return kb(e, m, c, k, g, n, h); + }(); + } + for (let w, y; u < d; u++) { + if ((y = p[u]) && !r[y]) { + r[y] = 1; + w = ib(this, y, v, 0, 0, !1, !1); + if (w = jb(w, e, g, m)) { + e = w; + break; + } + v && (g && w && e.length || (v = y)); + } + g && v && u === d - 1 && !e.length && (m = this.resolution, v = "", u = -1, r = B()); + } + return kb(e, m, c, k, g, n, h); +}; +function kb(a, c, b, e, d, f, g) { + let k = a.length, h = a; + if (1 < k) { + h = Fa(a, c, b, e, d, f, g); + } else if (1 === k) { + return g ? Ia.call(null, a[0], b, e) : new W(a[0]); + } + return g ? h : new W(h); +} +function hb(a, c, b, e, d, f, g) { + a = ib(this, a, c, b, e, d, f, g); + return this.db ? a.then(function(k) { + return d ? k || [] : new W(k); + }) : a && a.length ? d ? Ia.call(this, a, b, e) : new W(a) : d ? [] : new W(); +} +function jb(a, c, b, e) { + let d = []; + if (a && a.length) { + if (a.length <= e) { + c.push(a); + return; + } + for (let f = 0, g; f < e; f++) { + if (g = a[f]) { + d[f] = g; + } + } + if (d.length) { + c.push(d); + return; + } + } + if (!b) { + return d; + } +} +function ib(a, c, b, e, d, f, g, k) { + let h; + b && (h = a.bidirectional && c > b) && (h = b, b = c, c = h); + if (a.db) { + return a.db.get(c, b, e, d, f, g, k); + } + a = b ? (a = a.ctx.get(b)) && a.get(c) : a.map.get(c); + return a; +} +;N.prototype.remove = function(a, c) { + const b = this.reg.size && (this.fastupdate ? this.reg.get(a) : this.reg.has(a)); + if (b) { + if (this.fastupdate) { + for (let e = 0, d; e < b.length; e++) { + if (d = b[e]) { + if (2 > d.length) { + d.pop(); + } else { + const f = d.indexOf(a); + f === b.length - 1 ? d.pop() : d.splice(f, 1); + } + } + } + } else { + lb(this.map, a), this.depth && lb(this.ctx, a); + } + c || this.reg.delete(a); + } + this.db && (this.commit_task.push({del:a}), this.T && gb(this)); + this.cache && this.cache.remove(a); + return this; +}; +function lb(a, c) { + let b = 0; + if (a.constructor === Array) { + for (let e = 0, d, f; e < a.length; e++) { + if ((d = a[e]) && d.length) { + if (f = d.indexOf(c), 0 <= f) { + 1 < d.length ? (d.splice(f, 1), b++) : delete a[e]; + break; + } else { + b++; + } + } + } + } else { + for (let e of a.entries()) { + const d = e[0], f = lb(e[1], c); + f ? b += f : a.delete(d); + } + } + return b; +} +;function N(a, c) { + if (!this || this.constructor !== N) { + return new N(a); + } + if (a) { + var b = E(a) ? a : a.preset; + b && (db[b] || console.warn("Preset not found: " + b), a = Object.assign({}, db[b], a)); + } else { + a = {}; + } + b = a.context; + const e = !0 === b ? {depth:1} : b || {}, d = E(a.encoder) ? cb[a.encoder] : a.encode || a.encoder || Xa; + this.encoder = d.encode ? d : "object" === typeof d ? new ja(d) : {encode:d}; + this.resolution = a.resolution || 9; + this.tokenize = b = (b = a.tokenize) && "default" !== b && "exact" !== b && b || "strict"; + this.depth = "strict" === b && e.depth || 0; + this.bidirectional = !1 !== e.bidirectional; + this.fastupdate = !!a.fastupdate; + this.score = a.score || null; + e && "strict" !== this.tokenize && console.warn('Context-Search could not applied, because it is just supported when using the tokenizer "strict".'); + (b = a.keystore || 0) && (this.keystore = b); + this.map = b ? new S(b) : new Map(); + this.ctx = b ? new S(b) : new Map(); + this.reg = c || (this.fastupdate ? b ? new S(b) : new Map() : b ? new T(b) : new Set()); + this.U = e.resolution || 3; + this.rtl = d.rtl || a.rtl || !1; + this.cache = (b = a.cache || null) && new X(b); + this.resolve = !1 !== a.resolve; + if (b = a.db) { + this.db = this.mount(b); + } + this.T = !1 !== a.commit; + this.commit_task = []; + this.commit_timer = null; + this.priority = a.priority || 4; +} +t = N.prototype; +t.mount = function(a) { + this.commit_timer && (clearTimeout(this.commit_timer), this.commit_timer = null); + return a.mount(this); +}; +t.commit = function(a, c) { + this.commit_timer && (clearTimeout(this.commit_timer), this.commit_timer = null); + return this.db.commit(this, a, c); +}; +t.destroy = function() { + this.commit_timer && (clearTimeout(this.commit_timer), this.commit_timer = null); + return this.db.destroy(); +}; +function gb(a) { + a.commit_timer || (a.commit_timer = setTimeout(function() { + a.commit_timer = null; + a.db.commit(a, void 0, void 0); + }, 1)); +} +t.clear = function() { + this.map.clear(); + this.ctx.clear(); + this.reg.clear(); + this.cache && this.cache.clear(); + this.db && (this.commit_timer && clearTimeout(this.commit_timer), this.commit_timer = null, this.commit_task = [{clear:!0}]); + return this; +}; +t.append = function(a, c) { + return this.add(a, c, !0); +}; +t.contain = function(a) { + return this.db ? this.db.has(a) : this.reg.has(a); +}; +t.update = function(a, c) { + const b = this, e = this.remove(a); + return e && e.then ? e.then(() => b.add(a, c)) : this.add(a, c); +}; +function mb(a) { + let c = 0; + if (a.constructor === Array) { + for (let b = 0, e; b < a.length; b++) { + (e = a[b]) && (c += e.length); + } + } else { + for (const b of a.entries()) { + const e = b[0], d = mb(b[1]); + d ? c += d : a.delete(e); + } + } + return c; +} +t.cleanup = function() { + if (!this.fastupdate) { + return console.info('Cleanup the index isn\'t required when not using "fastupdate".'), this; + } + mb(this.map); + this.depth && mb(this.ctx); + return this; +}; +t.searchCache = Va; +t.export = function(a, c, b = 0, e = 0) { + let d, f; + switch(e) { + case 0: + d = "reg"; + f = wa(this.reg); + break; + case 1: + d = "cfg"; + f = null; + break; + case 2: + d = "map"; + f = sa(this.map, this.reg.size); + break; + case 3: + d = "ctx"; + f = ua(this.ctx, this.reg.size); + break; + default: + return; + } + return ya.call(this, a, c, d, f, b, e); +}; +t.import = function(a, c) { + if (c) { + switch("string" === typeof c && (c = JSON.parse(c)), a = a.split("."), "json" === a[a.length - 1] && a.pop(), 3 === a.length && a.shift(), a = 1 < a.length ? a[1] : a[0], a) { + case "reg": + this.fastupdate = !1; + this.reg = xa(c, this.reg); + break; + case "map": + this.map = ta(c, this.map); + break; + case "ctx": + this.ctx = va(c, this.ctx); + } + } +}; +t.serialize = function(a = !0) { + let c = "", b = "", e = ""; + if (this.reg.size) { + let f; + for (var d of this.reg.keys()) { + f || (f = typeof d), c += (c ? "," : "") + ("string" === f ? '"' + d + '"' : d); + } + c = "index.reg=new Set([" + c + "]);"; + b = za(this.map, f); + b = "index.map=new Map([" + b + "]);"; + for (const g of this.ctx.entries()) { + d = g[0]; + let k = za(g[1], f); + k = "new Map([" + k + "])"; + k = '["' + d + '",' + k + "]"; + e += (e ? "," : "") + k; + } + e = "index.ctx=new Map([" + e + "]);"; + } + return a ? "function inject(index){" + c + b + e + "}" : c + b + e; +}; +la(N.prototype); +const nb = "undefined" !== typeof window && (window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB), ob = ["map", "ctx", "tag", "reg", "cfg"], Y = B(); +function pb(a, c = {}) { + if (!this) { + return new pb(a, c); + } + "object" === typeof a && (c = a, a = a.name); + a || console.info("Default storage space was used, because a name was not passed."); + this.id = "flexsearch" + (a ? ":" + a.toLowerCase().replace(/[^a-z0-9_\-]/g, "") : ""); + this.field = c.field ? c.field.toLowerCase().replace(/[^a-z0-9_\-]/g, "") : ""; + this.type = c.type; + this.fastupdate = this.support_tag_search = !1; + this.db = null; + this.h = {}; +} +t = pb.prototype; +t.mount = function(a) { + if (!a.encoder) { + return a.mount(this); + } + a.db = this; + return this.open(); +}; +t.open = function() { + if (this.db) { + return this.db; + } + let a = this; + navigator.storage && navigator.storage.persist(); + Y[a.id] || (Y[a.id] = []); + Y[a.id].push(a.field); + const c = nb.open(a.id, 1); + c.onupgradeneeded = function() { + const b = a.db = this.result; + for (let e = 0, d; e < ob.length; e++) { + d = ob[e]; + for (let f = 0, g; f < Y[a.id].length; f++) { + g = Y[a.id][f], b.objectStoreNames.contains(d + ("reg" !== d ? g ? ":" + g : "" : "")) || b.createObjectStore(d + ("reg" !== d ? g ? ":" + g : "" : "")); + } + } + }; + return a.db = Z(c, function(b) { + a.db = b; + a.db.onversionchange = function() { + a.close(); + }; + }); +}; +t.close = function() { + this.db && this.db.close(); + this.db = null; +}; +t.destroy = function() { + const a = nb.deleteDatabase(this.id); + return Z(a); +}; +t.clear = function() { + const a = []; + for (let b = 0, e; b < ob.length; b++) { + e = ob[b]; + for (let d = 0, f; d < Y[this.id].length; d++) { + f = Y[this.id][d], a.push(e + ("reg" !== e ? f ? ":" + f : "" : "")); + } + } + const c = this.db.transaction(a, "readwrite"); + for (let b = 0; b < a.length; b++) { + c.objectStore(a[b]).clear(); + } + return Z(c); +}; +t.get = function(a, c, b = 0, e = 0, d = !0, f = !1) { + a = this.db.transaction((c ? "ctx" : "map") + (this.field ? ":" + this.field : ""), "readonly").objectStore((c ? "ctx" : "map") + (this.field ? ":" + this.field : "")).get(c ? c + ":" + a : a); + const g = this; + return Z(a).then(function(k) { + let h = []; + if (!k || !k.length) { + return h; + } + if (d) { + if (!b && !e && 1 === k.length) { + return k[0]; + } + for (let l = 0, n; l < k.length; l++) { + if ((n = k[l]) && n.length) { + if (e >= n.length) { + e -= n.length; + continue; + } + const m = b ? e + Math.min(n.length - e, b) : n.length; + for (let q = e; q < m; q++) { + h.push(n[q]); + } + e = 0; + if (h.length === b) { + break; + } + } + } + return f ? g.enrich(h) : h; + } + return k; + }); +}; +t.tag = function(a, c = 0, b = 0, e = !1) { + a = this.db.transaction("tag" + (this.field ? ":" + this.field : ""), "readonly").objectStore("tag" + (this.field ? ":" + this.field : "")).get(a); + const d = this; + return Z(a).then(function(f) { + if (!f || !f.length || b >= f.length) { + return []; + } + if (!c && !b) { + return f; + } + f = f.slice(b, b + c); + return e ? d.enrich(f) : f; + }); +}; +t.enrich = function(a) { + "object" !== typeof a && (a = [a]); + const c = this.db.transaction("reg", "readonly").objectStore("reg"), b = []; + for (let e = 0; e < a.length; e++) { + b[e] = Z(c.get(a[e])); + } + return Promise.all(b).then(function(e) { + for (let d = 0; d < e.length; d++) { + e[d] = {id:a[d], doc:e[d] ? JSON.parse(e[d]) : null}; + } + return e; + }); +}; +t.has = function(a) { + a = this.db.transaction("reg", "readonly").objectStore("reg").getKey(a); + return Z(a).then(function(c) { + return !!c; + }); +}; +t.search = null; +t.info = function() { +}; +t.transaction = function(a, c, b) { + a += "reg" !== a ? this.field ? ":" + this.field : "" : ""; + let e = this.h[a + ":" + c]; + if (e) { + return b.call(this, e); + } + let d = this.db.transaction(a, c); + this.h[a + ":" + c] = e = d.objectStore(a); + const f = b.call(this, e); + this.h[a + ":" + c] = null; + return Z(d).finally(function() { + d = e = null; + return f; + }); +}; +t.commit = async function(a, c, b) { + if (c) { + await this.clear(), a.commit_task = []; + } else { + let e = a.commit_task; + a.commit_task = []; + for (let d = 0, f; d < e.length; d++) { + if (f = e[d], f.clear) { + await this.clear(); + c = !0; + break; + } else { + e[d] = f.del; + } + } + c || (b || (e = e.concat(aa(a.reg))), e.length && await this.remove(e)); + } + a.reg.size && (await this.transaction("map", "readwrite", function(e) { + for (const d of a.map) { + const f = d[0], g = d[1]; + g.length && (c ? e.put(g, f) : e.get(f).onsuccess = function() { + let k = this.result; + var h; + if (k && k.length) { + const l = Math.max(k.length, g.length); + for (let n = 0, m, q; n < l; n++) { + if ((q = g[n]) && q.length) { + if ((m = k[n]) && m.length) { + for (h = 0; h < q.length; h++) { + m.push(q[h]); + } + } else { + k[n] = q; + } + h = 1; + } + } + } else { + k = g, h = 1; + } + h && e.put(k, f); + }); + } + }), await this.transaction("ctx", "readwrite", function(e) { + for (const d of a.ctx) { + const f = d[0], g = d[1]; + for (const k of g) { + const h = k[0], l = k[1]; + l.length && (c ? e.put(l, f + ":" + h) : e.get(f + ":" + h).onsuccess = function() { + let n = this.result; + var m; + if (n && n.length) { + const q = Math.max(n.length, l.length); + for (let p = 0, r, u; p < q; p++) { + if ((u = l[p]) && u.length) { + if ((r = n[p]) && r.length) { + for (m = 0; m < u.length; m++) { + r.push(u[m]); + } + } else { + n[p] = u; + } + m = 1; + } + } + } else { + n = l, m = 1; + } + m && e.put(n, f + ":" + h); + }); + } + } + }), a.store ? await this.transaction("reg", "readwrite", function(e) { + for (const d of a.store) { + const f = d[0], g = d[1]; + e.put("object" === typeof g ? JSON.stringify(g) : 1, f); + } + }) : a.bypass || await this.transaction("reg", "readwrite", function(e) { + for (const d of a.reg.keys()) { + e.put(1, d); + } + }), a.tag && await this.transaction("tag", "readwrite", function(e) { + for (const d of a.tag) { + const f = d[0], g = d[1]; + g.length && (e.get(f).onsuccess = function() { + let k = this.result; + k = k && k.length ? k.concat(g) : g; + e.put(k, f); + }); + } + }), a.map.clear(), a.ctx.clear(), a.tag && a.tag.clear(), a.store && a.store.clear(), a.document || a.reg.clear()); +}; +function qb(a, c, b) { + const e = a.value; + let d, f = 0; + for (let g = 0, k; g < e.length; g++) { + if (k = b ? e : e[g]) { + for (let h = 0, l, n; h < c.length; h++) { + if (n = c[h], l = k.indexOf(n), 0 <= l) { + if (d = 1, 1 < k.length) { + k.splice(l, 1); + } else { + e[g] = []; + break; + } + } + } + f += k.length; + } + if (b) { + break; + } + } + f ? d && a.update(e) : a.delete(); + a.continue(); +} +t.remove = function(a) { + "object" !== typeof a && (a = [a]); + return Promise.all([this.transaction("map", "readwrite", function(c) { + c.openCursor().onsuccess = function() { + const b = this.result; + b && qb(b, a); + }; + }), this.transaction("ctx", "readwrite", function(c) { + c.openCursor().onsuccess = function() { + const b = this.result; + b && qb(b, a); + }; + }), this.transaction("tag", "readwrite", function(c) { + c.openCursor().onsuccess = function() { + const b = this.result; + b && qb(b, a, !0); + }; + }), this.transaction("reg", "readwrite", function(c) { + for (let b = 0; b < a.length; b++) { + c.delete(a[b]); + } + })]); +}; +function Z(a, c) { + return new Promise((b, e) => { + a.onsuccess = a.oncomplete = function() { + c && c(this.result); + c = null; + b(this.result); + }; + a.onerror = a.onblocked = e; + a = null; + }); +} +;const rb = {Index:N, Charset:cb, Encoder:ja, Document:U, Worker:P, Resolver:W, IndexedDB:pb, Language:{}}, sb = "undefined" !== typeof self ? self : "undefined" !== typeof global ? global : self; +let ub; +(ub = sb.define) && ub.amd ? ub([], function() { + return rb; +}) : "object" === typeof sb.exports ? sb.exports = rb : sb.FlexSearch = rb; +}(this||self)); diff --git a/public/js/main-head.js b/public/js/main-head.js new file mode 100644 index 0000000..54f4cac --- /dev/null +++ b/public/js/main-head.js @@ -0,0 +1,22 @@ +// The section must not be in the banner.js (body) file because it can create a quick flash. + +if (localStorage.getItem('banner-closed')) { + document.documentElement.style.setProperty("--hextra-banner-height", "0px"); + document.documentElement.classList.add("hextra-banner-hidden"); +} + +; +// The section must not be in the theme.js (body) file because it can create a quick flash (switch between light and dark). + +function setTheme(theme) { + document.documentElement.classList.remove("light", "dark"); + + if (theme !== "light" && theme !== "dark") { + theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; + } + + document.documentElement.classList.add(theme); + document.documentElement.style.colorScheme = theme; +} + +setTheme("color-theme" in localStorage ? localStorage.getItem("color-theme") : 'light') diff --git a/public/js/main-head.min.2375b7265ea066ecb314226ce5ff17478453df5f350d34d830b3fc96fba21672.js b/public/js/main-head.min.2375b7265ea066ecb314226ce5ff17478453df5f350d34d830b3fc96fba21672.js new file mode 100644 index 0000000..199e004 --- /dev/null +++ b/public/js/main-head.min.2375b7265ea066ecb314226ce5ff17478453df5f350d34d830b3fc96fba21672.js @@ -0,0 +1 @@ +localStorage.getItem("banner-closed")&&(document.documentElement.style.setProperty("--hextra-banner-height","0px"),document.documentElement.classList.add("hextra-banner-hidden"));function setTheme(e){document.documentElement.classList.remove("light","dark"),e!=="light"&&e!=="dark"&&(e=window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"),document.documentElement.classList.add(e),document.documentElement.style.colorScheme=e}setTheme("color-theme"in localStorage?localStorage.getItem("color-theme"):"light") \ No newline at end of file diff --git a/public/js/main.js b/public/js/main.js new file mode 100644 index 0000000..9bd5712 --- /dev/null +++ b/public/js/main.js @@ -0,0 +1,1082 @@ +// Back to top button + +document.addEventListener("DOMContentLoaded", function () { + const backToTop = document.querySelector("#backToTop"); + if (backToTop) { + backToTop.addEventListener("click", scrollUp); + document.addEventListener("scroll", (e) => { + if (window.scrollY > 300) { + backToTop.classList.remove("hx:opacity-0"); + backToTop.removeAttribute("tabindex"); + } else { + backToTop.classList.add("hx:opacity-0"); + backToTop.setAttribute("tabindex", "-1"); + } + }); + } +}); + +function scrollUp() { + const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + window.scroll({ + top: 0, + left: 0, + behavior: prefersReducedMotion ? 'auto' : 'smooth', + }); +} + +; +// +; +// Copy button for code blocks + +document.addEventListener('DOMContentLoaded', function () { + const getCopyIcon = () => { + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.innerHTML = ` + + `; + svg.setAttribute('fill', 'none'); + svg.setAttribute('viewBox', '0 0 24 24'); + svg.setAttribute('stroke', 'currentColor'); + svg.setAttribute('stroke-width', '2'); + return svg; + } + + const getSuccessIcon = () => { + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.innerHTML = ` + + `; + svg.setAttribute('fill', 'none'); + svg.setAttribute('viewBox', '0 0 24 24'); + svg.setAttribute('stroke', 'currentColor'); + svg.setAttribute('stroke-width', '2'); + return svg; + } + + // Make scrollable code blocks focusable for keyboard users. + const updateScrollableCodeBlocks = () => { + document.querySelectorAll('.hextra-code-block pre, .highlight pre').forEach(function (pre) { + if (pre.scrollWidth > pre.clientWidth) { + pre.setAttribute('tabindex', '0'); + } else { + pre.removeAttribute('tabindex'); + } + }); + }; + + updateScrollableCodeBlocks(); + + let resizeRaf; + window.addEventListener('resize', () => { + if (resizeRaf) { + cancelAnimationFrame(resizeRaf); + } + resizeRaf = requestAnimationFrame(updateScrollableCodeBlocks); + }); + + document.querySelectorAll('.hextra-code-copy-btn').forEach(function (button) { + // Add copy and success icons + button.querySelector('.hextra-copy-icon')?.appendChild(getCopyIcon()); + button.querySelector('.hextra-success-icon')?.appendChild(getSuccessIcon()); + + // Add click event listener for copy button + button.addEventListener('click', function (e) { + e.preventDefault(); + // Get the code target + const target = button.parentElement.previousElementSibling; + let codeElement; + if (target.tagName === 'CODE') { + codeElement = target; + } else { + // Select the last code element in case line numbers are present + const codeElements = target.querySelectorAll('code'); + codeElement = codeElements[codeElements.length - 1]; + } + if (codeElement) { + let code = codeElement.innerText; + // Replace double newlines with single newlines in the innerText + // as each line inside has trailing newline '\n' + if ("lang" in codeElement.dataset) { + code = code.replace(/\n\n/g, '\n'); + } + navigator.clipboard.writeText(code).then(function () { + button.classList.add('copied'); + var originalLabel = button.getAttribute('aria-label'); + var copiedLabel = button.dataset.copiedLabel || 'Copied!'; + button.setAttribute('aria-label', copiedLabel); + setTimeout(function () { + button.classList.remove('copied'); + button.setAttribute('aria-label', originalLabel); + }, 1000); + }).catch(function (err) { + console.error('Failed to copy text: ', err); + }); + } else { + console.error('Target element not found'); + } + }); + }); +}); + +; +// +(function () { + const faviconEl = document.getElementById("favicon-svg"); + const faviconDarkExists = "false" === "true"; + + if (faviconEl && faviconDarkExists) { + const lightFavicon = '/favicon.svg'; + const darkFavicon = '/favicon-dark.svg'; + + const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)"); + + function updateFavicon(e) { + faviconEl.href = e.matches ? darkFavicon : lightFavicon; + } + + // Set favicon on load + updateFavicon(darkModeQuery); + + // Listen for system preference changes + darkModeQuery.addEventListener("change", updateFavicon); + } +})(); + +; +// Script for filetree shortcode collapsing/expanding folders used in the theme +// ====================================================================== +document.addEventListener("DOMContentLoaded", function () { + const folders = document.querySelectorAll(".hextra-filetree-folder"); + folders.forEach(function (folder) { + folder.addEventListener("click", function () { + Array.from(folder.children).forEach(function (el) { + el.dataset.state = el.dataset.state === "open" ? "closed" : "open"; + }); + var newState = folder.nextElementSibling.dataset.state === "open" ? "closed" : "open"; + folder.nextElementSibling.dataset.state = newState; + folder.setAttribute('aria-expanded', newState === 'open' ? 'true' : 'false'); + }); + }); +}); + +; +(function () { + const languageSwitchers = document.querySelectorAll('.hextra-language-switcher'); + const closeSwitcher = (switcher, focusSwitcher = false) => { + switcher.dataset.state = 'closed'; + switcher.setAttribute('aria-expanded', 'false'); + const optionsElement = switcher.nextElementSibling; + optionsElement.classList.add('hx:hidden'); + if (focusSwitcher) { + switcher.focus(); + } + }; + + const openSwitcher = (switcher, focusTarget = "none") => { + switcher.dataset.state = 'open'; + switcher.setAttribute('aria-expanded', 'true'); + const optionsElement = switcher.nextElementSibling; + if (optionsElement.classList.contains('hx:hidden')) { + toggleMenu(switcher); + } else { + resizeMenu(switcher); + } + + if (focusTarget !== "none") { + const items = Array.from(optionsElement.querySelectorAll('[role="menuitem"]')); + if (items.length > 0) { + const target = focusTarget === "last" ? items[items.length - 1] : items[0]; + target.focus(); + } + } + }; + + languageSwitchers.forEach((switcher) => { + switcher.addEventListener('click', (e) => { + e.preventDefault(); + + if (switcher.dataset.state === 'open') { + closeSwitcher(switcher); + } else { + openSwitcher(switcher); + } + }); + + switcher.addEventListener('keydown', (e) => { + if (e.key === 'ArrowDown') { + e.preventDefault(); + openSwitcher(switcher, 'first'); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + openSwitcher(switcher, 'last'); + } + }); + }); + + document.querySelectorAll('.hextra-language-options[role=menu]').forEach((menu) => { + menu.addEventListener('keydown', (e) => { + const items = Array.from(menu.querySelectorAll('[role="menuitem"]')); + if (items.length === 0) return; + + const currentIndex = items.indexOf(document.activeElement); + let newIndex; + + switch (e.key) { + case 'ArrowDown': + e.preventDefault(); + newIndex = (currentIndex + 1) % items.length; + items[newIndex].focus(); + break; + case 'ArrowUp': + e.preventDefault(); + newIndex = (currentIndex - 1 + items.length) % items.length; + items[newIndex].focus(); + break; + case 'Home': + e.preventDefault(); + items[0].focus(); + break; + case 'End': + e.preventDefault(); + items[items.length - 1].focus(); + break; + case 'Escape': { + e.preventDefault(); + const switcher = menu.previousElementSibling; + if (switcher) { + closeSwitcher(switcher, true); + } + break; + } + } + }); + }); + + window.addEventListener("resize", () => languageSwitchers.forEach(resizeMenu)); + + // Dismiss language switcher when clicking outside. + document.addEventListener('click', (e) => { + if (!e.target.closest('.hextra-language-switcher') && !e.target.closest('.hextra-language-options')) { + languageSwitchers.forEach((switcher) => { + closeSwitcher(switcher); + }); + } + }); +})(); + +; +// Hamburger menu for mobile navigation + +document.addEventListener('DOMContentLoaded', function () { + const menu = document.querySelector('.hextra-hamburger-menu'); + const sidebarContainer = document.querySelector('.hextra-sidebar-container'); + const mobileQuery = window.matchMedia('(max-width: 767px)'); + + function isMenuOpen() { + return menu.querySelector('svg').classList.contains('open'); + } + + // On mobile, the sidebar is off-screen so hide it from assistive tech + function syncAriaHidden() { + if (mobileQuery.matches) { + sidebarContainer.setAttribute('aria-hidden', isMenuOpen() ? 'false' : 'true'); + } else { + sidebarContainer.removeAttribute('aria-hidden'); + } + } + + // Set initial state + syncAriaHidden(); + mobileQuery.addEventListener('change', syncAriaHidden); + + function toggleMenu(options = {}) { + const { focusOnOpen = true } = options; + + // Toggle the hamburger menu + menu.querySelector('svg').classList.toggle('open'); + + // When the menu is open, we want to show the navigation sidebar + sidebarContainer.classList.toggle('hx:max-md:[transform:translate3d(0,-100%,0)]'); + sidebarContainer.classList.toggle('hx:max-md:[transform:translate3d(0,0,0)]'); + + // When the menu is open, we want to prevent the body from scrolling + document.body.classList.toggle('hx:overflow-hidden'); + document.body.classList.toggle('hx:md:overflow-auto'); + + // Sync aria-expanded and aria-hidden + const isOpen = isMenuOpen(); + menu.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); + syncAriaHidden(); + + // Move focus into sidebar when opening, restore when closing + if (isOpen) { + if (focusOnOpen) { + const firstFocusable = sidebarContainer.querySelector('a, button, input, [tabindex="0"]'); + if (firstFocusable) firstFocusable.focus(); + } + } else { + menu.focus(); + } + } + + menu.addEventListener('click', (e) => { + e.preventDefault(); + // Pointer-initiated clicks on mobile should not force focus into the search input, + // which opens the software keyboard immediately. + toggleMenu({ focusOnOpen: e.detail === 0 }); + }); + + // Close menu on Escape key (mobile only) + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && mobileQuery.matches && isMenuOpen()) { + toggleMenu(); + } + }); + + // Select all anchor tags in the sidebar container + const sidebarLinks = sidebarContainer.querySelectorAll('a'); + + // Add click event listener to each anchor tag + sidebarLinks.forEach(link => { + link.addEventListener('click', (e) => { + // Check if the href attribute contains a hash symbol (links to a heading) + if (link.getAttribute('href') && link.getAttribute('href').startsWith('#')) { + // Only dismiss overlay on mobile view + if (window.innerWidth < 768) { + toggleMenu(); + } + } + }); + }); +}); + +; +(function () { + const hiddenClass = "hx:hidden"; + const dropdownToggles = document.querySelectorAll(".hextra-nav-menu-toggle"); + const closeDropdown = (toggle, focusToggle = false) => { + toggle.dataset.state = "closed"; + toggle.setAttribute("aria-expanded", "false"); + const menuItemsElement = toggle.nextElementSibling; + menuItemsElement.classList.add(hiddenClass); + if (focusToggle) { + toggle.focus(); + } + }; + + const openDropdown = (toggle, focusTarget = "none") => { + // Close all other dropdowns first. + dropdownToggles.forEach((otherToggle) => { + if (otherToggle !== toggle) { + closeDropdown(otherToggle); + } + }); + + toggle.dataset.state = "open"; + toggle.setAttribute("aria-expanded", "true"); + const menuItemsElement = toggle.nextElementSibling; + + // Position dropdown centered with toggle. + menuItemsElement.style.position = "absolute"; + menuItemsElement.style.top = "100%"; + menuItemsElement.style.left = "50%"; + menuItemsElement.style.transform = "translateX(-50%)"; + menuItemsElement.style.zIndex = "1000"; + menuItemsElement.classList.remove(hiddenClass); + + if (focusTarget !== "none") { + const items = Array.from(menuItemsElement.querySelectorAll('[role="menuitem"]')); + if (items.length > 0) { + const target = focusTarget === "last" ? items[items.length - 1] : items[0]; + target.focus(); + } + } + }; + + dropdownToggles.forEach((toggle) => { + toggle.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + + // Toggle current dropdown. + const isOpen = toggle.dataset.state === "open"; + if (isOpen) { + closeDropdown(toggle); + } else { + openDropdown(toggle); + } + }); + + toggle.addEventListener("keydown", (e) => { + if (e.key === "ArrowDown") { + e.preventDefault(); + openDropdown(toggle, "first"); + } else if (e.key === "ArrowUp") { + e.preventDefault(); + openDropdown(toggle, "last"); + } + }); + }); + + document.querySelectorAll(".hextra-nav-menu-items[role=menu]").forEach((menu) => { + menu.addEventListener("keydown", (e) => { + const items = Array.from(menu.querySelectorAll('[role="menuitem"]')); + if (items.length === 0) return; + + const currentIndex = items.indexOf(document.activeElement); + let newIndex; + + switch (e.key) { + case "ArrowDown": + e.preventDefault(); + newIndex = (currentIndex + 1) % items.length; + items[newIndex].focus(); + break; + case "ArrowUp": + e.preventDefault(); + newIndex = (currentIndex - 1 + items.length) % items.length; + items[newIndex].focus(); + break; + case "Home": + e.preventDefault(); + items[0].focus(); + break; + case "End": + e.preventDefault(); + items[items.length - 1].focus(); + break; + case "Escape": { + e.preventDefault(); + const toggle = menu.previousElementSibling; + if (toggle) { + closeDropdown(toggle, true); + } + break; + } + } + }); + }); + + // Dismiss dropdown when clicking outside. + document.addEventListener("click", (e) => { + if (!e.target.closest(".hextra-nav-menu-toggle") && !e.target.closest(".hextra-nav-menu-items")) { + dropdownToggles.forEach((toggle) => { + closeDropdown(toggle); + }); + } + }); + + // Close dropdowns on escape key. + document.addEventListener("keydown", (e) => { + if (e.key === "Escape") { + dropdownToggles.forEach((toggle) => { + if (toggle.dataset.state === "open") { + closeDropdown(toggle, true); + } + }); + } + }); +})(); + +; +document.addEventListener('DOMContentLoaded', () => { + // Pre-fetch markdown content for all copy buttons to avoid Safari NotAllowedError + // Safari requires clipboard writes to happen synchronously within user gesture + const copyButtons = document.querySelectorAll('.hextra-page-context-menu-copy'); + const contentCache = new Map(); + + // Pre-fetch content for each button on page load + copyButtons.forEach(button => { + const url = button.dataset.url; + if (url) { + fetch(url) + .then(response => { + if (response.ok) return response.text(); + throw new Error('Failed to fetch'); + }) + .then(markdown => contentCache.set(url, markdown)) + .catch(error => console.error('Failed to pre-fetch markdown:', error)); + } + }); + + // Initialize copy buttons with synchronous clipboard access + copyButtons.forEach(button => { + button.addEventListener('click', () => { + const url = button.dataset.url; + const markdown = contentCache.get(url); + + if (markdown) { + // Synchronous clipboard write initiation - works in Safari + navigator.clipboard.writeText(markdown) + .then(() => { + button.classList.add('copied'); + setTimeout(() => button.classList.remove('copied'), 1000); + }) + .catch(error => console.error('Failed to copy markdown:', error)); + } else { + // Fallback: fetch and copy (may fail in Safari if content not pre-fetched) + fetch(url) + .then(response => { + if (!response.ok) throw new Error('Failed to fetch'); + return response.text(); + }) + .then(text => { + contentCache.set(url, text); + return navigator.clipboard.writeText(text); + }) + .then(() => { + button.classList.add('copied'); + setTimeout(() => button.classList.remove('copied'), 1000); + }) + .catch(error => console.error('Failed to copy markdown:', error)); + } + }); + }); + + // Initialize dropdown toggles + const dropdownToggles = document.querySelectorAll('.hextra-page-context-menu-toggle'); + dropdownToggles.forEach(toggle => { + const container = toggle.closest('.hextra-page-context-menu'); + const menu = container.querySelector('.hextra-page-context-menu-dropdown'); + const chevron = toggle.querySelector('[data-chevron]'); + + toggle.addEventListener('click', (e) => { + e.stopPropagation(); + const isOpen = toggle.dataset.state === 'open'; + + // Close all other dropdowns first + dropdownToggles.forEach(t => { + if (t !== toggle) { + t.dataset.state = 'closed'; + t.setAttribute('aria-expanded', 'false'); + const otherContainer = t.closest('.hextra-page-context-menu'); + const otherMenu = otherContainer.querySelector('.hextra-page-context-menu-dropdown'); + const otherChevron = t.querySelector('[data-chevron]'); + otherMenu.classList.add('hx:hidden'); + if (otherChevron) { + otherChevron.style.transform = ''; + } + } + }); + + // Toggle current + toggle.dataset.state = isOpen ? 'closed' : 'open'; + toggle.setAttribute('aria-expanded', isOpen ? 'false' : 'true'); + menu.classList.toggle('hx:hidden', isOpen); + + // Rotate chevron icon + if (chevron) { + chevron.style.transform = isOpen ? '' : 'rotate(180deg)'; + } + }); + }); + + // Close dropdown when clicking outside + document.addEventListener('click', (e) => { + // Check if click is outside any dropdown container + const isOutside = !e.target.closest('.hextra-page-context-menu'); + if (isOutside) { + dropdownToggles.forEach(toggle => { + toggle.dataset.state = 'closed'; + toggle.setAttribute('aria-expanded', 'false'); + const container = toggle.closest('.hextra-page-context-menu'); + const menu = container.querySelector('.hextra-page-context-menu-dropdown'); + const chevron = toggle.querySelector('[data-chevron]'); + menu.classList.add('hx:hidden'); + if (chevron) { + chevron.style.transform = ''; + } + }); + } + }); + + // Close dropdown on Escape key and return focus to toggle + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + dropdownToggles.forEach(toggle => { + if (toggle.dataset.state === 'open') { + const container = toggle.closest('.hextra-page-context-menu'); + closeDropdown(container); + toggle.focus(); + } + }); + } + }); + + // Helper to close dropdown + const closeDropdown = (container) => { + if (!container) return; + + const toggle = container.querySelector('.hextra-page-context-menu-toggle'); + const menu = container.querySelector('.hextra-page-context-menu-dropdown'); + + if (!toggle || !menu) return; + + const chevron = toggle.querySelector('[data-chevron]'); + toggle.dataset.state = 'closed'; + toggle.setAttribute('aria-expanded', 'false'); + menu.classList.add('hx:hidden'); + if (chevron) { + chevron.style.transform = ''; + } + }; + + // Handle dropdown menu copy action + document.querySelectorAll('.hextra-page-context-menu-dropdown button[data-action="copy"]').forEach(btn => { + btn.addEventListener('click', async (e) => { + e.stopPropagation(); + const container = btn.closest('.hextra-page-context-menu'); + if (!container) return; + + const copyBtn = container.querySelector('.hextra-page-context-menu-copy'); + if (!copyBtn) return; + + closeDropdown(container); + copyBtn.click(); + }); + }); + + // Handle dropdown menu view action + document.querySelectorAll('.hextra-page-context-menu-dropdown button[data-action="view"]').forEach(btn => { + btn.addEventListener('click', (e) => { + e.stopPropagation(); + const container = btn.closest('.hextra-page-context-menu'); + if (!container) return; + + const url = btn.dataset.url; + if (!url) return; + + closeDropdown(container); + window.open(url, '_blank', 'noopener,noreferrer'); + }); + }); +}); + +; +document.addEventListener("DOMContentLoaded", function () { + scrollToActiveItem(); + enableCollapsibles(); +}); + +function enableCollapsibles() { + const buttons = document.querySelectorAll(".hextra-sidebar-collapsible-button"); + buttons.forEach(function (button) { + button.addEventListener("click", function (e) { + e.preventDefault(); + const list = button.closest('li'); + if (list) { + list.classList.toggle("open"); + button.setAttribute('aria-expanded', list.classList.contains('open') ? 'true' : 'false'); + } + }); + }); +} + +function scrollToActiveItem() { + const sidebarScrollbar = document.querySelector("aside.hextra-sidebar-container > .hextra-scrollbar"); + const activeItems = document.querySelectorAll(".hextra-sidebar-active-item"); + const visibleActiveItem = Array.from(activeItems).find(function (activeItem) { + return activeItem.getBoundingClientRect().height > 0; + }); + + if (!visibleActiveItem) { + return; + } + + const yOffset = visibleActiveItem.clientHeight; + const yDistance = visibleActiveItem.getBoundingClientRect().top - sidebarScrollbar.getBoundingClientRect().top; + sidebarScrollbar.scrollTo({ + behavior: "instant", + top: yDistance - yOffset + }); +} + +; +function computeMenuTranslation(switcher, optionsElement) { + // Calculate the position of a language options element. + const switcherRect = switcher.getBoundingClientRect(); + + // Must be called before optionsElement.clientWidth. + optionsElement.style.minWidth = `${Math.max(switcherRect.width, 50)}px`; + + const isOnTop = switcher.dataset.location === 'top'; + const isOnBottom = switcher.dataset.location === 'bottom'; + const isOnBottomRight = switcher.dataset.location === 'bottom-right'; + const isRTL = document.documentElement.dir === 'rtl' + + // Stuck on the left side of the switcher. + let x = switcherRect.left; + + if (isOnTop && !isRTL || isOnBottom && isRTL || isOnBottomRight && !isRTL) { + // Stuck on the right side of the switcher. + x = switcherRect.right - optionsElement.clientWidth; + } + + // Stuck on the top of the switcher. + let y = switcherRect.top - window.innerHeight - 10; + + if (isOnTop) { + // Stuck on the bottom of the switcher. + y = switcherRect.top - window.innerHeight + optionsElement.clientHeight + switcher.clientHeight + 4; + } + + return { x: x, y: y }; +} + +function toggleMenu(switcher) { + const optionsElement = switcher.nextElementSibling; + + optionsElement.classList.toggle('hx:hidden'); + + // Calculate the position of a language options element. + const translate = computeMenuTranslation(switcher, optionsElement); + + optionsElement.style.transform = `translate3d(${translate.x}px, ${translate.y}px, 0)`; +} + +function resizeMenu(switcher) { + const optionsElement = switcher.nextElementSibling; + + if (optionsElement.classList.contains('hx:hidden')) return; + + // Calculate the position of a language options element. + const translate = computeMenuTranslation(switcher, optionsElement); + + optionsElement.style.transform = `translate3d(${translate.x}px, ${translate.y}px, 0)`; +} + +; +(function () { + function updateGroup(container, index) { + const tabs = Array.from(container.querySelectorAll('.hextra-tabs-toggle')); + tabs.forEach((tab, i) => { + tab.dataset.state = i === index ? 'selected' : ''; + if (i === index) { + tab.setAttribute('aria-selected', 'true'); + tab.tabIndex = 0; + } else { + tab.setAttribute('aria-selected', 'false'); + tab.tabIndex = -1; + } + }); + const panelsContainer = container.parentElement.nextElementSibling; + if (!panelsContainer) return; + Array.from(panelsContainer.children).forEach((panel, i) => { + panel.dataset.state = i === index ? 'selected' : ''; + panel.setAttribute('aria-hidden', i === index ? 'false' : 'true'); + if (i === index) { + panel.tabIndex = 0; + } else { + panel.removeAttribute('tabindex'); + } + }); + } + + const syncGroups = document.querySelectorAll('[data-tab-group]'); + + syncGroups.forEach((group) => { + const key = encodeURIComponent(group.dataset.tabGroup); + const saved = localStorage.getItem('hextra-tab-' + key); + if (saved !== null) { + updateGroup(group, parseInt(saved, 10)); + } + }); + + document.querySelectorAll('.hextra-tabs-toggle').forEach((button) => { + button.addEventListener('click', function (e) { + const targetButton = e.currentTarget; + const container = targetButton.parentElement; + const index = Array.from(container.querySelectorAll('.hextra-tabs-toggle')).indexOf( + targetButton + ); + + if (container.dataset.tabGroup) { + // Sync behavior: update all tab groups with the same name + const tabGroupValue = container.dataset.tabGroup; + const key = encodeURIComponent(tabGroupValue); + document + .querySelectorAll('[data-tab-group="' + tabGroupValue + '"]') + .forEach((grp) => updateGroup(grp, index)); + localStorage.setItem('hextra-tab-' + key, index.toString()); + } else { + // Non-sync behavior: update only this specific tab group + updateGroup(container, index); + } + }); + + // Keyboard navigation for tabs + button.addEventListener('keydown', function (e) { + const container = button.parentElement; + const tabs = Array.from(container.querySelectorAll('.hextra-tabs-toggle')); + const currentIndex = tabs.indexOf(button); + let newIndex; + + switch (e.key) { + case 'ArrowRight': + case 'ArrowDown': + e.preventDefault(); + newIndex = (currentIndex + 1) % tabs.length; + break; + case 'ArrowLeft': + case 'ArrowUp': + e.preventDefault(); + newIndex = (currentIndex - 1 + tabs.length) % tabs.length; + break; + case 'Home': + e.preventDefault(); + newIndex = 0; + break; + case 'End': + e.preventDefault(); + newIndex = tabs.length - 1; + break; + default: + return; + } + + if (container.dataset.tabGroup) { + const tabGroupValue = container.dataset.tabGroup; + const key = encodeURIComponent(tabGroupValue); + document + .querySelectorAll('[data-tab-group="' + tabGroupValue + '"]') + .forEach((grp) => updateGroup(grp, newIndex)); + localStorage.setItem('hextra-tab-' + key, newIndex.toString()); + } else { + updateGroup(container, newIndex); + } + tabs[newIndex].focus(); + }); + }); +})(); + +; +document.addEventListener("DOMContentLoaded", function () { + // Hugo task lists render bare checkboxes; provide an accessible name. + document.querySelectorAll("main#content li > input[type='checkbox']").forEach(function (checkbox) { + if (checkbox.hasAttribute("aria-label") || checkbox.hasAttribute("aria-labelledby")) { + return; + } + var listItem = checkbox.closest("li"); + if (!listItem) return; + + var labelText = listItem.textContent.replace(/\s+/g, " ").trim(); + if (labelText) { + checkbox.setAttribute("aria-label", labelText); + } + }); +}); + +; +// Light / Dark theme toggle +(function () { + const defaultTheme = 'light' + const themes = ["light", "dark"]; + + const themeToggleButtons = document.querySelectorAll(".hextra-theme-toggle"); + const themeToggleOptions = document.querySelectorAll(".hextra-theme-toggle-options button[role=menuitemradio]"); + + function applyTheme(theme) { + theme = themes.includes(theme) ? theme : "system"; + + themeToggleButtons.forEach((btn) => btn.parentElement.dataset.theme = theme ); + themeToggleOptions.forEach((option) => { + option.setAttribute('aria-checked', option.dataset.item === theme ? 'true' : 'false'); + }); + + localStorage.setItem("color-theme", theme); + } + + function switchTheme(theme) { + setTheme(theme); + applyTheme(theme); + } + + const colorTheme = "color-theme" in localStorage ? localStorage.getItem("color-theme") : defaultTheme; + switchTheme(colorTheme); + + // Add click event handler to the menu items. + themeToggleOptions.forEach((option) => { + option.addEventListener("click", function (e) { + e.preventDefault(); + + switchTheme(option.dataset.item); + }) + }) + + // Add click event handler to the buttons + themeToggleButtons.forEach((toggler) => { + toggler.addEventListener("click", function (e) { + e.preventDefault(); + + toggler.dataset.state = toggler.dataset.state === 'open' ? 'closed' : 'open'; + toggleMenu(toggler); + const isOpen = toggler.dataset.state === 'open'; + toggler.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); + + // Focus first menuitem when opening + if (isOpen) { + const firstItem = toggler.nextElementSibling.querySelector('button[role=menuitemradio]'); + if (firstItem) firstItem.focus(); + } + }); + }); + + window.addEventListener("resize", () => themeToggleButtons.forEach(resizeMenu)) + + // Dismiss the menu when clicking outside + document.addEventListener('click', (e) => { + if (e.target.closest('.hextra-theme-toggle') === null) { + themeToggleButtons.forEach((toggler) => { + toggler.dataset.state = 'closed'; + toggler.setAttribute('aria-expanded', 'false'); + toggler.nextElementSibling.classList.add('hx:hidden'); + }); + } + }); + + // Keyboard navigation for the theme menu + document.querySelectorAll('.hextra-theme-toggle-options[role=menu]').forEach(function (menu) { + menu.addEventListener('keydown', function (e) { + const items = Array.from(menu.querySelectorAll('button[role=menuitemradio]')); + const currentIndex = items.indexOf(document.activeElement); + let newIndex; + + switch (e.key) { + case 'ArrowDown': + e.preventDefault(); + newIndex = (currentIndex + 1) % items.length; + items[newIndex].focus(); + break; + case 'ArrowUp': + e.preventDefault(); + newIndex = (currentIndex - 1 + items.length) % items.length; + items[newIndex].focus(); + break; + case 'Home': + e.preventDefault(); + items[0].focus(); + break; + case 'End': + e.preventDefault(); + items[items.length - 1].focus(); + break; + case 'Escape': + e.preventDefault(); + var toggler = menu.previousElementSibling; + toggler.dataset.state = 'closed'; + toggler.setAttribute('aria-expanded', 'false'); + menu.classList.add('hx:hidden'); + toggler.focus(); + break; + } + }); + }); + + // Listen for system theme changes + window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => { + if (localStorage.getItem("color-theme") === "system") { + setTheme("system"); + } + }); +})(); + +; +/** + * TOC Scroll - Highlights active TOC links based on visible headings + * + * Uses Intersection Observer to track heading visibility and applies + * 'hextra-toc-active' class to corresponding TOC links. Selects the + * topmost heading when multiple are visible. + * + * Requires: .hextra-toc element, matching heading IDs, toc.css styles + */ +document.addEventListener("DOMContentLoaded", function () { + const toc = document.querySelector(".hextra-toc"); + if (!toc) return; + + const tocLinks = toc.querySelectorAll('a[href^="#"]'); + if (tocLinks.length === 0) return; + + const headingIds = Array.from(tocLinks).map((link) => link.getAttribute("href").substring(1)); + + const headings = headingIds.map((id) => document.getElementById(decodeURIComponent(id))).filter(Boolean); + if (headings.length === 0) return; + + let currentActiveLink = null; + let isHashNavigation = false; + + // Create intersection observer + const observer = new IntersectionObserver( + (entries) => { + // Skip observer updates during hash navigation + if (isHashNavigation) return; + + const visibleHeadings = entries.filter((entry) => entry.isIntersecting).map((entry) => entry.target); + + if (visibleHeadings.length === 0) return; + + // Find the heading closest to the top of the viewport + const topMostHeading = visibleHeadings.reduce((closest, heading) => { + const headingTop = heading.getBoundingClientRect().top; + const closestTop = closest.getBoundingClientRect().top; + return Math.abs(headingTop) < Math.abs(closestTop) ? heading : closest; + }); + + // Encode the id and make it lowercase to match the TOC link + const targetId = encodeURIComponent(topMostHeading.id).toLowerCase(); + const targetLink = toc.querySelector(`a[href="#${targetId}"]`); + + if (targetLink && targetLink !== currentActiveLink) { + // Remove active class from previous link + if (currentActiveLink) { + currentActiveLink.classList.remove("hextra-toc-active"); + currentActiveLink.removeAttribute("aria-current"); + } + + // Add active class to current link + targetLink.classList.add("hextra-toc-active"); + targetLink.setAttribute("aria-current", "location"); + currentActiveLink = targetLink; + } + }, + { + rootMargin: "-20px 0px -60% 0px", // Adjust sensitivity + threshold: [0, 0.1, 0.5, 1], + } + ); + + // Observe all headings + headings.forEach((heading) => observer.observe(heading)); + + // Handle direct navigation to page with hash + function handleHashNavigation() { + const hash = window.location.hash; // already url encoded + if (hash) { + const targetLink = toc.querySelector(`a[href="${hash}"]`); + if (targetLink) { + // Disable observer temporarily during hash navigation + isHashNavigation = true; + + if (currentActiveLink) { + currentActiveLink.classList.remove("hextra-toc-active"); + currentActiveLink.removeAttribute("aria-current"); + } + targetLink.classList.add("hextra-toc-active"); + targetLink.setAttribute("aria-current", "location"); + currentActiveLink = targetLink; + + // Re-enable observer after scroll settles + setTimeout(() => { isHashNavigation = false; }, 500); + return; + } + } + } + + // Handle hash changes navigation + window.addEventListener("hashchange", handleHashNavigation); + + // Handle initial load + setTimeout(handleHashNavigation, 100); +}); diff --git a/public/js/main.min.87785fd96557e898756188ba90bc3e2b22faddb10ae258b4d77bdb1c45dfef9e.js b/public/js/main.min.87785fd96557e898756188ba90bc3e2b22faddb10ae258b4d77bdb1c45dfef9e.js new file mode 100644 index 0000000..e9d5853 --- /dev/null +++ b/public/js/main.min.87785fd96557e898756188ba90bc3e2b22faddb10ae258b4d77bdb1c45dfef9e.js @@ -0,0 +1,6 @@ +document.addEventListener("DOMContentLoaded",function(){const e=document.querySelector("#backToTop");e&&(e.addEventListener("click",scrollUp),document.addEventListener("scroll",t=>{window.scrollY>300?(e.classList.remove("hx:opacity-0"),e.removeAttribute("tabindex")):(e.classList.add("hx:opacity-0"),e.setAttribute("tabindex","-1"))}))});function scrollUp(){const e=window.matchMedia("(prefers-reduced-motion: reduce)").matches;window.scroll({top:0,left:0,behavior:e?"auto":"smooth"})}document.addEventListener("DOMContentLoaded",function(){const n=()=>{const e=document.createElementNS("http://www.w3.org/2000/svg","svg");return e.innerHTML=` + + `,e.setAttribute("fill","none"),e.setAttribute("viewBox","0 0 24 24"),e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","2"),e},s=()=>{const e=document.createElementNS("http://www.w3.org/2000/svg","svg");return e.innerHTML=` + + `,e.setAttribute("fill","none"),e.setAttribute("viewBox","0 0 24 24"),e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","2"),e},t=()=>{document.querySelectorAll(".hextra-code-block pre, .highlight pre").forEach(function(e){e.scrollWidth>e.clientWidth?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")})};t();let e;window.addEventListener("resize",()=>{e&&cancelAnimationFrame(e),e=requestAnimationFrame(t)}),document.querySelectorAll(".hextra-code-copy-btn").forEach(function(e){e.querySelector(".hextra-copy-icon")?.appendChild(n()),e.querySelector(".hextra-success-icon")?.appendChild(s()),e.addEventListener("click",function(t){t.preventDefault();const s=e.parentElement.previousElementSibling;let n;if(s.tagName==="CODE")n=s;else{const e=s.querySelectorAll("code");n=e[e.length-1]}if(n){let t=n.innerText;"lang"in n.dataset&&(t=t.replace(/\n\n/g,` +`)),navigator.clipboard.writeText(t).then(function(){e.classList.add("copied");var t=e.getAttribute("aria-label"),n=e.dataset.copiedLabel||"Copied!";e.setAttribute("aria-label",n),setTimeout(function(){e.classList.remove("copied"),e.setAttribute("aria-label",t)},1e3)}).catch(function(e){console.error("Failed to copy text: ",e)})}else console.error("Target element not found")})})}),function(){const e=document.getElementById("favicon-svg"),n="false"==="true";if(e&&n){const s="/favicon.svg",o="/favicon-dark.svg",n=window.matchMedia("(prefers-color-scheme: dark)");function t(t){e.href=t.matches?o:s}t(n),n.addEventListener("change",t)}}(),document.addEventListener("DOMContentLoaded",function(){const e=document.querySelectorAll(".hextra-filetree-folder");e.forEach(function(e){e.addEventListener("click",function(){Array.from(e.children).forEach(function(e){e.dataset.state=e.dataset.state==="open"?"closed":"open"});var t=e.nextElementSibling.dataset.state==="open"?"closed":"open";e.nextElementSibling.dataset.state=t,e.setAttribute("aria-expanded",t==="open"?"true":"false")})})}),function(){const e=document.querySelectorAll(".hextra-language-switcher"),t=(e,t=!1)=>{e.dataset.state="closed",e.setAttribute("aria-expanded","false");const n=e.nextElementSibling;n.classList.add("hx:hidden"),t&&e.focus()},n=(e,t="none")=>{e.dataset.state="open",e.setAttribute("aria-expanded","true");const n=e.nextElementSibling;if(n.classList.contains("hx:hidden")?toggleMenu(e):resizeMenu(e),t!=="none"){const e=Array.from(n.querySelectorAll('[role="menuitem"]'));if(e.length>0){const n=t==="last"?e[e.length-1]:e[0];n.focus()}}};e.forEach(e=>{e.addEventListener("click",s=>{s.preventDefault(),e.dataset.state==="open"?t(e):n(e)}),e.addEventListener("keydown",t=>{t.key==="ArrowDown"?(t.preventDefault(),n(e,"first")):t.key==="ArrowUp"&&(t.preventDefault(),n(e,"last"))})}),document.querySelectorAll(".hextra-language-options[role=menu]").forEach(e=>{e.addEventListener("keydown",n=>{const s=Array.from(e.querySelectorAll('[role="menuitem"]'));if(s.length===0)return;const i=s.indexOf(document.activeElement);let o;switch(n.key){case"ArrowDown":n.preventDefault(),o=(i+1)%s.length,s[o].focus();break;case"ArrowUp":n.preventDefault(),o=(i-1+s.length)%s.length,s[o].focus();break;case"Home":n.preventDefault(),s[0].focus();break;case"End":n.preventDefault(),s[s.length-1].focus();break;case"Escape":{n.preventDefault();const s=e.previousElementSibling;s&&t(s,!0);break}}})}),window.addEventListener("resize",()=>e.forEach(resizeMenu)),document.addEventListener("click",n=>{!n.target.closest(".hextra-language-switcher")&&!n.target.closest(".hextra-language-options")&&e.forEach(e=>{t(e)})})}(),document.addEventListener("DOMContentLoaded",function(){const t=document.querySelector(".hextra-hamburger-menu"),e=document.querySelector(".hextra-sidebar-container"),n=window.matchMedia("(max-width: 767px)");function s(){return t.querySelector("svg").classList.contains("open")}function o(){n.matches?e.setAttribute("aria-hidden",s()?"false":"true"):e.removeAttribute("aria-hidden")}o(),n.addEventListener("change",o);function i(n={}){const{focusOnOpen:a=!0}=n;t.querySelector("svg").classList.toggle("open"),e.classList.toggle("hx:max-md:[transform:translate3d(0,-100%,0)]"),e.classList.toggle("hx:max-md:[transform:translate3d(0,0,0)]"),document.body.classList.toggle("hx:overflow-hidden"),document.body.classList.toggle("hx:md:overflow-auto");const i=s();if(t.setAttribute("aria-expanded",i?"true":"false"),o(),i){if(a){const t=e.querySelector('a, button, input, [tabindex="0"]');t&&t.focus()}}else t.focus()}t.addEventListener("click",e=>{e.preventDefault(),i({focusOnOpen:e.detail===0})}),document.addEventListener("keydown",e=>{e.key==="Escape"&&n.matches&&s()&&i()});const a=e.querySelectorAll("a");a.forEach(e=>{e.addEventListener("click",t=>{e.getAttribute("href")&&e.getAttribute("href").startsWith("#")&&window.innerWidth<768&&i()})})}),function(){const s="hx:hidden",t=document.querySelectorAll(".hextra-nav-menu-toggle"),e=(e,t=!1)=>{e.dataset.state="closed",e.setAttribute("aria-expanded","false");const n=e.nextElementSibling;n.classList.add(s),t&&e.focus()},n=(n,o="none")=>{t.forEach(t=>{t!==n&&e(t)}),n.dataset.state="open",n.setAttribute("aria-expanded","true");const i=n.nextElementSibling;if(i.style.position="absolute",i.style.top="100%",i.style.left="50%",i.style.transform="translateX(-50%)",i.style.zIndex="1000",i.classList.remove(s),o!=="none"){const e=Array.from(i.querySelectorAll('[role="menuitem"]'));if(e.length>0){const t=o==="last"?e[e.length-1]:e[0];t.focus()}}};t.forEach(t=>{t.addEventListener("click",s=>{s.preventDefault(),s.stopPropagation();const o=t.dataset.state==="open";o?e(t):n(t)}),t.addEventListener("keydown",e=>{e.key==="ArrowDown"?(e.preventDefault(),n(t,"first")):e.key==="ArrowUp"&&(e.preventDefault(),n(t,"last"))})}),document.querySelectorAll(".hextra-nav-menu-items[role=menu]").forEach(t=>{t.addEventListener("keydown",n=>{const s=Array.from(t.querySelectorAll('[role="menuitem"]'));if(s.length===0)return;const i=s.indexOf(document.activeElement);let o;switch(n.key){case"ArrowDown":n.preventDefault(),o=(i+1)%s.length,s[o].focus();break;case"ArrowUp":n.preventDefault(),o=(i-1+s.length)%s.length,s[o].focus();break;case"Home":n.preventDefault(),s[0].focus();break;case"End":n.preventDefault(),s[s.length-1].focus();break;case"Escape":{n.preventDefault();const s=t.previousElementSibling;s&&e(s,!0);break}}})}),document.addEventListener("click",n=>{!n.target.closest(".hextra-nav-menu-toggle")&&!n.target.closest(".hextra-nav-menu-items")&&t.forEach(t=>{e(t)})}),document.addEventListener("keydown",n=>{n.key==="Escape"&&t.forEach(t=>{t.dataset.state==="open"&&e(t,!0)})})}(),document.addEventListener("DOMContentLoaded",()=>{const s=document.querySelectorAll(".hextra-page-context-menu-copy"),t=new Map;s.forEach(e=>{const n=e.dataset.url;n&&fetch(n).then(e=>{if(e.ok)return e.text();throw new Error("Failed to fetch")}).then(e=>t.set(n,e)).catch(e=>console.error("Failed to pre-fetch markdown:",e))}),s.forEach(e=>{e.addEventListener("click",()=>{const n=e.dataset.url,s=t.get(n);s?navigator.clipboard.writeText(s).then(()=>{e.classList.add("copied"),setTimeout(()=>e.classList.remove("copied"),1e3)}).catch(e=>console.error("Failed to copy markdown:",e)):fetch(n).then(e=>{if(!e.ok)throw new Error("Failed to fetch");return e.text()}).then(e=>(t.set(n,e),navigator.clipboard.writeText(e))).then(()=>{e.classList.add("copied"),setTimeout(()=>e.classList.remove("copied"),1e3)}).catch(e=>console.error("Failed to copy markdown:",e))})});const e=document.querySelectorAll(".hextra-page-context-menu-toggle");e.forEach(t=>{const s=t.closest(".hextra-page-context-menu"),o=s.querySelector(".hextra-page-context-menu-dropdown"),n=t.querySelector("[data-chevron]");t.addEventListener("click",s=>{s.stopPropagation();const i=t.dataset.state==="open";e.forEach(e=>{if(e!==t){e.dataset.state="closed",e.setAttribute("aria-expanded","false");const n=e.closest(".hextra-page-context-menu"),s=n.querySelector(".hextra-page-context-menu-dropdown"),t=e.querySelector("[data-chevron]");s.classList.add("hx:hidden"),t&&(t.style.transform="")}}),t.dataset.state=i?"closed":"open",t.setAttribute("aria-expanded",i?"false":"true"),o.classList.toggle("hx:hidden",i),n&&(n.style.transform=i?"":"rotate(180deg)")})}),document.addEventListener("click",t=>{const n=!t.target.closest(".hextra-page-context-menu");n&&e.forEach(e=>{e.dataset.state="closed",e.setAttribute("aria-expanded","false");const n=e.closest(".hextra-page-context-menu"),s=n.querySelector(".hextra-page-context-menu-dropdown"),t=e.querySelector("[data-chevron]");s.classList.add("hx:hidden"),t&&(t.style.transform="")})}),document.addEventListener("keydown",t=>{t.key==="Escape"&&e.forEach(e=>{if(e.dataset.state==="open"){const t=e.closest(".hextra-page-context-menu");n(t),e.focus()}})});const n=e=>{if(!e)return;const t=e.querySelector(".hextra-page-context-menu-toggle"),n=e.querySelector(".hextra-page-context-menu-dropdown");if(!t||!n)return;const s=t.querySelector("[data-chevron]");t.dataset.state="closed",t.setAttribute("aria-expanded","false"),n.classList.add("hx:hidden"),s&&(s.style.transform="")};document.querySelectorAll('.hextra-page-context-menu-dropdown button[data-action="copy"]').forEach(e=>{e.addEventListener("click",async t=>{t.stopPropagation();const s=e.closest(".hextra-page-context-menu");if(!s)return;const o=s.querySelector(".hextra-page-context-menu-copy");if(!o)return;n(s),o.click()})}),document.querySelectorAll('.hextra-page-context-menu-dropdown button[data-action="view"]').forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const s=e.closest(".hextra-page-context-menu");if(!s)return;const o=e.dataset.url;if(!o)return;n(s),window.open(o,"_blank","noopener,noreferrer")})})}),document.addEventListener("DOMContentLoaded",function(){scrollToActiveItem(),enableCollapsibles()});function enableCollapsibles(){const e=document.querySelectorAll(".hextra-sidebar-collapsible-button");e.forEach(function(e){e.addEventListener("click",function(t){t.preventDefault();const n=e.closest("li");n&&(n.classList.toggle("open"),e.setAttribute("aria-expanded",n.classList.contains("open")?"true":"false"))})})}function scrollToActiveItem(){const t=document.querySelector("aside.hextra-sidebar-container > .hextra-scrollbar"),n=document.querySelectorAll(".hextra-sidebar-active-item"),e=Array.from(n).find(function(e){return e.getBoundingClientRect().height>0});if(!e)return;const s=e.clientHeight,o=e.getBoundingClientRect().top-t.getBoundingClientRect().top;t.scrollTo({behavior:"instant",top:o-s})}function computeMenuTranslation(e,t){const n=e.getBoundingClientRect();t.style.minWidth=`${Math.max(n.width,50)}px`;const o=e.dataset.location==="top",r=e.dataset.location==="bottom",c=e.dataset.location==="bottom-right",s=document.documentElement.dir==="rtl";let i=n.left;(o&&!s||r&&s||c&&!s)&&(i=n.right-t.clientWidth);let a=n.top-window.innerHeight-10;return o&&(a=n.top-window.innerHeight+t.clientHeight+e.clientHeight+4),{x:i,y:a}}function toggleMenu(e){const t=e.nextElementSibling;t.classList.toggle("hx:hidden");const n=computeMenuTranslation(e,t);t.style.transform=`translate3d(${n.x}px, ${n.y}px, 0)`}function resizeMenu(e){const t=e.nextElementSibling;if(t.classList.contains("hx:hidden"))return;const n=computeMenuTranslation(e,t);t.style.transform=`translate3d(${n.x}px, ${n.y}px, 0)`}(function(){function e(e,t){const s=Array.from(e.querySelectorAll(".hextra-tabs-toggle"));s.forEach((e,n)=>{e.dataset.state=n===t?"selected":"",n===t?(e.setAttribute("aria-selected","true"),e.tabIndex=0):(e.setAttribute("aria-selected","false"),e.tabIndex=-1)});const n=e.parentElement.nextElementSibling;if(!n)return;Array.from(n.children).forEach((e,n)=>{e.dataset.state=n===t?"selected":"",e.setAttribute("aria-hidden",n===t?"false":"true"),n===t?e.tabIndex=0:e.removeAttribute("tabindex")})}const t=document.querySelectorAll("[data-tab-group]");t.forEach(t=>{const s=encodeURIComponent(t.dataset.tabGroup),n=localStorage.getItem("hextra-tab-"+s);n!==null&&e(t,parseInt(n,10))}),document.querySelectorAll(".hextra-tabs-toggle").forEach(t=>{t.addEventListener("click",function(t){const o=t.currentTarget,n=o.parentElement,s=Array.from(n.querySelectorAll(".hextra-tabs-toggle")).indexOf(o);if(n.dataset.tabGroup){const t=n.dataset.tabGroup,o=encodeURIComponent(t);document.querySelectorAll('[data-tab-group="'+t+'"]').forEach(t=>e(t,s)),localStorage.setItem("hextra-tab-"+o,s.toString())}else e(n,s)}),t.addEventListener("keydown",function(n){const i=t.parentElement,o=Array.from(i.querySelectorAll(".hextra-tabs-toggle")),a=o.indexOf(t);let s;switch(n.key){case"ArrowRight":case"ArrowDown":n.preventDefault(),s=(a+1)%o.length;break;case"ArrowLeft":case"ArrowUp":n.preventDefault(),s=(a-1+o.length)%o.length;break;case"Home":n.preventDefault(),s=0;break;case"End":n.preventDefault(),s=o.length-1;break;default:return}if(i.dataset.tabGroup){const t=i.dataset.tabGroup,n=encodeURIComponent(t);document.querySelectorAll('[data-tab-group="'+t+'"]').forEach(t=>e(t,s)),localStorage.setItem("hextra-tab-"+n,s.toString())}else e(i,s);o[s].focus()})})})(),document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll("main#content li > input[type='checkbox']").forEach(function(e){if(e.hasAttribute("aria-label")||e.hasAttribute("aria-labelledby"))return;var t,n=e.closest("li");if(!n)return;t=n.textContent.replace(/\s+/g," ").trim(),t&&e.setAttribute("aria-label",t)})}),function(){const s="light",o=["light","dark"],e=document.querySelectorAll(".hextra-theme-toggle"),t=document.querySelectorAll(".hextra-theme-toggle-options button[role=menuitemradio]");function i(n){n=o.includes(n)?n:"system",e.forEach(e=>e.parentElement.dataset.theme=n),t.forEach(e=>{e.setAttribute("aria-checked",e.dataset.item===n?"true":"false")}),localStorage.setItem("color-theme",n)}function n(e){setTheme(e),i(e)}const a="color-theme"in localStorage?localStorage.getItem("color-theme"):s;n(a),t.forEach(e=>{e.addEventListener("click",function(t){t.preventDefault(),n(e.dataset.item)})}),e.forEach(e=>{e.addEventListener("click",function(t){t.preventDefault(),e.dataset.state=e.dataset.state==="open"?"closed":"open",toggleMenu(e);const n=e.dataset.state==="open";if(e.setAttribute("aria-expanded",n?"true":"false"),n){const t=e.nextElementSibling.querySelector("button[role=menuitemradio]");t&&t.focus()}})}),window.addEventListener("resize",()=>e.forEach(resizeMenu)),document.addEventListener("click",t=>{t.target.closest(".hextra-theme-toggle")===null&&e.forEach(e=>{e.dataset.state="closed",e.setAttribute("aria-expanded","false"),e.nextElementSibling.classList.add("hx:hidden")})}),document.querySelectorAll(".hextra-theme-toggle-options[role=menu]").forEach(function(e){e.addEventListener("keydown",function(t){const n=Array.from(e.querySelectorAll("button[role=menuitemradio]")),i=n.indexOf(document.activeElement);let s;switch(t.key){case"ArrowDown":t.preventDefault(),s=(i+1)%n.length,n[s].focus();break;case"ArrowUp":t.preventDefault(),s=(i-1+n.length)%n.length,n[s].focus();break;case"Home":t.preventDefault(),n[0].focus();break;case"End":t.preventDefault(),n[n.length-1].focus();break;case"Escape":t.preventDefault();var o=e.previousElementSibling;o.dataset.state="closed",o.setAttribute("aria-expanded","false"),e.classList.add("hx:hidden"),o.focus();break}})}),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{localStorage.getItem("color-theme")==="system"&&setTheme("system")})}(),document.addEventListener("DOMContentLoaded",function(){const t=document.querySelector(".hextra-toc");if(!t)return;const s=t.querySelectorAll('a[href^="#"]');if(s.length===0)return;const a=Array.from(s).map(e=>e.getAttribute("href").substring(1)),o=a.map(e=>document.getElementById(decodeURIComponent(e))).filter(Boolean);if(o.length===0)return;let e=null,n=!1;const r=new IntersectionObserver(s=>{if(n)return;const i=s.filter(e=>e.isIntersecting).map(e=>e.target);if(i.length===0)return;const a=i.reduce((e,t)=>{const n=t.getBoundingClientRect().top,s=e.getBoundingClientRect().top;return(n<0?-n:n)<(s<0?-s:s)?t:e}),r=encodeURIComponent(a.id).toLowerCase(),o=t.querySelector(`a[href="#${r}"]`);o&&o!==e&&(e&&(e.classList.remove("hextra-toc-active"),e.removeAttribute("aria-current")),o.classList.add("hextra-toc-active"),o.setAttribute("aria-current","location"),e=o)},{rootMargin:"-20px 0px -60% 0px",threshold:[0,.1,.5,1]});o.forEach(e=>r.observe(e));function i(){const s=window.location.hash;if(s){const o=t.querySelector(`a[href="${s}"]`);if(o){n=!0,e&&(e.classList.remove("hextra-toc-active"),e.removeAttribute("aria-current")),o.classList.add("hextra-toc-active"),o.setAttribute("aria-current","location"),e=o,setTimeout(()=>{n=!1},500);return}}}window.addEventListener("hashchange",i),setTimeout(i,100)}) \ No newline at end of file diff --git a/public/lizenz/index.html b/public/lizenz/index.html new file mode 100644 index 0000000..8262db8 --- /dev/null +++ b/public/lizenz/index.html @@ -0,0 +1,45 @@ +Lizenz & Mitwirkende – RAPPORTZum Inhalt springen

    Lizenz & Mitwirkende

    Lizenz von RAPPORT, Zugehörigkeit zu OpenBureau und Attribution der verwendeten Open-Source-Software.

    RAPPORT +

    RAPPORT — Studio Management Software für Architekturbüros.

    Quellcode: git.kgva.ch/karim/RAPPORT +Autor: Karim Gabriele Varano

    Lizenz +

    Lizenziert unter GNU Affero General Public License v3.0 oder höher (AGPL-3.0-or-later).

    Dies bedeutet im Kern:

    • Freie Nutzung für jeden Zweck (privat, kommerziell, behördlich)
    • Freier Zugang zum Quellcode
    • Freie Modifikation und Weitergabe — unter derselben Lizenz
    • Netzwerk-Nutzung gilt als Weitergabe — wer eine modifizierte Version als Service anbietet, muss den Quellcode offenlegen

    Eigene Builds, eigene Forks, eigene Anpassungen — alles erlaubt, solange Modifikationen wieder unter AGPL-3.0 veröffentlicht werden.

    OpenBureau +

    RAPPORT ist Teil der OpenBureau-Initiative — einer Sammlung freier Software-Werkzeuge für den Architektur-Büro-Alltag.

    Die Idee: Bürowissen — Workflows, Konventionen, Vorlagen — sollte nicht in proprietären Tools eingesperrt sein. OpenBureau-Tools setzen die in der Branche etablierten Konventionen (SIA-Normen, QR-Rechnung, Plan-Verwaltung) sauber um und stehen unter freien Lizenzen zur Verfügung.

    ToolZweckStatus
    RAPPORTStudio-Management Desktop-App (Zeit, Rechnungen, Projekte)Pre-Release
    Rapport ServerSelfhost-Stack (Postgres + Auth + Realtime + Storage)Pre-Release
    DOSSIERRhino-8-Plugin für Plan- und DokumentationsausgabePre-Release

    Mehr unter gabrielevarano.ch.

    Verwendete Open-Source-Software +

    RAPPORT baut auf freier Software auf. Diese Liste nennt die wichtigsten Komponenten und ihre jeweiligen Lizenzen.

    Frontend +

    SoftwareVerwendungLizenz
    React 19UI-BibliothekMIT
    ViteBuild-Tool, Dev-ServerMIT
    swissqrbillQR-EinzahlungsscheineMIT

    Desktop-Wrapper +

    SoftwareVerwendungLizenz
    Tauri 2Cross-Platform Desktop-WrapperMIT / Apache-2.0
    WebKitWebView-Engine (macOS)LGPL-2.1 / BSD

    Rapport Server (Selfhost-Stack) +

    SoftwareVerwendungLizenz
    PostgreSQLDatenbankPostgreSQL-Lizenz
    GoTrueAuthentication-ServiceMIT
    PostgRESTREST-API über Postgres-SchemaMIT
    RealtimeWebSocket-SyncApache-2.0
    StorageObject-Storage für Belege & LogosApache-2.0
    KongAPI-GatewayApache-2.0
    nginxFrontend-WebserverBSD-2-Clause
    Docker ComposeOrchestrierungApache-2.0

    Diese Website +

    SoftwareVerwendungLizenz
    HugoStatic-Site-GeneratorApache-2.0
    HextraHugo-Theme (von Xin)MIT
    InterUI-Schrift (Rasmus Andersson)SIL OFL 1.1
    Playfair DisplayHeadingsSIL OFL 1.1

    Alle Logos und Marken-Namen gehören ihren jeweiligen Eigentümern.

    Lizenz-Volltexte +

    Die vollständigen Lizenztexte:

    Mitwirkende +

    RAPPORT wird derzeit als Ein-Personen-Projekt von Karim Gabriele Varano entwickelt. Beiträge — Code, Issues, Übersetzungen, Vorlagen — sind willkommen.

    Wer beitragen möchte → Gitea Issues.

    Kontakt +

    Fragen zur Lizenz, kommerzieller Einsatz, Spezialfälle: Issue auf Gitea oder via gabrielevarano.ch.

    \ No newline at end of file diff --git a/public/lizenz/index.xml b/public/lizenz/index.xml new file mode 100644 index 0000000..e65054f --- /dev/null +++ b/public/lizenz/index.xml @@ -0,0 +1 @@ +RAPPORT – Lizenz & Mitwirkendehttps://rapport.gabrielevarano.ch/lizenz/Recent content in Lizenz & Mitwirkende on RAPPORTHugo -- gohugo.iode \ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..7d329b1 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1 @@ +User-agent: * diff --git a/public/server/index.html b/public/server/index.html new file mode 100644 index 0000000..55fa015 --- /dev/null +++ b/public/server/index.html @@ -0,0 +1,98 @@ +Rapport Server – RAPPORTZum Inhalt springen

    Rapport Server

    Self-Hosting

    Rapport Server — der vollständige Selfhost-Stack für Rapport. Eigene Daten, eigene Domain, eigener Server. Komplett Open-Source, Docker-Compose, AGPL-3.0.

    Wann brauchst du Rapport Server? +

    SzenarioLösung
    Ein Mensch, ein MacDesktop-App reicht — Installation
    Mehrere Personen im StudioRapport Server auf einem Mac Mini oder Linux-Server
    Verteiltes Team, Home-Office, Mobile-ZugriffRapport Server mit Reverse-Proxy + SSL
    Cloud-Hosting bei einem AnbieterRapport Server auf VPS/Hetzner/etc.

    Die Desktop-App speichert lokal als JSON. Rapport Server bringt Postgres + Multi-User + Realtime-Sync — für alle, die zu zweit oder im Team arbeiten.

    Architektur +

    Rapport Server bündelt sechs Open-Source-Komponenten zu einem stimmigen Stack:

    ┌──────────────────────────────────────────────────────┐
    +│  Browser / Desktop-App                                │
    +└──────────────┬───────────────────────┬───────────────┘
    +               │                       │
    +               ▼                       ▼
    +        ┌──────────┐            ┌──────────┐
    +        │  nginx   │            │   Kong   │  ← API-Gateway
    +        │  (app)   │            │          │
    +        └──────────┘            └──────────┘
    +                                      │
    +                  ┌───────────────────┼─────────────────────┐
    +                  ▼                   ▼                     ▼
    +              ┌────────┐         ┌──────────┐          ┌─────────┐
    +              │ GoTrue │         │PostgREST │          │ Realtime│
    +              │ (Auth) │         │  (API)   │          │  (WS)   │
    +              └────────┘         └──────────┘          └─────────┘
    +                  │                   │                     │
    +                  └───────────────────┼─────────────────────┘
    +                                      ▼
    +                            ┌─────────────────┐
    +                            │   PostgreSQL    │
    +                            └─────────────────┘
    +                                      │
    +                                      ▼
    +                            ┌─────────────────┐
    +                            │     Storage     │  ← Belege, Logos
    +                            └─────────────────┘
    KomponenteFunktion
    PostgreSQLDatenbank — alle Rapport-Daten
    GoTrueAuthentication — Email/Passwort, JWT-Tokens
    PostgRESTREST-API direkt aus dem Postgres-Schema
    RealtimeWebSocket-Sync für Live-Updates
    StorageObject-Storage für Belege & Logos
    KongAPI-Gateway, routet alle Calls
    app (nginx)Liefert das Rapport-Frontend (React-Build)

    Alles in einem Docker-Compose. Keine Cloud-Abhängigkeit, keine Telemetrie.

    Voraussetzungen +

    OSContainer-Runtime
    Linux (Ubuntu 22.04+, Debian 12+, …)Docker Engine + Compose v2
    macOS (Mac Mini etc.)Colima + Docker CLI — voll OSS

    Auf macOS funktionieren auch OrbStack oder Docker Desktop, beide sind aber proprietär. Colima ist die OSS-Alternative und für Selfhost vollkommen ausreichend.

    Plus: ein DNS-Name für TLS (z. B. rapport.studio.ch), und optional ein Reverse-Proxy wie Nginx Proxy Manager oder Caddy.

    Setup in 5 Schritten +

    1 · Repo klonen +

    git clone https://git.kgva.ch/karim/rapport-server.git
    +cd rapport-server

    2 · .env erstellen +

    cp .env.example .env

    In .env müssen mindestens diese Werte ersetzt werden:

    VariableWas
    POSTGRES_PASSWORDDB-Passwort (≥ 32 Zeichen, zufällig)
    JWT_SECRETJWT-Signatur (≥ 32 Zeichen, zufällig)
    ANON_KEY / SERVICE_ROLE_KEYaus JWT-Secret abgeleitet
    SITE_URLPublic-URL der Instanz (z. B. https://app.rapport.studio.ch)

    Zufallswerte generieren:

    openssl rand -hex 32   # für POSTGRES_PASSWORD und JWT_SECRET
    +node scripts/generate-keys.mjs   # ANON_KEY + SERVICE_ROLE_KEY aus JWT_SECRET

    3 · Migrations holen +

    Die SQL-Migrations stammen aus dem App-Repo. Einmal initial holen:

    ./scripts/sync-migrations.sh

    Bei späteren Rapport-Updates erneut ausführen, dann docker compose down && docker compose up -d.

    4 · Stack starten +

    docker compose up -d

    Erststart dauert ~1 Minute (Postgres initialisiert sich, Migrations laufen, Container starten).

    5 · Health-Check +

    docker compose ps

    Alle Container sollten healthy zeigen. Frontend ist dann erreichbar unter http://localhost:8080.

    Reverse-Proxy + HTTPS +

    Für Production-Setups mit eigener Domain — Caddy ist die einfachste Variante (config-as-code, Let’s-Encrypt automatisch):

    app.rapport.studio.ch {
    +  reverse_proxy localhost:8080
    +}
    +
    +api.rapport.studio.ch {
    +  reverse_proxy localhost:8000
    +}

    In .env dann:

    SITE_URL=https://app.rapport.studio.ch
    +API_EXTERNAL_URL=https://api.rapport.studio.ch

    Alternativ über Nginx Proxy Manager mit Web-UI — siehe Troubleshooting.

    Updates +

    git pull
    +./scripts/sync-migrations.sh   # falls neue SQL-Migrationen
    +docker compose pull             # neueste Container-Versionen
    +docker compose up -d            # neu starten

    Daten bleiben erhalten — das Volume postgres-data wird nicht angetastet.

    Backup +

    Postgres-Dump +

    docker compose exec -T db pg_dumpall -U postgres > backup-$(date +%Y%m%d).sql

    Empfohlen: per cron täglich + auf externe Disk / S3 / Backblaze sichern.

    Storage (Belege, Logos) +

    docker compose exec storage tar -czf - /var/lib/storage > storage-$(date +%Y%m%d).tar.gz

    Restore +

    docker compose down
    +docker volume rm rapport-server_postgres-data
    +docker compose up -d db
    +sleep 10
    +cat backup-YYYYMMDD.sql | docker compose exec -T db psql -U postgres
    +docker compose up -d

    Bezug Rapport ↔ Rapport Server +

    KomponenteWoLizenz
    Rapport Desktop-Appgit.kgva.ch/karim/RAPPORTAGPL-3.0
    Rapport Servergit.kgva.ch/karim/rapport-serverAGPL-3.0

    Die Desktop-App kann wahlweise im Lokal-Modus (JSON, kein Server nötig) oder im Cloud-Modus (gegen Rapport Server) betrieben werden. Wechsel erfolgt im Login-Bildschirm der App.

    Häufige Fragen +

    Brauche ich Supabase Cloud? +

    Nein. Rapport Server ist die Selfhost-Variante derselben Komponenten (Postgres + GoTrue + PostgREST + Realtime + Storage), aber komplett auf eigener Infrastruktur. Kein Supabase-Account, keine externe Abhängigkeit.

    Kann ich von der Desktop-App auf Rapport Server migrieren? +

    Im Login der Desktop-App auf Cloud wechseln, Server-URL eingeben, Account erstellen, Daten manuell wieder anlegen (Bürodaten, Mitarbeiter, Kunden). Ein direkter localStorage → Postgres-Import ist geplant.

    Was kostet das? +

    Nichts — Rapport Server ist Open-Source (AGPL-3.0). Du brauchst nur einen Server (Mac Mini, NAS, VPS) und ggf. eine Domain.

    Wieviel Server-Resourcen? +

    Ein Mac Mini M1 (8 GB RAM) reicht problemlos für ein 5-Personen-Studio. Linux-VPS mit 2 GB RAM + 2 CPU reicht auch.

    Quellcode +

    git.kgva.ch/karim/rapport-server — Issues, Pull Requests und Forks willkommen.

    \ No newline at end of file diff --git a/public/server/index.xml b/public/server/index.xml new file mode 100644 index 0000000..d637e58 --- /dev/null +++ b/public/server/index.xml @@ -0,0 +1 @@ +RAPPORT – Rapport Serverhttps://rapport.gabrielevarano.ch/server/Recent content in Rapport Server on RAPPORTHugo -- gohugo.iode \ No newline at end of file diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 0000000..c36f3b3 --- /dev/null +++ b/public/site.webmanifest @@ -0,0 +1,20 @@ +{ + "name": "Hextra", + "short_name": "Hextra", + "start_url": "index.html", + "icons": [ + { + "src": "android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#000000", + "background_color": "#000000", + "display": "standalone" +} diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 0000000..2762279 --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1 @@ +https://rapport.gabrielevarano.ch/docs/https://rapport.gabrielevarano.ch/docs/erste-schritte/https://rapport.gabrielevarano.ch/features/zeiterfassung/https://rapport.gabrielevarano.ch/features/https://rapport.gabrielevarano.ch/docs/installation/https://rapport.gabrielevarano.ch/features/rechnungen/https://rapport.gabrielevarano.ch/downloads/https://rapport.gabrielevarano.ch/docs/einrichtung/https://rapport.gabrielevarano.ch/features/projekte/https://rapport.gabrielevarano.ch/docs/arbeitsablauf/https://rapport.gabrielevarano.ch/faq/https://rapport.gabrielevarano.ch/features/mitarbeiter/https://rapport.gabrielevarano.ch/server/https://rapport.gabrielevarano.ch/docs/datenhaltung/https://rapport.gabrielevarano.ch/features/spesen/https://rapport.gabrielevarano.ch/features/protokolle/https://rapport.gabrielevarano.ch/docs/web-modus/https://rapport.gabrielevarano.ch/features/auto-updater/https://rapport.gabrielevarano.ch/docs/entwicklung/https://rapport.gabrielevarano.ch/features/system-tray/https://rapport.gabrielevarano.ch/docs/troubleshooting/https://rapport.gabrielevarano.ch/docs/changelog/https://rapport.gabrielevarano.ch/lizenz/https://rapport.gabrielevarano.ch/categories/https://rapport.gabrielevarano.ch/https://rapport.gabrielevarano.ch/tags/ \ No newline at end of file diff --git a/public/tags/index.html b/public/tags/index.html new file mode 100644 index 0000000..b5b9bb0 --- /dev/null +++ b/public/tags/index.html @@ -0,0 +1,32 @@ +Tags – RAPPORTZum Inhalt springen \ No newline at end of file diff --git a/public/tags/index.xml b/public/tags/index.xml new file mode 100644 index 0000000..01c6562 --- /dev/null +++ b/public/tags/index.xml @@ -0,0 +1 @@ +RAPPORT – Tagshttps://rapport.gabrielevarano.ch/tags/Recent content in Tags on RAPPORTHugo -- gohugo.iode \ No newline at end of file diff --git a/static/fonts/Krungthep.ttf b/static/fonts/Krungthep.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9415853ce42198e9dad5b2d6eb1bb62a693141a9 GIT binary patch literal 190152 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 R>-g6n9rr*}uBi`X{6FjT!4d!f literal 0 HcmV?d00001 diff --git a/themes/hextra/.devcontainer/devcontainer.json b/themes/hextra/.devcontainer/devcontainer.json new file mode 100644 index 0000000..d8a32b8 --- /dev/null +++ b/themes/hextra/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +{ + "image": "mcr.microsoft.com/devcontainers/go:1", + "features": { + "ghcr.io/devcontainers/features/hugo:1": { + "extended": true, + "version": "0.161.1" + }, + "ghcr.io/devcontainers/features/node:1": {} + }, + "customizations": { + "vscode": { + "extensions": ["mhutchie.git-graph", "esbenp.prettier-vscode", "tamasfe.even-better-toml", "budparr.language-hugo-vscode"] + } + }, + "postCreateCommand": "npm install", + "forwardPorts": [1313] +} diff --git a/themes/hextra/.gitattributes b/themes/hextra/.gitattributes new file mode 100644 index 0000000..0820376 --- /dev/null +++ b/themes/hextra/.gitattributes @@ -0,0 +1,3 @@ +# Mark generated files so they are collapsed by default in GitHub diffs +assets/css/compiled/main.css linguist-generated=true +docs/hugo_stats.json linguist-generated=true diff --git a/themes/hextra/.gitignore b/themes/hextra/.gitignore new file mode 100644 index 0000000..7377a08 --- /dev/null +++ b/themes/hextra/.gitignore @@ -0,0 +1,9 @@ +node_modules/ +public/ +resources/ + +.hugo_build.lock + +# Playwright +playwright-report/ +test-results/ diff --git a/themes/hextra/.prettierrc b/themes/hextra/.prettierrc new file mode 100644 index 0000000..28eaa1e --- /dev/null +++ b/themes/hextra/.prettierrc @@ -0,0 +1,21 @@ +{ + "plugins": [ + "prettier-plugin-go-template" + ], + "goTemplateBracketSpacing": true, + "htmlWhitespaceSensitivity": "css", + "printWidth": 200, + "singleQuote": false, + "tabWidth": 2, + "trailingComma": "es5", + "overrides": [ + { + "files": [ + "*.html" + ], + "options": { + "parser": "go-template" + } + } + ] +} diff --git a/themes/hextra/.vscode/settings.json b/themes/hextra/.vscode/settings.json new file mode 100644 index 0000000..4f3bc8d --- /dev/null +++ b/themes/hextra/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.tabSize": 2, + "markdown.extension.toc.levels": "2..6" +} diff --git a/themes/hextra/AGENTS.md b/themes/hextra/AGENTS.md new file mode 100644 index 0000000..c0bbe14 --- /dev/null +++ b/themes/hextra/AGENTS.md @@ -0,0 +1,193 @@ +# AGENTS.md + +This file provides guidance to AI coding agents when working with code in this repository. + +## Project Overview + +Hextra is a modern, responsive Hugo theme designed for creating documentation websites, technical blogs, and static sites. Built with Tailwind CSS, it offers features like full-text search, dark mode, multi-language support, and extensive customization options. + +## Development Commands + +### Initial Setup + +When working in a new worktree or fresh clone without `node_modules`, run `npm install` first to install dependencies: + +```bash +npm install +``` + +### Development Server + +```bash +# Start development server with theme reloading (recommended for theme development) +npm run dev:theme +``` + +### Building + +```bash +# Build the example site +npm run build + +# Build CSS assets only +npm run build:css +``` + +## Architecture Overview + +### Hugo Theme Structure + +- **Base Layout**: `layouts/baseof.html` wraps all pages +- **Specialized Layouts**: `layouts/docs/`, `layouts/blog/`, `layouts/hextra-home.html` +- **Partials**: Reusable components in `layouts/_partials/` + - Core UI: `navbar.html`, `sidebar.html`, `footer.html`, `breadcrumb.html`, `toc.html` + - Utilities: `layouts/_partials/utils/` for helper functions + - Custom overrides: `layouts/_partials/custom/` for user customizations +- **Shortcodes**: Custom Markdown extensions in `layouts/_shortcodes/` +- **Render Hooks**: Custom Markdown rendering in `layouts/_markup/` for codeblocks, headings, images, and links + +### Asset Organization + +``` +assets/ +├── css/ +│ ├── styles.css # Main stylesheet (Tailwind entry point) +│ ├── compiled/main.css # Built CSS output (generated) +│ ├── components/ # Component-specific styles +│ ├── chroma/ # Syntax highlighting themes +│ └── custom.css # User customization entry point +└── js/ + ├── core/ # Core JS components + └── flexsearch.js # Search functionality +``` + +### Key Components + +- **Search**: FlexSearch-powered full-text search (`assets/js/flexsearch.js`) +- **Navigation**: Responsive navbar and auto-generated sidebar +- **Theme Toggle**: Dark/light mode switching +- **Internationalization**: 20+ language support in `i18n/` + +### Content Features + +- **Shortcodes**: `callout`, `card`, `cards`, `tabs`, `tab`, `details`, `steps`, `filetree`, `jupyter`, `badge`, `icon`, `pdf`, `include`, `asciinema`, `term` +- **Code Features**: Syntax highlighting (Chroma), copy buttons, line numbers via render hooks +- **SEO**: Open Graph, Twitter Cards, structured data +- **Performance**: Minimal JavaScript, optimized CSS with Tailwind + +## Development Workflow + +### Example Site Development + +The `docs/` directory serves as both documentation and testing ground: + +- Test new features here before releasing +- Configuration examples in `docs/hugo.yaml` showing multi-language setup +- Content examples demonstrate all theme capabilities +- Run from docs with: `hugo server --themesDir=../..` + +### CSS Development Workflow + +- Source: `assets/css/styles.css` (main stylesheet) +- Build process: Tailwind CSS → PostCSS → `assets/css/compiled/main.css` +- Component styles organized in `assets/css/components/` +- Chroma syntax highlighting themes in `assets/css/chroma/` +- CSS compilation requires Node.js dependencies (PostCSS, Tailwind CSS v4+) + +#### Rebuilding CSS after template changes + +Tailwind CSS relies on `docs/hugo_stats.json` to know which HTML tags, classes, and IDs are actually used in the built site, so it can tree-shake unused styles. When you modify layouts, partials, or shortcodes you must **regenerate `hugo_stats.json` first**, then rebuild the CSS: + +1. **Generate `docs/hugo_stats.json`** — Run Hugo with the `dev.toml` config (which sets `build.buildStats.enable = true`): + + ```bash + # Using npm (starts a dev server that writes hugo_stats.json on every rebuild): + npm run dev:theme + + # Or a one-shot build using the raw Hugo command: + hugo --config=hugo.yaml,../dev.toml --themesDir=../.. --source=docs + ``` + +2. **Build the CSS** — With an up-to-date `hugo_stats.json` in place, compile the stylesheet: + + ```bash + npm run build:css + ``` + +> **Why two steps?** `dev.toml` mounts `docs/hugo_stats.json` into the Hugo asset pipeline (`assets/notwatching/hugo_stats.json`) and configures a cache-buster so that changes to the stats file trigger a CSS recompile during `dev:theme`. When running outside the dev server you need to perform these steps manually in order. + +### Customization Points + +- Custom partials: `layouts/_partials/custom/` +- Custom CSS: `assets/css/custom.css` +- Site-specific overrides: Copy any layout to your site's `layouts/` directory + +## Configuration & Requirements + +### Theme Requirements + +- Hugo minimum version: 0.146.0 (extended version required - see `theme.toml`) +- Go 1.20+ (as specified in `go.mod`) +- Node.js for CSS compilation (PostCSS, Tailwind CSS v4+) + +### Key Configuration Files + +- `docs/hugo.yaml` - Example Hugo configuration with multi-language setup +- `postcss.config.mjs` - PostCSS configuration for CSS processing +- `package.json` - Node.js dependencies and build scripts + +### Development Environment + +- Default Hugo development server: Port 1313 +- Development server runs with `--disableFastRender -D` for better development experience +- Theme development uses `--logLevel=debug` for detailed logging + +### Multi-language Support + +- Configure languages in `hugo.yaml` (supports 20+ languages including RTL) +- Translation files in `i18n/` directory (e.g., `en.yaml`, `fa.yaml`, `ja.yaml`, `zh-cn.yaml`) +- Example supports English, Persian (RTL), Japanese, and Simplified Chinese + +## Theme Development Guidelines + +### Hugo Theme Conventions + +- Theme files in this repository override Hugo defaults +- Follow Hugo's theme development guidelines for compatibility +- Maintain backward compatibility with existing configurations + +### JavaScript & Performance + +- All JavaScript components are designed to have minimal footprint +- Core JS components in `assets/js/core/`: `theme.js`, `nav-menu.js`, `code-copy.js`, `sidebar.js`, `tabs.js`, etc. +- FlexSearch powers offline full-text search (`assets/js/flexsearch.js`) + +### CSS Architecture + +- Uses Tailwind CSS v4+ with PostCSS processing +- Component-based CSS organization in `assets/css/components/` +- Compiled output goes to `assets/css/compiled/main.css` +- Prettier formatting for Go templates and code consistency + +### Accessibility (WCAG Compliance) + +All new features and UI changes must follow the [Web Content Accessibility Guidelines (WCAG) 2.2](https://www.w3.org/TR/WCAG22/) at the **AA** conformance level. Key requirements: + +- **Semantic HTML**: Use appropriate elements (`