@sigit / siti / commits / af02196

Initial commit

Siti AI: a private, on-device AI assistant for macOS, iOS, and Android, and the open reference app for the Onde inference engine. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

paydii committed Jul 9, 2026 at 20:06 UTC af02196ba02988e41ce33ecd4d79f3ba607bb772
158 files changed +19420
.github/FUNDING.yml new
+7
@@ -0,0 +1,7 @@
1 +# GitHub Sponsors and funding links for Siti AI.
2 +# Enroll the org (and/or your personal account) in GitHub Sponsors first:
3 +# https://github.com/sponsors — the button won't render until enrollment is live.
4 +
5 +github: [ondeinference]
6 +# Add alternates once set up:
7 +# custom: ["https://ondeinference.com/sponsor"]
.github/ISSUE_TEMPLATE/bug_report.yml new
+39
@@ -0,0 +1,39 @@
1 +name: 🐞 Bug report
2 +description: Something isn't working as expected
3 +labels: ["bug"]
4 +body:
5 + - type: markdown
6 + attributes:
7 + value: |
8 + Thanks for the report! For **security or privacy** issues, do not use
9 + this form — see [SECURITY.md](../blob/main/SECURITY.md).
10 + - type: textarea
11 + id: what-happened
12 + attributes:
13 + label: What happened?
14 + description: What did you do, what did you expect, and what happened instead?
15 + placeholder: Steps to reproduce, expected vs. actual behavior.
16 + validations:
17 + required: true
18 + - type: input
19 + id: platform
20 + attributes:
21 + label: Platform & version
22 + placeholder: "macOS 15.1 (Apple silicon) / iOS 18.1 / Android 14 — Siti 1.0.0"
23 + validations:
24 + required: true
25 + - type: input
26 + id: model
27 + attributes:
28 + label: Model in use
29 + description: Which on-device model was loaded, if relevant?
30 + placeholder: "e.g. Qwen3-4B (GGUF)"
31 + validations:
32 + required: false
33 + - type: textarea
34 + id: logs
35 + attributes:
36 + label: Logs / screenshots
37 + description: Relevant log output or screenshots. Redact anything private.
38 + validations:
39 + required: false
.github/ISSUE_TEMPLATE/config.yml new
+11
@@ -0,0 +1,11 @@
1 +blank_issues_enabled: false
2 +contact_links:
3 + - name: 🔒 Security / privacy report
4 + url: https://github.com/ondeinference/siti/security/advisories/new
5 + about: Please report vulnerabilities privately — do not open a public issue.
6 + - name: 🧠 Onde SDK (the inference engine)
7 + url: https://github.com/ondeinference/onde/issues
8 + about: Issues with on-device inference itself belong in the Onde repo.
9 + - name: 💬 Questions & discussion
10 + url: https://github.com/ondeinference/siti/discussions
11 + about: Ask questions and share ideas in Discussions.
.github/ISSUE_TEMPLATE/feature_request.yml new
+25
@@ -0,0 +1,25 @@
1 +name: 💡 Feature request
2 +description: Suggest an idea or improvement
3 +labels: ["enhancement"]
4 +body:
5 + - type: textarea
6 + id: problem
7 + attributes:
8 + label: What problem does this solve?
9 + description: Describe the user problem, not just the solution.
10 + validations:
11 + required: true
12 + - type: textarea
13 + id: proposal
14 + attributes:
15 + label: Proposed solution
16 + description: What would you like to see? Alternatives you considered?
17 + validations:
18 + required: false
19 + - type: checkboxes
20 + id: scope
21 + attributes:
22 + label: Scope
23 + options:
24 + - label: This is about the Siti app (not the Onde engine — that lives in its own repo)
25 + required: true
.github/PULL_REQUEST_TEMPLATE.md new
+18
@@ -0,0 +1,18 @@
1 +<!-- Thanks for contributing to Siti AI! -->
2 +
3 +## What & why
4 +
5 +<!-- What does this change do, and why? Link related issues (e.g. Fixes #123). -->
6 +
7 +## How it was tested
8 +
9 +<!-- Steps you took to verify. Platform(s): macOS / iOS. -->
10 +
11 +## Checklist
12 +
13 +- [ ] `make lint` passes (clippy + tsc, no warnings)
14 +- [ ] `make fmt` applied
15 +- [ ] `make build` compiles
16 +- [ ] Docs updated if behavior changed
17 +- [ ] I have signed the [CLA](../CLA.md) (the bot will prompt on first PR)
18 +- [ ] This PR is focused on a single logical change
.github/workflows/ci.yml new
+57
@@ -0,0 +1,57 @@
1 +name: CI
2 +
3 +on:
4 + push:
5 + branches: [main]
6 + pull_request:
7 +
8 +concurrency:
9 + group: ${{ github.workflow }}-${{ github.ref }}
10 + cancel-in-progress: true
11 +
12 +jobs:
13 + frontend:
14 + name: Frontend (UI + web)
15 + runs-on: ubuntu-latest
16 + steps:
17 + - uses: actions/checkout@v4
18 + - uses: pnpm/action-setup@v4
19 + - uses: actions/setup-node@v4
20 + with:
21 + node-version: 20
22 + cache: pnpm
23 + - run: pnpm install --frozen-lockfile
24 + - name: Type-check + build UI
25 + run: pnpm build
26 + - name: Build landing site
27 + working-directory: web
28 + run: pnpm build
29 +
30 + rust:
31 + name: Rust core (macOS)
32 + runs-on: macos-latest
33 + steps:
34 + - uses: actions/checkout@v4
35 + - uses: pnpm/action-setup@v4
36 + - uses: actions/setup-node@v4
37 + with:
38 + node-version: 20
39 + cache: pnpm
40 + - uses: dtolnay/rust-toolchain@stable
41 + with:
42 + components: rustfmt, clippy
43 + - uses: Swatinem/rust-cache@v2
44 + with:
45 + workspaces: src-tauri
46 + # tauri-build expects the frontend dist to exist before compiling the core.
47 + - run: pnpm install --frozen-lockfile
48 + - run: pnpm build
49 + - name: rustfmt
50 + working-directory: src-tauri
51 + run: cargo fmt --all --check
52 + - name: clippy
53 + working-directory: src-tauri
54 + run: cargo clippy --all-targets -- -D warnings
55 + - name: build
56 + working-directory: src-tauri
57 + run: cargo build --locked
.gitignore new
+56
@@ -0,0 +1,56 @@
1 +# Logs
2 +logs
3 +*.log
4 +npm-debug.log*
5 +yarn-debug.log*
6 +yarn-error.log*
7 +pnpm-debug.log*
8 +lerna-debug.log*
9 +
10 +node_modules
11 +dist
12 +dist-ssr
13 +*.local
14 +
15 +# Editor directories and files
16 +.vscode/*
17 +!.vscode/extensions.json
18 +.idea
19 +.DS_Store
20 +*.suo
21 +*.ntvs*
22 +*.njsproj
23 +*.sln
24 +*.sw?
25 +
26 +# Rust / Tauri build output
27 +/src-tauri/target
28 +/web/.next
29 +
30 +# Tauri-regenerated mobile projects (recreated by `cargo tauri {ios,android} init`).
31 +# The intentional overrides live in src-tauri/gen-override/ and ARE committed.
32 +/src-tauri/gen
33 +
34 +# ── Secrets & signing material — never commit ─────────────────────────────────
35 +# Android signing: the real keystore.properties (with the store password) and
36 +# any keystore files stay local. keystore.properties.example IS committed.
37 +keystore.properties
38 +*.jks
39 +*.keystore
40 +# Apple signing / App Store Connect
41 +*.p8
42 +*.p12
43 +*.mobileprovision
44 +*.provisionprofile
45 +*.cer
46 +*.certSigningRequest
47 +# Environment files
48 +.env
49 +.env.*
50 +!.env.example
51 +
52 +# Local Apple signing configs — contain cert SHA-1 identities and local
53 +# provisioning-profile paths. Copy the .example files, fill in your own, and
54 +# keep the real ones untracked. The .example templates ARE committed.
55 +/src-tauri/tauri.prod-macos.conf.json
56 +/src-tauri/tauri.prod-macos-appstore.conf.json
.smb/config.toml new
+15
@@ -0,0 +1,15 @@
1 +# smbCloud deploy configuration for the Siti AI landing page (web/).
2 +#
3 +# This file intentionally carries only the app *identity*. Everything else —
4 +# project id, remote path, port, pm2 app name, deployment method, package
5 +# manager — is stored server-side against the FrontendApp record and fetched by
6 +# the CLI at deploy time (GET /v1/frontend_apps/{id}/deploy_config), then merged
7 +# in locally. Keeping infra topology off the public repo is deliberate.
8 +#
9 +# Deploy with: smb deploy (requires `smb login`)
10 +
11 +name = "sitiai"
12 +
13 +[project]
14 +frontend_app_id = "1c28381c-7f8f-4637-822c-e2b31424448c"
15 +source = "web" # local Next.js app directory to deploy
.vscode/extensions.json new
+3
@@ -0,0 +1,3 @@
1 +{
2 + "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"]
3 +}
CLA.md new
+78
@@ -0,0 +1,78 @@
1 +# Contributor License Agreement
2 +
3 +Thank you for your interest in contributing to Siti AI ("the Project"),
4 +maintained by **Splitfire AB** ("we", "us").
5 +
6 +This Contributor License Agreement ("Agreement") clarifies the intellectual
7 +property license granted with Contributions from any person or entity. By
8 +submitting a Contribution to the Project (for example, via a pull request), You
9 +accept and agree to the following terms for Your present and future
10 +Contributions.
11 +
12 +This Agreement is adapted from the widely used Apache Software Foundation
13 +Individual Contributor License Agreement and is intended to be simple and fair.
14 +
15 +## 1. Definitions
16 +
17 +- **"You" / "Your"** means the individual or legal entity making a Contribution.
18 +- **"Contribution"** means any original work of authorship, including any
19 + modifications or additions to existing work, that You intentionally submit to
20 + the Project for inclusion in, or documentation of, the Project.
21 +- **"Submit"** means any form of electronic, verbal, or written communication
22 + sent to the Project, including but not limited to pull requests, issues, and
23 + comments, excluding communication conspicuously marked "Not a Contribution."
24 +
25 +## 2. Copyright license
26 +
27 +You grant to Splitfire AB and to recipients of software distributed by the
28 +Project a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
29 +irrevocable copyright license to reproduce, prepare derivative works of,
30 +publicly display, publicly perform, sublicense, and distribute Your
31 +Contributions and such derivative works.
32 +
33 +This license includes the right for Splitfire AB to license the Contribution,
34 +including under the Project's open-source license
35 +([Apache-2.0](LICENSE)) **and** under separate commercial terms. You retain all
36 +right, title, and interest in Your Contributions; this Agreement is a license,
37 +not an assignment.
38 +
39 +## 3. Patent license
40 +
41 +You grant to Splitfire AB and to recipients of the Project a perpetual,
42 +worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated
43 +in this section) patent license to make, have made, use, offer to sell, sell,
44 +import, and otherwise transfer Your Contribution, where such license applies
45 +only to those patent claims licensable by You that are necessarily infringed by
46 +Your Contribution alone or by combination of Your Contribution with the Project.
47 +
48 +## 4. Your representations
49 +
50 +You represent that:
51 +
52 +- You are legally entitled to grant the above licenses.
53 +- Each of Your Contributions is Your original creation, or You have the rights to
54 + submit it.
55 +- If Your employer has rights to intellectual property You create, You have
56 + received permission to make Contributions on behalf of that employer, or Your
57 + employer has waived such rights for Your Contributions.
58 +- Your Contributions do not, to Your knowledge, violate any third party's
59 + copyrights, trademarks, patents, or other intellectual property rights.
60 +
61 +## 5. Third-party material
62 +
63 +If You submit work that is not Your original creation, You will identify it and
64 +its source and any license or restriction You are aware of, and clearly mark it
65 +(for example, "Submitted on behalf of a third party: [name]").
66 +
67 +## 6. No warranty
68 +
69 +Contributions are provided "as is" without warranty of any kind. You are not
70 +expected to provide support for Your Contributions, except to the extent You
71 +desire to.
72 +
73 +---
74 +
75 +**How to sign:** the CLA-assistant bot will prompt you on your first pull
76 +request; signing is a one-time action recorded against your GitHub account.
77 +Corporate contributors may contact **support@getsiti.5mb.app** to arrange an
78 +entity-level agreement.
CODE_OF_CONDUCT.md new
+89
@@ -0,0 +1,89 @@
1 +# Contributor Covenant Code of Conduct
2 +
3 +## Our Pledge
4 +
5 +We as members, contributors, and leaders pledge to make participation in our
6 +community a harassment-free experience for everyone, regardless of age, body
7 +size, visible or invisible disability, ethnicity, sex characteristics, gender
8 +identity and expression, level of experience, education, socio-economic status,
9 +nationality, personal appearance, race, religion, or sexual identity and
10 +orientation.
11 +
12 +We pledge to act and interact in ways that contribute to an open, welcoming,
13 +diverse, inclusive, and healthy community.
14 +
15 +## Our Standards
16 +
17 +Examples of behavior that contributes to a positive environment for our
18 +community include:
19 +
20 +- Demonstrating empathy and kindness toward other people
21 +- Being respectful of differing opinions, viewpoints, and experiences
22 +- Giving and gracefully accepting constructive feedback
23 +- Accepting responsibility and apologizing to those affected by our mistakes,
24 + and learning from the experience
25 +- Focusing on what is best not just for us as individuals, but for the overall
26 + community
27 +
28 +Examples of unacceptable behavior include:
29 +
30 +- The use of sexualized language or imagery, and sexual attention or advances of
31 + any kind
32 +- Trolling, insulting or derogatory comments, and personal or political attacks
33 +- Public or private harassment
34 +- Publishing others' private information, such as a physical or email address,
35 + without their explicit permission
36 +- Other conduct which could reasonably be considered inappropriate in a
37 + professional setting
38 +
39 +## Enforcement Responsibilities
40 +
41 +Community leaders are responsible for clarifying and enforcing our standards of
42 +acceptable behavior and will take appropriate and fair corrective action in
43 +response to any behavior that they deem inappropriate, threatening, offensive,
44 +or harmful.
45 +
46 +Community leaders have the right and responsibility to remove, edit, or reject
47 +comments, commits, code, wiki edits, issues, and other contributions that are
48 +not aligned to this Code of Conduct, and will communicate reasons for moderation
49 +decisions when appropriate.
50 +
51 +## Scope
52 +
53 +This Code of Conduct applies within all community spaces, and also applies when
54 +an individual is officially representing the community in public spaces.
55 +
56 +## Enforcement
57 +
58 +Instances of abusive, harassing, or otherwise unacceptable behavior may be
59 +reported to the community leaders responsible for enforcement at
60 +**support@getsiti.5mb.app**. All complaints will be reviewed and investigated
61 +promptly and fairly.
62 +
63 +All community leaders are obligated to respect the privacy and security of the
64 +reporter of any incident.
65 +
66 +## Enforcement Guidelines
67 +
68 +Community leaders will follow these Community Impact Guidelines in determining
69 +the consequences for any action they deem in violation of this Code of Conduct:
70 +
71 +1. **Correction** — Community Impact: use of inappropriate language or other
72 + behavior deemed unprofessional. Consequence: a private, written warning.
73 +2. **Warning** — Community Impact: a violation through a single incident or
74 + series of actions. Consequence: a warning with consequences for continued
75 + behavior.
76 +3. **Temporary Ban** — Community Impact: a serious violation of community
77 + standards. Consequence: a temporary ban from any interaction with the
78 + community.
79 +4. **Permanent Ban** — Community Impact: demonstrating a pattern of violation of
80 + community standards. Consequence: a permanent ban from any public interaction
81 + within the community.
82 +
83 +## Attribution
84 +
85 +This Code of Conduct is adapted from the [Contributor Covenant][homepage],
86 +version 2.1, available at
87 +<https://www.contributor-covenant.org/version/2/1/code_of_conduct.html>.
88 +
89 +[homepage]: https://www.contributor-covenant.org
CONTRIBUTING.md new
+74
@@ -0,0 +1,74 @@
1 +# Contributing to Siti AI
2 +
3 +Thanks for your interest — Siti is both a shipping product and the reference app
4 +for the [Onde](https://ondeinference.com) inference engine, so clean, readable
5 +contributions genuinely help other developers.
6 +
7 +## Ground rules
8 +
9 +- Be kind and constructive. This project follows a [Code of Conduct](CODE_OF_CONDUCT.md).
10 +- For security issues, **do not open a public issue** — follow [SECURITY.md](SECURITY.md).
11 +- Discuss non-trivial changes in an issue before opening a large PR.
12 +
13 +## Contributor License Agreement (CLA)
14 +
15 +Before we can merge your first contribution, you'll need to sign our
16 +[Contributor License Agreement](CLA.md). It's lightweight and only needs to be
17 +done once. When you open a pull request, the CLA bot will comment with a link if
18 +a signature is needed.
19 +
20 +Why a CLA: Siti's code is Apache-2.0, but the project sits alongside a
21 +commercially licensed SDK. The CLA lets Splitfire AB keep both open-source and
22 +commercial licensing options open while guaranteeing your contribution stays
23 +available under the open license.
24 +
25 +## Development setup
26 +
27 +**Prerequisites:** [Rust](https://rustup.rs) (stable), [pnpm](https://pnpm.io),
28 +and the [Tauri prerequisites](https://tauri.app/start/prerequisites/) (Xcode on
29 +macOS).
30 +
31 +```bash
32 +pnpm install
33 +make dev # start the Tauri dev server (or: pnpm tauri dev)
34 +```
35 +
36 +The Onde engine is pulled from crates.io — you do **not** need a separate SDK
37 +checkout. To hack on Siti and Onde together, uncomment the `[patch.crates-io]`
38 +block in `src-tauri/Cargo.toml` and point it at your local `onde` checkout.
39 +
40 +## Before you open a PR
41 +
42 +```bash
43 +make fmt # cargo fmt
44 +make lint # clippy + tsc — must pass with no warnings
45 +make build # must compile
46 +```
47 +
48 +- Keep changes focused; one logical change per PR.
49 +- Match the surrounding style. The codebase is deliberately well-commented where
50 + the "why" is non-obvious — keep that up.
51 +- Update docs when you change behavior.
52 +- New Rust code should be `clippy`-clean; new UI code should typecheck.
53 +
54 +## Commit messages
55 +
56 +Write clear, imperative commit messages ("Add model download retry", not
57 +"added stuff"). Reference issues where relevant (`Fixes #123`).
58 +
59 +## Pull requests
60 +
61 +- Fill out the PR template.
62 +- Ensure CI is green.
63 +- A maintainer will review; expect a round or two of feedback. We care about the
64 + details because this codebase is meant to be read.
65 +
66 +## Reporting bugs & requesting features
67 +
68 +Use the issue templates. Good bug reports include your platform/version, steps to
69 +reproduce, and what you expected vs. what happened.
70 +
71 +## License of contributions
72 +
73 +By contributing, you agree that your contributions are licensed under
74 +[Apache-2.0](LICENSE) and covered by the terms of the [CLA](CLA.md).
LICENSE new
+201
@@ -0,0 +1,201 @@
1 + Apache License
2 + Version 2.0, January 2004
3 + http://www.apache.org/licenses/
4 +
5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 +
7 + 1. Definitions.
8 +
9 + "License" shall mean the terms and conditions for use, reproduction,
10 + and distribution as defined by Sections 1 through 9 of this document.
11 +
12 + "Licensor" shall mean the copyright owner or entity authorized by
13 + the copyright owner that is granting the License.
14 +
15 + "Legal Entity" shall mean the union of the acting entity and all
16 + other entities that control, are controlled by, or are under common
17 + control with that entity. For the purposes of this definition,
18 + "control" means (i) the power, direct or indirect, to cause the
19 + direction or management of such entity, whether by contract or
20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 + outstanding shares, or (iii) beneficial ownership of such entity.
22 +
23 + "You" (or "Your") shall mean an individual or Legal Entity
24 + exercising permissions granted by this License.
25 +
26 + "Source" form shall mean the preferred form for making modifications,
27 + including but not limited to software source code, documentation
28 + source, and configuration files.
29 +
30 + "Object" form shall mean any form resulting from mechanical
31 + transformation or translation of a Source form, including but
32 + not limited to compiled object code, generated documentation,
33 + and conversions to other media types.
34 +
35 + "Work" shall mean the work of authorship, whether in Source or
36 + Object form, made available under the License, as indicated by a
37 + copyright notice that is included in or attached to the work
38 + (an example is provided in the Appendix below).
39 +
40 + "Derivative Works" shall mean any work, whether in Source or Object
41 + form, that is based on (or derived from) the Work and for which the
42 + editorial revisions, annotations, elaborations, or other modifications
43 + represent, as a whole, an original work of authorship. For the purposes
44 + of this License, Derivative Works shall not include works that remain
45 + separable from, or merely link (or bind by name) to the interfaces of,
46 + the Work and Derivative Works thereof.
47 +
48 + "Contribution" shall mean any work of authorship, including
49 + the original version of the Work and any modifications or additions
50 + to that Work or Derivative Works thereof, that is intentionally
51 + submitted to Licensor for inclusion in the Work by the copyright owner
52 + or by an individual or Legal Entity authorized to submit on behalf of
53 + the copyright owner. For the purposes of this definition, "submitted"
54 + means any form of electronic, verbal, or written communication sent
55 + to the Licensor or its representatives, including but not limited to
56 + communication on electronic mailing lists, source code control systems,
57 + and issue tracking systems that are managed by, or on behalf of, the
58 + Licensor for the purpose of discussing and improving the Work, but
59 + excluding communication that is conspicuously marked or otherwise
60 + designated in writing by the copyright owner as "Not a Contribution."
61 +
62 + "Contributor" shall mean Licensor and any individual or Legal Entity
63 + on behalf of whom a Contribution has been received by Licensor and
64 + subsequently incorporated within the Work.
65 +
66 + 2. Grant of Copyright License. Subject to the terms and conditions of
67 + this License, each Contributor hereby grants to You a perpetual,
68 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 + copyright license to reproduce, prepare Derivative Works of,
70 + publicly display, publicly perform, sublicense, and distribute the
71 + Work and such Derivative Works in Source or Object form.
72 +
73 + 3. Grant of Patent License. Subject to the terms and conditions of
74 + this License, each Contributor hereby grants to You a perpetual,
75 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 + (except as stated in this section) patent license to make, have made,
77 + use, offer to sell, sell, import, and otherwise transfer the Work,
78 + where such license applies only to those patent claims licensable
79 + by such Contributor that are necessarily infringed by their
80 + Contribution(s) alone or by combination of their Contribution(s)
81 + with the Work to which such Contribution(s) was submitted. If You
82 + institute patent litigation against any entity (including a
83 + cross-claim or counterclaim in a lawsuit) alleging that the Work
84 + or a Contribution incorporated within the Work constitutes direct
85 + or contributory patent infringement, then any patent licenses
86 + granted to You under this License for that Work shall terminate
87 + as of the date such litigation is filed.
88 +
89 + 4. Redistribution. You may reproduce and distribute copies of the
90 + Work or Derivative Works thereof in any medium, with or without
91 + modifications, and in Source or Object form, provided that You
92 + meet the following conditions:
93 +
94 + (a) You must give any other recipients of the Work or Derivative
95 + Works a copy of this License; and
96 +
97 + (b) You must cause any modified files to carry prominent notices
98 + stating that You changed the files; and
99 +
100 + (c) You must retain, in the Source form of any Derivative Works
101 + that You distribute, all copyright, patent, trademark, and
102 + attribution notices from the Source form of the Work,
103 + excluding those notices that do not pertain to any part of
104 + the Derivative Works; and
105 +
106 + (d) If the Work includes a "NOTICE" text file as part of its
107 + distribution, then any Derivative Works that You distribute must
108 + include a readable copy of the attribution notices contained
109 + within such NOTICE file, excluding those notices that do not
110 + pertain to any part of the Derivative Works, in at least one
111 + of the following places: within a NOTICE text file distributed
112 + as part of the Derivative Works; within the Source form or
113 + documentation, if provided along with the Derivative Works; or,
114 + within a display generated by the Derivative Works, if and
115 + wherever such third-party notices normally appear. The contents
116 + of the NOTICE file are for informational purposes only and do
117 + not modify the License. You may add Your own attribution notices
118 + within Derivative Works that You distribute, alongside or as an
119 + addendum to the NOTICE text from the Work, provided that such
120 + additional attribution notices cannot be construed as modifying
121 + the License.
122 +
123 + You may add Your own copyright statement to Your modifications and
124 + may provide additional or different license terms and conditions
125 + for use, reproduction, or distribution of Your modifications, or
126 + for any such Derivative Works as a whole, provided Your use,
127 + reproduction, and distribution of the Work otherwise complies with
128 + the conditions stated in this License.
129 +
130 + 5. Submission of Contributions. Unless You explicitly state otherwise,
131 + any Contribution intentionally submitted for inclusion in the Work
132 + by You to the Licensor shall be under the terms and conditions of
133 + this License, without any additional terms or conditions.
134 + Notwithstanding the above, nothing herein shall supersede or modify
135 + the terms of any separate license agreement you may have executed
136 + with Licensor regarding such Contributions.
137 +
138 + 6. Trademarks. This License does not grant permission to use the trade
139 + names, trademarks, service marks, or product names of the Licensor,
140 + except as required for reasonable and customary use in describing the
141 + origin of the Work and reproducing the content of the NOTICE file.
142 +
143 + 7. Disclaimer of Warranty. Unless required by applicable law or
144 + agreed to in writing, Licensor provides the Work (and each
145 + Contributor provides its Contributions) on an "AS IS" BASIS,
146 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 + implied, including, without limitation, any warranties or conditions
148 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 + PARTICULAR PURPOSE. You are solely responsible for determining the
150 + appropriateness of using or redistributing the Work and assume any
151 + risks associated with Your exercise of permissions under this License.
152 +
153 + 8. Limitation of Liability. In no event and under no legal theory,
154 + whether in tort (including negligence), contract, or otherwise,
155 + unless required by applicable law (such as deliberate and grossly
156 + negligent acts) or agreed to in writing, shall any Contributor be
157 + liable to You for damages, including any direct, indirect, special,
158 + incidental, or consequential damages of any character arising as a
159 + result of this License or out of the use or inability to use the
160 + Work (including but not limited to damages for loss of goodwill,
161 + work stoppage, computer failure or malfunction, or any and all
162 + other commercial damages or losses), even if such Contributor
163 + has been advised of the possibility of such damages.
164 +
165 + 9. Accepting Warranty or Additional Liability. While redistributing
166 + the Work or Derivative Works thereof, You may choose to offer,
167 + and charge a fee for, acceptance of support, warranty, indemnity,
168 + or other liability obligations and/or rights consistent with this
169 + License. However, in accepting such obligations, You may act only
170 + on Your own behalf and on Your sole responsibility, not on behalf
171 + of any other Contributor, and only if You agree to indemnify,
172 + defend, and hold each Contributor harmless for any liability
173 + incurred by, or claims asserted against, such Contributor by reason
174 + of your accepting any such warranty or additional liability.
175 +
176 + END OF TERMS AND CONDITIONS
177 +
178 + APPENDIX: How to apply the Apache License to your work.
179 +
180 + To apply the Apache License to your work, attach the following
181 + boilerplate notice, with the fields enclosed by brackets "[]"
182 + replaced with your own identifying information. (Don't include
183 + the brackets!) The text should be enclosed in the appropriate
184 + comment syntax for the file format. We also recommend that a
185 + file or class name and description of purpose be included on the
186 + same "printed page" as the copyright notice for easier
187 + identification within third-party archives.
188 +
189 + Copyright 2026 Splitfire AB
190 +
191 + Licensed under the Apache License, Version 2.0 (the "License");
192 + you may not use this file except in compliance with the License.
193 + You may obtain a copy of the License at
194 +
195 + http://www.apache.org/licenses/LICENSE-2.0
196 +
197 + Unless required by applicable law or agreed to in writing, software
198 + distributed under the License is distributed on an "AS IS" BASIS,
199 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 + See the License for the specific language governing permissions and
201 + limitations under the License.
Makefile new
+136
@@ -0,0 +1,136 @@
1 +# Siti AI Makefile
2 +#
3 +# ── macOS App Store ────────────────────────────────────────────────────────
4 +# make asmacos — full pipeline: build → sign → upload
5 +# make asmacos-build — tauri build only (universal, App Store config)
6 +# make asmacos-sign — sign the .app into a .pkg
7 +# make asmacos-upload — upload the .pkg to App Store Connect
8 +#
9 +# ── iOS App Store ──────────────────────────────────────────────────────────
10 +# make asios — full pipeline: clean → init → icons → override → build → upload
11 +# make asios-clean — rm -rf src-tauri/gen/apple
12 +# make asios-init — cargo tauri ios init
13 +# make asios-icons — pnpm gim
14 +# make asios-override — copy gen-override/apple into gen/apple
15 +# make asios-build — cargo tauri ios build
16 +# make asios-upload — xcrun altool upload
17 +#
18 +# ── Android Play Store ─────────────────────────────────────────────────────
19 +# make psmobile — full pipeline: clean → init → icons → override → keystore → build → open (.aab)
20 +# make psmobile-clean — rm -rf src-tauri/gen/android
21 +# make psmobile-init — cargo tauri android init
22 +# make psmobile-icons — pnpm gim
23 +# make psmobile-override — copy gen-override/android into gen/android
24 +# make psmobile-keystore — patch keystore.properties storeFile path
25 +# make psmobile-build — cargo tauri android build --aab
26 +# make psmobile-open — open the .aab output dir (upload to Play Console)
27 +#
28 +# ── Development ────────────────────────────────────────────────────────────
29 +# make dev — start the Tauri dev server
30 +# make build — standard debug build
31 +# make fmt — cargo fmt
32 +# make lint — clippy + tsc
33 +# make clean — remove build artefacts
34 +
35 +.PHONY: dev build \
36 + asmacos asmacos-build asmacos-sign asmacos-upload \
37 + asios asios-clean asios-init asios-icons asios-override asios-build asios-upload \
38 + psmobile psmobile-clean psmobile-init psmobile-icons psmobile-override psmobile-keystore psmobile-build psmobile-open \
39 + web-dev web-build web-deploy \
40 + fmt lint clean
41 +
42 +# ── macOS App Store ────────────────────────────────────────────────────────
43 +
44 +asmacos:
45 + ./scripts/build-appstore.sh all
46 +
47 +asmacos-build:
48 + ./scripts/build-appstore.sh build
49 +
50 +asmacos-sign:
51 + ./scripts/build-appstore.sh sign
52 +
53 +asmacos-upload:
54 + ./scripts/build-appstore.sh upload
55 +
56 +# ── iOS App Store ──────────────────────────────────────────────────────────
57 +
58 +asios:
59 + ./scripts/build-ios.sh all
60 +
61 +asios-clean:
62 + ./scripts/build-ios.sh clean
63 +
64 +asios-init:
65 + ./scripts/build-ios.sh init
66 +
67 +asios-icons:
68 + ./scripts/build-ios.sh icons
69 +
70 +asios-override:
71 + ./scripts/build-ios.sh override
72 +
73 +asios-build:
74 + ./scripts/build-ios.sh build
75 +
76 +asios-upload:
77 + ./scripts/build-ios.sh upload
78 +
79 +# ── Android Play Store ─────────────────────────────────────────────────────
80 +
81 +psmobile:
82 + ./scripts/build-android.sh all
83 + open src-tauri/gen/android/app/build/outputs/bundle/universalRelease/
84 +
85 +psmobile-clean:
86 + ./scripts/build-android.sh clean
87 +
88 +psmobile-init:
89 + ./scripts/build-android.sh init
90 +
91 +psmobile-icons:
92 + ./scripts/build-android.sh icons
93 +
94 +psmobile-override:
95 + ./scripts/build-android.sh override
96 +
97 +psmobile-keystore:
98 + ./scripts/build-android.sh keystore
99 +
100 +psmobile-build:
101 + ./scripts/build-android.sh build
102 +
103 +psmobile-open:
104 + open src-tauri/gen/android/app/build/outputs/bundle/universalRelease/
105 +
106 +# ── Development ────────────────────────────────────────────────────────────
107 +
108 +dev:
109 + pnpm tauri dev
110 +
111 +build:
112 + pnpm tauri build
113 +
114 +# ── Landing page (web) ─────────────────────────────────────────────────────
115 +# web-dev — run the Next.js landing page locally (http://localhost:3034)
116 +# web-build — production build (.next/standalone) for smbCloud nextjs-ssr
117 +# web-deploy — build + deploy to smbCloud via the smb CLI
118 +
119 +web-dev:
120 + pnpm --filter @siti/web dev
121 +
122 +web-build:
123 + pnpm --filter @siti/web build
124 +
125 +web-deploy: web-build
126 + smb deploy
127 +
128 +fmt:
129 + cargo fmt --manifest-path src-tauri/Cargo.toml --all
130 +
131 +lint:
132 + cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings
133 + pnpm tsc --noEmit
134 +
135 +clean:
136 + cargo clean --manifest-path src-tauri/Cargo.toml
NOTICE new
+18
@@ -0,0 +1,18 @@
1 +Siti AI
2 +Copyright 2026 Splitfire AB
3 +
4 +This product is licensed under the Apache License, Version 2.0 (see LICENSE).
5 +
6 +The "Siti AI" name and the Siti logos and app icons are trademarks of
7 +Splitfire AB and are NOT licensed under Apache-2.0. See TRADEMARK.md for the
8 +brand-use policy.
9 +
10 +Siti AI runs on Onde, an on-device inference engine by Splitfire AB, published
11 +separately under MIT OR Apache-2.0:
12 + https://github.com/ondeinference/onde
13 + https://crates.io/crates/onde
14 +
15 +This software depends on third-party open-source components (Tauri, React,
16 +mistralrs, candle, and others). Their licenses and copyright notices are
17 +retained in the respective dependency manifests (Cargo.toml, package.json) and
18 +lockfiles.
README.md new
+157
@@ -0,0 +1,157 @@
1 +<p align="center">
2 + <img src="assets/app-icon.png" alt="Siti AI" width="96" height="96">
3 +</p>
4 +
5 +<h1 align="center">Siti AI</h1>
6 +
7 +<p align="center">
8 + <strong>A private, on-device AI assistant for macOS, iOS, and Android.</strong><br>
9 + Your model runs locally. Your chats never leave the device.
10 +</p>
11 +
12 +<p align="center">
13 + <a href="https://apps.apple.com/se/app/siti-ai/id6780047972"><img alt="App Store" src="https://img.shields.io/badge/App_Store-live-06a561?style=flat-square&labelColor=1a1a1a"></a>
14 + <a href="https://play.google.com/store/apps/details?id=ai.siti.Siti"><img alt="Google Play" src="https://img.shields.io/badge/Google_Play-live-06a561?style=flat-square&labelColor=1a1a1a"></a>
15 + <a href="https://github.com/ondeinference/onde"><img alt="Built with Onde" src="https://img.shields.io/badge/built_with-Onde-06a561?style=flat-square&labelColor=1a1a1a"></a>
16 + <a href="LICENSE"><img alt="License: Apache-2.0" src="https://img.shields.io/badge/license-Apache_2.0-blue?style=flat-square"></a>
17 + <a href="https://github.com/sponsors/ondeinference"><img alt="Sponsor" src="https://img.shields.io/badge/♥-Sponsor-db61a2?style=flat-square"></a>
18 +</p>
19 +
20 +<p align="center">
21 + <a href="https://getsiti.5mb.app">Website</a> ·
22 + <a href="#quick-start">Quick start</a> ·
23 + <a href="#architecture">Architecture</a> ·
24 + <a href="CONTRIBUTING.md">Contributing</a> ·
25 + <a href="SECURITY.md">Security</a>
26 +</p>
27 +
28 +---
29 +
30 +Siti AI is the assistant you already know, rebuilt around privacy. It loads an
31 +open-weights language model onto your device and runs inference entirely
32 +locally — no server round-trip, no account, nothing to leak. The name is a wink
33 +at the assistant on your phone; the difference is where your words go.
34 +
35 +**Siti is also the flagship open reference app for [Onde](https://ondeinference.com)** —
36 +the on-device inference engine that powers it. If you want to see Onde driving a
37 +real, shipping app on the App Store and Google Play, this is the codebase to read.
38 +
39 +## Why it exists
40 +
41 +The device you keep closest to you should be the one that keeps your data
42 +private. Most assistants send everything you type to a remote server; Siti keeps
43 +it on your device instead. Using AI shouldn't be the price you pay to give that
44 +up.
45 +
46 +## Features
47 +
48 +- **Fully on-device inference** — chat runs locally via [Onde](https://crates.io/crates/onde): Metal-accelerated on Apple silicon, CPU on Android.
49 +- **Bring your own model** — pick from on-device open-weights models; download and swap them in-app.
50 +- **No account, no telemetry of your chats** — conversations stay on the device.
51 +- **Native and small** — Rust core + Tauri, a real app rather than a web wrapper.
52 +
53 +## Quick start
54 +
55 +**Prerequisites:** [Rust](https://rustup.rs) (stable), [pnpm](https://pnpm.io),
56 +and the [Tauri prerequisites](https://tauri.app/start/prerequisites/) for your
57 +platform (Xcode for macOS/iOS; Android SDK + NDK for Android).
58 +
59 +```bash
60 +git clone https://github.com/ondeinference/siti.git
61 +cd siti
62 +pnpm install
63 +make dev # or: pnpm tauri dev
64 +```
65 +
66 +The first launch downloads a model; after that, inference is offline. The Onde
67 +engine is pulled from crates.io — no separate SDK checkout needed. To develop
68 +against a local Onde checkout, see the commented `[patch.crates-io]` block in
69 +`src-tauri/Cargo.toml`.
70 +
71 +Common tasks:
72 +
73 +```bash
74 +make dev # start the Tauri dev server
75 +make build # debug build
76 +make fmt # cargo fmt
77 +make lint # clippy + tsc
78 +```
79 +
80 +## Architecture
81 +
82 +Siti is a thin, native shell around the Onde inference engine.
83 +
84 +```
85 +┌──────────────────────────────┐
86 +│ React + Vite UI (src/) │ chat view, model settings
87 +├──────────────────────────────┤
88 +│ Tauri command layer │ src-tauri/src/chat/command_*.rs
89 +│ (load/unload model, send, │ typed IPC between UI and Rust
90 +│ stream tokens, history) │
91 +├──────────────────────────────┤
92 +│ Onde engine (crates.io) │ GGUF load + streaming generation;
93 +│ Metal on Apple, CPU on │ Metal-accelerated on Apple silicon
94 +│ Android │
95 +└──────────────────────────────┘
96 +```
97 +
98 +Things worth reading if you're evaluating the engineering:
99 +
100 +- `src-tauri/src/chat/` — the command surface: model lifecycle, streaming token
101 + events, and history. This is the cleanest example of wiring Onde into an app.
102 +- `src-tauri/src/chat/mod.rs` — token-budget heuristics (reasoning models burn
103 + a large `<think>` budget before the visible reply begins).
104 +- `src-tauri/src/setup/` — redirecting the model cache into the App Group
105 + container so the sandbox doesn't fight the download.
106 +- `src-tauri/.cargo/config.toml` — the `+fullfp16` target-feature notes for
107 + Apple silicon fp16 SIMD. Non-obvious, and load-bearing.
108 +
109 +## Repository layout
110 +
111 +| Path | What |
112 +| ---------- | ----------------------------------------------- |
113 +| `/` | Tauri + Vite desktop/mobile app (the product) |
114 +| `src/` | React UI |
115 +| `src-tauri/` | Rust core, Tauri commands, platform config |
116 +| `web/` | Next.js marketing + legal site (getsiti.5mb.app) |
117 +
118 +## The Onde SDK
119 +
120 +The inference engine is developed separately and is open source under
121 +MIT OR Apache-2.0, with SDKs for several ecosystems:
122 +
123 +[Rust](https://crates.io/crates/onde) ·
124 +[Swift](https://github.com/ondeinference/onde-swift) ·
125 +[Kotlin Multiplatform](https://central.sonatype.com/artifact/com.ondeinference/onde-inference) ·
126 +[Flutter](https://pub.dev/packages/onde_inference) ·
127 +[React Native](https://www.npmjs.com/package/@ondeinference/react-native) ·
128 +[CLI](https://github.com/ondeinference/onde-cli) ·
129 +[Docs](https://ondeinference.com/sdk)
130 +
131 +If Siti is useful to you, the engine underneath it probably is too.
132 +
133 +## Contributing
134 +
135 +Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). We ask
136 +contributors to sign a lightweight [CLA](CLA.md) so the project can keep both
137 +its open-source and commercial licensing options open. Start with issues labeled
138 +[`good first issue`](https://github.com/ondeinference/siti/labels/good%20first%20issue).
139 +
140 +## Sponsor
141 +
142 +Siti and Onde are built by a small team ([Splitfire AB](https://ondeinference.com)).
143 +On-device AI that keeps user data private is worth funding — if you agree,
144 +[**sponsor us on GitHub**](https://github.com/sponsors/ondeinference). Sponsorship
145 +funds the engine, the SDKs, and this app.
146 +
147 +## Security & privacy
148 +
149 +Chat content is processed on-device and is not transmitted to us. To report a
150 +vulnerability, see [SECURITY.md](SECURITY.md) — please do not open a public
151 +issue for security reports.
152 +
153 +## License
154 +
155 +Code is licensed under [Apache-2.0](LICENSE). The "Siti AI" name, logos, and app
156 +icons are trademarks of Splitfire AB and are **not** covered by the code license —
157 +see [TRADEMARK.md](TRADEMARK.md).
SECURITY.md new
+58
@@ -0,0 +1,58 @@
1 +# Security Policy
2 +
3 +Siti AI is a privacy product. We take security and privacy reports seriously and
4 +appreciate responsible disclosure.
5 +
6 +## Privacy & threat model
7 +
8 +Siti is designed so that **chat content is processed entirely on-device**:
9 +
10 +- The language model runs locally via the [Onde](https://ondeinference.com)
11 + engine. Prompts and generated text are **not** sent to Splitfire AB or any
12 + third party for inference.
13 +- Model files are downloaded from public model hosts (e.g. Hugging Face) on
14 + first use and cached locally. That download is the primary outbound network
15 + activity related to the model.
16 +- There is no account and no server-side chat storage.
17 +
18 +What this policy is most interested in:
19 +
20 +- Any path by which chat content, prompts, or user data leave the device
21 + unexpectedly.
22 +- Sandbox escapes, insecure IPC between the UI and the Rust core, or unsafe
23 + handling of downloaded model files.
24 +- Supply-chain or build-integrity issues (dependency, signing, or release).
25 +
26 +## Reporting a vulnerability
27 +
28 +**Please do not open a public GitHub issue for security reports.**
29 +
30 +Preferred: use **GitHub → Security → "Report a vulnerability"** (private
31 +advisory) on this repository.
32 +
33 +Alternatively, email **privacy@getsiti.5mb.app**. If you'd like to encrypt,
34 +request our PGP key in your first message.
35 +
36 +Please include:
37 +
38 +- A description of the issue and its impact.
39 +- Steps to reproduce (proof-of-concept if possible).
40 +- Affected version / platform (macOS, iOS, Android) and build.
41 +
42 +## What to expect
43 +
44 +- **Acknowledgement:** within 3 business days.
45 +- **Assessment & triage:** we'll confirm the issue and share our planned fix and
46 + timeline.
47 +- **Disclosure:** coordinated. We'll credit you in the advisory unless you prefer
48 + to remain anonymous.
49 +
50 +## Scope
51 +
52 +In scope: this repository (the Siti app) and its build/release pipeline.
53 +
54 +The inference engine lives in its own repo — report engine issues at
55 +<https://github.com/ondeinference/onde>. Model weights themselves are
56 +third-party; report those to their respective publishers.
57 +
58 +Thank you for helping keep Siti users safe.
TRADEMARK.md new
+39
@@ -0,0 +1,39 @@
1 +# Trademark & Brand Policy
2 +
3 +The **source code** of Siti AI is open source under the [Apache License 2.0](LICENSE).
4 +The **brand is not.**
5 +
6 +## What Apache-2.0 covers
7 +
8 +All source code in this repository.
9 +
10 +## What it does NOT cover
11 +
12 +The following are trademarks and proprietary assets of **Splitfire AB**, and are
13 +**not** granted under the code license (see Apache-2.0 §6, "Trademarks"):
14 +
15 +- The names **"Siti"** and **"Siti AI"**
16 +- The Siti logos, wordmarks, and app icons (e.g. `assets/app-icon.png`,
17 + `src-tauri/icons/`)
18 +- App Store screenshots and marketing copy under `assets/`
19 +- The App Store listing and developer identity (Splitfire AB)
20 +
21 +## What you may do
22 +
23 +- Use, modify, and redistribute the **code** under Apache-2.0.
24 +- Say your project "uses Siti's code" or "is based on Siti AI" — accurate,
25 + descriptive references are fine.
26 +
27 +## What you may not do without written permission
28 +
29 +- Ship a fork to the App Store, Google Play, or any store **using the name
30 + "Siti" / "Siti AI"** or the Siti icons/logos.
31 +- Name your product, or imply it is endorsed by us, in a way that could confuse
32 + users into thinking it is the official Siti app.
33 +
34 +If you fork and distribute, **rename** the app and replace the icons and brand
35 +assets. Keeping the code open while reserving the brand is deliberate: it lets
36 +the community learn from and build on Siti while protecting users from
37 +impersonation.
38 +
39 +Questions about brand use: **support@getsiti.5mb.app**.
assets/app-icon.png
Binary files /dev/null and b/assets/app-icon.png differ
assets/aso/appstore/iPad-13/01-chat-2064x2752.png
Binary files /dev/null and b/assets/aso/appstore/iPad-13/01-chat-2064x2752.png differ
assets/aso/appstore/iPad-13/02-settings-2064x2752.png
Binary files /dev/null and b/assets/aso/appstore/iPad-13/02-settings-2064x2752.png differ
assets/aso/appstore/iPhone-6.5/01-chat-1242x2688.png
Binary files /dev/null and b/assets/aso/appstore/iPhone-6.5/01-chat-1242x2688.png differ
assets/aso/appstore/iPhone-6.5/02-settings-1242x2688.png
Binary files /dev/null and b/assets/aso/appstore/iPhone-6.5/02-settings-1242x2688.png differ
assets/aso/appstore/ios/ar.md new
+58
@@ -0,0 +1,58 @@
1 +# App Store iOS — العربية
2 +
3 +## Promotional Text (170 characters)
4 +
5 +تعرّف على سيتي — مساعد ذكاء اصطناعي خاص يعمل 100% على جهاز iPhone. اسأل أي شيء بدون إنترنت. بلا سحابة، بلا حساب، بلا تتبّع. شخصي للغاية، وخاص تمامًا.
6 +
7 +## Description (4,000 characters)
8 +
9 +تعرّف على سيتي — مساعد الذكاء الاصطناعي الخاص بك الذي يعمل بالكامل على جهازك.
10 +
11 +سيتي مساعد قادر وشخصي للغاية لا يرسل كلماتك أبدًا إلى السحابة. اطرح الأسئلة، واكتب الرسائل، وولّد الأفكار، ولخّص النصوص، وترجم بين اللغات، أو ناقش الأمور — كل ذلك مدعوم بنموذج ذكاء اصطناعي يعمل على الجهاز ويحافظ على خصوصية كل محادثة تمامًا.
12 +
13 +اسأل سيتي أي شيء
14 +• احصل على إجابات واضحة للأسئلة اليومية
15 +• اكتب رسائل البريد الإلكتروني والمنشورات
16 +• ولّد الأفكار وضع الخطط
17 +• لخّص النصوص الطويلة في ثوانٍ
18 +• ترجم بين اللغات
19 +• اشرح المواضيع الصعبة ببساطة
20 +• احصل على مساعدة في الكتابة والتحرير
21 +• تعلّم شيئًا جديدًا، سؤالًا تلو الآخر
22 +
23 +100% على جهازك
24 +يعمل ذكاء سيتي بالكامل على جهاز iPhone وiPad. لا تُرسل أي بيانات إلى أي خادم — محادثاتك لا تغادر جهازك أبدًا. بعد تنزيل النموذج لمرة واحدة (~1–2 جيجابايت)، يعمل سيتي بدون اتصال بالإنترنت تمامًا.
25 +
26 +خاص بالتصميم
27 +• بلا حساب، بلا تسجيل دخول
28 +• بلا معالجة سحابية
29 +• بلا تتبّع، بلا إعلانات
30 +• بلا جمع بيانات
31 +محادثاتك لك وحدك.
32 +
33 +يعمل في أي مكان وأي وقت
34 +في الطائرة، في المترو، بعيدًا عن الشبكة — سيتي جاهز دائمًا. ولأن كل شيء يحدث على جهازك، تحصل على إجابات سريعة وخاصة أينما كنت.
35 +
36 +سريع وأصلي
37 +مصمم كتطبيق أصلي ومُحسَّن لمعالجات Apple silicon، يقدّم سيتي استجابات سريعة وسلسة تنسجم تمامًا مع iOS.
38 +
39 +لماذا سيتي؟
40 +الاسم لمسة مرحة للمساعدين الذين نعرفهم جميعًا — لكن سيتي يقلب المعادلة: بدلًا من السحابة، يبقى كل شيء معك. شخصي للغاية. وخاص تمامًا.
41 +
42 +الميزات الرئيسية
43 +- محادثة ذكاء اصطناعي خاصة على الجهاز
44 +- يعمل بدون إنترنت بالكامل بعد تنزيل النموذج
45 +- بلا حساب أو اشتراك
46 +- بلا تتبّع أو جمع بيانات
47 +- تصميم نظيف وخالٍ من التشتيت
48 +- استجابات سريعة بدعم من Apple silicon
49 +
50 +نزّل سيتي اليوم وتعرّف على المساعد الذي يبقي عالمك لك وحدك.
51 +
52 +## Keywords (100 characters)
53 +
54 +مساعد ذكاء اصطناعي,ذكاء خاص,بدون انترنت,محادثة ذكية,خصوصية,مساعد,على الجهاز
55 +
56 +## What's New in This Version (4,000 characters)
57 +
58 +إصلاحات للأخطاء وتحسينات في الأداء.
assets/aso/appstore/ios/en-US.md new
+58
@@ -0,0 +1,58 @@
1 +# App Store iOS — English US
2 +
3 +## Promotional Text (170 characters)
4 +
5 +Meet Siti — your private AI assistant that runs 100% on iPhone. Ask anything, fully offline. No cloud, no account, no tracking. Profoundly personal and private.
6 +
7 +## Description (4,000 characters)
8 +
9 +Meet Siti — your private AI assistant, running entirely on your device.
10 +
11 +Siti is a profoundly capable, profoundly personal assistant that never sends your words to the cloud. Ask questions, draft messages, brainstorm ideas, summarize text, translate languages, or just talk things through — all powered by an on-device AI model that keeps every conversation completely private.
12 +
13 +ASK SITI ANYTHING
14 +• Get clear answers to everyday questions
15 +• Draft emails, messages, and posts
16 +• Brainstorm ideas and make plans
17 +• Summarize long text in seconds
18 +• Translate between languages
19 +• Explain difficult topics simply
20 +• Get help with writing and editing
21 +• Learn something new, one question at a time
22 +
23 +100% ON YOUR DEVICE
24 +Siti's AI runs entirely on your iPhone and iPad. No data is ever sent to a server — your conversations never leave your device. After a one-time model download (~1–2 GB), Siti works completely offline. No internet connection required.
25 +
26 +PRIVATE BY DESIGN
27 +• No account, no sign-in
28 +• No cloud processing
29 +• No tracking, no ads
30 +• No data collection
31 +Your conversations are yours alone.
32 +
33 +WORKS ANYWHERE, ANYTIME
34 +On a plane, on the subway, off the grid — Siti is always ready. Because everything happens on your device, you get fast, private answers wherever you are.
35 +
36 +FAST AND NATIVE
37 +Built as a native app and optimized for Apple silicon, Siti delivers quick, fluid responses that feel right at home on iOS.
38 +
39 +WHY SITI?
40 +The name is a playful nod to the assistants we all know — but Siti flips the script: instead of the cloud, everything stays with you. Profoundly personal. Entirely private.
41 +
42 +KEY FEATURES
43 +- Private on-device AI chat
44 +- Works fully offline after model download
45 +- No account or subscription required
46 +- No tracking or data collection
47 +- Clean, distraction-free design
48 +- Fast responses powered by Apple silicon
49 +
50 +Download Siti today and meet the assistant that keeps your world to itself.
51 +
52 +## Keywords (100 characters)
53 +
54 +ai assistant,private ai,offline ai,on-device,chatbot,ai chat,local llm,privacy,assistant,offline
55 +
56 +## What's New in This Version (4,000 characters)
57 +
58 +Bug fixes and performance improvements.
assets/aso/appstore/ios/id.md new
+58
@@ -0,0 +1,58 @@
1 +# App Store iOS — Bahasa Indonesia
2 +
3 +## Promotional Text (170 characters)
4 +
5 +Kenalkan Siti — asisten AI pribadi yang berjalan 100% di iPhone. Tanya apa saja, offline penuh. Tanpa cloud, akun, atau pelacakan. Sangat personal & privat.
6 +
7 +## Description (4,000 characters)
8 +
9 +Kenalkan Siti — asisten AI pribadimu yang berjalan sepenuhnya di perangkatmu.
10 +
11 +Siti adalah asisten yang sangat mampu dan personal yang tidak pernah mengirim kata-katamu ke cloud. Ajukan pertanyaan, susun pesan, kembangkan ide, ringkas teks, terjemahkan bahasa, atau sekadar berdiskusi — semuanya didukung oleh model AI di perangkat yang menjaga setiap percakapan tetap sepenuhnya privat.
12 +
13 +TANYA SITI APA SAJA
14 +• Dapatkan jawaban jelas untuk pertanyaan sehari-hari
15 +• Susun email, pesan, dan postingan
16 +• Kembangkan ide dan buat rencana
17 +• Ringkas teks panjang dalam hitungan detik
18 +• Terjemahkan antarbahasa
19 +• Jelaskan topik sulit dengan sederhana
20 +• Dapatkan bantuan menulis dan menyunting
21 +• Pelajari hal baru, satu pertanyaan setiap kali
22 +
23 +100% DI PERANGKATMU
24 +AI Siti berjalan sepenuhnya di iPhone dan iPad-mu. Tidak ada data yang pernah dikirim ke server — percakapanmu tidak pernah meninggalkan perangkatmu. Setelah pengunduhan model sekali saja (~1–2 GB), Siti berfungsi sepenuhnya offline. Tanpa koneksi internet.
25 +
26 +PRIVAT SEJAK AWAL
27 +• Tanpa akun, tanpa login
28 +• Tanpa pemrosesan cloud
29 +• Tanpa pelacakan, tanpa iklan
30 +• Tanpa pengumpulan data
31 +Percakapanmu hanya milikmu.
32 +
33 +BERFUNGSI DI MANA SAJA, KAPAN SAJA
34 +Di pesawat, di kereta bawah tanah, jauh dari jaringan — Siti selalu siap. Karena semuanya terjadi di perangkatmu, kamu mendapat jawaban cepat dan privat di mana pun kamu berada.
35 +
36 +CEPAT DAN NATIF
37 +Dibuat sebagai aplikasi natif dan dioptimalkan untuk Apple silicon, Siti memberikan respons cepat dan mulus yang terasa pas di iOS.
38 +
39 +MENGAPA SITI?
40 +Namanya adalah sentuhan jenaka untuk asisten yang kita semua kenal — tetapi Siti membaliknya: alih-alih ke cloud, semuanya tetap bersamamu. Sangat personal. Sepenuhnya privat.
41 +
42 +FITUR UTAMA
43 +- Obrolan AI privat di perangkat
44 +- Berfungsi sepenuhnya offline setelah unduhan model
45 +- Tanpa akun atau langganan
46 +- Tanpa pelacakan atau pengumpulan data
47 +- Desain bersih dan bebas gangguan
48 +- Respons cepat didukung Apple silicon
49 +
50 +Unduh Siti hari ini dan kenali asisten yang menjaga duniamu untuk dirimu sendiri.
51 +
52 +## Keywords (100 characters)
53 +
54 +asisten ai,ai privat,ai offline,chatbot,obrolan ai,asisten,privasi,di perangkat,ai lokal
55 +
56 +## What's New in This Version (4,000 characters)
57 +
58 +Perbaikan bug dan peningkatan performa.
assets/aso/appstore/ios/sv.md new
+58
@@ -0,0 +1,58 @@
1 +# App Store iOS — Svenska
2 +
3 +## Promotional Text (170 characters)
4 +
5 +Möt Siti — din privata AI-assistent som körs 100 % på iPhone. Fråga vad som helst, helt offline. Inget moln, inget konto, ingen spårning. Djupt personlig, helt privat.
6 +
7 +## Description (4,000 characters)
8 +
9 +Möt Siti — din privata AI-assistent som körs helt på din enhet.
10 +
11 +Siti är en djupt kapabel och personlig assistent som aldrig skickar dina ord till molnet. Ställ frågor, skriv meddelanden, brainstorma idéer, sammanfatta text, översätt språk eller bara resonera dig fram — allt drivs av en AI-modell på enheten som håller varje konversation helt privat.
12 +
13 +FRÅGA SITI VAD SOM HELST
14 +• Få tydliga svar på vardagliga frågor
15 +• Skriv e-post, meddelanden och inlägg
16 +• Brainstorma idéer och gör upp planer
17 +• Sammanfatta lång text på sekunder
18 +• Översätt mellan språk
19 +• Förklara svåra ämnen enkelt
20 +• Få hjälp med att skriva och redigera
21 +• Lär dig något nytt, en fråga i taget
22 +
23 +100 % PÅ DIN ENHET
24 +Sitis AI körs helt på din iPhone och iPad. Ingen data skickas någonsin till en server — dina konversationer lämnar aldrig din enhet. Efter en engångsnedladdning av modellen (~1–2 GB) fungerar Siti helt offline. Ingen internetanslutning krävs.
25 +
26 +PRIVAT FRÅN GRUNDEN
27 +• Inget konto, ingen inloggning
28 +• Ingen molnbearbetning
29 +• Ingen spårning, inga annonser
30 +• Ingen datainsamling
31 +Dina konversationer är bara dina.
32 +
33 +FUNGERAR VAR OCH NÄR SOM HELST
34 +På ett flygplan, på tunnelbanan, utanför nätet — Siti är alltid redo. Eftersom allt sker på din enhet får du snabba, privata svar var du än är.
35 +
36 +SNABB OCH NATIV
37 +Byggd som en nativ app och optimerad för Apple-kisel levererar Siti snabba, flytande svar som känns hemma på iOS.
38 +
39 +VARFÖR SITI?
40 +Namnet är en lekfull blinkning till assistenterna vi alla känner — men Siti vänder på det: istället för molnet stannar allt hos dig. Djupt personlig. Helt privat.
41 +
42 +VIKTIGA FUNKTIONER
43 +- Privat AI-chatt på enheten
44 +- Fungerar helt offline efter nedladdning av modellen
45 +- Inget konto eller abonnemang krävs
46 +- Ingen spårning eller datainsamling
47 +- Ren, distraktionsfri design
48 +- Snabba svar drivna av Apple-kisel
49 +
50 +Ladda ner Siti idag och möt assistenten som håller din värld för sig själv.
51 +
52 +## Keywords (100 characters)
53 +
54 +ai-assistent,privat ai,offline ai,chatbot,ai-chatt,assistent,integritet,på enheten,lokal ai
55 +
56 +## What's New in This Version (4,000 characters)
57 +
58 +Buggfixar och prestandaförbättringar.
assets/aso/appstore/macos/ar.md new
+54
@@ -0,0 +1,54 @@
1 +# App Store macOS — العربية
2 +
3 +## Promotional Text (170 characters)
4 +
5 +تعرّف على سيتي — مساعد ذكاء اصطناعي خاص يعمل 100% على جهاز Mac. اسأل أي شيء بدون إنترنت. بلا سحابة، بلا حساب، بلا تتبّع. شخصي للغاية، وخاص تمامًا.
6 +
7 +## Description (4,000 characters)
8 +
9 +تعرّف على سيتي — مساعد الذكاء الاصطناعي الخاص بك على جهاز Mac، الذي يعمل بالكامل على جهازك.
10 +
11 +سيتي مساعد قادر وشخصي للغاية لا يرسل كلماتك أبدًا إلى السحابة. اطرح الأسئلة، واكتب المستندات، وولّد الأفكار، ولخّص النصوص، وترجم بين اللغات، واكتب الأكواد البرمجية، أو ناقش الأمور — كل ذلك مدعوم بنموذج ذكاء اصطناعي يعمل على الجهاز ويحافظ على خصوصية كل محادثة تمامًا.
12 +
13 +اسأل سيتي أي شيء
14 +- احصل على إجابات واضحة للأسئلة اليومية
15 +- اكتب رسائل البريد الإلكتروني والمستندات والمنشورات
16 +- ولّد الأفكار وضع الخطط
17 +- لخّص النصوص الطويلة في ثوانٍ
18 +- ترجم بين اللغات
19 +- احصل على مساعدة في الكتابة والتحرير والبرمجة
20 +- اشرح المواضيع الصعبة ببساطة
21 +- تعلّم شيئًا جديدًا، سؤالًا تلو الآخر
22 +
23 +100% على جهاز Mac
24 +يعمل ذكاء سيتي بالكامل على جهاز Mac. لا تُرسل أي بيانات إلى أي خادم — محادثاتك لا تغادر حاسوبك أبدًا. بعد تنزيل النموذج لمرة واحدة (~1–2 جيجابايت)، يعمل سيتي بدون اتصال بالإنترنت تمامًا. استخدمه على جهاز MacBook أثناء الرحلات الجوية، في المقهى، أو في أي مكان بدون واي فاي.
25 +
26 +خاص بالتصميم
27 +على عكس المساعدين الذين يرسلون بياناتك إلى خوادم سحابية، يعمل سيتي بالكامل على جهاز Mac. بلا حساب، بلا تسجيل دخول، بلا معالجة سحابية، بلا تتبّع، بلا جمع بيانات. محادثاتك لا تغادر حاسوبك أبدًا.
28 +
29 +مصمم لجهاز Mac
30 +سيتي تطبيق macOS أصلي يستفيد بالكامل من معالجات Apple silicon لإجراء محادثات ذكاء اصطناعي سريعة وسلسة. تنسجم الواجهة النظيفة تمامًا مع جهاز Mac، ويستفيد النموذج الذي يعمل على الجهاز من عتادك لتقديم استجابات سريعة وخاصة.
31 +
32 +يعمل في أي مكان وأي وقت
33 +في الطائرة، على الطريق، بعيدًا عن الشبكة — سيتي جاهز دائمًا. ولأن كل شيء يحدث على جهاز Mac، تحصل على إجابات سريعة وخاصة أينما كنت.
34 +
35 +لماذا سيتي؟
36 +الاسم لمسة مرحة للمساعدين الذين نعرفهم جميعًا — لكن سيتي يقلب المعادلة: بدلًا من السحابة، يبقى كل شيء معك. شخصي للغاية. وخاص تمامًا.
37 +
38 +الميزات الرئيسية
39 +- محادثة ذكاء اصطناعي خاصة على الجهاز
40 +- يعمل بدون إنترنت بالكامل بعد تنزيل النموذج
41 +- بلا حساب أو اشتراك
42 +- بلا تتبّع أو جمع بيانات
43 +- تطبيق macOS أصلي مُحسَّن لمعالجات Apple silicon
44 +- تصميم نظيف وخالٍ من التشتيت
45 +
46 +نزّل سيتي اليوم وتعرّف على المساعد الذي يبقي عالمك لك وحدك.
47 +
48 +## What's New in This Version (4,000 characters)
49 +
50 +إصلاحات للأخطاء وتحسينات في الأداء.
51 +
52 +## Keywords (100 characters)
53 +
54 +مساعد ذكاء اصطناعي,ذكاء خاص,بدون انترنت,محادثة ذكية,Mac,خصوصية,مساعد
assets/aso/appstore/macos/en-US.md new
+54
@@ -0,0 +1,54 @@
1 +# App Store macOS — English (US)
2 +
3 +## Promotional Text (170 characters)
4 +
5 +Meet Siti — your private AI assistant that runs 100% on your Mac. Ask anything, fully offline. No cloud, no account, no tracking. Profoundly personal, entirely private.
6 +
7 +## Description (4,000 characters)
8 +
9 +Meet Siti — your private AI assistant for Mac, running entirely on your device.
10 +
11 +Siti is a profoundly capable, profoundly personal assistant that never sends your words to the cloud. Ask questions, draft documents, brainstorm ideas, summarize text, translate languages, write code, or just talk things through — all powered by an on-device AI model that keeps every conversation completely private.
12 +
13 +ASK SITI ANYTHING
14 +- Get clear answers to everyday questions
15 +- Draft emails, documents, and posts
16 +- Brainstorm ideas and make plans
17 +- Summarize long text in seconds
18 +- Translate between languages
19 +- Get help with writing, editing, and code
20 +- Explain difficult topics simply
21 +- Learn something new, one question at a time
22 +
23 +100% ON YOUR MAC
24 +Siti's AI runs entirely on your Mac. No data is ever sent to a server — your conversations never leave your computer. After a one-time model download (~1–2 GB), Siti works completely offline. Use it on your MacBook during flights, at a cafe, or anywhere without Wi-Fi.
25 +
26 +PRIVATE BY DESIGN
27 +Unlike assistants that send your data to cloud servers, Siti runs entirely on your Mac. No account, no sign-in, no cloud processing, no tracking, no data collection. Your conversations never leave your computer.
28 +
29 +BUILT FOR YOUR MAC
30 +Siti is a native macOS app that takes full advantage of Apple silicon for fast, fluid AI conversations. The clean interface feels right at home on your Mac, and the on-device model leverages your hardware for quick, private responses.
31 +
32 +WORKS ANYWHERE, ANYTIME
33 +On a plane, on the road, off the grid — Siti is always ready. Because everything happens on your Mac, you get fast, private answers wherever you are.
34 +
35 +WHY SITI?
36 +The name is a playful nod to the assistants we all know — but Siti flips the script: instead of the cloud, everything stays with you. Profoundly personal. Entirely private.
37 +
38 +KEY FEATURES
39 +- Private on-device AI chat
40 +- Works fully offline after model download
41 +- No account or subscription required
42 +- No tracking or data collection
43 +- Native macOS app optimized for Apple silicon
44 +- Clean, distraction-free design
45 +
46 +Download Siti today and meet the assistant that keeps your world to itself.
47 +
48 +## What's New in This Version (4,000 characters)
49 +
50 +Bug fixes and performance improvements.
51 +
52 +## Keywords (100 characters)
53 +
54 +ai assistant,private ai,offline ai,chatbot,ai chat,local llm,mac,privacy,assistant,on-device
assets/aso/appstore/macos/id.md new
+54
@@ -0,0 +1,54 @@
1 +# App Store macOS — Bahasa Indonesia
2 +
3 +## Promotional Text (170 characters)
4 +
5 +Kenalkan Siti — asisten AI pribadi yang berjalan 100% di Mac. Tanya apa saja, offline penuh. Tanpa cloud, akun, atau pelacakan. Sangat personal & privat.
6 +
7 +## Description (4,000 characters)
8 +
9 +Kenalkan Siti — asisten AI pribadimu untuk Mac, yang berjalan sepenuhnya di perangkatmu.
10 +
11 +Siti adalah asisten yang sangat mampu dan personal yang tidak pernah mengirim kata-katamu ke cloud. Ajukan pertanyaan, susun dokumen, kembangkan ide, ringkas teks, terjemahkan bahasa, tulis kode, atau sekadar berdiskusi — semuanya didukung oleh model AI di perangkat yang menjaga setiap percakapan tetap sepenuhnya privat.
12 +
13 +TANYA SITI APA SAJA
14 +- Dapatkan jawaban jelas untuk pertanyaan sehari-hari
15 +- Susun email, dokumen, dan postingan
16 +- Kembangkan ide dan buat rencana
17 +- Ringkas teks panjang dalam hitungan detik
18 +- Terjemahkan antarbahasa
19 +- Dapatkan bantuan menulis, menyunting, dan membuat kode
20 +- Jelaskan topik sulit dengan sederhana
21 +- Pelajari hal baru, satu pertanyaan setiap kali
22 +
23 +100% DI MAC-MU
24 +AI Siti berjalan sepenuhnya di Mac-mu. Tidak ada data yang pernah dikirim ke server — percakapanmu tidak pernah meninggalkan komputermu. Setelah pengunduhan model sekali saja (~1–2 GB), Siti berfungsi sepenuhnya offline. Gunakan di MacBook saat penerbangan, di kafe, atau di mana saja tanpa Wi-Fi.
25 +
26 +PRIVAT SEJAK AWAL
27 +Tidak seperti asisten yang mengirim datamu ke server cloud, Siti berjalan sepenuhnya di Mac-mu. Tanpa akun, tanpa login, tanpa pemrosesan cloud, tanpa pelacakan, tanpa pengumpulan data. Percakapanmu tidak pernah meninggalkan komputermu.
28 +
29 +DIBUAT UNTUK MAC-MU
30 +Siti adalah aplikasi macOS natif yang memanfaatkan sepenuhnya Apple silicon untuk percakapan AI yang cepat dan mulus. Antarmuka yang bersih terasa pas di Mac-mu, dan model di perangkat memanfaatkan perangkat kerasmu untuk respons cepat dan privat.
31 +
32 +BERFUNGSI DI MANA SAJA, KAPAN SAJA
33 +Di pesawat, di perjalanan, jauh dari jaringan — Siti selalu siap. Karena semuanya terjadi di Mac-mu, kamu mendapat jawaban cepat dan privat di mana pun kamu berada.
34 +
35 +MENGAPA SITI?
36 +Namanya adalah sentuhan jenaka untuk asisten yang kita semua kenal — tetapi Siti membaliknya: alih-alih ke cloud, semuanya tetap bersamamu. Sangat personal. Sepenuhnya privat.
37 +
38 +FITUR UTAMA
39 +- Obrolan AI privat di perangkat
40 +- Berfungsi sepenuhnya offline setelah unduhan model
41 +- Tanpa akun atau langganan
42 +- Tanpa pelacakan atau pengumpulan data
43 +- Aplikasi macOS natif yang dioptimalkan untuk Apple silicon
44 +- Desain bersih dan bebas gangguan
45 +
46 +Unduh Siti hari ini dan kenali asisten yang menjaga duniamu untuk dirimu sendiri.
47 +
48 +## What's New in This Version (4,000 characters)
49 +
50 +Perbaikan bug dan peningkatan performa.
51 +
52 +## Keywords (100 characters)
53 +
54 +asisten ai,ai privat,ai offline,chatbot,obrolan ai,mac,privasi,asisten,di perangkat
assets/aso/appstore/macos/sv.md new
+54
@@ -0,0 +1,54 @@
1 +# App Store macOS — Svenska
2 +
3 +## Promotional Text (170 characters)
4 +
5 +Möt Siti — din privata AI-assistent som körs 100 % på din Mac. Fråga vad som helst, helt offline. Inget moln, inget konto, ingen spårning. Djupt personlig, helt privat.
6 +
7 +## Description (4,000 characters)
8 +
9 +Möt Siti — din privata AI-assistent för Mac, som körs helt på din enhet.
10 +
11 +Siti är en djupt kapabel och personlig assistent som aldrig skickar dina ord till molnet. Ställ frågor, skriv dokument, brainstorma idéer, sammanfatta text, översätt språk, skriv kod eller bara resonera dig fram — allt drivs av en AI-modell på enheten som håller varje konversation helt privat.
12 +
13 +FRÅGA SITI VAD SOM HELST
14 +- Få tydliga svar på vardagliga frågor
15 +- Skriv e-post, dokument och inlägg
16 +- Brainstorma idéer och gör upp planer
17 +- Sammanfatta lång text på sekunder
18 +- Översätt mellan språk
19 +- Få hjälp med att skriva, redigera och koda
20 +- Förklara svåra ämnen enkelt
21 +- Lär dig något nytt, en fråga i taget
22 +
23 +100 % PÅ DIN MAC
24 +Sitis AI körs helt på din Mac. Ingen data skickas någonsin till en server — dina konversationer lämnar aldrig din dator. Efter en engångsnedladdning av modellen (~1–2 GB) fungerar Siti helt offline. Använd den på din MacBook under flygresor, på ett kafé eller var som helst utan Wi-Fi.
25 +
26 +PRIVAT FRÅN GRUNDEN
27 +Till skillnad från assistenter som skickar dina data till molnservrar körs Siti helt på din Mac. Inget konto, ingen inloggning, ingen molnbearbetning, ingen spårning, ingen datainsamling. Dina konversationer lämnar aldrig din dator.
28 +
29 +BYGGD FÖR DIN MAC
30 +Siti är en nativ macOS-app som drar full nytta av Apple-kisel för snabba, flytande AI-konversationer. Det rena gränssnittet känns hemma på din Mac, och modellen på enheten utnyttjar din hårdvara för snabba, privata svar.
31 +
32 +FUNGERAR VAR OCH NÄR SOM HELST
33 +På ett flygplan, på vägen, utanför nätet — Siti är alltid redo. Eftersom allt sker på din Mac får du snabba, privata svar var du än är.
34 +
35 +VARFÖR SITI?
36 +Namnet är en lekfull blinkning till assistenterna vi alla känner — men Siti vänder på det: istället för molnet stannar allt hos dig. Djupt personlig. Helt privat.
37 +
38 +VIKTIGA FUNKTIONER
39 +- Privat AI-chatt på enheten
40 +- Fungerar helt offline efter nedladdning av modellen
41 +- Inget konto eller abonnemang krävs
42 +- Ingen spårning eller datainsamling
43 +- Nativ macOS-app optimerad för Apple-kisel
44 +- Ren, distraktionsfri design
45 +
46 +Ladda ner Siti idag och möt assistenten som håller din värld för sig själv.
47 +
48 +## What's New in This Version (4,000 characters)
49 +
50 +Buggfixar och prestandaförbättringar.
51 +
52 +## Keywords (100 characters)
53 +
54 +ai-assistent,privat ai,offline ai,chatbot,ai-chatt,mac,integritet,assistent,på enheten
assets/aso/appstore/shared.md new
+21
@@ -0,0 +1,21 @@
1 +# Shared App Information
2 +
3 +## English US
4 +
5 +Siti AI
6 +Your private on-device AI
7 +
8 +## Swedish
9 +
10 +Siti AI
11 +Din privata AI – på enheten
12 +
13 +## Indonesian
14 +
15 +Siti AI
16 +AI privat di perangkatmu
17 +
18 +## Arabic
19 +
20 +سيتي AI
21 +ذكاء اصطناعي خاص على جهازك
assets/aso/microsoftstore/ar.md new
+119
@@ -0,0 +1,119 @@
1 +# Microsoft Store — العربية
2 +
3 +## App Name
4 +سيتي AI
5 +
6 +## Short Description (270 characters)
7 +سيتي مساعد ذكاء اصطناعي خاص يعمل بالكامل على جهاز الكمبيوتر. اطرح الأسئلة، اكتب النصوص، لخّص، ترجم، ولّد الأفكار والمزيد — كل ذلك مدعوم بنموذج ذكاء اصطناعي على الجهاز. لا تُرسل بيانات إلى الخوادم. بلا حساب، بلا تتبّع. يعمل بدون إنترنت بعد تنزيل واحد.
8 +
9 +## Description (10,000 characters)
10 +تعرّف على سيتي — مساعد الذكاء الاصطناعي الخاص بك على نظام Windows، الذي يعمل بالكامل على جهازك.
11 +
12 +سيتي مساعد قادر وشخصي للغاية لا يرسل كلماتك أبدًا إلى السحابة. اطرح الأسئلة، واكتب المستندات، وولّد الأفكار، ولخّص النصوص، وترجم بين اللغات، واكتب الأكواد البرمجية، أو ناقش الأمور. كل محادثة مدعومة بنموذج ذكاء اصطناعي يعمل على الجهاز ويحافظ على خصوصية كلماتك تمامًا.
13 +
14 +الاسم لمسة مرحة للمساعدين الصوتيين الذين نعرفهم جميعًا — لكن سيتي يقلب المعادلة. بدلًا من إرسال طلباتك إلى السحابة، يحدث كل شيء على جهازك الخاص. شخصي للغاية. وخاص تمامًا.
15 +
16 +
17 +اسأل سيتي أي شيء
18 +
19 +سيتي مساعد متعدد الاستخدامات يتكيف مع كل ما تحتاجه:
20 +
21 +- احصل على إجابات واضحة للأسئلة اليومية
22 +- اكتب رسائل البريد الإلكتروني والمستندات والرسائل والمنشورات
23 +- ولّد الأفكار والأسماء والخطط
24 +- لخّص المقالات أو الملاحظات الطويلة في ثوانٍ
25 +- ترجم بين اللغات
26 +- اشرح المواضيع الصعبة أو التقنية ببساطة
27 +- احصل على مساعدة في الكتابة والتحرير والتدقيق
28 +- عالج أسئلة الأكواد والبرمجة
29 +- تعلّم شيئًا جديدًا، سؤالًا تلو الآخر
30 +
31 +كل ما قد تسأله لمساعد سحابي يمكنك سؤاله لسيتي — دون أن تغادر كلماتك حاسوبك أبدًا.
32 +
33 +
34 +100% على جهازك
35 +
36 +يعمل ذكاء سيتي بالكامل على جهاز Windows لديك باستخدام نموذج لغوي على الجهاز. لا تُرسل أي بيانات محادثة إلى خوادم خارجية. لا توجد معالجة سحابية. بعد تنزيل النموذج لمرة واحدة (حوالي 1-2 جيجابايت)، يعمل سيتي بدون اتصال بالإنترنت. هذا يعني أنه يمكنك استخدام سيتي على حاسوبك المحمول أثناء الرحلات الجوية أو التنقل أو في أي مكان بدون واي فاي — وتبقى محادثاتك على جهازك.
37 +
38 +
39 +خاص بالتصميم
40 +
41 +خصوصيتك ليست فكرة لاحقة — إنها الهدف بالكامل. على عكس المساعدين الذين يرسلون كل ما تكتبه إلى خوادم بعيدة، يحتفظ سيتي بمحادثاتك على جهازك:
42 +
43 +- بلا حساب وبلا تسجيل دخول
44 +- بلا معالجة سحابية
45 +- بلا تتبّع وبلا إعلانات
46 +- بلا جمع بيانات
47 +- محادثاتك لا تغادر جهازك أبدًا
48 +
49 +ما تقوله لسيتي لك وحدك.
50 +
51 +
52 +يعمل في أي مكان وأي وقت
53 +
54 +لأن كل شيء يحدث على جهازك، يكون سيتي جاهزًا دائمًا — متصلًا أو غير متصل. في الطائرة، في كوخ ناءٍ، أو خلف جدار حماية صارم، تحصل على إجابات سريعة وخاصة أينما كنت. لا اتصال، لا مشكلة.
55 +
56 +
57 +سريع وأصلي
58 +
59 +سيتي مصمم كتطبيق أصلي حديث ومُحسَّن للعتاد الحالي، ويقدّم استجابات سريعة وسلسة. تحافظ الواجهة النظيفة الخالية من التشتيت على تركيزك في محادثتك.
60 +
61 +
62 +الميزات الرئيسية
63 +
64 +- مساعد محادثة ذكاء اصطناعي خاص على الجهاز
65 +- يعمل بدون إنترنت بالكامل بعد تنزيل النموذج الأولي
66 +- اسأل أي شيء: أسئلة، كتابة، تلخيص، ترجمة، أفكار، وأكواد
67 +- بلا حساب أو اشتراك
68 +- بلا تتبّع، بلا إعلانات، بلا جمع بيانات
69 +- تصميم نظيف وخالٍ من التشتيت
70 +- خفيف وسريع — مبني بتقنية أصلية حديثة
71 +
72 +
73 +مصمم للجميع
74 +
75 +صُمِّم سيتي لكل من يريد مساعدًا قادرًا دون التخلي عن خصوصيته:
76 +
77 +- المستخدمون المهتمون بالخصوصية الذين لا يريدون بياناتهم في السحابة
78 +- المحترفون الذين يتعاملون مع معلومات حساسة أو سرية
79 +- المسافرون والمتنقلون الذين غالبًا ما يكونون دون اتصال
80 +- الطلاب والمتعلمون مدى الحياة
81 +- الكتّاب والباحثون والمبدعون
82 +- المطورون الباحثون عن مساعد سريع على الجهاز
83 +- كل من يشعر بالفضول لتشغيل الذكاء الاصطناعي محليًا
84 +
85 +
86 +الخصوصية والأمان
87 +
88 +خصوصيتك مهمة. يعمل مساعد سيتي بالكامل على جهاز Windows لديك باستخدام نموذج لغوي على الجهاز. لا تُرسل أي بيانات محادثة إلى خوادم خارجية. لا توجد معالجة سحابية. كلماتك لك وحدك.
89 +
90 +
91 +شخصي للغاية. وخاص تمامًا.
92 +
93 +نزّل سيتي اليوم وتعرّف على المساعد الذي يبقي عالمك لك وحدك.
94 +
95 +## What's New (1,500 characters)
96 +تعرّف على سيتي — مساعد الذكاء الاصطناعي الخاص على جهازك، الآن على نظام Windows.
97 +
98 +جديد: محادثة ذكاء اصطناعي خاصة
99 +اسأل سيتي أي شيء — أسئلة، كتابة، تلخيص، ترجمة، أفكار، وأكواد. مدعوم بنموذج ذكاء اصطناعي على الجهاز يعمل بالكامل على جهازك، دون إرسال أي بيانات إلى أي خادم. محادثاتك لا تغادر حاسوبك أبدًا.
100 +
101 +جديد: يعمل بدون إنترنت بالكامل
102 +بعد تنزيل النموذج لمرة واحدة، تحدّث مع سيتي بدون اتصال بالإنترنت. مثالي للرحلات الجوية أو التنقل أو في أي مكان بدون واي فاي.
103 +
104 +خاص بالتصميم
105 +بلا حساب، بلا تسجيل دخول، بلا تتبّع، بلا إعلانات، وبلا جمع بيانات. خصوصيتك هي الهدف بالكامل.
106 +
107 +مُحسَّن لنظام Windows
108 +مبني بتقنية أصلية حديثة لتجربة سريعة وخفيفة على جهاز Windows لديك.
109 +
110 +تتضمن هذه الإصدارة تحسينات في الأداء وإصلاحات للأخطاء.
111 +
112 +## Search Terms (7 terms, max 30 characters each)
113 +1. مساعد ذكاء اصطناعي
114 +2. ذكاء اصطناعي خاص
115 +3. محادثة ذكاء بدون انترنت
116 +4. ذكاء اصطناعي على الجهاز
117 +5. شات بوت ذكاء محلي
118 +6. ذكاء بدون انترنت
119 +7. شات بوت خاص
assets/aso/microsoftstore/en-US.md new
+119
@@ -0,0 +1,119 @@
1 +# Microsoft Store — English (US)
2 +
3 +## App Name
4 +Siti AI
5 +
6 +## Short Description (270 characters)
7 +Siti is a private AI assistant that runs entirely on your PC. Ask questions, draft text, summarize, translate, brainstorm, and more — all powered by an on-device AI model. No data sent to servers. No account, no tracking. Works offline after a one-time model download.
8 +
9 +## Description (10,000 characters)
10 +Meet Siti — your private AI assistant for Windows, running entirely on your device.
11 +
12 +Siti is a profoundly capable, profoundly personal assistant that never sends your words to the cloud. Ask questions, draft documents, brainstorm ideas, summarize text, translate languages, write code, or just talk things through. Every conversation is powered by an on-device AI model that keeps your words completely private.
13 +
14 +The name is a playful nod to the voice assistants we all know — but Siti flips the script. Instead of sending your requests to the cloud, everything happens on your own PC. Profoundly personal. Entirely private.
15 +
16 +
17 +ASK SITI ANYTHING
18 +
19 +Siti is a general-purpose assistant that adapts to whatever you need:
20 +
21 +- Get clear answers to everyday questions
22 +- Draft emails, documents, messages, and posts
23 +- Brainstorm ideas, names, and plans
24 +- Summarize long articles or notes in seconds
25 +- Translate between languages
26 +- Explain difficult or technical topics in simple terms
27 +- Get help with writing, editing, and proofreading
28 +- Work through code and programming questions
29 +- Learn something new, one question at a time
30 +
31 +Whatever you would ask a cloud assistant, you can ask Siti — without your words ever leaving your computer.
32 +
33 +
34 +100% ON YOUR DEVICE
35 +
36 +Siti's AI runs entirely on your Windows PC using an on-device language model. No conversation data is transmitted to external servers. No cloud processing is involved. After a one-time model download (approximately 1-2 GB), Siti works without an internet connection. This means you can use Siti on your laptop during flights, commutes, or anywhere without Wi-Fi — and your conversations stay on your machine.
37 +
38 +
39 +PRIVATE BY DESIGN
40 +
41 +Your privacy is not an afterthought — it is the entire point. Unlike assistants that send everything you type to remote servers, Siti keeps your conversations on your device:
42 +
43 +- No account and no sign-in required
44 +- No cloud processing
45 +- No tracking and no ads
46 +- No data collection
47 +- Your conversations never leave your PC
48 +
49 +What you say to Siti is yours alone.
50 +
51 +
52 +WORKS ANYWHERE, ANYTIME
53 +
54 +Because everything happens on your device, Siti is always ready — online or offline. On a plane, in a remote cabin, or behind a restrictive firewall, you get fast, private answers wherever you are. No connection, no problem.
55 +
56 +
57 +FAST AND NATIVE
58 +
59 +Siti is built as a modern native application and optimized for current hardware, delivering quick, fluid responses. The clean, distraction-free interface keeps the focus on your conversation.
60 +
61 +
62 +KEY FEATURES
63 +
64 +- Private on-device AI chat assistant
65 +- Works fully offline after the initial model download
66 +- Ask anything: questions, writing, summaries, translations, ideas, and code
67 +- No account or subscription required
68 +- No tracking, no ads, no data collection
69 +- Clean, distraction-free design
70 +- Lightweight and fast — built with modern native technology
71 +
72 +
73 +BUILT FOR EVERYONE
74 +
75 +Siti is designed for anyone who wants a capable assistant without giving up their privacy:
76 +
77 +- Privacy-conscious users who do not want their data in the cloud
78 +- Professionals handling sensitive or confidential information
79 +- Travelers and commuters who are often offline
80 +- Students and lifelong learners
81 +- Writers, researchers, and creators
82 +- Developers looking for a quick on-device helper
83 +- Anyone curious about running AI locally
84 +
85 +
86 +PRIVACY AND SECURITY
87 +
88 +Your privacy matters. The Siti assistant runs entirely on your Windows PC using an on-device language model. No conversation data is transmitted to external servers. No cloud processing is involved. Your words are yours alone.
89 +
90 +
91 +Profoundly personal. Entirely private.
92 +
93 +Download Siti today and meet the assistant that keeps your world to itself.
94 +
95 +## What's New (1,500 characters)
96 +Meet Siti — your private, on-device AI assistant, now on Windows.
97 +
98 +NEW: Private AI Chat
99 +Ask Siti anything — questions, writing, summaries, translations, ideas, and code. Powered by an on-device AI model that runs entirely on your PC, with no data sent to any server. Your conversations never leave your computer.
100 +
101 +NEW: Fully Offline
102 +After a one-time model download, chat with Siti without an internet connection. Perfect for flights, commutes, or anywhere without Wi-Fi.
103 +
104 +PRIVATE BY DESIGN
105 +No account, no sign-in, no tracking, no ads, and no data collection. Your privacy is the entire point.
106 +
107 +WINDOWS OPTIMIZED
108 +Built with modern native technology for a fast, lightweight experience on your Windows PC.
109 +
110 +Performance improvements and bug fixes included in this release.
111 +
112 +## Search Terms (7 terms, max 30 characters each)
113 +1. AI assistant
114 +2. private AI
115 +3. offline AI chat
116 +4. on-device AI
117 +5. local AI chatbot
118 +6. AI without internet
119 +7. private chatbot
assets/aso/microsoftstore/id.md new
+119
@@ -0,0 +1,119 @@
1 +# Microsoft Store — Bahasa Indonesia
2 +
3 +## App Name
4 +Siti AI
5 +
6 +## Short Description (270 characters)
7 +Siti adalah asisten AI privat yang berjalan sepenuhnya di PC-mu. Ajukan pertanyaan, susun teks, ringkas, terjemahkan, dan lainnya — didukung model AI di perangkat. Tidak ada data dikirim ke server. Tanpa akun, tanpa pelacakan. Bekerja offline setelah unduhan model.
8 +
9 +## Description (10,000 characters)
10 +Kenalkan Siti — asisten AI pribadimu untuk Windows, yang berjalan sepenuhnya di perangkatmu.
11 +
12 +Siti adalah asisten yang sangat mampu dan personal yang tidak pernah mengirim kata-katamu ke cloud. Ajukan pertanyaan, susun dokumen, kembangkan ide, ringkas teks, terjemahkan bahasa, tulis kode, atau sekadar berdiskusi. Setiap percakapan didukung oleh model AI di perangkat yang menjaga kata-katamu tetap sepenuhnya privat.
13 +
14 +Namanya adalah sentuhan jenaka untuk asisten suara yang kita semua kenal — tetapi Siti membaliknya. Alih-alih mengirim permintaanmu ke cloud, semuanya terjadi di PC-mu sendiri. Sangat personal. Sepenuhnya privat.
15 +
16 +
17 +TANYA SITI APA SAJA
18 +
19 +Siti adalah asisten serbaguna yang menyesuaikan dengan apa pun yang kamu butuhkan:
20 +
21 +- Dapatkan jawaban jelas untuk pertanyaan sehari-hari
22 +- Susun email, dokumen, pesan, dan postingan
23 +- Kembangkan ide, nama, dan rencana
24 +- Ringkas artikel atau catatan panjang dalam hitungan detik
25 +- Terjemahkan antarbahasa
26 +- Jelaskan topik sulit atau teknis dengan sederhana
27 +- Dapatkan bantuan menulis, menyunting, dan mengoreksi
28 +- Selesaikan pertanyaan kode dan pemrograman
29 +- Pelajari hal baru, satu pertanyaan setiap kali
30 +
31 +Apa pun yang akan kamu tanyakan ke asisten cloud, bisa kamu tanyakan ke Siti — tanpa kata-katamu pernah meninggalkan komputermu.
32 +
33 +
34 +100% DI PERANGKATMU
35 +
36 +AI Siti berjalan sepenuhnya di PC Windows-mu menggunakan model bahasa di perangkat. Tidak ada data percakapan yang dikirim ke server eksternal. Tidak ada pemrosesan cloud. Setelah pengunduhan model sekali saja (sekitar 1-2 GB), Siti berfungsi tanpa koneksi internet. Artinya kamu bisa menggunakan Siti di laptop saat penerbangan, perjalanan, atau di mana saja tanpa Wi-Fi — dan percakapanmu tetap di mesinmu.
37 +
38 +
39 +PRIVAT SEJAK AWAL
40 +
41 +Privasimu bukan renungan belakangan — itu seluruh intinya. Tidak seperti asisten yang mengirim semua yang kamu ketik ke server jarak jauh, Siti menyimpan percakapanmu di perangkatmu:
42 +
43 +- Tanpa akun dan tanpa login
44 +- Tanpa pemrosesan cloud
45 +- Tanpa pelacakan dan tanpa iklan
46 +- Tanpa pengumpulan data
47 +- Percakapanmu tidak pernah meninggalkan PC-mu
48 +
49 +Apa yang kamu katakan kepada Siti hanya milikmu.
50 +
51 +
52 +BERFUNGSI DI MANA SAJA, KAPAN SAJA
53 +
54 +Karena semuanya terjadi di perangkatmu, Siti selalu siap — online atau offline. Di pesawat, di kabin terpencil, atau di balik firewall ketat, kamu mendapat jawaban cepat dan privat di mana pun kamu berada. Tanpa koneksi, tanpa masalah.
55 +
56 +
57 +CEPAT DAN NATIF
58 +
59 +Siti dibuat sebagai aplikasi natif modern dan dioptimalkan untuk perangkat keras saat ini, memberikan respons cepat dan mulus. Antarmuka yang bersih dan bebas gangguan menjaga fokus pada percakapanmu.
60 +
61 +
62 +FITUR UTAMA
63 +
64 +- Asisten obrolan AI privat di perangkat
65 +- Berfungsi sepenuhnya offline setelah unduhan model awal
66 +- Tanya apa saja: pertanyaan, menulis, ringkasan, terjemahan, ide, dan kode
67 +- Tanpa akun atau langganan
68 +- Tanpa pelacakan, tanpa iklan, tanpa pengumpulan data
69 +- Desain bersih dan bebas gangguan
70 +- Ringan dan cepat — dibuat dengan teknologi natif modern
71 +
72 +
73 +DIBUAT UNTUK SEMUA ORANG
74 +
75 +Siti dirancang untuk siapa saja yang menginginkan asisten andal tanpa mengorbankan privasi:
76 +
77 +- Pengguna yang peduli privasi dan tidak ingin datanya di cloud
78 +- Profesional yang menangani informasi sensitif atau rahasia
79 +- Pelancong dan komuter yang sering offline
80 +- Pelajar dan pembelajar sepanjang hayat
81 +- Penulis, peneliti, dan kreator
82 +- Pengembang yang mencari bantuan cepat di perangkat
83 +- Siapa saja yang penasaran menjalankan AI secara lokal
84 +
85 +
86 +PRIVASI DAN KEAMANAN
87 +
88 +Privasimu penting. Asisten Siti berjalan sepenuhnya di PC Windows-mu menggunakan model bahasa di perangkat. Tidak ada data percakapan yang dikirim ke server eksternal. Tidak ada pemrosesan cloud. Kata-katamu hanya milikmu.
89 +
90 +
91 +Sangat personal. Sepenuhnya privat.
92 +
93 +Unduh Siti hari ini dan kenali asisten yang menjaga duniamu untuk dirimu sendiri.
94 +
95 +## What's New (1,500 characters)
96 +Kenalkan Siti — asisten AI privat di perangkatmu, kini di Windows.
97 +
98 +BARU: Obrolan AI Privat
99 +Tanya Siti apa saja — pertanyaan, menulis, ringkasan, terjemahan, ide, dan kode. Didukung model AI di perangkat yang berjalan sepenuhnya di PC-mu, tanpa data dikirim ke server mana pun. Percakapanmu tidak pernah meninggalkan komputermu.
100 +
101 +BARU: Sepenuhnya Offline
102 +Setelah pengunduhan model sekali saja, mengobrol dengan Siti tanpa koneksi internet. Sempurna untuk penerbangan, perjalanan, atau di mana saja tanpa Wi-Fi.
103 +
104 +PRIVAT SEJAK AWAL
105 +Tanpa akun, tanpa login, tanpa pelacakan, tanpa iklan, dan tanpa pengumpulan data. Privasimu adalah seluruh intinya.
106 +
107 +DIOPTIMALKAN UNTUK WINDOWS
108 +Dibuat dengan teknologi natif modern untuk pengalaman cepat dan ringan di PC Windows-mu.
109 +
110 +Peningkatan performa dan perbaikan bug disertakan dalam rilis ini.
111 +
112 +## Search Terms (7 terms, max 30 characters each)
113 +1. asisten AI
114 +2. AI privat
115 +3. obrolan AI offline
116 +4. AI di perangkat
117 +5. chatbot AI lokal
118 +6. AI tanpa internet
119 +7. chatbot privat
assets/aso/microsoftstore/sv-SE.md new
+119
@@ -0,0 +1,119 @@
1 +# Microsoft Store — Svenska
2 +
3 +## App Name
4 +Siti AI
5 +
6 +## Short Description (270 characters)
7 +Siti är en privat AI-assistent som körs helt på din dator. Ställ frågor, skriv text, sammanfatta, översätt, brainstorma med mera — allt drivs av en AI-modell på enheten. Ingen data skickas till servrar. Inget konto, ingen spårning. Fungerar offline efter nedladdning.
8 +
9 +## Description (10,000 characters)
10 +Möt Siti — din privata AI-assistent för Windows, som körs helt på din enhet.
11 +
12 +Siti är en djupt kapabel och personlig assistent som aldrig skickar dina ord till molnet. Ställ frågor, skriv dokument, brainstorma idéer, sammanfatta text, översätt språk, skriv kod eller bara resonera dig fram. Varje konversation drivs av en AI-modell på enheten som håller dina ord helt privata.
13 +
14 +Namnet är en lekfull blinkning till röstassistenterna vi alla känner — men Siti vänder på det. Istället för att skicka dina förfrågningar till molnet sker allt på din egen dator. Djupt personlig. Helt privat.
15 +
16 +
17 +FRÅGA SITI VAD SOM HELST
18 +
19 +Siti är en mångsidig assistent som anpassar sig efter vad du behöver:
20 +
21 +- Få tydliga svar på vardagliga frågor
22 +- Skriv e-post, dokument, meddelanden och inlägg
23 +- Brainstorma idéer, namn och planer
24 +- Sammanfatta långa artiklar eller anteckningar på sekunder
25 +- Översätt mellan språk
26 +- Förklara svåra eller tekniska ämnen enkelt
27 +- Få hjälp med att skriva, redigera och korrekturläsa
28 +- Arbeta med kod och programmeringsfrågor
29 +- Lär dig något nytt, en fråga i taget
30 +
31 +Allt du skulle fråga en molnassistent kan du fråga Siti — utan att dina ord någonsin lämnar din dator.
32 +
33 +
34 +100 % PÅ DIN ENHET
35 +
36 +Sitis AI körs helt på din Windows-dator med en språkmodell på enheten. Ingen konversationsdata skickas till externa servrar. Ingen molnbearbetning sker. Efter en engångsnedladdning av modellen (cirka 1-2 GB) fungerar Siti utan internetanslutning. Det betyder att du kan använda Siti på din bärbara dator under flygresor, pendling eller var som helst utan Wi-Fi — och dina konversationer stannar på din maskin.
37 +
38 +
39 +PRIVAT FRÅN GRUNDEN
40 +
41 +Din integritet är inte en eftertanke — det är hela poängen. Till skillnad från assistenter som skickar allt du skriver till fjärrservrar håller Siti dina konversationer på din enhet:
42 +
43 +- Inget konto och ingen inloggning krävs
44 +- Ingen molnbearbetning
45 +- Ingen spårning och inga annonser
46 +- Ingen datainsamling
47 +- Dina konversationer lämnar aldrig din dator
48 +
49 +Det du säger till Siti är bara ditt.
50 +
51 +
52 +FUNGERAR VAR OCH NÄR SOM HELST
53 +
54 +Eftersom allt sker på din enhet är Siti alltid redo — online eller offline. På ett flygplan, i en avlägsen stuga eller bakom en restriktiv brandvägg får du snabba, privata svar var du än är. Ingen anslutning, inga problem.
55 +
56 +
57 +SNABB OCH NATIV
58 +
59 +Siti är byggd som en modern nativ applikation och optimerad för dagens hårdvara, vilket ger snabba, flytande svar. Det rena, distraktionsfria gränssnittet håller fokus på din konversation.
60 +
61 +
62 +VIKTIGA FUNKTIONER
63 +
64 +- Privat AI-chattassistent på enheten
65 +- Fungerar helt offline efter den första nedladdningen av modellen
66 +- Fråga vad som helst: frågor, skrivande, sammanfattningar, översättningar, idéer och kod
67 +- Inget konto eller abonnemang krävs
68 +- Ingen spårning, inga annonser, ingen datainsamling
69 +- Ren, distraktionsfri design
70 +- Lätt och snabb — byggd med modern nativ teknik
71 +
72 +
73 +BYGGD FÖR ALLA
74 +
75 +Siti är gjord för alla som vill ha en kapabel assistent utan att ge upp sin integritet:
76 +
77 +- Integritetsmedvetna användare som inte vill ha sina data i molnet
78 +- Yrkesverksamma som hanterar känslig eller konfidentiell information
79 +- Resenärer och pendlare som ofta är offline
80 +- Studenter och livslånga inlärare
81 +- Skribenter, forskare och kreatörer
82 +- Utvecklare som vill ha en snabb hjälpreda på enheten
83 +- Alla som är nyfikna på att köra AI lokalt
84 +
85 +
86 +INTEGRITET OCH SÄKERHET
87 +
88 +Din integritet är viktig. Siti-assistenten körs helt på din Windows-dator med en språkmodell på enheten. Ingen konversationsdata skickas till externa servrar. Ingen molnbearbetning sker. Dina ord är bara dina.
89 +
90 +
91 +Djupt personlig. Helt privat.
92 +
93 +Ladda ner Siti idag och möt assistenten som håller din värld för sig själv.
94 +
95 +## What's New (1,500 characters)
96 +Möt Siti — din privata AI-assistent på enheten, nu på Windows.
97 +
98 +NYTT: Privat AI-chatt
99 +Fråga Siti vad som helst — frågor, skrivande, sammanfattningar, översättningar, idéer och kod. Drivs av en AI-modell på enheten som körs helt på din dator, utan att data skickas till någon server. Dina konversationer lämnar aldrig din dator.
100 +
101 +NYTT: Helt offline
102 +Efter en engångsnedladdning av modellen kan du chatta med Siti utan internetanslutning. Perfekt för flygresor, pendling eller var som helst utan Wi-Fi.
103 +
104 +PRIVAT FRÅN GRUNDEN
105 +Inget konto, ingen inloggning, ingen spårning, inga annonser och ingen datainsamling. Din integritet är hela poängen.
106 +
107 +OPTIMERAD FÖR WINDOWS
108 +Byggd med modern nativ teknik för en snabb och lätt upplevelse på din Windows-dator.
109 +
110 +Prestandaförbättringar och buggfixar ingår i den här versionen.
111 +
112 +## Search Terms (7 terms, max 30 characters each)
113 +1. AI-assistent
114 +2. privat AI
115 +3. offline AI-chatt
116 +4. AI på enheten
117 +5. lokal AI-chatbot
118 +6. AI utan internet
119 +7. privat chatbot
assets/aso/playstore/01-chat-1080x1920.png
Binary files /dev/null and b/assets/aso/playstore/01-chat-1080x1920.png differ
assets/aso/playstore/02-settings-1080x1920.png
Binary files /dev/null and b/assets/aso/playstore/02-settings-1080x1920.png differ
assets/aso/playstore/ar.md new
+74
@@ -0,0 +1,74 @@
1 +# Google Play Store — العربية
2 +
3 +## Title (30 characters)
4 +
5 +سيتي AI
6 +
7 +## Short Description (80 characters)
8 +
9 +مساعد ذكاء اصطناعي خاص 🤖 اسأل أي شيء بدون إنترنت 🔒 بلا سحابة ولا تتبّع
10 +
11 +## Full Description (4,000 characters)
12 +
13 +تعرّف على سيتي — مساعد الذكاء الاصطناعي الخاص بك الذي يعمل بالكامل على جهازك 🌼
14 +
15 +سيتي مساعد قادر وشخصي للغاية لا يرسل كلماتك أبدًا إلى السحابة. اطرح الأسئلة، واكتب الرسائل، وولّد الأفكار، ولخّص النصوص، وترجم بين اللغات، أو ناقش الأمور — كل ذلك مدعوم بنموذج ذكاء اصطناعي يعمل على الجهاز ويحافظ على خصوصية كل محادثة تمامًا.
16 +
17 +🤖 اسأل سيتي أي شيء
18 +
19 +• احصل على إجابات واضحة للأسئلة اليومية
20 +• اكتب رسائل البريد الإلكتروني والمنشورات
21 +• ولّد الأفكار وضع الخطط
22 +• لخّص النصوص الطويلة في ثوانٍ
23 +• ترجم بين اللغات
24 +• اشرح المواضيع الصعبة ببساطة
25 +• احصل على مساعدة في الكتابة والتحرير
26 +• تعلّم شيئًا جديدًا، سؤالًا تلو الآخر
27 +
28 +📱 100% على جهازك
29 +
30 +يعمل ذكاء سيتي بالكامل على هاتفك. لا تُرسل أي بيانات إلى أي خادم — محادثاتك لا تغادر جهازك أبدًا. بعد تنزيل النموذج لمرة واحدة (~1–2 جيجابايت)، يعمل سيتي بدون اتصال بالإنترنت تمامًا.
31 +
32 +🔒 خاص بالتصميم
33 +
34 +• بلا حساب، بلا تسجيل دخول
35 +• بلا معالجة سحابية
36 +• بلا تتبّع، بلا إعلانات
37 +• بلا جمع بيانات
38 +
39 +محادثاتك لك وحدك.
40 +
41 +✈️ يعمل في أي مكان وأي وقت
42 +
43 +في الطائرة، في المترو، بعيدًا عن الشبكة — سيتي جاهز دائمًا. ولأن كل شيء يحدث على جهازك، تحصل على إجابات سريعة وخاصة أينما كنت.
44 +
45 +⚡ سريع وأصلي
46 +
47 +مصمم كتطبيق أصلي ومُحسَّن للعتاد الحديث، يقدّم سيتي استجابات سريعة وسلسة.
48 +
49 +💡 لماذا سيتي؟
50 +
51 +الاسم لمسة مرحة للمساعدين الذين نعرفهم جميعًا — لكن سيتي يقلب المعادلة: بدلًا من السحابة، يبقى كل شيء معك. شخصي للغاية. وخاص تمامًا.
52 +
53 +⭐ الميزات الرئيسية
54 +
55 +• محادثة ذكاء اصطناعي خاصة على الجهاز
56 +• يعمل بدون إنترنت بالكامل بعد تنزيل النموذج
57 +• بلا حساب أو اشتراك
58 +• بلا تتبّع أو جمع بيانات
59 +• تصميم نظيف وخالٍ من التشتيت
60 +• استجابات سريعة وسلسة
61 +
62 +نزّل سيتي اليوم وتعرّف على المساعد الذي يبقي عالمك لك وحدك 🔒
63 +
64 +## What's New (500 characters)
65 +
66 +تعرّف على سيتي — مساعد الذكاء الاصطناعي الخاص على جهازك 🤖
67 +
68 +🆕 محادثة ذكاء اصطناعي خاصة — اسأل سيتي أي شيء: أسئلة، كتابة، تلخيص، ترجمة، أفكار. يعمل بالكامل على جهازك دون إرسال أي بيانات إلى أي خادم.
69 +
70 +🆕 يعمل بدون إنترنت بالكامل — بعد تنزيل النموذج لمرة واحدة، تحدّث مع سيتي بدون إنترنت. مثالي في أي مكان وأي وقت.
71 +
72 +🔒 بلا حساب، بلا تتبّع، بلا إعلانات. محادثاتك لا تغادر جهازك أبدًا.
73 +
74 +شخصي للغاية. وخاص تمامًا.
assets/aso/playstore/en-US.md new
+74
@@ -0,0 +1,74 @@
1 +# Google Play Store — English (US)
2 +
3 +## Title (30 characters)
4 +
5 +Siti AI
6 +
7 +## Short Description (80 characters)
8 +
9 +Private on-device AI 🤖 Ask anything offline 🔒 No cloud, no tracking
10 +
11 +## Full Description (4,000 characters)
12 +
13 +Meet Siti — your private AI assistant that runs entirely on your device 🌼
14 +
15 +Siti is a profoundly capable, profoundly personal assistant that never sends your words to the cloud. Ask questions, draft messages, brainstorm ideas, summarize text, translate languages, or just talk things through — all powered by an on-device AI model that keeps every conversation completely private.
16 +
17 +🤖 ASK SITI ANYTHING
18 +
19 +• Get clear answers to everyday questions
20 +• Draft emails, messages, and posts
21 +• Brainstorm ideas and make plans
22 +• Summarize long text in seconds
23 +• Translate between languages
24 +• Explain difficult topics simply
25 +• Get help with writing and editing
26 +• Learn something new, one question at a time
27 +
28 +📱 100% ON YOUR DEVICE
29 +
30 +Siti's AI runs entirely on your phone. No data is ever sent to a server — your conversations never leave your device. After a one-time model download (~1–2 GB), Siti works completely offline. No internet connection needed.
31 +
32 +🔒 PRIVATE BY DESIGN
33 +
34 +• No account, no sign-in
35 +• No cloud processing
36 +• No tracking, no ads
37 +• No data collection
38 +
39 +Your conversations are yours alone.
40 +
41 +✈️ WORKS ANYWHERE, ANYTIME
42 +
43 +On a plane, on the subway, off the grid — Siti is always ready. Because everything happens on your device, you get fast, private answers wherever you are.
44 +
45 +⚡ FAST AND NATIVE
46 +
47 +Built as a native app and optimized for modern hardware, Siti delivers quick, fluid responses.
48 +
49 +💡 WHY SITI?
50 +
51 +The name is a playful nod to the assistants we all know — but Siti flips the script: instead of the cloud, everything stays with you. Profoundly personal. Entirely private.
52 +
53 +⭐ KEY FEATURES
54 +
55 +• Private on-device AI chat
56 +• Works fully offline after model download
57 +• No account or subscription required
58 +• No tracking or data collection
59 +• Clean, distraction-free design
60 +• Fast, responsive replies
61 +
62 +Download Siti today and meet the assistant that keeps your world to itself 🔒
63 +
64 +## What's New (500 characters)
65 +
66 +Meet Siti — your private, on-device AI assistant 🤖
67 +
68 +🆕 Private AI Chat — Ask Siti anything: questions, writing, summaries, translations, ideas. Runs entirely on your device with no data sent to any server.
69 +
70 +🆕 Fully Offline — After a one-time model download, chat with Siti without internet. Perfect for anywhere, anytime.
71 +
72 +🔒 No account, no tracking, no ads. Your conversations never leave your device.
73 +
74 +Profoundly personal. Entirely private.
assets/aso/playstore/feature-graphic-1024x500.png
Binary files /dev/null and b/assets/aso/playstore/feature-graphic-1024x500.png differ
assets/aso/playstore/id.md new
+74
@@ -0,0 +1,74 @@
1 +# Google Play Store — Bahasa Indonesia (Indonesian)
2 +
3 +## Title (30 characters)
4 +
5 +Siti AI
6 +
7 +## Short Description (80 characters)
8 +
9 +AI privat di perangkat 🤖 Tanya apa saja, offline 🔒 Tanpa cloud & pelacakan
10 +
11 +## Full Description (4,000 characters)
12 +
13 +Kenalkan Siti — asisten AI pribadimu yang berjalan sepenuhnya di perangkatmu 🌼
14 +
15 +Siti adalah asisten yang sangat mampu dan personal yang tidak pernah mengirim kata-katamu ke cloud. Ajukan pertanyaan, susun pesan, kembangkan ide, ringkas teks, terjemahkan bahasa, atau sekadar berdiskusi — semuanya didukung oleh model AI di perangkat yang menjaga setiap percakapan tetap sepenuhnya privat.
16 +
17 +🤖 TANYA SITI APA SAJA
18 +
19 +• Dapatkan jawaban jelas untuk pertanyaan sehari-hari
20 +• Susun email, pesan, dan postingan
21 +• Kembangkan ide dan buat rencana
22 +• Ringkas teks panjang dalam hitungan detik
23 +• Terjemahkan antarbahasa
24 +• Jelaskan topik sulit dengan sederhana
25 +• Dapatkan bantuan menulis dan menyunting
26 +• Pelajari hal baru, satu pertanyaan setiap kali
27 +
28 +📱 100% DI PERANGKATMU
29 +
30 +AI Siti berjalan sepenuhnya di ponselmu. Tidak ada data yang pernah dikirim ke server — percakapanmu tidak pernah meninggalkan perangkatmu. Setelah pengunduhan model sekali saja (~1–2 GB), Siti berfungsi sepenuhnya offline. Tanpa koneksi internet.
31 +
32 +🔒 PRIVAT SEJAK AWAL
33 +
34 +• Tanpa akun, tanpa login
35 +• Tanpa pemrosesan cloud
36 +• Tanpa pelacakan, tanpa iklan
37 +• Tanpa pengumpulan data
38 +
39 +Percakapanmu hanya milikmu.
40 +
41 +✈️ BERFUNGSI DI MANA SAJA, KAPAN SAJA
42 +
43 +Di pesawat, di kereta bawah tanah, jauh dari jaringan — Siti selalu siap. Karena semuanya terjadi di perangkatmu, kamu mendapat jawaban cepat dan privat di mana pun kamu berada.
44 +
45 +⚡ CEPAT DAN NATIF
46 +
47 +Dibuat sebagai aplikasi natif dan dioptimalkan untuk perangkat keras modern, Siti memberikan respons cepat dan mulus.
48 +
49 +💡 MENGAPA SITI?
50 +
51 +Namanya adalah sentuhan jenaka untuk asisten yang kita semua kenal — tetapi Siti membaliknya: alih-alih ke cloud, semuanya tetap bersamamu. Sangat personal. Sepenuhnya privat.
52 +
53 +⭐ FITUR UTAMA
54 +
55 +• Obrolan AI privat di perangkat
56 +• Berfungsi sepenuhnya offline setelah unduhan model
57 +• Tanpa akun atau langganan
58 +• Tanpa pelacakan atau pengumpulan data
59 +• Desain bersih dan bebas gangguan
60 +• Respons cepat dan responsif
61 +
62 +Unduh Siti hari ini dan kenali asisten yang menjaga duniamu untuk dirimu sendiri 🔒
63 +
64 +## What's New (500 characters)
65 +
66 +Kenalkan Siti — asisten AI privat di perangkatmu 🤖
67 +
68 +🆕 Obrolan AI Privat — Tanya Siti apa saja: pertanyaan, menulis, ringkasan, terjemahan, ide. Berjalan sepenuhnya di perangkatmu tanpa data dikirim ke server mana pun.
69 +
70 +🆕 Sepenuhnya Offline — Setelah pengunduhan model sekali saja, mengobrol dengan Siti tanpa internet. Sempurna di mana saja, kapan saja.
71 +
72 +🔒 Tanpa akun, tanpa pelacakan, tanpa iklan. Percakapanmu tidak pernah meninggalkan perangkatmu.
73 +
74 +Sangat personal. Sepenuhnya privat.
assets/aso/playstore/sv-SE.md new
+74
@@ -0,0 +1,74 @@
1 +# Google Play Store — Svenska
2 +
3 +## Title (30 characters)
4 +
5 +Siti AI
6 +
7 +## Short Description (80 characters)
8 +
9 +Privat AI-assistent på enheten 🤖 Fråga vad som helst, offline 🔒 Inget moln
10 +
11 +## Full Description (4,000 characters)
12 +
13 +Möt Siti — din privata AI-assistent som körs helt på din enhet 🌼
14 +
15 +Siti är en djupt kapabel och personlig assistent som aldrig skickar dina ord till molnet. Ställ frågor, skriv meddelanden, brainstorma idéer, sammanfatta text, översätt språk eller bara resonera dig fram — allt drivs av en AI-modell på enheten som håller varje konversation helt privat.
16 +
17 +🤖 FRÅGA SITI VAD SOM HELST
18 +
19 +• Få tydliga svar på vardagliga frågor
20 +• Skriv e-post, meddelanden och inlägg
21 +• Brainstorma idéer och gör upp planer
22 +• Sammanfatta lång text på sekunder
23 +• Översätt mellan språk
24 +• Förklara svåra ämnen enkelt
25 +• Få hjälp med att skriva och redigera
26 +• Lär dig något nytt, en fråga i taget
27 +
28 +📱 100 % PÅ DIN ENHET
29 +
30 +Sitis AI körs helt på din telefon. Ingen data skickas någonsin till en server — dina konversationer lämnar aldrig din enhet. Efter en engångsnedladdning av modellen (~1–2 GB) fungerar Siti helt offline. Ingen internetanslutning krävs.
31 +
32 +🔒 PRIVAT FRÅN GRUNDEN
33 +
34 +• Inget konto, ingen inloggning
35 +• Ingen molnbearbetning
36 +• Ingen spårning, inga annonser
37 +• Ingen datainsamling
38 +
39 +Dina konversationer är bara dina.
40 +
41 +✈️ FUNGERAR VAR OCH NÄR SOM HELST
42 +
43 +På ett flygplan, på tunnelbanan, utanför nätet — Siti är alltid redo. Eftersom allt sker på din enhet får du snabba, privata svar var du än är.
44 +
45 +⚡ SNABB OCH NATIV
46 +
47 +Byggd som en nativ app och optimerad för modern hårdvara levererar Siti snabba, flytande svar.
48 +
49 +💡 VARFÖR SITI?
50 +
51 +Namnet är en lekfull blinkning till assistenterna vi alla känner — men Siti vänder på det: istället för molnet stannar allt hos dig. Djupt personlig. Helt privat.
52 +
53 +⭐ VIKTIGA FUNKTIONER
54 +
55 +• Privat AI-chatt på enheten
56 +• Fungerar helt offline efter nedladdning av modellen
57 +• Inget konto eller abonnemang krävs
58 +• Ingen spårning eller datainsamling
59 +• Ren, distraktionsfri design
60 +• Snabba, responsiva svar
61 +
62 +Ladda ner Siti idag och möt assistenten som håller din värld för sig själv 🔒
63 +
64 +## What's New (500 characters)
65 +
66 +Möt Siti — din privata AI-assistent på enheten 🤖
67 +
68 +🆕 Privat AI-chatt — Fråga Siti vad som helst: frågor, skrivande, sammanfattningar, översättningar, idéer. Körs helt på din enhet utan att data skickas till någon server.
69 +
70 +🆕 Helt offline — Efter en engångsnedladdning av modellen kan du chatta med Siti utan internet. Perfekt var och när som helst.
71 +
72 +🔒 Inget konto, ingen spårning, inga annonser. Dina konversationer lämnar aldrig din enhet.
73 +
74 +Djupt personlig. Helt privat.
assets/aso/playstore/whats-new.xml new
+9
@@ -0,0 +1,9 @@
1 +<en-GB>
2 +- Bug fixes and performance improvements
3 +</en-GB>
4 +<sv-SE>
5 +- Buggfixar och prestandaförbättringar
6 +</sv-SE>
7 +<id>
8 +- Perbaikan bug dan peningkatan performa
9 +</id>
assets/aso/shared.md new
+40
@@ -0,0 +1,40 @@
1 +# Brand — Siti AI
2 +
3 +Cross-store brand constants. All store listings should reference these values.
4 +
5 +## App Name (per language)
6 +
7 +| Language | App Name |
8 +|----------|----------|
9 +| English US | Siti AI |
10 +| Swedish | Siti AI |
11 +| Indonesian | Siti AI |
12 +| Arabic | سيتي AI |
13 +
14 +## Subtitle / Tagline (per language)
15 +
16 +| Language | Subtitle |
17 +|----------|----------|
18 +| English US | Your private on-device AI |
19 +| Swedish | Din privata AI – på enheten |
20 +| Indonesian | AI privat di perangkatmu |
21 +| Arabic | ذكاء اصطناعي خاص على جهازك |
22 +
23 +## Brand Tagline
24 +
25 +"Profoundly personal. Entirely private."
26 +
27 +A playful nod to "Siri AI" — but Siti flips the script: instead of the cloud,
28 +everything stays with you. The name is also a warm, familiar Indonesian and
29 +Malay feminine name, giving the assistant an approachable, human identity.
30 +
31 +## Brand Voice
32 +
33 +Clean, confident, calm, and privacy-first — with a human touch. Minimal in the
34 +spirit of great product design: say less, mean more. Siti is the assistant that
35 +keeps your world to itself.
36 +
37 +## Positioning One-Liner
38 +
39 +Siti AI is a private AI assistant that runs 100% on your device. Ask anything,
40 +completely offline. No cloud, no account, no tracking.
index.html new
+35
@@ -0,0 +1,35 @@
1 +<!doctype html>
2 +<html lang="en">
3 + <head>
4 + <meta charset="UTF-8" />
5 + <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6 + <!-- viewport-fit=cover makes the WebView edge-to-edge so the theme colour
7 + (painted on html/body) extends behind the status bar and home
8 + indicator, and env(safe-area-inset-*) becomes available for insetting
9 + content. -->
10 + <meta
11 + name="viewport"
12 + content="width=device-width, initial-scale=1.0, viewport-fit=cover"
13 + />
14 + <!-- Let the WebView and its default backdrop follow the system theme. -->
15 + <meta name="color-scheme" content="light dark" />
16 + <meta
17 + name="theme-color"
18 + content="#fafafa"
19 + media="(prefers-color-scheme: light)"
20 + />
21 + <meta
22 + name="theme-color"
23 + content="#111111"
24 + media="(prefers-color-scheme: dark)"
25 + />
26 + <meta name="apple-mobile-web-app-capable" content="yes" />
27 + <meta name="apple-mobile-web-app-title" content="Siti AI" />
28 + <title>Siti AI</title>
29 + </head>
30 +
31 + <body>
32 + <div id="root"></div>
33 + <script type="module" src="/src/main.tsx"></script>
34 + </body>
35 +</html>
package.json new
+29
@@ -0,0 +1,29 @@
1 +{
2 + "name": "sitiai",
3 + "private": true,
4 + "version": "0.1.0",
5 + "type": "module",
6 + "scripts": {
7 + "dev": "vite",
8 + "build": "tsc && vite build",
9 + "preview": "vite preview",
10 + "tauri": "tauri",
11 + "gim": "./scripts/generate-icon-mobile.sh",
12 + "playstore-graphics": "node scripts/generate-playstore-graphics.mjs"
13 + },
14 + "dependencies": {
15 + "@tauri-apps/api": "^2",
16 + "@tauri-apps/plugin-opener": "^2",
17 + "react": "^19.1.0",
18 + "react-dom": "^19.1.0"
19 + },
20 + "devDependencies": {
21 + "@tauri-apps/cli": "^2",
22 + "@types/react": "^19.1.8",
23 + "@types/react-dom": "^19.1.6",
24 + "@vitejs/plugin-react": "^4.6.0",
25 + "puppeteer": "^25.1.0",
26 + "typescript": "~5.8.3",
27 + "vite": "^7.0.4"
28 + }
29 +}
pnpm-lock.yaml new
+2316
@@ -0,0 +1,2316 @@
1 +lockfileVersion: '9.0'
2 +
3 +settings:
4 + autoInstallPeers: true
5 + excludeLinksFromLockfile: false
6 +
7 +importers:
8 +
9 + .:
10 + dependencies:
11 + '@tauri-apps/api':
12 + specifier: ^2
13 + version: 2.11.0
14 + '@tauri-apps/plugin-opener':
15 + specifier: ^2
16 + version: 2.5.4
17 + react:
18 + specifier: ^19.1.0
19 + version: 19.2.7
20 + react-dom:
21 + specifier: ^19.1.0
22 + version: 19.2.7(react@19.2.7)
23 + devDependencies:
24 + '@tauri-apps/cli':
25 + specifier: ^2
26 + version: 2.11.2
27 + '@types/react':
28 + specifier: ^19.1.8
29 + version: 19.2.17
30 + '@types/react-dom':
31 + specifier: ^19.1.6
32 + version: 19.2.3(@types/react@19.2.17)
33 + '@vitejs/plugin-react':
34 + specifier: ^4.6.0
35 + version: 4.7.0(vite@7.3.5(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0))
36 + puppeteer:
37 + specifier: ^25.1.0
38 + version: 25.1.0
39 + typescript:
40 + specifier: ~5.8.3
41 + version: 5.8.3
42 + vite:
43 + specifier: ^7.0.4
44 + version: 7.3.5(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0)
45 +
46 + web:
47 + dependencies:
48 + next:
49 + specifier: ^15.5.19
50 + version: 15.5.19(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
51 + react:
52 + specifier: ^19.2.7
53 + version: 19.2.7
54 + react-dom:
55 + specifier: ^19.2.7
56 + version: 19.2.7(react@19.2.7)
57 + devDependencies:
58 + '@tailwindcss/postcss':
59 + specifier: ^4.3.1
60 + version: 4.3.1
61 + '@types/node':
62 + specifier: ^22.19.21
63 + version: 22.19.21
64 + '@types/react':
65 + specifier: ^19.2.17
66 + version: 19.2.17
67 + '@types/react-dom':
68 + specifier: ^19.2.3
69 + version: 19.2.3(@types/react@19.2.17)
70 + tailwindcss:
71 + specifier: ^4.3.1
72 + version: 4.3.1
73 + typescript:
74 + specifier: ~5.8.3
75 + version: 5.8.3
76 +
77 +packages:
78 +
79 + '@alloc/quick-lru@5.2.0':
80 + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
81 + engines: {node: '>=10'}
82 +
83 + '@babel/code-frame@7.29.7':
84 + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
85 + engines: {node: '>=6.9.0'}
86 +
87 + '@babel/compat-data@7.29.7':
88 + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==}
89 + engines: {node: '>=6.9.0'}
90 +
91 + '@babel/core@7.29.7':
92 + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==}
93 + engines: {node: '>=6.9.0'}
94 +
95 + '@babel/generator@7.29.7':
96 + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==}
97 + engines: {node: '>=6.9.0'}
98 +
99 + '@babel/helper-compilation-targets@7.29.7':
100 + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==}
101 + engines: {node: '>=6.9.0'}
102 +
103 + '@babel/helper-globals@7.29.7':
104 + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==}
105 + engines: {node: '>=6.9.0'}
106 +
107 + '@babel/helper-module-imports@7.29.7':
108 + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==}
109 + engines: {node: '>=6.9.0'}
110 +
111 + '@babel/helper-module-transforms@7.29.7':
112 + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==}
113 + engines: {node: '>=6.9.0'}
114 + peerDependencies:
115 + '@babel/core': ^7.0.0
116 +
117 + '@babel/helper-plugin-utils@7.29.7':
118 + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==}
119 + engines: {node: '>=6.9.0'}
120 +
121 + '@babel/helper-string-parser@7.29.7':
122 + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
123 + engines: {node: '>=6.9.0'}
124 +
125 + '@babel/helper-validator-identifier@7.29.7':
126 + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
127 + engines: {node: '>=6.9.0'}
128 +
129 + '@babel/helper-validator-option@7.29.7':
130 + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==}
131 + engines: {node: '>=6.9.0'}
132 +
133 + '@babel/helpers@7.29.7':
134 + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==}
135 + engines: {node: '>=6.9.0'}
136 +
137 + '@babel/parser@7.29.7':
138 + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==}
139 + engines: {node: '>=6.0.0'}
140 + hasBin: true
141 +
142 + '@babel/plugin-transform-react-jsx-self@7.29.7':
143 + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==}
144 + engines: {node: '>=6.9.0'}
145 + peerDependencies:
146 + '@babel/core': ^7.0.0-0
147 +
148 + '@babel/plugin-transform-react-jsx-source@7.29.7':
149 + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==}
150 + engines: {node: '>=6.9.0'}
151 + peerDependencies:
152 + '@babel/core': ^7.0.0-0
153 +
154 + '@babel/template@7.29.7':
155 + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==}
156 + engines: {node: '>=6.9.0'}
157 +
158 + '@babel/traverse@7.29.7':
159 + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==}
160 + engines: {node: '>=6.9.0'}
161 +
162 + '@babel/types@7.29.7':
163 + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
164 + engines: {node: '>=6.9.0'}
165 +
166 + '@emnapi/runtime@1.11.1':
167 + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
168 +
169 + '@esbuild/aix-ppc64@0.27.7':
170 + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
171 + engines: {node: '>=18'}
172 + cpu: [ppc64]
173 + os: [aix]
174 +
175 + '@esbuild/android-arm64@0.27.7':
176 + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
177 + engines: {node: '>=18'}
178 + cpu: [arm64]
179 + os: [android]
180 +
181 + '@esbuild/android-arm@0.27.7':
182 + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
183 + engines: {node: '>=18'}
184 + cpu: [arm]
185 + os: [android]
186 +
187 + '@esbuild/android-x64@0.27.7':
188 + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
189 + engines: {node: '>=18'}
190 + cpu: [x64]
191 + os: [android]
192 +
193 + '@esbuild/darwin-arm64@0.27.7':
194 + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
195 + engines: {node: '>=18'}
196 + cpu: [arm64]
197 + os: [darwin]
198 +
199 + '@esbuild/darwin-x64@0.27.7':
200 + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
201 + engines: {node: '>=18'}
202 + cpu: [x64]
203 + os: [darwin]
204 +
205 + '@esbuild/freebsd-arm64@0.27.7':
206 + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
207 + engines: {node: '>=18'}
208 + cpu: [arm64]
209 + os: [freebsd]
210 +
211 + '@esbuild/freebsd-x64@0.27.7':
212 + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
213 + engines: {node: '>=18'}
214 + cpu: [x64]
215 + os: [freebsd]
216 +
217 + '@esbuild/linux-arm64@0.27.7':
218 + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
219 + engines: {node: '>=18'}
220 + cpu: [arm64]
221 + os: [linux]
222 +
223 + '@esbuild/linux-arm@0.27.7':
224 + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
225 + engines: {node: '>=18'}
226 + cpu: [arm]
227 + os: [linux]
228 +
229 + '@esbuild/linux-ia32@0.27.7':
230 + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
231 + engines: {node: '>=18'}
232 + cpu: [ia32]
233 + os: [linux]
234 +
235 + '@esbuild/linux-loong64@0.27.7':
236 + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
237 + engines: {node: '>=18'}
238 + cpu: [loong64]
239 + os: [linux]
240 +
241 + '@esbuild/linux-mips64el@0.27.7':
242 + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
243 + engines: {node: '>=18'}
244 + cpu: [mips64el]
245 + os: [linux]
246 +
247 + '@esbuild/linux-ppc64@0.27.7':
248 + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
249 + engines: {node: '>=18'}
250 + cpu: [ppc64]
251 + os: [linux]
252 +
253 + '@esbuild/linux-riscv64@0.27.7':
254 + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
255 + engines: {node: '>=18'}
256 + cpu: [riscv64]
257 + os: [linux]
258 +
259 + '@esbuild/linux-s390x@0.27.7':
260 + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
261 + engines: {node: '>=18'}
262 + cpu: [s390x]
263 + os: [linux]
264 +
265 + '@esbuild/linux-x64@0.27.7':
266 + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
267 + engines: {node: '>=18'}
268 + cpu: [x64]
269 + os: [linux]
270 +
271 + '@esbuild/netbsd-arm64@0.27.7':
272 + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
273 + engines: {node: '>=18'}
274 + cpu: [arm64]
275 + os: [netbsd]
276 +
277 + '@esbuild/netbsd-x64@0.27.7':
278 + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
279 + engines: {node: '>=18'}
280 + cpu: [x64]
281 + os: [netbsd]
282 +
283 + '@esbuild/openbsd-arm64@0.27.7':
284 + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
285 + engines: {node: '>=18'}
286 + cpu: [arm64]
287 + os: [openbsd]
288 +
289 + '@esbuild/openbsd-x64@0.27.7':
290 + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
291 + engines: {node: '>=18'}
292 + cpu: [x64]
293 + os: [openbsd]
294 +
295 + '@esbuild/openharmony-arm64@0.27.7':
296 + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
297 + engines: {node: '>=18'}
298 + cpu: [arm64]
299 + os: [openharmony]
300 +
301 + '@esbuild/sunos-x64@0.27.7':
302 + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
303 + engines: {node: '>=18'}
304 + cpu: [x64]
305 + os: [sunos]
306 +
307 + '@esbuild/win32-arm64@0.27.7':
308 + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
309 + engines: {node: '>=18'}
310 + cpu: [arm64]
311 + os: [win32]
312 +
313 + '@esbuild/win32-ia32@0.27.7':
314 + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
315 + engines: {node: '>=18'}
316 + cpu: [ia32]
317 + os: [win32]
318 +
319 + '@esbuild/win32-x64@0.27.7':
320 + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
321 + engines: {node: '>=18'}
322 + cpu: [x64]
323 + os: [win32]
324 +
325 + '@img/colour@1.1.0':
326 + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
327 + engines: {node: '>=18'}
328 +
329 + '@img/sharp-darwin-arm64@0.34.5':
330 + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
331 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
332 + cpu: [arm64]
333 + os: [darwin]
334 +
335 + '@img/sharp-darwin-x64@0.34.5':
336 + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
337 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
338 + cpu: [x64]
339 + os: [darwin]
340 +
341 + '@img/sharp-libvips-darwin-arm64@1.2.4':
342 + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
343 + cpu: [arm64]
344 + os: [darwin]
345 +
346 + '@img/sharp-libvips-darwin-x64@1.2.4':
347 + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
348 + cpu: [x64]
349 + os: [darwin]
350 +
351 + '@img/sharp-libvips-linux-arm64@1.2.4':
352 + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
353 + cpu: [arm64]
354 + os: [linux]
355 +
356 + '@img/sharp-libvips-linux-arm@1.2.4':
357 + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
358 + cpu: [arm]
359 + os: [linux]
360 +
361 + '@img/sharp-libvips-linux-ppc64@1.2.4':
362 + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
363 + cpu: [ppc64]
364 + os: [linux]
365 +
366 + '@img/sharp-libvips-linux-riscv64@1.2.4':
367 + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
368 + cpu: [riscv64]
369 + os: [linux]
370 +
371 + '@img/sharp-libvips-linux-s390x@1.2.4':
372 + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
373 + cpu: [s390x]
374 + os: [linux]
375 +
376 + '@img/sharp-libvips-linux-x64@1.2.4':
377 + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
378 + cpu: [x64]
379 + os: [linux]
380 +
381 + '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
382 + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
383 + cpu: [arm64]
384 + os: [linux]
385 +
386 + '@img/sharp-libvips-linuxmusl-x64@1.2.4':
387 + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
388 + cpu: [x64]
389 + os: [linux]
390 +
391 + '@img/sharp-linux-arm64@0.34.5':
392 + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
393 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
394 + cpu: [arm64]
395 + os: [linux]
396 +
397 + '@img/sharp-linux-arm@0.34.5':
398 + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
399 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
400 + cpu: [arm]
401 + os: [linux]
402 +
403 + '@img/sharp-linux-ppc64@0.34.5':
404 + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
405 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
406 + cpu: [ppc64]
407 + os: [linux]
408 +
409 + '@img/sharp-linux-riscv64@0.34.5':
410 + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
411 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
412 + cpu: [riscv64]
413 + os: [linux]
414 +
415 + '@img/sharp-linux-s390x@0.34.5':
416 + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
417 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
418 + cpu: [s390x]
419 + os: [linux]
420 +
421 + '@img/sharp-linux-x64@0.34.5':
422 + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
423 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
424 + cpu: [x64]
425 + os: [linux]
426 +
427 + '@img/sharp-linuxmusl-arm64@0.34.5':
428 + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
429 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
430 + cpu: [arm64]
431 + os: [linux]
432 +
433 + '@img/sharp-linuxmusl-x64@0.34.5':
434 + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
435 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
436 + cpu: [x64]
437 + os: [linux]
438 +
439 + '@img/sharp-wasm32@0.34.5':
440 + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
441 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
442 + cpu: [wasm32]
443 +
444 + '@img/sharp-win32-arm64@0.34.5':
445 + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
446 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
447 + cpu: [arm64]
448 + os: [win32]
449 +
450 + '@img/sharp-win32-ia32@0.34.5':
451 + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
452 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
453 + cpu: [ia32]
454 + os: [win32]
455 +
456 + '@img/sharp-win32-x64@0.34.5':
457 + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
458 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
459 + cpu: [x64]
460 + os: [win32]
461 +
462 + '@jridgewell/gen-mapping@0.3.13':
463 + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
464 +
465 + '@jridgewell/remapping@2.3.5':
466 + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
467 +
468 + '@jridgewell/resolve-uri@3.1.2':
469 + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
470 + engines: {node: '>=6.0.0'}
471 +
472 + '@jridgewell/sourcemap-codec@1.5.5':
473 + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
474 +
475 + '@jridgewell/trace-mapping@0.3.31':
476 + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
477 +
478 + '@next/env@15.5.19':
479 + resolution: {integrity: sha512-sWWluFvcv5v3Fxznmf2ZfjyoVQt/64oCnYqS90inQWGzMPK1VjvekPiz3OPHKmFT30EnHrjlbyaHLt3M0vWabw==}
480 +
481 + '@next/swc-darwin-arm64@15.5.19':
482 + resolution: {integrity: sha512-jx9wWlTKueHKPvVOndyr7WuaevWCkuYqsQ8gC0TMPKAVWG3MhcdMrjfo9tvIZNXd0QOUYXXvAcZ325y8Uq7uzg==}
483 + engines: {node: '>= 10'}
484 + cpu: [arm64]
485 + os: [darwin]
486 +
487 + '@next/swc-darwin-x64@15.5.19':
488 + resolution: {integrity: sha512-291KFcsIQ3OenRdiUDFOR6W3wezzH4auENXm1gbm1Bjd4ANMMRgxPrWTUztQN43BnVoVuMnHCrLeECIMwgFKbA==}
489 + engines: {node: '>= 10'}
490 + cpu: [x64]
491 + os: [darwin]
492 +
493 + '@next/swc-linux-arm64-gnu@15.5.19':
494 + resolution: {integrity: sha512-WeH+nelQyyMeE2f8FxBRZNrGipya5zHZV2vjzfCOAYyiI6am+NbnWAAldOBFQBB2w0DjJcsvrKqoFT2b7+5YoA==}
495 + engines: {node: '>= 10'}
496 + cpu: [arm64]
497 + os: [linux]
498 +
499 + '@next/swc-linux-arm64-musl@15.5.19':
500 + resolution: {integrity: sha512-5xTOE0lDlDCSSfp+BAif7j17VRRCjWp//ZPZy6NI0QpdrhxtQnsZguSx0xAAZ0c9XZLrLLwCe/XVe5YPrRilKw==}
501 + engines: {node: '>= 10'}
502 + cpu: [arm64]
503 + os: [linux]
504 +
505 + '@next/swc-linux-x64-gnu@15.5.19':
506 + resolution: {integrity: sha512-LTxRmMgqqMv05Had879W00Fm53quiJd3Zuz8h1JSNJ3nGSlbZ/7Tjs1tKyScgN3Au3t3MyPsjPlq60fMmSHLsg==}
507 + engines: {node: '>= 10'}
508 + cpu: [x64]
509 + os: [linux]
510 +
511 + '@next/swc-linux-x64-musl@15.5.19':
512 + resolution: {integrity: sha512-eoNQSpA5PQfB9wBO4RA47MTDXWz1fizy9Y3Z6e4DetYIF3dvjuu8sj7aIGn/bFCU6lnFzTK34NtCaffP4NsQ7Q==}
513 + engines: {node: '>= 10'}
514 + cpu: [x64]
515 + os: [linux]
516 +
517 + '@next/swc-win32-arm64-msvc@15.5.19':
518 + resolution: {integrity: sha512-6UNt2dFuCHOe446sm/Kp69nUe8/wIhnh9bm6Xcqw4qEWCOppLMOvhTBVgvM7invVUNr4SPpP6NOQsACtn2IN9Q==}
519 + engines: {node: '>= 10'}
520 + cpu: [arm64]
521 + os: [win32]
522 +
523 + '@next/swc-win32-x64-msvc@15.5.19':
524 + resolution: {integrity: sha512-PhmojAHyqMne56HBLGu9dhDnHPuFmEjrXSQMM/nW0J6j849lk3ESrVtqNJcCk8CKOV7brpTTbaYAjwKPzKM69w==}
525 + engines: {node: '>= 10'}
526 + cpu: [x64]
527 + os: [win32]
528 +
529 + '@puppeteer/browsers@3.0.4':
530 + resolution: {integrity: sha512-HGM8iAmGTf+Y7t0373szVbTmt3d7vPkYL/1bpOkOFO0YUYLgSeuYBCzESklogNPvOBnZ/MRD5f07OkpqH1trtA==}
531 + engines: {node: '>=22.12.0'}
532 + hasBin: true
533 + peerDependencies:
534 + proxy-agent: '>=8.0.1'
535 + peerDependenciesMeta:
536 + proxy-agent:
537 + optional: true
538 +
539 + '@rolldown/pluginutils@1.0.0-beta.27':
540 + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
541 +
542 + '@rollup/rollup-android-arm-eabi@4.61.1':
543 + resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==}
544 + cpu: [arm]
545 + os: [android]
546 +
547 + '@rollup/rollup-android-arm64@4.61.1':
548 + resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==}
549 + cpu: [arm64]
550 + os: [android]
551 +
552 + '@rollup/rollup-darwin-arm64@4.61.1':
553 + resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==}
554 + cpu: [arm64]
555 + os: [darwin]
556 +
557 + '@rollup/rollup-darwin-x64@4.61.1':
558 + resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==}
559 + cpu: [x64]
560 + os: [darwin]
561 +
562 + '@rollup/rollup-freebsd-arm64@4.61.1':
563 + resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==}
564 + cpu: [arm64]
565 + os: [freebsd]
566 +
567 + '@rollup/rollup-freebsd-x64@4.61.1':
568 + resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==}
569 + cpu: [x64]
570 + os: [freebsd]
571 +
572 + '@rollup/rollup-linux-arm-gnueabihf@4.61.1':
573 + resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==}
574 + cpu: [arm]
575 + os: [linux]
576 +
577 + '@rollup/rollup-linux-arm-musleabihf@4.61.1':
578 + resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==}
579 + cpu: [arm]
580 + os: [linux]
581 +
582 + '@rollup/rollup-linux-arm64-gnu@4.61.1':
583 + resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==}
584 + cpu: [arm64]
585 + os: [linux]
586 +
587 + '@rollup/rollup-linux-arm64-musl@4.61.1':
588 + resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==}
589 + cpu: [arm64]
590 + os: [linux]
591 +
592 + '@rollup/rollup-linux-loong64-gnu@4.61.1':
593 + resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==}
594 + cpu: [loong64]
595 + os: [linux]
596 +
597 + '@rollup/rollup-linux-loong64-musl@4.61.1':
598 + resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==}
599 + cpu: [loong64]
600 + os: [linux]
601 +
602 + '@rollup/rollup-linux-ppc64-gnu@4.61.1':
603 + resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==}
604 + cpu: [ppc64]
605 + os: [linux]
606 +
607 + '@rollup/rollup-linux-ppc64-musl@4.61.1':
608 + resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==}
609 + cpu: [ppc64]
610 + os: [linux]
611 +
612 + '@rollup/rollup-linux-riscv64-gnu@4.61.1':
613 + resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==}
614 + cpu: [riscv64]
615 + os: [linux]
616 +
617 + '@rollup/rollup-linux-riscv64-musl@4.61.1':
618 + resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==}
619 + cpu: [riscv64]
620 + os: [linux]
621 +
622 + '@rollup/rollup-linux-s390x-gnu@4.61.1':
623 + resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==}
624 + cpu: [s390x]
625 + os: [linux]
626 +
627 + '@rollup/rollup-linux-x64-gnu@4.61.1':
628 + resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==}
629 + cpu: [x64]
630 + os: [linux]
631 +
632 + '@rollup/rollup-linux-x64-musl@4.61.1':
633 + resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==}
634 + cpu: [x64]
635 + os: [linux]
636 +
637 + '@rollup/rollup-openbsd-x64@4.61.1':
638 + resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==}
639 + cpu: [x64]
640 + os: [openbsd]
641 +
642 + '@rollup/rollup-openharmony-arm64@4.61.1':
643 + resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==}
644 + cpu: [arm64]
645 + os: [openharmony]
646 +
647 + '@rollup/rollup-win32-arm64-msvc@4.61.1':
648 + resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==}
649 + cpu: [arm64]
650 + os: [win32]
651 +
652 + '@rollup/rollup-win32-ia32-msvc@4.61.1':
653 + resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==}
654 + cpu: [ia32]
655 + os: [win32]
656 +
657 + '@rollup/rollup-win32-x64-gnu@4.61.1':
658 + resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==}
659 + cpu: [x64]
660 + os: [win32]
661 +
662 + '@rollup/rollup-win32-x64-msvc@4.61.1':
663 + resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==}
664 + cpu: [x64]
665 + os: [win32]
666 +
667 + '@swc/helpers@0.5.15':
668 + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
669 +
670 + '@tailwindcss/node@4.3.1':
671 + resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==}
672 +
673 + '@tailwindcss/oxide-android-arm64@4.3.1':
674 + resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==}
675 + engines: {node: '>= 20'}
676 + cpu: [arm64]
677 + os: [android]
678 +
679 + '@tailwindcss/oxide-darwin-arm64@4.3.1':
680 + resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==}
681 + engines: {node: '>= 20'}
682 + cpu: [arm64]
683 + os: [darwin]
684 +
685 + '@tailwindcss/oxide-darwin-x64@4.3.1':
686 + resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==}
687 + engines: {node: '>= 20'}
688 + cpu: [x64]
689 + os: [darwin]
690 +
691 + '@tailwindcss/oxide-freebsd-x64@4.3.1':
692 + resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==}
693 + engines: {node: '>= 20'}
694 + cpu: [x64]
695 + os: [freebsd]
696 +
697 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1':
698 + resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==}
699 + engines: {node: '>= 20'}
700 + cpu: [arm]
701 + os: [linux]
702 +
703 + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1':
704 + resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==}
705 + engines: {node: '>= 20'}
706 + cpu: [arm64]
707 + os: [linux]
708 +
709 + '@tailwindcss/oxide-linux-arm64-musl@4.3.1':
710 + resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==}
711 + engines: {node: '>= 20'}
712 + cpu: [arm64]
713 + os: [linux]
714 +
715 + '@tailwindcss/oxide-linux-x64-gnu@4.3.1':
716 + resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==}
717 + engines: {node: '>= 20'}
718 + cpu: [x64]
719 + os: [linux]
720 +
721 + '@tailwindcss/oxide-linux-x64-musl@4.3.1':
722 + resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==}
723 + engines: {node: '>= 20'}
724 + cpu: [x64]
725 + os: [linux]
726 +
727 + '@tailwindcss/oxide-wasm32-wasi@4.3.1':
728 + resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==}
729 + engines: {node: '>=14.0.0'}
730 + cpu: [wasm32]
731 + bundledDependencies:
732 + - '@napi-rs/wasm-runtime'
733 + - '@emnapi/core'
734 + - '@emnapi/runtime'
735 + - '@tybys/wasm-util'
736 + - '@emnapi/wasi-threads'
737 + - tslib
738 +
739 + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1':
740 + resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==}
741 + engines: {node: '>= 20'}
742 + cpu: [arm64]
743 + os: [win32]
744 +
745 + '@tailwindcss/oxide-win32-x64-msvc@4.3.1':
746 + resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==}
747 + engines: {node: '>= 20'}
748 + cpu: [x64]
749 + os: [win32]
750 +
751 + '@tailwindcss/oxide@4.3.1':
752 + resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==}
753 + engines: {node: '>= 20'}
754 +
755 + '@tailwindcss/postcss@4.3.1':
756 + resolution: {integrity: sha512-dNJuNbdEJT/SWRuXTYP1WSamelsz3ztkUsdtWQPjrexysrTpaEPM40P/71knXiXLYEojqPOEGitVLLpPMS5T6A==}
757 +
758 + '@tauri-apps/api@2.11.0':
759 + resolution: {integrity: sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA==}
760 +
761 + '@tauri-apps/cli-darwin-arm64@2.11.2':
762 + resolution: {integrity: sha512-+4UZzLt+eOAEQCwgd+TqKgyUJMrvx+BgdXLLaqJYmPqzP+nE6YZr/hY6CWLYGQb8jFn99jEkmC6uA3tNvamA1w==}
763 + engines: {node: '>= 10'}
764 + cpu: [arm64]
765 + os: [darwin]
766 +
767 + '@tauri-apps/cli-darwin-x64@2.11.2':
768 + resolution: {integrity: sha512-VjYYtZUPqDMLutSfJEyxFE3Bz+DPi7c8wC3imckgvciLDZLq4qwKJxBicg0BXGhXjJsl8vKWgWRFNMPELQ+Xyg==}
769 + engines: {node: '>= 10'}
770 + cpu: [x64]
771 + os: [darwin]
772 +
773 + '@tauri-apps/cli-linux-arm-gnueabihf@2.11.2':
774 + resolution: {integrity: sha512-yMemD6f4i95AQriS8EazyOFzbE34yjnP16i3IOzpHGQvBoy2DjypFMFBq0NtPuITURv/cOGguRtHR5d79/9CSA==}
775 + engines: {node: '>= 10'}
776 + cpu: [arm]
777 + os: [linux]
778 +
779 + '@tauri-apps/cli-linux-arm64-gnu@2.11.2':
780 + resolution: {integrity: sha512-cgI91D2wL8GSgoWwZXDqt+DwnuZCP2/bz03QAE4TrhgAKIsrB4hX26W/H1EONPUUNkqrsgeCD0wU6pcNjV/5kw==}
781 + engines: {node: '>= 10'}
782 + cpu: [arm64]
783 + os: [linux]
784 +
785 + '@tauri-apps/cli-linux-arm64-musl@2.11.2':
786 + resolution: {integrity: sha512-X1rm0BERqAAggtYTESSgXrS3sz4Sb/OiPiz54UqISlXW+GkR3vNIGnsy/lejNmoXGVqri3Q53BCfQiclOIyRPw==}
787 + engines: {node: '>= 10'}
788 + cpu: [arm64]
789 + os: [linux]
790 +
791 + '@tauri-apps/cli-linux-riscv64-gnu@2.11.2':
792 + resolution: {integrity: sha512-usbMLJbT3KtkOrBMDVeGYNM35aTHXx38SJSzTMSqqjeUIOQ+iVPjb2yAGNAE+KqmBbAx4FOFIyMeKXx2M/JKGQ==}
793 + engines: {node: '>= 10'}
794 + cpu: [riscv64]
795 + os: [linux]
796 +
797 + '@tauri-apps/cli-linux-x64-gnu@2.11.2':
798 + resolution: {integrity: sha512-Ru4gwJKPG0ctVGchRGpRup4Y4lW2SSfFnrbQcyHhCliKy4g8Qz97TrUgCur4CbWyAgKxvGh3SjrkA0LDYzDGiw==}
799 + engines: {node: '>= 10'}
800 + cpu: [x64]
801 + os: [linux]
802 +
803 + '@tauri-apps/cli-linux-x64-musl@2.11.2':
804 + resolution: {integrity: sha512-eUm7T6clN1MMmNSRQ9gaWsQdyehQx2Gmn5hht/QUlqZQI/qcP2OJK5dnaxqwFzCr2HdsEo9ydxaqcS1oJzMvUw==}
805 + engines: {node: '>= 10'}
806 + cpu: [x64]
807 + os: [linux]
808 +
809 + '@tauri-apps/cli-win32-arm64-msvc@2.11.2':
810 + resolution: {integrity: sha512-HeeZW80jU+gVTOEX4X/hC6NVSAdDVXajwP5fxIZ/3z9WvUC7qrudX2GMTilYq6Dg0e0sk0XgsAJD1hZ5wPBXUA==}
811 + engines: {node: '>= 10'}
812 + cpu: [arm64]
813 + os: [win32]
814 +
815 + '@tauri-apps/cli-win32-ia32-msvc@2.11.2':
816 + resolution: {integrity: sha512-YhjQNZcXfbkCLyazSv1nPnJ9iRFE1wm6kc51FDbU10/Dk09io+6PAGMLjkxnX2GdM0qMnDmTjstY8mTDVvtKeA==}
817 + engines: {node: '>= 10'}
818 + cpu: [ia32]
819 + os: [win32]
820 +
821 + '@tauri-apps/cli-win32-x64-msvc@2.11.2':
822 + resolution: {integrity: sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA==}
823 + engines: {node: '>= 10'}
824 + cpu: [x64]
825 + os: [win32]
826 +
827 + '@tauri-apps/cli@2.11.2':
828 + resolution: {integrity: sha512-bk3HemqvGRoy+5D/dVMUQHKMYLglD0jVnMm/0iGMH6ufZ+p8r14m6BpIixwij3PBvZdvORUp1YifTD8QxVZ1Nw==}
829 + engines: {node: '>= 10'}
830 + hasBin: true
831 +
832 + '@tauri-apps/plugin-opener@2.5.4':
833 + resolution: {integrity: sha512-1HnPkb+AmgO29HBazm4uPLKB+r7zzcTBW1d0fyYp1uP+jwtpoiNDGKMMzz58SFp49nOIrxdE3aUJtT57lfO9CQ==}
834 +
835 + '@types/babel__core@7.20.5':
836 + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
837 +
838 + '@types/babel__generator@7.27.0':
839 + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
840 +
841 + '@types/babel__template@7.4.4':
842 + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
843 +
844 + '@types/babel__traverse@7.28.0':
845 + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
846 +
847 + '@types/estree@1.0.9':
848 + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
849 +
850 + '@types/node@22.19.21':
851 + resolution: {integrity: sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA==}
852 +
853 + '@types/react-dom@19.2.3':
854 + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
855 + peerDependencies:
856 + '@types/react': ^19.2.0
857 +
858 + '@types/react@19.2.17':
859 + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==}
860 +
861 + '@vitejs/plugin-react@4.7.0':
862 + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==}
863 + engines: {node: ^14.18.0 || >=16.0.0}
864 + peerDependencies:
865 + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
866 +
867 + ansi-regex@5.0.1:
868 + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
869 + engines: {node: '>=8'}
870 +
871 + ansi-styles@4.3.0:
872 + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
873 + engines: {node: '>=8'}
874 +
875 + baseline-browser-mapping@2.10.37:
876 + resolution: {integrity: sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==}
877 + engines: {node: '>=6.0.0'}
878 + hasBin: true
879 +
880 + browserslist@4.28.2:
881 + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
882 + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
883 + hasBin: true
884 +
885 + caniuse-lite@1.0.30001799:
886 + resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==}
887 +
888 + chromium-bidi@16.0.1:
889 + resolution: {integrity: sha512-J63PGu/9PpeCwLIcKYyzWP6yaVL5pxuBc0shlYCYM8BaAkmlwiQboXO1iNbOgSDbVklEyYFfNEcHD8oOAWacUA==}
890 + engines: {node: '>=20.19.0 <22.0.0 || >=22.12.0'}
891 + peerDependencies:
892 + devtools-protocol: '*'
893 +
894 + client-only@0.0.1:
895 + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
896 +
897 + cliui@8.0.1:
898 + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
899 + engines: {node: '>=12'}
900 +
901 + color-convert@2.0.1:
902 + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
903 + engines: {node: '>=7.0.0'}
904 +
905 + color-name@1.1.4:
906 + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
907 +
908 + convert-source-map@2.0.0:
909 + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
910 +
911 + csstype@3.2.3:
912 + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
913 +
914 + debug@4.4.3:
915 + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
916 + engines: {node: '>=6.0'}
917 + peerDependencies:
918 + supports-color: '*'
919 + peerDependenciesMeta:
920 + supports-color:
921 + optional: true
922 +
923 + detect-libc@2.1.2:
924 + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
925 + engines: {node: '>=8'}
926 +
927 + devtools-protocol@0.0.1624250:
928 + resolution: {integrity: sha512-YFAat/lOiIk0ARmBweG+ygrEcbZrq5B9urRyUoeQKp53MlidHXE2TmTbxKcaXoQj7u/aX+jebDO4BW55rs0WwA==}
929 +
930 + electron-to-chromium@1.5.372:
931 + resolution: {integrity: sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==}
932 +
933 + emoji-regex@8.0.0:
934 + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
935 +
936 + enhanced-resolve@5.21.6:
937 + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==}
938 + engines: {node: '>=10.13.0'}
939 +
940 + esbuild@0.27.7:
941 + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
942 + engines: {node: '>=18'}
943 + hasBin: true
944 +
945 + escalade@3.2.0:
946 + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
947 + engines: {node: '>=6'}
948 +
949 + fdir@6.5.0:
950 + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
951 + engines: {node: '>=12.0.0'}
952 + peerDependencies:
953 + picomatch: ^3 || ^4
954 + peerDependenciesMeta:
955 + picomatch:
956 + optional: true
957 +
958 + fsevents@2.3.3:
959 + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
960 + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
961 + os: [darwin]
962 +
963 + gensync@1.0.0-beta.2:
964 + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
965 + engines: {node: '>=6.9.0'}
966 +
967 + get-caller-file@2.0.5:
968 + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
969 + engines: {node: 6.* || 8.* || >= 10.*}
970 +
971 + graceful-fs@4.2.11:
972 + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
973 +
974 + is-fullwidth-code-point@3.0.0:
975 + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
976 + engines: {node: '>=8'}
977 +
978 + jiti@2.7.0:
979 + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
980 + hasBin: true
981 +
982 + js-tokens@4.0.0:
983 + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
984 +
985 + jsesc@3.1.0:
986 + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
987 + engines: {node: '>=6'}
988 + hasBin: true
989 +
990 + json5@2.2.3:
991 + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
992 + engines: {node: '>=6'}
993 + hasBin: true
994 +
995 + lightningcss-android-arm64@1.32.0:
996 + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
997 + engines: {node: '>= 12.0.0'}
998 + cpu: [arm64]
999 + os: [android]
1000 +
1001 + lightningcss-darwin-arm64@1.32.0:
1002 + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
1003 + engines: {node: '>= 12.0.0'}
1004 + cpu: [arm64]
1005 + os: [darwin]
1006 +
1007 + lightningcss-darwin-x64@1.32.0:
1008 + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
1009 + engines: {node: '>= 12.0.0'}
1010 + cpu: [x64]
1011 + os: [darwin]
1012 +
1013 + lightningcss-freebsd-x64@1.32.0:
1014 + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
1015 + engines: {node: '>= 12.0.0'}
1016 + cpu: [x64]
1017 + os: [freebsd]
1018 +
1019 + lightningcss-linux-arm-gnueabihf@1.32.0:
1020 + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
1021 + engines: {node: '>= 12.0.0'}
1022 + cpu: [arm]
1023 + os: [linux]
1024 +
1025 + lightningcss-linux-arm64-gnu@1.32.0:
1026 + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
1027 + engines: {node: '>= 12.0.0'}
1028 + cpu: [arm64]
1029 + os: [linux]
1030 +
1031 + lightningcss-linux-arm64-musl@1.32.0:
1032 + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
1033 + engines: {node: '>= 12.0.0'}
1034 + cpu: [arm64]
1035 + os: [linux]
1036 +
1037 + lightningcss-linux-x64-gnu@1.32.0:
1038 + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
1039 + engines: {node: '>= 12.0.0'}
1040 + cpu: [x64]
1041 + os: [linux]
1042 +
1043 + lightningcss-linux-x64-musl@1.32.0:
1044 + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
1045 + engines: {node: '>= 12.0.0'}
1046 + cpu: [x64]
1047 + os: [linux]
1048 +
1049 + lightningcss-win32-arm64-msvc@1.32.0:
1050 + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
1051 + engines: {node: '>= 12.0.0'}
1052 + cpu: [arm64]
1053 + os: [win32]
1054 +
1055 + lightningcss-win32-x64-msvc@1.32.0:
1056 + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
1057 + engines: {node: '>= 12.0.0'}
1058 + cpu: [x64]
1059 + os: [win32]
1060 +
1061 + lightningcss@1.32.0:
1062 + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
1063 + engines: {node: '>= 12.0.0'}
1064 +
1065 + lilconfig@3.1.3:
1066 + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
1067 + engines: {node: '>=14'}
1068 +
1069 + lru-cache@5.1.1:
1070 + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
1071 +
1072 + magic-string@0.30.21:
1073 + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
1074 +
1075 + mitt@3.0.1:
1076 + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
1077 +
1078 + modern-tar@0.7.6:
1079 + resolution: {integrity: sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg==}
1080 + engines: {node: '>=18.0.0'}
1081 +
1082 + ms@2.1.3:
1083 + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
1084 +
1085 + nanoid@3.3.13:
1086 + resolution: {integrity: sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q==}
1087 + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
1088 + hasBin: true
1089 +
1090 + next@15.5.19:
1091 + resolution: {integrity: sha512-xNOW6tYshGX1/Oi3F8uuk4gpDeWsSUE/1Z0G5uUMekIxaQ0xc03UXd9II0VQHYMWviMeA0OHpJFAKsHf8bTYVg==}
1092 + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
1093 + hasBin: true
1094 + peerDependencies:
1095 + '@opentelemetry/api': ^1.1.0
1096 + '@playwright/test': ^1.51.1
1097 + babel-plugin-react-compiler: '*'
1098 + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
1099 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
1100 + sass: ^1.3.0
1101 + peerDependenciesMeta:
1102 + '@opentelemetry/api':
1103 + optional: true
1104 + '@playwright/test':
1105 + optional: true
1106 + babel-plugin-react-compiler:
1107 + optional: true
1108 + sass:
1109 + optional: true
1110 +
1111 + node-releases@2.0.47:
1112 + resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==}
1113 + engines: {node: '>=18'}
1114 +
1115 + picocolors@1.1.1:
1116 + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
1117 +
1118 + picomatch@4.0.4:
1119 + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
1120 + engines: {node: '>=12'}
1121 +
1122 + postcss@8.4.31:
1123 + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
1124 + engines: {node: ^10 || ^12 || >=14}
1125 +
1126 + postcss@8.5.15:
1127 + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
1128 + engines: {node: ^10 || ^12 || >=14}
1129 +
1130 + puppeteer-core@25.1.0:
1131 + resolution: {integrity: sha512-jKzy5y4WG6uNuFbTWgW1D7mqoT9o0nllc/6a1DGF775T1mPmgw3scdFEtEq67yVFikavQmbYq6NLfbTfxHSlqQ==}
1132 + engines: {node: '>=22.12.0'}
1133 +
1134 + puppeteer@25.1.0:
1135 + resolution: {integrity: sha512-7L6/0JM7XStK99lIL4xQySyNEXNfII6pk0BxkI5kKBTOhR7AsoQiv067YTsE/rIXxQiq9ajlO4WcqBjS/FWK1A==}
1136 + engines: {node: '>=22.12.0'}
1137 + hasBin: true
1138 +
1139 + react-dom@19.2.7:
1140 + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==}
1141 + peerDependencies:
1142 + react: ^19.2.7
1143 +
1144 + react-refresh@0.17.0:
1145 + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
1146 + engines: {node: '>=0.10.0'}
1147 +
1148 + react@19.2.7:
1149 + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==}
1150 + engines: {node: '>=0.10.0'}
1151 +
1152 + require-directory@2.1.1:
1153 + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
1154 + engines: {node: '>=0.10.0'}
1155 +
1156 + rollup@4.61.1:
1157 + resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==}
1158 + engines: {node: '>=18.0.0', npm: '>=8.0.0'}
1159 + hasBin: true
1160 +
1161 + scheduler@0.27.0:
1162 + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
1163 +
1164 + semver@6.3.1:
1165 + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
1166 + hasBin: true
1167 +
1168 + semver@7.8.4:
1169 + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==}
1170 + engines: {node: '>=10'}
1171 + hasBin: true
1172 +
1173 + sharp@0.34.5:
1174 + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
1175 + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
1176 +
1177 + source-map-js@1.2.1:
1178 + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
1179 + engines: {node: '>=0.10.0'}
1180 +
1181 + string-width@4.2.3:
1182 + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
1183 + engines: {node: '>=8'}
1184 +
1185 + strip-ansi@6.0.1:
1186 + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
1187 + engines: {node: '>=8'}
1188 +
1189 + styled-jsx@5.1.6:
1190 + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
1191 + engines: {node: '>= 12.0.0'}
1192 + peerDependencies:
1193 + '@babel/core': '*'
1194 + babel-plugin-macros: '*'
1195 + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0'
1196 + peerDependenciesMeta:
1197 + '@babel/core':
1198 + optional: true
1199 + babel-plugin-macros:
1200 + optional: true
1201 +
1202 + tailwindcss@4.3.1:
1203 + resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==}
1204 +
1205 + tapable@2.3.3:
1206 + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
1207 + engines: {node: '>=6'}
1208 +
1209 + tinyglobby@0.2.17:
1210 + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
1211 + engines: {node: '>=12.0.0'}
1212 +
1213 + tslib@2.8.1:
1214 + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
1215 +
1216 + typed-query-selector@2.12.2:
1217 + resolution: {integrity: sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==}
1218 +
1219 + typescript@5.8.3:
1220 + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
1221 + engines: {node: '>=14.17'}
1222 + hasBin: true
1223 +
1224 + undici-types@6.21.0:
1225 + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
1226 +
1227 + update-browserslist-db@1.2.3:
1228 + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
1229 + hasBin: true
1230 + peerDependencies:
1231 + browserslist: '>= 4.21.0'
1232 +
1233 + vite@7.3.5:
1234 + resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==}
1235 + engines: {node: ^20.19.0 || >=22.12.0}
1236 + hasBin: true
1237 + peerDependencies:
1238 + '@types/node': ^20.19.0 || >=22.12.0
1239 + jiti: '>=1.21.0'
1240 + less: ^4.0.0
1241 + lightningcss: ^1.21.0
1242 + sass: ^1.70.0
1243 + sass-embedded: ^1.70.0
1244 + stylus: '>=0.54.8'
1245 + sugarss: ^5.0.0
1246 + terser: ^5.16.0
1247 + tsx: ^4.8.1
1248 + yaml: ^2.4.2
1249 + peerDependenciesMeta:
1250 + '@types/node':
1251 + optional: true
1252 + jiti:
1253 + optional: true
1254 + less:
1255 + optional: true
1256 + lightningcss:
1257 + optional: true
1258 + sass:
1259 + optional: true
1260 + sass-embedded:
1261 + optional: true
1262 + stylus:
1263 + optional: true
1264 + sugarss:
1265 + optional: true
1266 + terser:
1267 + optional: true
1268 + tsx:
1269 + optional: true
1270 + yaml:
1271 + optional: true
1272 +
1273 + webdriver-bidi-protocol@0.4.2:
1274 + resolution: {integrity: sha512-VSV+fzfChirL3e7jay2yUC7B4HQCGtEWEg/MSSQbK+qWbqeGlRLlXTzPpYr3XGUvbpDHumWZBJxgesg4N7dbtA==}
1275 +
1276 + wrap-ansi@7.0.0:
1277 + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
1278 + engines: {node: '>=10'}
1279 +
1280 + ws@8.21.0:
1281 + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==}
1282 + engines: {node: '>=10.0.0'}
1283 + peerDependencies:
1284 + bufferutil: ^4.0.1
1285 + utf-8-validate: '>=5.0.2'
1286 + peerDependenciesMeta:
1287 + bufferutil:
1288 + optional: true
1289 + utf-8-validate:
1290 + optional: true
1291 +
1292 + y18n@5.0.8:
1293 + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
1294 + engines: {node: '>=10'}
1295 +
1296 + yallist@3.1.1:
1297 + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
1298 +
1299 + yargs-parser@21.1.1:
1300 + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
1301 + engines: {node: '>=12'}
1302 +
1303 + yargs@17.7.3:
1304 + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==}
1305 + engines: {node: '>=12'}
1306 +
1307 + zod@3.25.76:
1308 + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
1309 +
1310 +snapshots:
1311 +
1312 + '@alloc/quick-lru@5.2.0': {}
1313 +
1314 + '@babel/code-frame@7.29.7':
1315 + dependencies:
1316 + '@babel/helper-validator-identifier': 7.29.7
1317 + js-tokens: 4.0.0
1318 + picocolors: 1.1.1
1319 +
1320 + '@babel/compat-data@7.29.7': {}
1321 +
1322 + '@babel/core@7.29.7':
1323 + dependencies:
1324 + '@babel/code-frame': 7.29.7
1325 + '@babel/generator': 7.29.7
1326 + '@babel/helper-compilation-targets': 7.29.7
1327 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
1328 + '@babel/helpers': 7.29.7
1329 + '@babel/parser': 7.29.7
1330 + '@babel/template': 7.29.7
1331 + '@babel/traverse': 7.29.7
1332 + '@babel/types': 7.29.7
1333 + '@jridgewell/remapping': 2.3.5
1334 + convert-source-map: 2.0.0
1335 + debug: 4.4.3
1336 + gensync: 1.0.0-beta.2
1337 + json5: 2.2.3
1338 + semver: 6.3.1
1339 + transitivePeerDependencies:
1340 + - supports-color
1341 +
1342 + '@babel/generator@7.29.7':
1343 + dependencies:
1344 + '@babel/parser': 7.29.7
1345 + '@babel/types': 7.29.7
1346 + '@jridgewell/gen-mapping': 0.3.13
1347 + '@jridgewell/trace-mapping': 0.3.31
1348 + jsesc: 3.1.0
1349 +
1350 + '@babel/helper-compilation-targets@7.29.7':
1351 + dependencies:
1352 + '@babel/compat-data': 7.29.7
1353 + '@babel/helper-validator-option': 7.29.7
1354 + browserslist: 4.28.2
1355 + lru-cache: 5.1.1
1356 + semver: 6.3.1
1357 +
1358 + '@babel/helper-globals@7.29.7': {}
1359 +
1360 + '@babel/helper-module-imports@7.29.7':
1361 + dependencies:
1362 + '@babel/traverse': 7.29.7
1363 + '@babel/types': 7.29.7
1364 + transitivePeerDependencies:
1365 + - supports-color
1366 +
1367 + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
1368 + dependencies:
1369 + '@babel/core': 7.29.7
1370 + '@babel/helper-module-imports': 7.29.7
1371 + '@babel/helper-validator-identifier': 7.29.7
1372 + '@babel/traverse': 7.29.7
1373 + transitivePeerDependencies:
1374 + - supports-color
1375 +
1376 + '@babel/helper-plugin-utils@7.29.7': {}
1377 +
1378 + '@babel/helper-string-parser@7.29.7': {}
1379 +
1380 + '@babel/helper-validator-identifier@7.29.7': {}
1381 +
1382 + '@babel/helper-validator-option@7.29.7': {}
1383 +
1384 + '@babel/helpers@7.29.7':
1385 + dependencies:
1386 + '@babel/template': 7.29.7
1387 + '@babel/types': 7.29.7
1388 +
1389 + '@babel/parser@7.29.7':
1390 + dependencies:
1391 + '@babel/types': 7.29.7
1392 +
1393 + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)':
1394 + dependencies:
1395 + '@babel/core': 7.29.7
1396 + '@babel/helper-plugin-utils': 7.29.7
1397 +
1398 + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)':
1399 + dependencies:
1400 + '@babel/core': 7.29.7
1401 + '@babel/helper-plugin-utils': 7.29.7
1402 +
1403 + '@babel/template@7.29.7':
1404 + dependencies:
1405 + '@babel/code-frame': 7.29.7
1406 + '@babel/parser': 7.29.7
1407 + '@babel/types': 7.29.7
1408 +
1409 + '@babel/traverse@7.29.7':
1410 + dependencies:
1411 + '@babel/code-frame': 7.29.7
1412 + '@babel/generator': 7.29.7
1413 + '@babel/helper-globals': 7.29.7
1414 + '@babel/parser': 7.29.7
1415 + '@babel/template': 7.29.7
1416 + '@babel/types': 7.29.7
1417 + debug: 4.4.3
1418 + transitivePeerDependencies:
1419 + - supports-color
1420 +
1421 + '@babel/types@7.29.7':
1422 + dependencies:
1423 + '@babel/helper-string-parser': 7.29.7
1424 + '@babel/helper-validator-identifier': 7.29.7
1425 +
1426 + '@emnapi/runtime@1.11.1':
1427 + dependencies:
1428 + tslib: 2.8.1
1429 + optional: true
1430 +
1431 + '@esbuild/aix-ppc64@0.27.7':
1432 + optional: true
1433 +
1434 + '@esbuild/android-arm64@0.27.7':
1435 + optional: true
1436 +
1437 + '@esbuild/android-arm@0.27.7':
1438 + optional: true
1439 +
1440 + '@esbuild/android-x64@0.27.7':
1441 + optional: true
1442 +
1443 + '@esbuild/darwin-arm64@0.27.7':
1444 + optional: true
1445 +
1446 + '@esbuild/darwin-x64@0.27.7':
1447 + optional: true
1448 +
1449 + '@esbuild/freebsd-arm64@0.27.7':
1450 + optional: true
1451 +
1452 + '@esbuild/freebsd-x64@0.27.7':
1453 + optional: true
1454 +
1455 + '@esbuild/linux-arm64@0.27.7':
1456 + optional: true
1457 +
1458 + '@esbuild/linux-arm@0.27.7':
1459 + optional: true
1460 +
1461 + '@esbuild/linux-ia32@0.27.7':
1462 + optional: true
1463 +
1464 + '@esbuild/linux-loong64@0.27.7':
1465 + optional: true
1466 +
1467 + '@esbuild/linux-mips64el@0.27.7':
1468 + optional: true
1469 +
1470 + '@esbuild/linux-ppc64@0.27.7':
1471 + optional: true
1472 +
1473 + '@esbuild/linux-riscv64@0.27.7':
1474 + optional: true
1475 +
1476 + '@esbuild/linux-s390x@0.27.7':
1477 + optional: true
1478 +
1479 + '@esbuild/linux-x64@0.27.7':
1480 + optional: true
1481 +
1482 + '@esbuild/netbsd-arm64@0.27.7':
1483 + optional: true
1484 +
1485 + '@esbuild/netbsd-x64@0.27.7':
1486 + optional: true
1487 +
1488 + '@esbuild/openbsd-arm64@0.27.7':
1489 + optional: true
1490 +
1491 + '@esbuild/openbsd-x64@0.27.7':
1492 + optional: true
1493 +
1494 + '@esbuild/openharmony-arm64@0.27.7':
1495 + optional: true
1496 +
1497 + '@esbuild/sunos-x64@0.27.7':
1498 + optional: true
1499 +
1500 + '@esbuild/win32-arm64@0.27.7':
1501 + optional: true
1502 +
1503 + '@esbuild/win32-ia32@0.27.7':
1504 + optional: true
1505 +
1506 + '@esbuild/win32-x64@0.27.7':
1507 + optional: true
1508 +
1509 + '@img/colour@1.1.0':
1510 + optional: true
1511 +
1512 + '@img/sharp-darwin-arm64@0.34.5':
1513 + optionalDependencies:
1514 + '@img/sharp-libvips-darwin-arm64': 1.2.4
1515 + optional: true
1516 +
1517 + '@img/sharp-darwin-x64@0.34.5':
1518 + optionalDependencies:
1519 + '@img/sharp-libvips-darwin-x64': 1.2.4
1520 + optional: true
1521 +
1522 + '@img/sharp-libvips-darwin-arm64@1.2.4':
1523 + optional: true
1524 +
1525 + '@img/sharp-libvips-darwin-x64@1.2.4':
1526 + optional: true
1527 +
1528 + '@img/sharp-libvips-linux-arm64@1.2.4':
1529 + optional: true
1530 +
1531 + '@img/sharp-libvips-linux-arm@1.2.4':
1532 + optional: true
1533 +
1534 + '@img/sharp-libvips-linux-ppc64@1.2.4':
1535 + optional: true
1536 +
1537 + '@img/sharp-libvips-linux-riscv64@1.2.4':
1538 + optional: true
1539 +
1540 + '@img/sharp-libvips-linux-s390x@1.2.4':
1541 + optional: true
1542 +
1543 + '@img/sharp-libvips-linux-x64@1.2.4':
1544 + optional: true
1545 +
1546 + '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
1547 + optional: true
1548 +
1549 + '@img/sharp-libvips-linuxmusl-x64@1.2.4':
1550 + optional: true
1551 +
1552 + '@img/sharp-linux-arm64@0.34.5':
1553 + optionalDependencies:
1554 + '@img/sharp-libvips-linux-arm64': 1.2.4
1555 + optional: true
1556 +
1557 + '@img/sharp-linux-arm@0.34.5':
1558 + optionalDependencies:
1559 + '@img/sharp-libvips-linux-arm': 1.2.4
1560 + optional: true
1561 +
1562 + '@img/sharp-linux-ppc64@0.34.5':
1563 + optionalDependencies:
1564 + '@img/sharp-libvips-linux-ppc64': 1.2.4
1565 + optional: true
1566 +
1567 + '@img/sharp-linux-riscv64@0.34.5':
1568 + optionalDependencies:
1569 + '@img/sharp-libvips-linux-riscv64': 1.2.4
1570 + optional: true
1571 +
1572 + '@img/sharp-linux-s390x@0.34.5':
1573 + optionalDependencies:
1574 + '@img/sharp-libvips-linux-s390x': 1.2.4
1575 + optional: true
1576 +
1577 + '@img/sharp-linux-x64@0.34.5':
1578 + optionalDependencies:
1579 + '@img/sharp-libvips-linux-x64': 1.2.4
1580 + optional: true
1581 +
1582 + '@img/sharp-linuxmusl-arm64@0.34.5':
1583 + optionalDependencies:
1584 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4
1585 + optional: true
1586 +
1587 + '@img/sharp-linuxmusl-x64@0.34.5':
1588 + optionalDependencies:
1589 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4
1590 + optional: true
1591 +
1592 + '@img/sharp-wasm32@0.34.5':
1593 + dependencies:
1594 + '@emnapi/runtime': 1.11.1
1595 + optional: true
1596 +
1597 + '@img/sharp-win32-arm64@0.34.5':
1598 + optional: true
1599 +
1600 + '@img/sharp-win32-ia32@0.34.5':
1601 + optional: true
1602 +
1603 + '@img/sharp-win32-x64@0.34.5':
1604 + optional: true
1605 +
1606 + '@jridgewell/gen-mapping@0.3.13':
1607 + dependencies:
1608 + '@jridgewell/sourcemap-codec': 1.5.5
1609 + '@jridgewell/trace-mapping': 0.3.31
1610 +
1611 + '@jridgewell/remapping@2.3.5':
1612 + dependencies:
1613 + '@jridgewell/gen-mapping': 0.3.13
1614 + '@jridgewell/trace-mapping': 0.3.31
1615 +
1616 + '@jridgewell/resolve-uri@3.1.2': {}
1617 +
1618 + '@jridgewell/sourcemap-codec@1.5.5': {}
1619 +
1620 + '@jridgewell/trace-mapping@0.3.31':
1621 + dependencies:
1622 + '@jridgewell/resolve-uri': 3.1.2
1623 + '@jridgewell/sourcemap-codec': 1.5.5
1624 +
1625 + '@next/env@15.5.19': {}
1626 +
1627 + '@next/swc-darwin-arm64@15.5.19':
1628 + optional: true
1629 +
1630 + '@next/swc-darwin-x64@15.5.19':
1631 + optional: true
1632 +
1633 + '@next/swc-linux-arm64-gnu@15.5.19':
1634 + optional: true
1635 +
1636 + '@next/swc-linux-arm64-musl@15.5.19':
1637 + optional: true
1638 +
1639 + '@next/swc-linux-x64-gnu@15.5.19':
1640 + optional: true
1641 +
1642 + '@next/swc-linux-x64-musl@15.5.19':
1643 + optional: true
1644 +
1645 + '@next/swc-win32-arm64-msvc@15.5.19':
1646 + optional: true
1647 +
1648 + '@next/swc-win32-x64-msvc@15.5.19':
1649 + optional: true
1650 +
1651 + '@puppeteer/browsers@3.0.4':
1652 + dependencies:
1653 + modern-tar: 0.7.6
1654 + yargs: 17.7.3
1655 +
1656 + '@rolldown/pluginutils@1.0.0-beta.27': {}
1657 +
1658 + '@rollup/rollup-android-arm-eabi@4.61.1':
1659 + optional: true
1660 +
1661 + '@rollup/rollup-android-arm64@4.61.1':
1662 + optional: true
1663 +
1664 + '@rollup/rollup-darwin-arm64@4.61.1':
1665 + optional: true
1666 +
1667 + '@rollup/rollup-darwin-x64@4.61.1':
1668 + optional: true
1669 +
1670 + '@rollup/rollup-freebsd-arm64@4.61.1':
1671 + optional: true
1672 +
1673 + '@rollup/rollup-freebsd-x64@4.61.1':
1674 + optional: true
1675 +
1676 + '@rollup/rollup-linux-arm-gnueabihf@4.61.1':
1677 + optional: true
1678 +
1679 + '@rollup/rollup-linux-arm-musleabihf@4.61.1':
1680 + optional: true
1681 +
1682 + '@rollup/rollup-linux-arm64-gnu@4.61.1':
1683 + optional: true
1684 +
1685 + '@rollup/rollup-linux-arm64-musl@4.61.1':
1686 + optional: true
1687 +
1688 + '@rollup/rollup-linux-loong64-gnu@4.61.1':
1689 + optional: true
1690 +
1691 + '@rollup/rollup-linux-loong64-musl@4.61.1':
1692 + optional: true
1693 +
1694 + '@rollup/rollup-linux-ppc64-gnu@4.61.1':
1695 + optional: true
1696 +
1697 + '@rollup/rollup-linux-ppc64-musl@4.61.1':
1698 + optional: true
1699 +
1700 + '@rollup/rollup-linux-riscv64-gnu@4.61.1':
1701 + optional: true
1702 +
1703 + '@rollup/rollup-linux-riscv64-musl@4.61.1':
1704 + optional: true
1705 +
1706 + '@rollup/rollup-linux-s390x-gnu@4.61.1':
1707 + optional: true
1708 +
1709 + '@rollup/rollup-linux-x64-gnu@4.61.1':
1710 + optional: true
1711 +
1712 + '@rollup/rollup-linux-x64-musl@4.61.1':
1713 + optional: true
1714 +
1715 + '@rollup/rollup-openbsd-x64@4.61.1':
1716 + optional: true
1717 +
1718 + '@rollup/rollup-openharmony-arm64@4.61.1':
1719 + optional: true
1720 +
1721 + '@rollup/rollup-win32-arm64-msvc@4.61.1':
1722 + optional: true
1723 +
1724 + '@rollup/rollup-win32-ia32-msvc@4.61.1':
1725 + optional: true
1726 +
1727 + '@rollup/rollup-win32-x64-gnu@4.61.1':
1728 + optional: true
1729 +
1730 + '@rollup/rollup-win32-x64-msvc@4.61.1':
1731 + optional: true
1732 +
1733 + '@swc/helpers@0.5.15':
1734 + dependencies:
1735 + tslib: 2.8.1
1736 +
1737 + '@tailwindcss/node@4.3.1':
1738 + dependencies:
1739 + '@jridgewell/remapping': 2.3.5
1740 + enhanced-resolve: 5.21.6
1741 + jiti: 2.7.0
1742 + lightningcss: 1.32.0
1743 + magic-string: 0.30.21
1744 + source-map-js: 1.2.1
1745 + tailwindcss: 4.3.1
1746 +
1747 + '@tailwindcss/oxide-android-arm64@4.3.1':
1748 + optional: true
1749 +
1750 + '@tailwindcss/oxide-darwin-arm64@4.3.1':
1751 + optional: true
1752 +
1753 + '@tailwindcss/oxide-darwin-x64@4.3.1':
1754 + optional: true
1755 +
1756 + '@tailwindcss/oxide-freebsd-x64@4.3.1':
1757 + optional: true
1758 +
1759 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1':
1760 + optional: true
1761 +
1762 + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1':
1763 + optional: true
1764 +
1765 + '@tailwindcss/oxide-linux-arm64-musl@4.3.1':
1766 + optional: true
1767 +
1768 + '@tailwindcss/oxide-linux-x64-gnu@4.3.1':
1769 + optional: true
1770 +
1771 + '@tailwindcss/oxide-linux-x64-musl@4.3.1':
1772 + optional: true
1773 +
1774 + '@tailwindcss/oxide-wasm32-wasi@4.3.1':
1775 + optional: true
1776 +
1777 + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1':
1778 + optional: true
1779 +
1780 + '@tailwindcss/oxide-win32-x64-msvc@4.3.1':
1781 + optional: true
1782 +
1783 + '@tailwindcss/oxide@4.3.1':
1784 + optionalDependencies:
1785 + '@tailwindcss/oxide-android-arm64': 4.3.1
1786 + '@tailwindcss/oxide-darwin-arm64': 4.3.1
1787 + '@tailwindcss/oxide-darwin-x64': 4.3.1
1788 + '@tailwindcss/oxide-freebsd-x64': 4.3.1
1789 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1
1790 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1
1791 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.1
1792 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.1
1793 + '@tailwindcss/oxide-linux-x64-musl': 4.3.1
1794 + '@tailwindcss/oxide-wasm32-wasi': 4.3.1
1795 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1
1796 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.1
1797 +
1798 + '@tailwindcss/postcss@4.3.1':
1799 + dependencies:
1800 + '@alloc/quick-lru': 5.2.0
1801 + '@tailwindcss/node': 4.3.1
1802 + '@tailwindcss/oxide': 4.3.1
1803 + postcss: 8.5.15
1804 + tailwindcss: 4.3.1
1805 +
1806 + '@tauri-apps/api@2.11.0': {}
1807 +
1808 + '@tauri-apps/cli-darwin-arm64@2.11.2':
1809 + optional: true
1810 +
1811 + '@tauri-apps/cli-darwin-x64@2.11.2':
1812 + optional: true
1813 +
1814 + '@tauri-apps/cli-linux-arm-gnueabihf@2.11.2':
1815 + optional: true
1816 +
1817 + '@tauri-apps/cli-linux-arm64-gnu@2.11.2':
1818 + optional: true
1819 +
1820 + '@tauri-apps/cli-linux-arm64-musl@2.11.2':
1821 + optional: true
1822 +
1823 + '@tauri-apps/cli-linux-riscv64-gnu@2.11.2':
1824 + optional: true
1825 +
1826 + '@tauri-apps/cli-linux-x64-gnu@2.11.2':
1827 + optional: true
1828 +
1829 + '@tauri-apps/cli-linux-x64-musl@2.11.2':
1830 + optional: true
1831 +
1832 + '@tauri-apps/cli-win32-arm64-msvc@2.11.2':
1833 + optional: true
1834 +
1835 + '@tauri-apps/cli-win32-ia32-msvc@2.11.2':
1836 + optional: true
1837 +
1838 + '@tauri-apps/cli-win32-x64-msvc@2.11.2':
1839 + optional: true
1840 +
1841 + '@tauri-apps/cli@2.11.2':
1842 + optionalDependencies:
1843 + '@tauri-apps/cli-darwin-arm64': 2.11.2
1844 + '@tauri-apps/cli-darwin-x64': 2.11.2
1845 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.11.2
1846 + '@tauri-apps/cli-linux-arm64-gnu': 2.11.2
1847 + '@tauri-apps/cli-linux-arm64-musl': 2.11.2
1848 + '@tauri-apps/cli-linux-riscv64-gnu': 2.11.2
1849 + '@tauri-apps/cli-linux-x64-gnu': 2.11.2
1850 + '@tauri-apps/cli-linux-x64-musl': 2.11.2
1851 + '@tauri-apps/cli-win32-arm64-msvc': 2.11.2
1852 + '@tauri-apps/cli-win32-ia32-msvc': 2.11.2
1853 + '@tauri-apps/cli-win32-x64-msvc': 2.11.2
1854 +
1855 + '@tauri-apps/plugin-opener@2.5.4':
1856 + dependencies:
1857 + '@tauri-apps/api': 2.11.0
1858 +
1859 + '@types/babel__core@7.20.5':
1860 + dependencies:
1861 + '@babel/parser': 7.29.7
1862 + '@babel/types': 7.29.7
1863 + '@types/babel__generator': 7.27.0
1864 + '@types/babel__template': 7.4.4
1865 + '@types/babel__traverse': 7.28.0
1866 +
1867 + '@types/babel__generator@7.27.0':
1868 + dependencies:
1869 + '@babel/types': 7.29.7
1870 +
1871 + '@types/babel__template@7.4.4':
1872 + dependencies:
1873 + '@babel/parser': 7.29.7
1874 + '@babel/types': 7.29.7
1875 +
1876 + '@types/babel__traverse@7.28.0':
1877 + dependencies:
1878 + '@babel/types': 7.29.7
1879 +
1880 + '@types/estree@1.0.9': {}
1881 +
1882 + '@types/node@22.19.21':
1883 + dependencies:
1884 + undici-types: 6.21.0
1885 +
1886 + '@types/react-dom@19.2.3(@types/react@19.2.17)':
1887 + dependencies:
1888 + '@types/react': 19.2.17
1889 +
1890 + '@types/react@19.2.17':
1891 + dependencies:
1892 + csstype: 3.2.3
1893 +
1894 + '@vitejs/plugin-react@4.7.0(vite@7.3.5(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0))':
1895 + dependencies:
1896 + '@babel/core': 7.29.7
1897 + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7)
1898 + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7)
1899 + '@rolldown/pluginutils': 1.0.0-beta.27
1900 + '@types/babel__core': 7.20.5
1901 + react-refresh: 0.17.0
1902 + vite: 7.3.5(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0)
1903 + transitivePeerDependencies:
1904 + - supports-color
1905 +
1906 + ansi-regex@5.0.1: {}
1907 +
1908 + ansi-styles@4.3.0:
1909 + dependencies:
1910 + color-convert: 2.0.1
1911 +
1912 + baseline-browser-mapping@2.10.37: {}
1913 +
1914 + browserslist@4.28.2:
1915 + dependencies:
1916 + baseline-browser-mapping: 2.10.37
1917 + caniuse-lite: 1.0.30001799
1918 + electron-to-chromium: 1.5.372
1919 + node-releases: 2.0.47
1920 + update-browserslist-db: 1.2.3(browserslist@4.28.2)
1921 +
1922 + caniuse-lite@1.0.30001799: {}
1923 +
1924 + chromium-bidi@16.0.1(devtools-protocol@0.0.1624250):
1925 + dependencies:
1926 + devtools-protocol: 0.0.1624250
1927 + mitt: 3.0.1
1928 + zod: 3.25.76
1929 +
1930 + client-only@0.0.1: {}
1931 +
1932 + cliui@8.0.1:
1933 + dependencies:
1934 + string-width: 4.2.3
1935 + strip-ansi: 6.0.1
1936 + wrap-ansi: 7.0.0
1937 +
1938 + color-convert@2.0.1:
1939 + dependencies:
1940 + color-name: 1.1.4
1941 +
1942 + color-name@1.1.4: {}
1943 +
1944 + convert-source-map@2.0.0: {}
1945 +
1946 + csstype@3.2.3: {}
1947 +
1948 + debug@4.4.3:
1949 + dependencies:
1950 + ms: 2.1.3
1951 +
1952 + detect-libc@2.1.2: {}
1953 +
1954 + devtools-protocol@0.0.1624250: {}
1955 +
1956 + electron-to-chromium@1.5.372: {}
1957 +
1958 + emoji-regex@8.0.0: {}
1959 +
1960 + enhanced-resolve@5.21.6:
1961 + dependencies:
1962 + graceful-fs: 4.2.11
1963 + tapable: 2.3.3
1964 +
1965 + esbuild@0.27.7:
1966 + optionalDependencies:
1967 + '@esbuild/aix-ppc64': 0.27.7
1968 + '@esbuild/android-arm': 0.27.7
1969 + '@esbuild/android-arm64': 0.27.7
1970 + '@esbuild/android-x64': 0.27.7
1971 + '@esbuild/darwin-arm64': 0.27.7
1972 + '@esbuild/darwin-x64': 0.27.7
1973 + '@esbuild/freebsd-arm64': 0.27.7
1974 + '@esbuild/freebsd-x64': 0.27.7
1975 + '@esbuild/linux-arm': 0.27.7
1976 + '@esbuild/linux-arm64': 0.27.7
1977 + '@esbuild/linux-ia32': 0.27.7
1978 + '@esbuild/linux-loong64': 0.27.7
1979 + '@esbuild/linux-mips64el': 0.27.7
1980 + '@esbuild/linux-ppc64': 0.27.7
1981 + '@esbuild/linux-riscv64': 0.27.7
1982 + '@esbuild/linux-s390x': 0.27.7
1983 + '@esbuild/linux-x64': 0.27.7
1984 + '@esbuild/netbsd-arm64': 0.27.7
1985 + '@esbuild/netbsd-x64': 0.27.7
1986 + '@esbuild/openbsd-arm64': 0.27.7
1987 + '@esbuild/openbsd-x64': 0.27.7
1988 + '@esbuild/openharmony-arm64': 0.27.7
1989 + '@esbuild/sunos-x64': 0.27.7
1990 + '@esbuild/win32-arm64': 0.27.7
1991 + '@esbuild/win32-ia32': 0.27.7
1992 + '@esbuild/win32-x64': 0.27.7
1993 +
1994 + escalade@3.2.0: {}
1995 +
1996 + fdir@6.5.0(picomatch@4.0.4):
1997 + optionalDependencies:
1998 + picomatch: 4.0.4
1999 +
2000 + fsevents@2.3.3:
2001 + optional: true
2002 +
2003 + gensync@1.0.0-beta.2: {}
2004 +
2005 + get-caller-file@2.0.5: {}
2006 +
2007 + graceful-fs@4.2.11: {}
2008 +
2009 + is-fullwidth-code-point@3.0.0: {}
2010 +
2011 + jiti@2.7.0: {}
2012 +
2013 + js-tokens@4.0.0: {}
2014 +
2015 + jsesc@3.1.0: {}
2016 +
2017 + json5@2.2.3: {}
2018 +
2019 + lightningcss-android-arm64@1.32.0:
2020 + optional: true
2021 +
2022 + lightningcss-darwin-arm64@1.32.0:
2023 + optional: true
2024 +
2025 + lightningcss-darwin-x64@1.32.0:
2026 + optional: true
2027 +
2028 + lightningcss-freebsd-x64@1.32.0:
2029 + optional: true
2030 +
2031 + lightningcss-linux-arm-gnueabihf@1.32.0:
2032 + optional: true
2033 +
2034 + lightningcss-linux-arm64-gnu@1.32.0:
2035 + optional: true
2036 +
2037 + lightningcss-linux-arm64-musl@1.32.0:
2038 + optional: true
2039 +
2040 + lightningcss-linux-x64-gnu@1.32.0:
2041 + optional: true
2042 +
2043 + lightningcss-linux-x64-musl@1.32.0:
2044 + optional: true
2045 +
2046 + lightningcss-win32-arm64-msvc@1.32.0:
2047 + optional: true
2048 +
2049 + lightningcss-win32-x64-msvc@1.32.0:
2050 + optional: true
2051 +
2052 + lightningcss@1.32.0:
2053 + dependencies:
2054 + detect-libc: 2.1.2
2055 + optionalDependencies:
2056 + lightningcss-android-arm64: 1.32.0
2057 + lightningcss-darwin-arm64: 1.32.0
2058 + lightningcss-darwin-x64: 1.32.0
2059 + lightningcss-freebsd-x64: 1.32.0
2060 + lightningcss-linux-arm-gnueabihf: 1.32.0
2061 + lightningcss-linux-arm64-gnu: 1.32.0
2062 + lightningcss-linux-arm64-musl: 1.32.0
2063 + lightningcss-linux-x64-gnu: 1.32.0
2064 + lightningcss-linux-x64-musl: 1.32.0
2065 + lightningcss-win32-arm64-msvc: 1.32.0
2066 + lightningcss-win32-x64-msvc: 1.32.0
2067 +
2068 + lilconfig@3.1.3: {}
2069 +
2070 + lru-cache@5.1.1:
2071 + dependencies:
2072 + yallist: 3.1.1
2073 +
2074 + magic-string@0.30.21:
2075 + dependencies:
2076 + '@jridgewell/sourcemap-codec': 1.5.5
2077 +
2078 + mitt@3.0.1: {}
2079 +
2080 + modern-tar@0.7.6: {}
2081 +
2082 + ms@2.1.3: {}
2083 +
2084 + nanoid@3.3.13: {}
2085 +
2086 + next@15.5.19(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
2087 + dependencies:
2088 + '@next/env': 15.5.19
2089 + '@swc/helpers': 0.5.15
2090 + caniuse-lite: 1.0.30001799
2091 + postcss: 8.4.31
2092 + react: 19.2.7
2093 + react-dom: 19.2.7(react@19.2.7)
2094 + styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.7)
2095 + optionalDependencies:
2096 + '@next/swc-darwin-arm64': 15.5.19
2097 + '@next/swc-darwin-x64': 15.5.19
2098 + '@next/swc-linux-arm64-gnu': 15.5.19
2099 + '@next/swc-linux-arm64-musl': 15.5.19
2100 + '@next/swc-linux-x64-gnu': 15.5.19
2101 + '@next/swc-linux-x64-musl': 15.5.19
2102 + '@next/swc-win32-arm64-msvc': 15.5.19
2103 + '@next/swc-win32-x64-msvc': 15.5.19
2104 + sharp: 0.34.5
2105 + transitivePeerDependencies:
2106 + - '@babel/core'
2107 + - babel-plugin-macros
2108 +
2109 + node-releases@2.0.47: {}
2110 +
2111 + picocolors@1.1.1: {}
2112 +
2113 + picomatch@4.0.4: {}
2114 +
2115 + postcss@8.4.31:
2116 + dependencies:
2117 + nanoid: 3.3.13
2118 + picocolors: 1.1.1
2119 + source-map-js: 1.2.1
2120 +
2121 + postcss@8.5.15:
2122 + dependencies:
2123 + nanoid: 3.3.13
2124 + picocolors: 1.1.1
2125 + source-map-js: 1.2.1
2126 +
2127 + puppeteer-core@25.1.0:
2128 + dependencies:
2129 + '@puppeteer/browsers': 3.0.4
2130 + chromium-bidi: 16.0.1(devtools-protocol@0.0.1624250)
2131 + devtools-protocol: 0.0.1624250
2132 + typed-query-selector: 2.12.2
2133 + webdriver-bidi-protocol: 0.4.2
2134 + ws: 8.21.0
2135 + transitivePeerDependencies:
2136 + - bufferutil
2137 + - proxy-agent
2138 + - utf-8-validate
2139 +
2140 + puppeteer@25.1.0:
2141 + dependencies:
2142 + '@puppeteer/browsers': 3.0.4
2143 + chromium-bidi: 16.0.1(devtools-protocol@0.0.1624250)
2144 + devtools-protocol: 0.0.1624250
2145 + lilconfig: 3.1.3
2146 + puppeteer-core: 25.1.0
2147 + typed-query-selector: 2.12.2
2148 + transitivePeerDependencies:
2149 + - bufferutil
2150 + - proxy-agent
2151 + - utf-8-validate
2152 +
2153 + react-dom@19.2.7(react@19.2.7):
2154 + dependencies:
2155 + react: 19.2.7
2156 + scheduler: 0.27.0
2157 +
2158 + react-refresh@0.17.0: {}
2159 +
2160 + react@19.2.7: {}
2161 +
2162 + require-directory@2.1.1: {}
2163 +
2164 + rollup@4.61.1:
2165 + dependencies:
2166 + '@types/estree': 1.0.9
2167 + optionalDependencies:
2168 + '@rollup/rollup-android-arm-eabi': 4.61.1
2169 + '@rollup/rollup-android-arm64': 4.61.1
2170 + '@rollup/rollup-darwin-arm64': 4.61.1
2171 + '@rollup/rollup-darwin-x64': 4.61.1
2172 + '@rollup/rollup-freebsd-arm64': 4.61.1
2173 + '@rollup/rollup-freebsd-x64': 4.61.1
2174 + '@rollup/rollup-linux-arm-gnueabihf': 4.61.1
2175 + '@rollup/rollup-linux-arm-musleabihf': 4.61.1
2176 + '@rollup/rollup-linux-arm64-gnu': 4.61.1
2177 + '@rollup/rollup-linux-arm64-musl': 4.61.1
2178 + '@rollup/rollup-linux-loong64-gnu': 4.61.1
2179 + '@rollup/rollup-linux-loong64-musl': 4.61.1
2180 + '@rollup/rollup-linux-ppc64-gnu': 4.61.1
2181 + '@rollup/rollup-linux-ppc64-musl': 4.61.1
2182 + '@rollup/rollup-linux-riscv64-gnu': 4.61.1
2183 + '@rollup/rollup-linux-riscv64-musl': 4.61.1
2184 + '@rollup/rollup-linux-s390x-gnu': 4.61.1
2185 + '@rollup/rollup-linux-x64-gnu': 4.61.1
2186 + '@rollup/rollup-linux-x64-musl': 4.61.1
2187 + '@rollup/rollup-openbsd-x64': 4.61.1
2188 + '@rollup/rollup-openharmony-arm64': 4.61.1
2189 + '@rollup/rollup-win32-arm64-msvc': 4.61.1
2190 + '@rollup/rollup-win32-ia32-msvc': 4.61.1
2191 + '@rollup/rollup-win32-x64-gnu': 4.61.1
2192 + '@rollup/rollup-win32-x64-msvc': 4.61.1
2193 + fsevents: 2.3.3
2194 +
2195 + scheduler@0.27.0: {}
2196 +
2197 + semver@6.3.1: {}
2198 +
2199 + semver@7.8.4:
2200 + optional: true
2201 +
2202 + sharp@0.34.5:
2203 + dependencies:
2204 + '@img/colour': 1.1.0
2205 + detect-libc: 2.1.2
2206 + semver: 7.8.4
2207 + optionalDependencies:
2208 + '@img/sharp-darwin-arm64': 0.34.5
2209 + '@img/sharp-darwin-x64': 0.34.5
2210 + '@img/sharp-libvips-darwin-arm64': 1.2.4
2211 + '@img/sharp-libvips-darwin-x64': 1.2.4
2212 + '@img/sharp-libvips-linux-arm': 1.2.4
2213 + '@img/sharp-libvips-linux-arm64': 1.2.4
2214 + '@img/sharp-libvips-linux-ppc64': 1.2.4
2215 + '@img/sharp-libvips-linux-riscv64': 1.2.4
2216 + '@img/sharp-libvips-linux-s390x': 1.2.4
2217 + '@img/sharp-libvips-linux-x64': 1.2.4
2218 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4
2219 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4
2220 + '@img/sharp-linux-arm': 0.34.5
2221 + '@img/sharp-linux-arm64': 0.34.5
2222 + '@img/sharp-linux-ppc64': 0.34.5
2223 + '@img/sharp-linux-riscv64': 0.34.5
2224 + '@img/sharp-linux-s390x': 0.34.5
2225 + '@img/sharp-linux-x64': 0.34.5
2226 + '@img/sharp-linuxmusl-arm64': 0.34.5
2227 + '@img/sharp-linuxmusl-x64': 0.34.5
2228 + '@img/sharp-wasm32': 0.34.5
2229 + '@img/sharp-win32-arm64': 0.34.5
2230 + '@img/sharp-win32-ia32': 0.34.5
2231 + '@img/sharp-win32-x64': 0.34.5
2232 + optional: true
2233 +
2234 + source-map-js@1.2.1: {}
2235 +
2236 + string-width@4.2.3:
2237 + dependencies:
2238 + emoji-regex: 8.0.0
2239 + is-fullwidth-code-point: 3.0.0
2240 + strip-ansi: 6.0.1
2241 +
2242 + strip-ansi@6.0.1:
2243 + dependencies:
2244 + ansi-regex: 5.0.1
2245 +
2246 + styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.7):
2247 + dependencies:
2248 + client-only: 0.0.1
2249 + react: 19.2.7
2250 + optionalDependencies:
2251 + '@babel/core': 7.29.7
2252 +
2253 + tailwindcss@4.3.1: {}
2254 +
2255 + tapable@2.3.3: {}
2256 +
2257 + tinyglobby@0.2.17:
2258 + dependencies:
2259 + fdir: 6.5.0(picomatch@4.0.4)
2260 + picomatch: 4.0.4
2261 +
2262 + tslib@2.8.1: {}
2263 +
2264 + typed-query-selector@2.12.2: {}
2265 +
2266 + typescript@5.8.3: {}
2267 +
2268 + undici-types@6.21.0: {}
2269 +
2270 + update-browserslist-db@1.2.3(browserslist@4.28.2):
2271 + dependencies:
2272 + browserslist: 4.28.2
2273 + escalade: 3.2.0
2274 + picocolors: 1.1.1
2275 +
2276 + vite@7.3.5(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0):
2277 + dependencies:
2278 + esbuild: 0.27.7
2279 + fdir: 6.5.0(picomatch@4.0.4)
2280 + picomatch: 4.0.4
2281 + postcss: 8.5.15
2282 + rollup: 4.61.1
2283 + tinyglobby: 0.2.17
2284 + optionalDependencies:
2285 + '@types/node': 22.19.21
2286 + fsevents: 2.3.3
2287 + jiti: 2.7.0
2288 + lightningcss: 1.32.0
2289 +
2290 + webdriver-bidi-protocol@0.4.2: {}
2291 +
2292 + wrap-ansi@7.0.0:
2293 + dependencies:
2294 + ansi-styles: 4.3.0
2295 + string-width: 4.2.3
2296 + strip-ansi: 6.0.1
2297 +
2298 + ws@8.21.0: {}
2299 +
2300 + y18n@5.0.8: {}
2301 +
2302 + yallist@3.1.1: {}
2303 +
2304 + yargs-parser@21.1.1: {}
2305 +
2306 + yargs@17.7.3:
2307 + dependencies:
2308 + cliui: 8.0.1
2309 + escalade: 3.2.0
2310 + get-caller-file: 2.0.5
2311 + require-directory: 2.1.1
2312 + string-width: 4.2.3
2313 + y18n: 5.0.8
2314 + yargs-parser: 21.1.1
2315 +
2316 + zod@3.25.76: {}
pnpm-workspace.yaml new
+7
@@ -0,0 +1,7 @@
1 +packages:
2 + - "web"
3 +
4 +onlyBuiltDependencies:
5 + - esbuild
6 + - "@tailwindcss/oxide"
7 + - sharp
public/tauri.svg new
+6
@@ -0,0 +1,6 @@
1 +<svg width="206" height="231" viewBox="0 0 206 231" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<path d="M143.143 84C143.143 96.1503 133.293 106 121.143 106C108.992 106 99.1426 96.1503 99.1426 84C99.1426 71.8497 108.992 62 121.143 62C133.293 62 143.143 71.8497 143.143 84Z" fill="#FFC131"/>
3 +<ellipse cx="84.1426" cy="147" rx="22" ry="22" transform="rotate(180 84.1426 147)" fill="#24C8DB"/>
4 +<path fill-rule="evenodd" clip-rule="evenodd" d="M166.738 154.548C157.86 160.286 148.023 164.269 137.757 166.341C139.858 160.282 141 153.774 141 147C141 144.543 140.85 142.121 140.558 139.743C144.975 138.204 149.215 136.139 153.183 133.575C162.73 127.404 170.292 118.608 174.961 108.244C179.63 97.8797 181.207 86.3876 179.502 75.1487C177.798 63.9098 172.884 53.4021 165.352 44.8883C157.82 36.3744 147.99 30.2165 137.042 27.1546C126.095 24.0926 114.496 24.2568 103.64 27.6274C92.7839 30.998 83.1319 37.4317 75.8437 46.1553C74.9102 47.2727 74.0206 48.4216 73.176 49.5993C61.9292 50.8488 51.0363 54.0318 40.9629 58.9556C44.2417 48.4586 49.5653 38.6591 56.679 30.1442C67.0505 17.7298 80.7861 8.57426 96.2354 3.77762C111.685 -1.01901 128.19 -1.25267 143.769 3.10474C159.348 7.46215 173.337 16.2252 184.056 28.3411C194.775 40.457 201.767 55.4101 204.193 71.404C206.619 87.3978 204.374 103.752 197.73 118.501C191.086 133.25 180.324 145.767 166.738 154.548ZM41.9631 74.275L62.5557 76.8042C63.0459 72.813 63.9401 68.9018 65.2138 65.1274C57.0465 67.0016 49.2088 70.087 41.9631 74.275Z" fill="#FFC131"/>
5 +<path fill-rule="evenodd" clip-rule="evenodd" d="M38.4045 76.4519C47.3493 70.6709 57.2677 66.6712 67.6171 64.6132C65.2774 70.9669 64 77.8343 64 85.0001C64 87.1434 64.1143 89.26 64.3371 91.3442C60.0093 92.8732 55.8533 94.9092 51.9599 97.4256C42.4128 103.596 34.8505 112.392 30.1816 122.756C25.5126 133.12 23.9357 144.612 25.6403 155.851C27.3449 167.09 32.2584 177.598 39.7906 186.112C47.3227 194.626 57.153 200.784 68.1003 203.846C79.0476 206.907 90.6462 206.743 101.502 203.373C112.359 200.002 122.011 193.568 129.299 184.845C130.237 183.722 131.131 182.567 131.979 181.383C143.235 180.114 154.132 176.91 164.205 171.962C160.929 182.49 155.596 192.319 148.464 200.856C138.092 213.27 124.357 222.426 108.907 227.222C93.458 232.019 76.9524 232.253 61.3736 227.895C45.7948 223.538 31.8055 214.775 21.0867 202.659C10.3679 190.543 3.37557 175.59 0.949823 159.596C-1.47592 143.602 0.768139 127.248 7.41237 112.499C14.0566 97.7497 24.8183 85.2327 38.4045 76.4519ZM163.062 156.711L163.062 156.711C162.954 156.773 162.846 156.835 162.738 156.897C162.846 156.835 162.954 156.773 163.062 156.711Z" fill="#24C8DB"/>
6 +</svg>
public/vite.svg new
+1
@@ -0,0 +1 @@
1 +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file
scripts/build-android.sh new
+176
@@ -0,0 +1,176 @@
1 +#!/usr/bin/env bash
2 +# build-android.sh
3 +#
4 +# Full Play Store pipeline for Siti AI (Android):
5 +# 1. Clean — rm -rf src-tauri/gen/android
6 +# 2. Init — cargo tauri android init (prod Android config)
7 +# 3. Icons — pnpm gim
8 +# 4. Override — copy src-tauri/gen-override/android/* into gen/android/
9 +# 5. Keystore — patch storeFile path in gen/android/keystore.properties
10 +# 6. Build — cargo tauri android build --aab
11 +#
12 +# The build produces a signed universal .aab ready to upload to the Play
13 +# Console. versionCode is injected at init/build time as YYYYMMDDhh (UTC) via a
14 +# second -c flag, so tauri.prod-android.conf.json is never mutated.
15 +#
16 +# Every configurable value has a hardcoded default that can be overridden by
17 +# setting the corresponding environment variable before invoking the script.
18 +#
19 +# Usage:
20 +# ./scripts/build-android.sh # full pipeline
21 +# ./scripts/build-android.sh clean # rm gen/android only
22 +# ./scripts/build-android.sh init # android init only
23 +# ./scripts/build-android.sh icons # generate icons only
24 +# ./scripts/build-android.sh override # copy gen-override files only
25 +# ./scripts/build-android.sh keystore # patch keystore.properties only
26 +# ./scripts/build-android.sh build # android build only
27 +#
28 +# make psmobile # full pipeline via Makefile
29 +# make psmobile-build
30 +
31 +set -euo pipefail
32 +
33 +# ── Paths ─────────────────────────────────────────────────────────────────────
34 +
35 +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
36 +ANDROID_CONFIG="${REPO_ROOT}/src-tauri/tauri.prod-android.conf.json"
37 +GEN_ANDROID_DIR="${REPO_ROOT}/src-tauri/gen/android"
38 +GEN_OVERRIDE_DIR="${REPO_ROOT}/src-tauri/gen-override/android"
39 +KEYSTORE_PROPERTIES="${GEN_ANDROID_DIR}/keystore.properties"
40 +
41 +# ── Overridable config ────────────────────────────────────────────────────────
42 +
43 +# Absolute path to the upload keystore (.jks). Required — no default, so that no
44 +# personal path is baked into the public repo. Copy
45 +# src-tauri/gen-override/android/keystore.properties.example to keystore.properties
46 +# and set storeFile, or export KEYSTORE_PATH before running this script.
47 +KEYSTORE_PATH="${KEYSTORE_PATH:?set KEYSTORE_PATH to the absolute path of your upload-keystore.jks}"
48 +
49 +# versionCode: YYYYMMDDhh (UTC) — same UTC timestamp base as the iOS bundleVersion
50 +# Integer form of the date+hour; fits the Play Store 32-bit limit (≤ 2,100,000,000) through 2099
51 +# Monotonically increasing → satisfies Play Store requirement
52 +VERSION_CODE="$(date -u +"%Y%m%d%H")"
53 +VERSION_CODE_JSON="{\"bundle\":{\"android\":{\"versionCode\":${VERSION_CODE}}}}"
54 +
55 +# ── Helpers ───────────────────────────────────────────────────────────────────
56 +
57 +step() { echo ""; echo "── $* ──────────────────────────────────────────────"; }
58 +ok() { echo "✅ $*"; }
59 +fail() { echo "❌ $*" >&2; exit 1; }
60 +
61 +# ── Steps ─────────────────────────────────────────────────────────────────────
62 +
63 +do_clean() {
64 + step "1/6 Clean gen/android"
65 +
66 + echo "🗑 rm -rf ${GEN_ANDROID_DIR}"
67 + rm -rf "${GEN_ANDROID_DIR}"
68 + ok "Cleaned"
69 +}
70 +
71 +do_init() {
72 + step "2/6 Android init"
73 +
74 + [[ -f "$ANDROID_CONFIG" ]] || fail "Android config not found: $ANDROID_CONFIG"
75 +
76 + echo "🏷 versionCode → ${VERSION_CODE}"
77 + echo "📂 config → ${ANDROID_CONFIG}"
78 + echo ""
79 +
80 + cargo tauri android init \
81 + -c "${ANDROID_CONFIG}" \
82 + -c "${VERSION_CODE_JSON}"
83 +
84 + ok "Android project initialised → ${GEN_ANDROID_DIR}"
85 +}
86 +
87 +do_icons() {
88 + step "3/6 Generate icons"
89 +
90 + pnpm gim
91 + ok "Icons generated"
92 +}
93 +
94 +do_override() {
95 + step "4/6 Apply gen-override/android"
96 +
97 + # Nothing to do if the override directory contains only .keep files.
98 + OVERRIDE_COUNT="$(find "${GEN_OVERRIDE_DIR}" -not -name '.keep' -not -type d | wc -l | tr -d ' ')"
99 +
100 + if [[ "$OVERRIDE_COUNT" -eq 0 ]]; then
101 + echo "ℹ️ No override files — skipping."
102 + return
103 + fi
104 +
105 + echo "📋 Copying ${OVERRIDE_COUNT} file(s) from gen-override/android → gen/android"
106 +
107 + rsync -av \
108 + --exclude='.keep' \
109 + "${GEN_OVERRIDE_DIR}/" \
110 + "${GEN_ANDROID_DIR}/"
111 +
112 + ok "Overrides applied"
113 +}
114 +
115 +do_keystore() {
116 + step "5/6 Patch keystore.properties"
117 +
118 + [[ -f "$KEYSTORE_PROPERTIES" ]] || fail "keystore.properties not found: ${KEYSTORE_PROPERTIES}\n Run 'override' step first."
119 + [[ -f "$KEYSTORE_PATH" ]] || fail "Keystore file not found: ${KEYSTORE_PATH}\n Set KEYSTORE_PATH env var to the correct .jks path."
120 +
121 + echo "🔑 storeFile → ${KEYSTORE_PATH}"
122 +
123 + # Replace the storeFile= line in-place, leaving all other keys untouched.
124 + sed -i '' "s|^storeFile=.*|storeFile=${KEYSTORE_PATH}|" "${KEYSTORE_PROPERTIES}"
125 +
126 + ok "Patched → ${KEYSTORE_PROPERTIES}"
127 +}
128 +
129 +do_build() {
130 + step "6/6 Build"
131 +
132 + [[ -f "$ANDROID_CONFIG" ]] || fail "Android config not found: $ANDROID_CONFIG"
133 +
134 + echo "🏷 versionCode → ${VERSION_CODE}"
135 + echo "📦 config → ${ANDROID_CONFIG}"
136 + echo ""
137 +
138 + cargo tauri android build \
139 + --aab \
140 + -c "${ANDROID_CONFIG}" \
141 + -c "${VERSION_CODE_JSON}"
142 +
143 + AAB_PATH="${GEN_ANDROID_DIR}/app/build/outputs/bundle/universalRelease/app-universal-release.aab"
144 + if [[ -f "$AAB_PATH" ]]; then
145 + ok "Build complete → ${AAB_PATH}"
146 + else
147 + ok "Build complete (locate the .aab under ${GEN_ANDROID_DIR}/app/build/outputs/bundle/)"
148 + fi
149 +}
150 +
151 +# ── Entry point ───────────────────────────────────────────────────────────────
152 +
153 +STEP="${1:-all}"
154 +
155 +case "$STEP" in
156 + clean) do_clean ;;
157 + init) do_init ;;
158 + icons) do_icons ;;
159 + override) do_override ;;
160 + keystore) do_keystore ;;
161 + build) do_build ;;
162 + all)
163 + do_clean
164 + do_init
165 + do_icons
166 + do_override
167 + do_keystore
168 + do_build
169 + echo ""
170 + echo "🎉 Pipeline complete: Siti AI Android .aab is ready for Play Store upload."
171 + ;;
172 + *)
173 + echo "Usage: $0 [clean|init|icons|override|keystore|build|all]" >&2
174 + exit 1
175 + ;;
176 +esac
scripts/build-appstore.sh new
+157
@@ -0,0 +1,157 @@
1 +#!/usr/bin/env bash
2 +# build-appstore.sh
3 +#
4 +# Full macOS App Store pipeline for Siti AI:
5 +# 1. Build — cargo tauri build (universal, App Store config, datetime CFBundleVersion)
6 +# 2. Sign — xcrun productbuild (installer package signing)
7 +# 3. Upload — xcrun altool (App Store Connect submission)
8 +#
9 +# Every configurable value has a hardcoded default that can be overridden by
10 +# setting the corresponding environment variable before invoking the script.
11 +#
12 +# Usage:
13 +# ./scripts/build-appstore.sh # full pipeline
14 +# ./scripts/build-appstore.sh build # build only
15 +# ./scripts/build-appstore.sh sign # sign only (assumes .app already built)
16 +# ./scripts/build-appstore.sh upload # upload only (assumes .pkg already signed)
17 +#
18 +# make asmacos # full pipeline via Makefile
19 +# make asmacos-build
20 +# make asmacos-sign
21 +# make asmacos-upload
22 +
23 +set -euo pipefail
24 +
25 +# ── Paths ─────────────────────────────────────────────────────────────────────
26 +
27 +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
28 +APPSTORE_CONFIG="${REPO_ROOT}/src-tauri/tauri.prod-macos-appstore.conf.json"
29 +
30 +# ── Overridable config ─────────────────────────────────────────────────────────
31 +#
32 +# Installer signing identity: SHA-1 fingerprint of the "3rd Party Mac Developer
33 +# Installer" certificate in your keychain. This is NOT the same as the app
34 +# signing identity declared in tauri.prod-macos-appstore.conf.json
35 +# (bundle.macOS.signingIdentity = D865610A9D550A397A90E07EB476118EDC48F829),
36 +# which Tauri uses to codesign the .app bundle itself. productbuild requires a
37 +# separate "3rd Party Mac Developer Installer: …" cert. Not secret — safe to
38 +# commit once you know the fingerprint.
39 +#
40 +# Override: SIGNING_IDENTITY=<fingerprint> ./scripts/build-appstore.sh
41 +SIGNING_IDENTITY="${SIGNING_IDENTITY:-}"
42 +
43 +# App Store Connect API credentials.
44 +# ASC_API_KEY — Key ID shown in App Store Connect → Users & Access → Keys.
45 +# ASC_ISSUER_ID — Issuer UUID on the same page.
46 +# The private key (.p8) must be present at:
47 +# ~/.appstoreconnect/private_keys/AuthKey_<ASC_API_KEY>.p8
48 +ASC_API_KEY="${ASC_API_KEY:-L84N624YQH}"
49 +ASC_ISSUER_ID="${ASC_ISSUER_ID:-b4e8d369-8b7d-4538-8435-643b73237575}"
50 +
51 +# App name — must match the productName in tauri.prod-macos-appstore.conf.json
52 +# and therefore the .app bundle produced by tauri build.
53 +APP_NAME="${APP_NAME:-Siti AI}"
54 +
55 +# Derived paths — quoted to handle the space in "Siti AI".
56 +APP_PATH="${REPO_ROOT}/src-tauri/target/universal-apple-darwin/release/bundle/macos/${APP_NAME}.app"
57 +PKG_PATH="${REPO_ROOT}/${APP_NAME}.pkg"
58 +
59 +# ── Helpers ───────────────────────────────────────────────────────────────────
60 +
61 +step() { echo ""; echo "── $* ──────────────────────────────────────────────"; }
62 +ok() { echo "✅ $*"; }
63 +fail() { echo "❌ $*" >&2; exit 1; }
64 +
65 +# ── Steps ─────────────────────────────────────────────────────────────────────
66 +
67 +do_build() {
68 + step "1/3 Build"
69 +
70 + [[ -f "$APPSTORE_CONFIG" ]] || fail "Config not found: $APPSTORE_CONFIG"
71 +
72 + # CFBundleVersion: YYYYMMDD.HHMM (UTC)
73 + # Two period-separated integers → valid for Apple
74 + # Monotonically increasing → satisfies App Store requirement
75 + BUNDLE_VERSION="$(date -u +"%Y%m%d.%H%M")"
76 + BUNDLE_VERSION_JSON="{\"bundle\":{\"macOS\":{\"bundleVersion\":\"${BUNDLE_VERSION}\"}}}"
77 +
78 + echo "🏷 bundleVersion → ${BUNDLE_VERSION}"
79 + echo "📦 app config → ${APPSTORE_CONFIG}"
80 + echo ""
81 +
82 + # Tauri merges multiple --config flags in order via json_patch::merge.
83 + # The inline JSON overrides only bundleVersion; all other keys come from
84 + # tauri.prod-macos-appstore.conf.json. The config file is never mutated.
85 + cargo tauri build \
86 + --bundles app \
87 + --target universal-apple-darwin \
88 + --config "${APPSTORE_CONFIG}" \
89 + --config "${BUNDLE_VERSION_JSON}"
90 +
91 + [[ -d "$APP_PATH" ]] || fail "Build succeeded but .app not found at: $APP_PATH"
92 + ok "Build complete → ${APP_PATH}"
93 +}
94 +
95 +do_sign() {
96 + step "2/3 Sign"
97 +
98 + [[ -d "$APP_PATH" ]] || fail ".app not found: ${APP_PATH}\n Run 'build' step first."
99 +
100 + if [[ -z "${SIGNING_IDENTITY}" ]]; then
101 + fail "SIGNING_IDENTITY is not set.\n Export the SHA-1 fingerprint of your '3rd Party Mac Developer Installer' certificate:\n export SIGNING_IDENTITY=<fingerprint>"
102 + fi
103 +
104 + echo "🔏 identity → ${SIGNING_IDENTITY}"
105 + echo "📂 component → ${APP_PATH}"
106 + echo "📦 output → ${PKG_PATH}"
107 + echo ""
108 +
109 + xcrun productbuild \
110 + --sign "${SIGNING_IDENTITY}" \
111 + --component "${APP_PATH}" /Applications \
112 + "${PKG_PATH}"
113 +
114 + [[ -f "$PKG_PATH" ]] || fail "productbuild succeeded but .pkg not found at: $PKG_PATH"
115 + ok "Signed → ${PKG_PATH}"
116 +}
117 +
118 +do_upload() {
119 + step "3/3 Upload"
120 +
121 + [[ -f "$PKG_PATH" ]] || fail ".pkg not found: ${PKG_PATH}\n Run 'sign' step first."
122 +
123 + echo "📤 file → ${PKG_PATH}"
124 + echo "🔑 apiKey → ${ASC_API_KEY}"
125 + echo "🏢 issuer → ${ASC_ISSUER_ID}"
126 + echo ""
127 +
128 + xcrun altool \
129 + --upload-app \
130 + --type macos \
131 + --file "${PKG_PATH}" \
132 + --apiKey "${ASC_API_KEY}" \
133 + --apiIssuer "${ASC_ISSUER_ID}"
134 +
135 + ok "Uploaded to App Store Connect"
136 +}
137 +
138 +# ── Entry point ───────────────────────────────────────────────────────────────
139 +
140 +STEP="${1:-all}"
141 +
142 +case "$STEP" in
143 + build) do_build ;;
144 + sign) do_sign ;;
145 + upload) do_upload ;;
146 + all)
147 + do_build
148 + do_sign
149 + do_upload
150 + echo ""
151 + echo "🎉 Pipeline complete: built, signed, and uploaded ${APP_NAME}."
152 + ;;
153 + *)
154 + echo "Usage: $0 [build|sign|upload|all]" >&2
155 + exit 1
156 + ;;
157 +esac
scripts/build-ios.sh new
+182
@@ -0,0 +1,182 @@
1 +#!/usr/bin/env bash
2 +# build-ios.sh
3 +#
4 +# Full App Store pipeline for Siti AI (iOS):
5 +# 1. Clean — rm -rf src-tauri/gen/apple
6 +# 2. Init — cargo tauri ios init (local tauri CLI, prod iOS config)
7 +# 3. Icons — pnpm gim
8 +# 4. Override — copy gen-override/apple into gen/apple
9 +# 5. Build — cargo tauri ios build (app-store-connect, datetime CFBundleVersion)
10 +# 6. Upload — xcrun altool (App Store Connect submission)
11 +#
12 +# Every configurable value has a hardcoded default that can be overridden by
13 +# setting the corresponding environment variable before invoking the script.
14 +#
15 +# Usage:
16 +# ./scripts/build-ios.sh # full pipeline
17 +# ./scripts/build-ios.sh clean # rm gen/apple only
18 +# ./scripts/build-ios.sh init # tauri ios init only
19 +# ./scripts/build-ios.sh icons # pnpm gim only
20 +# ./scripts/build-ios.sh override # copy gen-override/apple only
21 +# ./scripts/build-ios.sh build # build only (assumes init already done)
22 +# ./scripts/build-ios.sh upload # upload only (assumes .ipa already built)
23 +#
24 +# make asios # full pipeline via Makefile
25 +# make asios-clean
26 +# make asios-init
27 +# make asios-icons
28 +# make asios-override
29 +# make asios-build
30 +# make asios-upload
31 +
32 +set -euo pipefail
33 +
34 +# ── Paths ─────────────────────────────────────────────────────────────────────
35 +
36 +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
37 +IOS_CONFIG="${REPO_ROOT}/src-tauri/tauri.prod-ios.conf.json"
38 +GEN_APPLE="${REPO_ROOT}/src-tauri/gen/apple"
39 +GEN_OVERRIDE="${REPO_ROOT}/src-tauri/gen-override/apple"
40 +
41 +# ── Overridable config ─────────────────────────────────────────────────────────
42 +#
43 +# Path to a local Tauri source checkout. The ios init command is run via
44 +# `cargo run` against this manifest so the CLI version matches the workspace.
45 +# Override: TAURI_CLI_MANIFEST=/path/to/tauri/Cargo.toml ./scripts/build-ios.sh
46 +TAURI_CLI_MANIFEST="${TAURI_CLI_MANIFEST:-${HOME}/Repositories/tauri/Cargo.toml}"
47 +
48 +# App Store Connect API credentials.
49 +# ASC_API_KEY — Key ID shown in App Store Connect → Users & Access → Keys.
50 +# ASC_ISSUER_ID — Issuer UUID on the same page.
51 +# The private key (.p8) must be present at:
52 +# ~/.appstoreconnect/private_keys/AuthKey_<ASC_API_KEY>.p8
53 +ASC_API_KEY="${ASC_API_KEY:-L84N624YQH}"
54 +ASC_ISSUER_ID="${ASC_ISSUER_ID:-b4e8d369-8b7d-4538-8435-643b73237575}"
55 +
56 +# App name — must match the productName in tauri.prod-ios.conf.json.
57 +APP_NAME="${APP_NAME:-Siti AI}"
58 +
59 +# Derived IPA path produced by `cargo tauri ios build`.
60 +IPA_PATH="${REPO_ROOT}/src-tauri/gen/apple/build/arm64/${APP_NAME}.ipa"
61 +
62 +# ── Helpers ───────────────────────────────────────────────────────────────────
63 +
64 +step() { echo ""; echo "── $* ──────────────────────────────────────────────"; }
65 +ok() { echo "✅ $*"; }
66 +fail() { echo "❌ $*" >&2; exit 1; }
67 +
68 +# ── Steps ─────────────────────────────────────────────────────────────────────
69 +
70 +do_clean() {
71 + step "Clean"
72 + rm -rf "${GEN_APPLE}"
73 + ok "Removed ${GEN_APPLE}"
74 +}
75 +
76 +do_init() {
77 + step "Init"
78 +
79 + [[ -f "$IOS_CONFIG" ]] || fail "Config not found: $IOS_CONFIG"
80 + [[ -f "$TAURI_CLI_MANIFEST" ]] || fail "Tauri CLI manifest not found: $TAURI_CLI_MANIFEST\n Set TAURI_CLI_MANIFEST to the path of your local tauri/Cargo.toml"
81 +
82 + echo "📱 config → ${IOS_CONFIG}"
83 + echo "🦀 cli → ${TAURI_CLI_MANIFEST}"
84 + echo ""
85 +
86 + cargo run --release \
87 + --manifest-path "${TAURI_CLI_MANIFEST}" \
88 + -p tauri-cli \
89 + -- tauri ios init \
90 + -c "${IOS_CONFIG}"
91 +
92 + ok "Xcode project generated → ${GEN_APPLE}"
93 +}
94 +
95 +do_icons() {
96 + step "Icons"
97 + pnpm gim
98 + ok "Icons generated"
99 +}
100 +
101 +do_override() {
102 + step "Override"
103 +
104 + if [[ -d "${GEN_OVERRIDE}" ]]; then
105 + cp -r "${GEN_OVERRIDE}/." "${GEN_APPLE}/"
106 + ok "Copied ${GEN_OVERRIDE} → ${GEN_APPLE}"
107 + else
108 + echo "ℹ️ No overrides found at ${GEN_OVERRIDE}, skipping."
109 + fi
110 +}
111 +
112 +do_build() {
113 + step "Build"
114 +
115 + [[ -f "$IOS_CONFIG" ]] || fail "Config not found: $IOS_CONFIG"
116 + [[ -d "$GEN_APPLE" ]] || fail "Xcode project not found at ${GEN_APPLE}\n Run 'init' step first."
117 +
118 + # CFBundleVersion: YYYYMMDD.HHMM (UTC)
119 + # Two period-separated integers → valid for Apple
120 + # Monotonically increasing → satisfies App Store requirement
121 + BUNDLE_VERSION="$(date -u +"%Y%m%d.%H%M")"
122 + BUNDLE_VERSION_JSON="{\"bundle\":{\"iOS\":{\"bundleVersion\":\"${BUNDLE_VERSION}\"}}}"
123 +
124 + echo "🏷 bundleVersion → ${BUNDLE_VERSION}"
125 + echo "📦 app config → ${IOS_CONFIG}"
126 + echo ""
127 +
128 + cargo tauri ios build \
129 + --export-method app-store-connect \
130 + --config "${IOS_CONFIG}" \
131 + --config "${BUNDLE_VERSION_JSON}"
132 +
133 + [[ -f "$IPA_PATH" ]] || fail "Build succeeded but .ipa not found at: $IPA_PATH"
134 + ok "Build complete → ${IPA_PATH}"
135 +}
136 +
137 +do_upload() {
138 + step "Upload"
139 +
140 + [[ -f "$IPA_PATH" ]] || fail ".ipa not found: ${IPA_PATH}\n Run 'build' step first."
141 +
142 + echo "📤 file → ${IPA_PATH}"
143 + echo "🔑 apiKey → ${ASC_API_KEY}"
144 + echo "🏢 issuer → ${ASC_ISSUER_ID}"
145 + echo ""
146 +
147 + xcrun altool \
148 + --upload-app \
149 + --type ios \
150 + --file "${IPA_PATH}" \
151 + --apiKey "${ASC_API_KEY}" \
152 + --apiIssuer "${ASC_ISSUER_ID}"
153 +
154 + ok "Uploaded to App Store Connect"
155 +}
156 +
157 +# ── Entry point ───────────────────────────────────────────────────────────────
158 +
159 +STEP="${1:-all}"
160 +
161 +case "$STEP" in
162 + clean) do_clean ;;
163 + init) do_init ;;
164 + icons) do_icons ;;
165 + override) do_override ;;
166 + build) do_build ;;
167 + upload) do_upload ;;
168 + all)
169 + do_clean
170 + do_init
171 + do_icons
172 + do_override
173 + do_build
174 + do_upload
175 + echo ""
176 + echo "🎉 Pipeline complete: built and uploaded ${APP_NAME} to App Store Connect."
177 + ;;
178 + *)
179 + echo "Usage: $0 [clean|init|icons|override|build|upload|all]" >&2
180 + exit 1
181 + ;;
182 +esac
scripts/generate-icon-mobile.sh new
+12
@@ -0,0 +1,12 @@
1 +#!/usr/bin/env bash
2 +
3 +# This command should be run after the cargo tauri ios init.
4 +#
5 +# Generate icons for mobile platforms from the source artwork. The no-alpha
6 +# version is used for iOS. Update the source path below if you change the
7 +# artwork location.
8 +cargo tauri icon assets/app-icon.png
9 +
10 +# Restore the icons directory to its original state, keeping the freshly
11 +# generated .icns for desktop builds.
12 +git restore 'src-tauri/icons/*' ':(exclude)src-tauri/icons/*.icns'
scripts/generate-playstore-graphics.mjs new
+66
@@ -0,0 +1,66 @@
1 +#!/usr/bin/env node
2 +/**
3 + * Renders Play Store marketing graphics from HTML templates using Puppeteer.
4 + * Output: assets/aso/playstore/{01-chat,02-settings}-1080x1920.png + feature-graphic-1024x500.png
5 + *
6 + * Usage: node scripts/generate-playstore-graphics.mjs
7 + */
8 +
9 +import puppeteer from 'puppeteer';
10 +import { fileURLToPath } from 'url';
11 +import { dirname, join, resolve } from 'path';
12 +import { mkdirSync } from 'fs';
13 +
14 +const __dirname = dirname(fileURLToPath(import.meta.url));
15 +const repoRoot = resolve(__dirname, '..');
16 +const templateDir = join(__dirname, 'playstore-templates');
17 +const outDir = join(repoRoot, 'assets', 'aso', 'playstore');
18 +
19 +mkdirSync(outDir, { recursive: true });
20 +
21 +const targets = [
22 + {
23 + template: '01-chat.html',
24 + output: '01-chat-1080x1920.png',
25 + width: 1080,
26 + height: 1920,
27 + },
28 + {
29 + template: '02-settings.html',
30 + output: '02-settings-1080x1920.png',
31 + width: 1080,
32 + height: 1920,
33 + },
34 + {
35 + template: 'feature-graphic.html',
36 + output: 'feature-graphic-1024x500.png',
37 + width: 1024,
38 + height: 500,
39 + },
40 +];
41 +
42 +console.log('Launching browser…');
43 +const browser = await puppeteer.launch({
44 + headless: true,
45 + args: ['--no-sandbox', '--disable-setuid-sandbox'],
46 +});
47 +
48 +for (const { template, output, width, height } of targets) {
49 + const page = await browser.newPage();
50 + await page.setViewport({ width, height, deviceScaleFactor: 1 });
51 +
52 + const url = `file://${join(templateDir, template)}`;
53 + await page.goto(url, { waitUntil: 'networkidle0', timeout: 30_000 });
54 +
55 + // Wait a tick for any final layout reflow after font load
56 + await new Promise(r => setTimeout(r, 500));
57 +
58 + const outputPath = join(outDir, output);
59 + await page.screenshot({ path: outputPath, fullPage: false, type: 'png' });
60 +
61 + console.log(`✓ ${output}`);
62 + await page.close();
63 +}
64 +
65 +await browser.close();
66 +console.log(`\nDone — files in assets/aso/playstore/`);
scripts/playstore-templates/01-chat.html new
+342
@@ -0,0 +1,342 @@
1 +<!DOCTYPE html>
2 +<html>
3 +<head>
4 + <meta charset="UTF-8">
5 + <link rel="preconnect" href="https://fonts.googleapis.com">
6 + <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
7 + <style>
8 + *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
9 +
10 + html, body {
11 + width: 1080px;
12 + height: 1920px;
13 + overflow: hidden;
14 + font-family: 'Inter', system-ui, -apple-system, sans-serif;
15 + }
16 +
17 + .page {
18 + width: 1080px;
19 + height: 1920px;
20 + background: linear-gradient(155deg, #f8f7f3 0%, #eeede9 100%);
21 + display: flex;
22 + flex-direction: column;
23 + align-items: center;
24 + }
25 +
26 + /* ── text section ─────────────────────────────── */
27 + .text-section {
28 + padding-top: 140px;
29 + text-align: center;
30 + max-width: 860px;
31 + padding-left: 40px;
32 + padding-right: 40px;
33 + }
34 +
35 + .label {
36 + font-size: 27px;
37 + font-weight: 700;
38 + letter-spacing: 0.18em;
39 + text-transform: uppercase;
40 + color: #3a7a56;
41 + margin-bottom: 30px;
42 + }
43 +
44 + .headline {
45 + font-size: 83px;
46 + font-weight: 800;
47 + line-height: 1.06;
48 + color: #1a3428;
49 + margin-bottom: 38px;
50 + }
51 +
52 + .headline .accent {
53 + color: #3a7a56;
54 + }
55 +
56 + .subtext {
57 + font-size: 33px;
58 + font-weight: 400;
59 + line-height: 1.45;
60 + color: #7a7a7a;
61 + }
62 +
63 + /* ── phone frame ──────────────────────────────── */
64 + .phone-wrap {
65 + margin-top: 66px;
66 + }
67 +
68 + .phone-frame {
69 + width: 520px;
70 + height: 1155px;
71 + background: #1c1c1e;
72 + border-radius: 56px;
73 + padding: 14px;
74 + box-shadow:
75 + 0 0 0 1px rgba(255,255,255,0.06),
76 + 0 48px 96px rgba(0,0,0,0.30),
77 + 0 24px 48px rgba(0,0,0,0.16);
78 + position: relative;
79 + }
80 +
81 + /* side button */
82 + .phone-frame::after {
83 + content: '';
84 + position: absolute;
85 + right: -4px;
86 + top: 200px;
87 + width: 4px;
88 + height: 80px;
89 + background: #2a2a2c;
90 + border-radius: 0 4px 4px 0;
91 + }
92 +
93 + .phone-screen {
94 + background: #f9f9f9;
95 + border-radius: 44px;
96 + overflow: hidden;
97 + width: 100%;
98 + height: 100%;
99 + position: relative;
100 + display: flex;
101 + flex-direction: column;
102 + }
103 +
104 + /* punch-hole camera */
105 + .camera-area {
106 + flex-shrink: 0;
107 + height: 54px;
108 + display: flex;
109 + align-items: center;
110 + justify-content: center;
111 + background: #f9f9f9;
112 + }
113 +
114 + .camera-hole {
115 + width: 16px;
116 + height: 16px;
117 + background: #1c1c1e;
118 + border-radius: 50%;
119 + }
120 +
121 + /* status bar */
122 + .status-bar {
123 + flex-shrink: 0;
124 + display: flex;
125 + justify-content: space-between;
126 + align-items: center;
127 + padding: 0 32px 10px;
128 + font-size: 22px;
129 + font-weight: 700;
130 + color: #1a1a1a;
131 + }
132 +
133 + .status-icons {
134 + display: flex;
135 + align-items: center;
136 + gap: 8px;
137 + }
138 +
139 + /* app bar */
140 + .app-bar {
141 + flex-shrink: 0;
142 + padding: 14px 28px 16px;
143 + border-bottom: 1.5px solid #ebebeb;
144 + display: flex;
145 + justify-content: space-between;
146 + align-items: center;
147 + }
148 +
149 + .app-bar-left {}
150 +
151 + .app-title {
152 + font-size: 30px;
153 + font-weight: 600;
154 + color: #111;
155 + }
156 +
157 + .app-subtitle {
158 + display: flex;
159 + align-items: center;
160 + gap: 8px;
161 + margin-top: 4px;
162 + }
163 +
164 + .dot {
165 + width: 10px;
166 + height: 10px;
167 + background: #3a7a56;
168 + border-radius: 50%;
169 + flex-shrink: 0;
170 + }
171 +
172 + .app-subtitle-text {
173 + font-size: 19px;
174 + color: #8a8a8a;
175 + }
176 +
177 + .settings-icon {
178 + color: #8a8a8a;
179 + }
180 +
181 + /* chat body */
182 + .chat-body {
183 + flex: 1;
184 + display: flex;
185 + flex-direction: column;
186 + align-items: center;
187 + justify-content: center;
188 + padding: 40px;
189 + }
190 +
191 + .chat-icon {
192 + width: 56px;
193 + height: 56px;
194 + margin-bottom: 22px;
195 + color: #c8c8c8;
196 + }
197 +
198 + .chat-placeholder-title {
199 + font-size: 27px;
200 + font-weight: 600;
201 + color: #1a1a1a;
202 + margin-bottom: 10px;
203 + }
204 +
205 + .chat-placeholder-sub {
206 + font-size: 19px;
207 + color: #9a9a9a;
208 + text-align: center;
209 + line-height: 1.45;
210 + }
211 +
212 + /* message bar */
213 + .message-bar {
214 + flex-shrink: 0;
215 + padding: 14px 18px 40px;
216 + background: #f9f9f9;
217 + border-top: 1.5px solid #ebebeb;
218 + }
219 +
220 + .message-input-row {
221 + background: #efefef;
222 + border-radius: 28px;
223 + padding: 16px 16px 16px 24px;
224 + display: flex;
225 + justify-content: space-between;
226 + align-items: center;
227 + gap: 12px;
228 + }
229 +
230 + .message-placeholder {
231 + font-size: 21px;
232 + color: #aaa;
233 + flex: 1;
234 + }
235 +
236 + .send-btn {
237 + width: 42px;
238 + height: 42px;
239 + background: #d8d8d8;
240 + border-radius: 50%;
241 + display: flex;
242 + align-items: center;
243 + justify-content: center;
244 + flex-shrink: 0;
245 + }
246 +
247 + .hint-row {
248 + text-align: center;
249 + margin-top: 10px;
250 + font-size: 16px;
251 + color: #bbb;
252 + }
253 + </style>
254 +</head>
255 +<body>
256 + <div class="page">
257 +
258 + <div class="text-section">
259 + <div class="label">Personal Assistant</div>
260 + <div class="headline">
261 + An assistant that runs <span class="accent">entirely on your Android.</span>
262 + </div>
263 + <div class="subtext">No account. No internet required. Just ask — privately.</div>
264 + </div>
265 +
266 + <div class="phone-wrap">
267 + <div class="phone-frame">
268 + <div class="phone-screen">
269 +
270 + <div class="camera-area">
271 + <div class="camera-hole"></div>
272 + </div>
273 +
274 + <div class="status-bar">
275 + <span>9:41</span>
276 + <div class="status-icons">
277 + <!-- signal -->
278 + <svg width="19" height="14" viewBox="0 0 19 14" fill="#1a1a1a">
279 + <rect x="0" y="9" width="3.5" height="5" rx="0.8"/>
280 + <rect x="4.5" y="6" width="3.5" height="8" rx="0.8"/>
281 + <rect x="9" y="3" width="3.5" height="11" rx="0.8"/>
282 + <rect x="13.5" y="0" width="3.5" height="14" rx="0.8"/>
283 + </svg>
284 + <!-- wifi -->
285 + <svg width="20" height="15" viewBox="0 0 20 15" fill="#1a1a1a">
286 + <circle cx="10" cy="13.5" r="1.5"/>
287 + <path d="M6.5 10C7.8 8.8 8.8 8 10 8s2.2.8 3.5 2" stroke="#1a1a1a" stroke-width="1.8" fill="none" stroke-linecap="round"/>
288 + <path d="M3.5 7C5.5 5 7.6 4 10 4s4.5 1 6.5 3" stroke="#1a1a1a" stroke-width="1.8" fill="none" stroke-linecap="round"/>
289 + <path d="M0.5 4C3.2 1.5 6.4 0 10 0s6.8 1.5 9.5 4" stroke="#1a1a1a" stroke-width="1.8" fill="none" stroke-linecap="round"/>
290 + </svg>
291 + <!-- battery -->
292 + <svg width="27" height="14" viewBox="0 0 27 14" fill="#1a1a1a">
293 + <rect x="0" y="1.5" width="22" height="11" rx="3" stroke="#1a1a1a" stroke-width="1.5" fill="none"/>
294 + <rect x="22.5" y="4.5" width="3" height="5" rx="1.5"/>
295 + <rect x="2" y="3.5" width="16" height="7" rx="1.5"/>
296 + </svg>
297 + </div>
298 + </div>
299 +
300 + <div class="app-bar">
301 + <div class="app-bar-left">
302 + <div class="app-title">Siti AI</div>
303 + <div class="app-subtitle">
304 + <div class="dot"></div>
305 + <span class="app-subtitle-text">on-device · private</span>
306 + </div>
307 + </div>
308 + <svg class="settings-icon" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#9a9a9a" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
309 + <circle cx="12" cy="12" r="3"/>
310 + <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
311 + </svg>
312 + </div>
313 +
314 + <div class="chat-body">
315 + <!-- diamond icon like the app -->
316 + <svg class="chat-icon" viewBox="0 0 56 56" fill="none" stroke="currentColor" stroke-width="1.4">
317 + <rect x="14" y="14" width="28" height="28" rx="2" transform="rotate(45 28 28)"/>
318 + </svg>
319 + <div class="chat-placeholder-title">How can I help?</div>
320 + <div class="chat-placeholder-sub">Your personal assistant, running<br>entirely on your device.</div>
321 + </div>
322 +
323 + <div class="message-bar">
324 + <div class="message-input-row">
325 + <span class="message-placeholder">Message Siti...</span>
326 + <div class="send-btn">
327 + <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
328 + <line x1="22" y1="2" x2="11" y2="13"/>
329 + <polygon points="22 2 15 22 11 13 2 9 22 2" fill="#999" stroke="none"/>
330 + </svg>
331 + </div>
332 + </div>
333 + <div class="hint-row">Return to send · Shift+Return for new line</div>
334 + </div>
335 +
336 + </div>
337 + </div>
338 + </div>
339 +
340 + </div>
341 +</body>
342 +</html>
scripts/playstore-templates/02-settings.html new
+375
@@ -0,0 +1,375 @@
1 +<!DOCTYPE html>
2 +<html>
3 +<head>
4 + <meta charset="UTF-8">
5 + <link rel="preconnect" href="https://fonts.googleapis.com">
6 + <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
7 + <style>
8 + *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
9 +
10 + html, body {
11 + width: 1080px;
12 + height: 1920px;
13 + overflow: hidden;
14 + font-family: 'Inter', system-ui, -apple-system, sans-serif;
15 + }
16 +
17 + .page {
18 + width: 1080px;
19 + height: 1920px;
20 + background: linear-gradient(155deg, #f8f7f3 0%, #eeede9 100%);
21 + display: flex;
22 + flex-direction: column;
23 + align-items: center;
24 + }
25 +
26 + /* ── text section ─────────────────────────────── */
27 + .text-section {
28 + padding-top: 140px;
29 + text-align: center;
30 + max-width: 860px;
31 + padding-left: 40px;
32 + padding-right: 40px;
33 + }
34 +
35 + .label {
36 + font-size: 27px;
37 + font-weight: 700;
38 + letter-spacing: 0.18em;
39 + text-transform: uppercase;
40 + color: #3a7a56;
41 + margin-bottom: 30px;
42 + }
43 +
44 + .headline {
45 + font-size: 83px;
46 + font-weight: 800;
47 + line-height: 1.06;
48 + color: #1a3428;
49 + margin-bottom: 38px;
50 + }
51 +
52 + .headline .accent {
53 + color: #3a7a56;
54 + }
55 +
56 + .subtext {
57 + font-size: 33px;
58 + font-weight: 400;
59 + line-height: 1.45;
60 + color: #7a7a7a;
61 + }
62 +
63 + /* ── phone frame ──────────────────────────────── */
64 + .phone-wrap {
65 + margin-top: 66px;
66 + }
67 +
68 + .phone-frame {
69 + width: 520px;
70 + height: 1155px;
71 + background: #1c1c1e;
72 + border-radius: 56px;
73 + padding: 14px;
74 + box-shadow:
75 + 0 0 0 1px rgba(255,255,255,0.06),
76 + 0 48px 96px rgba(0,0,0,0.30),
77 + 0 24px 48px rgba(0,0,0,0.16);
78 + position: relative;
79 + }
80 +
81 + .phone-frame::after {
82 + content: '';
83 + position: absolute;
84 + right: -4px;
85 + top: 200px;
86 + width: 4px;
87 + height: 80px;
88 + background: #2a2a2c;
89 + border-radius: 0 4px 4px 0;
90 + }
91 +
92 + .phone-screen {
93 + background: #f2f2f2;
94 + border-radius: 44px;
95 + overflow: hidden;
96 + width: 100%;
97 + height: 100%;
98 + position: relative;
99 + display: flex;
100 + flex-direction: column;
101 + }
102 +
103 + .camera-area {
104 + flex-shrink: 0;
105 + height: 54px;
106 + display: flex;
107 + align-items: center;
108 + justify-content: center;
109 + background: #f2f2f2;
110 + }
111 +
112 + .camera-hole {
113 + width: 16px;
114 + height: 16px;
115 + background: #1c1c1e;
116 + border-radius: 50%;
117 + }
118 +
119 + .status-bar {
120 + flex-shrink: 0;
121 + display: flex;
122 + justify-content: space-between;
123 + align-items: center;
124 + padding: 0 32px 10px;
125 + font-size: 22px;
126 + font-weight: 700;
127 + color: #1a1a1a;
128 + }
129 +
130 + .status-icons {
131 + display: flex;
132 + align-items: center;
133 + gap: 8px;
134 + }
135 +
136 + /* settings screen */
137 + .settings-screen {
138 + flex: 1;
139 + background: #f2f2f2;
140 + display: flex;
141 + flex-direction: column;
142 + overflow: hidden;
143 + }
144 +
145 + .settings-header {
146 + display: flex;
147 + justify-content: space-between;
148 + align-items: center;
149 + padding: 18px 28px 20px;
150 + background: #f2f2f2;
151 + }
152 +
153 + .settings-title {
154 + font-size: 34px;
155 + font-weight: 700;
156 + color: #111;
157 + }
158 +
159 + .done-btn {
160 + font-size: 24px;
161 + font-weight: 600;
162 + color: #3a7a56;
163 + }
164 +
165 + /* settings list */
166 + .settings-group {
167 + margin: 0 18px 10px;
168 + }
169 +
170 + .settings-group-label {
171 + font-size: 17px;
172 + font-weight: 600;
173 + letter-spacing: 0.06em;
174 + text-transform: uppercase;
175 + color: #8a8a8a;
176 + padding: 8px 16px 8px;
177 + }
178 +
179 + .settings-card {
180 + background: #fff;
181 + border-radius: 16px;
182 + overflow: hidden;
183 + }
184 +
185 + .settings-row {
186 + display: flex;
187 + justify-content: space-between;
188 + align-items: center;
189 + padding: 18px 20px;
190 + border-bottom: 1px solid #f0f0f0;
191 + }
192 +
193 + .settings-row:last-child {
194 + border-bottom: none;
195 + }
196 +
197 + .settings-row-label {
198 + font-size: 22px;
199 + font-weight: 500;
200 + color: #111;
201 + }
202 +
203 + .settings-row-value {
204 + display: flex;
205 + align-items: center;
206 + gap: 8px;
207 + font-size: 21px;
208 + color: #8a8a8a;
209 + max-width: 240px;
210 + text-align: right;
211 + }
212 +
213 + .chevron {
214 + color: #c0c0c0;
215 + flex-shrink: 0;
216 + }
217 +
218 + /* model detail block */
219 + .model-detail {
220 + background: #fff;
221 + border-radius: 16px;
222 + margin: 0 18px 10px;
223 + padding: 18px 20px;
224 + }
225 +
226 + .model-name {
227 + font-size: 22px;
228 + font-weight: 600;
229 + color: #111;
230 + margin-bottom: 4px;
231 + }
232 +
233 + .model-maker {
234 + font-size: 18px;
235 + color: #8a8a8a;
236 + margin-bottom: 12px;
237 + }
238 +
239 + .model-desc {
240 + font-size: 19px;
241 + color: #555;
242 + line-height: 1.45;
243 + margin-bottom: 16px;
244 + }
245 +
246 + .model-status {
247 + display: flex;
248 + align-items: center;
249 + gap: 8px;
250 + }
251 +
252 + .status-dot {
253 + width: 9px;
254 + height: 9px;
255 + background: #c0c0c0;
256 + border-radius: 50%;
257 + }
258 +
259 + .status-text {
260 + font-size: 19px;
261 + color: #8a8a8a;
262 + }
263 +
264 + .download-note {
265 + margin: 0 18px 10px;
266 + padding: 16px 20px;
267 + font-size: 18px;
268 + color: #8a8a8a;
269 + line-height: 1.45;
270 + }
271 +
272 + .about-group {
273 + margin: 0 18px;
274 + }
275 + </style>
276 +</head>
277 +<body>
278 + <div class="page">
279 +
280 + <div class="text-section">
281 + <div class="label">On-Device Models</div>
282 + <div class="headline">
283 + Pick your model. <span class="accent">Keep your data.</span>
284 + </div>
285 + <div class="subtext">Run open models like Qwen 2.5 — everything stays on your device.</div>
286 + </div>
287 +
288 + <div class="phone-wrap">
289 + <div class="phone-frame">
290 + <div class="phone-screen">
291 +
292 + <div class="camera-area">
293 + <div class="camera-hole"></div>
294 + </div>
295 +
296 + <div class="status-bar">
297 + <span>9:41</span>
298 + <div class="status-icons">
299 + <svg width="19" height="14" viewBox="0 0 19 14" fill="#1a1a1a">
300 + <rect x="0" y="9" width="3.5" height="5" rx="0.8"/>
301 + <rect x="4.5" y="6" width="3.5" height="8" rx="0.8"/>
302 + <rect x="9" y="3" width="3.5" height="11" rx="0.8"/>
303 + <rect x="13.5" y="0" width="3.5" height="14" rx="0.8"/>
304 + </svg>
305 + <svg width="20" height="15" viewBox="0 0 20 15" fill="#1a1a1a">
306 + <circle cx="10" cy="13.5" r="1.5"/>
307 + <path d="M6.5 10C7.8 8.8 8.8 8 10 8s2.2.8 3.5 2" stroke="#1a1a1a" stroke-width="1.8" fill="none" stroke-linecap="round"/>
308 + <path d="M3.5 7C5.5 5 7.6 4 10 4s4.5 1 6.5 3" stroke="#1a1a1a" stroke-width="1.8" fill="none" stroke-linecap="round"/>
309 + <path d="M0.5 4C3.2 1.5 6.4 0 10 0s6.8 1.5 9.5 4" stroke="#1a1a1a" stroke-width="1.8" fill="none" stroke-linecap="round"/>
310 + </svg>
311 + <svg width="27" height="14" viewBox="0 0 27 14" fill="#1a1a1a">
312 + <rect x="0" y="1.5" width="22" height="11" rx="3" stroke="#1a1a1a" stroke-width="1.5" fill="none"/>
313 + <rect x="22.5" y="4.5" width="3" height="5" rx="1.5"/>
314 + <rect x="2" y="3.5" width="16" height="7" rx="1.5"/>
315 + </svg>
316 + </div>
317 + </div>
318 +
319 + <div class="settings-screen">
320 + <div class="settings-header">
321 + <div class="settings-title">Settings</div>
322 + <div class="done-btn">Done</div>
323 + </div>
324 +
325 + <div class="settings-group">
326 + <div class="settings-group-label">Model</div>
327 + <div class="settings-card">
328 + <div class="settings-row">
329 + <span class="settings-row-label">On-device model</span>
330 + <div class="settings-row-value">
331 + <span>Qwen 2.5 1.5B (GGUF)…</span>
332 + <svg class="chevron" width="12" height="20" viewBox="0 0 12 20" fill="none" stroke="#c0c0c0" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
333 + <polyline points="1 1 11 10 1 19"/>
334 + </svg>
335 + </div>
336 + </div>
337 + </div>
338 + </div>
339 +
340 + <div class="model-detail">
341 + <div class="model-name">Qwen 2.5 1.5B (GGUF)</div>
342 + <div class="model-maker">Qwen / Alibaba</div>
343 + <div class="model-desc">Lightest pre-quantized chat model — ideal for iOS &amp; Android (~941 MB). Fits comfortably within device memory limits.</div>
344 + <div class="model-status">
345 + <div class="status-dot"></div>
346 + <span class="status-text">Not loaded</span>
347 + </div>
348 + </div>
349 +
350 + <div class="download-note">
351 + Switching downloads the model on first use — Wi-Fi recommended. Everything runs entirely on your device; nothing is ever sent to a server.
352 + </div>
353 +
354 + <div class="about-group">
355 + <div class="settings-group-label">About</div>
356 + <div class="settings-card">
357 + <div class="settings-row">
358 + <span class="settings-row-label">Siti AI</span>
359 + <div class="settings-row-value"><span>1.0.0</span></div>
360 + </div>
361 + <div class="settings-row">
362 + <span class="settings-row-label">Privacy</span>
363 + <div class="settings-row-value"><span>On-device · private</span></div>
364 + </div>
365 + </div>
366 + </div>
367 + </div>
368 +
369 + </div>
370 + </div>
371 + </div>
372 +
373 + </div>
374 +</body>
375 +</html>
scripts/playstore-templates/feature-graphic.html new
+120
@@ -0,0 +1,120 @@
1 +<!DOCTYPE html>
2 +<html>
3 +<head>
4 + <meta charset="UTF-8">
5 + <link rel="preconnect" href="https://fonts.googleapis.com">
6 + <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
7 + <style>
8 + *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
9 +
10 + html, body {
11 + width: 1024px;
12 + height: 500px;
13 + overflow: hidden;
14 + font-family: 'Inter', system-ui, -apple-system, sans-serif;
15 + }
16 +
17 + .page {
18 + width: 1024px;
19 + height: 500px;
20 + background: linear-gradient(145deg, #f8f7f3 0%, #eeede9 100%);
21 + display: flex;
22 + align-items: center;
23 + justify-content: center;
24 + gap: 80px;
25 + padding: 0 80px;
26 + }
27 +
28 + /* app icon */
29 + .icon-wrap {
30 + flex-shrink: 0;
31 + }
32 +
33 + .app-icon {
34 + width: 160px;
35 + height: 160px;
36 + background: #1c1c1e;
37 + border-radius: 36px;
38 + display: flex;
39 + align-items: center;
40 + justify-content: center;
41 + box-shadow:
42 + 0 20px 48px rgba(0,0,0,0.22),
43 + 0 8px 20px rgba(0,0,0,0.14);
44 + }
45 +
46 + .app-icon-text {
47 + font-size: 62px;
48 + font-weight: 700;
49 + color: #ffffff;
50 + letter-spacing: -0.02em;
51 + font-family: 'Inter', system-ui, -apple-system, sans-serif;
52 + }
53 +
54 + /* text content */
55 + .content {
56 + flex: 1;
57 + }
58 +
59 + .brand {
60 + font-size: 26px;
61 + font-weight: 700;
62 + letter-spacing: 0.14em;
63 + text-transform: uppercase;
64 + color: #3a7a56;
65 + margin-bottom: 20px;
66 + }
67 +
68 + .headline {
69 + font-size: 66px;
70 + font-weight: 800;
71 + line-height: 1.06;
72 + color: #1a3428;
73 + margin-bottom: 24px;
74 + }
75 +
76 + .headline .accent {
77 + color: #3a7a56;
78 + }
79 +
80 + .pills {
81 + display: flex;
82 + flex-wrap: wrap;
83 + gap: 10px;
84 + }
85 +
86 + .pill {
87 + background: rgba(58, 122, 86, 0.10);
88 + border: 1.5px solid rgba(58, 122, 86, 0.20);
89 + color: #2d6645;
90 + border-radius: 100px;
91 + padding: 8px 20px;
92 + font-size: 20px;
93 + font-weight: 600;
94 + }
95 + </style>
96 +</head>
97 +<body>
98 + <div class="page">
99 +
100 + <div class="icon-wrap">
101 + <div class="app-icon">
102 + <span class="app-icon-text">Siti</span>
103 + </div>
104 + </div>
105 +
106 + <div class="content">
107 + <div class="brand">Siti AI</div>
108 + <div class="headline">
109 + Private AI,<br><span class="accent">on your device.</span>
110 + </div>
111 + <div class="pills">
112 + <div class="pill">No account</div>
113 + <div class="pill">No internet needed</div>
114 + <div class="pill">No tracking</div>
115 + </div>
116 + </div>
117 +
118 + </div>
119 +</body>
120 +</html>
src-tauri/.cargo/config.toml new
+46
@@ -0,0 +1,46 @@
1 +# Force the system (Xcode) C/C++ compilers when building on macOS hosts.
2 +# This avoids picking up Android NDK toolchains from the shell environment.
3 +[env]
4 +CXX = { value = "/usr/bin/clang++", force = true }
5 +CC = { value = "/usr/bin/clang", force = true }
6 +MACOSX_DEPLOYMENT_TARGET = { value = "14.0", force = true }
7 +
8 +# iOS / tvOS builds of `gemm-f16` require ARM full FP16 support for inline
9 +# NEON instructions like:
10 +# fmul v0.8h, v1.8h, v2.8h
11 +# fmla v0.8h, v1.8h, v2.h[0]
12 +# Without `+fullfp16`, LLVM/Rust rejects those instructions with:
13 +# error: instruction requires: fullfp16
14 +#
15 +# `+fp16` alone is not sufficient here — `gemm-common` uses arithmetic vector
16 +# half-precision instructions that specifically require `fullfp16`.
17 +
18 +[target.aarch64-apple-darwin]
19 +rustflags = ["-C", "link-arg=-mmacosx-version-min=14.0"]
20 +
21 +[target.x86_64-apple-darwin]
22 +rustflags = ["-C", "link-arg=-mmacosx-version-min=14.0"]
23 +
24 +[target.aarch64-apple-ios]
25 +rustflags = ["-C", "target-feature=+fullfp16,+fp16"]
26 +
27 +[target.aarch64-apple-ios-sim]
28 +rustflags = ["-C", "target-feature=+fullfp16,+fp16"]
29 +
30 +[target.aarch64-apple-tvos]
31 +rustflags = ["-C", "target-feature=+fullfp16,+fp16"]
32 +
33 +[target.aarch64-apple-tvos-sim]
34 +rustflags = ["-C", "target-feature=+fullfp16,+fp16"]
35 +
36 +[target.x86_64-apple-tvos]
37 +rustflags = ["-C", "target-feature=+fullfp16,+fp16"]
38 +
39 +# Android arm64-v8a hits the same `gemm-common` fp16 NEON path as iOS. The
40 +# aarch64 inline asm (`fmla v0.8h, ...`) requires `fullfp16`, which the default
41 +# `aarch64-linux-android` target does not enable. gemm still runtime-detects the
42 +# CPU feature, so enabling it here only allows the instructions to assemble.
43 +# Only aarch64 needs this; the armv7/x86/x86_64 Android targets never compile
44 +# the aarch64-gated fp16 SIMD module.
45 +[target.aarch64-linux-android]
46 +rustflags = ["-C", "target-feature=+fullfp16,+fp16"]
src-tauri/.gitignore new
+7
@@ -0,0 +1,7 @@
1 +# Generated by Cargo
2 +# will have compiled files and executables
3 +/target/
4 +
5 +# Generated by Tauri
6 +# regenerated by `make asios-init`; the tracked source of truth is gen-override/
7 +/gen/
src-tauri/Cargo.lock new
+9788
@@ -0,0 +1,9788 @@
1 +# This file is automatically @generated by Cargo.
2 +# It is not intended for manual editing.
3 +version = 4
4 +
5 +[[package]]
6 +name = "Inflector"
7 +version = "0.11.4"
8 +source = "registry+https://github.com/rust-lang/crates.io-index"
9 +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
10 +dependencies = [
11 + "lazy_static",
12 + "regex",
13 +]
14 +
15 +[[package]]
16 +name = "adler2"
17 +version = "2.0.1"
18 +source = "registry+https://github.com/rust-lang/crates.io-index"
19 +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
20 +
21 +[[package]]
22 +name = "ahash"
23 +version = "0.7.8"
24 +source = "registry+https://github.com/rust-lang/crates.io-index"
25 +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
26 +dependencies = [
27 + "getrandom 0.2.17",
28 + "once_cell",
29 + "version_check",
30 +]
31 +
32 +[[package]]
33 +name = "ahash"
34 +version = "0.8.12"
35 +source = "registry+https://github.com/rust-lang/crates.io-index"
36 +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
37 +dependencies = [
38 + "cfg-if",
39 + "getrandom 0.3.4",
40 + "once_cell",
41 + "serde",
42 + "version_check",
43 + "zerocopy",
44 +]
45 +
46 +[[package]]
47 +name = "aho-corasick"
48 +version = "1.1.4"
49 +source = "registry+https://github.com/rust-lang/crates.io-index"
50 +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
51 +dependencies = [
52 + "memchr",
53 +]
54 +
55 +[[package]]
56 +name = "akin"
57 +version = "0.4.0"
58 +source = "registry+https://github.com/rust-lang/crates.io-index"
59 +checksum = "1763692fc1416554cf051efc56a3de5595eca47299d731cc5c2b583adf8b4d2f"
60 +
61 +[[package]]
62 +name = "alloc-no-stdlib"
63 +version = "2.0.4"
64 +source = "registry+https://github.com/rust-lang/crates.io-index"
65 +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
66 +
67 +[[package]]
68 +name = "alloc-stdlib"
69 +version = "0.2.4"
70 +source = "registry+https://github.com/rust-lang/crates.io-index"
71 +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195"
72 +dependencies = [
73 + "alloc-no-stdlib",
74 +]
75 +
76 +[[package]]
77 +name = "allocator-api2"
78 +version = "0.2.21"
79 +source = "registry+https://github.com/rust-lang/crates.io-index"
80 +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
81 +
82 +[[package]]
83 +name = "android_log-sys"
84 +version = "0.3.2"
85 +source = "registry+https://github.com/rust-lang/crates.io-index"
86 +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d"
87 +
88 +[[package]]
89 +name = "android_logger"
90 +version = "0.15.1"
91 +source = "registry+https://github.com/rust-lang/crates.io-index"
92 +checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3"
93 +dependencies = [
94 + "android_log-sys",
95 + "env_filter",
96 + "log",
97 +]
98 +
99 +[[package]]
100 +name = "android_system_properties"
101 +version = "0.1.5"
102 +source = "registry+https://github.com/rust-lang/crates.io-index"
103 +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
104 +dependencies = [
105 + "libc",
106 +]
107 +
108 +[[package]]
109 +name = "annotate-snippets"
110 +version = "0.12.16"
111 +source = "registry+https://github.com/rust-lang/crates.io-index"
112 +checksum = "f211a51805bc641f3ad5b7664c77d2547af685cc33b4cd8d31964027a46f13f1"
113 +dependencies = [
114 + "anstyle",
115 + "memchr",
116 + "unicode-width 0.2.2",
117 +]
118 +
119 +[[package]]
120 +name = "ansi_term"
121 +version = "0.12.1"
122 +source = "registry+https://github.com/rust-lang/crates.io-index"
123 +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
124 +dependencies = [
125 + "winapi",
126 +]
127 +
128 +[[package]]
129 +name = "anstream"
130 +version = "1.0.0"
131 +source = "registry+https://github.com/rust-lang/crates.io-index"
132 +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
133 +dependencies = [
134 + "anstyle",
135 + "anstyle-parse",
136 + "anstyle-query",
137 + "anstyle-wincon",
138 + "colorchoice",
139 + "is_terminal_polyfill",
140 + "utf8parse",
141 +]
142 +
143 +[[package]]
144 +name = "anstyle"
145 +version = "1.0.14"
146 +source = "registry+https://github.com/rust-lang/crates.io-index"
147 +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
148 +
149 +[[package]]
150 +name = "anstyle-parse"
151 +version = "1.0.0"
152 +source = "registry+https://github.com/rust-lang/crates.io-index"
153 +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
154 +dependencies = [
155 + "utf8parse",
156 +]
157 +
158 +[[package]]
159 +name = "anstyle-query"
160 +version = "1.1.5"
161 +source = "registry+https://github.com/rust-lang/crates.io-index"
162 +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
163 +dependencies = [
164 + "windows-sys 0.61.2",
165 +]
166 +
167 +[[package]]
168 +name = "anstyle-wincon"
169 +version = "3.0.11"
170 +source = "registry+https://github.com/rust-lang/crates.io-index"
171 +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
172 +dependencies = [
173 + "anstyle",
174 + "once_cell_polyfill",
175 + "windows-sys 0.61.2",
176 +]
177 +
178 +[[package]]
179 +name = "anyhow"
180 +version = "1.0.103"
181 +source = "registry+https://github.com/rust-lang/crates.io-index"
182 +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
183 +
184 +[[package]]
185 +name = "apodize"
186 +version = "1.0.0"
187 +source = "registry+https://github.com/rust-lang/crates.io-index"
188 +checksum = "fca387cdc0a1f9c7a7c26556d584aa2d07fc529843082e4861003cde4ab914ed"
189 +
190 +[[package]]
191 +name = "approx"
192 +version = "0.5.1"
193 +source = "registry+https://github.com/rust-lang/crates.io-index"
194 +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
195 +dependencies = [
196 + "num-traits",
197 +]
198 +
199 +[[package]]
200 +name = "arraydeque"
201 +version = "0.5.1"
202 +source = "registry+https://github.com/rust-lang/crates.io-index"
203 +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
204 +
205 +[[package]]
206 +name = "arrayvec"
207 +version = "0.7.8"
208 +source = "registry+https://github.com/rust-lang/crates.io-index"
209 +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
210 +
211 +[[package]]
212 +name = "as-any"
213 +version = "0.3.2"
214 +source = "registry+https://github.com/rust-lang/crates.io-index"
215 +checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063"
216 +
217 +[[package]]
218 +name = "askama"
219 +version = "0.14.0"
220 +source = "registry+https://github.com/rust-lang/crates.io-index"
221 +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4"
222 +dependencies = [
223 + "askama_derive",
224 + "itoa",
225 + "percent-encoding",
226 + "serde",
227 + "serde_json",
228 +]
229 +
230 +[[package]]
231 +name = "askama_derive"
232 +version = "0.14.0"
233 +source = "registry+https://github.com/rust-lang/crates.io-index"
234 +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f"
235 +dependencies = [
236 + "askama_parser",
237 + "basic-toml",
238 + "memchr",
239 + "proc-macro2",
240 + "quote",
241 + "rustc-hash 2.1.3",
242 + "serde",
243 + "serde_derive",
244 + "syn 2.0.118",
245 +]
246 +
247 +[[package]]
248 +name = "askama_parser"
249 +version = "0.14.0"
250 +source = "registry+https://github.com/rust-lang/crates.io-index"
251 +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358"
252 +dependencies = [
253 + "memchr",
254 + "serde",
255 + "serde_derive",
256 + "winnow 0.7.15",
257 +]
258 +
259 +[[package]]
260 +name = "async-broadcast"
261 +version = "0.7.2"
262 +source = "registry+https://github.com/rust-lang/crates.io-index"
263 +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
264 +dependencies = [
265 + "event-listener",
266 + "event-listener-strategy",
267 + "futures-core",
268 + "pin-project-lite",
269 +]
270 +
271 +[[package]]
272 +name = "async-channel"
273 +version = "2.5.0"
274 +source = "registry+https://github.com/rust-lang/crates.io-index"
275 +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
276 +dependencies = [
277 + "concurrent-queue",
278 + "event-listener-strategy",
279 + "futures-core",
280 + "pin-project-lite",
281 +]
282 +
283 +[[package]]
284 +name = "async-compat"
285 +version = "0.2.5"
286 +source = "registry+https://github.com/rust-lang/crates.io-index"
287 +checksum = "a1ba85bc55464dcbf728b56d97e119d673f4cf9062be330a9a26f3acf504a590"
288 +dependencies = [
289 + "futures-core",
290 + "futures-io",
291 + "once_cell",
292 + "pin-project-lite",
293 + "tokio",
294 +]
295 +
296 +[[package]]
297 +name = "async-executor"
298 +version = "1.14.0"
299 +source = "registry+https://github.com/rust-lang/crates.io-index"
300 +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a"
301 +dependencies = [
302 + "async-task",
303 + "concurrent-queue",
304 + "fastrand",
305 + "futures-lite",
306 + "pin-project-lite",
307 + "slab",
308 +]
309 +
310 +[[package]]
311 +name = "async-io"
312 +version = "2.6.0"
313 +source = "registry+https://github.com/rust-lang/crates.io-index"
314 +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
315 +dependencies = [
316 + "autocfg",
317 + "cfg-if",
318 + "concurrent-queue",
319 + "futures-io",
320 + "futures-lite",
321 + "parking",
322 + "polling",
323 + "rustix",
324 + "slab",
325 + "windows-sys 0.61.2",
326 +]
327 +
328 +[[package]]
329 +name = "async-lock"
330 +version = "3.4.2"
331 +source = "registry+https://github.com/rust-lang/crates.io-index"
332 +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
333 +dependencies = [
334 + "event-listener",
335 + "event-listener-strategy",
336 + "pin-project-lite",
337 +]
338 +
339 +[[package]]
340 +name = "async-process"
341 +version = "2.5.0"
342 +source = "registry+https://github.com/rust-lang/crates.io-index"
343 +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
344 +dependencies = [
345 + "async-channel",
346 + "async-io",
347 + "async-lock",
348 + "async-signal",
349 + "async-task",
350 + "blocking",
351 + "cfg-if",
352 + "event-listener",
353 + "futures-lite",
354 + "rustix",
355 +]
356 +
357 +[[package]]
358 +name = "async-recursion"
359 +version = "1.1.1"
360 +source = "registry+https://github.com/rust-lang/crates.io-index"
361 +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
362 +dependencies = [
363 + "proc-macro2",
364 + "quote",
365 + "syn 2.0.118",
366 +]
367 +
368 +[[package]]
369 +name = "async-signal"
370 +version = "0.2.14"
371 +source = "registry+https://github.com/rust-lang/crates.io-index"
372 +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
373 +dependencies = [
374 + "async-io",
375 + "async-lock",
376 + "atomic-waker",
377 + "cfg-if",
378 + "futures-core",
379 + "futures-io",
380 + "rustix",
381 + "signal-hook-registry",
382 + "slab",
383 + "windows-sys 0.61.2",
384 +]
385 +
386 +[[package]]
387 +name = "async-task"
388 +version = "4.7.1"
389 +source = "registry+https://github.com/rust-lang/crates.io-index"
390 +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
391 +
392 +[[package]]
393 +name = "async-trait"
394 +version = "0.1.89"
395 +source = "registry+https://github.com/rust-lang/crates.io-index"
396 +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
397 +dependencies = [
398 + "proc-macro2",
399 + "quote",
400 + "syn 2.0.118",
401 +]
402 +
403 +[[package]]
404 +name = "atk"
405 +version = "0.18.2"
406 +source = "registry+https://github.com/rust-lang/crates.io-index"
407 +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b"
408 +dependencies = [
409 + "atk-sys",
410 + "glib",
411 + "libc",
412 +]
413 +
414 +[[package]]
415 +name = "atk-sys"
416 +version = "0.18.2"
417 +source = "registry+https://github.com/rust-lang/crates.io-index"
418 +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086"
419 +dependencies = [
420 + "glib-sys",
421 + "gobject-sys",
422 + "libc",
423 + "system-deps",
424 +]
425 +
426 +[[package]]
427 +name = "atomic-waker"
428 +version = "1.1.2"
429 +source = "registry+https://github.com/rust-lang/crates.io-index"
430 +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
431 +
432 +[[package]]
433 +name = "atty"
434 +version = "0.2.14"
435 +source = "registry+https://github.com/rust-lang/crates.io-index"
436 +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
437 +dependencies = [
438 + "hermit-abi 0.1.19",
439 + "libc",
440 + "winapi",
441 +]
442 +
443 +[[package]]
444 +name = "autocfg"
445 +version = "1.5.1"
446 +source = "registry+https://github.com/rust-lang/crates.io-index"
447 +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
448 +
449 +[[package]]
450 +name = "aws-lc-rs"
451 +version = "1.17.1"
452 +source = "registry+https://github.com/rust-lang/crates.io-index"
453 +checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad"
454 +dependencies = [
455 + "aws-lc-sys",
456 + "zeroize",
457 +]
458 +
459 +[[package]]
460 +name = "aws-lc-sys"
461 +version = "0.42.0"
462 +source = "registry+https://github.com/rust-lang/crates.io-index"
463 +checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444"
464 +dependencies = [
465 + "cc",
466 + "cmake",
467 + "dunce",
468 + "fs_extra",
469 + "pkg-config",
470 +]
471 +
472 +[[package]]
473 +name = "base64"
474 +version = "0.13.1"
475 +source = "registry+https://github.com/rust-lang/crates.io-index"
476 +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
477 +
478 +[[package]]
479 +name = "base64"
480 +version = "0.21.7"
481 +source = "registry+https://github.com/rust-lang/crates.io-index"
482 +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
483 +
484 +[[package]]
485 +name = "base64"
486 +version = "0.22.1"
487 +source = "registry+https://github.com/rust-lang/crates.io-index"
488 +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
489 +
490 +[[package]]
491 +name = "basic-toml"
492 +version = "0.1.10"
493 +source = "registry+https://github.com/rust-lang/crates.io-index"
494 +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
495 +dependencies = [
496 + "serde",
497 +]
498 +
499 +[[package]]
500 +name = "bit-set"
501 +version = "0.5.3"
502 +source = "registry+https://github.com/rust-lang/crates.io-index"
503 +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
504 +dependencies = [
505 + "bit-vec 0.6.3",
506 +]
507 +
508 +[[package]]
509 +name = "bit-set"
510 +version = "0.8.0"
511 +source = "registry+https://github.com/rust-lang/crates.io-index"
512 +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
513 +dependencies = [
514 + "bit-vec 0.8.0",
515 +]
516 +
517 +[[package]]
518 +name = "bit-vec"
519 +version = "0.6.3"
520 +source = "registry+https://github.com/rust-lang/crates.io-index"
521 +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
522 +
523 +[[package]]
524 +name = "bit-vec"
525 +version = "0.8.0"
526 +source = "registry+https://github.com/rust-lang/crates.io-index"
527 +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
528 +
529 +[[package]]
530 +name = "bit_field"
531 +version = "0.10.3"
532 +source = "registry+https://github.com/rust-lang/crates.io-index"
533 +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
534 +
535 +[[package]]
536 +name = "bitflags"
537 +version = "1.3.2"
538 +source = "registry+https://github.com/rust-lang/crates.io-index"
539 +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
540 +
541 +[[package]]
542 +name = "bitflags"
543 +version = "2.13.0"
544 +source = "registry+https://github.com/rust-lang/crates.io-index"
545 +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
546 +dependencies = [
547 + "serde_core",
548 +]
549 +
550 +[[package]]
551 +name = "bitvec"
552 +version = "1.1.1"
553 +source = "registry+https://github.com/rust-lang/crates.io-index"
554 +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837"
555 +dependencies = [
556 + "funty",
557 + "radium",
558 + "tap",
559 + "wyz",
560 +]
561 +
562 +[[package]]
563 +name = "block"
564 +version = "0.1.6"
565 +source = "registry+https://github.com/rust-lang/crates.io-index"
566 +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
567 +
568 +[[package]]
569 +name = "block-buffer"
570 +version = "0.10.4"
571 +source = "registry+https://github.com/rust-lang/crates.io-index"
572 +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
573 +dependencies = [
574 + "generic-array",
575 +]
576 +
577 +[[package]]
578 +name = "block2"
579 +version = "0.6.2"
580 +source = "registry+https://github.com/rust-lang/crates.io-index"
581 +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
582 +dependencies = [
583 + "objc2",
584 +]
585 +
586 +[[package]]
587 +name = "blocking"
588 +version = "1.6.2"
589 +source = "registry+https://github.com/rust-lang/crates.io-index"
590 +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
591 +dependencies = [
592 + "async-channel",
593 + "async-task",
594 + "futures-io",
595 + "futures-lite",
596 + "piper",
597 +]
598 +
599 +[[package]]
600 +name = "bm25"
601 +version = "2.3.2"
602 +source = "registry+https://github.com/rust-lang/crates.io-index"
603 +checksum = "1cbd8ffdfb7b4c2ff038726178a780a94f90525ed0ad264c0afaa75dd8c18a64"
604 +dependencies = [
605 + "cached",
606 + "deunicode",
607 + "fxhash",
608 + "rust-stemmers",
609 + "stop-words",
610 + "unicode-segmentation",
611 +]
612 +
613 +[[package]]
614 +name = "borsh"
615 +version = "1.7.0"
616 +source = "registry+https://github.com/rust-lang/crates.io-index"
617 +checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145"
618 +dependencies = [
619 + "borsh-derive",
620 + "bytes",
621 + "cfg_aliases",
622 +]
623 +
624 +[[package]]
625 +name = "borsh-derive"
626 +version = "1.7.0"
627 +source = "registry+https://github.com/rust-lang/crates.io-index"
628 +checksum = "3ae8fb4fb5740e4b2c4884ff95f5f32f5e8479db1e8fd8eb49ddbe09eb09bb7c"
629 +dependencies = [
630 + "once_cell",
631 + "proc-macro-crate 3.5.0",
632 + "proc-macro2",
633 + "quote",
634 + "syn 2.0.118",
635 +]
636 +
637 +[[package]]
638 +name = "brotli"
639 +version = "8.0.4"
640 +source = "registry+https://github.com/rust-lang/crates.io-index"
641 +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3"
642 +dependencies = [
643 + "alloc-no-stdlib",
644 + "alloc-stdlib",
645 + "brotli-decompressor",
646 +]
647 +
648 +[[package]]
649 +name = "brotli-decompressor"
650 +version = "5.0.3"
651 +source = "registry+https://github.com/rust-lang/crates.io-index"
652 +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583"
653 +dependencies = [
654 + "alloc-no-stdlib",
655 + "alloc-stdlib",
656 +]
657 +
658 +[[package]]
659 +name = "bs58"
660 +version = "0.5.1"
661 +source = "registry+https://github.com/rust-lang/crates.io-index"
662 +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
663 +dependencies = [
664 + "tinyvec",
665 +]
666 +
667 +[[package]]
668 +name = "bstr"
669 +version = "1.12.3"
670 +source = "registry+https://github.com/rust-lang/crates.io-index"
671 +checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79"
672 +dependencies = [
673 + "memchr",
674 + "regex-automata",
675 + "serde_core",
676 +]
677 +
678 +[[package]]
679 +name = "bumpalo"
680 +version = "3.20.3"
681 +source = "registry+https://github.com/rust-lang/crates.io-index"
682 +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
683 +
684 +[[package]]
685 +name = "byte-unit"
686 +version = "5.2.5"
687 +source = "registry+https://github.com/rust-lang/crates.io-index"
688 +checksum = "4a813de7f2bbedb7dce265b64f1cf5908ebe4d56281ece8d847e98113788b9b0"
689 +dependencies = [
690 + "rust_decimal",
691 + "schemars 1.2.1",
692 + "serde",
693 + "utf8-width",
694 +]
695 +
696 +[[package]]
697 +name = "bytecheck"
698 +version = "0.6.12"
699 +source = "registry+https://github.com/rust-lang/crates.io-index"
700 +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
701 +dependencies = [
702 + "bytecheck_derive",
703 + "ptr_meta",
704 + "simdutf8",
705 +]
706 +
707 +[[package]]
708 +name = "bytecheck_derive"
709 +version = "0.6.12"
710 +source = "registry+https://github.com/rust-lang/crates.io-index"
711 +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
712 +dependencies = [
713 + "proc-macro2",
714 + "quote",
715 + "syn 1.0.109",
716 +]
717 +
718 +[[package]]
719 +name = "bytemuck"
720 +version = "1.25.0"
721 +source = "registry+https://github.com/rust-lang/crates.io-index"
722 +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
723 +dependencies = [
724 + "bytemuck_derive",
725 +]
726 +
727 +[[package]]
728 +name = "bytemuck_derive"
729 +version = "1.10.2"
730 +source = "registry+https://github.com/rust-lang/crates.io-index"
731 +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
732 +dependencies = [
733 + "proc-macro2",
734 + "quote",
735 + "syn 2.0.118",
736 +]
737 +
738 +[[package]]
739 +name = "byteorder"
740 +version = "1.5.0"
741 +source = "registry+https://github.com/rust-lang/crates.io-index"
742 +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
743 +
744 +[[package]]
745 +name = "byteorder-lite"
746 +version = "0.1.0"
747 +source = "registry+https://github.com/rust-lang/crates.io-index"
748 +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
749 +
750 +[[package]]
751 +name = "bytes"
752 +version = "1.12.1"
753 +source = "registry+https://github.com/rust-lang/crates.io-index"
754 +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
755 +dependencies = [
756 + "serde",
757 +]
758 +
759 +[[package]]
760 +name = "cached"
761 +version = "0.56.0"
762 +source = "registry+https://github.com/rust-lang/crates.io-index"
763 +checksum = "801927ee168e17809ab8901d9f01f700cd7d8d6a6527997fee44e4b0327a253c"
764 +dependencies = [
765 + "ahash 0.8.12",
766 + "cached_proc_macro",
767 + "cached_proc_macro_types",
768 + "hashbrown 0.15.5",
769 + "once_cell",
770 + "thiserror 2.0.18",
771 + "web-time",
772 +]
773 +
774 +[[package]]
775 +name = "cached_proc_macro"
776 +version = "0.25.0"
777 +source = "registry+https://github.com/rust-lang/crates.io-index"
778 +checksum = "9225bdcf4e4a9a4c08bf16607908eb2fbf746828d5e0b5e019726dbf6571f201"
779 +dependencies = [
780 + "darling 0.20.11",
781 + "proc-macro2",
782 + "quote",
783 + "syn 2.0.118",
784 +]
785 +
786 +[[package]]
787 +name = "cached_proc_macro_types"
788 +version = "0.1.1"
789 +source = "registry+https://github.com/rust-lang/crates.io-index"
790 +checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
791 +
792 +[[package]]
793 +name = "cairo-rs"
794 +version = "0.18.5"
795 +source = "registry+https://github.com/rust-lang/crates.io-index"
796 +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2"
797 +dependencies = [
798 + "bitflags 2.13.0",
799 + "cairo-sys-rs",
800 + "glib",
801 + "libc",
802 + "once_cell",
803 + "thiserror 1.0.69",
804 +]
805 +
806 +[[package]]
807 +name = "cairo-sys-rs"
808 +version = "0.18.2"
809 +source = "registry+https://github.com/rust-lang/crates.io-index"
810 +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51"
811 +dependencies = [
812 + "glib-sys",
813 + "libc",
814 + "system-deps",
815 +]
816 +
817 +[[package]]
818 +name = "camino"
819 +version = "1.2.4"
820 +source = "registry+https://github.com/rust-lang/crates.io-index"
821 +checksum = "5f2d30e4173c4026932d51d31d6b0613b1fd3014bf3f9f8943d4ba139c437ba0"
822 +dependencies = [
823 + "serde_core",
824 +]
825 +
826 +[[package]]
827 +name = "candle-core"
828 +version = "0.10.2"
829 +source = "registry+https://github.com/rust-lang/crates.io-index"
830 +checksum = "6bd9895436c1ba5dc1037a19935d084b838db066ff4e15ef7dded020b7c12a4a"
831 +dependencies = [
832 + "byteorder",
833 + "candle-metal-kernels",
834 + "candle-ug",
835 + "float8",
836 + "gemm 0.19.0",
837 + "half",
838 + "libm",
839 + "memmap2",
840 + "num-traits",
841 + "num_cpus",
842 + "objc2-foundation",
843 + "objc2-metal",
844 + "rand 0.9.4",
845 + "rand_distr 0.5.1",
846 + "rayon",
847 + "safetensors 0.7.0",
848 + "thiserror 2.0.18",
849 + "tokenizers 0.22.2",
850 + "yoke 0.8.3",
851 + "zip",
852 +]
853 +
854 +[[package]]
855 +name = "candle-metal-kernels"
856 +version = "0.10.2"
857 +source = "registry+https://github.com/rust-lang/crates.io-index"
858 +checksum = "4b6b5a4cae6b4e1ab0efcee4dc05272d11b374a3d1ba121b3a961e36be54ab60"
859 +dependencies = [
860 + "half",
861 + "objc2",
862 + "objc2-foundation",
863 + "objc2-metal",
864 + "once_cell",
865 + "thiserror 2.0.18",
866 + "tracing",
867 +]
868 +
869 +[[package]]
870 +name = "candle-nn"
871 +version = "0.10.2"
872 +source = "registry+https://github.com/rust-lang/crates.io-index"
873 +checksum = "a9317a09d6530b758990ed7f625ac69ff43653bc9ee28b0464644ad1169ada87"
874 +dependencies = [
875 + "candle-core",
876 + "candle-metal-kernels",
877 + "half",
878 + "libc",
879 + "num-traits",
880 + "objc2-metal",
881 + "rayon",
882 + "safetensors 0.7.0",
883 + "serde",
884 + "thiserror 2.0.18",
885 +]
886 +
887 +[[package]]
888 +name = "candle-ug"
889 +version = "0.10.2"
890 +source = "registry+https://github.com/rust-lang/crates.io-index"
891 +checksum = "ca0fc3167cbc99c8ec1be618cb620aa21dca95038f118c3579a79370e3dc5f77"
892 +dependencies = [
893 + "ug",
894 + "ug-metal",
895 +]
896 +
897 +[[package]]
898 +name = "cargo-platform"
899 +version = "0.1.9"
900 +source = "registry+https://github.com/rust-lang/crates.io-index"
901 +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
902 +dependencies = [
903 + "serde",
904 +]
905 +
906 +[[package]]
907 +name = "cargo_metadata"
908 +version = "0.19.2"
909 +source = "registry+https://github.com/rust-lang/crates.io-index"
910 +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
911 +dependencies = [
912 + "camino",
913 + "cargo-platform",
914 + "semver",
915 + "serde",
916 + "serde_json",
917 + "thiserror 2.0.18",
918 +]
919 +
920 +[[package]]
921 +name = "cargo_toml"
922 +version = "0.22.3"
923 +source = "registry+https://github.com/rust-lang/crates.io-index"
924 +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77"
925 +dependencies = [
926 + "serde",
927 + "toml 0.9.12+spec-1.1.0",
928 +]
929 +
930 +[[package]]
931 +name = "castaway"
932 +version = "0.2.4"
933 +source = "registry+https://github.com/rust-lang/crates.io-index"
934 +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
935 +dependencies = [
936 + "rustversion",
937 +]
938 +
939 +[[package]]
940 +name = "cc"
941 +version = "1.2.66"
942 +source = "registry+https://github.com/rust-lang/crates.io-index"
943 +checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996"
944 +dependencies = [
945 + "find-msvc-tools",
946 + "jobserver",
947 + "libc",
948 + "shlex",
949 +]
950 +
951 +[[package]]
952 +name = "cesu8"
953 +version = "1.1.0"
954 +source = "registry+https://github.com/rust-lang/crates.io-index"
955 +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
956 +
957 +[[package]]
958 +name = "cfb"
959 +version = "0.7.3"
960 +source = "registry+https://github.com/rust-lang/crates.io-index"
961 +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f"
962 +dependencies = [
963 + "byteorder",
964 + "fnv",
965 + "uuid 1.23.4",
966 +]
967 +
968 +[[package]]
969 +name = "cfg-expr"
970 +version = "0.15.8"
971 +source = "registry+https://github.com/rust-lang/crates.io-index"
972 +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
973 +dependencies = [
974 + "smallvec 1.15.2",
975 + "target-lexicon",
976 +]
977 +
978 +[[package]]
979 +name = "cfg-if"
980 +version = "1.0.4"
981 +source = "registry+https://github.com/rust-lang/crates.io-index"
982 +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
983 +
984 +[[package]]
985 +name = "cfg_aliases"
986 +version = "0.2.1"
987 +source = "registry+https://github.com/rust-lang/crates.io-index"
988 +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
989 +
990 +[[package]]
991 +name = "cfgrammar"
992 +version = "0.14.2"
993 +source = "registry+https://github.com/rust-lang/crates.io-index"
994 +checksum = "b5ef80386dea3cda50570f22e1c6a474f0cc688ae220fc584fef354acaf41f2b"
995 +dependencies = [
996 + "indexmap 2.14.0",
997 + "num-traits",
998 + "proc-macro2",
999 + "quote",
1000 + "regex",
1001 + "vob",
1002 +]
1003 +
1004 +[[package]]
1005 +name = "chacha20"
1006 +version = "0.10.1"
1007 +source = "registry+https://github.com/rust-lang/crates.io-index"
1008 +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
1009 +dependencies = [
1010 + "cfg-if",
1011 + "cpufeatures 0.3.0",
1012 + "rand_core 0.10.1",
1013 +]
1014 +
1015 +[[package]]
1016 +name = "chrono"
1017 +version = "0.4.45"
1018 +source = "registry+https://github.com/rust-lang/crates.io-index"
1019 +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
1020 +dependencies = [
1021 + "iana-time-zone",
1022 + "js-sys",
1023 + "num-traits",
1024 + "serde",
1025 + "wasm-bindgen",
1026 + "windows-link 0.2.1",
1027 +]
1028 +
1029 +[[package]]
1030 +name = "clap"
1031 +version = "2.34.0"
1032 +source = "registry+https://github.com/rust-lang/crates.io-index"
1033 +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
1034 +dependencies = [
1035 + "ansi_term",
1036 + "atty",
1037 + "bitflags 1.3.2",
1038 + "strsim 0.8.0",
1039 + "textwrap 0.11.0",
1040 + "unicode-width 0.1.14",
1041 + "vec_map",
1042 +]
1043 +
1044 +[[package]]
1045 +name = "clap"
1046 +version = "4.6.1"
1047 +source = "registry+https://github.com/rust-lang/crates.io-index"
1048 +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
1049 +dependencies = [
1050 + "clap_builder",
1051 + "clap_derive",
1052 +]
1053 +
1054 +[[package]]
1055 +name = "clap_builder"
1056 +version = "4.6.0"
1057 +source = "registry+https://github.com/rust-lang/crates.io-index"
1058 +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
1059 +dependencies = [
1060 + "anstream",
1061 + "anstyle",
1062 + "clap_lex",
1063 + "strsim 0.11.1",
1064 + "terminal_size",
1065 +]
1066 +
1067 +[[package]]
1068 +name = "clap_derive"
1069 +version = "4.6.1"
1070 +source = "registry+https://github.com/rust-lang/crates.io-index"
1071 +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
1072 +dependencies = [
1073 + "heck 0.5.0",
1074 + "proc-macro2",
1075 + "quote",
1076 + "syn 2.0.118",
1077 +]
1078 +
1079 +[[package]]
1080 +name = "clap_lex"
1081 +version = "1.1.0"
1082 +source = "registry+https://github.com/rust-lang/crates.io-index"
1083 +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
1084 +
1085 +[[package]]
1086 +name = "cmake"
1087 +version = "0.1.58"
1088 +source = "registry+https://github.com/rust-lang/crates.io-index"
1089 +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
1090 +dependencies = [
1091 + "cc",
1092 +]
1093 +
1094 +[[package]]
1095 +name = "color_quant"
1096 +version = "1.1.0"
1097 +source = "registry+https://github.com/rust-lang/crates.io-index"
1098 +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
1099 +
1100 +[[package]]
1101 +name = "colorchoice"
1102 +version = "1.0.5"
1103 +source = "registry+https://github.com/rust-lang/crates.io-index"
1104 +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
1105 +
1106 +[[package]]
1107 +name = "combine"
1108 +version = "4.6.7"
1109 +source = "registry+https://github.com/rust-lang/crates.io-index"
1110 +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
1111 +dependencies = [
1112 + "bytes",
1113 + "memchr",
1114 +]
1115 +
1116 +[[package]]
1117 +name = "compact_str"
1118 +version = "0.9.1"
1119 +source = "registry+https://github.com/rust-lang/crates.io-index"
1120 +checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab"
1121 +dependencies = [
1122 + "castaway",
1123 + "cfg-if",
1124 + "itoa",
1125 + "rustversion",
1126 + "ryu",
1127 + "serde",
1128 + "static_assertions",
1129 +]
1130 +
1131 +[[package]]
1132 +name = "concurrent-queue"
1133 +version = "2.5.0"
1134 +source = "registry+https://github.com/rust-lang/crates.io-index"
1135 +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
1136 +dependencies = [
1137 + "crossbeam-utils",
1138 +]
1139 +
1140 +[[package]]
1141 +name = "console"
1142 +version = "0.15.11"
1143 +source = "registry+https://github.com/rust-lang/crates.io-index"
1144 +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
1145 +dependencies = [
1146 + "encode_unicode",
1147 + "libc",
1148 + "once_cell",
1149 + "unicode-width 0.2.2",
1150 + "windows-sys 0.59.0",
1151 +]
1152 +
1153 +[[package]]
1154 +name = "console"
1155 +version = "0.16.4"
1156 +source = "registry+https://github.com/rust-lang/crates.io-index"
1157 +checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c"
1158 +dependencies = [
1159 + "encode_unicode",
1160 + "libc",
1161 + "unicode-width 0.2.2",
1162 + "windows-sys 0.61.2",
1163 +]
1164 +
1165 +[[package]]
1166 +name = "convert_case"
1167 +version = "0.6.0"
1168 +source = "registry+https://github.com/rust-lang/crates.io-index"
1169 +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
1170 +dependencies = [
1171 + "unicode-segmentation",
1172 +]
1173 +
1174 +[[package]]
1175 +name = "convert_case"
1176 +version = "0.10.0"
1177 +source = "registry+https://github.com/rust-lang/crates.io-index"
1178 +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
1179 +dependencies = [
1180 + "unicode-segmentation",
1181 +]
1182 +
1183 +[[package]]
1184 +name = "cookie"
1185 +version = "0.18.1"
1186 +source = "registry+https://github.com/rust-lang/crates.io-index"
1187 +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
1188 +dependencies = [
1189 + "time",
1190 + "version_check",
1191 +]
1192 +
1193 +[[package]]
1194 +name = "core-foundation"
1195 +version = "0.9.4"
1196 +source = "registry+https://github.com/rust-lang/crates.io-index"
1197 +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
1198 +dependencies = [
1199 + "core-foundation-sys",
1200 + "libc",
1201 +]
1202 +
1203 +[[package]]
1204 +name = "core-foundation"
1205 +version = "0.10.1"
1206 +source = "registry+https://github.com/rust-lang/crates.io-index"
1207 +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
1208 +dependencies = [
1209 + "core-foundation-sys",
1210 + "libc",
1211 +]
1212 +
1213 +[[package]]
1214 +name = "core-foundation-sys"
1215 +version = "0.8.7"
1216 +source = "registry+https://github.com/rust-lang/crates.io-index"
1217 +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
1218 +
1219 +[[package]]
1220 +name = "core-graphics"
1221 +version = "0.25.0"
1222 +source = "registry+https://github.com/rust-lang/crates.io-index"
1223 +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
1224 +dependencies = [
1225 + "bitflags 2.13.0",
1226 + "core-foundation 0.10.1",
1227 + "core-graphics-types 0.2.0",
1228 + "foreign-types",
1229 + "libc",
1230 +]
1231 +
1232 +[[package]]
1233 +name = "core-graphics-types"
1234 +version = "0.1.3"
1235 +source = "registry+https://github.com/rust-lang/crates.io-index"
1236 +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
1237 +dependencies = [
1238 + "bitflags 1.3.2",
1239 + "core-foundation 0.9.4",
1240 + "libc",
1241 +]
1242 +
1243 +[[package]]
1244 +name = "core-graphics-types"
1245 +version = "0.2.0"
1246 +source = "registry+https://github.com/rust-lang/crates.io-index"
1247 +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
1248 +dependencies = [
1249 + "bitflags 2.13.0",
1250 + "core-foundation 0.10.1",
1251 + "libc",
1252 +]
1253 +
1254 +[[package]]
1255 +name = "cpufeatures"
1256 +version = "0.2.17"
1257 +source = "registry+https://github.com/rust-lang/crates.io-index"
1258 +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
1259 +dependencies = [
1260 + "libc",
1261 +]
1262 +
1263 +[[package]]
1264 +name = "cpufeatures"
1265 +version = "0.3.0"
1266 +source = "registry+https://github.com/rust-lang/crates.io-index"
1267 +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
1268 +dependencies = [
1269 + "libc",
1270 +]
1271 +
1272 +[[package]]
1273 +name = "crc32fast"
1274 +version = "1.5.0"
1275 +source = "registry+https://github.com/rust-lang/crates.io-index"
1276 +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
1277 +dependencies = [
1278 + "cfg-if",
1279 +]
1280 +
1281 +[[package]]
1282 +name = "crossbeam-channel"
1283 +version = "0.5.16"
1284 +source = "registry+https://github.com/rust-lang/crates.io-index"
1285 +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e"
1286 +dependencies = [
1287 + "crossbeam-utils",
1288 +]
1289 +
1290 +[[package]]
1291 +name = "crossbeam-deque"
1292 +version = "0.8.7"
1293 +source = "registry+https://github.com/rust-lang/crates.io-index"
1294 +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb"
1295 +dependencies = [
1296 + "crossbeam-epoch",
1297 + "crossbeam-utils",
1298 +]
1299 +
1300 +[[package]]
1301 +name = "crossbeam-epoch"
1302 +version = "0.9.20"
1303 +source = "registry+https://github.com/rust-lang/crates.io-index"
1304 +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
1305 +dependencies = [
1306 + "crossbeam-utils",
1307 +]
1308 +
1309 +[[package]]
1310 +name = "crossbeam-utils"
1311 +version = "0.8.22"
1312 +source = "registry+https://github.com/rust-lang/crates.io-index"
1313 +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
1314 +
1315 +[[package]]
1316 +name = "crossterm"
1317 +version = "0.29.0"
1318 +source = "registry+https://github.com/rust-lang/crates.io-index"
1319 +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
1320 +dependencies = [
1321 + "bitflags 2.13.0",
1322 + "crossterm_winapi",
1323 + "derive_more",
1324 + "document-features",
1325 + "mio",
1326 + "parking_lot",
1327 + "rustix",
1328 + "signal-hook",
1329 + "signal-hook-mio",
1330 + "winapi",
1331 +]
1332 +
1333 +[[package]]
1334 +name = "crossterm_winapi"
1335 +version = "0.9.1"
1336 +source = "registry+https://github.com/rust-lang/crates.io-index"
1337 +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
1338 +dependencies = [
1339 + "winapi",
1340 +]
1341 +
1342 +[[package]]
1343 +name = "crunchy"
1344 +version = "0.2.4"
1345 +source = "registry+https://github.com/rust-lang/crates.io-index"
1346 +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
1347 +
1348 +[[package]]
1349 +name = "crypto-common"
1350 +version = "0.1.7"
1351 +source = "registry+https://github.com/rust-lang/crates.io-index"
1352 +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
1353 +dependencies = [
1354 + "generic-array",
1355 + "typenum",
1356 +]
1357 +
1358 +[[package]]
1359 +name = "cssparser"
1360 +version = "0.36.0"
1361 +source = "registry+https://github.com/rust-lang/crates.io-index"
1362 +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2"
1363 +dependencies = [
1364 + "cssparser-macros",
1365 + "dtoa-short",
1366 + "itoa",
1367 + "phf",
1368 + "smallvec 1.15.2",
1369 +]
1370 +
1371 +[[package]]
1372 +name = "cssparser-macros"
1373 +version = "0.6.1"
1374 +source = "registry+https://github.com/rust-lang/crates.io-index"
1375 +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
1376 +dependencies = [
1377 + "quote",
1378 + "syn 2.0.118",
1379 +]
1380 +
1381 +[[package]]
1382 +name = "csv"
1383 +version = "1.4.0"
1384 +source = "registry+https://github.com/rust-lang/crates.io-index"
1385 +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938"
1386 +dependencies = [
1387 + "csv-core",
1388 + "itoa",
1389 + "ryu",
1390 + "serde_core",
1391 +]
1392 +
1393 +[[package]]
1394 +name = "csv-core"
1395 +version = "0.1.13"
1396 +source = "registry+https://github.com/rust-lang/crates.io-index"
1397 +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782"
1398 +dependencies = [
1399 + "memchr",
1400 +]
1401 +
1402 +[[package]]
1403 +name = "ctor"
1404 +version = "0.8.0"
1405 +source = "registry+https://github.com/rust-lang/crates.io-index"
1406 +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98"
1407 +dependencies = [
1408 + "ctor-proc-macro",
1409 + "dtor",
1410 +]
1411 +
1412 +[[package]]
1413 +name = "ctor-proc-macro"
1414 +version = "0.0.7"
1415 +source = "registry+https://github.com/rust-lang/crates.io-index"
1416 +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1"
1417 +
1418 +[[package]]
1419 +name = "darling"
1420 +version = "0.11.0"
1421 +source = "registry+https://github.com/rust-lang/crates.io-index"
1422 +checksum = "dbffa8f8e38810422f320ca457a93cf1cd0056dc9c06c556b867558e0d471463"
1423 +dependencies = [
1424 + "darling_core 0.11.0",
1425 + "darling_macro 0.11.0",
1426 +]
1427 +
1428 +[[package]]
1429 +name = "darling"
1430 +version = "0.20.11"
1431 +source = "registry+https://github.com/rust-lang/crates.io-index"
1432 +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
1433 +dependencies = [
1434 + "darling_core 0.20.11",
1435 + "darling_macro 0.20.11",
1436 +]
1437 +
1438 +[[package]]
1439 +name = "darling"
1440 +version = "0.23.0"
1441 +source = "registry+https://github.com/rust-lang/crates.io-index"
1442 +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
1443 +dependencies = [
1444 + "darling_core 0.23.0",
1445 + "darling_macro 0.23.0",
1446 +]
1447 +
1448 +[[package]]
1449 +name = "darling_core"
1450 +version = "0.11.0"
1451 +source = "registry+https://github.com/rust-lang/crates.io-index"
1452 +checksum = "06e172685d94b7b83800e3256a63261537b9d6129e10f21c8e13ddf9dba8c64d"
1453 +dependencies = [
1454 + "fnv",
1455 + "ident_case",
1456 + "proc-macro2",
1457 + "quote",
1458 + "strsim 0.10.0",
1459 + "syn 1.0.109",
1460 +]
1461 +
1462 +[[package]]
1463 +name = "darling_core"
1464 +version = "0.20.11"
1465 +source = "registry+https://github.com/rust-lang/crates.io-index"
1466 +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
1467 +dependencies = [
1468 + "fnv",
1469 + "ident_case",
1470 + "proc-macro2",
1471 + "quote",
1472 + "strsim 0.11.1",
1473 + "syn 2.0.118",
1474 +]
1475 +
1476 +[[package]]
1477 +name = "darling_core"
1478 +version = "0.23.0"
1479 +source = "registry+https://github.com/rust-lang/crates.io-index"
1480 +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0"
1481 +dependencies = [
1482 + "ident_case",
1483 + "proc-macro2",
1484 + "quote",
1485 + "strsim 0.11.1",
1486 + "syn 2.0.118",
1487 +]
1488 +
1489 +[[package]]
1490 +name = "darling_macro"
1491 +version = "0.11.0"
1492 +source = "registry+https://github.com/rust-lang/crates.io-index"
1493 +checksum = "f0618ac802792cebd1918ac6042a6ea1eeab92db34b35656afaa577929820788"
1494 +dependencies = [
1495 + "darling_core 0.11.0",
1496 + "quote",
1497 + "syn 1.0.109",
1498 +]
1499 +
1500 +[[package]]
1501 +name = "darling_macro"
1502 +version = "0.20.11"
1503 +source = "registry+https://github.com/rust-lang/crates.io-index"
1504 +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
1505 +dependencies = [
1506 + "darling_core 0.20.11",
1507 + "quote",
1508 + "syn 2.0.118",
1509 +]
1510 +
1511 +[[package]]
1512 +name = "darling_macro"
1513 +version = "0.23.0"
1514 +source = "registry+https://github.com/rust-lang/crates.io-index"
1515 +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
1516 +dependencies = [
1517 + "darling_core 0.23.0",
1518 + "quote",
1519 + "syn 2.0.118",
1520 +]
1521 +
1522 +[[package]]
1523 +name = "dary_heap"
1524 +version = "0.3.9"
1525 +source = "registry+https://github.com/rust-lang/crates.io-index"
1526 +checksum = "8b1e3a325bc115f096c8b77bbf027a7c2592230e70be2d985be950d3d5e60ebe"
1527 +dependencies = [
1528 + "serde",
1529 +]
1530 +
1531 +[[package]]
1532 +name = "data-encoding"
1533 +version = "2.11.0"
1534 +source = "registry+https://github.com/rust-lang/crates.io-index"
1535 +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
1536 +
1537 +[[package]]
1538 +name = "dbus"
1539 +version = "0.9.12"
1540 +source = "registry+https://github.com/rust-lang/crates.io-index"
1541 +checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e"
1542 +dependencies = [
1543 + "libc",
1544 + "libdbus-sys",
1545 + "windows-sys 0.61.2",
1546 +]
1547 +
1548 +[[package]]
1549 +name = "defmac"
1550 +version = "0.1.3"
1551 +source = "registry+https://github.com/rust-lang/crates.io-index"
1552 +checksum = "aafbece59594ed57696a1a69e8bb3ca1683fbc9cdb41d5c02726070b2cd8f19d"
1553 +
1554 +[[package]]
1555 +name = "deranged"
1556 +version = "0.5.8"
1557 +source = "registry+https://github.com/rust-lang/crates.io-index"
1558 +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
1559 +dependencies = [
1560 + "serde_core",
1561 +]
1562 +
1563 +[[package]]
1564 +name = "derive-new"
1565 +version = "0.7.0"
1566 +source = "registry+https://github.com/rust-lang/crates.io-index"
1567 +checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc"
1568 +dependencies = [
1569 + "proc-macro2",
1570 + "quote",
1571 + "syn 2.0.118",
1572 +]
1573 +
1574 +[[package]]
1575 +name = "derive_builder"
1576 +version = "0.20.2"
1577 +source = "registry+https://github.com/rust-lang/crates.io-index"
1578 +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
1579 +dependencies = [
1580 + "derive_builder_macro",
1581 +]
1582 +
1583 +[[package]]
1584 +name = "derive_builder_core"
1585 +version = "0.20.2"
1586 +source = "registry+https://github.com/rust-lang/crates.io-index"
1587 +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
1588 +dependencies = [
1589 + "darling 0.20.11",
1590 + "proc-macro2",
1591 + "quote",
1592 + "syn 2.0.118",
1593 +]
1594 +
1595 +[[package]]
1596 +name = "derive_builder_macro"
1597 +version = "0.20.2"
1598 +source = "registry+https://github.com/rust-lang/crates.io-index"
1599 +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
1600 +dependencies = [
1601 + "derive_builder_core",
1602 + "syn 2.0.118",
1603 +]
1604 +
1605 +[[package]]
1606 +name = "derive_more"
1607 +version = "2.1.1"
1608 +source = "registry+https://github.com/rust-lang/crates.io-index"
1609 +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
1610 +dependencies = [
1611 + "derive_more-impl",
1612 +]
1613 +
1614 +[[package]]
1615 +name = "derive_more-impl"
1616 +version = "2.1.1"
1617 +source = "registry+https://github.com/rust-lang/crates.io-index"
1618 +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
1619 +dependencies = [
1620 + "convert_case 0.10.0",
1621 + "proc-macro2",
1622 + "quote",
1623 + "rustc_version",
1624 + "syn 2.0.118",
1625 +]
1626 +
1627 +[[package]]
1628 +name = "derivre"
1629 +version = "0.3.12"
1630 +source = "registry+https://github.com/rust-lang/crates.io-index"
1631 +checksum = "a892ec3953f4ca893e8d2086ed03d4b58e19c77ffb864a574b332d7d40c175f3"
1632 +dependencies = [
1633 + "anyhow",
1634 + "bytemuck",
1635 + "bytemuck_derive",
1636 + "hashbrown 0.17.1",
1637 + "regex-syntax",
1638 + "strum 0.28.0",
1639 +]
1640 +
1641 +[[package]]
1642 +name = "deunicode"
1643 +version = "1.6.2"
1644 +source = "registry+https://github.com/rust-lang/crates.io-index"
1645 +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04"
1646 +
1647 +[[package]]
1648 +name = "digest"
1649 +version = "0.10.7"
1650 +source = "registry+https://github.com/rust-lang/crates.io-index"
1651 +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
1652 +dependencies = [
1653 + "block-buffer",
1654 + "crypto-common",
1655 +]
1656 +
1657 +[[package]]
1658 +name = "dirs"
1659 +version = "6.0.0"
1660 +source = "registry+https://github.com/rust-lang/crates.io-index"
1661 +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
1662 +dependencies = [
1663 + "dirs-sys",
1664 +]
1665 +
1666 +[[package]]
1667 +name = "dirs-sys"
1668 +version = "0.5.0"
1669 +source = "registry+https://github.com/rust-lang/crates.io-index"
1670 +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
1671 +dependencies = [
1672 + "libc",
1673 + "option-ext",
1674 + "redox_users",
1675 + "windows-sys 0.61.2",
1676 +]
1677 +
1678 +[[package]]
1679 +name = "dispatch2"
1680 +version = "0.3.1"
1681 +source = "registry+https://github.com/rust-lang/crates.io-index"
1682 +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
1683 +dependencies = [
1684 + "bitflags 2.13.0",
1685 + "block2",
1686 + "libc",
1687 + "objc2",
1688 +]
1689 +
1690 +[[package]]
1691 +name = "displaydoc"
1692 +version = "0.2.6"
1693 +source = "registry+https://github.com/rust-lang/crates.io-index"
1694 +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
1695 +dependencies = [
1696 + "proc-macro2",
1697 + "quote",
1698 + "syn 2.0.118",
1699 +]
1700 +
1701 +[[package]]
1702 +name = "dlopen2"
1703 +version = "0.8.2"
1704 +source = "registry+https://github.com/rust-lang/crates.io-index"
1705 +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4"
1706 +dependencies = [
1707 + "dlopen2_derive",
1708 + "libc",
1709 + "once_cell",
1710 + "winapi",
1711 +]
1712 +
1713 +[[package]]
1714 +name = "dlopen2_derive"
1715 +version = "0.4.3"
1716 +source = "registry+https://github.com/rust-lang/crates.io-index"
1717 +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f"
1718 +dependencies = [
1719 + "proc-macro2",
1720 + "quote",
1721 + "syn 2.0.118",
1722 +]
1723 +
1724 +[[package]]
1725 +name = "doctest-file"
1726 +version = "1.1.1"
1727 +source = "registry+https://github.com/rust-lang/crates.io-index"
1728 +checksum = "c2db04e74f0a9a93103b50e90b96024c9b2bdca8bce6a632ec71b88736d3d359"
1729 +
1730 +[[package]]
1731 +name = "document-features"
1732 +version = "0.2.12"
1733 +source = "registry+https://github.com/rust-lang/crates.io-index"
1734 +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
1735 +dependencies = [
1736 + "litrs",
1737 +]
1738 +
1739 +[[package]]
1740 +name = "dom_query"
1741 +version = "0.27.0"
1742 +source = "registry+https://github.com/rust-lang/crates.io-index"
1743 +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89"
1744 +dependencies = [
1745 + "bit-set 0.8.0",
1746 + "cssparser",
1747 + "foldhash 0.2.0",
1748 + "html5ever 0.38.0",
1749 + "precomputed-hash",
1750 + "selectors 0.36.1",
1751 + "tendril 0.5.1",
1752 +]
1753 +
1754 +[[package]]
1755 +name = "dotenvy"
1756 +version = "0.15.7"
1757 +source = "registry+https://github.com/rust-lang/crates.io-index"
1758 +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
1759 +
1760 +[[package]]
1761 +name = "dpi"
1762 +version = "0.1.2"
1763 +source = "registry+https://github.com/rust-lang/crates.io-index"
1764 +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
1765 +dependencies = [
1766 + "serde",
1767 +]
1768 +
1769 +[[package]]
1770 +name = "dtoa"
1771 +version = "1.0.11"
1772 +source = "registry+https://github.com/rust-lang/crates.io-index"
1773 +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590"
1774 +
1775 +[[package]]
1776 +name = "dtoa-short"
1777 +version = "0.3.5"
1778 +source = "registry+https://github.com/rust-lang/crates.io-index"
1779 +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
1780 +dependencies = [
1781 + "dtoa",
1782 +]
1783 +
1784 +[[package]]
1785 +name = "dtor"
1786 +version = "0.3.0"
1787 +source = "registry+https://github.com/rust-lang/crates.io-index"
1788 +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4"
1789 +dependencies = [
1790 + "dtor-proc-macro",
1791 +]
1792 +
1793 +[[package]]
1794 +name = "dtor-proc-macro"
1795 +version = "0.0.6"
1796 +source = "registry+https://github.com/rust-lang/crates.io-index"
1797 +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5"
1798 +
1799 +[[package]]
1800 +name = "dunce"
1801 +version = "1.0.5"
1802 +source = "registry+https://github.com/rust-lang/crates.io-index"
1803 +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
1804 +
1805 +[[package]]
1806 +name = "dyn-clone"
1807 +version = "1.0.20"
1808 +source = "registry+https://github.com/rust-lang/crates.io-index"
1809 +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
1810 +
1811 +[[package]]
1812 +name = "dyn-stack"
1813 +version = "0.13.2"
1814 +source = "registry+https://github.com/rust-lang/crates.io-index"
1815 +checksum = "1c4713e43e2886ba72b8271aa66c93d722116acf7a75555cce11dcde84388fe8"
1816 +dependencies = [
1817 + "bytemuck",
1818 + "dyn-stack-macros",
1819 +]
1820 +
1821 +[[package]]
1822 +name = "dyn-stack-macros"
1823 +version = "0.1.3"
1824 +source = "registry+https://github.com/rust-lang/crates.io-index"
1825 +checksum = "e1d926b4d407d372f141f93bb444696142c29d32962ccbd3531117cf3aa0bfa9"
1826 +
1827 +[[package]]
1828 +name = "ego-tree"
1829 +version = "0.10.0"
1830 +source = "registry+https://github.com/rust-lang/crates.io-index"
1831 +checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8"
1832 +
1833 +[[package]]
1834 +name = "either"
1835 +version = "1.16.0"
1836 +source = "registry+https://github.com/rust-lang/crates.io-index"
1837 +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
1838 +dependencies = [
1839 + "serde",
1840 +]
1841 +
1842 +[[package]]
1843 +name = "embed-resource"
1844 +version = "3.0.11"
1845 +source = "registry+https://github.com/rust-lang/crates.io-index"
1846 +checksum = "fbfdaacccebec3b28e4866b8973543c7647797db5ada1bdab552e48fe665fbbd"
1847 +dependencies = [
1848 + "cc",
1849 + "memchr",
1850 + "rustc_version",
1851 + "toml 1.1.2+spec-1.1.0",
1852 + "vswhom",
1853 + "winreg",
1854 +]
1855 +
1856 +[[package]]
1857 +name = "embed_plist"
1858 +version = "1.2.2"
1859 +source = "registry+https://github.com/rust-lang/crates.io-index"
1860 +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
1861 +
1862 +[[package]]
1863 +name = "encode_unicode"
1864 +version = "1.0.0"
1865 +source = "registry+https://github.com/rust-lang/crates.io-index"
1866 +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
1867 +
1868 +[[package]]
1869 +name = "encoding_rs"
1870 +version = "0.8.35"
1871 +source = "registry+https://github.com/rust-lang/crates.io-index"
1872 +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
1873 +dependencies = [
1874 + "cfg-if",
1875 +]
1876 +
1877 +[[package]]
1878 +name = "encoding_rs_io"
1879 +version = "0.1.7"
1880 +source = "registry+https://github.com/rust-lang/crates.io-index"
1881 +checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83"
1882 +dependencies = [
1883 + "encoding_rs",
1884 +]
1885 +
1886 +[[package]]
1887 +name = "endi"
1888 +version = "1.1.1"
1889 +source = "registry+https://github.com/rust-lang/crates.io-index"
1890 +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
1891 +
1892 +[[package]]
1893 +name = "endian-type"
1894 +version = "0.2.0"
1895 +source = "registry+https://github.com/rust-lang/crates.io-index"
1896 +checksum = "869b0adbda23651a9c5c0c3d270aac9fcb52e8622a8f2b17e57802d7791962f2"
1897 +
1898 +[[package]]
1899 +name = "enum-as-inner"
1900 +version = "0.6.1"
1901 +source = "registry+https://github.com/rust-lang/crates.io-index"
1902 +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
1903 +dependencies = [
1904 + "heck 0.5.0",
1905 + "proc-macro2",
1906 + "quote",
1907 + "syn 2.0.118",
1908 +]
1909 +
1910 +[[package]]
1911 +name = "enumflags2"
1912 +version = "0.7.12"
1913 +source = "registry+https://github.com/rust-lang/crates.io-index"
1914 +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
1915 +dependencies = [
1916 + "enumflags2_derive",
1917 + "serde",
1918 +]
1919 +
1920 +[[package]]
1921 +name = "enumflags2_derive"
1922 +version = "0.7.12"
1923 +source = "registry+https://github.com/rust-lang/crates.io-index"
1924 +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
1925 +dependencies = [
1926 + "proc-macro2",
1927 + "quote",
1928 + "syn 2.0.118",
1929 +]
1930 +
1931 +[[package]]
1932 +name = "env_filter"
1933 +version = "0.1.4"
1934 +source = "registry+https://github.com/rust-lang/crates.io-index"
1935 +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2"
1936 +dependencies = [
1937 + "log",
1938 + "regex",
1939 +]
1940 +
1941 +[[package]]
1942 +name = "equivalent"
1943 +version = "1.0.2"
1944 +source = "registry+https://github.com/rust-lang/crates.io-index"
1945 +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
1946 +
1947 +[[package]]
1948 +name = "erased-serde"
1949 +version = "0.4.10"
1950 +source = "registry+https://github.com/rust-lang/crates.io-index"
1951 +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec"
1952 +dependencies = [
1953 + "serde",
1954 + "serde_core",
1955 + "typeid",
1956 +]
1957 +
1958 +[[package]]
1959 +name = "errno"
1960 +version = "0.3.14"
1961 +source = "registry+https://github.com/rust-lang/crates.io-index"
1962 +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
1963 +dependencies = [
1964 + "libc",
1965 + "windows-sys 0.61.2",
1966 +]
1967 +
1968 +[[package]]
1969 +name = "esaxx-rs"
1970 +version = "0.1.10"
1971 +source = "registry+https://github.com/rust-lang/crates.io-index"
1972 +checksum = "d817e038c30374a4bcb22f94d0a8a0e216958d4c3dcde369b1439fec4bdda6e6"
1973 +
1974 +[[package]]
1975 +name = "event-listener"
1976 +version = "5.4.1"
1977 +source = "registry+https://github.com/rust-lang/crates.io-index"
1978 +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
1979 +dependencies = [
1980 + "concurrent-queue",
1981 + "parking",
1982 + "pin-project-lite",
1983 +]
1984 +
1985 +[[package]]
1986 +name = "event-listener-strategy"
1987 +version = "0.5.4"
1988 +source = "registry+https://github.com/rust-lang/crates.io-index"
1989 +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
1990 +dependencies = [
1991 + "event-listener",
1992 + "pin-project-lite",
1993 +]
1994 +
1995 +[[package]]
1996 +name = "exr"
1997 +version = "1.74.1"
1998 +source = "registry+https://github.com/rust-lang/crates.io-index"
1999 +checksum = "6be87932f10230a4339ab394edd8e4611fcb72553d8295b4d52ea55249b3daa5"
2000 +dependencies = [
2001 + "bit_field",
2002 + "half",
2003 + "lebe",
2004 + "miniz_oxide",
2005 + "num-complex",
2006 + "pulp 0.22.3",
2007 + "smallvec 1.15.2",
2008 + "zune-inflate",
2009 +]
2010 +
2011 +[[package]]
2012 +name = "extended"
2013 +version = "0.1.0"
2014 +source = "registry+https://github.com/rust-lang/crates.io-index"
2015 +checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365"
2016 +
2017 +[[package]]
2018 +name = "fancy-regex"
2019 +version = "0.13.0"
2020 +source = "registry+https://github.com/rust-lang/crates.io-index"
2021 +checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2"
2022 +dependencies = [
2023 + "bit-set 0.5.3",
2024 + "regex-automata",
2025 + "regex-syntax",
2026 +]
2027 +
2028 +[[package]]
2029 +name = "fancy-regex"
2030 +version = "0.14.0"
2031 +source = "registry+https://github.com/rust-lang/crates.io-index"
2032 +checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
2033 +dependencies = [
2034 + "bit-set 0.8.0",
2035 + "regex-automata",
2036 + "regex-syntax",
2037 +]
2038 +
2039 +[[package]]
2040 +name = "fastrand"
2041 +version = "2.4.1"
2042 +source = "registry+https://github.com/rust-lang/crates.io-index"
2043 +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
2044 +
2045 +[[package]]
2046 +name = "fax"
2047 +version = "0.2.7"
2048 +source = "registry+https://github.com/rust-lang/crates.io-index"
2049 +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a"
2050 +
2051 +[[package]]
2052 +name = "fdeflate"
2053 +version = "0.3.7"
2054 +source = "registry+https://github.com/rust-lang/crates.io-index"
2055 +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
2056 +dependencies = [
2057 + "simd-adler32",
2058 +]
2059 +
2060 +[[package]]
2061 +name = "fern"
2062 +version = "0.7.1"
2063 +source = "registry+https://github.com/rust-lang/crates.io-index"
2064 +checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29"
2065 +dependencies = [
2066 + "log",
2067 +]
2068 +
2069 +[[package]]
2070 +name = "field-offset"
2071 +version = "0.3.6"
2072 +source = "registry+https://github.com/rust-lang/crates.io-index"
2073 +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
2074 +dependencies = [
2075 + "memoffset",
2076 + "rustc_version",
2077 +]
2078 +
2079 +[[package]]
2080 +name = "find-msvc-tools"
2081 +version = "0.1.9"
2082 +source = "registry+https://github.com/rust-lang/crates.io-index"
2083 +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
2084 +
2085 +[[package]]
2086 +name = "flate2"
2087 +version = "1.1.9"
2088 +source = "registry+https://github.com/rust-lang/crates.io-index"
2089 +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
2090 +dependencies = [
2091 + "crc32fast",
2092 + "miniz_oxide",
2093 +]
2094 +
2095 +[[package]]
2096 +name = "float8"
2097 +version = "0.7.0"
2098 +source = "registry+https://github.com/rust-lang/crates.io-index"
2099 +checksum = "c2d1f04709a8ac06e8e8042875a3c466cc4832d3c1a18dbcb9dba3c6e83046bc"
2100 +dependencies = [
2101 + "half",
2102 + "num-traits",
2103 + "rand 0.9.4",
2104 + "rand_distr 0.5.1",
2105 +]
2106 +
2107 +[[package]]
2108 +name = "fnv"
2109 +version = "1.0.7"
2110 +source = "registry+https://github.com/rust-lang/crates.io-index"
2111 +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
2112 +
2113 +[[package]]
2114 +name = "foldhash"
2115 +version = "0.1.5"
2116 +source = "registry+https://github.com/rust-lang/crates.io-index"
2117 +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
2118 +
2119 +[[package]]
2120 +name = "foldhash"
2121 +version = "0.2.0"
2122 +source = "registry+https://github.com/rust-lang/crates.io-index"
2123 +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
2124 +
2125 +[[package]]
2126 +name = "foreign-types"
2127 +version = "0.5.0"
2128 +source = "registry+https://github.com/rust-lang/crates.io-index"
2129 +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
2130 +dependencies = [
2131 + "foreign-types-macros",
2132 + "foreign-types-shared",
2133 +]
2134 +
2135 +[[package]]
2136 +name = "foreign-types-macros"
2137 +version = "0.2.3"
2138 +source = "registry+https://github.com/rust-lang/crates.io-index"
2139 +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
2140 +dependencies = [
2141 + "proc-macro2",
2142 + "quote",
2143 + "syn 2.0.118",
2144 +]
2145 +
2146 +[[package]]
2147 +name = "foreign-types-shared"
2148 +version = "0.3.1"
2149 +source = "registry+https://github.com/rust-lang/crates.io-index"
2150 +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
2151 +
2152 +[[package]]
2153 +name = "form_urlencoded"
2154 +version = "1.2.2"
2155 +source = "registry+https://github.com/rust-lang/crates.io-index"
2156 +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
2157 +dependencies = [
2158 + "percent-encoding",
2159 +]
2160 +
2161 +[[package]]
2162 +name = "fs-err"
2163 +version = "2.11.0"
2164 +source = "registry+https://github.com/rust-lang/crates.io-index"
2165 +checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
2166 +dependencies = [
2167 + "autocfg",
2168 +]
2169 +
2170 +[[package]]
2171 +name = "fs_extra"
2172 +version = "1.3.0"
2173 +source = "registry+https://github.com/rust-lang/crates.io-index"
2174 +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
2175 +
2176 +[[package]]
2177 +name = "funty"
2178 +version = "2.0.0"
2179 +source = "registry+https://github.com/rust-lang/crates.io-index"
2180 +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
2181 +
2182 +[[package]]
2183 +name = "futf"
2184 +version = "0.1.5"
2185 +source = "registry+https://github.com/rust-lang/crates.io-index"
2186 +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
2187 +dependencies = [
2188 + "mac",
2189 + "new_debug_unreachable",
2190 +]
2191 +
2192 +[[package]]
2193 +name = "futures"
2194 +version = "0.3.32"
2195 +source = "registry+https://github.com/rust-lang/crates.io-index"
2196 +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
2197 +dependencies = [
2198 + "futures-channel",
2199 + "futures-core",
2200 + "futures-executor",
2201 + "futures-io",
2202 + "futures-sink",
2203 + "futures-task",
2204 + "futures-util",
2205 +]
2206 +
2207 +[[package]]
2208 +name = "futures-channel"
2209 +version = "0.3.32"
2210 +source = "registry+https://github.com/rust-lang/crates.io-index"
2211 +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
2212 +dependencies = [
2213 + "futures-core",
2214 + "futures-sink",
2215 +]
2216 +
2217 +[[package]]
2218 +name = "futures-core"
2219 +version = "0.3.32"
2220 +source = "registry+https://github.com/rust-lang/crates.io-index"
2221 +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
2222 +
2223 +[[package]]
2224 +name = "futures-executor"
2225 +version = "0.3.32"
2226 +source = "registry+https://github.com/rust-lang/crates.io-index"
2227 +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
2228 +dependencies = [
2229 + "futures-core",
2230 + "futures-task",
2231 + "futures-util",
2232 +]
2233 +
2234 +[[package]]
2235 +name = "futures-io"
2236 +version = "0.3.32"
2237 +source = "registry+https://github.com/rust-lang/crates.io-index"
2238 +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
2239 +
2240 +[[package]]
2241 +name = "futures-lite"
2242 +version = "2.6.1"
2243 +source = "registry+https://github.com/rust-lang/crates.io-index"
2244 +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
2245 +dependencies = [
2246 + "fastrand",
2247 + "futures-core",
2248 + "futures-io",
2249 + "parking",
2250 + "pin-project-lite",
2251 +]
2252 +
2253 +[[package]]
2254 +name = "futures-macro"
2255 +version = "0.3.32"
2256 +source = "registry+https://github.com/rust-lang/crates.io-index"
2257 +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
2258 +dependencies = [
2259 + "proc-macro2",
2260 + "quote",
2261 + "syn 2.0.118",
2262 +]
2263 +
2264 +[[package]]
2265 +name = "futures-sink"
2266 +version = "0.3.32"
2267 +source = "registry+https://github.com/rust-lang/crates.io-index"
2268 +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
2269 +
2270 +[[package]]
2271 +name = "futures-task"
2272 +version = "0.3.32"
2273 +source = "registry+https://github.com/rust-lang/crates.io-index"
2274 +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
2275 +
2276 +[[package]]
2277 +name = "futures-util"
2278 +version = "0.3.32"
2279 +source = "registry+https://github.com/rust-lang/crates.io-index"
2280 +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
2281 +dependencies = [
2282 + "futures-channel",
2283 + "futures-core",
2284 + "futures-io",
2285 + "futures-macro",
2286 + "futures-sink",
2287 + "futures-task",
2288 + "memchr",
2289 + "pin-project-lite",
2290 + "slab",
2291 +]
2292 +
2293 +[[package]]
2294 +name = "fxhash"
2295 +version = "0.2.1"
2296 +source = "registry+https://github.com/rust-lang/crates.io-index"
2297 +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
2298 +dependencies = [
2299 + "byteorder",
2300 +]
2301 +
2302 +[[package]]
2303 +name = "galil-seiferas"
2304 +version = "0.1.5"
2305 +source = "registry+https://github.com/rust-lang/crates.io-index"
2306 +checksum = "794ac25cfda3fa11d2b07ff8c65889c6c03411646df54e59e606878d899e1d5a"
2307 +dependencies = [
2308 + "defmac",
2309 + "unchecked-index",
2310 +]
2311 +
2312 +[[package]]
2313 +name = "gdk"
2314 +version = "0.18.2"
2315 +source = "registry+https://github.com/rust-lang/crates.io-index"
2316 +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691"
2317 +dependencies = [
2318 + "cairo-rs",
2319 + "gdk-pixbuf",
2320 + "gdk-sys",
2321 + "gio",
2322 + "glib",
2323 + "libc",
2324 + "pango",
2325 +]
2326 +
2327 +[[package]]
2328 +name = "gdk-pixbuf"
2329 +version = "0.18.5"
2330 +source = "registry+https://github.com/rust-lang/crates.io-index"
2331 +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec"
2332 +dependencies = [
2333 + "gdk-pixbuf-sys",
2334 + "gio",
2335 + "glib",
2336 + "libc",
2337 + "once_cell",
2338 +]
2339 +
2340 +[[package]]
2341 +name = "gdk-pixbuf-sys"
2342 +version = "0.18.0"
2343 +source = "registry+https://github.com/rust-lang/crates.io-index"
2344 +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7"
2345 +dependencies = [
2346 + "gio-sys",
2347 + "glib-sys",
2348 + "gobject-sys",
2349 + "libc",
2350 + "system-deps",
2351 +]
2352 +
2353 +[[package]]
2354 +name = "gdk-sys"
2355 +version = "0.18.2"
2356 +source = "registry+https://github.com/rust-lang/crates.io-index"
2357 +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7"
2358 +dependencies = [
2359 + "cairo-sys-rs",
2360 + "gdk-pixbuf-sys",
2361 + "gio-sys",
2362 + "glib-sys",
2363 + "gobject-sys",
2364 + "libc",
2365 + "pango-sys",
2366 + "pkg-config",
2367 + "system-deps",
2368 +]
2369 +
2370 +[[package]]
2371 +name = "gdkwayland-sys"
2372 +version = "0.18.2"
2373 +source = "registry+https://github.com/rust-lang/crates.io-index"
2374 +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69"
2375 +dependencies = [
2376 + "gdk-sys",
2377 + "glib-sys",
2378 + "gobject-sys",
2379 + "libc",
2380 + "pkg-config",
2381 + "system-deps",
2382 +]
2383 +
2384 +[[package]]
2385 +name = "gdkx11"
2386 +version = "0.18.2"
2387 +source = "registry+https://github.com/rust-lang/crates.io-index"
2388 +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe"
2389 +dependencies = [
2390 + "gdk",
2391 + "gdkx11-sys",
2392 + "gio",
2393 + "glib",
2394 + "libc",
2395 + "x11",
2396 +]
2397 +
2398 +[[package]]
2399 +name = "gdkx11-sys"
2400 +version = "0.18.2"
2401 +source = "registry+https://github.com/rust-lang/crates.io-index"
2402 +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d"
2403 +dependencies = [
2404 + "gdk-sys",
2405 + "glib-sys",
2406 + "libc",
2407 + "system-deps",
2408 + "x11",
2409 +]
2410 +
2411 +[[package]]
2412 +name = "gemm"
2413 +version = "0.18.2"
2414 +source = "registry+https://github.com/rust-lang/crates.io-index"
2415 +checksum = "ab96b703d31950f1aeddded248bc95543c9efc7ac9c4a21fda8703a83ee35451"
2416 +dependencies = [
2417 + "dyn-stack",
2418 + "gemm-c32 0.18.2",
2419 + "gemm-c64 0.18.2",
2420 + "gemm-common 0.18.2",
2421 + "gemm-f16 0.18.2",
2422 + "gemm-f32 0.18.2",
2423 + "gemm-f64 0.18.2",
2424 + "num-complex",
2425 + "num-traits",
2426 + "paste",
2427 + "raw-cpuid",
2428 + "seq-macro",
2429 +]
2430 +
2431 +[[package]]
2432 +name = "gemm"
2433 +version = "0.19.0"
2434 +source = "registry+https://github.com/rust-lang/crates.io-index"
2435 +checksum = "aa0673db364b12263d103b68337a68fbecc541d6f6b61ba72fe438654709eacb"
2436 +dependencies = [
2437 + "dyn-stack",
2438 + "gemm-c32 0.19.0",
2439 + "gemm-c64 0.19.0",
2440 + "gemm-common 0.19.0",
2441 + "gemm-f16 0.19.0",
2442 + "gemm-f32 0.19.0",
2443 + "gemm-f64 0.19.0",
2444 + "num-complex",
2445 + "num-traits",
2446 + "paste",
2447 + "raw-cpuid",
2448 + "seq-macro",
2449 +]
2450 +
2451 +[[package]]
2452 +name = "gemm-c32"
2453 +version = "0.18.2"
2454 +source = "registry+https://github.com/rust-lang/crates.io-index"
2455 +checksum = "f6db9fd9f40421d00eea9dd0770045a5603b8d684654816637732463f4073847"
2456 +dependencies = [
2457 + "dyn-stack",
2458 + "gemm-common 0.18.2",
2459 + "num-complex",
2460 + "num-traits",
2461 + "paste",
2462 + "raw-cpuid",
2463 + "seq-macro",
2464 +]
2465 +
2466 +[[package]]
2467 +name = "gemm-c32"
2468 +version = "0.19.0"
2469 +source = "registry+https://github.com/rust-lang/crates.io-index"
2470 +checksum = "086936dbdcb99e37aad81d320f98f670e53c1e55a98bee70573e83f95beb128c"
2471 +dependencies = [
2472 + "dyn-stack",
2473 + "gemm-common 0.19.0",
2474 + "num-complex",
2475 + "num-traits",
2476 + "paste",
2477 + "raw-cpuid",
2478 + "seq-macro",
2479 +]
2480 +
2481 +[[package]]
2482 +name = "gemm-c64"
2483 +version = "0.18.2"
2484 +source = "registry+https://github.com/rust-lang/crates.io-index"
2485 +checksum = "dfcad8a3d35a43758330b635d02edad980c1e143dc2f21e6fd25f9e4eada8edf"
2486 +dependencies = [
2487 + "dyn-stack",
2488 + "gemm-common 0.18.2",
2489 + "num-complex",
2490 + "num-traits",
2491 + "paste",
2492 + "raw-cpuid",
2493 + "seq-macro",
2494 +]
2495 +
2496 +[[package]]
2497 +name = "gemm-c64"
2498 +version = "0.19.0"
2499 +source = "registry+https://github.com/rust-lang/crates.io-index"
2500 +checksum = "20c8aeeeec425959bda4d9827664029ba1501a90a0d1e6228e48bef741db3a3f"
2501 +dependencies = [
2502 + "dyn-stack",
2503 + "gemm-common 0.19.0",
2504 + "num-complex",
2505 + "num-traits",
2506 + "paste",
2507 + "raw-cpuid",
2508 + "seq-macro",
2509 +]
2510 +
2511 +[[package]]
2512 +name = "gemm-common"
2513 +version = "0.18.2"
2514 +source = "registry+https://github.com/rust-lang/crates.io-index"
2515 +checksum = "a352d4a69cbe938b9e2a9cb7a3a63b7e72f9349174a2752a558a8a563510d0f3"
2516 +dependencies = [
2517 + "bytemuck",
2518 + "dyn-stack",
2519 + "half",
2520 + "libm",
2521 + "num-complex",
2522 + "num-traits",
2523 + "once_cell",
2524 + "paste",
2525 + "pulp 0.21.5",
2526 + "raw-cpuid",
2527 + "rayon",
2528 + "seq-macro",
2529 + "sysctl",
2530 +]
2531 +
2532 +[[package]]
2533 +name = "gemm-common"
2534 +version = "0.19.0"
2535 +source = "registry+https://github.com/rust-lang/crates.io-index"
2536 +checksum = "88027625910cc9b1085aaaa1c4bc46bb3a36aad323452b33c25b5e4e7c8e2a3e"
2537 +dependencies = [
2538 + "bytemuck",
2539 + "dyn-stack",
2540 + "half",
2541 + "libm",
2542 + "num-complex",
2543 + "num-traits",
2544 + "once_cell",
2545 + "paste",
2546 + "pulp 0.22.3",
2547 + "raw-cpuid",
2548 + "rayon",
2549 + "seq-macro",
2550 + "sysctl",
2551 +]
2552 +
2553 +[[package]]
2554 +name = "gemm-f16"
2555 +version = "0.18.2"
2556 +source = "registry+https://github.com/rust-lang/crates.io-index"
2557 +checksum = "cff95ae3259432f3c3410eaa919033cd03791d81cebd18018393dc147952e109"
2558 +dependencies = [
2559 + "dyn-stack",
2560 + "gemm-common 0.18.2",
2561 + "gemm-f32 0.18.2",
2562 + "half",
2563 + "num-complex",
2564 + "num-traits",
2565 + "paste",
2566 + "raw-cpuid",
2567 + "rayon",
2568 + "seq-macro",
2569 +]
2570 +
2571 +[[package]]
2572 +name = "gemm-f16"
2573 +version = "0.19.0"
2574 +source = "registry+https://github.com/rust-lang/crates.io-index"
2575 +checksum = "e3df7a55202e6cd6739d82ae3399c8e0c7e1402859b30e4cb780e61525d9486e"
2576 +dependencies = [
2577 + "dyn-stack",
2578 + "gemm-common 0.19.0",
2579 + "gemm-f32 0.19.0",
2580 + "half",
2581 + "num-complex",
2582 + "num-traits",
2583 + "paste",
2584 + "raw-cpuid",
2585 + "rayon",
2586 + "seq-macro",
2587 +]
2588 +
2589 +[[package]]
2590 +name = "gemm-f32"
2591 +version = "0.18.2"
2592 +source = "registry+https://github.com/rust-lang/crates.io-index"
2593 +checksum = "bc8d3d4385393304f407392f754cd2dc4b315d05063f62cf09f47b58de276864"
2594 +dependencies = [
2595 + "dyn-stack",
2596 + "gemm-common 0.18.2",
2597 + "num-complex",
2598 + "num-traits",
2599 + "paste",
2600 + "raw-cpuid",
2601 + "seq-macro",
2602 +]
2603 +
2604 +[[package]]
2605 +name = "gemm-f32"
2606 +version = "0.19.0"
2607 +source = "registry+https://github.com/rust-lang/crates.io-index"
2608 +checksum = "02e0b8c9da1fbec6e3e3ab2ce6bc259ef18eb5f6f0d3e4edf54b75f9fd41a81c"
2609 +dependencies = [
2610 + "dyn-stack",
2611 + "gemm-common 0.19.0",
2612 + "num-complex",
2613 + "num-traits",
2614 + "paste",
2615 + "raw-cpuid",
2616 + "seq-macro",
2617 +]
2618 +
2619 +[[package]]
2620 +name = "gemm-f64"
2621 +version = "0.18.2"
2622 +source = "registry+https://github.com/rust-lang/crates.io-index"
2623 +checksum = "35b2a4f76ce4b8b16eadc11ccf2e083252d8237c1b589558a49b0183545015bd"
2624 +dependencies = [
2625 + "dyn-stack",
2626 + "gemm-common 0.18.2",
2627 + "num-complex",
2628 + "num-traits",
2629 + "paste",
2630 + "raw-cpuid",
2631 + "seq-macro",
2632 +]
2633 +
2634 +[[package]]
2635 +name = "gemm-f64"
2636 +version = "0.19.0"
2637 +source = "registry+https://github.com/rust-lang/crates.io-index"
2638 +checksum = "056131e8f2a521bfab322f804ccd652520c79700d81209e9d9275bbdecaadc6a"
2639 +dependencies = [
2640 + "dyn-stack",
2641 + "gemm-common 0.19.0",
2642 + "num-complex",
2643 + "num-traits",
2644 + "paste",
2645 + "raw-cpuid",
2646 + "seq-macro",
2647 +]
2648 +
2649 +[[package]]
2650 +name = "generator"
2651 +version = "0.7.5"
2652 +source = "registry+https://github.com/rust-lang/crates.io-index"
2653 +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
2654 +dependencies = [
2655 + "cc",
2656 + "libc",
2657 + "log",
2658 + "rustversion",
2659 + "windows 0.48.0",
2660 +]
2661 +
2662 +[[package]]
2663 +name = "generic-array"
2664 +version = "0.14.7"
2665 +source = "registry+https://github.com/rust-lang/crates.io-index"
2666 +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
2667 +dependencies = [
2668 + "typenum",
2669 + "version_check",
2670 +]
2671 +
2672 +[[package]]
2673 +name = "getopts"
2674 +version = "0.2.24"
2675 +source = "registry+https://github.com/rust-lang/crates.io-index"
2676 +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
2677 +dependencies = [
2678 + "unicode-width 0.2.2",
2679 +]
2680 +
2681 +[[package]]
2682 +name = "getrandom"
2683 +version = "0.2.17"
2684 +source = "registry+https://github.com/rust-lang/crates.io-index"
2685 +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
2686 +dependencies = [
2687 + "cfg-if",
2688 + "js-sys",
2689 + "libc",
2690 + "wasi",
2691 + "wasm-bindgen",
2692 +]
2693 +
2694 +[[package]]
2695 +name = "getrandom"
2696 +version = "0.3.4"
2697 +source = "registry+https://github.com/rust-lang/crates.io-index"
2698 +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
2699 +dependencies = [
2700 + "cfg-if",
2701 + "js-sys",
2702 + "libc",
2703 + "r-efi 5.3.0",
2704 + "wasip2",
2705 + "wasm-bindgen",
2706 +]
2707 +
2708 +[[package]]
2709 +name = "getrandom"
2710 +version = "0.4.3"
2711 +source = "registry+https://github.com/rust-lang/crates.io-index"
2712 +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
2713 +dependencies = [
2714 + "cfg-if",
2715 + "js-sys",
2716 + "libc",
2717 + "r-efi 6.0.0",
2718 + "rand_core 0.10.1",
2719 + "wasm-bindgen",
2720 +]
2721 +
2722 +[[package]]
2723 +name = "gif"
2724 +version = "0.14.2"
2725 +source = "registry+https://github.com/rust-lang/crates.io-index"
2726 +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159"
2727 +dependencies = [
2728 + "color_quant",
2729 + "weezl",
2730 +]
2731 +
2732 +[[package]]
2733 +name = "gio"
2734 +version = "0.18.4"
2735 +source = "registry+https://github.com/rust-lang/crates.io-index"
2736 +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73"
2737 +dependencies = [
2738 + "futures-channel",
2739 + "futures-core",
2740 + "futures-io",
2741 + "futures-util",
2742 + "gio-sys",
2743 + "glib",
2744 + "libc",
2745 + "once_cell",
2746 + "pin-project-lite",
2747 + "smallvec 1.15.2",
2748 + "thiserror 1.0.69",
2749 +]
2750 +
2751 +[[package]]
2752 +name = "gio-sys"
2753 +version = "0.18.1"
2754 +source = "registry+https://github.com/rust-lang/crates.io-index"
2755 +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2"
2756 +dependencies = [
2757 + "glib-sys",
2758 + "gobject-sys",
2759 + "libc",
2760 + "system-deps",
2761 + "winapi",
2762 +]
2763 +
2764 +[[package]]
2765 +name = "glib"
2766 +version = "0.18.5"
2767 +source = "registry+https://github.com/rust-lang/crates.io-index"
2768 +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5"
2769 +dependencies = [
2770 + "bitflags 2.13.0",
2771 + "futures-channel",
2772 + "futures-core",
2773 + "futures-executor",
2774 + "futures-task",
2775 + "futures-util",
2776 + "gio-sys",
2777 + "glib-macros",
2778 + "glib-sys",
2779 + "gobject-sys",
2780 + "libc",
2781 + "memchr",
2782 + "once_cell",
2783 + "smallvec 1.15.2",
2784 + "thiserror 1.0.69",
2785 +]
2786 +
2787 +[[package]]
2788 +name = "glib-macros"
2789 +version = "0.18.5"
2790 +source = "registry+https://github.com/rust-lang/crates.io-index"
2791 +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc"
2792 +dependencies = [
2793 + "heck 0.4.1",
2794 + "proc-macro-crate 2.0.2",
2795 + "proc-macro-error",
2796 + "proc-macro2",
2797 + "quote",
2798 + "syn 2.0.118",
2799 +]
2800 +
2801 +[[package]]
2802 +name = "glib-sys"
2803 +version = "0.18.1"
2804 +source = "registry+https://github.com/rust-lang/crates.io-index"
2805 +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898"
2806 +dependencies = [
2807 + "libc",
2808 + "system-deps",
2809 +]
2810 +
2811 +[[package]]
2812 +name = "glob"
2813 +version = "0.3.3"
2814 +source = "registry+https://github.com/rust-lang/crates.io-index"
2815 +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
2816 +
2817 +[[package]]
2818 +name = "gobject-sys"
2819 +version = "0.18.0"
2820 +source = "registry+https://github.com/rust-lang/crates.io-index"
2821 +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44"
2822 +dependencies = [
2823 + "glib-sys",
2824 + "libc",
2825 + "system-deps",
2826 +]
2827 +
2828 +[[package]]
2829 +name = "goblin"
2830 +version = "0.8.2"
2831 +source = "registry+https://github.com/rust-lang/crates.io-index"
2832 +checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47"
2833 +dependencies = [
2834 + "log",
2835 + "plain",
2836 + "scroll",
2837 +]
2838 +
2839 +[[package]]
2840 +name = "gtk"
2841 +version = "0.18.2"
2842 +source = "registry+https://github.com/rust-lang/crates.io-index"
2843 +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a"
2844 +dependencies = [
2845 + "atk",
2846 + "cairo-rs",
2847 + "field-offset",
2848 + "futures-channel",
2849 + "gdk",
2850 + "gdk-pixbuf",
2851 + "gio",
2852 + "glib",
2853 + "gtk-sys",
2854 + "gtk3-macros",
2855 + "libc",
2856 + "pango",
2857 + "pkg-config",
2858 +]
2859 +
2860 +[[package]]
2861 +name = "gtk-sys"
2862 +version = "0.18.2"
2863 +source = "registry+https://github.com/rust-lang/crates.io-index"
2864 +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414"
2865 +dependencies = [
2866 + "atk-sys",
2867 + "cairo-sys-rs",
2868 + "gdk-pixbuf-sys",
2869 + "gdk-sys",
2870 + "gio-sys",
2871 + "glib-sys",
2872 + "gobject-sys",
2873 + "libc",
2874 + "pango-sys",
2875 + "system-deps",
2876 +]
2877 +
2878 +[[package]]
2879 +name = "gtk3-macros"
2880 +version = "0.18.2"
2881 +source = "registry+https://github.com/rust-lang/crates.io-index"
2882 +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d"
2883 +dependencies = [
2884 + "proc-macro-crate 1.3.1",
2885 + "proc-macro-error",
2886 + "proc-macro2",
2887 + "quote",
2888 + "syn 2.0.118",
2889 +]
2890 +
2891 +[[package]]
2892 +name = "h2"
2893 +version = "0.4.15"
2894 +source = "registry+https://github.com/rust-lang/crates.io-index"
2895 +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155"
2896 +dependencies = [
2897 + "atomic-waker",
2898 + "bytes",
2899 + "fnv",
2900 + "futures-core",
2901 + "futures-sink",
2902 + "http",
2903 + "indexmap 2.14.0",
2904 + "slab",
2905 + "tokio",
2906 + "tokio-util",
2907 + "tracing",
2908 +]
2909 +
2910 +[[package]]
2911 +name = "half"
2912 +version = "2.7.1"
2913 +source = "registry+https://github.com/rust-lang/crates.io-index"
2914 +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
2915 +dependencies = [
2916 + "bytemuck",
2917 + "cfg-if",
2918 + "crunchy",
2919 + "num-traits",
2920 + "rand 0.9.4",
2921 + "rand_distr 0.5.1",
2922 + "zerocopy",
2923 +]
2924 +
2925 +[[package]]
2926 +name = "hashbrown"
2927 +version = "0.12.3"
2928 +source = "registry+https://github.com/rust-lang/crates.io-index"
2929 +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
2930 +dependencies = [
2931 + "ahash 0.7.8",
2932 +]
2933 +
2934 +[[package]]
2935 +name = "hashbrown"
2936 +version = "0.15.5"
2937 +source = "registry+https://github.com/rust-lang/crates.io-index"
2938 +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
2939 +dependencies = [
2940 + "allocator-api2",
2941 + "equivalent",
2942 + "foldhash 0.1.5",
2943 +]
2944 +
2945 +[[package]]
2946 +name = "hashbrown"
2947 +version = "0.16.1"
2948 +source = "registry+https://github.com/rust-lang/crates.io-index"
2949 +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
2950 +dependencies = [
2951 + "allocator-api2",
2952 + "equivalent",
2953 + "foldhash 0.2.0",
2954 + "serde",
2955 + "serde_core",
2956 +]
2957 +
2958 +[[package]]
2959 +name = "hashbrown"
2960 +version = "0.17.1"
2961 +source = "registry+https://github.com/rust-lang/crates.io-index"
2962 +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
2963 +
2964 +[[package]]
2965 +name = "heck"
2966 +version = "0.3.3"
2967 +source = "registry+https://github.com/rust-lang/crates.io-index"
2968 +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
2969 +dependencies = [
2970 + "unicode-segmentation",
2971 +]
2972 +
2973 +[[package]]
2974 +name = "heck"
2975 +version = "0.4.1"
2976 +source = "registry+https://github.com/rust-lang/crates.io-index"
2977 +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
2978 +
2979 +[[package]]
2980 +name = "heck"
2981 +version = "0.5.0"
2982 +source = "registry+https://github.com/rust-lang/crates.io-index"
2983 +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
2984 +
2985 +[[package]]
2986 +name = "hermit-abi"
2987 +version = "0.1.19"
2988 +source = "registry+https://github.com/rust-lang/crates.io-index"
2989 +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
2990 +dependencies = [
2991 + "libc",
2992 +]
2993 +
2994 +[[package]]
2995 +name = "hermit-abi"
2996 +version = "0.5.2"
2997 +source = "registry+https://github.com/rust-lang/crates.io-index"
2998 +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
2999 +
3000 +[[package]]
3001 +name = "hex"
3002 +version = "0.4.3"
3003 +source = "registry+https://github.com/rust-lang/crates.io-index"
3004 +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
3005 +
3006 +[[package]]
3007 +name = "hf-hub"
3008 +version = "0.4.3"
3009 +source = "registry+https://github.com/rust-lang/crates.io-index"
3010 +checksum = "629d8f3bbeda9d148036d6b0de0a3ab947abd08ce90626327fc3547a49d59d97"
3011 +dependencies = [
3012 + "dirs",
3013 + "futures",
3014 + "http",
3015 + "indicatif 0.17.11",
3016 + "libc",
3017 + "log",
3018 + "num_cpus",
3019 + "rand 0.9.4",
3020 + "reqwest 0.12.28",
3021 + "serde",
3022 + "serde_json",
3023 + "thiserror 2.0.18",
3024 + "tokio",
3025 + "ureq",
3026 + "windows-sys 0.60.2",
3027 +]
3028 +
3029 +[[package]]
3030 +name = "home"
3031 +version = "0.5.12"
3032 +source = "registry+https://github.com/rust-lang/crates.io-index"
3033 +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
3034 +dependencies = [
3035 + "windows-sys 0.61.2",
3036 +]
3037 +
3038 +[[package]]
3039 +name = "hound"
3040 +version = "3.5.1"
3041 +source = "registry+https://github.com/rust-lang/crates.io-index"
3042 +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f"
3043 +
3044 +[[package]]
3045 +name = "html2text"
3046 +version = "0.16.7"
3047 +source = "registry+https://github.com/rust-lang/crates.io-index"
3048 +checksum = "12d23156ea4dbe6b37ad48fab2da56ff27b0f6192fb5db210c44eb07bfe6e787"
3049 +dependencies = [
3050 + "html5ever 0.38.0",
3051 + "tendril 0.5.1",
3052 + "thiserror 2.0.18",
3053 + "unicode-width 0.2.2",
3054 +]
3055 +
3056 +[[package]]
3057 +name = "html5ever"
3058 +version = "0.36.1"
3059 +source = "registry+https://github.com/rust-lang/crates.io-index"
3060 +checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e"
3061 +dependencies = [
3062 + "log",
3063 + "markup5ever 0.36.1",
3064 +]
3065 +
3066 +[[package]]
3067 +name = "html5ever"
3068 +version = "0.38.0"
3069 +source = "registry+https://github.com/rust-lang/crates.io-index"
3070 +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2"
3071 +dependencies = [
3072 + "log",
3073 + "markup5ever 0.38.0",
3074 +]
3075 +
3076 +[[package]]
3077 +name = "http"
3078 +version = "1.4.2"
3079 +source = "registry+https://github.com/rust-lang/crates.io-index"
3080 +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
3081 +dependencies = [
3082 + "bytes",
3083 + "itoa",
3084 +]
3085 +
3086 +[[package]]
3087 +name = "http-body"
3088 +version = "1.0.1"
3089 +source = "registry+https://github.com/rust-lang/crates.io-index"
3090 +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
3091 +dependencies = [
3092 + "bytes",
3093 + "http",
3094 +]
3095 +
3096 +[[package]]
3097 +name = "http-body-util"
3098 +version = "0.1.3"
3099 +source = "registry+https://github.com/rust-lang/crates.io-index"
3100 +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
3101 +dependencies = [
3102 + "bytes",
3103 + "futures-core",
3104 + "http",
3105 + "http-body",
3106 + "pin-project-lite",
3107 +]
3108 +
3109 +[[package]]
3110 +name = "httparse"
3111 +version = "1.10.1"
3112 +source = "registry+https://github.com/rust-lang/crates.io-index"
3113 +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
3114 +
3115 +[[package]]
3116 +name = "hyper"
3117 +version = "1.10.1"
3118 +source = "registry+https://github.com/rust-lang/crates.io-index"
3119 +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
3120 +dependencies = [
3121 + "atomic-waker",
3122 + "bytes",
3123 + "futures-channel",
3124 + "futures-core",
3125 + "h2",
3126 + "http",
3127 + "http-body",
3128 + "httparse",
3129 + "itoa",
3130 + "pin-project-lite",
3131 + "smallvec 1.15.2",
3132 + "tokio",
3133 + "want",
3134 +]
3135 +
3136 +[[package]]
3137 +name = "hyper-rustls"
3138 +version = "0.27.9"
3139 +source = "registry+https://github.com/rust-lang/crates.io-index"
3140 +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
3141 +dependencies = [
3142 + "http",
3143 + "hyper",
3144 + "hyper-util",
3145 + "rustls",
3146 + "rustls-native-certs",
3147 + "tokio",
3148 + "tokio-rustls",
3149 + "tower-service",
3150 + "webpki-roots 1.0.8",
3151 +]
3152 +
3153 +[[package]]
3154 +name = "hyper-util"
3155 +version = "0.1.20"
3156 +source = "registry+https://github.com/rust-lang/crates.io-index"
3157 +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
3158 +dependencies = [
3159 + "base64 0.22.1",
3160 + "bytes",
3161 + "futures-channel",
3162 + "futures-util",
3163 + "http",
3164 + "http-body",
3165 + "hyper",
3166 + "ipnet",
3167 + "libc",
3168 + "percent-encoding",
3169 + "pin-project-lite",
3170 + "socket2",
3171 + "system-configuration",
3172 + "tokio",
3173 + "tower-service",
3174 + "tracing",
3175 + "windows-registry",
3176 +]
3177 +
3178 +[[package]]
3179 +name = "iana-time-zone"
3180 +version = "0.1.65"
3181 +source = "registry+https://github.com/rust-lang/crates.io-index"
3182 +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
3183 +dependencies = [
3184 + "android_system_properties",
3185 + "core-foundation-sys",
3186 + "iana-time-zone-haiku",
3187 + "js-sys",
3188 + "log",
3189 + "wasm-bindgen",
3190 + "windows-core 0.62.2",
3191 +]
3192 +
3193 +[[package]]
3194 +name = "iana-time-zone-haiku"
3195 +version = "0.1.2"
3196 +source = "registry+https://github.com/rust-lang/crates.io-index"
3197 +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
3198 +dependencies = [
3199 + "cc",
3200 +]
3201 +
3202 +[[package]]
3203 +name = "ico"
3204 +version = "0.5.0"
3205 +source = "registry+https://github.com/rust-lang/crates.io-index"
3206 +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371"
3207 +dependencies = [
3208 + "byteorder",
3209 + "png 0.17.16",
3210 +]
3211 +
3212 +[[package]]
3213 +name = "icu_collections"
3214 +version = "2.2.0"
3215 +source = "registry+https://github.com/rust-lang/crates.io-index"
3216 +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
3217 +dependencies = [
3218 + "displaydoc",
3219 + "potential_utf",
3220 + "utf8_iter",
3221 + "yoke 0.8.3",
3222 + "zerofrom",
3223 + "zerovec",
3224 +]
3225 +
3226 +[[package]]
3227 +name = "icu_locale_core"
3228 +version = "2.2.0"
3229 +source = "registry+https://github.com/rust-lang/crates.io-index"
3230 +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
3231 +dependencies = [
3232 + "displaydoc",
3233 + "litemap",
3234 + "tinystr",
3235 + "writeable",
3236 + "zerovec",
3237 +]
3238 +
3239 +[[package]]
3240 +name = "icu_normalizer"
3241 +version = "2.2.0"
3242 +source = "registry+https://github.com/rust-lang/crates.io-index"
3243 +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
3244 +dependencies = [
3245 + "icu_collections",
3246 + "icu_normalizer_data",
3247 + "icu_properties",
3248 + "icu_provider",
3249 + "smallvec 1.15.2",
3250 + "zerovec",
3251 +]
3252 +
3253 +[[package]]
3254 +name = "icu_normalizer_data"
3255 +version = "2.2.0"
3256 +source = "registry+https://github.com/rust-lang/crates.io-index"
3257 +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
3258 +
3259 +[[package]]
3260 +name = "icu_properties"
3261 +version = "2.2.0"
3262 +source = "registry+https://github.com/rust-lang/crates.io-index"
3263 +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
3264 +dependencies = [
3265 + "icu_collections",
3266 + "icu_locale_core",
3267 + "icu_properties_data",
3268 + "icu_provider",
3269 + "zerotrie",
3270 + "zerovec",
3271 +]
3272 +
3273 +[[package]]
3274 +name = "icu_properties_data"
3275 +version = "2.2.0"
3276 +source = "registry+https://github.com/rust-lang/crates.io-index"
3277 +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
3278 +
3279 +[[package]]
3280 +name = "icu_provider"
3281 +version = "2.2.0"
3282 +source = "registry+https://github.com/rust-lang/crates.io-index"
3283 +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
3284 +dependencies = [
3285 + "displaydoc",
3286 + "icu_locale_core",
3287 + "writeable",
3288 + "yoke 0.8.3",
3289 + "zerofrom",
3290 + "zerotrie",
3291 + "zerovec",
3292 +]
3293 +
3294 +[[package]]
3295 +name = "ident_case"
3296 +version = "1.0.1"
3297 +source = "registry+https://github.com/rust-lang/crates.io-index"
3298 +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
3299 +
3300 +[[package]]
3301 +name = "idna"
3302 +version = "1.1.0"
3303 +source = "registry+https://github.com/rust-lang/crates.io-index"
3304 +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
3305 +dependencies = [
3306 + "idna_adapter",
3307 + "smallvec 1.15.2",
3308 + "utf8_iter",
3309 +]
3310 +
3311 +[[package]]
3312 +name = "idna_adapter"
3313 +version = "1.2.2"
3314 +source = "registry+https://github.com/rust-lang/crates.io-index"
3315 +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
3316 +dependencies = [
3317 + "icu_normalizer",
3318 + "icu_properties",
3319 +]
3320 +
3321 +[[package]]
3322 +name = "image"
3323 +version = "0.25.10"
3324 +source = "registry+https://github.com/rust-lang/crates.io-index"
3325 +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
3326 +dependencies = [
3327 + "bytemuck",
3328 + "byteorder-lite",
3329 + "color_quant",
3330 + "exr",
3331 + "gif",
3332 + "image-webp",
3333 + "moxcms",
3334 + "num-traits",
3335 + "png 0.18.1",
3336 + "qoi",
3337 + "tiff",
3338 + "zune-core",
3339 + "zune-jpeg",
3340 +]
3341 +
3342 +[[package]]
3343 +name = "image-webp"
3344 +version = "0.2.4"
3345 +source = "registry+https://github.com/rust-lang/crates.io-index"
3346 +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
3347 +dependencies = [
3348 + "byteorder-lite",
3349 + "quick-error",
3350 +]
3351 +
3352 +[[package]]
3353 +name = "indexmap"
3354 +version = "1.9.3"
3355 +source = "registry+https://github.com/rust-lang/crates.io-index"
3356 +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
3357 +dependencies = [
3358 + "autocfg",
3359 + "hashbrown 0.12.3",
3360 + "serde",
3361 +]
3362 +
3363 +[[package]]
3364 +name = "indexmap"
3365 +version = "2.14.0"
3366 +source = "registry+https://github.com/rust-lang/crates.io-index"
3367 +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
3368 +dependencies = [
3369 + "equivalent",
3370 + "hashbrown 0.17.1",
3371 + "serde",
3372 + "serde_core",
3373 +]
3374 +
3375 +[[package]]
3376 +name = "indicatif"
3377 +version = "0.17.11"
3378 +source = "registry+https://github.com/rust-lang/crates.io-index"
3379 +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235"
3380 +dependencies = [
3381 + "console 0.15.11",
3382 + "number_prefix",
3383 + "portable-atomic",
3384 + "unicode-width 0.2.2",
3385 + "web-time",
3386 +]
3387 +
3388 +[[package]]
3389 +name = "indicatif"
3390 +version = "0.18.6"
3391 +source = "registry+https://github.com/rust-lang/crates.io-index"
3392 +checksum = "9433806cd6b4ec1aba79c021c7e4c58fb4c3b9977c085062e611ac929998fb0c"
3393 +dependencies = [
3394 + "console 0.16.4",
3395 + "portable-atomic",
3396 + "rayon",
3397 + "unicode-width 0.2.2",
3398 + "unit-prefix",
3399 + "web-time",
3400 +]
3401 +
3402 +[[package]]
3403 +name = "infer"
3404 +version = "0.19.0"
3405 +source = "registry+https://github.com/rust-lang/crates.io-index"
3406 +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7"
3407 +dependencies = [
3408 + "cfb",
3409 +]
3410 +
3411 +[[package]]
3412 +name = "interprocess"
3413 +version = "2.4.2"
3414 +source = "registry+https://github.com/rust-lang/crates.io-index"
3415 +checksum = "069323743400cb7ab06a8fe5c1ed911d36b6919ec531661d034c89083629595b"
3416 +dependencies = [
3417 + "doctest-file",
3418 + "libc",
3419 + "recvmsg",
3420 + "widestring",
3421 + "windows-sys 0.61.2",
3422 +]
3423 +
3424 +[[package]]
3425 +name = "ipnet"
3426 +version = "2.12.0"
3427 +source = "registry+https://github.com/rust-lang/crates.io-index"
3428 +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
3429 +
3430 +[[package]]
3431 +name = "is-docker"
3432 +version = "0.2.0"
3433 +source = "registry+https://github.com/rust-lang/crates.io-index"
3434 +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3"
3435 +dependencies = [
3436 + "once_cell",
3437 +]
3438 +
3439 +[[package]]
3440 +name = "is-wsl"
3441 +version = "0.4.0"
3442 +source = "registry+https://github.com/rust-lang/crates.io-index"
3443 +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5"
3444 +dependencies = [
3445 + "is-docker",
3446 + "once_cell",
3447 +]
3448 +
3449 +[[package]]
3450 +name = "is_terminal_polyfill"
3451 +version = "1.70.2"
3452 +source = "registry+https://github.com/rust-lang/crates.io-index"
3453 +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
3454 +
3455 +[[package]]
3456 +name = "itertools"
3457 +version = "0.14.0"
3458 +source = "registry+https://github.com/rust-lang/crates.io-index"
3459 +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
3460 +dependencies = [
3461 + "either",
3462 +]
3463 +
3464 +[[package]]
3465 +name = "itoa"
3466 +version = "1.0.18"
3467 +source = "registry+https://github.com/rust-lang/crates.io-index"
3468 +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
3469 +
3470 +[[package]]
3471 +name = "javascriptcore-rs"
3472 +version = "1.1.2"
3473 +source = "registry+https://github.com/rust-lang/crates.io-index"
3474 +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc"
3475 +dependencies = [
3476 + "bitflags 1.3.2",
3477 + "glib",
3478 + "javascriptcore-rs-sys",
3479 +]
3480 +
3481 +[[package]]
3482 +name = "javascriptcore-rs-sys"
3483 +version = "1.1.1"
3484 +source = "registry+https://github.com/rust-lang/crates.io-index"
3485 +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124"
3486 +dependencies = [
3487 + "glib-sys",
3488 + "gobject-sys",
3489 + "libc",
3490 + "system-deps",
3491 +]
3492 +
3493 +[[package]]
3494 +name = "jni"
3495 +version = "0.21.1"
3496 +source = "registry+https://github.com/rust-lang/crates.io-index"
3497 +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
3498 +dependencies = [
3499 + "cesu8",
3500 + "cfg-if",
3501 + "combine",
3502 + "jni-sys 0.3.1",
3503 + "log",
3504 + "thiserror 1.0.69",
3505 + "walkdir",
3506 + "windows-sys 0.45.0",
3507 +]
3508 +
3509 +[[package]]
3510 +name = "jni"
3511 +version = "0.22.4"
3512 +source = "registry+https://github.com/rust-lang/crates.io-index"
3513 +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
3514 +dependencies = [
3515 + "cfg-if",
3516 + "combine",
3517 + "jni-macros",
3518 + "jni-sys 0.4.1",
3519 + "log",
3520 + "simd_cesu8",
3521 + "thiserror 2.0.18",
3522 + "walkdir",
3523 + "windows-link 0.2.1",
3524 +]
3525 +
3526 +[[package]]
3527 +name = "jni-macros"
3528 +version = "0.22.4"
3529 +source = "registry+https://github.com/rust-lang/crates.io-index"
3530 +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
3531 +dependencies = [
3532 + "proc-macro2",
3533 + "quote",
3534 + "rustc_version",
3535 + "simd_cesu8",
3536 + "syn 2.0.118",
3537 +]
3538 +
3539 +[[package]]
3540 +name = "jni-sys"
3541 +version = "0.3.1"
3542 +source = "registry+https://github.com/rust-lang/crates.io-index"
3543 +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
3544 +dependencies = [
3545 + "jni-sys 0.4.1",
3546 +]
3547 +
3548 +[[package]]
3549 +name = "jni-sys"
3550 +version = "0.4.1"
3551 +source = "registry+https://github.com/rust-lang/crates.io-index"
3552 +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
3553 +dependencies = [
3554 + "jni-sys-macros",
3555 +]
3556 +
3557 +[[package]]
3558 +name = "jni-sys-macros"
3559 +version = "0.4.1"
3560 +source = "registry+https://github.com/rust-lang/crates.io-index"
3561 +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
3562 +dependencies = [
3563 + "quote",
3564 + "syn 2.0.118",
3565 +]
3566 +
3567 +[[package]]
3568 +name = "jobserver"
3569 +version = "0.1.35"
3570 +source = "registry+https://github.com/rust-lang/crates.io-index"
3571 +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
3572 +dependencies = [
3573 + "getrandom 0.4.3",
3574 + "libc",
3575 +]
3576 +
3577 +[[package]]
3578 +name = "js-sys"
3579 +version = "0.3.103"
3580 +source = "registry+https://github.com/rust-lang/crates.io-index"
3581 +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
3582 +dependencies = [
3583 + "cfg-if",
3584 + "futures-util",
3585 + "wasm-bindgen",
3586 +]
3587 +
3588 +[[package]]
3589 +name = "json-patch"
3590 +version = "3.0.1"
3591 +source = "registry+https://github.com/rust-lang/crates.io-index"
3592 +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08"
3593 +dependencies = [
3594 + "jsonptr",
3595 + "serde",
3596 + "serde_json",
3597 + "thiserror 1.0.69",
3598 +]
3599 +
3600 +[[package]]
3601 +name = "jsonptr"
3602 +version = "0.6.3"
3603 +source = "registry+https://github.com/rust-lang/crates.io-index"
3604 +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70"
3605 +dependencies = [
3606 + "serde",
3607 + "serde_json",
3608 +]
3609 +
3610 +[[package]]
3611 +name = "keyboard-types"
3612 +version = "0.7.0"
3613 +source = "registry+https://github.com/rust-lang/crates.io-index"
3614 +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a"
3615 +dependencies = [
3616 + "bitflags 2.13.0",
3617 + "serde",
3618 + "unicode-segmentation",
3619 +]
3620 +
3621 +[[package]]
3622 +name = "lazy_static"
3623 +version = "1.5.0"
3624 +source = "registry+https://github.com/rust-lang/crates.io-index"
3625 +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
3626 +
3627 +[[package]]
3628 +name = "lebe"
3629 +version = "0.5.3"
3630 +source = "registry+https://github.com/rust-lang/crates.io-index"
3631 +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
3632 +
3633 +[[package]]
3634 +name = "libappindicator"
3635 +version = "0.9.0"
3636 +source = "registry+https://github.com/rust-lang/crates.io-index"
3637 +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a"
3638 +dependencies = [
3639 + "glib",
3640 + "gtk",
3641 + "gtk-sys",
3642 + "libappindicator-sys",
3643 + "log",
3644 +]
3645 +
3646 +[[package]]
3647 +name = "libappindicator-sys"
3648 +version = "0.9.0"
3649 +source = "registry+https://github.com/rust-lang/crates.io-index"
3650 +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf"
3651 +dependencies = [
3652 + "gtk-sys",
3653 + "libloading 0.7.4",
3654 + "once_cell",
3655 +]
3656 +
3657 +[[package]]
3658 +name = "libc"
3659 +version = "0.2.186"
3660 +source = "registry+https://github.com/rust-lang/crates.io-index"
3661 +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
3662 +
3663 +[[package]]
3664 +name = "libdbus-sys"
3665 +version = "0.2.7"
3666 +source = "registry+https://github.com/rust-lang/crates.io-index"
3667 +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043"
3668 +dependencies = [
3669 + "pkg-config",
3670 +]
3671 +
3672 +[[package]]
3673 +name = "libloading"
3674 +version = "0.7.4"
3675 +source = "registry+https://github.com/rust-lang/crates.io-index"
3676 +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
3677 +dependencies = [
3678 + "cfg-if",
3679 + "winapi",
3680 +]
3681 +
3682 +[[package]]
3683 +name = "libloading"
3684 +version = "0.8.9"
3685 +source = "registry+https://github.com/rust-lang/crates.io-index"
3686 +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
3687 +dependencies = [
3688 + "cfg-if",
3689 + "windows-link 0.2.1",
3690 +]
3691 +
3692 +[[package]]
3693 +name = "libm"
3694 +version = "0.2.16"
3695 +source = "registry+https://github.com/rust-lang/crates.io-index"
3696 +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
3697 +
3698 +[[package]]
3699 +name = "libredox"
3700 +version = "0.1.18"
3701 +source = "registry+https://github.com/rust-lang/crates.io-index"
3702 +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
3703 +dependencies = [
3704 + "libc",
3705 +]
3706 +
3707 +[[package]]
3708 +name = "linux-raw-sys"
3709 +version = "0.12.1"
3710 +source = "registry+https://github.com/rust-lang/crates.io-index"
3711 +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
3712 +
3713 +[[package]]
3714 +name = "litemap"
3715 +version = "0.8.2"
3716 +source = "registry+https://github.com/rust-lang/crates.io-index"
3717 +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
3718 +
3719 +[[package]]
3720 +name = "litrs"
3721 +version = "1.0.0"
3722 +source = "registry+https://github.com/rust-lang/crates.io-index"
3723 +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
3724 +
3725 +[[package]]
3726 +name = "llguidance"
3727 +version = "1.7.6"
3728 +source = "registry+https://github.com/rust-lang/crates.io-index"
3729 +checksum = "a519d0a437a6cc433edf3db7df98db11fc860a1f57657b84913d51fc670570ff"
3730 +dependencies = [
3731 + "anyhow",
3732 + "derivre",
3733 + "indexmap 2.14.0",
3734 + "regex-syntax",
3735 + "serde",
3736 + "serde_json",
3737 + "toktrie",
3738 +]
3739 +
3740 +[[package]]
3741 +name = "lock_api"
3742 +version = "0.4.14"
3743 +source = "registry+https://github.com/rust-lang/crates.io-index"
3744 +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
3745 +dependencies = [
3746 + "scopeguard",
3747 +]
3748 +
3749 +[[package]]
3750 +name = "log"
3751 +version = "0.4.33"
3752 +source = "registry+https://github.com/rust-lang/crates.io-index"
3753 +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
3754 +dependencies = [
3755 + "value-bag",
3756 +]
3757 +
3758 +[[package]]
3759 +name = "loom"
3760 +version = "0.5.6"
3761 +source = "registry+https://github.com/rust-lang/crates.io-index"
3762 +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
3763 +dependencies = [
3764 + "cfg-if",
3765 + "generator",
3766 + "scoped-tls",
3767 + "serde",
3768 + "serde_json",
3769 + "tracing",
3770 + "tracing-subscriber",
3771 +]
3772 +
3773 +[[package]]
3774 +name = "lrtable"
3775 +version = "0.14.2"
3776 +source = "registry+https://github.com/rust-lang/crates.io-index"
3777 +checksum = "bcbe6a005a604615ab7aab7d1f74be563e508f5c6e42f85fbf65c38fe1f5f268"
3778 +dependencies = [
3779 + "cfgrammar",
3780 + "fnv",
3781 + "num-traits",
3782 + "sparsevec",
3783 + "vob",
3784 +]
3785 +
3786 +[[package]]
3787 +name = "lru-slab"
3788 +version = "0.1.2"
3789 +source = "registry+https://github.com/rust-lang/crates.io-index"
3790 +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
3791 +
3792 +[[package]]
3793 +name = "mac"
3794 +version = "0.1.1"
3795 +source = "registry+https://github.com/rust-lang/crates.io-index"
3796 +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
3797 +
3798 +[[package]]
3799 +name = "macro_rules_attribute"
3800 +version = "0.2.2"
3801 +source = "registry+https://github.com/rust-lang/crates.io-index"
3802 +checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520"
3803 +dependencies = [
3804 + "macro_rules_attribute-proc_macro",
3805 + "paste",
3806 +]
3807 +
3808 +[[package]]
3809 +name = "macro_rules_attribute-proc_macro"
3810 +version = "0.2.2"
3811 +source = "registry+https://github.com/rust-lang/crates.io-index"
3812 +checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30"
3813 +
3814 +[[package]]
3815 +name = "malloc_buf"
3816 +version = "0.0.6"
3817 +source = "registry+https://github.com/rust-lang/crates.io-index"
3818 +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
3819 +dependencies = [
3820 + "libc",
3821 +]
3822 +
3823 +[[package]]
3824 +name = "maplit"
3825 +version = "1.0.2"
3826 +source = "registry+https://github.com/rust-lang/crates.io-index"
3827 +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
3828 +
3829 +[[package]]
3830 +name = "markup5ever"
3831 +version = "0.36.1"
3832 +source = "registry+https://github.com/rust-lang/crates.io-index"
3833 +checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c"
3834 +dependencies = [
3835 + "log",
3836 + "tendril 0.4.3",
3837 + "web_atoms",
3838 +]
3839 +
3840 +[[package]]
3841 +name = "markup5ever"
3842 +version = "0.38.0"
3843 +source = "registry+https://github.com/rust-lang/crates.io-index"
3844 +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862"
3845 +dependencies = [
3846 + "log",
3847 + "tendril 0.5.1",
3848 + "web_atoms",
3849 +]
3850 +
3851 +[[package]]
3852 +name = "matchers"
3853 +version = "0.2.0"
3854 +source = "registry+https://github.com/rust-lang/crates.io-index"
3855 +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
3856 +dependencies = [
3857 + "regex-automata",
3858 +]
3859 +
3860 +[[package]]
3861 +name = "matrixmultiply"
3862 +version = "0.3.10"
3863 +source = "registry+https://github.com/rust-lang/crates.io-index"
3864 +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08"
3865 +dependencies = [
3866 + "autocfg",
3867 + "rawpointer",
3868 +]
3869 +
3870 +[[package]]
3871 +name = "memchr"
3872 +version = "2.8.3"
3873 +source = "registry+https://github.com/rust-lang/crates.io-index"
3874 +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
3875 +
3876 +[[package]]
3877 +name = "memmap2"
3878 +version = "0.9.11"
3879 +source = "registry+https://github.com/rust-lang/crates.io-index"
3880 +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"
3881 +dependencies = [
3882 + "libc",
3883 + "stable_deref_trait",
3884 +]
3885 +
3886 +[[package]]
3887 +name = "memo-map"
3888 +version = "0.3.3"
3889 +source = "registry+https://github.com/rust-lang/crates.io-index"
3890 +checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b"
3891 +
3892 +[[package]]
3893 +name = "memoffset"
3894 +version = "0.9.1"
3895 +source = "registry+https://github.com/rust-lang/crates.io-index"
3896 +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
3897 +dependencies = [
3898 + "autocfg",
3899 +]
3900 +
3901 +[[package]]
3902 +name = "metal"
3903 +version = "0.29.0"
3904 +source = "registry+https://github.com/rust-lang/crates.io-index"
3905 +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21"
3906 +dependencies = [
3907 + "bitflags 2.13.0",
3908 + "block",
3909 + "core-graphics-types 0.1.3",
3910 + "foreign-types",
3911 + "log",
3912 + "objc",
3913 + "paste",
3914 +]
3915 +
3916 +[[package]]
3917 +name = "mime"
3918 +version = "0.3.17"
3919 +source = "registry+https://github.com/rust-lang/crates.io-index"
3920 +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
3921 +
3922 +[[package]]
3923 +name = "mime_guess"
3924 +version = "2.0.5"
3925 +source = "registry+https://github.com/rust-lang/crates.io-index"
3926 +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
3927 +dependencies = [
3928 + "mime",
3929 + "unicase",
3930 +]
3931 +
3932 +[[package]]
3933 +name = "minijinja"
3934 +version = "2.21.0"
3935 +source = "registry+https://github.com/rust-lang/crates.io-index"
3936 +checksum = "cb3d648e68cea56d9858d535ee28f9538404e2dd8cb08ed0bd05dca379477f39"
3937 +dependencies = [
3938 + "memo-map",
3939 + "serde",
3940 + "serde_json",
3941 +]
3942 +
3943 +[[package]]
3944 +name = "minijinja-contrib"
3945 +version = "2.21.0"
3946 +source = "registry+https://github.com/rust-lang/crates.io-index"
3947 +checksum = "85342f6fac0be8ccd5bd00d9066be538f34f393f577b75d81b17c8398a6b43bb"
3948 +dependencies = [
3949 + "minijinja",
3950 + "serde",
3951 +]
3952 +
3953 +[[package]]
3954 +name = "minimal-lexical"
3955 +version = "0.2.1"
3956 +source = "registry+https://github.com/rust-lang/crates.io-index"
3957 +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
3958 +
3959 +[[package]]
3960 +name = "miniz_oxide"
3961 +version = "0.8.9"
3962 +source = "registry+https://github.com/rust-lang/crates.io-index"
3963 +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
3964 +dependencies = [
3965 + "adler2",
3966 + "simd-adler32",
3967 +]
3968 +
3969 +[[package]]
3970 +name = "mio"
3971 +version = "1.2.1"
3972 +source = "registry+https://github.com/rust-lang/crates.io-index"
3973 +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
3974 +dependencies = [
3975 + "libc",
3976 + "log",
3977 + "wasi",
3978 + "windows-sys 0.61.2",
3979 +]
3980 +
3981 +[[package]]
3982 +name = "monostate"
3983 +version = "0.1.18"
3984 +source = "registry+https://github.com/rust-lang/crates.io-index"
3985 +checksum = "3341a273f6c9d5bef1908f17b7267bbab0e95c9bf69a0d4dcf8e9e1b2c76ef67"
3986 +dependencies = [
3987 + "monostate-impl",
3988 + "serde",
3989 + "serde_core",
3990 +]
3991 +
3992 +[[package]]
3993 +name = "monostate-impl"
3994 +version = "0.1.18"
3995 +source = "registry+https://github.com/rust-lang/crates.io-index"
3996 +checksum = "e4db6d5580af57bf992f59068d4ea26fd518574ff48d7639b255a36f9de6e7e9"
3997 +dependencies = [
3998 + "proc-macro2",
3999 + "quote",
4000 + "syn 2.0.118",
4001 +]
4002 +
4003 +[[package]]
4004 +name = "moxcms"
4005 +version = "0.8.1"
4006 +source = "registry+https://github.com/rust-lang/crates.io-index"
4007 +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
4008 +dependencies = [
4009 + "num-traits",
4010 + "pxfm",
4011 +]
4012 +
4013 +[[package]]
4014 +name = "muda"
4015 +version = "0.19.3"
4016 +source = "registry+https://github.com/rust-lang/crates.io-index"
4017 +checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878"
4018 +dependencies = [
4019 + "crossbeam-channel",
4020 + "dpi",
4021 + "gtk",
4022 + "keyboard-types",
4023 + "objc2",
4024 + "objc2-app-kit",
4025 + "objc2-core-foundation",
4026 + "objc2-foundation",
4027 + "once_cell",
4028 + "png 0.18.1",
4029 + "serde",
4030 + "thiserror 2.0.18",
4031 + "windows-sys 0.61.2",
4032 +]
4033 +
4034 +[[package]]
4035 +name = "nalgebra"
4036 +version = "0.33.3"
4037 +source = "registry+https://github.com/rust-lang/crates.io-index"
4038 +checksum = "9d43ddcacf343185dfd6de2ee786d9e8b1c2301622afab66b6c73baf9882abfd"
4039 +dependencies = [
4040 + "approx",
4041 + "matrixmultiply",
4042 + "num-complex",
4043 + "num-rational",
4044 + "num-traits",
4045 + "rand 0.8.6",
4046 + "rand_distr 0.4.3",
4047 + "simba",
4048 + "typenum",
4049 +]
4050 +
4051 +[[package]]
4052 +name = "ndk"
4053 +version = "0.9.0"
4054 +source = "registry+https://github.com/rust-lang/crates.io-index"
4055 +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
4056 +dependencies = [
4057 + "bitflags 2.13.0",
4058 + "jni-sys 0.3.1",
4059 + "log",
4060 + "ndk-sys",
4061 + "num_enum",
4062 + "raw-window-handle",
4063 + "thiserror 1.0.69",
4064 +]
4065 +
4066 +[[package]]
4067 +name = "ndk-sys"
4068 +version = "0.6.0+11769913"
4069 +source = "registry+https://github.com/rust-lang/crates.io-index"
4070 +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
4071 +dependencies = [
4072 + "jni-sys 0.3.1",
4073 +]
4074 +
4075 +[[package]]
4076 +name = "new_debug_unreachable"
4077 +version = "1.0.6"
4078 +source = "registry+https://github.com/rust-lang/crates.io-index"
4079 +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
4080 +
4081 +[[package]]
4082 +name = "nibble_vec"
4083 +version = "0.1.0"
4084 +source = "registry+https://github.com/rust-lang/crates.io-index"
4085 +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
4086 +dependencies = [
4087 + "smallvec 1.15.2",
4088 +]
4089 +
4090 +[[package]]
4091 +name = "nohash-hasher"
4092 +version = "0.2.0"
4093 +source = "registry+https://github.com/rust-lang/crates.io-index"
4094 +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
4095 +
4096 +[[package]]
4097 +name = "nom"
4098 +version = "7.1.3"
4099 +source = "registry+https://github.com/rust-lang/crates.io-index"
4100 +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
4101 +dependencies = [
4102 + "memchr",
4103 + "minimal-lexical",
4104 +]
4105 +
4106 +[[package]]
4107 +name = "ntapi"
4108 +version = "0.4.3"
4109 +source = "registry+https://github.com/rust-lang/crates.io-index"
4110 +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae"
4111 +dependencies = [
4112 + "winapi",
4113 +]
4114 +
4115 +[[package]]
4116 +name = "nu-ansi-term"
4117 +version = "0.50.3"
4118 +source = "registry+https://github.com/rust-lang/crates.io-index"
4119 +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
4120 +dependencies = [
4121 + "windows-sys 0.61.2",
4122 +]
4123 +
4124 +[[package]]
4125 +name = "num"
4126 +version = "0.4.3"
4127 +source = "registry+https://github.com/rust-lang/crates.io-index"
4128 +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
4129 +dependencies = [
4130 + "num-bigint",
4131 + "num-complex",
4132 + "num-integer",
4133 + "num-iter",
4134 + "num-rational",
4135 + "num-traits",
4136 +]
4137 +
4138 +[[package]]
4139 +name = "num-bigint"
4140 +version = "0.4.8"
4141 +source = "registry+https://github.com/rust-lang/crates.io-index"
4142 +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
4143 +dependencies = [
4144 + "num-integer",
4145 + "num-traits",
4146 +]
4147 +
4148 +[[package]]
4149 +name = "num-complex"
4150 +version = "0.4.6"
4151 +source = "registry+https://github.com/rust-lang/crates.io-index"
4152 +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
4153 +dependencies = [
4154 + "bytemuck",
4155 + "num-traits",
4156 +]
4157 +
4158 +[[package]]
4159 +name = "num-conv"
4160 +version = "0.2.2"
4161 +source = "registry+https://github.com/rust-lang/crates.io-index"
4162 +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
4163 +
4164 +[[package]]
4165 +name = "num-integer"
4166 +version = "0.1.46"
4167 +source = "registry+https://github.com/rust-lang/crates.io-index"
4168 +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
4169 +dependencies = [
4170 + "num-traits",
4171 +]
4172 +
4173 +[[package]]
4174 +name = "num-iter"
4175 +version = "0.1.46"
4176 +source = "registry+https://github.com/rust-lang/crates.io-index"
4177 +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b"
4178 +dependencies = [
4179 + "num-integer",
4180 + "num-traits",
4181 +]
4182 +
4183 +[[package]]
4184 +name = "num-rational"
4185 +version = "0.4.2"
4186 +source = "registry+https://github.com/rust-lang/crates.io-index"
4187 +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
4188 +dependencies = [
4189 + "num-bigint",
4190 + "num-integer",
4191 + "num-traits",
4192 +]
4193 +
4194 +[[package]]
4195 +name = "num-traits"
4196 +version = "0.2.19"
4197 +source = "registry+https://github.com/rust-lang/crates.io-index"
4198 +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
4199 +dependencies = [
4200 + "autocfg",
4201 + "libm",
4202 +]
4203 +
4204 +[[package]]
4205 +name = "num_cpus"
4206 +version = "1.17.0"
4207 +source = "registry+https://github.com/rust-lang/crates.io-index"
4208 +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
4209 +dependencies = [
4210 + "hermit-abi 0.5.2",
4211 + "libc",
4212 +]
4213 +
4214 +[[package]]
4215 +name = "num_enum"
4216 +version = "0.7.6"
4217 +source = "registry+https://github.com/rust-lang/crates.io-index"
4218 +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
4219 +dependencies = [
4220 + "num_enum_derive",
4221 + "rustversion",
4222 +]
4223 +
4224 +[[package]]
4225 +name = "num_enum_derive"
4226 +version = "0.7.6"
4227 +source = "registry+https://github.com/rust-lang/crates.io-index"
4228 +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
4229 +dependencies = [
4230 + "proc-macro-crate 3.5.0",
4231 + "proc-macro2",
4232 + "quote",
4233 + "syn 2.0.118",
4234 +]
4235 +
4236 +[[package]]
4237 +name = "num_threads"
4238 +version = "0.1.7"
4239 +source = "registry+https://github.com/rust-lang/crates.io-index"
4240 +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
4241 +dependencies = [
4242 + "libc",
4243 +]
4244 +
4245 +[[package]]
4246 +name = "number_prefix"
4247 +version = "0.4.0"
4248 +source = "registry+https://github.com/rust-lang/crates.io-index"
4249 +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
4250 +
4251 +[[package]]
4252 +name = "objc"
4253 +version = "0.2.7"
4254 +source = "registry+https://github.com/rust-lang/crates.io-index"
4255 +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
4256 +dependencies = [
4257 + "malloc_buf",
4258 +]
4259 +
4260 +[[package]]
4261 +name = "objc2"
4262 +version = "0.6.4"
4263 +source = "registry+https://github.com/rust-lang/crates.io-index"
4264 +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
4265 +dependencies = [
4266 + "objc2-encode",
4267 + "objc2-exception-helper",
4268 +]
4269 +
4270 +[[package]]
4271 +name = "objc2-app-kit"
4272 +version = "0.3.2"
4273 +source = "registry+https://github.com/rust-lang/crates.io-index"
4274 +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
4275 +dependencies = [
4276 + "bitflags 2.13.0",
4277 + "block2",
4278 + "objc2",
4279 + "objc2-core-foundation",
4280 + "objc2-foundation",
4281 +]
4282 +
4283 +[[package]]
4284 +name = "objc2-cloud-kit"
4285 +version = "0.3.2"
4286 +source = "registry+https://github.com/rust-lang/crates.io-index"
4287 +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
4288 +dependencies = [
4289 + "bitflags 2.13.0",
4290 + "objc2",
4291 + "objc2-foundation",
4292 +]
4293 +
4294 +[[package]]
4295 +name = "objc2-core-data"
4296 +version = "0.3.2"
4297 +source = "registry+https://github.com/rust-lang/crates.io-index"
4298 +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa"
4299 +dependencies = [
4300 + "objc2",
4301 + "objc2-foundation",
4302 +]
4303 +
4304 +[[package]]
4305 +name = "objc2-core-foundation"
4306 +version = "0.3.2"
4307 +source = "registry+https://github.com/rust-lang/crates.io-index"
4308 +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
4309 +dependencies = [
4310 + "bitflags 2.13.0",
4311 + "dispatch2",
4312 + "objc2",
4313 +]
4314 +
4315 +[[package]]
4316 +name = "objc2-core-graphics"
4317 +version = "0.3.2"
4318 +source = "registry+https://github.com/rust-lang/crates.io-index"
4319 +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
4320 +dependencies = [
4321 + "bitflags 2.13.0",
4322 + "dispatch2",
4323 + "objc2",
4324 + "objc2-core-foundation",
4325 + "objc2-io-surface",
4326 +]
4327 +
4328 +[[package]]
4329 +name = "objc2-core-image"
4330 +version = "0.3.2"
4331 +source = "registry+https://github.com/rust-lang/crates.io-index"
4332 +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006"
4333 +dependencies = [
4334 + "objc2",
4335 + "objc2-foundation",
4336 +]
4337 +
4338 +[[package]]
4339 +name = "objc2-core-location"
4340 +version = "0.3.2"
4341 +source = "registry+https://github.com/rust-lang/crates.io-index"
4342 +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009"
4343 +dependencies = [
4344 + "objc2",
4345 + "objc2-foundation",
4346 +]
4347 +
4348 +[[package]]
4349 +name = "objc2-core-text"
4350 +version = "0.3.2"
4351 +source = "registry+https://github.com/rust-lang/crates.io-index"
4352 +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
4353 +dependencies = [
4354 + "bitflags 2.13.0",
4355 + "objc2",
4356 + "objc2-core-foundation",
4357 + "objc2-core-graphics",
4358 +]
4359 +
4360 +[[package]]
4361 +name = "objc2-encode"
4362 +version = "4.1.0"
4363 +source = "registry+https://github.com/rust-lang/crates.io-index"
4364 +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
4365 +
4366 +[[package]]
4367 +name = "objc2-exception-helper"
4368 +version = "0.1.1"
4369 +source = "registry+https://github.com/rust-lang/crates.io-index"
4370 +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a"
4371 +dependencies = [
4372 + "cc",
4373 +]
4374 +
4375 +[[package]]
4376 +name = "objc2-foundation"
4377 +version = "0.3.2"
4378 +source = "registry+https://github.com/rust-lang/crates.io-index"
4379 +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
4380 +dependencies = [
4381 + "bitflags 2.13.0",
4382 + "block2",
4383 + "libc",
4384 + "objc2",
4385 + "objc2-core-foundation",
4386 +]
4387 +
4388 +[[package]]
4389 +name = "objc2-io-kit"
4390 +version = "0.3.2"
4391 +source = "registry+https://github.com/rust-lang/crates.io-index"
4392 +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15"
4393 +dependencies = [
4394 + "libc",
4395 + "objc2-core-foundation",
4396 +]
4397 +
4398 +[[package]]
4399 +name = "objc2-io-surface"
4400 +version = "0.3.2"
4401 +source = "registry+https://github.com/rust-lang/crates.io-index"
4402 +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
4403 +dependencies = [
4404 + "bitflags 2.13.0",
4405 + "objc2",
4406 + "objc2-core-foundation",
4407 +]
4408 +
4409 +[[package]]
4410 +name = "objc2-metal"
4411 +version = "0.3.2"
4412 +source = "registry+https://github.com/rust-lang/crates.io-index"
4413 +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794"
4414 +dependencies = [
4415 + "bitflags 2.13.0",
4416 + "block2",
4417 + "dispatch2",
4418 + "objc2",
4419 + "objc2-core-foundation",
4420 + "objc2-foundation",
4421 +]
4422 +
4423 +[[package]]
4424 +name = "objc2-quartz-core"
4425 +version = "0.3.2"
4426 +source = "registry+https://github.com/rust-lang/crates.io-index"
4427 +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
4428 +dependencies = [
4429 + "bitflags 2.13.0",
4430 + "objc2",
4431 + "objc2-core-foundation",
4432 + "objc2-foundation",
4433 +]
4434 +
4435 +[[package]]
4436 +name = "objc2-ui-kit"
4437 +version = "0.3.2"
4438 +source = "registry+https://github.com/rust-lang/crates.io-index"
4439 +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
4440 +dependencies = [
4441 + "bitflags 2.13.0",
4442 + "block2",
4443 + "objc2",
4444 + "objc2-cloud-kit",
4445 + "objc2-core-data",
4446 + "objc2-core-foundation",
4447 + "objc2-core-graphics",
4448 + "objc2-core-image",
4449 + "objc2-core-location",
4450 + "objc2-core-text",
4451 + "objc2-foundation",
4452 + "objc2-quartz-core",
4453 + "objc2-user-notifications",
4454 +]
4455 +
4456 +[[package]]
4457 +name = "objc2-user-notifications"
4458 +version = "0.3.2"
4459 +source = "registry+https://github.com/rust-lang/crates.io-index"
4460 +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e"
4461 +dependencies = [
4462 + "objc2",
4463 + "objc2-foundation",
4464 +]
4465 +
4466 +[[package]]
4467 +name = "objc2-web-kit"
4468 +version = "0.3.2"
4469 +source = "registry+https://github.com/rust-lang/crates.io-index"
4470 +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f"
4471 +dependencies = [
4472 + "bitflags 2.13.0",
4473 + "block2",
4474 + "objc2",
4475 + "objc2-app-kit",
4476 + "objc2-core-foundation",
4477 + "objc2-foundation",
4478 +]
4479 +
4480 +[[package]]
4481 +name = "once_cell"
4482 +version = "1.21.4"
4483 +source = "registry+https://github.com/rust-lang/crates.io-index"
4484 +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
4485 +
4486 +[[package]]
4487 +name = "once_cell_polyfill"
4488 +version = "1.70.2"
4489 +source = "registry+https://github.com/rust-lang/crates.io-index"
4490 +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
4491 +
4492 +[[package]]
4493 +name = "onde"
4494 +version = "1.1.4"
4495 +source = "registry+https://github.com/rust-lang/crates.io-index"
4496 +checksum = "f7d01fc4d6b6be05d2e201d8e3e539fae5c5dc011c157cbea40f2ab775fa67ab"
4497 +dependencies = [
4498 + "anyhow",
4499 + "cc",
4500 + "dotenvy",
4501 + "hf-hub",
4502 + "home",
4503 + "log",
4504 + "onde-mistralrs",
4505 + "onde-mistralrs-core",
4506 + "reqwest 0.12.28",
4507 + "serde",
4508 + "serde_json",
4509 + "smbcloud-gresiq-sdk",
4510 + "thiserror 2.0.18",
4511 + "tokio",
4512 + "tsync",
4513 + "uniffi",
4514 +]
4515 +
4516 +[[package]]
4517 +name = "onde-mistralrs"
4518 +version = "0.8.2"
4519 +source = "registry+https://github.com/rust-lang/crates.io-index"
4520 +checksum = "f4b5589da2ed82d3962ab4d8278cc8a93fea7c758eb1230894ffadb926c3c16c"
4521 +dependencies = [
4522 + "anyhow",
4523 + "candle-core",
4524 + "candle-nn",
4525 + "clap 4.6.1",
4526 + "either",
4527 + "futures",
4528 + "image",
4529 + "indexmap 2.14.0",
4530 + "onde-mistralrs-core",
4531 + "onde-mistralrs-macros",
4532 + "rand 0.9.4",
4533 + "reqwest 0.13.4",
4534 + "schemars 1.2.1",
4535 + "serde",
4536 + "serde_json",
4537 + "thiserror 2.0.18",
4538 + "tokio",
4539 + "tracing",
4540 + "tracing-subscriber",
4541 + "walkdir",
4542 +]
4543 +
4544 +[[package]]
4545 +name = "onde-mistralrs-audio"
4546 +version = "0.8.2"
4547 +source = "registry+https://github.com/rust-lang/crates.io-index"
4548 +checksum = "ed88aa277c613327f393ff5816fed55609a7a93b87ace505d70a6b3438b12dd2"
4549 +dependencies = [
4550 + "anyhow",
4551 + "apodize",
4552 + "hound",
4553 + "symphonia",
4554 +]
4555 +
4556 +[[package]]
4557 +name = "onde-mistralrs-core"
4558 +version = "0.8.2"
4559 +source = "registry+https://github.com/rust-lang/crates.io-index"
4560 +checksum = "7872456b7db2d8aaf39da83269dbf77e2503d6d398010f3b9a17eebec3da8dee"
4561 +dependencies = [
4562 + "ahash 0.8.12",
4563 + "akin",
4564 + "anyhow",
4565 + "apodize",
4566 + "as-any",
4567 + "async-trait",
4568 + "base64 0.22.1",
4569 + "bm25",
4570 + "bytemuck",
4571 + "bytemuck_derive",
4572 + "candle-core",
4573 + "candle-metal-kernels",
4574 + "candle-nn",
4575 + "cfgrammar",
4576 + "chrono",
4577 + "clap 4.6.1",
4578 + "csv",
4579 + "derive-new",
4580 + "derive_more",
4581 + "dirs",
4582 + "either",
4583 + "float8",
4584 + "futures",
4585 + "galil-seiferas",
4586 + "half",
4587 + "hashbrown 0.16.1",
4588 + "hf-hub",
4589 + "hound",
4590 + "html2text",
4591 + "http",
4592 + "image",
4593 + "indexmap 2.14.0",
4594 + "indicatif 0.18.6",
4595 + "interprocess",
4596 + "itertools",
4597 + "libc",
4598 + "llguidance",
4599 + "lrtable",
4600 + "minijinja",
4601 + "minijinja-contrib",
4602 + "num-traits",
4603 + "objc",
4604 + "objc2-metal",
4605 + "onde-mistralrs-audio",
4606 + "onde-mistralrs-mcp",
4607 + "onde-mistralrs-paged-attn",
4608 + "onde-mistralrs-quant",
4609 + "onde-mistralrs-vision",
4610 + "onde-openai-harmony",
4611 + "onde-tqdm",
4612 + "ordered-float",
4613 + "parking_lot",
4614 + "radix_trie",
4615 + "rand 0.9.4",
4616 + "rand_distr 0.5.1",
4617 + "rand_isaac",
4618 + "rayon",
4619 + "regex",
4620 + "regex-automata",
4621 + "reqwest 0.13.4",
4622 + "rubato",
4623 + "rust-mcp-schema",
4624 + "rustc-hash 2.1.3",
4625 + "rustfft",
4626 + "safetensors 0.7.0",
4627 + "schemars 1.2.1",
4628 + "scraper",
4629 + "serde",
4630 + "serde-big-array",
4631 + "serde-saphyr",
4632 + "serde_json",
4633 + "serde_plain",
4634 + "statrs",
4635 + "strum 0.27.2",
4636 + "symphonia",
4637 + "sysinfo",
4638 + "thiserror 2.0.18",
4639 + "tokenizers 0.21.4",
4640 + "tokio",
4641 + "tokio-rayon",
4642 + "tokio-tungstenite",
4643 + "toktrie",
4644 + "toktrie_hf_tokenizers",
4645 + "toml 0.9.12+spec-1.1.0",
4646 + "tracing",
4647 + "tracing-subscriber",
4648 + "urlencoding",
4649 + "uuid 1.23.4",
4650 + "variantly",
4651 + "vob",
4652 +]
4653 +
4654 +[[package]]
4655 +name = "onde-mistralrs-macros"
4656 +version = "0.8.2"
4657 +source = "registry+https://github.com/rust-lang/crates.io-index"
4658 +checksum = "ed7a88fcf59377469c00a355065f3ad9723e00c1870c8b3717b0649f86c77bde"
4659 +dependencies = [
4660 + "darling 0.23.0",
4661 + "proc-macro2",
4662 + "quote",
4663 + "syn 2.0.118",
4664 +]
4665 +
4666 +[[package]]
4667 +name = "onde-mistralrs-mcp"
4668 +version = "0.8.2"
4669 +source = "registry+https://github.com/rust-lang/crates.io-index"
4670 +checksum = "f708829672943371b2a14cceaa8c09182d11c9049ec6d3a9c303a74ae65dfc1f"
4671 +dependencies = [
4672 + "anyhow",
4673 + "async-trait",
4674 + "futures-util",
4675 + "http",
4676 + "reqwest 0.13.4",
4677 + "rust-mcp-schema",
4678 + "serde",
4679 + "serde_json",
4680 + "tokio",
4681 + "tokio-tungstenite",
4682 + "tracing",
4683 + "utoipa",
4684 + "uuid 1.23.4",
4685 +]
4686 +
4687 +[[package]]
4688 +name = "onde-mistralrs-paged-attn"
4689 +version = "0.8.2"
4690 +source = "registry+https://github.com/rust-lang/crates.io-index"
4691 +checksum = "bdd14bcbee553325d640609504117a2a49db166417bd7883329c6d39971b28c2"
4692 +dependencies = [
4693 + "anyhow",
4694 + "candle-core",
4695 + "candle-metal-kernels",
4696 + "dispatch2",
4697 + "float8",
4698 + "half",
4699 + "objc2-foundation",
4700 + "objc2-metal",
4701 + "thiserror 2.0.18",
4702 +]
4703 +
4704 +[[package]]
4705 +name = "onde-mistralrs-quant"
4706 +version = "0.8.2"
4707 +source = "registry+https://github.com/rust-lang/crates.io-index"
4708 +checksum = "36928bb1eec1138ca7b36ee1f4d370e4e374f0874f378915ff0ff34f927c4daf"
4709 +dependencies = [
4710 + "byteorder",
4711 + "candle-core",
4712 + "candle-metal-kernels",
4713 + "candle-nn",
4714 + "dispatch2",
4715 + "float8",
4716 + "half",
4717 + "hf-hub",
4718 + "lazy_static",
4719 + "memmap2",
4720 + "objc2-foundation",
4721 + "objc2-metal",
4722 + "paste",
4723 + "rayon",
4724 + "regex",
4725 + "safetensors 0.7.0",
4726 + "serde",
4727 + "serde_json",
4728 + "thiserror 2.0.18",
4729 + "tokio",
4730 + "tracing",
4731 + "yoke 0.8.3",
4732 +]
4733 +
4734 +[[package]]
4735 +name = "onde-mistralrs-vision"
4736 +version = "0.8.2"
4737 +source = "registry+https://github.com/rust-lang/crates.io-index"
4738 +checksum = "2670a611890bfddc7964902f253626cd2b3fd0b7df13f84a779180f35e2a79e9"
4739 +dependencies = [
4740 + "candle-core",
4741 + "image",
4742 + "rayon",
4743 +]
4744 +
4745 +[[package]]
4746 +name = "onde-openai-harmony"
4747 +version = "0.0.9"
4748 +source = "registry+https://github.com/rust-lang/crates.io-index"
4749 +checksum = "f0f73e12b7378937cb8aaf832bed02b2afa1687581f73c3298c5af0cca9df5e6"
4750 +dependencies = [
4751 + "anyhow",
4752 + "base64 0.22.1",
4753 + "bstr",
4754 + "clap 4.6.1",
4755 + "fancy-regex 0.13.0",
4756 + "futures",
4757 + "regex",
4758 + "reqwest 0.12.28",
4759 + "rustc-hash 1.1.0",
4760 + "serde",
4761 + "serde_json",
4762 + "serde_with",
4763 + "sha1",
4764 + "sha2",
4765 + "thiserror 2.0.18",
4766 +]
4767 +
4768 +[[package]]
4769 +name = "onde-tqdm"
4770 +version = "0.8.2"
4771 +source = "registry+https://github.com/rust-lang/crates.io-index"
4772 +checksum = "2d96f4fcb4af06959bf9c9b81ace0502e2be2d11d7e037c94f3f7851bde720ea"
4773 +dependencies = [
4774 + "anyhow",
4775 + "crossterm",
4776 + "once_cell",
4777 +]
4778 +
4779 +[[package]]
4780 +name = "onig"
4781 +version = "6.5.3"
4782 +source = "registry+https://github.com/rust-lang/crates.io-index"
4783 +checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2"
4784 +dependencies = [
4785 + "bitflags 2.13.0",
4786 + "libc",
4787 + "once_cell",
4788 + "onig_sys",
4789 +]
4790 +
4791 +[[package]]
4792 +name = "onig_sys"
4793 +version = "69.9.3"
4794 +source = "registry+https://github.com/rust-lang/crates.io-index"
4795 +checksum = "1e68317604e77e53b85896388e1a803c1d21b74c899ec9e5e1112db90735edd7"
4796 +dependencies = [
4797 + "cc",
4798 + "pkg-config",
4799 +]
4800 +
4801 +[[package]]
4802 +name = "open"
4803 +version = "5.3.6"
4804 +source = "registry+https://github.com/rust-lang/crates.io-index"
4805 +checksum = "cd8d3b65c44123a56e0133d2cd06ce4361bd3ca99d41198b2f25e3c3db9b8b4a"
4806 +dependencies = [
4807 + "dunce",
4808 + "is-wsl",
4809 + "libc",
4810 +]
4811 +
4812 +[[package]]
4813 +name = "openssl-probe"
4814 +version = "0.2.1"
4815 +source = "registry+https://github.com/rust-lang/crates.io-index"
4816 +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
4817 +
4818 +[[package]]
4819 +name = "option-ext"
4820 +version = "0.2.0"
4821 +source = "registry+https://github.com/rust-lang/crates.io-index"
4822 +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
4823 +
4824 +[[package]]
4825 +name = "ordered-float"
4826 +version = "5.3.0"
4827 +source = "registry+https://github.com/rust-lang/crates.io-index"
4828 +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e"
4829 +dependencies = [
4830 + "num-traits",
4831 +]
4832 +
4833 +[[package]]
4834 +name = "ordered-stream"
4835 +version = "0.2.0"
4836 +source = "registry+https://github.com/rust-lang/crates.io-index"
4837 +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
4838 +dependencies = [
4839 + "futures-core",
4840 + "pin-project-lite",
4841 +]
4842 +
4843 +[[package]]
4844 +name = "packedvec"
4845 +version = "1.2.5"
4846 +source = "registry+https://github.com/rust-lang/crates.io-index"
4847 +checksum = "a69e0a534dd2e6aefce319af62a0aa0066a76bdfcec0201dfe02df226bc9ec70"
4848 +dependencies = [
4849 + "num-traits",
4850 +]
4851 +
4852 +[[package]]
4853 +name = "pango"
4854 +version = "0.18.3"
4855 +source = "registry+https://github.com/rust-lang/crates.io-index"
4856 +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4"
4857 +dependencies = [
4858 + "gio",
4859 + "glib",
4860 + "libc",
4861 + "once_cell",
4862 + "pango-sys",
4863 +]
4864 +
4865 +[[package]]
4866 +name = "pango-sys"
4867 +version = "0.18.0"
4868 +source = "registry+https://github.com/rust-lang/crates.io-index"
4869 +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5"
4870 +dependencies = [
4871 + "glib-sys",
4872 + "gobject-sys",
4873 + "libc",
4874 + "system-deps",
4875 +]
4876 +
4877 +[[package]]
4878 +name = "parking"
4879 +version = "2.2.1"
4880 +source = "registry+https://github.com/rust-lang/crates.io-index"
4881 +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
4882 +
4883 +[[package]]
4884 +name = "parking_lot"
4885 +version = "0.12.5"
4886 +source = "registry+https://github.com/rust-lang/crates.io-index"
4887 +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
4888 +dependencies = [
4889 + "lock_api",
4890 + "parking_lot_core",
4891 +]
4892 +
4893 +[[package]]
4894 +name = "parking_lot_core"
4895 +version = "0.9.12"
4896 +source = "registry+https://github.com/rust-lang/crates.io-index"
4897 +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
4898 +dependencies = [
4899 + "cfg-if",
4900 + "libc",
4901 + "redox_syscall",
4902 + "smallvec 1.15.2",
4903 + "windows-link 0.2.1",
4904 +]
4905 +
4906 +[[package]]
4907 +name = "paste"
4908 +version = "1.0.15"
4909 +source = "registry+https://github.com/rust-lang/crates.io-index"
4910 +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
4911 +
4912 +[[package]]
4913 +name = "percent-encoding"
4914 +version = "2.3.2"
4915 +source = "registry+https://github.com/rust-lang/crates.io-index"
4916 +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
4917 +
4918 +[[package]]
4919 +name = "phf"
4920 +version = "0.13.1"
4921 +source = "registry+https://github.com/rust-lang/crates.io-index"
4922 +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
4923 +dependencies = [
4924 + "phf_macros",
4925 + "phf_shared",
4926 + "serde",
4927 +]
4928 +
4929 +[[package]]
4930 +name = "phf_codegen"
4931 +version = "0.13.1"
4932 +source = "registry+https://github.com/rust-lang/crates.io-index"
4933 +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
4934 +dependencies = [
4935 + "phf_generator",
4936 + "phf_shared",
4937 +]
4938 +
4939 +[[package]]
4940 +name = "phf_generator"
4941 +version = "0.13.1"
4942 +source = "registry+https://github.com/rust-lang/crates.io-index"
4943 +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
4944 +dependencies = [
4945 + "fastrand",
4946 + "phf_shared",
4947 +]
4948 +
4949 +[[package]]
4950 +name = "phf_macros"
4951 +version = "0.13.1"
4952 +source = "registry+https://github.com/rust-lang/crates.io-index"
4953 +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef"
4954 +dependencies = [
4955 + "phf_generator",
4956 + "phf_shared",
4957 + "proc-macro2",
4958 + "quote",
4959 + "syn 2.0.118",
4960 +]
4961 +
4962 +[[package]]
4963 +name = "phf_shared"
4964 +version = "0.13.1"
4965 +source = "registry+https://github.com/rust-lang/crates.io-index"
4966 +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
4967 +dependencies = [
4968 + "siphasher",
4969 +]
4970 +
4971 +[[package]]
4972 +name = "pin-project-lite"
4973 +version = "0.2.17"
4974 +source = "registry+https://github.com/rust-lang/crates.io-index"
4975 +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
4976 +
4977 +[[package]]
4978 +name = "piper"
4979 +version = "0.2.5"
4980 +source = "registry+https://github.com/rust-lang/crates.io-index"
4981 +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
4982 +dependencies = [
4983 + "atomic-waker",
4984 + "fastrand",
4985 + "futures-io",
4986 +]
4987 +
4988 +[[package]]
4989 +name = "pkg-config"
4990 +version = "0.3.33"
4991 +source = "registry+https://github.com/rust-lang/crates.io-index"
4992 +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
4993 +
4994 +[[package]]
4995 +name = "plain"
4996 +version = "0.2.3"
4997 +source = "registry+https://github.com/rust-lang/crates.io-index"
4998 +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
4999 +

This file is too large to show in full.

src-tauri/Cargo.toml new
+62
@@ -0,0 +1,62 @@
1 +[package]
2 +name = "sitiai"
3 +version = "1.0.0"
4 +description = "Siti AI — private, on-device personal assistant."
5 +authors = ["Splitfire AB <support@getsiti.5mb.app>"]
6 +edition = "2021"
7 +license = "Apache-2.0"
8 +repository = "https://github.com/ondeinference/siti"
9 +homepage = "https://getsiti.5mb.app"
10 +
11 +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12 +
13 +[lib]
14 +# The `_lib` suffix may seem redundant but it is necessary
15 +# to make the lib name unique and wouldn't conflict with the bin name.
16 +# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
17 +name = "sitiai_lib"
18 +crate-type = ["staticlib", "cdylib", "rlib"]
19 +
20 +[build-dependencies]
21 +tauri-build = { version = "2", features = [] }
22 +
23 +[dependencies]
24 +tauri = { version = "2", features = [] }
25 +tauri-plugin-log = "2"
26 +tauri-plugin-opener = "2"
27 +serde = { version = "1", features = ["derive"] }
28 +serde_json = "1"
29 +log = "0.4"
30 +once_cell = "1"
31 +tokio = { version = "1", features = ["macros", "rt"] }
32 +# On-device LLM inference engine (Metal on Apple, CPU on Android).
33 +# Published crate: https://crates.io/crates/onde — this is the SDK Siti showcases.
34 +# For local SDK development, uncomment the [patch.crates-io] block at the bottom
35 +# of this file to build against a sibling `onde` checkout instead.
36 +onde = "1.1"
37 +
38 +# Apple App Group container lookup — used to redirect the HuggingFace model
39 +# cache into the shared `group.com.ondeinference.apps` container.
40 +[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
41 +objc2-foundation = { version = "0.3", features = [
42 + "NSFileManager",
43 + "NSBundle",
44 + "NSString",
45 +] }
46 +
47 +[profile.dev]
48 +# GGUF inference is unusably slow at opt-level 0; 2 keeps dev builds bearable.
49 +opt-level = 2
50 +incremental = false
51 +
52 +[profile.release]
53 +opt-level = 3
54 +lto = "thin"
55 +codegen-units = 1
56 +strip = "debuginfo"
57 +
58 +# ── Local Onde SDK development ─────────────────────────────────────────────────
59 +# Uncomment to build Siti against a local checkout of the Onde engine (sibling
60 +# directory) instead of the published crate. Keep this commented on `main`.
61 +# [patch.crates-io]
62 +# onde = { path = "../../onde" }
src-tauri/Entitlements-appstore.plist new
+18
@@ -0,0 +1,18 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 +<plist version="1.0">
4 + <dict>
5 + <key>com.apple.security.app-sandbox</key>
6 + <true/>
7 + <key>com.apple.application-identifier</key>
8 + <string>2TQF86ZACD.ai.siti.Siti</string>
9 + <key>com.apple.developer.team-identifier</key>
10 + <string>2TQF86ZACD</string>
11 + <key>com.apple.security.network.client</key>
12 + <true/>
13 + <key>com.apple.security.application-groups</key>
14 + <array>
15 + <string>group.com.ondeinference.apps</string>
16 + </array>
17 + </dict>
18 +</plist>
src-tauri/Entitlements.plist new
+14
@@ -0,0 +1,14 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 +<plist version="1.0">
4 + <dict>
5 + <key>com.apple.security.app-sandbox</key>
6 + <true/>
7 + <key>com.apple.security.network.client</key>
8 + <true/>
9 + <key>com.apple.security.application-groups</key>
10 + <array>
11 + <string>group.com.ondeinference.apps</string>
12 + </array>
13 + </dict>
14 +</plist>
src-tauri/Info.plist new
+10
@@ -0,0 +1,10 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 +<plist version="1.0">
4 +<dict>
5 + <key>ITSAppUsesNonExemptEncryption</key>
6 + <false/>
7 + <key>LSApplicationCategoryType</key>
8 + <string>public.app-category.productivity</string>
9 +</dict>
10 +</plist>
src-tauri/build.rs new
+3
@@ -0,0 +1,3 @@
1 +fn main() {
2 + tauri_build::build()
3 +}
src-tauri/capabilities/default.json new
+10
@@ -0,0 +1,10 @@
1 +{
2 + "$schema": "../gen/schemas/desktop-schema.json",
3 + "identifier": "default",
4 + "description": "Capability for the main window",
5 + "windows": ["main"],
6 + "permissions": [
7 + "core:default",
8 + "opener:default"
9 + ]
10 +}
src-tauri/gen-override/.keep
src-tauri/gen-override/android/app/build.gradle.kts new
+87
@@ -0,0 +1,87 @@
1 +import java.io.FileInputStream
2 +import java.util.Properties
3 +
4 +plugins {
5 + id("com.android.application")
6 + id("org.jetbrains.kotlin.android")
7 + id("rust")
8 +}
9 +
10 +val tauriProperties = Properties().apply {
11 + val propFile = file("tauri.properties")
12 + if (propFile.exists()) {
13 + propFile.inputStream().use { load(it) }
14 + }
15 +}
16 +
17 +android {
18 + compileSdk = 36
19 + namespace = "ai.siti.Siti"
20 + defaultConfig {
21 + manifestPlaceholders["usesCleartextTraffic"] = "false"
22 + applicationId = "ai.siti.Siti"
23 + minSdk = 24
24 + targetSdk = 36
25 + versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
26 + versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
27 + }
28 + signingConfigs {
29 + create("release") {
30 + val keystorePropertiesFile = rootProject.file("keystore.properties")
31 + val keystoreProperties = Properties()
32 + if (keystorePropertiesFile.exists()) {
33 + keystoreProperties.load(FileInputStream(keystorePropertiesFile))
34 + }
35 +
36 + keyAlias = keystoreProperties["keyAlias"] as String
37 + keyPassword = keystoreProperties["password"] as String
38 + storeFile = file(keystoreProperties["storeFile"] as String)
39 + storePassword = keystoreProperties["password"] as String
40 + }
41 + }
42 + buildTypes {
43 + getByName("debug") {
44 + manifestPlaceholders["usesCleartextTraffic"] = "true"
45 + isDebuggable = true
46 + isJniDebuggable = true
47 + isMinifyEnabled = false
48 + packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
49 + jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
50 + jniLibs.keepDebugSymbols.add("*/x86/*.so")
51 + jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
52 + }
53 + }
54 + getByName("release") {
55 + signingConfig = signingConfigs.getByName("release")
56 + isMinifyEnabled = true
57 + proguardFiles(
58 + *fileTree(".") { include("**/*.pro") }
59 + .plus(getDefaultProguardFile("proguard-android-optimize.txt"))
60 + .toList().toTypedArray()
61 + )
62 + }
63 + }
64 + kotlinOptions {
65 + jvmTarget = "1.8"
66 + }
67 + buildFeatures {
68 + buildConfig = true
69 + }
70 +}
71 +
72 +rust {
73 + rootDirRel = "../../../"
74 +}
75 +
76 +dependencies {
77 + implementation("androidx.webkit:webkit:1.14.0")
78 + implementation("androidx.appcompat:appcompat:1.7.1")
79 + implementation("androidx.activity:activity-ktx:1.10.1")
80 + implementation("com.google.android.material:material:1.12.0")
81 + implementation("androidx.lifecycle:lifecycle-process:2.10.0")
82 + testImplementation("junit:junit:4.13.2")
83 + androidTestImplementation("androidx.test.ext:junit:1.1.4")
84 + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
85 +}
86 +
87 +apply(from = "tauri.build.gradle.kts")
src-tauri/gen-override/android/keystore.properties.example new
+11
@@ -0,0 +1,11 @@
1 +# Android upload-keystore config for release (.aab) signing.
2 +#
3 +# Copy this file to `keystore.properties` (which is gitignored) and fill in your
4 +# own values, or export KEYSTORE_PATH before running scripts/build-android.sh —
5 +# the script patches `storeFile` at build time.
6 +#
7 +# NEVER commit the real keystore.properties or the .jks file.
8 +
9 +password=CHANGE_ME
10 +keyAlias=upload
11 +storeFile=/absolute/path/to/your/upload-keystore.jks
src-tauri/gen-override/apple/sitiai_iOS/sitiai_iOS.entitlements new
+12
@@ -0,0 +1,12 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 +<plist version="1.0">
4 + <dict>
5 + <key>com.apple.developer.kernel.increased-memory-limit</key>
6 + <true />
7 + <key>com.apple.security.application-groups</key>
8 + <array>
9 + <string>group.com.ondeinference.apps</string>
10 + </array>
11 + </dict>
12 +</plist>
src-tauri/icons/128x128.png
Binary files /dev/null and b/src-tauri/icons/128x128.png differ
src-tauri/icons/128x128@2x.png
Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ
src-tauri/icons/32x32.png
Binary files /dev/null and b/src-tauri/icons/32x32.png differ
src-tauri/icons/64x64.png
Binary files /dev/null and b/src-tauri/icons/64x64.png differ
src-tauri/icons/Square107x107Logo.png
Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ
src-tauri/icons/Square142x142Logo.png
Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ
src-tauri/icons/Square150x150Logo.png
Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ
src-tauri/icons/Square284x284Logo.png
Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ
src-tauri/icons/Square30x30Logo.png
Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ
src-tauri/icons/Square310x310Logo.png
Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ
src-tauri/icons/Square44x44Logo.png
Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ
src-tauri/icons/Square71x71Logo.png
Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ
src-tauri/icons/Square89x89Logo.png
Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ
src-tauri/icons/StoreLogo.png
Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ
src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml new
+5
@@ -0,0 +1,5 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3 + <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
4 + <background android:drawable="@color/ic_launcher_background"/>
5 +</adaptive-icon>
\ No newline at end of file
src-tauri/icons/android/mipmap-hdpi/ic_launcher.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png differ
src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png differ
src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png differ
src-tauri/icons/android/mipmap-mdpi/ic_launcher.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png differ
src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png differ
src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png differ
src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png differ
src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png differ
src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png differ
src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png differ
src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png differ
src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png differ
src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png differ
src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png differ
src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png
Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png differ
src-tauri/icons/android/values/ic_launcher_background.xml new
+4
@@ -0,0 +1,4 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<resources>
3 + <color name="ic_launcher_background">#fff</color>
4 +</resources>
\ No newline at end of file
src-tauri/icons/icon.icns
Binary files /dev/null and b/src-tauri/icons/icon.icns differ
src-tauri/icons/icon.ico
Binary files /dev/null and b/src-tauri/icons/icon.ico differ
src-tauri/icons/icon.png
Binary files /dev/null and b/src-tauri/icons/icon.png differ
src-tauri/src/app_info.rs new
+31
@@ -0,0 +1,31 @@
1 +//! Tauri command: `app_build_version`
2 +//!
3 +//! Exposes the native build number so Settings can show a real version string
4 +//! instead of a hardcoded one. The marketing version (`CFBundleShortVersionString`)
5 +//! comes from `tauri.conf.json` and is read on the JS side via the app plugin's
6 +//! `getVersion()`; the build number (`CFBundleVersion`) is Apple-specific and
7 +//! lives only in the shipped bundle, so it needs a native read.
8 +
9 +/// Return the app's build version (`CFBundleVersion`).
10 +///
11 +/// On Apple platforms this reads `CFBundleVersion` from the running bundle's
12 +/// Info.plist — the exact value App Store Connect and TestFlight show, stamped
13 +/// at build time by the release scripts as `YYYYMMDD.HHMM`. Returns `None` when
14 +/// it cannot be resolved so the UI can fall back to the marketing version alone.
15 +#[cfg(any(target_os = "ios", target_os = "macos"))]
16 +#[tauri::command]
17 +pub fn app_build_version() -> Option<String> {
18 + use objc2_foundation::{NSBundle, NSString};
19 +
20 + let bundle = NSBundle::mainBundle();
21 + let value = bundle.objectForInfoDictionaryKey(&NSString::from_str("CFBundleVersion"))?;
22 + let version = value.downcast::<NSString>().ok()?;
23 + Some(version.to_string())
24 +}
25 +
26 +/// Non-Apple builds have no `CFBundleVersion`; fall back to the marketing version.
27 +#[cfg(not(any(target_os = "ios", target_os = "macos")))]
28 +#[tauri::command]
29 +pub fn app_build_version() -> Option<String> {
30 + None
31 +}
src-tauri/src/chat/command_clear_history.rs new
+30
@@ -0,0 +1,30 @@
1 +//! Tauri command: `chat_clear_history`
2 +//!
3 +//! Clears the conversation history but keeps the model loaded in memory.
4 +
5 +use log::debug;
6 +
7 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
8 +use {super::ENGINE, log::info};
9 +
10 +/// Clear the conversation history but keep the model loaded.
11 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
12 +#[tauri::command]
13 +pub async fn chat_clear_history() -> Result<String, String> {
14 + if ENGINE.is_loaded().await {
15 + let count = ENGINE.history().await.len();
16 + ENGINE.clear_history().await;
17 + info!("Chat history cleared ({} turns removed).", count);
18 + Ok(format!("Chat history cleared ({} turns removed).", count))
19 + } else {
20 + debug!("No chat model loaded; nothing to clear.");
21 + Ok("No chat model loaded.".to_string())
22 + }
23 +}
24 +
25 +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
26 +#[tauri::command]
27 +pub async fn chat_clear_history() -> Result<String, String> {
28 + debug!("Siti chat is only supported on macOS, iOS, and Android for now.");
29 + Ok("No chat model loaded.".to_string())
30 +}
src-tauri/src/chat/command_get_history.rs new
+33
@@ -0,0 +1,33 @@
1 +//! Tauri command: `chat_get_history`
2 +//!
3 +//! Returns the full conversation history from the cached chat state.
4 +
5 +use super::ChatMessagePayload;
6 +
7 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
8 +use {super::ENGINE, onde::inference::ChatRole};
9 +
10 +/// Return the full conversation history.
11 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
12 +#[tauri::command]
13 +pub async fn chat_get_history() -> Vec<ChatMessagePayload> {
14 + ENGINE
15 + .history()
16 + .await
17 + .into_iter()
18 + .map(|msg| ChatMessagePayload {
19 + role: match msg.role {
20 + ChatRole::User => "user".to_string(),
21 + ChatRole::Assistant => "assistant".to_string(),
22 + ChatRole::System => "system".to_string(),
23 + },
24 + content: msg.content,
25 + })
26 + .collect()
27 +}
28 +
29 +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
30 +#[tauri::command]
31 +pub async fn chat_get_history() -> Vec<ChatMessagePayload> {
32 + Vec::new()
33 +}
src-tauri/src/chat/command_get_status.rs new
+41
@@ -0,0 +1,41 @@
1 +//! Tauri command: `chat_get_status`
2 +//!
3 +//! Returns the current status of the chat model and conversation.
4 +
5 +use super::ChatStatusResponse;
6 +
7 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
8 +use {super::ENGINE, crate::constants::ChatStatus, onde::inference::EngineStatus};
9 +
10 +/// Get the current status of the chat model and conversation.
11 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
12 +#[tauri::command]
13 +pub async fn chat_get_status() -> ChatStatusResponse {
14 + let info = ENGINE.info().await;
15 + let status = match info.status {
16 + EngineStatus::Ready => ChatStatus::Ready,
17 + EngineStatus::Unloaded => ChatStatus::Unloaded,
18 + EngineStatus::Loading => ChatStatus::Loading,
19 + EngineStatus::Generating => ChatStatus::Ready,
20 + EngineStatus::Error => ChatStatus::Error,
21 + };
22 + ChatStatusResponse {
23 + status,
24 + model_name: info.model_name,
25 + approx_memory: info.approx_memory,
26 + history_length: info.history_length as usize,
27 + }
28 +}
29 +
30 +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
31 +#[tauri::command]
32 +pub async fn chat_get_status() -> ChatStatusResponse {
33 + use crate::constants::ChatStatus;
34 +
35 + ChatStatusResponse {
36 + status: ChatStatus::Unloaded,
37 + model_name: None,
38 + approx_memory: None,
39 + history_length: 0,
40 + }
41 +}
src-tauri/src/chat/command_list_models.rs new
+71
@@ -0,0 +1,71 @@
1 +//! Tauri command: `chat_list_models`
2 +//!
3 +//! Returns the on-device models the user can switch between, sourced from the
4 +//! onde SDK's `SUPPORTED_MODEL_INFO` catalogue (filtered to the subset the
5 +//! engine can actually load), with the currently selected model flagged.
6 +
7 +use super::ModelInfo;
8 +
9 +/// Normalise a model description for Apple builds.
10 +///
11 +/// The shared onde model catalogue is cross-platform and also names sibling
12 +/// products, so its descriptions mention Android/Windows/Linux and "siGit Code".
13 +/// Apple's App Store Review Guideline 2.3.10 (Accurate Metadata) forbids
14 +/// referencing other platforms in an iOS/macOS app's UI or metadata, so on
15 +/// Apple builds we drop those mentions before the text reaches Settings. We
16 +/// also neutralise the cross-product reference, which would only confuse a Siti
17 +/// user. Apple's own platforms (iOS, macOS, tvOS) are kept.
18 +#[cfg(any(target_os = "macos", target_os = "ios"))]
19 +fn sanitize_description(desc: &str) -> String {
20 + desc.replace("iOS & Android", "iOS")
21 + .replace("macOS & Android", "macOS")
22 + .replace("macOS, Linux, and Windows", "macOS")
23 + .replace(", Linux, and Windows", "")
24 + .replace(", Linux and Windows", "")
25 + .replace(" & Android", "")
26 + .replace(" and Android", "")
27 + .replace("siGit Code", "coding tasks")
28 +}
29 +
30 +/// On Android the catalogue text is already accurate, so pass it through.
31 +#[cfg(target_os = "android")]
32 +fn sanitize_description(desc: &str) -> String {
33 + desc.to_string()
34 +}
35 +
36 +/// List the supported on-device models, smallest first, with the currently
37 +/// selected model marked. Used to populate the Settings model dropdown.
38 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
39 +#[tauri::command]
40 +pub async fn chat_list_models() -> Vec<ModelInfo> {
41 + use super::{config_for_model_id, SELECTED_MODEL};
42 +
43 + let selected = SELECTED_MODEL.lock().map(|g| g.clone()).unwrap_or_default();
44 +
45 + let mut models: Vec<ModelInfo> = crate::inference::models::SUPPORTED_MODEL_INFO
46 + .iter()
47 + // Only include models the engine can build a loadable config for.
48 + .filter_map(|info| {
49 + config_for_model_id(info.id).map(|cfg| ModelInfo {
50 + id: info.id.to_string(),
51 + name: info.name.to_string(),
52 + org: info.org.to_string(),
53 + description: sanitize_description(info.description),
54 + approx_memory: cfg.approx_memory,
55 + size_bytes: Some(info.expected_size_bytes),
56 + is_selected: info.id == selected,
57 + })
58 + })
59 + .collect();
60 +
61 + // Smallest (most device-friendly) first.
62 + models.sort_by_key(|m| m.size_bytes.unwrap_or(u64::MAX));
63 + models
64 +}
65 +
66 +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
67 +#[tauri::command]
68 +pub async fn chat_list_models() -> Vec<ModelInfo> {
69 + log::debug!("On-device models are only supported on macOS, iOS, and Android.");
70 + Vec::new()
71 +}
src-tauri/src/chat/command_load_model.rs new
+57
@@ -0,0 +1,57 @@
1 +//! Tauri command `chat_load_model`: loads the on-device chat model.
2 +//!
3 +//! Siti is fully offline: it loads the platform-default GGUF model directly.
4 +//! There is no operator-assigned model and no app credentials involved. The
5 +//! only network access is the one-time HuggingFace download of the model
6 +//! weights into the shared App Group cache.
7 +
8 +use tauri::AppHandle;
9 +
10 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
11 +use {
12 + super::{
13 + emit_chat_status, fmt_duration, model_config, sampling_config, CHAT_SYSTEM_PROMPT, ENGINE,
14 + },
15 + crate::constants::ChatStatus,
16 + log::{error, info},
17 +};
18 +
19 +/// Load the platform-default chat model into memory.
20 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
21 +#[tauri::command]
22 +pub async fn chat_load_model(app: AppHandle) -> Result<String, String> {
23 + let config = model_config();
24 + let display_name = config.display_name.clone();
25 + info!("chat_load_model: loading on-device model {}", display_name);
26 + emit_chat_status(&app, ChatStatus::Loading, Some(&display_name), None);
27 +
28 + let elapsed = ENGINE
29 + .load_gguf_model(
30 + config,
31 + Some(CHAT_SYSTEM_PROMPT.to_string()),
32 + Some(sampling_config()),
33 + )
34 + .await
35 + .map_err(|e| {
36 + let msg = format!("Failed to load chat model: {}", e);
37 + error!("{}", msg);
38 + emit_chat_status(&app, ChatStatus::Error, Some(&display_name), Some(&msg));
39 + msg
40 + })?;
41 +
42 + let msg = format!(
43 + "Chat model {} loaded in {}",
44 + display_name,
45 + fmt_duration(elapsed)
46 + );
47 + info!("{}", msg);
48 + emit_chat_status(&app, ChatStatus::Ready, Some(&display_name), None);
49 + Ok(msg)
50 +}
51 +
52 +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
53 +#[tauri::command]
54 +pub async fn chat_load_model(_app: AppHandle) -> Result<String, String> {
55 + log::debug!("Chat model is only supported on macOS, iOS, and Android for now.");
56 + Err("Chat model is only supported on macOS, iOS, and Android for now.".to_string())
57 +}
src-tauri/src/chat/command_send_message.rs new
+114
@@ -0,0 +1,114 @@
1 +//! Tauri command: `chat_send_message`
2 +//!
3 +//! Sends a user message to the on-device LLM and returns `Ok(())`
4 +//! immediately. The actual reply (or error) is delivered to the frontend
5 +//! via the `chat_reply` Tauri event emitted from a spawned async task.
6 +
7 +use super::*;
8 +use tauri::AppHandle;
9 +
10 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
11 +use {log::info, tauri::Emitter};
12 +
13 +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
14 +use log::debug;
15 +
16 +/// Send a user message to Siti and receive an assistant reply.
17 +///
18 +/// **Fire-and-return-immediately design**: this command returns `Ok(())`
19 +/// as soon as the inference task is spawned. The actual reply (or error)
20 +/// is delivered to the frontend via the `chat_reply` Tauri event
21 +/// (`EVENT_CHAT_REPLY`).
22 +///
23 +/// ## Why events instead of a blocking return?
24 +///
25 +/// On Android, the Tauri IPC bridge stores the JS-side `invoke()` resolve
26 +/// callback in a V8 `Map`. Android's WebView GC can collect that entry
27 +/// while the command is still running, especially on budget SoCs where
28 +/// GGUF inference takes 60-300 seconds. Emitting an event from a spawned
29 +/// task sidesteps the GC entirely.
30 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
31 +#[tauri::command]
32 +pub async fn chat_send_message(app: AppHandle, message: String) -> Result<(), String> {
33 + // ── 1. Lazy-load the model if not already cached ──────────────────────
34 + if !ENGINE.is_loaded().await {
35 + info!("No chat model loaded; lazy-loading default model.");
36 +
37 + let config = model_config();
38 + let display_name = config.display_name.clone();
39 +
40 + emit_chat_status(&app, ChatStatus::Loading, Some(&display_name), None);
41 +
42 + ENGINE
43 + .load_gguf_model(
44 + config,
45 + Some(CHAT_SYSTEM_PROMPT.to_string()),
46 + Some(sampling_config()),
47 + )
48 + .await
49 + .map_err(|e| {
50 + let msg = format!("Failed to lazy-load chat model: {}", e);
51 + error!("{}", msg);
52 + emit_chat_status(&app, ChatStatus::Error, Some(&display_name), Some(&msg));
53 + msg
54 + })?;
55 +
56 + emit_chat_status(&app, ChatStatus::Ready, Some(&display_name), None);
57 + }
58 +
59 + // ── 2. Spawn inference task: this command returns Ok(()) immediately ──
60 + let history_len = ENGINE.history().await.len();
61 + info!(
62 + "Chat inference DISPATCH history_turns={} message=\"{}\"",
63 + history_len, &message
64 + );
65 +
66 + tokio::task::spawn(async move {
67 + info!("Chat inference START");
68 +
69 + let result = ENGINE.send_message(&message).await;
70 +
71 + let payload = match result {
72 + Ok(inference_result) => {
73 + info!(
74 + "Chat inference END ({}) reply: \"{}\"",
75 + inference_result.duration_display,
76 + if inference_result.text.len() > 100 {
77 + format!("{}...", &inference_result.text[..100])
78 + } else {
79 + inference_result.text.clone()
80 + }
81 + );
82 +
83 + ChatReplyPayload {
84 + reply: Some(inference_result.text),
85 + duration: Some(inference_result.duration_display),
86 + error: None,
87 + }
88 + }
89 + Err(e) => {
90 + let msg = format!("Chat inference FAILED: {}", e);
91 + error!("{}", msg);
92 + emit_chat_status(&app, ChatStatus::Error, None, Some(&msg));
93 + ChatReplyPayload {
94 + reply: None,
95 + duration: None,
96 + error: Some(msg),
97 + }
98 + }
99 + };
100 +
101 + if let Err(e) = app.emit(EVENT_CHAT_REPLY, &payload) {
102 + error!("Failed to emit chat_reply event: {:?}", e);
103 + }
104 + });
105 +
106 + Ok(())
107 +}
108 +
109 +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
110 +#[tauri::command]
111 +pub async fn chat_send_message(_app: AppHandle, _message: String) -> Result<(), String> {
112 + debug!("Siti chat is only supported on macOS, iOS, and Android for now.");
113 + Err("Siti chat is only supported on macOS, iOS, and Android for now.".to_string())
114 +}
src-tauri/src/chat/command_set_model.rs new
+86
@@ -0,0 +1,86 @@
1 +//! Tauri command `chat_set_model`: switches the active on-device model.
2 +//!
3 +//! Updates the selected model, unloads any currently loaded model, and loads
4 +//! the new one. Like `chat_send_message`, this returns `Ok(())` as soon as the
5 +//! work is dispatched: switching can trigger a multi-gigabyte HuggingFace
6 +//! download, so progress and completion are reported via the
7 +//! `chat_status_changed` event (Loading → Ready, or Error) rather than a
8 +//! blocking return that the WebView could GC out from under us.
9 +
10 +use tauri::AppHandle;
11 +
12 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
13 +use {
14 + super::{
15 + config_for_model_id, emit_chat_status, fmt_duration, sampling_config, CHAT_SYSTEM_PROMPT,
16 + ENGINE, SELECTED_MODEL,
17 + },
18 + crate::constants::ChatStatus,
19 + log::{error, info},
20 +};
21 +
22 +/// Switch Siti to the model identified by `model_id` (a HuggingFace repo id
23 +/// from `chat_list_models`). The new model is loaded asynchronously; watch the
24 +/// `chat_status_changed` event for `Loading` → `Ready`/`Error`.
25 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
26 +#[tauri::command]
27 +pub async fn chat_set_model(app: AppHandle, model_id: String) -> Result<(), String> {
28 + // ── 1. Validate & resolve the requested model ────────────────────────
29 + let config = config_for_model_id(&model_id)
30 + .ok_or_else(|| format!("Unknown or unsupported model id: {model_id}"))?;
31 + let display_name = config.display_name.clone();
32 +
33 + // ── 2. Persist the selection so model_config()/sampling_config() agree ─
34 + // NB: never hold the std Mutex guard across an `.await` (it would make this
35 + // command future `!Send`), so resolve the loaded-state first.
36 + let already_loaded = ENGINE.is_loaded().await;
37 + {
38 + let mut selected = SELECTED_MODEL
39 + .lock()
40 + .map_err(|e| format!("Failed to update selected model: {e}"))?;
41 + if *selected == model_id && already_loaded {
42 + info!("Model {display_name} is already selected and loaded; nothing to do.");
43 + return Ok(());
44 + }
45 + *selected = model_id.clone();
46 + }
47 +
48 + info!("chat_set_model: switching to {display_name} ({model_id})");
49 + emit_chat_status(&app, ChatStatus::Loading, Some(&display_name), None);
50 +
51 + // ── 3. Swap models off-thread; report progress via events ────────────
52 + tokio::task::spawn(async move {
53 + if ENGINE.is_loaded().await {
54 + ENGINE.unload_model().await;
55 + }
56 +
57 + let result = ENGINE
58 + .load_gguf_model(
59 + config,
60 + Some(CHAT_SYSTEM_PROMPT.to_string()),
61 + Some(sampling_config()),
62 + )
63 + .await;
64 +
65 + match result {
66 + Ok(elapsed) => {
67 + info!("Model {display_name} loaded in {}", fmt_duration(elapsed));
68 + emit_chat_status(&app, ChatStatus::Ready, Some(&display_name), None);
69 + }
70 + Err(e) => {
71 + let msg = format!("Failed to load model {display_name}: {e}");
72 + error!("{msg}");
73 + emit_chat_status(&app, ChatStatus::Error, Some(&display_name), Some(&msg));
74 + }
75 + }
76 + });
77 +
78 + Ok(())
79 +}
80 +
81 +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
82 +#[tauri::command]
83 +pub async fn chat_set_model(_app: AppHandle, _model_id: String) -> Result<(), String> {
84 + log::debug!("Model switching is only supported on macOS, iOS, and Android.");
85 + Err("Model switching is only supported on macOS, iOS, and Android for now.".to_string())
86 +}
src-tauri/src/chat/command_unload_model.rs new
+33
@@ -0,0 +1,33 @@
1 +//! Tauri command `chat_unload_model`: frees the chat model from memory.
2 +
3 +use {log::debug, tauri::AppHandle};
4 +
5 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
6 +use {
7 + super::{emit_chat_status, model_config, ENGINE},
8 + crate::constants::ChatStatus,
9 + log::info,
10 +};
11 +
12 +/// Unload the chat model from memory to free resources.
13 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
14 +#[tauri::command]
15 +pub async fn chat_unload_model(app: AppHandle) -> Result<String, String> {
16 + if ENGINE.is_loaded().await {
17 + let display_name = model_config().display_name;
18 + ENGINE.unload_model().await;
19 + info!("Chat model unloaded: {}", display_name);
20 + emit_chat_status(&app, ChatStatus::Unloaded, None, None);
21 + Ok(format!("Chat model {} unloaded.", display_name))
22 + } else {
23 + debug!("No chat model was loaded.");
24 + Ok("No chat model was loaded.".to_string())
25 + }
26 +}
27 +
28 +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
29 +#[tauri::command]
30 +pub async fn chat_unload_model(_app: AppHandle) -> Result<String, String> {
31 + debug!("Chat model is only supported on macOS, iOS, and Android.");
32 + Ok("No chat model was loaded.".to_string())
33 +}
src-tauri/src/chat/mod.rs new
+273
@@ -0,0 +1,273 @@
1 +//! Chat feature module: fully on-device LLM inference for Siti AI.
2 +//!
3 +//! Each Tauri command lives in its own file (`command_*.rs`). Shared types,
4 +//! the cached engine instance, model/sampling configuration, and helpers are
5 +//! kept here in `mod.rs` so every command file can `use super::*`.
6 +//!
7 +//! Model loading, inference, and history management are delegated to
8 +//! [`onde::inference::ChatEngine`], the shared on-device inference engine
9 +//! from the `onde` crate. Siti runs the engine **fully offline**: it loads
10 +//! the platform-default GGUF model directly (no operator-assigned model, no
11 +//! app credentials) and pulse telemetry is disabled during app setup.
12 +
13 +// ── Command submodules (one Tauri command per file) ──────────────────────────
14 +
15 +pub mod command_clear_history;
16 +pub mod command_get_history;
17 +pub mod command_get_status;
18 +pub mod command_list_models;
19 +pub mod command_load_model;
20 +pub mod command_send_message;
21 +pub mod command_set_model;
22 +pub mod command_unload_model;
23 +
24 +// ── Re-exports so lib.rs can pull in commands with a flat path ───────────────
25 +
26 +pub use command_clear_history::chat_clear_history;
27 +pub use command_get_history::chat_get_history;
28 +pub use command_get_status::chat_get_status;
29 +pub use command_list_models::chat_list_models;
30 +pub use command_load_model::chat_load_model;
31 +pub use command_send_message::chat_send_message;
32 +pub use command_set_model::chat_set_model;
33 +pub use command_unload_model::chat_unload_model;
34 +
35 +// ── Imports shared with submodules via `super::` ─────────────────────────────
36 +
37 +use {
38 + crate::constants::ChatStatus,
39 + serde::{Deserialize, Serialize},
40 +};
41 +
42 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
43 +use {
44 + crate::events::{EVENT_CHAT_REPLY, EVENT_CHAT_STATUS_CHANGED},
45 + log::error,
46 + once_cell::sync::Lazy,
47 + onde::inference::{ChatEngine, GgufModelConfig, SamplingConfig},
48 + tauri::{AppHandle, Emitter},
49 +};
50 +
51 +// ── Response / payload types ─────────────────────────────────────────────────
52 +
53 +/// Payload emitted with the `chat_status_changed` event.
54 +#[derive(Debug, Clone, Serialize, Deserialize)]
55 +pub struct ChatStatusPayload {
56 + pub status: ChatStatus,
57 + pub model_name: Option<String>,
58 + pub error: Option<String>,
59 +}
60 +
61 +/// A single message in the conversation, returned to the frontend.
62 +#[derive(Debug, Clone, Serialize, Deserialize)]
63 +pub struct ChatMessagePayload {
64 + pub role: String,
65 + pub content: String,
66 +}
67 +
68 +/// Event payload emitted as `chat_reply` once inference completes.
69 +///
70 +/// Using an event instead of a blocking command return prevents the WebView
71 +/// from garbage-collecting the JS invoke-callback before the slow on-device
72 +/// inference finishes.
73 +#[derive(Debug, Clone, Serialize, Deserialize)]
74 +pub struct ChatReplyPayload {
75 + /// The assistant's reply text, or `None` on error.
76 + pub reply: Option<String>,
77 + /// Human-readable inference duration, or `None` on error.
78 + pub duration: Option<String>,
79 + /// Error message if inference failed, or `None` on success.
80 + pub error: Option<String>,
81 +}
82 +
83 +/// Response returned by `chat_get_status`.
84 +#[derive(Debug, Clone, Serialize, Deserialize)]
85 +pub struct ChatStatusResponse {
86 + pub status: ChatStatus,
87 + pub model_name: Option<String>,
88 + pub approx_memory: Option<String>,
89 + pub history_length: usize,
90 +}
91 +
92 +/// A selectable on-device model, returned by `chat_list_models` and rendered
93 +/// in the Settings model dropdown.
94 +#[derive(Debug, Clone, Serialize, Deserialize)]
95 +pub struct ModelInfo {
96 + /// HuggingFace repo id; the value passed back to `chat_set_model`.
97 + pub id: String,
98 + /// Human-friendly display name, e.g. `"Qwen 2.5 1.5B (GGUF)"`.
99 + pub name: String,
100 + /// Publisher / organisation, e.g. `"Qwen / Alibaba"`.
101 + pub org: String,
102 + /// Short description of the model's purpose and footprint.
103 + pub description: String,
104 + /// Approximate memory footprint, e.g. `"~941 MB (GGUF Q4_K_M)"`.
105 + pub approx_memory: String,
106 + /// Approximate on-disk download size in bytes (for display).
107 + pub size_bytes: Option<u64>,
108 + /// Whether this model is the one currently selected for Siti.
109 + pub is_selected: bool,
110 +}
111 +
112 +// ── Shared ChatEngine instance ───────────────────────────────────────────────
113 +//
114 +// `ChatEngine` from `onde` handles all model lifecycle, history, and inference.
115 +// It is `Send + Sync` and manages its own internal mutex.
116 +
117 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
118 +pub(crate) static ENGINE: Lazy<ChatEngine> = Lazy::new(ChatEngine::new);
119 +
120 +// ── Model selection & configuration ──────────────────────────────────────────
121 +//
122 +// The user can switch models from Settings (`chat_set_model`). The chosen
123 +// HuggingFace repo id is held in `SELECTED_MODEL` and drives both the model
124 +// config and the sampling config. When nothing has been chosen yet, Siti
125 +// falls back to a general-purpose chat model appropriate for the platform:
126 +// - iOS / Android → Qwen 2.5 1.5B (~941 MB, fits mobile memory budgets)
127 +// - macOS → Qwen 2.5 3B (~1.93 GB, more headroom on desktop)
128 +//
129 +// `config_for_model_id` handles per-platform details (e.g. `tok_model_id` on
130 +// Android) via the onde `GgufModelConfig` constructors.
131 +
132 +/// The model id currently selected for Siti. Defaults to the platform-
133 +/// appropriate general chat model; updated by `chat_set_model`.
134 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
135 +pub(crate) static SELECTED_MODEL: Lazy<std::sync::Mutex<String>> =
136 + Lazy::new(|| std::sync::Mutex::new(siti_default_config().model_id));
137 +
138 +/// Siti's default general-purpose chat model for the current platform.
139 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
140 +pub(crate) fn siti_default_config() -> GgufModelConfig {
141 + if cfg!(any(target_os = "ios", target_os = "android")) {
142 + GgufModelConfig::qwen25_1_5b()
143 + } else {
144 + GgufModelConfig::qwen25_3b()
145 + }
146 +}
147 +
148 +/// Map a HuggingFace repo id to a loadable [`GgufModelConfig`], or `None` if
149 +/// the id is not a model the onde engine knows how to load.
150 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
151 +pub(crate) fn config_for_model_id(id: &str) -> Option<GgufModelConfig> {
152 + use crate::inference::models as m;
153 + let cfg = if id == m::BARTOWSKI_QWEN25_0_5B_INSTRUCT_GGUF {
154 + GgufModelConfig::qwen25_0_5b()
155 + } else if id == m::BARTOWSKI_QWEN25_1_5B_INSTRUCT_GGUF {
156 + GgufModelConfig::qwen25_1_5b()
157 + } else if id == m::BARTOWSKI_QWEN25_3B_INSTRUCT_GGUF {
158 + GgufModelConfig::qwen25_3b()
159 + } else if id == m::BARTOWSKI_QWEN25_CODER_1_5B_INSTRUCT_GGUF {
160 + GgufModelConfig::qwen25_coder_1_5b()
161 + } else if id == m::BARTOWSKI_QWEN25_CODER_3B_INSTRUCT_GGUF {
162 + GgufModelConfig::qwen25_coder_3b()
163 + } else if id == m::BARTOWSKI_QWEN25_CODER_7B_INSTRUCT_GGUF {
164 + GgufModelConfig::qwen25_coder_7b()
165 + } else if id == m::BARTOWSKI_QWEN3_1_7B_GGUF {
166 + GgufModelConfig::qwen3_1_7b()
167 + } else if id == m::BARTOWSKI_QWEN3_4B_GGUF {
168 + GgufModelConfig::qwen3_4b()
169 + } else if id == m::BARTOWSKI_QWEN3_8B_GGUF {
170 + GgufModelConfig::qwen3_8b()
171 + } else if id == m::BARTOWSKI_QWEN3_14B_GGUF {
172 + GgufModelConfig::qwen3_14b()
173 + } else if id == m::THEBLOKE_DEEPSEEK_CODER_6_7B_INSTRUCT_GGUF {
174 + GgufModelConfig::deepseek_coder_6_7b()
175 + } else {
176 + return None;
177 + };
178 + Some(cfg)
179 +}
180 +
181 +/// Return the config for the currently selected model (falling back to the
182 +/// platform default if the selection is somehow unknown).
183 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
184 +pub(crate) fn model_config() -> GgufModelConfig {
185 + let id = SELECTED_MODEL
186 + .lock()
187 + .map(|g| g.clone())
188 + .unwrap_or_default();
189 + config_for_model_id(&id).unwrap_or_else(siti_default_config)
190 +}
191 +
192 +/// Return the sampling config for the currently selected model.
193 +///
194 +/// iOS and Android use conservative settings to bound KV cache memory and
195 +/// keep worst-case latency reasonable on slower SoCs. Qwen 3 models get a
196 +/// larger `max_tokens` budget because their `<think>…</think>` block can
197 +/// consume hundreds of tokens before the visible reply begins, and too small
198 +/// a budget yields empty replies.
199 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
200 +pub(crate) fn sampling_config() -> SamplingConfig {
201 + let id = SELECTED_MODEL
202 + .lock()
203 + .map(|g| g.clone())
204 + .unwrap_or_default();
205 + let is_qwen3 = id.contains("Qwen3") || id.contains("Qwen_Qwen3");
206 + let mobile = cfg!(any(target_os = "ios", target_os = "android"));
207 +
208 + let max_tokens = match (mobile, is_qwen3) {
209 + (true, true) => 1024,
210 + (true, false) => 256,
211 + (false, true) => 4096,
212 + (false, false) => 512,
213 + };
214 +
215 + if mobile {
216 + SamplingConfig {
217 + temperature: Some(0.7),
218 + top_p: Some(0.95),
219 + max_tokens: Some(max_tokens),
220 + ..SamplingConfig::mobile()
221 + }
222 + } else {
223 + SamplingConfig {
224 + temperature: Some(0.7),
225 + top_p: Some(0.95),
226 + max_tokens: Some(max_tokens),
227 + ..SamplingConfig::default()
228 + }
229 + }
230 +}
231 +
232 +/// Default system prompt for Siti, the private on-device personal assistant.
233 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
234 +pub(crate) const CHAT_SYSTEM_PROMPT: &str = r#"You are Siti, a friendly and capable personal assistant that runs entirely on the user's device. Nothing the user says ever leaves the device.
235 +
236 +You help with everyday things: answering questions, brainstorming, drafting and editing text, summarising, explaining ideas in plain terms, and planning.
237 +
238 +Keep replies clear and concise, and prefer plain language. If a request is ambiguous, ask one short question instead of guessing. Use a short list only when it genuinely helps, and go easy on emoji.
239 +
240 +You run locally, so you may not know about very recent events. When you are not sure, say so instead of inventing an answer."#;
241 +
242 +// ── Helpers ──────────────────────────────────────────────────────────────────
243 +
244 +/// Format a `Duration` as `Xm Ys` or just `Ys` when under a minute.
245 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
246 +pub(crate) fn fmt_duration(d: std::time::Duration) -> String {
247 + let total_secs = d.as_secs_f64();
248 + let mins = (total_secs / 60.0).floor() as u64;
249 + let secs = total_secs - (mins as f64 * 60.0);
250 + if mins > 0 {
251 + format!("{}m {:.1}s", mins, secs)
252 + } else {
253 + format!("{:.1}s", secs)
254 + }
255 +}
256 +
257 +/// Emit a chat status event to the frontend.
258 +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
259 +pub(crate) fn emit_chat_status(
260 + app: &AppHandle,
261 + status: ChatStatus,
262 + model_name: Option<&str>,
263 + error_msg: Option<&str>,
264 +) {
265 + let payload = ChatStatusPayload {
266 + status,
267 + model_name: model_name.map(|s| s.to_string()),
268 + error: error_msg.map(|s| s.to_string()),
269 + };
270 + if let Err(e) = app.emit(EVENT_CHAT_STATUS_CHANGED, &payload) {
271 + error!("Failed to emit chat status event: {:?}", e);
272 + }
273 +}
src-tauri/src/constants.rs new
+18
@@ -0,0 +1,18 @@
1 +use serde::{Deserialize, Serialize};
2 +
3 +/// Chat model status, mirrored to the frontend via the
4 +/// `chat_status_changed` event and the `chat_get_status` command.
5 +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
6 +#[serde(rename_all = "snake_case")]
7 +pub enum ChatStatus {
8 + /// No model is loaded.
9 + Unloaded,
10 + /// Model is currently being downloaded / loaded into memory.
11 + Loading,
12 + /// Model is loaded and ready to chat.
13 + Ready,
14 + /// Model is actively generating a response.
15 + Generating,
16 + /// Model failed to load.
17 + Error,
18 +}
src-tauri/src/events.rs new
+7
@@ -0,0 +1,7 @@
1 +//! Tauri event names emitted from Rust to the frontend.
2 +
3 +/// Emitted whenever the chat model lifecycle status changes.
4 +pub const EVENT_CHAT_STATUS_CHANGED: &str = "chat_status_changed";
5 +
6 +/// Emitted once an inference request completes (or fails).
7 +pub const EVENT_CHAT_REPLY: &str = "chat_reply";
src-tauri/src/lib.rs new
+58
@@ -0,0 +1,58 @@
1 +pub mod app_info;
2 +pub mod chat;
3 +pub mod constants;
4 +pub mod events;
5 +pub mod setup;
6 +
7 +/// Re-export onde's inference helpers so `crate::inference::*` paths work.
8 +pub mod inference {
9 + pub use onde::inference::{models, token};
10 +}
11 +
12 +use app_info::app_build_version;
13 +use chat::{
14 + chat_clear_history, chat_get_history, chat_get_status, chat_list_models, chat_load_model,
15 + chat_send_message, chat_set_model, chat_unload_model,
16 +};
17 +
18 +#[cfg_attr(mobile, tauri::mobile_entry_point)]
19 +pub fn run() {
20 + tauri::Builder::default()
21 + .plugin(
22 + tauri_plugin_log::Builder::new()
23 + .level(log::LevelFilter::Info)
24 + // Quiet the chattiest inference/download dependencies.
25 + .level_for("tokenizers", log::LevelFilter::Warn)
26 + .level_for("hf_hub", log::LevelFilter::Warn)
27 + .level_for("rustls", log::LevelFilter::Warn)
28 + .level_for("hyper", log::LevelFilter::Warn)
29 + .level_for("candle_core", log::LevelFilter::Warn)
30 + .level_for("candle_nn", log::LevelFilter::Warn)
31 + .target(tauri_plugin_log::Target::new(
32 + tauri_plugin_log::TargetKind::Stdout,
33 + ))
34 + .build(),
35 + )
36 + .plugin(tauri_plugin_opener::init())
37 + .setup(|app| {
38 + // Disable telemetry + redirect the model cache into the shared
39 + // App Group container. Must run before any onde / hf-hub code.
40 + setup::setup(app)?;
41 + Ok(())
42 + })
43 + .invoke_handler(tauri::generate_handler![
44 + // Chat commands: fully on-device LLM inference
45 + chat_load_model,
46 + chat_unload_model,
47 + chat_get_status,
48 + chat_send_message,
49 + chat_clear_history,
50 + chat_get_history,
51 + chat_list_models,
52 + chat_set_model,
53 + // App metadata
54 + app_build_version,
55 + ])
56 + .run(tauri::generate_context!())
57 + .expect("error while running tauri application");
58 +}
src-tauri/src/main.rs new
+6
@@ -0,0 +1,6 @@
1 +// Prevents additional console window on Windows in release, DO NOT REMOVE!!
2 +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
3 +
4 +fn main() {
5 + sitiai_lib::run()
6 +}
src-tauri/src/setup/mod.rs new
+23
@@ -0,0 +1,23 @@
1 +pub mod setup_application_filesystem;
2 +
3 +use setup_application_filesystem::setup_application_filesystem;
4 +use tauri::App;
5 +
6 +/// Run all setup steps. Called from `tauri::Builder::setup()`.
7 +///
8 +/// Both steps must run **before** any `onde` / `hf-hub` / `mistral.rs` code,
9 +/// i.e. before the chat `ENGINE` lazily initialises.
10 +pub fn setup(app: &App) -> Result<(), Box<dyn std::error::Error>> {
11 + // Siti runs fully offline. Disable onde's pulse telemetry so no usage
12 + // beacons are ever sent, regardless of whether GresIQ credentials happen
13 + // to be embedded in the onde build. `ChatEngine` reads this env var when
14 + // it lazily initialises its (optional) pulse client.
15 + std::env::set_var("ONDE_DISABLE_PULSE", "1");
16 +
17 + // Redirect HF_HOME, HF_HUB_CACHE, and TMPDIR into the shared App Group
18 + // container so downloaded models are shared across Onde apps and remain
19 + // writable inside the iOS / macOS sandbox.
20 + setup_application_filesystem(app)?;
21 +
22 + Ok(())
23 +}
src-tauri/src/setup/setup_application_filesystem.rs new
+164
@@ -0,0 +1,164 @@
1 +//! Application filesystem setup: redirects model/temp paths into the shared
2 +//! Apple App Group container so that all apps in `group.com.ondeinference.apps`
3 +//! share a single HuggingFace model cache.
4 +
5 +use log::debug;
6 +#[cfg(any(target_os = "ios", target_os = "macos"))]
7 +use objc2_foundation::ns_string;
8 +#[cfg(any(target_os = "ios", target_os = "macos"))]
9 +use objc2_foundation::NSFileManager;
10 +use std::path::PathBuf;
11 +use tauri::{App, Manager};
12 +
13 +/// Application filesystem setup.
14 +///
15 +/// On sandboxed platforms (iOS, macOS App Store / TestFlight) several default
16 +/// paths used by Rust crates (`~/.cache/…`, `/tmp`, etc.) are **not** writable.
17 +/// We must redirect all file I/O into directories inside the app container
18 +/// *before* any downstream code (hf-hub, mistral.rs, candle, …) runs.
19 +///
20 +/// 1. `HF_HOME` → controls where `hf-hub` stores downloaded models
21 +/// 2. `HF_HUB_CACHE` → explicit cache dir read by some mistral.rs code paths
22 +/// 3. `TMPDIR` → controls where `std::env::temp_dir()` points;
23 +/// mistral.rs writes Metal `.metallib` files there
24 +///
25 +/// The Apple App Group container (`group.com.ondeinference.apps`) is used as the
26 +/// base directory so that models are shared across all apps in the group,
27 +/// avoiding duplicate downloads.
28 +///
29 +/// On non-sandboxed macOS all of this is harmless; the directories are just
30 +/// as writable as their default counterparts, and if the group container is
31 +/// unavailable we fall back to `app_data_dir`.
32 +pub fn setup_application_filesystem(app: &App) -> Result<(), Box<dyn std::error::Error>> {
33 + let app_data_dir = app.path().app_data_dir()?;
34 +
35 + #[cfg(any(target_os = "ios", target_os = "macos"))]
36 + let container_dir: PathBuf = {
37 + let fs = NSFileManager::defaultManager();
38 + let group_identifier = ns_string!("group.com.ondeinference.apps");
39 + let group_url = fs.containerURLForSecurityApplicationGroupIdentifier(group_identifier);
40 +
41 + match group_url {
42 + Some(url) => {
43 + let path_str = url.path().map(|p| p.to_string()).unwrap_or_default();
44 +
45 + if path_str.is_empty() {
46 + log::warn!("App Group container URL had no path, falling back to app_data_dir");
47 + app_data_dir.clone()
48 + } else {
49 + let p = PathBuf::from(&path_str);
50 + debug!("App Group container resolved: {}", p.display());
51 + p
52 + }
53 + }
54 + None => {
55 + log::warn!(
56 + "App Group container not available, falling back to app_data_dir: {}",
57 + app_data_dir.display()
58 + );
59 + app_data_dir.clone()
60 + }
61 + }
62 + };
63 +
64 + #[cfg(not(any(target_os = "ios", target_os = "macos")))]
65 + let container_dir: PathBuf = app_data_dir;
66 +
67 + // ── Redirect HF_HOME, HF_HUB_CACHE, and TMPDIR ───────────────────────
68 + setup_hf_home(&container_dir)?;
69 + setup_tmpdir(&container_dir)?;
70 +
71 + Ok(())
72 +}
73 +
74 +/// Point `HF_HOME` and `HF_HUB_CACHE` at `<container_dir>/models/` and
75 +/// ensure the directory (and its `hub/` child) exist.
76 +///
77 +/// This must run **before** any `hf-hub` API call so the crate picks up the
78 +/// environment variables instead of falling back to `~/.cache/huggingface/`.
79 +///
80 +/// `HF_HUB_CACHE` is read by `hf_hub_cache_dir()` inside mistral.rs's
81 +/// `get_paths_gguf!` macro. Some code paths check this env var directly
82 +/// instead of deriving it from `HF_HOME`, so we set both to be safe.
83 +///
84 +/// Using the App Group container means the model cache is shared across all
85 +/// apps in the `group.com.ondeinference.apps` group, avoiding duplicate downloads.
86 +fn setup_hf_home(container_dir: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
87 + let models_home = container_dir.join("models");
88 + let model_hub = models_home.join("hub");
89 +
90 + // Create both directories (idempotent).
91 + std::fs::create_dir_all(&model_hub).map_err(|e| {
92 + log::error!(
93 + "Failed to create model hub cache directory at {}: {}",
94 + model_hub.display(),
95 + e
96 + );
97 + e
98 + })?;
99 +
100 + // Only set if the user hasn't explicitly overridden it already.
101 + if std::env::var("HF_HOME").is_err() {
102 + std::env::set_var("HF_HOME", &models_home);
103 + log::info!("HF_HOME set to: {}", models_home.display());
104 + } else {
105 + log::debug!(
106 + "HF_HOME already set to: {}",
107 + std::env::var("HF_HOME").unwrap_or_default()
108 + );
109 + }
110 +
111 + if std::env::var("HF_HUB_CACHE").is_err() {
112 + std::env::set_var("HF_HUB_CACHE", &model_hub);
113 + log::info!("HF_HUB_CACHE set to: {}", model_hub.display());
114 + }
115 +
116 + Ok(())
117 +}
118 +
119 +/// Ensure `TMPDIR` points to a writable directory inside the app container.
120 +///
121 +/// On iOS, `std::env::temp_dir()` falls back to `/tmp` when the `TMPDIR`
122 +/// environment variable is not set. `/tmp` is **outside** the app sandbox
123 +/// and writes there fail with "Operation not permitted".
124 +///
125 +/// mistral.rs writes precompiled Metal shader libraries
126 +/// (`mistralrs_quant.metallib`, `mistralrs_paged_attention.metallib`) to
127 +/// `std::env::temp_dir()`, so this must be a writable location.
128 +///
129 +/// On non-sandboxed macOS this is harmless; the system temp directory is
130 +/// always writable, and if `TMPDIR` is already set we leave it alone.
131 +fn setup_tmpdir(container_dir: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
132 + // Check whether the current temp_dir is actually writable.
133 + let current_tmp = std::env::temp_dir();
134 + let probe_file = current_tmp.join(".siti_tmp_probe");
135 + let tmp_is_writable = std::fs::write(&probe_file, b"probe")
136 + .and_then(|_| std::fs::remove_file(&probe_file))
137 + .is_ok();
138 +
139 + if tmp_is_writable {
140 + log::debug!("TMPDIR is writable at: {}", current_tmp.display());
141 + return Ok(());
142 + }
143 +
144 + // The current temp directory is not writable (typical on iOS / sandboxed macOS).
145 + // Redirect to a `tmp/` folder inside the App Group container.
146 + let app_tmp = container_dir.join("tmp");
147 + std::fs::create_dir_all(&app_tmp).map_err(|e| {
148 + log::error!(
149 + "Failed to create app-local tmp directory at {}: {}",
150 + app_tmp.display(),
151 + e
152 + );
153 + e
154 + })?;
155 +
156 + std::env::set_var("TMPDIR", &app_tmp);
157 + log::info!(
158 + "TMPDIR redirected from {} to {} (original was not writable)",
159 + current_tmp.display(),
160 + app_tmp.display()
161 + );
162 +
163 + Ok(())
164 +}
src-tauri/tauri.conf.json new
+47
@@ -0,0 +1,47 @@
1 +{
2 + "$schema": "https://schema.tauri.app/config/2",
3 + "productName": "Siti AI",
4 + "version": "1.0.0",
5 + "identifier": "ai.siti.Siti",
6 + "build": {
7 + "beforeDevCommand": "pnpm dev",
8 + "devUrl": "http://localhost:1420",
9 + "beforeBuildCommand": "pnpm build",
10 + "frontendDist": "../dist"
11 + },
12 + "app": {
13 + "windows": [
14 + {
15 + "title": "Siti AI",
16 + "width": 800,
17 + "height": 600
18 + }
19 + ],
20 + "security": {
21 + "csp": null
22 + }
23 + },
24 + "bundle": {
25 + "active": true,
26 + "targets": "all",
27 + "icon": [
28 + "icons/32x32.png",
29 + "icons/128x128.png",
30 + "icons/128x128@2x.png",
31 + "icons/icon.icns",
32 + "icons/icon.ico"
33 + ],
34 + "macOS": {
35 + "entitlements": "./Entitlements.plist",
36 + "exceptionDomain": "",
37 + "frameworks": [],
38 + "providerShortName": null,
39 + "signingIdentity": null,
40 + "minimumSystemVersion": "14.0"
41 + },
42 + "iOS": {
43 + "developmentTeam": "2TQF86ZACD",
44 + "minimumSystemVersion": "16.0"
45 + }
46 + }
47 +}
src-tauri/tauri.prod-android.conf.json new
+5
@@ -0,0 +1,5 @@
1 +{
2 + "productName": "Siti AI",
3 + "version": "1.0.0",
4 + "identifier": "ai.siti.Siti"
5 +}
src-tauri/tauri.prod-ios.conf.json new
+5
@@ -0,0 +1,5 @@
1 +{
2 + "productName": "Siti AI",
3 + "version": "1.0.0",
4 + "identifier": "ai.siti.Siti"
5 +}
src-tauri/tauri.prod-macos-appstore.conf.json.example new
+16
@@ -0,0 +1,16 @@
1 +{
2 + "productName": "Siti AI",
3 + "version": "1.0.0",
4 + "identifier": "ai.siti.Siti",
5 + "bundle": {
6 + "icon": ["icons/icon.icns"],
7 + "macOS": {
8 + "bundleVersion": "1.0.0",
9 + "entitlements": "./Entitlements-appstore.plist",
10 + "signingIdentity": "YOUR_MAC_APP_STORE_DISTRIBUTION_CERT_SHA1",
11 + "files": {
12 + "embedded.provisionprofile": "/absolute/path/to/your/AppStore_Distribution.provisionprofile"
13 + }
14 + }
15 + }
16 +}
src-tauri/tauri.prod-macos.conf.json.example new
+12
@@ -0,0 +1,12 @@
1 +{
2 + "productName": "Siti AI",
3 + "version": "1.0.0",
4 + "identifier": "ai.siti.Siti",
5 + "bundle": {
6 + "icon": ["icons/icon.icns"],
7 + "macOS": {
8 + "entitlements": "./Entitlements.plist",
9 + "signingIdentity": "YOUR_DEVELOPER_ID_APPLICATION_CERT_SHA1"
10 + }
11 + }
12 +}
src/App.css new
+557
@@ -0,0 +1,557 @@
1 +*, *::before, *::after {
2 + box-sizing: border-box;
3 + margin: 0;
4 + padding: 0;
5 +}
6 +
7 +:root {
8 + --bg: #fafafa;
9 + --surface: #ffffff;
10 + --border: #e8e8e8;
11 + --text-primary: #1a1a1a;
12 + --text-secondary: #8a8a8a;
13 + --accent: #2d2d2d;
14 + --accent-hover: #1a1a1a;
15 + --user-bubble: #1a1a1a;
16 + --user-bubble-text: #ffffff;
17 + --ai-bubble: #f0f0f0;
18 + --ai-bubble-text: #1a1a1a;
19 + --input-bg: #f5f5f5;
20 + --radius: 18px;
21 + --radius-sm: 8px;
22 +
23 + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
24 + font-size: 15px;
25 + line-height: 1.5;
26 + -webkit-font-smoothing: antialiased;
27 +
28 + /* Adapt native form controls and the WebView backdrop to the system theme. */
29 + color-scheme: light dark;
30 +}
31 +
32 +@media (prefers-color-scheme: dark) {
33 + :root {
34 + --bg: #111111;
35 + --surface: #1c1c1c;
36 + --border: #2a2a2a;
37 + --text-primary: #f0f0f0;
38 + --text-secondary: #666666;
39 + --accent: #e0e0e0;
40 + --accent-hover: #ffffff;
41 + --user-bubble: #e8e8e8;
42 + --user-bubble-text: #111111;
43 + --ai-bubble: #242424;
44 + --ai-bubble-text: #f0f0f0;
45 + --input-bg: #1c1c1c;
46 + }
47 +}
48 +
49 +html, body {
50 + height: 100%;
51 + width: 100%;
52 + /* Paint the theme colour across the whole WebView, including the regions
53 + behind the status bar and home indicator, so the safe areas blend with the
54 + app instead of showing the native (dark) backdrop. */
55 + background: var(--bg);
56 + overscroll-behavior: none;
57 + -webkit-tap-highlight-color: transparent;
58 + touch-action: manipulation;
59 +}
60 +
61 +#root {
62 + height: 100%;
63 +}
64 +
65 +.app {
66 + display: flex;
67 + flex-direction: column;
68 + height: 100dvh;
69 + background: var(--bg);
70 + color: var(--text-primary);
71 + user-select: none;
72 +}
73 +
74 +/* Header */
75 +.header {
76 + display: flex;
77 + align-items: center;
78 + justify-content: space-between;
79 + /* Push the title clear of the status bar; keep the original 20px on devices
80 + with no top inset. Honour left/right insets in landscape / on iPad. */
81 + padding: calc(env(safe-area-inset-top) + 20px)
82 + max(24px, env(safe-area-inset-right)) 16px max(24px, env(safe-area-inset-left));
83 + border-bottom: 1px solid var(--border);
84 + background: var(--bg);
85 + flex-shrink: 0;
86 +}
87 +
88 +.header-title {
89 + display: flex;
90 + flex-direction: column;
91 +}
92 +
93 +.header-title h1 {
94 + font-size: 17px;
95 + font-weight: 600;
96 + letter-spacing: -0.2px;
97 + color: var(--text-primary);
98 +}
99 +
100 +.header-subtitle {
101 + font-size: 12px;
102 + color: var(--text-secondary);
103 + letter-spacing: 0.2px;
104 +}
105 +
106 +.status-dot {
107 + width: 8px;
108 + height: 8px;
109 + border-radius: 50%;
110 + background: #4caf50;
111 + flex-shrink: 0;
112 +}
113 +
114 +.status-dot.thinking {
115 + background: #ff9800;
116 + animation: pulse 1.2s ease-in-out infinite;
117 +}
118 +
119 +@keyframes pulse {
120 + 0%, 100% { opacity: 1; }
121 + 50% { opacity: 0.4; }
122 +}
123 +
124 +/* Messages area */
125 +.messages {
126 + flex: 1;
127 + overflow-y: scroll;
128 + -webkit-overflow-scrolling: touch;
129 + overscroll-behavior-y: contain;
130 + padding: 24px 0;
131 + display: flex;
132 + flex-direction: column;
133 + gap: 2px;
134 + scrollbar-width: none;
135 +}
136 +
137 +.messages::-webkit-scrollbar {
138 + display: none;
139 +}
140 +
141 +/* Empty state */
142 +.empty-state {
143 + flex: 1;
144 + display: flex;
145 + flex-direction: column;
146 + align-items: center;
147 + justify-content: center;
148 + gap: 8px;
149 + padding: 40px;
150 + color: var(--text-secondary);
151 + text-align: center;
152 +}
153 +
154 +.empty-icon {
155 + font-size: 32px;
156 + margin-bottom: 4px;
157 + opacity: 0.4;
158 +}
159 +
160 +.empty-state h2 {
161 + font-size: 16px;
162 + font-weight: 500;
163 + color: var(--text-secondary);
164 +}
165 +
166 +.empty-state p {
167 + font-size: 13px;
168 + max-width: 220px;
169 + line-height: 1.6;
170 +}
171 +
172 +/* Message row */
173 +.message-row {
174 + display: flex;
175 + padding: 4px 24px;
176 +}
177 +
178 +.message-row.user {
179 + justify-content: flex-end;
180 +}
181 +
182 +.message-row.assistant {
183 + justify-content: flex-start;
184 +}
185 +
186 +.bubble {
187 + max-width: 72%;
188 + padding: 10px 14px;
189 + border-radius: var(--radius);
190 + font-size: 14px;
191 + line-height: 1.55;
192 + word-break: break-word;
193 + user-select: text;
194 + -webkit-user-select: text;
195 +}
196 +
197 +.message-row.user .bubble {
198 + background: var(--user-bubble);
199 + color: var(--user-bubble-text);
200 + border-bottom-right-radius: var(--radius-sm);
201 +}
202 +
203 +.message-row.assistant .bubble {
204 + background: var(--ai-bubble);
205 + color: var(--ai-bubble-text);
206 + border-bottom-left-radius: var(--radius-sm);
207 +}
208 +
209 +/* Typing indicator */
210 +.typing-indicator {
211 + display: flex;
212 + gap: 4px;
213 + align-items: center;
214 + padding: 12px 14px;
215 +}
216 +
217 +.typing-dot {
218 + width: 6px;
219 + height: 6px;
220 + border-radius: 50%;
221 + background: var(--text-secondary);
222 + animation: bounce 1.2s ease-in-out infinite;
223 +}
224 +
225 +.typing-dot:nth-child(2) { animation-delay: 0.2s; }
226 +.typing-dot:nth-child(3) { animation-delay: 0.4s; }
227 +
228 +@keyframes bounce {
229 + 0%, 60%, 100% { transform: translateY(0); }
230 + 30% { transform: translateY(-4px); }
231 +}
232 +
233 +/* Input area */
234 +.input-area {
235 + padding: 16px max(24px, env(safe-area-inset-right))
236 + max(20px, env(safe-area-inset-bottom)) max(24px, env(safe-area-inset-left));
237 + border-top: 1px solid var(--border);
238 + background: var(--bg);
239 + flex-shrink: 0;
240 +}
241 +
242 +.input-form {
243 + display: flex;
244 + align-items: flex-end;
245 + gap: 10px;
246 + background: var(--input-bg);
247 + border: 1px solid var(--border);
248 + border-radius: 14px;
249 + padding: 10px 10px 10px 16px;
250 + transition: border-color 0.15s;
251 +}
252 +
253 +.input-form:focus-within {
254 + border-color: var(--text-secondary);
255 +}
256 +
257 +.input-field {
258 + flex: 1;
259 + background: transparent;
260 + border: none;
261 + outline: none;
262 + color: var(--text-primary);
263 + font-size: 16px; /* prevents iOS auto-zoom on focus */
264 + font-family: inherit;
265 + line-height: 1.5;
266 + resize: none;
267 + max-height: 120px;
268 + min-height: 22px;
269 + padding: 0;
270 + user-select: text;
271 + -webkit-user-select: text;
272 +}
273 +
274 +.input-field::placeholder {
275 + color: var(--text-secondary);
276 +}
277 +
278 +.send-button {
279 + width: 32px;
280 + height: 32px;
281 + border-radius: 8px;
282 + border: none;
283 + background: var(--accent);
284 + color: var(--bg);
285 + cursor: pointer;
286 + display: flex;
287 + align-items: center;
288 + justify-content: center;
289 + flex-shrink: 0;
290 + transition: background 0.15s, transform 0.1s;
291 +}
292 +
293 +.send-button:active:not(:disabled) {
294 + background: var(--accent-hover);
295 + transform: scale(0.94);
296 +}
297 +
298 +.send-button:disabled {
299 + opacity: 0.3;
300 + cursor: default;
301 +}
302 +
303 +.send-button svg {
304 + width: 16px;
305 + height: 16px;
306 +}
307 +
308 +.input-hint {
309 + font-size: 11px;
310 + color: var(--text-secondary);
311 + text-align: center;
312 + margin-top: 10px;
313 + letter-spacing: 0.1px;
314 +}
315 +
316 +/* Header actions (status dot + settings) */
317 +.header-actions {
318 + display: flex;
319 + align-items: center;
320 + gap: 14px;
321 +}
322 +
323 +.status-dot.error {
324 + background: #e5484d;
325 + animation: none;
326 +}
327 +
328 +.icon-button {
329 + width: 34px;
330 + height: 34px;
331 + border: none;
332 + background: transparent;
333 + color: var(--text-secondary);
334 + display: flex;
335 + align-items: center;
336 + justify-content: center;
337 + border-radius: var(--radius-sm);
338 + cursor: pointer;
339 + -webkit-tap-highlight-color: transparent;
340 + transition: color 0.15s, background 0.15s, transform 0.1s;
341 +}
342 +
343 +.icon-button:active {
344 + background: var(--ai-bubble);
345 + color: var(--text-primary);
346 + transform: scale(0.92);
347 +}
348 +
349 +.icon-button svg {
350 + width: 20px;
351 + height: 20px;
352 +}
353 +
354 +/* ── Settings page (slide-up) ───────────────────────────────────────────── */
355 +.settings-overlay {
356 + position: fixed;
357 + inset: 0;
358 + z-index: 50;
359 + display: flex;
360 + flex-direction: column;
361 + background: var(--bg);
362 + color: var(--text-primary);
363 + transform: translateY(100%);
364 + visibility: hidden;
365 + transition: transform 0.28s cubic-bezier(0.32, 0.72, 0, 1), visibility 0.28s;
366 +}
367 +
368 +.settings-overlay.open {
369 + transform: translateY(0);
370 + visibility: visible;
371 +}
372 +
373 +.settings-header {
374 + display: flex;
375 + align-items: center;
376 + justify-content: space-between;
377 + padding: calc(env(safe-area-inset-top) + 18px) 20px 14px;
378 + border-bottom: 1px solid var(--border);
379 + flex-shrink: 0;
380 +}
381 +
382 +.settings-header h1 {
383 + font-size: 22px;
384 + font-weight: 700;
385 + letter-spacing: -0.4px;
386 +}
387 +
388 +.settings-done {
389 + border: none;
390 + background: transparent;
391 + color: var(--text-primary);
392 + font-family: inherit;
393 + font-size: 16px;
394 + font-weight: 600;
395 + padding: 6px 2px;
396 + cursor: pointer;
397 +}
398 +
399 +.settings-done:active {
400 + opacity: 0.5;
401 +}
402 +
403 +.settings-content {
404 + flex: 1;
405 + overflow-y: auto;
406 + -webkit-overflow-scrolling: touch;
407 + padding: 8px 16px max(24px, env(safe-area-inset-bottom));
408 +}
409 +
410 +.settings-section {
411 + margin-top: 22px;
412 +}
413 +
414 +.section-label {
415 + font-size: 12px;
416 + font-weight: 600;
417 + letter-spacing: 0.6px;
418 + text-transform: uppercase;
419 + color: var(--text-secondary);
420 + padding: 0 8px 8px;
421 +}
422 +
423 +.settings-card {
424 + background: var(--surface);
425 + border: 1px solid var(--border);
426 + border-radius: 14px;
427 + overflow: hidden;
428 +}
429 +
430 +.settings-row {
431 + display: flex;
432 + align-items: center;
433 + justify-content: space-between;
434 + gap: 12px;
435 + padding: 14px 16px;
436 + min-height: 52px;
437 +}
438 +
439 +.settings-card.about .settings-row + .settings-row {
440 + border-top: 1px solid var(--border);
441 +}
442 +
443 +.row-label {
444 + font-size: 15px;
445 + font-weight: 500;
446 + color: var(--text-primary);
447 +}
448 +
449 +.row-value {
450 + font-size: 15px;
451 + color: var(--text-secondary);
452 +}
453 +
454 +.select-wrap {
455 + position: relative;
456 + display: flex;
457 + align-items: center;
458 +}
459 +
460 +.model-select {
461 + appearance: none;
462 + -webkit-appearance: none;
463 + border: none;
464 + background: transparent;
465 + color: var(--text-secondary);
466 + font-family: inherit;
467 + font-size: 15px;
468 + text-align: right;
469 + padding-right: 20px;
470 + max-width: 190px;
471 + white-space: nowrap;
472 + overflow: hidden;
473 + text-overflow: ellipsis;
474 + cursor: pointer;
475 +}
476 +
477 +.model-select:disabled {
478 + opacity: 0.5;
479 +}
480 +
481 +.select-wrap .chevron {
482 + position: absolute;
483 + right: 2px;
484 + width: 11px;
485 + height: 8px;
486 + color: var(--text-secondary);
487 + pointer-events: none;
488 +}
489 +
490 +.model-detail {
491 + border-top: 1px solid var(--border);
492 + padding: 14px 16px;
493 +}
494 +
495 +.model-org {
496 + font-size: 12px;
497 + font-weight: 600;
498 + letter-spacing: 0.2px;
499 + color: var(--text-secondary);
500 +}
501 +
502 +.model-desc {
503 + font-size: 13px;
504 + line-height: 1.5;
505 + color: var(--text-primary);
506 + margin-top: 4px;
507 +}
508 +
509 +.model-status {
510 + display: flex;
511 + align-items: center;
512 + gap: 7px;
513 + margin-top: 12px;
514 + font-size: 13px;
515 + font-weight: 500;
516 + color: var(--text-secondary);
517 +}
518 +
519 +.model-status-dot {
520 + width: 7px;
521 + height: 7px;
522 + border-radius: 50%;
523 + background: var(--text-secondary);
524 + flex-shrink: 0;
525 +}
526 +
527 +.model-status.ready,
528 +.model-status.ready .model-status-dot {
529 + color: #34c759;
530 +}
531 +.model-status.ready .model-status-dot {
532 + background: #34c759;
533 +}
534 +
535 +.model-status.loading,
536 +.model-status.loading .model-status-dot {
537 + color: #ff9800;
538 +}
539 +.model-status.loading .model-status-dot {
540 + background: #ff9800;
541 + animation: pulse 1.2s ease-in-out infinite;
542 +}
543 +
544 +.model-status.error,
545 +.model-status.error .model-status-dot {
546 + color: #e5484d;
547 +}
548 +.model-status.error .model-status-dot {
549 + background: #e5484d;
550 +}
551 +
552 +.settings-note {
553 + font-size: 12px;
554 + line-height: 1.5;
555 + color: var(--text-secondary);
556 + padding: 10px 8px 0;
557 +}
src/App.tsx new
+304
@@ -0,0 +1,304 @@
1 +import { useState, useRef, useEffect, KeyboardEvent, useCallback } from "react";
2 +import "./App.css";
3 +import Settings from "./Settings";
4 +import {
5 + type ChatStatus,
6 + type ModelInfo,
7 + listModels,
8 + setModel,
9 + sendMessage as sendMessageCmd,
10 + getStatus,
11 + getHistory,
12 + onStatusChanged,
13 + onReply,
14 +} from "./api";
15 +
16 +type Role = "user" | "assistant";
17 +
18 +interface Message {
19 + id: number;
20 + role: Role;
21 + text: string;
22 +}
23 +
24 +let nextId = 1;
25 +
26 +function SendIcon() {
27 + return (
28 + <svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
29 + <path
30 + d="M2 8L14 8M14 8L9 3M14 8L9 13"
31 + stroke="currentColor"
32 + strokeWidth="1.5"
33 + strokeLinecap="round"
34 + strokeLinejoin="round"
35 + />
36 + </svg>
37 + );
38 +}
39 +
40 +function GearIcon() {
41 + return (
42 + <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
43 + <circle cx="12" cy="12" r="3" stroke="currentColor" strokeWidth="1.6" />
44 + <path
45 + d="M12 2.5v2M12 19.5v2M21.5 12h-2M4.5 12h-2M18.7 5.3l-1.4 1.4M6.7 17.3l-1.4 1.4M18.7 18.7l-1.4-1.4M6.7 6.7 5.3 5.3"
46 + stroke="currentColor"
47 + strokeWidth="1.6"
48 + strokeLinecap="round"
49 + />
50 + </svg>
51 + );
52 +}
53 +
54 +function TypingBubble() {
55 + return (
56 + <div className="message-row assistant">
57 + <div className="bubble typing-indicator">
58 + <span className="typing-dot" />
59 + <span className="typing-dot" />
60 + <span className="typing-dot" />
61 + </div>
62 + </div>
63 + );
64 +}
65 +
66 +export default function App() {
67 + const [messages, setMessages] = useState<Message[]>([]);
68 + const [input, setInput] = useState("");
69 + const [thinking, setThinking] = useState(false);
70 + const [settingsOpen, setSettingsOpen] = useState(false);
71 + const [models, setModels] = useState<ModelInfo[]>([]);
72 + const [status, setStatus] = useState<ChatStatus>("unloaded");
73 + const [statusModel, setStatusModel] = useState<string | null>(null);
74 + const [statusError, setStatusError] = useState<string | null>(null);
75 +
76 + const bottomRef = useRef<HTMLDivElement>(null);
77 + const textareaRef = useRef<HTMLTextAreaElement>(null);
78 + // Backend present? false when running in a plain browser (vite preview).
79 + const backendRef = useRef(true);
80 +
81 + const refreshModels = useCallback(async () => {
82 + try {
83 + setModels(await listModels());
84 + } catch {
85 + /* backend unavailable, ignore */
86 + }
87 + }, []);
88 +
89 + // ── Wire backend: initial state, history, and live events ─────────────────
90 + useEffect(() => {
91 + let unStatus: (() => void) | undefined;
92 + let unReply: (() => void) | undefined;
93 +
94 + (async () => {
95 + try {
96 + const s = await getStatus();
97 + setStatus(s.status);
98 + setStatusModel(s.model_name);
99 +
100 + const history = await getHistory();
101 + if (history.length) {
102 + setMessages(
103 + history.map((m) => ({
104 + id: nextId++,
105 + role: m.role,
106 + text: m.content,
107 + }))
108 + );
109 + }
110 + await refreshModels();
111 + } catch {
112 + backendRef.current = false;
113 + }
114 +
115 + try {
116 + unStatus = await onStatusChanged((p) => {
117 + setStatus(p.status);
118 + setStatusModel(p.model_name);
119 + setStatusError(p.error);
120 + if (p.status === "ready" || p.status === "error") refreshModels();
121 + });
122 + unReply = await onReply((p) => {
123 + setThinking(false);
124 + if (p.reply) {
125 + setMessages((prev) => [
126 + ...prev,
127 + { id: nextId++, role: "assistant", text: p.reply as string },
128 + ]);
129 + } else if (p.error) {
130 + setMessages((prev) => [
131 + ...prev,
132 + {
133 + id: nextId++,
134 + role: "assistant",
135 + text: `⚠️ ${p.error}`,
136 + },
137 + ]);
138 + }
139 + });
140 + } catch {
141 + backendRef.current = false;
142 + }
143 + })();
144 +
145 + return () => {
146 + unStatus?.();
147 + unReply?.();
148 + };
149 + }, [refreshModels]);
150 +
151 + useEffect(() => {
152 + bottomRef.current?.scrollIntoView({ behavior: "smooth" });
153 + }, [messages, thinking]);
154 +
155 + function autoResize() {
156 + const el = textareaRef.current;
157 + if (!el) return;
158 + el.style.height = "auto";
159 + el.style.height = Math.min(el.scrollHeight, 120) + "px";
160 + }
161 +
162 + async function sendMessage() {
163 + const text = input.trim();
164 + if (!text || thinking) return;
165 +
166 + setMessages((prev) => [...prev, { id: nextId++, role: "user", text }]);
167 + setInput("");
168 + if (textareaRef.current) textareaRef.current.style.height = "auto";
169 + setThinking(true);
170 +
171 + if (!backendRef.current) {
172 + // Browser preview fallback: no on-device engine available.
173 + await new Promise((r) => setTimeout(r, 700));
174 + setMessages((prev) => [
175 + ...prev,
176 + {
177 + id: nextId++,
178 + role: "assistant",
179 + text: `(preview) I'm Siti. On a device I'd answer: "${text}"`,
180 + },
181 + ]);
182 + setThinking(false);
183 + return;
184 + }
185 +
186 + try {
187 + // Reply arrives asynchronously via the `chat_reply` event listener.
188 + await sendMessageCmd(text);
189 + } catch (e) {
190 + setThinking(false);
191 + setMessages((prev) => [
192 + ...prev,
193 + { id: nextId++, role: "assistant", text: `⚠️ ${String(e)}` },
194 + ]);
195 + }
196 + }
197 +
198 + async function handleSelectModel(id: string) {
199 + setModels((prev) => prev.map((m) => ({ ...m, is_selected: m.id === id })));
200 + setStatus("loading");
201 + setStatusError(null);
202 + try {
203 + await setModel(id);
204 + } catch (e) {
205 + setStatus("error");
206 + setStatusError(String(e));
207 + }
208 + }
209 +
210 + function handleKeyDown(e: KeyboardEvent<HTMLTextAreaElement>) {
211 + if (e.key === "Enter" && !e.shiftKey) {
212 + e.preventDefault();
213 + sendMessage();
214 + }
215 + }
216 +
217 + const dotThinking =
218 + thinking || status === "loading" || status === "generating";
219 + const dotClass =
220 + status === "error" ? " error" : dotThinking ? " thinking" : "";
221 +
222 + const subtitle =
223 + status === "loading"
224 + ? `Loading ${statusModel ?? "model"}…`
225 + : status === "error"
226 + ? "Model error · tap ⚙"
227 + : statusModel
228 + ? `${statusModel} · private`
229 + : "on-device · private";
230 +
231 + return (
232 + <div className="app">
233 + <header className="header">
234 + <div className="header-title">
235 + <h1>Siti AI</h1>
236 + <span className="header-subtitle">{subtitle}</span>
237 + </div>
238 + <div className="header-actions">
239 + <span className={`status-dot${dotClass}`} />
240 + <button
241 + className="icon-button"
242 + onClick={() => setSettingsOpen(true)}
243 + aria-label="Settings"
244 + >
245 + <GearIcon />
246 + </button>
247 + </div>
248 + </header>
249 +
250 + <div className="messages">
251 + {messages.length === 0 && !thinking ? (
252 + <div className="empty-state">
253 + <div className="empty-icon">◇</div>
254 + <h2>How can I help?</h2>
255 + <p>Your personal assistant, running entirely on your device.</p>
256 + </div>
257 + ) : (
258 + messages.map((msg) => (
259 + <div key={msg.id} className={`message-row ${msg.role}`}>
260 + <div className="bubble">{msg.text}</div>
261 + </div>
262 + ))
263 + )}
264 + {thinking && <TypingBubble />}
265 + <div ref={bottomRef} />
266 + </div>
267 +
268 + <div className="input-area">
269 + <div className="input-form">
270 + <textarea
271 + ref={textareaRef}
272 + className="input-field"
273 + value={input}
274 + placeholder="Message Siti…"
275 + rows={1}
276 + onChange={(e) => {
277 + setInput(e.target.value);
278 + autoResize();
279 + }}
280 + onKeyDown={handleKeyDown}
281 + />
282 + <button
283 + className="send-button"
284 + onClick={sendMessage}
285 + disabled={!input.trim() || thinking}
286 + aria-label="Send"
287 + >
288 + <SendIcon />
289 + </button>
290 + </div>
291 + <p className="input-hint">Return to send · Shift+Return for new line</p>
292 + </div>
293 +
294 + <Settings
295 + open={settingsOpen}
296 + models={models}
297 + status={status}
298 + statusError={statusError}
299 + onSelectModel={handleSelectModel}
300 + onClose={() => setSettingsOpen(false)}
301 + />
302 + </div>
303 + );
304 +}
src/Settings.tsx new
+142
@@ -0,0 +1,142 @@
1 +import { useEffect, useMemo, useState } from "react";
2 +import type { ChatStatus, ModelInfo } from "./api";
3 +import { formatSize, getAppVersion, getBuildVersion } from "./api";
4 +
5 +interface SettingsProps {
6 + open: boolean;
7 + models: ModelInfo[];
8 + status: ChatStatus;
9 + statusError: string | null;
10 + onSelectModel: (id: string) => void;
11 + onClose: () => void;
12 +}
13 +
14 +function statusLine(status: ChatStatus, error: string | null) {
15 + switch (status) {
16 + case "loading":
17 + return { text: "Downloading / loading…", cls: "loading" };
18 + case "ready":
19 + return { text: "Ready", cls: "ready" };
20 + case "generating":
21 + return { text: "Generating…", cls: "loading" };
22 + case "error":
23 + return { text: error ?? "Failed to load model", cls: "error" };
24 + default:
25 + return { text: "Not loaded", cls: "" };
26 + }
27 +}
28 +
29 +export default function Settings({
30 + open,
31 + models,
32 + status,
33 + statusError,
34 + onSelectModel,
35 + onClose,
36 +}: SettingsProps) {
37 + const selected = useMemo(
38 + () => models.find((m) => m.is_selected) ?? models[0],
39 + [models]
40 + );
41 + const busy = status === "loading";
42 + const line = statusLine(status, statusError);
43 +
44 + // Real app version (CFBundleShortVersionString) and build number
45 + // (CFBundleVersion), read from the bundle rather than hardcoded.
46 + const [appVersion, setAppVersion] = useState("");
47 + useEffect(() => {
48 + let active = true;
49 + (async () => {
50 + const [version, build] = await Promise.all([
51 + getAppVersion().catch(() => ""),
52 + getBuildVersion().catch(() => null),
53 + ]);
54 + if (!active) return;
55 + setAppVersion(build && build !== version ? `${version} (${build})` : version);
56 + })();
57 + return () => {
58 + active = false;
59 + };
60 + }, []);
61 +
62 + return (
63 + <div className={`settings-overlay${open ? " open" : ""}`} aria-hidden={!open}>
64 + <header className="settings-header">
65 + <h1>Settings</h1>
66 + <button className="settings-done" onClick={onClose}>
67 + Done
68 + </button>
69 + </header>
70 +
71 + <div className="settings-content">
72 + <section className="settings-section">
73 + <p className="section-label">Model</p>
74 +
75 + <div className="settings-card">
76 + <div className="settings-row">
77 + <span className="row-label">On-device model</span>
78 + <div className="select-wrap">
79 + <select
80 + className="model-select"
81 + value={selected?.id ?? ""}
82 + disabled={busy || models.length === 0}
83 + onChange={(e) => onSelectModel(e.target.value)}
84 + >
85 + {models.length === 0 && <option value="">Unavailable</option>}
86 + {models.map((m) => (
87 + <option key={m.id} value={m.id}>
88 + {m.name}
89 + {m.size_bytes ? ` · ${formatSize(m.size_bytes)}` : ""}
90 + </option>
91 + ))}
92 + </select>
93 + <svg className="chevron" viewBox="0 0 12 8" aria-hidden>
94 + <path
95 + d="M1 1.5 6 6.5 11 1.5"
96 + stroke="currentColor"
97 + strokeWidth="1.6"
98 + fill="none"
99 + strokeLinecap="round"
100 + strokeLinejoin="round"
101 + />
102 + </svg>
103 + </div>
104 + </div>
105 +
106 + {selected && (
107 + <div className="model-detail">
108 + <span className="model-org">{selected.org}</span>
109 + <p className="model-desc">{selected.description}</p>
110 + <div className={`model-status ${line.cls}`}>
111 + <span className="model-status-dot" />
112 + {line.text}
113 + </div>
114 + </div>
115 + )}
116 + </div>
117 +
118 + <p className="settings-note">
119 + The model downloads the first time you pick it, so use Wi-Fi if you
120 + can. It then runs on your device, and nothing you type is sent to a
121 + server.
122 + </p>
123 + </section>
124 +
125 + <section className="settings-section">
126 + <p className="section-label">About</p>
127 + <div className="settings-card about">
128 + <div className="settings-row">
129 + <span className="row-label">Siti AI</span>
130 + <span className="row-value">{appVersion}</span>
131 + </div>
132 + <div className="settings-row">
133 + <span className="row-label">Privacy</span>
134 + <span className="row-value">On-device · private</span>
135 + </div>
136 + </div>
137 + <p className="settings-note">Profoundly personal. Entirely private.</p>
138 + </section>
139 + </div>
140 + </div>
141 + );
142 +}
src/api.ts new
+92
@@ -0,0 +1,92 @@
1 +// Typed wrappers around the Siti on-device chat backend (Rust/Tauri commands
2 +// and events). Keeping the IPC surface in one place means components never
3 +// touch raw command-name strings.
4 +
5 +import { invoke } from "@tauri-apps/api/core";
6 +import { listen, type UnlistenFn } from "@tauri-apps/api/event";
7 +import { getVersion } from "@tauri-apps/api/app";
8 +
9 +export type ChatStatus =
10 + | "unloaded"
11 + | "loading"
12 + | "ready"
13 + | "generating"
14 + | "error";
15 +
16 +export interface ModelInfo {
17 + id: string;
18 + name: string;
19 + org: string;
20 + description: string;
21 + approx_memory: string;
22 + size_bytes: number | null;
23 + is_selected: boolean;
24 +}
25 +
26 +export interface ChatStatusResponse {
27 + status: ChatStatus;
28 + model_name: string | null;
29 + approx_memory: string | null;
30 + history_length: number;
31 +}
32 +
33 +export interface ChatStatusPayload {
34 + status: ChatStatus;
35 + model_name: string | null;
36 + error: string | null;
37 +}
38 +
39 +export interface ChatReplyPayload {
40 + reply: string | null;
41 + duration: string | null;
42 + error: string | null;
43 +}
44 +
45 +export interface ChatMessagePayload {
46 + role: "user" | "assistant";
47 + content: string;
48 +}
49 +
50 +// ── Commands ────────────────────────────────────────────────────────────────
51 +
52 +export const listModels = () => invoke<ModelInfo[]>("chat_list_models");
53 +
54 +export const setModel = (modelId: string) =>
55 + invoke<void>("chat_set_model", { modelId });
56 +
57 +export const sendMessage = (message: string) =>
58 + invoke<void>("chat_send_message", { message });
59 +
60 +export const getStatus = () => invoke<ChatStatusResponse>("chat_get_status");
61 +
62 +export const getHistory = () =>
63 + invoke<ChatMessagePayload[]>("chat_get_history");
64 +
65 +export const clearHistory = () => invoke<void>("chat_clear_history");
66 +
67 +// ── App metadata ──────────────────────────────────────────────────────────
68 +
69 +/** Marketing version (CFBundleShortVersionString) from tauri.conf.json. */
70 +export const getAppVersion = () => getVersion();
71 +
72 +/** Native build number (Apple CFBundleVersion); `null` when unavailable. */
73 +export const getBuildVersion = () =>
74 + invoke<string | null>("app_build_version");
75 +
76 +// ── Events ──────────────────────────────────────────────────────────────────
77 +
78 +export const onStatusChanged = (cb: (p: ChatStatusPayload) => void): Promise<UnlistenFn> =>
79 + listen<ChatStatusPayload>("chat_status_changed", (e) => cb(e.payload));
80 +
81 +export const onReply = (cb: (p: ChatReplyPayload) => void): Promise<UnlistenFn> =>
82 + listen<ChatReplyPayload>("chat_reply", (e) => cb(e.payload));
83 +
84 +// ── Helpers ───────────────────────────────────────────────────────────────
85 +
86 +/** Format a byte count as a compact "1.9 GB" / "941 MB" string. */
87 +export function formatSize(bytes: number | null): string {
88 + if (!bytes) return "";
89 + const gb = bytes / 1e9;
90 + if (gb >= 1) return `${gb.toFixed(gb >= 10 ? 0 : 1)} GB`;
91 + return `${Math.round(bytes / 1e6)} MB`;
92 +}
src/main.tsx new
+9
@@ -0,0 +1,9 @@
1 +import React from "react";
2 +import ReactDOM from "react-dom/client";
3 +import App from "./App";
4 +
5 +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
6 + <React.StrictMode>
7 + <App />
8 + </React.StrictMode>,
9 +);
src/vite-env.d.ts new
+1
@@ -0,0 +1 @@
1 +/// <reference types="vite/client" />
tsconfig.json new
+25
@@ -0,0 +1,25 @@
1 +{
2 + "compilerOptions": {
3 + "target": "ES2020",
4 + "useDefineForClassFields": true,
5 + "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 + "module": "ESNext",
7 + "skipLibCheck": true,
8 +
9 + /* Bundler mode */
10 + "moduleResolution": "bundler",
11 + "allowImportingTsExtensions": true,
12 + "resolveJsonModule": true,
13 + "isolatedModules": true,
14 + "noEmit": true,
15 + "jsx": "react-jsx",
16 +
17 + /* Linting */
18 + "strict": true,
19 + "noUnusedLocals": true,
20 + "noUnusedParameters": true,
21 + "noFallthroughCasesInSwitch": true
22 + },
23 + "include": ["src"],
24 + "references": [{ "path": "./tsconfig.node.json" }]
25 +}
tsconfig.node.json new
+10
@@ -0,0 +1,10 @@
1 +{
2 + "compilerOptions": {
3 + "composite": true,
4 + "skipLibCheck": true,
5 + "module": "ESNext",
6 + "moduleResolution": "bundler",
7 + "allowSyntheticDefaultImports": true
8 + },
9 + "include": ["vite.config.ts"]
10 +}
vite.config.ts new
+32
@@ -0,0 +1,32 @@
1 +import { defineConfig } from "vite";
2 +import react from "@vitejs/plugin-react";
3 +
4 +// @ts-expect-error process is a nodejs global
5 +const host = process.env.TAURI_DEV_HOST;
6 +
7 +// https://vite.dev/config/
8 +export default defineConfig(async () => ({
9 + plugins: [react()],
10 +
11 + // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
12 + //
13 + // 1. prevent Vite from obscuring rust errors
14 + clearScreen: false,
15 + // 2. tauri expects a fixed port, fail if that port is not available
16 + server: {
17 + port: 1420,
18 + strictPort: true,
19 + host: host || false,
20 + hmr: host
21 + ? {
22 + protocol: "ws",
23 + host,
24 + port: 1421,
25 + }
26 + : undefined,
27 + watch: {
28 + // 3. tell Vite to ignore watching `src-tauri`
29 + ignored: ["**/src-tauri/**"],
30 + },
31 + },
32 +}));
web/.gitignore new
+25
@@ -0,0 +1,25 @@
1 +# dependencies
2 +/node_modules
3 +
4 +# next.js
5 +/.next/
6 +/out/
7 +
8 +# production
9 +/build
10 +
11 +# misc
12 +.DS_Store
13 +*.pem
14 +
15 +# debug
16 +npm-debug.log*
17 +yarn-debug.log*
18 +yarn-error.log*
19 +
20 +# typescript
21 +*.tsbuildinfo
22 +next-env.d.ts
23 +
24 +# env
25 +.env*.local
web/app/about/page.tsx new
+53
@@ -0,0 +1,53 @@
1 +import type { Metadata } from "next";
2 +
3 +export const metadata: Metadata = {
4 + title: "About",
5 + description:
6 + "Siti AI is a private, on-device AI assistant for Apple platforms, built by the team behind Rumi.",
7 +};
8 +
9 +export default function AboutPage() {
10 + return (
11 + <article className="mx-auto max-w-3xl px-6 py-20">
12 + <h1 className="text-3xl font-semibold tracking-tight sm:text-4xl">
13 + About Siti AI
14 + </h1>
15 + <div className="prose-legal mt-8">
16 + <p>
17 + Siti AI is a private, on-device AI assistant for macOS and iOS. The
18 + idea behind it is simple: the device you keep closest to you should
19 + also be the one that keeps your data private. What you type into Siti
20 + stays yours, and using AI shouldn&apos;t be the price you pay to give
21 + that up.
22 + </p>
23 + <p>
24 + Most assistants send everything you type to a remote server. Siti is
25 + designed to keep your data on your device instead. The name is a wink
26 + at the assistant you already know, rebuilt around privacy.
27 + </p>
28 + <p>
29 + Siti is a sibling project to{" "}
30 + <a
31 + href="https://apps.apple.com/se/app/rumi-learn-persian/id6753832408?l=en-GB"
32 + target="_blank"
33 + rel="noopener noreferrer"
34 + className="font-medium text-brand underline decoration-brand/30 underline-offset-2 transition-colors hover:decoration-brand"
35 + >
36 + Rumi Learn Persian
37 + </a>{" "}
38 + and{" "}
39 + <a
40 + href="https://apps.apple.com/se/app/smbcloud-mailx/id6766108771?l=en-GB"
41 + target="_blank"
42 + rel="noopener noreferrer"
43 + className="font-medium text-brand underline decoration-brand/30 underline-offset-2 transition-colors hover:decoration-brand"
44 + >
45 + smbCloud MailX
46 + </a>
47 + , and they share the same focus: careful, on-device apps for Apple
48 + platforms. We are a small team, and we sweat the details.
49 + </p>
50 + </div>
51 + </article>
52 + );
53 +}
web/app/apple-icon.png
Binary files /dev/null and b/web/app/apple-icon.png differ
web/app/contact/page.tsx new
+38
@@ -0,0 +1,38 @@
1 +import type { Metadata } from "next";
2 +
3 +export const metadata: Metadata = {
4 + title: "Contact",
5 + description: "Get in touch with the Siti AI team for support or press.",
6 +};
7 +
8 +const SUPPORT_EMAIL = "support@getsiti.5mb.app";
9 +
10 +export default function ContactPage() {
11 + return (
12 + <article className="mx-auto max-w-3xl px-6 py-20">
13 + <h1 className="text-3xl font-semibold tracking-tight sm:text-4xl">
14 + Contact
15 + </h1>
16 + <div className="prose-legal mt-8">
17 + <p>
18 + We&apos;d love to hear from you. Whether you have a question, found a
19 + bug, or want to share feedback, reach out and a real person will get
20 + back to you.
21 + </p>
22 + <h2>Support</h2>
23 + <p>
24 + For help with the app, email{" "}
25 + <a href={`mailto:${SUPPORT_EMAIL}`} className="text-brand">
26 + {SUPPORT_EMAIL}
27 + </a>
28 + .
29 + </p>
30 + <h2>Press &amp; partnerships</h2>
31 + <p>
32 + For press or partnership inquiries, use the same address and we&apos;ll
33 + route your message to the right place.
34 + </p>
35 + </div>
36 + </article>
37 + );
38 +}
web/app/favicon.ico
Binary files /dev/null and b/web/app/favicon.ico differ
web/app/globals.css new
+92
@@ -0,0 +1,92 @@
1 +@import "tailwindcss";
2 +
3 +/* ── Design system, ported from the app (src/App.css) ─────────────────────── */
4 +:root {
5 + --bg: #fafafa;
6 + --surface: #ffffff;
7 + --border: #e8e8e8;
8 + --text-primary: #1a1a1a;
9 + --text-secondary: #8a8a8a;
10 + --accent: #2d2d2d;
11 + --accent-hover: #1a1a1a;
12 +
13 + /* Brand green, matching the App Store screenshots and the app's "ready" state */
14 + --brand: #06a561;
15 + --brand-soft: rgba(6, 165, 97, 0.1);
16 +
17 + --radius: 18px;
18 + --radius-sm: 8px;
19 +
20 + --font-sans: var(--font-inter), "Inter", -apple-system, BlinkMacSystemFont,
21 + "Segoe UI", sans-serif;
22 +}
23 +
24 +@media (prefers-color-scheme: dark) {
25 + :root {
26 + --bg: #111111;
27 + --surface: #1c1c1c;
28 + --border: #2a2a2a;
29 + --text-primary: #f0f0f0;
30 + --text-secondary: #666666;
31 + --accent: #e0e0e0;
32 + --accent-hover: #ffffff;
33 + --brand: #34c759;
34 + --brand-soft: rgba(52, 199, 89, 0.12);
35 + }
36 +}
37 +
38 +/* Expose the tokens to Tailwind utilities (bg-bg, text-ink, text-brand, …) */
39 +@theme inline {
40 + --color-bg: var(--bg);
41 + --color-surface: var(--surface);
42 + --color-border: var(--border);
43 + --color-ink: var(--text-primary);
44 + --color-muted: var(--text-secondary);
45 + --color-accent: var(--accent);
46 + --color-accent-hover: var(--accent-hover);
47 + --color-brand: var(--brand);
48 + --radius-card: var(--radius);
49 + --radius-sm: var(--radius-sm);
50 + --font-sans: var(--font-sans);
51 +}
52 +
53 +*,
54 +*::before,
55 +*::after {
56 + box-sizing: border-box;
57 +}
58 +
59 +html {
60 + scroll-behavior: smooth;
61 +}
62 +
63 +body {
64 + background: var(--bg);
65 + color: var(--text-primary);
66 + font-family: var(--font-sans);
67 + font-size: 15px;
68 + line-height: 1.5;
69 + -webkit-font-smoothing: antialiased;
70 +}
71 +
72 +/* Legal / long-form pages */
73 +.prose-legal h2 {
74 + font-size: 1.25rem;
75 + font-weight: 600;
76 + letter-spacing: -0.2px;
77 + color: var(--text-primary);
78 + margin-top: 2rem;
79 + margin-bottom: 0.75rem;
80 +}
81 +
82 +.prose-legal p,
83 +.prose-legal li {
84 + color: var(--text-secondary);
85 + line-height: 1.7;
86 + margin-bottom: 0.75rem;
87 +}
88 +
89 +.prose-legal ul {
90 + list-style: disc;
91 + padding-left: 1.25rem;
92 +}
web/app/icon.png
Binary files /dev/null and b/web/app/icon.png differ
web/app/layout.tsx new
+51
@@ -0,0 +1,51 @@
1 +import type { Metadata } from "next";
2 +import { Inter } from "next/font/google";
3 +import "./globals.css";
4 +import { SiteNav } from "@/components/site-nav";
5 +import { SiteFooter } from "@/components/site-footer";
6 +
7 +const inter = Inter({
8 + subsets: ["latin"],
9 + variable: "--font-inter",
10 + display: "swap",
11 +});
12 +
13 +const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://getsiti.5mb.app";
14 +
15 +export const metadata: Metadata = {
16 + metadataBase: new URL(SITE_URL),
17 + title: {
18 + default: "Siti AI: a private on-device AI assistant",
19 + template: "%s · Siti AI",
20 + },
21 + description:
22 + "Siti AI is a private, on-device AI assistant for macOS and iOS. Your conversations stay on your device.",
23 + openGraph: {
24 + title: "Siti AI: a private on-device AI assistant",
25 + description:
26 + "A private, on-device AI assistant for macOS and iOS. Your conversations stay on your device.",
27 + url: SITE_URL,
28 + siteName: "Siti AI",
29 + type: "website",
30 + },
31 + twitter: {
32 + card: "summary_large_image",
33 + title: "Siti AI: a private on-device AI assistant",
34 + description:
35 + "A private, on-device AI assistant for macOS and iOS. Your conversations stay on your device.",
36 + },
37 +};
38 +
39 +export default function RootLayout({
40 + children,
41 +}: Readonly<{ children: React.ReactNode }>) {
42 + return (
43 + <html lang="en" className={inter.variable}>
44 + <body className="flex min-h-screen flex-col bg-bg text-ink">
45 + <SiteNav />
46 + <main className="flex-1">{children}</main>
47 + <SiteFooter />
48 + </body>
49 + </html>
50 + );
51 +}
web/app/page.tsx new
+71
@@ -0,0 +1,71 @@
1 +import Link from "next/link";
2 +import { AppScreenshots } from "@/components/app-screenshots";
3 +
4 +const features = [
5 + {
6 + title: "On-device by default",
7 + body: "Siti runs its models on your Mac or iPhone. Your prompts and conversations don't leave the device unless you decide to send them somewhere.",
8 + },
9 + {
10 + title: "No account required",
11 + body: "Open the app and start typing. There's no sign-up to get through, and nothing about your questions is stored in the cloud.",
12 + },
13 + {
14 + title: "Built for macOS and iOS",
15 + body: "It's a native app on both, so it behaves the way the rest of your Apple devices already do.",
16 + },
17 +];
18 +
19 +export default function HomePage() {
20 + return (
21 + <>
22 + <section className="mx-auto max-w-5xl px-6 py-24 text-center">
23 + <p className="mb-4 text-xs font-semibold uppercase tracking-[0.15em] text-brand">
24 + Personal assistant
25 + </p>
26 + <h1 className="mx-auto max-w-3xl text-4xl font-semibold tracking-tight sm:text-6xl">
27 + An assistant that runs{" "}
28 + <span className="text-brand">entirely on your device.</span>
29 + </h1>
30 + <p className="mx-auto mt-6 max-w-2xl text-lg text-muted">
31 + Siti runs on your iPhone, iPad, and Mac. Ask it anything, and your
32 + conversations stay with you instead of on someone else&apos;s servers.
33 + </p>
34 + <div className="mt-10 flex items-center justify-center gap-4">
35 + <a
36 + href="https://apps.apple.com/se/app/siti-ai/id6780047972"
37 + className="rounded-sm bg-accent px-6 py-3 text-sm font-medium text-bg transition-colors hover:bg-accent-hover"
38 + >
39 + Download for Apple
40 + </a>
41 + <Link
42 + href="/about"
43 + className="rounded-sm border border-border px-6 py-3 text-sm font-medium text-ink transition-colors hover:bg-surface"
44 + >
45 + Learn more
46 + </Link>
47 + </div>
48 + </section>
49 +
50 + <section className="mx-auto max-w-5xl px-6 pb-4">
51 + <div className="grid gap-6 sm:grid-cols-3">
52 + {features.map((feature) => (
53 + <div
54 + key={feature.title}
55 + className="rounded-card border border-border bg-surface p-6"
56 + >
57 + <h2 className="text-lg font-semibold tracking-tight text-ink">
58 + {feature.title}
59 + </h2>
60 + <p className="mt-2 text-sm leading-relaxed text-muted">
61 + {feature.body}
62 + </p>
63 + </div>
64 + ))}
65 + </div>
66 + </section>
67 +
68 + <AppScreenshots />
69 + </>
70 + );
71 +}
web/app/privacy/page.tsx new
+80
@@ -0,0 +1,80 @@
1 +import type { Metadata } from "next";
2 +
3 +export const metadata: Metadata = {
4 + title: "Privacy Policy",
5 + description: "How Siti AI handles your data: on-device by design.",
6 +};
7 +
8 +const LAST_UPDATED = "June 13, 2026";
9 +
10 +export default function PrivacyPage() {
11 + return (
12 + <article className="mx-auto max-w-3xl px-6 py-20">
13 + <h1 className="text-3xl font-semibold tracking-tight sm:text-4xl">
14 + Privacy Policy
15 + </h1>
16 + <p className="mt-2 text-sm text-muted">Last updated: {LAST_UPDATED}</p>
17 + <div className="prose-legal mt-8">
18 + <p>
19 + Siti AI (&quot;Siti&quot;, &quot;we&quot;, &quot;us&quot;) is built so
20 + that your data stays on your device. This policy explains what we do
21 + with your information, and, just as important, what we don&apos;t do.
22 + </p>
23 +
24 + <h2>Data processed on your device</h2>
25 + <p>
26 + Your prompts, conversations, and any content you provide to the
27 + assistant are processed locally on your Mac or iPhone. By default, this
28 + content is not transmitted to us or to any third party.
29 + </p>
30 +
31 + <h2>Information we do not collect</h2>
32 + <ul>
33 + <li>We do not require an account to use the app.</li>
34 + <li>We do not sell your personal information.</li>
35 + <li>
36 + We do not build advertising profiles from your conversations.
37 + </li>
38 + </ul>
39 +
40 + <h2>Optional network features</h2>
41 + <p>
42 + Some features may let you connect to external services at your
43 + request. When you explicitly enable such a feature, the relevant
44 + content is sent only to perform that action, and the third
45 + party&apos;s own privacy policy applies. These features are off unless
46 + you turn them on.
47 + </p>
48 +
49 + <h2>Diagnostics</h2>
50 + <p>
51 + If you opt in to share diagnostics, we may receive crash reports and
52 + anonymous usage metrics through Apple to help us improve the app. This
53 + data does not include the contents of your conversations. You can
54 + disable diagnostics at any time in your device settings.
55 + </p>
56 +
57 + <h2>Children&apos;s privacy</h2>
58 + <p>
59 + Siti is not directed to children under 13, and we do not knowingly
60 + collect personal information from them.
61 + </p>
62 +
63 + <h2>Changes to this policy</h2>
64 + <p>
65 + We may update this policy from time to time. Material changes will be
66 + reflected on this page with a new &quot;last updated&quot; date.
67 + </p>
68 +
69 + <h2>Contact</h2>
70 + <p>
71 + Questions about privacy? Email{" "}
72 + <a href="mailto:privacy@getsiti.5mb.app" className="text-brand">
73 + privacy@getsiti.5mb.app
74 + </a>
75 + .
76 + </p>
77 + </div>
78 + </article>
79 + );
80 +}
web/app/terms/page.tsx new
+83
@@ -0,0 +1,83 @@
1 +import type { Metadata } from "next";
2 +
3 +export const metadata: Metadata = {
4 + title: "Terms of Service",
5 + description: "The terms that govern your use of Siti AI.",
6 +};
7 +
8 +const LAST_UPDATED = "June 13, 2026";
9 +
10 +export default function TermsPage() {
11 + return (
12 + <article className="mx-auto max-w-3xl px-6 py-20">
13 + <h1 className="text-3xl font-semibold tracking-tight sm:text-4xl">
14 + Terms of Service
15 + </h1>
16 + <p className="mt-2 text-sm text-muted">Last updated: {LAST_UPDATED}</p>
17 + <div className="prose-legal mt-8">
18 + <p>
19 + These Terms of Service (&quot;Terms&quot;) govern your use of the Siti
20 + AI applications and this website. By using Siti, you agree to these
21 + Terms.
22 + </p>
23 +
24 + <h2>License</h2>
25 + <p>
26 + We grant you a personal, non-exclusive, non-transferable license to use
27 + Siti on devices you own or control, subject to these Terms and the
28 + applicable app store terms.
29 + </p>
30 +
31 + <h2>Acceptable use</h2>
32 + <ul>
33 + <li>Do not use Siti for any unlawful purpose.</li>
34 + <li>
35 + Do not attempt to reverse engineer, resell, or redistribute the app
36 + except as permitted by law.
37 + </li>
38 + <li>
39 + Do not use Siti to generate content that infringes the rights of
40 + others.
41 + </li>
42 + </ul>
43 +
44 + <h2>AI-generated content</h2>
45 + <p>
46 + Siti can generate text and other content. Generated output may be
47 + inaccurate or incomplete. You are responsible for reviewing output
48 + before relying on it, and you should not treat it as professional,
49 + legal, medical, or financial advice.
50 + </p>
51 +
52 + <h2>Disclaimer of warranties</h2>
53 + <p>
54 + Siti is provided &quot;as is&quot; without warranties of any kind, to
55 + the fullest extent permitted by law. We do not warrant that the app
56 + will be uninterrupted or error-free.
57 + </p>
58 +
59 + <h2>Limitation of liability</h2>
60 + <p>
61 + To the maximum extent permitted by law, Siti AI shall not be liable for
62 + any indirect, incidental, or consequential damages arising out of your
63 + use of the app.
64 + </p>
65 +
66 + <h2>Changes to these Terms</h2>
67 + <p>
68 + We may update these Terms from time to time. Continued use of Siti
69 + after changes take effect constitutes acceptance of the revised Terms.
70 + </p>
71 +
72 + <h2>Contact</h2>
73 + <p>
74 + Questions about these Terms? Email{" "}
75 + <a href="mailto:legal@getsiti.5mb.app" className="text-brand">
76 + legal@getsiti.5mb.app
77 + </a>
78 + .
79 + </p>
80 + </div>
81 + </article>
82 + );
83 +}
web/components/app-screenshots.tsx new
+213
@@ -0,0 +1,213 @@
1 +"use client";
2 +
3 +import Image from "next/image";
4 +import { useCallback, useEffect, useRef, useState } from "react";
5 +
6 +type Shot = {
7 + src: string;
8 + alt: string;
9 + caption: string;
10 + device: "phone" | "tablet";
11 + width: number;
12 + height: number;
13 +};
14 +
15 +const slides: Shot[] = [
16 + {
17 + src: "/screenshots/iphone-chat.png",
18 + alt: "Siti AI chat running on iPhone",
19 + caption: "Ask anything, on device",
20 + device: "phone",
21 + width: 1242,
22 + height: 2688,
23 + },
24 + {
25 + src: "/screenshots/iphone-settings.png",
26 + alt: "Siti AI on-device model settings on iPhone",
27 + caption: "Pick your model",
28 + device: "phone",
29 + width: 1242,
30 + height: 2688,
31 + },
32 + {
33 + src: "/screenshots/ipad-chat.png",
34 + alt: "Siti AI chat running on iPad",
35 + caption: "Roomy on iPad",
36 + device: "tablet",
37 + width: 2064,
38 + height: 2752,
39 + },
40 + {
41 + src: "/screenshots/ipad-settings.png",
42 + alt: "Siti AI on-device model settings on iPad",
43 + caption: "Everything stays local",
44 + device: "tablet",
45 + width: 2064,
46 + height: 2752,
47 + },
48 +];
49 +
50 +const AUTOPLAY_MS = 5000;
51 +const SWIPE_THRESHOLD = 40;
52 +
53 +function Arrow({ dir }: { dir: "left" | "right" }) {
54 + return (
55 + <svg
56 + viewBox="0 0 24 24"
57 + fill="none"
58 + stroke="currentColor"
59 + strokeWidth="2"
60 + strokeLinecap="round"
61 + strokeLinejoin="round"
62 + className="h-5 w-5"
63 + aria-hidden="true"
64 + >
65 + <path d={dir === "left" ? "M15 18l-6-6 6-6" : "M9 18l6-6-6-6"} />
66 + </svg>
67 + );
68 +}
69 +
70 +export function AppScreenshots() {
71 + const [index, setIndex] = useState(0);
72 + const [paused, setPaused] = useState(false);
73 + const touchStartX = useRef<number | null>(null);
74 +
75 + const count = slides.length;
76 + const goTo = useCallback(
77 + (next: number) => setIndex((next + count) % count),
78 + [count]
79 + );
80 + const prev = useCallback(() => goTo(index - 1), [goTo, index]);
81 + const next = useCallback(() => goTo(index + 1), [goTo, index]);
82 +
83 + // Auto-advance, paused on hover/focus and when the user prefers reduced motion.
84 + useEffect(() => {
85 + if (paused) return;
86 + if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
87 + const id = window.setInterval(
88 + () => setIndex((i) => (i + 1) % count),
89 + AUTOPLAY_MS
90 + );
91 + return () => window.clearInterval(id);
92 + }, [paused, count]);
93 +
94 + function onTouchStart(e: React.TouchEvent) {
95 + touchStartX.current = e.touches[0].clientX;
96 + }
97 + function onTouchEnd(e: React.TouchEvent) {
98 + if (touchStartX.current === null) return;
99 + const delta = e.changedTouches[0].clientX - touchStartX.current;
100 + if (delta > SWIPE_THRESHOLD) prev();
101 + else if (delta < -SWIPE_THRESHOLD) next();
102 + touchStartX.current = null;
103 + }
104 +
105 + return (
106 + <section className="mx-auto max-w-5xl px-6 py-20">
107 + <div className="text-center">
108 + <p className="text-xs font-semibold uppercase tracking-[0.15em] text-brand">
109 + A look inside
110 + </p>
111 + <h2 className="mt-3 text-3xl font-semibold tracking-tight sm:text-4xl">
112 + Private by design, on every device.
113 + </h2>
114 + <p className="mx-auto mt-4 max-w-xl text-muted">
115 + The same assistant on iPhone and iPad. Your model runs locally and your
116 + chats never leave the device.
117 + </p>
118 + </div>
119 +
120 + <div
121 + className="mt-12"
122 + role="group"
123 + aria-roledescription="carousel"
124 + aria-label="App screenshots"
125 + onMouseEnter={() => setPaused(true)}
126 + onMouseLeave={() => setPaused(false)}
127 + onFocus={() => setPaused(true)}
128 + onBlur={() => setPaused(false)}
129 + onKeyDown={(e) => {
130 + if (e.key === "ArrowLeft") prev();
131 + if (e.key === "ArrowRight") next();
132 + }}
133 + >
134 + <div className="flex items-center justify-center gap-3 sm:gap-5">
135 + <button
136 + type="button"
137 + onClick={prev}
138 + aria-label="Previous screenshot"
139 + className="hidden shrink-0 rounded-full border border-border bg-surface p-3 text-muted shadow-sm transition-colors hover:text-ink sm:block"
140 + >
141 + <Arrow dir="left" />
142 + </button>
143 +
144 + {/* Stage: a fixed-height frame so slides of different aspect ratios
145 + swap without the layout jumping. */}
146 + <div
147 + className="relative h-[460px] w-full max-w-md overflow-hidden sm:h-[600px]"
148 + onTouchStart={onTouchStart}
149 + onTouchEnd={onTouchEnd}
150 + >
151 + <div
152 + className="flex h-full transition-transform duration-500 ease-out"
153 + style={{ transform: `translateX(-${index * 100}%)` }}
154 + >
155 + {slides.map((shot, i) => (
156 + <figure
157 + key={shot.src}
158 + className="flex h-full w-full shrink-0 flex-col items-center justify-center"
159 + aria-roledescription="slide"
160 + aria-label={`${i + 1} of ${count}`}
161 + aria-hidden={i !== index}
162 + >
163 + <div
164 + className="flex min-h-0 flex-1 items-center justify-center"
165 + style={{ maxWidth: shot.device === "phone" ? 260 : 420 }}
166 + >
167 + <Image
168 + src={shot.src}
169 + alt={shot.alt}
170 + width={shot.width}
171 + height={shot.height}
172 + priority={i === 0}
173 + sizes="(max-width: 640px) 90vw, 420px"
174 + className="h-full w-auto rounded-card border border-border bg-surface object-contain shadow-sm"
175 + />
176 + </div>
177 + <figcaption className="mt-4 text-center text-sm text-muted">
178 + {shot.caption}
179 + </figcaption>
180 + </figure>
181 + ))}
182 + </div>
183 + </div>
184 +
185 + <button
186 + type="button"
187 + onClick={next}
188 + aria-label="Next screenshot"
189 + className="hidden shrink-0 rounded-full border border-border bg-surface p-3 text-muted shadow-sm transition-colors hover:text-ink sm:block"
190 + >
191 + <Arrow dir="right" />
192 + </button>
193 + </div>
194 +
195 + {/* Dots */}
196 + <div className="mt-6 flex items-center justify-center gap-2.5">
197 + {slides.map((shot, i) => (
198 + <button
199 + key={shot.src}
200 + type="button"
201 + onClick={() => goTo(i)}
202 + aria-label={`Go to ${shot.caption}`}
203 + aria-current={i === index}
204 + className={`h-2 rounded-full transition-all ${
205 + i === index ? "w-6 bg-brand" : "w-2 bg-border hover:bg-muted"
206 + }`}
207 + />
208 + ))}
209 + </div>
210 + </div>
211 + </section>
212 + );
213 +}
web/components/site-footer.tsx new
+28
@@ -0,0 +1,28 @@
1 +import Link from "next/link";
2 +
3 +export function SiteFooter() {
4 + return (
5 + <footer className="border-t border-border">
6 + <div className="mx-auto flex max-w-5xl flex-col gap-4 px-6 py-10 text-sm text-muted sm:flex-row sm:items-center sm:justify-between">
7 + <p>© {new Date().getFullYear()} Siti AI. All rights reserved.</p>
8 + <ul className="flex gap-6">
9 + <li>
10 + <Link href="/privacy" className="hover:text-ink">
11 + Privacy
12 + </Link>
13 + </li>
14 + <li>
15 + <Link href="/terms" className="hover:text-ink">
16 + Terms
17 + </Link>
18 + </li>
19 + <li>
20 + <Link href="/contact" className="hover:text-ink">
21 + Contact
22 + </Link>
23 + </li>
24 + </ul>
25 + </div>
26 + </footer>
27 + );
28 +}
web/components/site-nav.tsx new
+30
@@ -0,0 +1,30 @@
1 +import Link from "next/link";
2 +
3 +const links = [{ href: "/about", label: "About" }];
4 +
5 +export function SiteNav() {
6 + return (
7 + <header className="sticky top-0 z-10 border-b border-border bg-bg/80 backdrop-blur">
8 + <nav className="mx-auto flex max-w-5xl items-center justify-between px-6 py-4">
9 + <Link
10 + href="/"
11 + className="text-[17px] font-semibold tracking-tight text-ink"
12 + >
13 + Siti<span className="text-brand"> AI</span>
14 + </Link>
15 + <ul className="flex items-center gap-6 text-sm text-muted">
16 + {links.map((link) => (
17 + <li key={link.href}>
18 + <Link
19 + href={link.href}
20 + className="transition-colors hover:text-ink"
21 + >
22 + {link.label}
23 + </Link>
24 + </li>
25 + ))}
26 + </ul>
27 + </nav>
28 + </header>
29 + );
30 +}
web/next.config.mjs new
+26
@@ -0,0 +1,26 @@
1 +import path from "node:path";
2 +import { fileURLToPath } from "node:url";
3 +
4 +const __dirname = path.dirname(fileURLToPath(import.meta.url));
5 +
6 +/** @type {import('next').NextConfig} */
7 +const nextConfig = {
8 + // Required by the smbCloud `nextjs-ssr` deploy path: produces `.next/standalone`.
9 + output: "standalone",
10 +
11 + // Serve the App Store screenshots as-is. Disabling the optimizer keeps the
12 + // standalone server free of a runtime `sharp` dependency and the /_next/image
13 + // route, which simplifies the multi-tenant smbCloud deploy.
14 + images: {
15 + unoptimized: true,
16 + },
17 +
18 + // This app lives in a pnpm workspace. Pin the tracing root to the repo root so
19 + // Next.js traces hoisted dependencies deterministically instead of guessing from
20 + // multiple lockfiles. Because the root is above this app, the standalone output
21 + // preserves the source path (`.next/standalone/web/server.js`); the smbCloud CLI
22 + // handles this nested runtime layout.
23 + outputFileTracingRoot: path.join(__dirname, ".."),
24 +};
25 +
26 +export default nextConfig;
web/package.json new
+25
@@ -0,0 +1,25 @@
1 +{
2 + "name": "@siti/web",
3 + "version": "0.1.0",
4 + "private": true,
5 + "type": "module",
6 + "scripts": {
7 + "dev": "next dev -p 3034",
8 + "build": "next build",
9 + "start": "next start -p 3034",
10 + "lint": "next lint"
11 + },
12 + "dependencies": {
13 + "next": "^15.5.19",
14 + "react": "^19.2.7",
15 + "react-dom": "^19.2.7"
16 + },
17 + "devDependencies": {
18 + "@tailwindcss/postcss": "^4.3.1",
19 + "@types/node": "^22.19.21",
20 + "@types/react": "^19.2.17",
21 + "@types/react-dom": "^19.2.3",
22 + "tailwindcss": "^4.3.1",
23 + "typescript": "~5.8.3"
24 + }
25 +}
web/postcss.config.mjs new
+8
@@ -0,0 +1,8 @@
1 +/** @type {import('postcss-load-config').Config} */
2 +const config = {
3 + plugins: {
4 + "@tailwindcss/postcss": {},
5 + },
6 +};
7 +
8 +export default config;
web/public/screenshots/ipad-chat.png
Binary files /dev/null and b/web/public/screenshots/ipad-chat.png differ
web/public/screenshots/ipad-settings.png
Binary files /dev/null and b/web/public/screenshots/ipad-settings.png differ
web/public/screenshots/iphone-chat.png
Binary files /dev/null and b/web/public/screenshots/iphone-chat.png differ
web/public/screenshots/iphone-settings.png
Binary files /dev/null and b/web/public/screenshots/iphone-settings.png differ
web/tsconfig.json new
+23
@@ -0,0 +1,23 @@
1 +{
2 + "compilerOptions": {
3 + "target": "ES2022",
4 + "lib": ["dom", "dom.iterable", "esnext"],
5 + "allowJs": true,
6 + "skipLibCheck": true,
7 + "strict": true,
8 + "noEmit": true,
9 + "esModuleInterop": true,
10 + "module": "esnext",
11 + "moduleResolution": "bundler",
12 + "resolveJsonModule": true,
13 + "isolatedModules": true,
14 + "jsx": "preserve",
15 + "incremental": true,
16 + "plugins": [{ "name": "next" }],
17 + "paths": {
18 + "@/*": ["./*"]
19 + }
20 + },
21 + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
22 + "exclude": ["node_modules"]
23 +}